Greg131
Posts: 2
Joined: Tue Jan 02, 2018 3:41 am

Waiting for serial confirmation from Arduino

Wed Jan 03, 2018 8:11 pm

I'm trying to control a tracked robot using a bluetooth controller. My Raspberry Pi 3 is getting the bluetooth control signal, then passing it over to the Arduino through USB where that controls the motors.

I have a two-line LCD screen displaying the speed values for troubleshooting purposes. As I push up and down on the sticks on the controller, I can see the values changing as expected, but after a few seconds it stops working.
If I add a 1 second delay in my Python script after it sends the string, it seems to work indefinetly. My only theory is the Arduino's buffer is filling up since the RPi is sending so much information.

Rather than just putting in an arbitrary delay, I'd like to add something so that the Arduino could send a confirmation back through serial when it is done, and then the Raspberry Pi would wait until it receives that to send another string. I know how to send back through serial, but how would you get the RPi to wait for the confirmation?

I modified from code that was used to make a game controller monitoring window to put the string together:

Code: Select all

import pygame
import serial

ser =  serial.Serial('/dev/ttyACM0', 9600)


pygame.init()
 
#Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates
clock = pygame.time.Clock()

# Initialize the joysticks
pygame.joystick.init()
    

# -------- Main Program Loop -----------
while done==False:
    # EVENT PROCESSING STEP
    for event in pygame.event.get(): # User did something
        if event.type == pygame.QUIT: # If user clicked close
            done=True # Flag that we are done so we exit this loop
        
        # Possible joystick actions: JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUTTONUP JOYHATMOTION
           
 
    # DRAWING STEP
    # First, clear the screen to white. Don't put other drawing commands
    # above this, or they will be erased with this command.

    # Get count of joysticks
    joystick_count = pygame.joystick.get_count()

    
    # For each joystick:
    for i in range(joystick_count):
        joystick = pygame.joystick.Joystick(i)
        joystick.init()
        # Get the name from the OS for the controller/joystick
        #name = joystick.get_name()
    
        # Usually axis run in pairs, up/down for one, and left/right for
        # the other.
        LeftControl = joystick.get_axis( 1 )
        RightControl = joystick.get_axis( 3 )
        LCString =  str(int(round(LeftControl * 100,0) ) )
        RCString =  str(int(round(RightControl * 100,0) ) )
	
        abutton = joystick.get_button( 0 )
        aButString =  str(abutton)
        fullString = "<" +  LCString + ","  +  RCString + "," + aButString + ">"
        ser.write(fullString)
	    
    # ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
    
    # Go ahead and update the screen with what we've drawn.

    # Limit to 20 frames per second
    clock.tick(1)
    
# Close the window and quit.
# If you forget this line, the program will 'hang'
# on exit if running from IDLE.
pygame.quit ()


Once it gets to the Arduino, it parses the string into left track and right track speed and direction, and then uses that to control the motors.

Code: Select all

#include <MotorDriver.h>
#include <seeed_pwm.h>

#include <SoftwareSerial.h>

MotorDriver motor;

const int TxPin = 2;

SoftwareSerial mySerial = SoftwareSerial(255, TxPin);

const byte numChars = 32;
char receivedControl[numChars];
char tempChars[numChars];

int left = 0;
int right = 0;
int abutton = 0;
boolean newData = false;

void setup() {
  // put your setup code here, to run once:
pinMode(TxPin, OUTPUT);
digitalWrite(TxPin,HIGH);
mySerial.begin(9600);
delay(100);
mySerial.write(12);
delay(5);
mySerial.print("Left:");
mySerial.write(13);
mySerial.print("Right:");
Serial.begin(9600);
motor.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
    recvControl();
    if (newData == true) {
        strcpy(tempChars, receivedControl);
    parseData();
    showNewData();
    newData = false;
    }
}
void recvControl() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedControl[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedControl[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    left = atoi(strtokIndx);
//    strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
 
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    right = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, ",");
    abutton = atoi(strtokIndx);     // convert this part to a float

}

void controlDrive(){
  motor.speed(0,left*-1);
  motor.speed(1,right*-1);
}
void showNewData() {
    if (newData == true) {
      if (left >=0){
        mySerial.write(134);
        mySerial.print("+");
        mySerial.print(left);
      }
      else {
        mySerial.write(134);
        mySerial.print(left); 
      }
      if (right >=0){
        mySerial.write(154);
        mySerial.print("+");
        mySerial.print(right);
      }
      else {
        mySerial.write(154);
        mySerial.print(right); 
      }
       
        newData = false;
     }
}
    

Greg131
Posts: 2
Joined: Tue Jan 02, 2018 3:41 am

Re: Waiting for serial confirmation from Arduino

Thu Jan 04, 2018 3:52 am

I found something that looked similar to what I was looking for, and it seems to work. I'm not 100% sure if it's the correct way of doing it, since I don't have a ton of programming experience, but I'd be open to feedback, or the solution might help someone else.

