My goal for FabAcademy was to get basic responsiveness over TWI and dispensing functionality, I wound up making 2 versions of the software, one to run on the ATMega328P μController to achieve functionality and demonstrate that the concept was workable and a second on the ATTiny44 μController in an attempt to reduce the final cost of the project, ultimately I did not get the second version fully functional which I will document below.
The firmware itself operates as a state machine with 3 basic states at the time of this writing. States of 0x09 and below are read states wherein the module is expecting to be returning a value to the master at the next read whereas states of 0x10 and above are write states in which the module is expecting a value to be sent in a subsequent transaction.
- State 0x00
- The default state, the module will return a single 8 bit integer representing the condition of the module. 0x00 for all OK, 0x01 for currently dispensing (busy) and 0x02 and larger for error conditions.
- State 0x01
- Report dispensed product, the next 4 reads will return a 32 bit integer representing the product ID that the module is configured to dispense. Currently this is hardcoded at compile time however ultimately it should be stored in EEPROM.
- State 0x02
- Report stock level, currently unimplemented this functions in an identical manner to the report dispensed product state but instead reports the stock level of the module.
- State 0x10
- The dispense state, the next received byte will be interpreted as a quantity to dispense and the module will trigger the dispense function with the quantity as the first argument.
Version 1: ATMega328P
The ATMega328P version of the AMPD module firmware implements all the basic functions that I will describe in the version 2 summary below, it however uses the Wire and Servo libraries from the Arduino environment that do not function on the ATTiny.
Hardware-wise I have used both a Fabduino and a series of Freetronics Arduino Uno compatibles to test the software and confirm its functionality (depending on what I had remembered to put in my box of bits and pieces at the time).
Version 2: ATTiny44
I designed a small with 2 LEDs and a 2x3 pin header laid out to allow direct connection of 2 standard servo connectors. My intention for this module however is to connect one servo and to use the other connection for the optical reflectance sensor I designed in Input Devices, this sensor will detect the passing battery as it falls out of the dispensing rotor, triggering an interrupt on the microcontroller which in turn allows the dispensing loop to continue.
The LEDs are intended to operate as status flag lights for debugging purposes.
The blocking issue for the ATTiny44 is a problem with generating servo pulses, the code is relatively simple using a timer with the millis function to trigger a pulse every 20ms and the delayMicroseconds function to generate the pulse of between 500 and 2500 μs. It does this beautifully for about 33 seconds.
And then this happens, I suspect the issue is an integer overflow somewhere but I have decided it would be best to rework the code to do away with the Arduino functions in the long run anyway, this can be a problem for future Daniel.
The 'server' component of the AMPD software stack is a Python based daemon running on a Raspberry Pi, I am using the Flask microframework to implement the RESTful interface. The base URI for the AMPD system is http://hostaddress/ampd/
The probe function is a rudimentary autodiscovery function, it currently attempts a TWI write transaction with a predefined range of valid addresses sending a '0x00'. If a successful transmission is made the daemon will then attempt a read from the same address, if a working AMPD module is at that address is should send back it's status code, if all is well this should also be '0x00'.
We now know that a functioning AMPD module is at the address we're probing, the daemon will now try and find out what the module is dispensing. The product code is currently hardcoded in firmware at upload as a 32 bit integer and can be read from the module by sending the appropriate state code (0x01). The software is currently reading each byte in a separate transaction due to some errors I was encountering with spurious data appearing when attempting block reads.
Finally we know both the address and product code of the module we're talking to and can add its details to the data structure I'm using to keep track of all the attached modules.
Does what it says on the box, dumps a JSON encoded copy of the data structure populated by the /sys/probe method, this way we can see what modules have been detected by the autodiscovery function.
This method will trigger the dispense function on a given module and (hopefully) dispense the specified number of products, the module TWI address is retrieved from the data structure populated by the probe method based on the specified product ID. Under the hood this basically sends 2 TWI send transactions, the first is 0x10 to set the module state, the second is an 8 bit integer representing the quantity to dispense.