2 files changed, 137 insertions, 2 deletions
diff --git a/host/proto/doc/proto.txt b/host/proto/doc/proto.txt
new file mode 100644
@@ -0,0 +1,128 @@
+ proto - Host proto communication API.
+:Author: Nicolas Schodet
+This module handles the communication protocol defined in the *proto* AVR
+module. It can be used to communicate through a read serial port with an
+embedded program running on an AVR, or using a pseudo-terminal or standard
+input/output to communicate with a program compiled for the host system.
+This module also handles the master part of the protocol:
+- Acknowledgement from the slave device.
+- Retransmission if no acknowledgement is received.
+- Retransmission if the slave signals an error.
+This module does not handle higher level protocols, such as duplicates
+handling or frame sequence number.
+To understand this documentation, you should be familiar with the protocol,
+which is explained in the AVR module documentation.
+First, create a Proto instance. For example to connect with a real serial
+ proto = Proto (serial.Serial ('/dev/ttyS0'), time.time, 0.1)
+To connect to a program running on host, with a logging output::
+ def log (text):
+ print text
+ proto = Proto (proto.popen_io.PopenIO ('test_program.host'), time.time, 0.1,
+ log = log)
+You need to synchronise regularly with the remote program. You have two
+The first one can be used if you have nothing else to execute apart from the
+protocol handling. In this case, just call the wait function. If a argument
+is given, this should be a callable object which will return True to stop
+The wait function will only return if all frames have been sent and the
+condition argument returns True.
+Example, wait forever::
+ proto.wait ()
+Or, wait until an internal counter has reached a value::
+ proto.wait (lambda: my_counter == 3)
+The second option should be used if you need concurrent access to several
+sources (GUI interface, several proto, other events...). In this case, the
+sync function should be called regularly (at least every timeout period), and
+the read function should be called if data is available on the proto file
+descriptor which can be retrieved as usual using the fileno function.
+ while not proto.sync ():
+ fds = select.select ((proto, other_input), (), (), 0.1)
+ for fd in fds:
+ fd.read ()
+Sending a frame
+To send a frame, use the send function.
+First argument is the command character. Then comes the frame arguments format
+followed by the frame arguments. The format follows the struct module fmt
+ proto.send ('z') # Send !z
+ proto.send ('a', 'b', 1) # Send !a01
+ proto.send ('b', 'bhl', 1, 2, 84942856) # Send !b01000205102008
+The frame is not sent immediately, but once wait or sync is called and all the
+previously enqueued frames are acknowledged.
+Receiving a frame
+When a frame is received, a user callback will be called if it has been
+To register a callback, use the register function. The first argument is the
+frame command to bind to this callback, the second one is the frame arguments
+format, and the last one is the callback.
+Here, the distinction between signed and unsigned arguments is important.
+If a frame is received with the registered command and a number of bytes
+matching the frame arguments format, the callback is called with arguments
+ def my_callback1 (a, b):
+ print "callback1", a, b
+ def my_callback2 (a):
+ print "callback2", a
+ proto.register ('x', 'bB', my_callback1)
+ proto.register ('y', 'l', my_callback2)
+ # If !xffff is received, this will print:
+ # callback1 255 -1
+ # If !y05102008 is received, this will print:
+ # callback2 84942856
+Keep in mind that the protocol can not make the distinction between the format
+'bbbb' and 'l' as it uses the same number of bytes.
diff --git a/host/proto/proto.py b/host/proto/proto.py
index 36616d9c..9eb401e1 100644
@@ -33,7 +33,14 @@ class Proto:
def __init__ (self, file, date, timeout, log = None):
"""Initialise and set file (serial port, pty, socket...), date
- function and timeout value."""
+ function and timeout value.
+ - file: open file connected to the slave device.
+ - date: when called, should return the current time.
+ - timeout: time after which retransmission is done.
+ - log: if defined, will be called with a log string.
self.file = file
self.date = date
self.last_send = None
@@ -70,7 +77,7 @@ class Proto:
def wait (self, cond = None):
"""Wait forever or until cond () is True."""
- while not (self.sync () and (cond is None or cond ())):
+ while not (self.sync () and (cond is not None or cond ())):
fds = select.select ((self,), (), (), self.timeout)
for i in fds:
assert i is self