luppupj2
Posts: 1
Joined: Sat Dec 08, 2018 3:29 am

Python stops Sending to Arduino

Sat Dec 08, 2018 3:40 am

Hello,
The last few days I have been having an issue where Python 3.5, on Linux, stops sending serial data to an Arduino Mega. On the Pi 3 B+, I have a GPS on the UART pins, an IMU running Arduino on ttyACM1 and the Mega on ttyACM0. Python seems to be reading from the IMU and GPS without an issue but after running through the loop about 4-6 times fails to send anything to the Mega and the code comes to a halt. I have verified that the code completely stops when attempting to send to the Mega but works fine after restarting the python code.

The purpose of the code is to receive data from the IMU and GPS, calculate heading, and calculate motor values based on the required heading to a waypoint.
I am sending a simple string to the Arduino made up of motor values and a comma for parsing.
Any help is appreciated.

Idahowalker
Posts: 444
Joined: Wed Jan 03, 2018 5:43 pm

Re: Python stops Sending to Arduino

Sat Dec 08, 2018 7:06 pm

The Python code?

I know, not a Arduino forum but the Arduino code?

I take it you are using level shifters of some sort between the RPi and Mega?

I have ran a RPi to Mega serial com app that sent a cht load of data back and forth for months at a time, so I know its possible.

I now run a RPi to DUE application. I switched to the DUE for the 32 bits, the increased CPU clock rate, no need for level shifters and, also I can use uMT, a RTOS, on the DUE.
My DUE loop code:

Code: Select all

void loop()
{ for ( ;; )
  {
    if ( stream2Ptr->available() >= 1 )
    {
      Kernel.Ev_Send(iEvtID_A, EVENT_A); // takes a semaphore to cause this loop to wait, whiles processing received data.
      Kernel.Sm_Claim(SEM_ID_04, uMT_WAIT); // stop and wait for serial to be received and release of SEMAPHORE token
    }  //  serial available
    Kernel.Tk_Yield();
  }
} // loop
One of the process, a loop, where the RPi sends a command to the DUE, the DUE processes the command and triggers an event to run another process pB, pB runs and sends its results to the RPi, the RPI processes the results, and sends a command to the DUE. The entire loop is dependent upon each step of the chain to continually work and the speed of the chain is how fast each component can run their varoius functions. But, on occasion the serial link will drop a character, the message will fail and the loop will collapse.

One of the processes triggered by a 32kHz interrupt on the RPi is a counter. One of the process in the above loop sets the counter to 0. If the count gets to 1280, an event is triggered to restart the collapsed loop; a watchdog thingy.

Thus, if your process is dependent upon the data being transmitted / received, you might want to consider a watchdog.

Next, if you are using a callback on the RPi to detect serial comms you should not have much processing in the call back. I have found that if a callback recvies a trigger whiles it is still doing stuff the callback will fail and it will not produce an error message. If your callback is calling a function the callback is busy till the other function is completed and if a trigger comes into the callback the callback fails. The callback must do the least amount of work as possible.

Here is my serial recieve code:

Code: Select all

def fSerialCallBack(iChannel):

    sReceive = b""

    ## print( "fSerialCallBack triggered")
    try:
        if serialPort.open:
            #disable uno from collecting and sending data while Pi is busy processing
            GPIO.output(iPiReady, GPIO.HIGH)
            sReceive = serialPort.readline()
            fProcessData(sReceive.decode('utf-8')) 
        else:

            print("Port", sConstants[iZero], " !open")
    except:
        pass
    finally:
        #RASPBERRY PI READY
        if serialPort.open:
            try:
                serialPort.flushInput()
            except:
                pass
    return;
Notice that the code, basically, just receives data and passes it on to fProcessData(sReceive.decode('utf-8')). The fProcessData(sReceive.decode('utf-8')) by the callback is considered part of the callbacks thread and will prevent interrupts from happening and, if it runs for a long time can stop the serial callback from doing its thing and will not produce an error message.

Here is a code snippet for fProcessData(sReceive.decode('utf-8')) :

Code: Select all

def fProcessData(sTandH):

    ## global bAspectChangeInProgress
    sTemp = ""

    try:
        ## print( "fProcessData " + sTandH)
        sTemp = sTandH.strip() # for error massage
        if ( len(sTandH) <=  iTwo ):
            return;
        ## print( "fProcessData " + sTandH)
        sTandH = sTandH.strip()
        sTandH = sTandH.split()
####        if (sTandH[iZero] == sConstants[iOne] ):
            if (sTandH[iOne].isdigit()):
                oOD.fDoTheObjectDetectThing( sTandH[iOne], bAspectChangeInProgress, fltDistanceAngle, int(sTandH[iTwo]), int(sTandH[iThree]) )
In this code a quick message decode is done and the info is passed onto a class for processing.
In the class is this function:

Code: Select all

def fDoTheObjectDetectThing( self, sDistanceLength, bAspectChangeInProgress, fltDistanceAngle, iSignalStrength, sMotion ):
        try: 
            self.ReturnPulse = int(sDistanceLength)
            self.bAspectChangeInProgress = bAspectChangeInProgress
            self.fltDistanceAngle = fltDistanceAngle
            self.SignalStrength = iSignalStrength
            self.bMotion = bool( sMotion )
            self.oTrigger.set()
        except Exception as e:
            print("oOD.fDoTheObjectDetectThing " + str(e))      
        return;
The above function distrubites the data received and triggers a threaded event. At the moment the threaded event is triggered, the serial callback is released to receive.

Here is the code in my 32kHz interrupt handler:

Code: Select all

def f32K( iChannel ):
####
    oTrigger32K.set()
#### 
    return;
A threaded event trigger. I found if I add any more code then that the callback stops without generating an error.

I found that the callbacks can run for days or just minutes before failing.

Learn Python threading, threading queues, and threading events is my suggestion and get the serial processing code to the least amount as possible. Remember any function called by the callback becomes part of the callback thread.
Without knowing why you are deleting my postings, I will not know how...

PhatFil
Posts: 926
Joined: Thu Apr 13, 2017 3:55 pm
Location: Oxford UK

Re: Python stops Sending to Arduino

Sat Dec 08, 2018 7:48 pm

if the offending loop is a wait/query/publish loop? why not make the program a single execute publish and terminate procedure, and then call it regularly from a cron job or a governing script.

Return to “Python”