1

I try to make a my Arduino microcontroller and my Mac talk together, and have created a functional serial connection. My computer is sending data to my Arduino, and my Arduino is sending a '1' when it is ready to receive a new piece of data.

I have created an if-else statement (Python script below), which is either sending a new line of data to the Arduino or waiting for the Arduino to be ready for receiving a new line of data.

The problem is that ser.read() in the first part of the Python script always returns '1', which means that the script is sending the individual data lines faster than the Arduino connected stepper motors can possible react.

In the Arduino script you can see that I am sending the state-status in the first line of the serialEvent() function, which in my world should let the Arduino finish its job, before a new "task" is coming. However, it is for some reason not working. Can anybody help me out here?

Python script

import os
import time
import serial

# Name of csv file with drawing coordinates
csvFile = "scaled_coordinates.csv"

# Create serial connection
ser = serial.Serial(port='/dev/tty.usbserial-A9005bDh', baudrate=9600)

wd = os.getcwd()
myFile = open(wd + "/coordinates/" + csvFile)

state = '1'

while True: # Exits when there is no more lines to read

    if state == '0': # Wait for Arduino to be ready
        state = ser.read()

    elif state == '1': # Send one more line to Arduino
        line = myFile.readline()
        if not line:
            break
        print line
        ser.write(line)
        #time.sleep(1)

        state = '0' # Wait for Arduino before reading next line

myFile.close

Arduino loop function

void loop() {

  serialEvent(); // Call the serial function

  if (coord_complete) {

    // Steps to move from currrent to new point
    target1 = steps(x_current, y_current, x_new, y_new, 1);
    target2 = steps(x_current, y_current, x_new, y_new, 2);

    // Start moving
    stepper1.move(target1);
    stepper2.move(target2);

    // Update current position
    x_current = x_new;
    y_current = y_new;

    // Reset variables
    x_complete = false;
    y_complete = false;
    coord_complete = false;
  }

  // Stay in while loop until steppermotors is done
  while ((stepper1.distanceToGo() != 0) && (stepper2.distanceToGo() != 0)) {
    stepper1.run();
    stepper2.run();
  }
}

Arduino serialEvent function

void serialEvent() {

  Serial.write('1'); // Tell Python that Arduino is ready for one more line

  while (Serial.available() && coord_complete == false) {
    char ch = Serial.read(); // Get new character
    Serial.print(ch);

    // If digit; add it to coord_string
    if (isDigit(ch)) {
      coord_string[index++] = ch;

    // Else if ch is ","; then rename to x_new
    } else if (ch == ',') {
      coord_string[index++] = NULL;                   // Finish coord_string
      x_new = atoi(coord_string);                     // Convert to integer
      x_complete = true;                              // Change x_complete to true
      index = 0;                                      // Reset index
      memset(coord_string, 0, sizeof(coord_string));  // Reset coord_string

    // Else if ch is a new line; then rename as y_new
    } else if (ch == ';') {
      //Serial.write('0');
      coord_string[index++] = NULL;
      y_new = atoi(coord_string);
      y_complete = true;
      index = 0;
      memset(coord_string, 0, sizeof(coord_string));
    }

    // Ends while-loop when true
    coord_complete = x_complete * y_complete;
  }
}

Edit

The current Python code looks like this:

import os
import time
import serial

# Name of csv file with drawing coordinates
csvGraphic = "Scaled_coordinates.csv"

# Create serial connection
ser = serial.Serial(port='/dev/tty.usbserial-A9005bDh', baudrate=9600)

wd = os.getcwd()
myFile = open(wd + "/graphics/" + csvGraphic)

state = '1'

while True: # Exits when there is no more lines to read

  print "state", state

  if state == '0': # Wait for Arduino to be ready
    state = str(ser.read())

  elif state == '1': # Send one more line to Arduino
    line = myFile.readline()
    if not line:
      ser.close()
      break
    print line
    ser.write(line)
    state = '0' # Wait for Arduino before reading next line

ser.close()
myFile.close

The Python output is shown below. The code is executed in one go, without waiting for the Arduino. It seems like the line state = str(ser.read()) reads data which is in some kind of serial buffer. I guess the solution is to clear the buffer. I just don't know how.

state 1
239,275;

state 0
state 1
1100,275;

state 0
state 1
300,400;

state 0
state 1
200,400;

state 0
state 1
200,300;

state 0
state 1
[Finished in 0.1s]
1
  • Not 100 % suer what you mean. But I suppose i open the connection in the line saying ser = serial.Serial(port='/dev/tty.usbserial-A9005bDh', baudrate=9600). Commented Nov 19, 2015 at 19:34

2 Answers 2

1

