Monday, February 17, 2014

First step -- a chat interface

The first thing we need is a way to communicate between python and the arduino over the serial port.

Python side

The `pyserial` library makes things easy here.
## 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)
view raw chat.py hosted with ❤ by GitHub

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.
#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");
}
}
view raw Chat.ino hosted with ❤ by GitHub

The actual `receive_chat` function resides in a library.
#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;
}
view raw chat.cpp hosted with ❤ by GitHub

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