Package: camino
CaminoCamino enables blazing fast, non-intrusive communication over a serial connection between
Python and up to 256 Arduinos. It provides a simple way to call functions on a remote
Arduino, sending and receiving up to 255 bytes of data. Besides the built in functions
(digital_write,pin_mode,analog_read, etc.), approximately 250 user-defined
functions can be defined.Table of ContentsInstallationExampleAdding Custom FunctionsFeatures and LimitationsOptionsProtocolContributingInstallationPythonTo install the latest Python release ofcamino, simply runpip install camino. The
library requirespyserial. This should be installed automatically, but additional
configuration may be required for some setups. See thepyserial docsfor details.To install the latest development version (for testing or contributing), clone this
repository and runpip install -e .from inside the root directory.ArduinoCamino is available through the Arduino IDE Library Manager:This version is shipped with the default options, and should work for most users.If you need to change options (see theFeatures and Options section),
you'll need to manually include the library. Download this repository. Then copysrc/Camino.cppandsrc/Camino.hinto your sketch. If using the Arduino IDE,
you can use the "Add File" option. To avoid conflicts, make sure youuninstallthe library if you installed it via the IDE.Installing manually also means you won't have the example sketches mentioned below.
It's recommended to use the defaults options (and install normally) to get familiar
with the library before changing them.ExampleOnce the Python and Arduino libraries are installed, you should be able to run the
examples.ArduinoInstallexamples/CaminoExampleto your Arduino. It is copied below:#include"Camino.h"voidsetup(){camino.begin(9600);}voidloop(){}// Example that returns the sum of some data (%256)voidadd(bytedataLength,byte*dataArray){bytesum=0;for(bytei=0;i<dataLength;i++){sum+=dataArray[i];}returns(sum);}// Example that returns a stringvoidsayHi(bytedataLength,byte*dataArray){returns("I can say hi!");}BEGIN_CALLABLES{{"say_hi",sayHi},{"add",add},}END_CALLABLES;When uploading the sketch, take note of which serial port the Arduino is connected
to. Be sure to keep it plugged in.PythonOpen a Python shell.>>>importcaminoCreate aSerialConnectionfor the Arduino's port. On Linux these look like/dev/ttyS0,/dev/ttyACM0, etc. On Windows they look likeCOM1,COM2, etc.
Make sure the baud rate matches what is in the sketch.>>>connection=camino.SerialConnection(port='/dev/ttyACM0',baud=9600)Connect to an Arduino.>>>arduino=camino.Arduino(connection)This will begin communicating with the Arduino to discover which functions are
available. This should take less than a second. Now you can use the built in
methods:>>>arduino.pin_mode(12,0)# set pin 12 to INPUT>>>arduino.pin_mode(16,1)# set pin 16 to OUTPUT>>>value=arduino.digital_read(12,out=int)>>>arduino.digital_write(16,value)As well as the custom functions defined:>>>print(arduino.say_hi(out=str))Icansayhi!>>>print(arduino.add(1,2,3,out=int))6Adding Custom FunctionsThe following functions are defined for all Arduinos:pin_mode
digital_write
digital_read
analog_read
analog_writeAs demonstrated in the example, if you want to do something else, you can
define functions specific to your application.Acallableis a function that can be called by Camino from Python.In order to be callable, a function must have return typevoidand accept
exactlybyte dataLength, byte *dataas arguments.Then, you must include the callable in an entry of theCALLABLESsection.
In this entry, you specify the Python name for the callable, and a
reference to the function. The Python name should be no longer thanMAX_DATA_LENGTH - 1characters, because it is sent in a packet.Assume you have a single callable calledtriggerPiston, and want to be able
to call it from python asarduino.trigger_piston(). At the bottom of your
Arduino code, you'll write the following:BEGIN_CALLABLES{{"trigger_piston",triggerPiston},}END_CALLABLES;Returning DataTo allow all callables to have the same type, data is returned using one
of the availablereturns(...)methods, rather thanreturnstatements.To return a scalar (byte,char,unsigned short,short,unsigned int,int,unsigned long, orlong), you can pass it directly. For instancereturns((int) -1067);.
Be sure that your signedness in Python matches what you send in C.To return strings, you can pass aconst char*, likereturns("hello");.For anything else, you can pass a length andbyte *. For instancereturns(dataLength, data);,
which is how the builtinechocallable works.You do not need to callreturns(...).  If a function does not callreturns(...),
the Arduino simply responds with no data (Nonein Python).  Callingreturns(...)multiple times overwrites the return value.Limitations to Callable FunctionsCallables have limitations.They are executed within an interrupt. This means they should be fast, and
they can't usedelay,millis,Serial, or other interrupt-dependent
functions. For a good overview of the limitations to interrupts on Arduino,
seeNick Gammon's notes. If you want to use
interrupt-dependent functions or expect the call to take a long time, you can
have code running inloopdependent on some global variables. You can, of course,
change global variables from inside a Callable.When a problem occurs with a transmission, a command packet may be resent up
to 3 times. Therefore, you should design your callables to beidempotent.
Repeat calls with the same arguments should have the same effect as a single call.
For instance, instead of creating atoggle_ledcallable, you should createset_ledand keep track of the state in Python. Of course, if you are getting
any failed function calls, something is probably amiss with your serial connection.Using Custom Functions from PythonCallables act as a link between an Arduino function and the corresponding Python
function. The Python names are sent from the Arduino when they establish a connection,
and automatically added to theArduinopython object.You can call these functions as you would call any other function. Note that Camino
isnotthread safe--only call one function at a time.Callables automatically convert input arguments to abytesobject. For instance,
a singlestris converted usingordon each character, or a bunch ofints are
appended into abytesobject. If the automatic conversion doesn't work as expected,
you can pass abytesobject directly.If the callable returns no data, Python returnsNone. Otherwise, the return type
defaults tobytes. You can have Camino automatically convert thebytesto
another type by passing theoutkeyword argument. The options are:bytes- rawbytesreturned by the Callableint- convert the bytes to a single Pythonintusing little endian. You should also specify if
the number is signed by passingsigned=True(default) orsigned=False.str- convert the bytes to a PythonstrFeatures and LimitationsOne Python host can control up to 256 individually addressable Arduinos. Pass
the desired address of the Arduino as the second parameter tocamino.open. In
Python, pass the address as the second argument to theArduinoconstructor.Each packet may contain between 0 and 255 bytes of data. It is a known issue
that packets with more than around 100 bytes of data are unreliable in some
setups.Camino operates entirely through interrupts, so does not require any code inloop. For instance, you can easily collect data from sensors inloop(using
delays, serial writes, etc.), and still be able to quickly report aggregate data
when polled using a callable.Lastly, Camino is incredibly fast, with low overhead. Even at low baud-rates, it is
possible to PWM digital pins using onlydigital_writefrom within Python.OptionsA few options are listed inCamino.hfor more advanced use cases.If you want to use values other than the defaults, you'll need to manually download
and include the library in your sketch, and change them inCamino.h.UART Port:PORTThe UART port used by Camino. Can be-1,0,1,2. We default to UART0, which is
usually the USB port. This means you can't use Serial in your sketch. For
other boards, like the Arduino Mega, you could have Camino listen on UART2
(Serial2) and use the normal Serial port in parallel.Maximum Data Length in Packets:MAX_DATA_LENGTHThe maximum length of the data section for packets (to and from the Arduino).
Value must be at least 16 and at most 255. Making it smaller reduces the
memory footprint of Camino.Command Timeout:COMMAND_TIMEOUT_MS.The command timeout in milliseconds. When a new byte in a packet is received,
if this period has elapsed since the start of the packet, the earlier
bytes are discarded and Camino assumes that a new packet is beginning.Defaults to 100 milliseconds. With a well-behaved connection, this should
never matter.Transmission HooksThere are three possible macros that act as hooks into the lifecycle of a
packet transmission:initTransmissions()is called in begin()beginTransmission()is called before the first byte is sent in a response packet.endTransmission()is called after the last byte is sent in a response packet.Sometimes you may need to do some preparation before a packet can be sent.
For instance, you may have a pin that enables a pull down resistor to remove
noise from your transmission line when not sending anything. Or when debugging, it
may be useful to shine an LED when packets are being sent.ProtocolThe protocol refers to the host Python machine as the Master, and the Arduino
as the Slave.The protocol follows a simple rule that no slave may transmit unless it is in
response to a command from the master, and only one response per command is
allowed.The master's command packet always takes the same format. It specifies which
Arduino is being addressed, which command should be run, and what data to
pass to the command.The slave can respond in one of three ways:Response with data, for instance withdigital_readResponse with no data, for instance withdigital_writeResend request, if there is a problem understanding the command.Single Command Communication FlowPython sends a command packet containing an address, command, and data.All Arduinos connected to the serial port read the entire packet.If the checksum is invalid, Arduino transmits a Resend Request.Otherwise, check if the address matches the Arduino. If it doesn't,
ignore the command.Otherwise, if the checksum is valid, and the address matches the
Arduino, run the specified command with the given data. Then,
respond with either data or no data.Python will attempt to send a command packet up to 3 times before giving up
and raising aCaminoError.Packet StructureCommand packet1 byte1 byte1 byte1 byte1 bytedata lengthbytes1 byte0xAA0x55addresscommanddata lengthdatachecksumChecksum is the sum of address, command, data length, and all bytes of data, mod 256.Response with data1 byte1 byte1 bytedata lengthbytes1 byte0xAC0xACdata lengthdatachecksum*Checksum is the sum of data length and all bytes of data, mod 256.Response with no data1 byte1 byte0xA90xA9Resend request1 byte1 byte0xB80xB8ContributingPlease visit theIssues Pagefor a list of
known issues. Feel free to add issues, or contribute comments to issues that affect
you.Contributions in the form of PRs are also welcome, either adding new features or
solving existing issues.This project was originally developed in theDos Pueblos Engineering Academy. Specifically,
it was used to communicate between an Arduino Mega and a Raspberry Pi in the Air Guitar
project. We used RS-485, with some voltage steppers and other custom circuits. For a more
detailed description of that, seethis page.