I added this after the python script sends the string to Arduino:

Code: Select all

def CheckReadUntil(self, readUntil):
            outputCharacters = []
            while 1:
                ch = self.ser.read()
                if len(ch) == 0:
                    break
                outputCharacters += ch
                if outputCharacters[-len(readUntil):]==readUntil:
                    break
                outputLines = ''.join(outputCharacters)
                return outputLines
I then had the Arduino send back "readUntil":

Code: Select all

void confirm() {
  Serial.print("readUntil");
}


Now, the whole Python script is:

Code: Select all

import pygame
import serial
import time

ser =  serial.Serial('/dev/ttyACM0', 9600, timeout = None)

pygame.init()
 
#Loop until the user clicks the close button.
done = False

# Used to manage how fast the screen updates
clock = pygame.time.Clock()

# Initialize the joysticks
pygame.joystick.init()
    

# -------- Main Program Loop -----------
while done==False:
    # EVENT PROCESSING STEP
    for event in pygame.event.get(): # User did something
        if event.type == pygame.QUIT: # If user clicked close
            done=True # Flag that we are done so we exit this loop
        
        # Possible joystick actions: JOYAXISMOTION JOYBALLMOTION JOYBUTTONDOWN JOYBUTTONUP JOYHATMOTION
           
 
    # DRAWING STEP
    # First, clear the screen to white. Don't put other drawing commands
    # above this, or they will be erased with this command.

    # Get count of joysticks
    joystick_count = pygame.joystick.get_count()

    
    # For each joystick:
    for i in range(joystick_count):
        joystick = pygame.joystick.Joystick(i)
        joystick.init()
        # Get the name from the OS for the controller/joystick
        #name = joystick.get_name()
    
        # Usually axis run in pairs, up/down for one, and left/right for
        # the other.
        LeftControl = joystick.get_axis( 1 )
        RightControl = joystick.get_axis( 3 )
        LCString =  str(int(round(LeftControl * 100,0) ) )
        RCString =  str(int(round(RightControl * 100,0) ) )
	
        abutton = joystick.get_button( 0 )
        aButString =  str(abutton)
        fullString = "<" +  LCString + ","  +  RCString + "," + aButString + ">"
        ser.write(fullString)
        ser.flushInput()
        def CheckReadUntil(self, readUntil):
            outputCharacters = []
            while 1:
                ch = self.ser.read()
                if len(ch) == 0:
                    break
                outputCharacters += ch
                if outputCharacters[-len(readUntil):]==readUntil:
                    break
                outputLines = ''.join(outputCharacters)
                return outputLines

                        
    # ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
    
    # Go ahead and update the screen with what we've drawn.

    # Limit to 20 frames per second
    clock.tick(20)
    
# Close the window and quit.
# If you forget this line, the program will 'hang'
# on exit if running from IDLE.
pygame.quit ()
And the Arduino Sketch is:

Code: Select all

#include <MotorDriver.h>
#include <seeed_pwm.h>
#include <SoftwareSerial.h>

MotorDriver motor;

const int TxPin = 2;

SoftwareSerial mySerial = SoftwareSerial(255, TxPin);

const byte numChars = 32;
char receivedControl[numChars];
char tempChars[numChars];

int left = 0;
int right = 0;
int abutton = 0;
boolean newData = false;

void setup() {
  // put your setup code here, to run once:
pinMode(TxPin, OUTPUT);
digitalWrite(TxPin,HIGH);
mySerial.begin(9600);
delay(100);
mySerial.write(12);
delay(5);
mySerial.print("Left:");
mySerial.write(13);
mySerial.print("Right:");
Serial.begin(9600);
motor.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
    recvControl();
    if (newData == true) {
        strcpy(tempChars, receivedControl);
    parseData();
    showNewData();
    confirm();
    newData = false;
    }
}
void recvControl() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedControl[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedControl[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    left = atoi(strtokIndx);
//    strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
 
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    right = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, ",");
    abutton = atoi(strtokIndx);     // convert this part to a float

}

void controlDrive(){
  motor.speed(0,left*-1);
  motor.speed(1,right*-1);
}
void showNewData() {
    if (newData == true) {
      if (left >=0){
        mySerial.write(134);
        mySerial.print("+");
        mySerial.print("   ");
        mySerial.write(135);
        mySerial.print(left);
      }
      else {
        mySerial.write(134);
        mySerial.print("    ");
        mySerial.write(134);
        mySerial.print(left); 
      }
      if (right >=0){
        mySerial.write(154);
        mySerial.print("+");
        mySerial.print("   ");
        mySerial.write(155);
        mySerial.print(right);
      }
      else {
        mySerial.write(154);
        mySerial.print("    ");
        mySerial.write(154);
        mySerial.print(right); 
      }
      //  Serial.print("This just in ... ");
      //  Serial.println(left);
        newData = false;
     }
}

    
void confirm() {
  Serial.print("readUntil");
}

Return to “Python”