0

Currently I am using an Arduino in combination with Python. I am writing the Python code now and for an unknown reason it breaks halfway an if-loop. I think the reason might be the serial communication... I'll try to explain it with my code.

For easy reading: the parts of the code that are of interest are between rows of '====='. I put the rest of the code in for context.

Arduino Code

const int mPin      = 2;  // Pin attached to the Mark button
const int cPin      = 3;  // Pin attached to the Capture button
const int qPin      = 4;  // Pin attached to the Quit button
const int solAPin   = 6;  // Solenoid logic pin A - RELEASE PIN
const int solBPin   = 7;  // Solenoid logic pin B - CLOSE PIN
const int ledPin    = 13; // Onboard LED

boolean lastmButton  = 0;
boolean currmButton  = 0; 
boolean lastcButton  = 0;  
boolean currcButton  = 0;  
boolean lastqButton  = 0; 
boolean currqButton  = 0; 

void setup() {
  // Initial code, run at startup
  pinMode(mPin, INPUT); // Reads button press
  pinMode(cPin, INPUT); // Reads button press
  pinMode(qPin, INPUT); // Reads button press
  pinMode(solAPin, OUTPUT);
  pinMode(solBPin, OUTPUT);
  pinMode(ledPin, OUTPUT);

  digitalWrite(solAPin, LOW);
  digitalWrite(solBPin, LOW);
  digitalWrite(ledPin, LOW);

  Serial.begin(9600); // Open Python communication line
}

boolean debounce(boolean last, int Pin) {
  boolean current = digitalRead(Pin);
  if (last != current)
  {
    delay(5);
    current = digitalRead(Pin);
  }
  return current;
}

void loop() {
  // Main code, continious loop
  currmButton = debounce(lastmButton, mPin);
  currcButton = debounce(lastcButton, cPin);
  currqButton = debounce(lastqButton, qPin);
  // ===========================================================
  if (currmButton == HIGH && lastmButton == LOW) {
    mark();
  }
  if (currcButton == HIGH && lastcButton == LOW) {
    Serial.print("C");
  }
  if (currqButton == HIGH && lastqButton == LOW) {
    Serial.print("Q");
  }
  lastmButton = currmButton;
  lastcButton = currcButton;
  lastqButton = currqButton;
}
//==============================================================

void mark() {
  digitalWrite(solBPin, HIGH); // Close Pin, pencil moves down
  delay(300);
  digitalWrite(solBPin, LOW);
  digitalWrite(solAPin, HIGH);  // Release Pin
  delay(100);
  digitalWrite(solAPin, LOW);   // Neutral
  Serial.print("M");
}

The debounce-code is not optimal, but it works for now. As you can see; the Arduino writes M/C/Q to Python when respectively the M/C/Q buttons is pressed. This all works fine.

Now the problem:

Python Code

if (__name__ == '__main__' and fail == False):
# Create directory
    print("Startup")   
    index = 1   # Image counter
    if os.path.exists(image_path) == False:
        os.makedirs(image_path)

# Find the Arduino Port
    try:
        portname = ""
        ports = list(serial.tools.list_ports.comports())
        for p in ports:
            if "Arduino" in p[1]:
                portname = p[0]
                break
        print("Arduino found on " + portname)
    except:
        print("Setup failed:\n  Arduino not found")
        fail = True

# Open Arduino serial communication           
    try:    
        arduino = serial.Serial(portname, baudrate=9600, timeout=1)    
        arduino.close()
        arduino.open()
        print("Arduino Connected")
    except serial.SerialException:
        print("Setup failed:\n  Could not connect to Arduino")
        fail = True

# Find and open the camera stream
    try:
        for i in range (10):
            cap = cv2.VideoCapture(i)
            if (cap.read()):
                if cap.isOpened() == False:
                    cap.open()
                break
        print("Camera Connected\n")
    except:
        print("Setup failed:\n  Camera not found")
        fail = True


#%%# MAIN LOOP
#====================================================================

    while True:

        k = arduino.readline()
        if   k == 'M': # Mark & capture
            print('Arduino: Marked')
            print ("Python Captured\n")
        elif k == 'C':# Capture
            print "Arduino: Python, capture this dot please" # This line is executed when K == C (button pressed on Arduino)
            print "Python: Captured!\n" # This line is not executed directly afterwards, but appears together with the line above when the button is pressed for the SECOND time!

        elif k == 'Q': # Quit 
            print "Arduino: Quit the program"                
            break
#=====================================================================

When running the Python code, the Arduino connection is set up. When one of the buttons is pressed, the Arduino sends the correct signal to Python, so far so good. But: only the first row of the if-loop is executed. The remaining rows are executed when the button is pressed for the second time, followed by the first row again (since the button is pressed for a second time).

For instance, running the code and pressing 'C' twice results in the following console output:

Startup
Arduino found on COM4
Arduino Connected
Camera Connected
[button C is pressed for first time]

Arduino: Python, capture this dot please
[buttong C is pressed for second time]
Python: Captured!

Arduino: Python, capture this dot please

What I've tried so far:

  • Placing delays in the Arduino code
  • Placing 'Serial.flush()' in the Arduino code
  • Adjusting the way the info is printed, different quotes/brackets etc.

So this is where I'm stuck and I don't really know where to look for the bug. If any extra information is necessary, I'm happy to provide it. I hope someone here can help me!

EDIT:

As I mentioned in the comments, placing a print-command in the while loop magically fixes the issue:

k = arduino.readline()
print k
if   k == 'M': # Mark & capture

But why this resolves the issue is still unclear to me.

Thanks in advance, Stijn

Stijn
  • 1
  • 1
  • What have you tried? Have you tried simply running a terminal program on the PC and checking that only CMQ are received? Have you in the python tried printing k and len(k) which should be 1 after it has been read from the arduino? – DisappointedByUnaccountableMod Dec 20 '16 at 09:45
  • Thank you for your response. I have indeed tried this before and the received messages from the Arduino are only C/M/Q. But after trying it again I found out that the code works when I place 'print k' between 'k = arduino.readline()' and the if-loop 'if k ==M .....'. Strange huh? – Stijn Dec 21 '16 at 00:48
  • I would try adding `sys.stdout.flush()` after each pair of *python* `print` instructions, and see what happens.. sometimes *"debugging"* using *stdout* rather than a *debugger* shows spurious behaviours like this that are just the result of the *output buffer* not being *flushed* immediately. – Patrick Trentin Dec 27 '16 at 17:12
  • **Note:** `Serial.print()` does not print a line at all, it just prints the data and that's it. Therefore, normally the line of code `k = arduino.readline()` would have blocked because there is no `line feed` in the input stream. Your code works because of the option `timeout=1` in the creation of the `Serial` instance, which makes `readline()` return early with a partial string. However this is bad design, because in principle more than one character could be waiting in the input queue when `readline` is invoked. – Patrick Trentin Dec 27 '16 at 17:23
  • Therefore, I would remove the `timeout=1` setting and use `Serial.println()` instead. Note that you would have to remove the trailing *line-feed* from `k` before doing any *string comparison* in *python*. An alternative option would be to read one `byte` at a time on the *python* end, which might however cause some other issues in case you start sending characters that do not belong to the *ASCII* table. – Patrick Trentin Dec 27 '16 at 17:23

0 Answers0