Arduino Uno --> Raspberry Pi W/Python 3.4 *** Serial communications w/UART

hi Robin,

I didn't even see your reply when I posted earlier.

I will give it a go this weekend and report back on my findings.

Yes, I know how much this stuff is a pain in the butt between 2.7 and 3.4. Documentation is sketchy at best and for someone like me that has never touched Python before, it is extremely difficult to find solutions that work.

Thanks a bunch, will give it a go.

Chris D

Good. I nearly said something rude in response to Reply #19 :slight_smile:

...R

hi Robin,

Well, the errors are gone, however, this is what happens...

"Arduino is ready" is printed on the PI side.

PI sends the command and prints (Sent from PC = Loop NUM 0 ........)
Arduino responds with (Reply Received Msg LED1 NewFlash 200 .......)
PI sends the command and prints ("Sent from PC = LOOP NUM 1......)

And it does nothing more at this point in time.

I suspect it is hanging in the watingForReply loop at this time.

Chris

Post the exact two programs (Python and Arduino) that you are using and I will try them.

What is the exact OS on your RPi?

...R

Hi Robin

I Installed the NOOBS package which is Rasbian (Graphical User Interface) on the PI 3.

Here is the python program

#=====================================

#  Function Definitions

#=====================================

def sendToArduino(sendStr):
  #ser.write(sendStr)  # python 2.7
  ser.write(sendStr.encode('utf-8')) #python 3.4


#======================================

def recvFromArduino():
  global startMarker, endMarker
  
  ck = ""
  x = "z" # any value that is not an end- or startMarker
  byteCount = -1 # to allow for the fact that the last increment will be one too many
  
  # wait for the start character
  while  ord(x) != startMarker: 
    x = ser.read()
  
  # save data until the end marker is found
  while ord(x) != endMarker:
    if ord(x) != startMarker:
      #ck = ck + x #python 2.7
      ck = ck + x.decode("utf-8")
      byteCount += 1
    x = ser.read()
  
  return(ck)


#============================

def waitForArduino():

   # wait until the Arduino sends 'Arduino Ready' - allows time for Arduino reset
   # it also ensures that any bytes left over from a previous message are discarded
   
    global startMarker, endMarker
    
    msg = ""
    while msg.find("Arduino is ready") == -1:
      

      while ser.inWaiting() == 0:
       
        pass
        
      msg = recvFromArduino()

      print(msg)
      print()
      
#======================================

def runTest(td):
  numLoops = len(td)
  waitingForReply = False

  n = 0
  while n < numLoops:

    teststr = td[n]

    if waitingForReply == False:
      sendToArduino(teststr)
      print ("Sent from PC -- LOOP NUM " + str(n) + " TEST STR " + teststr)
      waitingForReply = True

    if waitingForReply == True:

      while ser.inWaiting() == 0:
        pass
        
      dataRecvd = recvFromArduino()
      print( "Reply Received  " + dataRecvd)
      n += 1
      waitingForReply = False

      print( "===========")

    time.sleep(5)


#======================================

# THE DEMO PROGRAM STARTS HERE

#======================================

import serial
import time

print()
print()

# NOTE the user must ensure that the serial port and baudrate are correct
#ser = serial.Serial("/dev/ttyS0",9600, timeout =1)
serPort = "/dev/ttyS0"
baudRate = 9600
ser = serial.Serial(serPort, baudRate)


print( "Serial port " + serPort + " opened  Baudrate " + str(baudRate))


startMarker = 60
endMarker = 62


waitForArduino()


testData = []
testData.append("<LED1,200,0.2>")
testData.append("<LED1,800,0.7>")
testData.append("<LED2,800,0.5>")
testData.append("<LED2,200,0.2>")
testData.append("<LED1,200,0.7>")

runTest(testData)


ser.close

And here is the ARDUINO program

#include <Servo.h>

Servo myServo;
byte servoPin = 8;
byte servoMin = 10;
byte servoMax = 170;
byte servoPos = 0;
byte newServoPos = servoMin;

const byte numLEDs = 2;
byte ledPin[numLEDs] = {12, 13};
unsigned long LEDinterval[numLEDs] = {200, 400};
unsigned long prevLEDmillis[numLEDs] = {0, 0};

const byte buffSize = 40;
char inputBuffer[buffSize];
const char startMarker = '<';
const char endMarker = '>';
byte bytesRecvd = 0;
boolean readInProgress = false;
boolean newDataFromPC = false;

char messageFromPC[buffSize] = {0};
int newFlashInterval = 0;
float servoFraction = 0.0; // fraction of servo range to move


unsigned long curMillis;

unsigned long prevReplyToPCmillis = 0;
unsigned long replyToPCinterval = 1000;

//=============

void setup() {
  Serial.begin(9600);
  
    // flash LEDs so we know we are alive
  for (byte n = 0; n < numLEDs; n++) {
     pinMode(ledPin[n], OUTPUT);
     digitalWrite(ledPin[n], HIGH);
  }
  delay(500); // delay() is OK in setup as it only happens once
  
  for (byte n = 0; n < numLEDs; n++) {
     digitalWrite(ledPin[n], LOW);
  }
  
    // initialize the servo
  myServo.attach(servoPin);
  moveServo();
  
    // tell the PC we are ready
  Serial.println("<Arduino is ready>");
}

//=============

void loop() {
  curMillis = millis();
  getDataFromPC();
  updateFlashInterval();
  updateServoPos();
  replyToPC();
  flashLEDs();
  moveServo();
}

//=============

void getDataFromPC() {

    // receive data from PC and save it into inputBuffer
    
  if(Serial.available() > 0) {

    char x = Serial.read();

      // the order of these IF clauses is significant
      
    if (x == endMarker) {
      readInProgress = false;
      newDataFromPC = true;
      inputBuffer[bytesRecvd] = 0;
      parseData();
    }
    
    if(readInProgress) {
      inputBuffer[bytesRecvd] = x;
      bytesRecvd ++;
      if (bytesRecvd == buffSize) {
        bytesRecvd = buffSize - 1;
      }
    }

    if (x == startMarker) { 
      bytesRecvd = 0; 
      readInProgress = true;
    }
  }
}

//=============
 
void parseData() {

    // split the data into its parts
    
  char * strtokIndx; // this is used by strtok() as an index
  
  strtokIndx = strtok(inputBuffer,",");      // get the first part - the string
  strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
  
  strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
  newFlashInterval = atoi(strtokIndx);     // convert this part to an integer
  
  strtokIndx = strtok(NULL, ","); 
  servoFraction = atof(strtokIndx);     // convert this part to a float

}

//=============

void replyToPC() {

  if (newDataFromPC) {
    newDataFromPC = false;
    Serial.print("<Msg ");
    Serial.print(messageFromPC);
    Serial.print(" NewFlash ");
    Serial.print(newFlashInterval);
    Serial.print(" SrvFrac ");
    Serial.print(servoFraction);
    Serial.print(" SrvPos ");
    Serial.print(newServoPos);
    Serial.print(" Time ");
    Serial.print(curMillis >> 9); // divide by 512 is approx = half-seconds
    Serial.println(">");
  }
}

//============

void updateFlashInterval() {

   // this illustrates using different inputs to call different functions
  if (strcmp(messageFromPC, "LED1") == 0) {
     updateLED1();
  }
  
  if (strcmp(messageFromPC, "LED2") == 0) {
     updateLED2();
  }
}

//=============

void updateLED1() {

  if (newFlashInterval > 100) {
    LEDinterval[0] = newFlashInterval;
  }
}

//=============

void updateLED2() {

  if (newFlashInterval > 100) {
    LEDinterval[1] = newFlashInterval;
  }
}

//=============

void flashLEDs() {

  for (byte n = 0; n < numLEDs; n++) {
    if (curMillis - prevLEDmillis[n] >= LEDinterval[n]) {
       prevLEDmillis[n] += LEDinterval[n];
       digitalWrite( ledPin[n], ! digitalRead( ledPin[n]) );
    }
  }
}

//=============

void updateServoPos() {

  byte servoRange = servoMax - servoMin;
  if (servoFraction >= 0 && servoFraction <= 1) {
    newServoPos = servoMin + ((float) servoRange * servoFraction);
  }
}

//=============

void moveServo() {
  if (servoPos != newServoPos) {
    servoPos = newServoPos;
    myServo.write(servoPos);
  }
}

I Installed the NOOBS package

How appropriate.

Chris_D:
Here is the python program

It works fine on this laptop.

The only change I made was to set the Python code serial port to TTYACM0 - which is how the Uno appears on my system.

My wild guess is that the Arduino is not getting the full message - but i can't think why.

In the Python function recvFromArduino() add a line to print each x as it is received and tell me what happens.

...R

PaulS:
How appropriate.

PaulS,

Just go away, your idea of helping isn't what anyone wants. If you feel like slinging insults, go down to the nearest biker bar and pick a fight there. I am sure it will be much more enjoyable.

Chris D.

Hi Robin,

Okay, I did what you suggested and I received back the whole message that was sent.

The data that came back from the print(x) upon power up of UNO was as such :

b'A'
b'r'
b'd'

and so until the last few lines...

b'd'
b'y'
b'>'

The data that came back from the print(x) was as such for the actual command:

b'M'
b's'
b'g'

and so until the last few lines...

b' '
b'1'
b'>'

followed with Reply Received Msg LED1 NewFlash.....
then =============
Sent from PC - LOOP NUM1 TEST STR <LED1,800,0,7>

After that, nothing is happening....

Chris D

I have no experience with RaspberryPIs so I'm really shooting in the dark.

I assume your Uno is connected to the RPi using the USB connection?

Have you any means to send data from your Arduino to a PC while the Arduino is connected to the RPi? For example have you a USB-TTL cable or another Arduino?

...R

Hi Robin,

The UNO is connected to the PI via I/O pins which are marked for TX/RX so they are (should be) hardware UARTS. Currently getting the serial port (of any type) on the PI to work is a bit of a mystery and requires changing a number of config files etc. I know I got through that part because it does communicate (most of the time).

I will dig around and find something to use with a PC and do some further experimenting. I have another UNO on order as a back-up so I could use that too. I also have a logic analyzer around here somewhere. Have not used it in years but that should allow me to see what the signals are actually sending.

I suspect this could be hardware related as well. Once I get the new UNO and PI back-ups, I am going to run the same tests and trials on them to see if they behave differently.

Thank you very much for all your help and assistance on this crazy problem. Thanks also for your patience!

It will probably be a couple of weeks before I can try all these other things. If I find anything that is conclusive I will report back here with the details.

Thanks again!

Chris D

Chris_D:
The UNO is connected to the PI via I/O pins which are marked for TX/RX so they are (should be) hardware UARTS.

That is probably a help. For testing you could create a SoftwareSerial port on the Uno and use two other pins to communicate with the RPI. That will leave HardwareSerial free for sending debug messages to the PC.

If you create an instance of SoftwareSerial called (say) SoftSerial then in the existing Arduino code change ALL the references from Serial to SoftSerial.

Then just add a line Serial.println(messageFromPC); so it sends the data to your PC (not the Pi) before your call parseData() (because parseData() will modify the data.

...R

If you create an instance of SoftwareSerial called (say) SoftSerial then in the existing Arduino code change ALL the references from Serial to SoftSerial.

Or, say, pi, since that is what you are talking to, not a SoftSerial...

PaulS:
Or, say, pi, since that is what you are talking to, not a SoftSerial...

I know where you are coming from but in the circumstances I think "SoftSerial" is less confusing. I assume the OP will revert to HardwareSerial when he has things figured out.

...R

Hi Robin2,

Just wanted to update you on what I hope is the final outcome.

I have tried two different UNO's and two different Raspberry Pi, all yielded the same results, some good data mixed in with a lot of bad data.

After a lot of searching on Google and the various forums, I found what appears to be the cause and solution to the problem.

It is all on the PI side of things. Apparently, on the PI 3, they "engineers" decided to add bluetooth which also affected the functionality of the UART. There were a couple of workarounds, one that required setting the core frequency to 250 (normal is MUCH higher). The other workaround was to edit a number of files and add a patch file which re-directs the serial ports but doesn't require slowing down the core frequency.

Once all this was done, UART serial communications runs like it should, fast, reliable, and simple.

Thanks again for all your help.

Chris D

Thanks for the update. That sort of thing can be very frustrating.

...R