NEED HELP Communication from Arduino Uno to Pi


#1

Hi All,

Hoping somebody could help with a project… first post so be gentle. Overarching project is a laser trip wire, but i’ve maxed out the GPIO pins on Pi doing multiple other functions, so using an Arduino UNO for the Keypad where they enter a code and shut down the system etc. Hoping the below makes some sense, ive incl code. If anyone can advise it would be greatly appreciated.

Set-up

  • Numeric keypad is connected to Arduino Uno via GPIO pins.
  • Arduino Uno is connected to raspberry Pi via USB port.
    Aim
    I want the Arduino to read keypress inputs form the keypad and store them as a variable, appending each keypress to the end of the string. When “enter”(# Key) is pressed the variable is checked against a pre-set password to check if its correct. When correct it returns the value “3” and if incorrect it returns the value “5”. This is then output over a serial connection to the Pi on port 9600.
    I want the Pi to loop through logging a bunch of other inputs from its GPIO pins ( I have simplified this in the attached example to the command ‘print “test”’), unless it gets the value “3” (correct password) from the Arduino.

Issue
The Python while loop appears to wait for the signal from the Arduino before continuing with the loop and does not continue to print “test”. It only prints “test” after it has received a signal from the Arduino…

Pi Code

'#Arduino Uno connected to USB port on raspberry Pi (locked to /ttyACM0). reading on port 9600.

import serial
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)

'#Read from the USB serial port on the raspberry Pi
ser = serial.Serial('/dev/ttyACM0', 9600)   

 # Set up a loop
while True:				  
check = int(float(ser.readline()))      # Reads the value received from the USB serial port, converts it to an integer and assigns it to the variable 'check'
  #timeout = 1			 ' # Not sure what this function does or if it is useful here.
print check                         '# Prints value assigned to 'check'
    
print "test"                     '# Prints "test"

Arduino Code:

//Aurdino Keypad Comm to Pi

//include the password library: 
'#include <Password.h>
'#include <Wire.h>

// include the keypad code:
'#include <Keypad.h>

// state the password:
Password password = Password( "3570" );


const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
//Define Key map
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = { 5, 6, 7, 8 }; //connect to the row pinouts of the keypad
byte colPins[COLS] = { 2, 3, 4}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {
  Serial.begin (9600);
  
  keypad.addEventListener(keypadEvent); //add an event listener for this keypad
  keypad.setDebounceTime(75);  //less bounce is more responsive
}

void loop() {
 //Serial.println("0");
 keypad.getKey();  // take keypad input
}

// 
void keypadEvent(KeypadEvent eKey){
  switch (keypad.getState()){
      case PRESSED:
      switch (eKey){
      case '#': guessPassword(); break;  // # is the 'enter' key
      default:
      password.append(eKey);
     }
  }  
}

void guessPassword(){
  if (password.evaluate()){       //if code is correct:
    delay(10);
    Serial.println(3); // pi gets message code is correct
    password.reset();  //resets password after correct entry
  }
  else{
    password.reset();  //resets password after INCORRECT entry
    delay(0);
    Serial.println(5); // pi gets message code is incorrect.
    delay(0);
  }
}

#2

You should use three backticks to format you code, so we can read it.

Like so:

```
Your code here
```

ser.readline() is a blocking operation, which means nothing following it will be executed until it has completed. If there’s no line to read, it will simply wait forever.

You probably need to include a timeout and handle the timeout exceptions accordingly. In the case of PySerial a timeout condition will return '' (empty string).

>>> s.readline()
''

And coercing an empty string into an integer will fail:

>>> int(s.readline())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: ''

See: http://pyserial.readthedocs.io/en/latest/shortintro.html

Be careful when using readline(). Do specify a timeout when opening the serial port otherwise it could block forever if no newline character is received. Also note that readlines() only works with a timeout. readlines() depends on having a timeout and interprets that as EOF (end of file). It raises an exception if the port is not opened correctly.

And for all the options available when instantiating the Serial class:

http://pyserial.readthedocs.io/en/latest/pyserial_api.html

Notably:

Possible values for the parameter timeout which controls the behavior of read():

  • timeout = None: wait forever / until requested number of bytes are received
  • timeout = 0: non-blocking mode, return immediately in any case, returning zero or more, up to the requested number of bytes
  • timeout = x: set timeout to x seconds (float allowed) returns immediately when the requested number of bytes are available, otherwise wait until the timeout expires and return all bytes that were received until then.