Gazebo- A New wired networking protocol (with IO control sketch)

Hello everyone! I am new to this forum and to Arduino in general(I've done PIC for years though) and wanted to share a project I have been working on. It is a protocol that lets a master discover devices, ask them about their capabilities, read and write data, and perform remote function calls. Devices have a fixed 128 bit UUID so no more dip switches or buying vendor ids.

My original intent was to create a modern hobbyist oriented lighting control protocol but it got way outta hand fast :slight_smile:
The protocol supports discovery,error reporting,units of measure, arrays,nested arrays,enumerations,nested arrays of enumerations, FIFO queues(messagewise or itemwise) and a whole bunch of other stuff.

The key idea is that devices document themselves. In the below code example the computer had no idea that the device was supposed to have a parameter named AnalogWrite, it asked the device for its parameter list, then asked for details about each parameter.

I have only tested over USB with the leonardo so far, and the code is early pre alpha and needs a ton of work, but I do have a somewhat stable working prototype. I intend to test with RS485 soon.

I have made a simple sketch called SpookyCastle with Firmata-like features using the protocol, and a python library which is well documented.
The sketch used compiles to around 9k of code for the Leonardo.
Here is a code example:

Python 3.2 (r32:88445, Feb 20 2011, 21:30:00) [MSC v.1500 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>> 
>>> manager = NetworkManager('com6') #using serial port com6
>>> manager.EnumerateSlaves()  #Search the "bus"( not rs486 at the moment)
>>> manager.slaves #after 15s per device or so it populates the list
{'Z3RoczR2bG9nZmVk': <Gazebo Slave SpookyCastle for Arduino I/O board with ID Z3RoczR2bG9nZmVk>}
>>> s =  manager.slaves['Z3RoczR2bG9nZmVk']
>>> s.params #lets take a look at the parameters the slave exposes
{'AnalogWrite': <Parameter Object AnalogWrite of type void with interpretation void>, 'DigitalWrite': <Parameter Object DigitalWrite of type void with interpretation void>, 'AnalogRead': <Parameter Object AnalogRead of type uint16 with interpretation Volts*204.8>, 'TheRaven': <Parameter Object TheRaven of type UTF-8[0:80] with interpretation GrimUngainlyGhastlyGauntAndOminousBirdOfYore>, 'DigitalRead': <Parameter Object DigitalRead of type uint8 with interpretation boolean>, 'PinMode': <Parameter Object PinMode of type void with interpretation void>}
>>> analogWrite = s.params['AnalogWrite']
>>>analogWrite.pinfo()

Readable parameter of type void to be interpreted as void
This parameter plays role none in group none of type none
The following arguments are required when reading from this parameter(in first to last order):
GazeboReadArgument(name='pin', type='uint8', interpretation='number')
GazeboReadArgument(name='value', type='uint8', interpretation='duty*255')
Reads are idempotent(two succesive reads will produce the same data absent external changes).


The slave provides the following description of ths parameter:Use ~460Hz PWM to write an analog value to a pin.
>>> analogWrite(3,127)
True
>>> #It returned true because the slave sent an ACK
>>> digitalRead = s.params['DigitalRead']
>>> digitalRead.read(1)
0
>>> #Parameters are callable objects, not functions. calling the param() directly
>>> #is an alias for param.read()

>>> #in Gazebo, function calls are read operations with arguments. Gazebo parameters also may support write operations, which are what the sound like.
>>> digitalRead.read(1)
1
>>> #After connecting voltage to Arduino input 1, we see that we read a 1 from it.
>>> digitalWrite = s.params['DigitalWrite']
>>> digitalWrite(2,'high')
True
>>> #Gazebo supports enumeration types
>>> digitalWrite(2,0)
True
>>> #Which are compatible with simple numbers
>>>master.close() #Otherwise pyserial sits around hogging the port till you restart or close from task manager

All the code is available here:

Keep in mind the core protocol is not backwards compatible till version 1.0

As a repeat offender in the serial monitoring and control network protocol designing field I find this quite interesting.

My original intent was to create a modern hobbyist oriented lighting control protocol but it got way outta hand fast smiley

I know the feeling :slight_smile:

I've gone away from the master-slave model because of perceived reliability issues (IE lose the master you lose the network) but I appreciate that's how many networks run and it does work.

A slave should respond to a broadcast packet exactly as it would a packet addressed only to it.

How can more than one slave respond to a broadcast message?

This feature means that multiple baud rates can coexist on one bus without the use of auto-baud detection.

I was going to ask how this could be reliable but I see further down in the doc that you recommend a single bit rate if you want more reliability.


Rob

.~lock.ElectronicsSpreadsheet.ods#

Probably should not include ODS lock files in the repository.

Graynomad:
I should probably use clearer language. "respond" in "A slave should respond to a broadcast packet exactly as it would a packet addressed only to it." was intended to simplify implementation of slaves by allowing them to send data back even if there is no point because it will be garbled, and also the line was meant to imply that any packet can be used broadcasted or multicasted. I will change that.

With regard to the reliability concerns of master-slave networks, I actually originally was going to go with a CSMA-CA type scheme, but I did not like the fact that two devices could start transmitting at the same time and garble things up, and unless you acknowledged everything(which would require either using BREAK sequences or something like that that can't interfere, or having only one device acknowledge).

I also thought about CAN style voltage levels(wired-OR), but I didn't really want to give up compatibility with common USB to serial adapters(which have 1ms latency on a lot of machines).

I suppose I could have done token-ring but there are a lot of edge cases especially on cheap noisy unshielded unterminated lines with lots of branches(again, taking input from horror stories of DMX use cases, I want at least that level of performance).

I admit that the overhead of having to poll each device repeatedly creates bandwidth issues and the master is a single point of failure but the applications I had in mind are certainly not of the life-safety sort(especially when there are already excellent protocols designed for that).

with regards to the multiple baud rates and modbus integration, Yeah, probably not reliable because you expose devices to what looks to them like semi-random noise. But for really really non critical stuff like blinking a light that nobody is likely looking at at any given moment I'd imagine it would be fine.

Thanks a bunch for the input! Always nice to have feedback from someone experienced in the field.

Coding Badly:
Nope, that ODS lock file should not be in my repo. Thanks for the heads-up! Which reminds me I need a setup.py file in the repo...

EDIT/UPDATE:
Got rid of the lock file, added a setup.py