Think I found it. Your SerialEvent() is called at the begining of your loop. The first thing it does is write('1') meaning each time loop is executed it tells your python code it's ready for new instructions (Even if no instructions were given!) and filling buffer with lot on '1' which you read one by one

Try this:

void SerialEvent(){
    if((stepper1.distanceToGo() == 0) && (stepper2.distanceToGo() == 0)){
        Serial.write('1');
    }
    //Rest of the function

Also I think at the end of your loop you want

while((stepper1.distanceToGo() != 0) || (stepper2.distanceToGo() != 0))

Instead of while((stepper1.distanceToGo() != 0) && (stepper2.distanceToGo() != 0))

Sign up to request clarification or add additional context in comments.

7 Comments

Still not working. Maybe I have to empty the state buffer or something? I have implemented the if-statement you suggest but without luck. Also, I removed the line saying Serial.print(ch); in the serialEventfunction. Regarding the while loop and the && or || operator, it should be the && because both of the motors need to finish before the execution of the code continues.
If I put print "test" in the top of the Python while loop, it should keep on printing it no matter what. However, after the second execution of the if-statement inside the while loop, (most often) nothing is printet, and the program is not finishing. Other times, it executes it all in one go, like the state == '1' at all the time.
@Christoffer if both motors need to finish then you must use ||, example when stepper1 ended and stepper2 doesn't: while((0 == 0) && (3 != 0)) -> while( true && false) -> while(false) -> exits.
About the python code, try to change print "test" at the begining of the while loop for a print state. One thing I read from pyserial documentation: Changed in version 2.5: Returns an instance of bytes when available (Python 2.6 and newer) and str otherwise. Maybe you have to convert what you have read to string, state = str(ser.read())
You are absolutely right about the ||operator. I have edited the post with the new Python code and the output. I think the problem is buffer related, and I somehow need to clear it.
|
1

Thanks to Mr. E I found a solution. Basically I needed to clear the serial buffer before I read or write (flushInput() and flushOutput() in Python and flush() in Arduino) new data between Python and Arduino. Also, a small delay in the Python and Arduino code was necessary to implement in order to make it work.

Pyhton

import os
import time
import serial

# Name of csv file with drawing coordinates
fileName = "coordinates.csv"

# Create serial connection
ser = serial.Serial(port='/dev/tty.usbserial-A9005bDh', baudrate=9600)

wd = os.getcwd()
myFile = open(wd + "/graphics/" + fileName)

#num_lines = sum(1 for line in myFile)

state = '0'
idx = 0

while True: # Exits when there is no more lines to read

    #print "state", state

    while state == '0': # Wait for Arduino to be ready
        ser.flushInput() # Clear input buffer
        state = str(ser.read())

    if state == '1': # Send one more line to Arduino
        line = myFile.readline()
        if not line:
            break
        print "Coordinate", idx
        print line
        ser.flushOutput() # Clear output buffer
        ser.write(line)

        time.sleep(0.1)

        idx = idx + 1
        state = '0' # Set waiting flag – make Arduino wait for next line

ser.close()
myFile.close

Arduino serialEvent function

void serialEvent() {

  if ((stepper1.distanceToGo() == 0) && (stepper2.distanceToGo() == 0)) {
    Serial.write('1'); // Tell Python that Arduino is ready for one more line
    delay(10);
    Serial.flush(); // clear buffer
  }

  while (Serial.available() && coord_complete == false) {
    char ch = Serial.read(); // Get new character
    Serial.flush();

    // If digit; add it to coord_string
    if (isDigit(ch)) {
      coord_string[index++] = ch;

    // Else if ch is ","; then rename to x_new
    } else if (ch == ',') {
      coord_string[index++] = NULL;                   // Finish coord_string
      x_new = atoi(coord_string);                     // Convert to integer
      x_complete = true;                              // Change x_complete to true
      index = 0;                                      // Reset index
      memset(coord_string, 0, sizeof(coord_string));  // Reset coord_string

    // Else if ch is a new line; then rename as y_new
    } else if (ch == ';') {
      //Serial.write('0');
      coord_string[index++] = NULL;
      y_new = atoi(coord_string);
      y_complete = true;
      index = 0;
      memset(coord_string, 0, sizeof(coord_string));
    }

    // Ends while-loop when true
    coord_complete = x_complete * y_complete;
  }
}

1 Comment

This is perhaps not the best solution. Your calls to Serial.flush inside the read loop are particularly worrisome. Rather than relying on clearing the input buffer and delays, it would probably be better to have the Arduino only send status when it changes, rather than to send repeats which can stack up in a buffer. If you are concerned about a missed response possibly causing the python to wait forever, you can implement a query command which does not cause any movement, but just tells the Arduino to send its status.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.