Python side
The `pyserial` library makes things easy here.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Set up device | |
# 0 means return whatever is available immediately | |
# otherwise, wait for specified time | |
# 0.01 takes a noticeable but small amount of CPU time | |
self.ser = serial.Serial(serial_port, 9600, timeout=serial_timeout) | |
# Wait for it to initialize the arduino | |
time.sleep(0.1) | |
self.ser.flushInput() | |
## From device to user | |
def read_from_device(device): | |
"""Receives information from device and appends""" | |
new_lines = device.readlines() | |
return new_lines | |
## From user to device | |
def write_to_device(device, data): | |
if data is not None: | |
device.write(data) | |
The `timeout` keyword is a key element here. That defines how long `readlines` will wait for input before returning. If you set it to 0, it will return whatever it finds immediately. The problem with that approach is that we're going to be calling this function over and over again in an infinite loop. I suspect a smaller number of calls with a small delay will end up being more efficient than a much larger number of calls with a near-zero delay. That's something to test layer.
The `write` function actually writes immediately and returns ("non-blocking").
The Arduino side
The arduino needs something to say. Let's have it report the time every second. Also, let's have it acknowledge any chat it receives, simply by echoing back whatever it hears.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "chat.h" | |
void setup() | |
{ | |
Serial.begin(9600); | |
Serial.println("Running setup."); | |
} | |
unsigned long speak_at = 1000; | |
unsigned long interval = 1000; | |
void loop() | |
{ | |
unsigned long time = millis(); | |
// Announce the time, occasionally ending with a newline | |
if (time > speak_at) | |
{ | |
/* | |
if (speak_at % 3 == 0) | |
Serial.println(time); | |
else | |
Serial.print(time); | |
*/ | |
Serial.println((String) "The time is " + time); | |
speak_at += interval; | |
} | |
// Receive any chat | |
String received_chat; | |
received_chat = receive_chat(); | |
if (received_chat.length() > 0) | |
{ | |
// Do something with it | |
Serial.println((String) "You sent a string of " + | |
received_chat.length() + " characters"); | |
} | |
} |
The actual `receive_chat` function resides in a library.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Arduino.h" | |
#include "chat.h" | |
String receive_buffer = ""; | |
String receive_line = ""; | |
String receive_chat() | |
{ | |
// If characters available, add to buffer | |
int n_chars = Serial.available(); | |
char got = 'X'; | |
String return_value = ""; | |
for(int ichar = 0; ichar < n_chars; ichar++) | |
{ | |
got = Serial.read(); | |
if (got == '\n') | |
{ | |
// Add the newline | |
receive_buffer += got; | |
// Put buffer in line | |
// Start buffer over | |
receive_line = receive_buffer; | |
receive_buffer = ""; | |
break; | |
} | |
else | |
{ | |
receive_buffer += got; | |
} | |
} | |
// If a line available, echo it | |
if (receive_line.length() > 0) | |
{ | |
Serial.print("ACK "); | |
// Strip the newline | |
// This needs to take Windows newlines into account too | |
Serial.print(receive_line.substring(0, receive_line.length()-1)); | |
Serial.println(""); | |
return_value = receive_line; | |
receive_line = ""; | |
} | |
return return_value; | |
} |
Things are more complicated here than in Python because we have only low-level functions available. Whenever there are new characters available, we add them to a global variable `receive_buffer`. It has to be global because it needs to persist over many calls to this function.
Once the buffer gets to a newline, it stops taking characters and returns the line it received. It also echoes it on the serial port so that Python knows its message was received.
What if multiple lines are waiting to be received? This will only return the first one. This is easier than coding up a linked list of multiple commands. It also means that other things have a chance to run on the rest of the update cycle.
Now the arduino can communicate with Python. But how does Python interact with the user? The user might be a real person, or it might be another Python script, for instance one controlling the state machine. The next post will cover the methods by which the user can read what the arduino is saying, and type in responses.
No comments:
Post a Comment