I’ve spent the evening trying to get the espresso controller and the WiFi controller talking to each other. I’ve been very thankful to have my Siglent digital oscilloscope that my good friend Taufan bought me for Christmas one year. He also had the good taste to include the serial decoding license (which is very handy as it includes decoding for CAN, TWI, I2C, SPI, RS232 and more). This proved invaluable at showing both how well my code was working and how I always manage to miss something small.
The AVR microcontroller looked to be transmitting what it should and responding the way it should. The ESP8266, on the other hand, was not interpreting it as it should. What you can see in the image below is the microcontroller responding with a status, which is actually a structure indicating the machine status in the RAM of the microcontroller. It contains information like whether the power is on, the state of all the relays, buttons, LEDs and what state each grouphead is in. The ESP8266 seemed to not be understanding it though.
After some debugging, it turned out to be a C structure packing issue due to the way that the 8-bit AVR compiler packs C structures (on the main machine controller) and how the 32-bit Xtensa compiler (didn’t) pack structures. After using a pragma in my ESP8266 code to ensure the structure is packed the way it was on the 8-bit AVR, they were understanding each other perfectly!
I created a pretty rudimentary serial protocol for the two controllers to speak to each other. At some point, I’ll add some more control commands, and create a serial bootloader so the ESP8266 can download a new firmware directly to the espresso machine controller.
At the moment, the espresso machine controller is the TWI slave, and the ESP8266 the master. This is opposite of what I wanted, but it’s because the current ESP8266 API doesn’t support being a TWI slave. I’m sure I could “bit-bang” it considering the ESP8266 runs at 80MHz, but for now this suffices.
Basically, I’ve assigned the TWI slave address 86 (because I had to pick something) to a “status register” that is read-only and anytime a device reads from it, the main controller will respond with its status structure, read directly from active RAM.
I assigned the TWI slave address of 87 to a read/write register where the microcontroller first expects a command to be written, and then will respond to the command and implemented the following commands:
- Power off
- Power on
- Send configuration
- Send maintenance counters
- Send software version
- Send machine information
- Send raw ADC values
- Reset maintenance counters
You’ll notice that at the moment, there are no commands to remotely activate a group, for instance. At some point I may add them, but I’ve been trying to keep it simple for now. The AVR obviously has limited memory. One drawback of trying to preserve RAM in the AVR right now is that when a command that returns more than one byte of information (all of them), the AVR reads them directly from RAM when sending upon the TWI interrupt, which means that theoretically, something could change between transmitting bytes and the message received could be slightly inconsistent. The TWI bus runs at 100KHz, so something would have to change pretty fast, but it’s possible. For instance, if the WiFi interface requested the maintenance counters, there are quite a few 32-bit numbers in there so the structure ends up being 40 bytes long. It’s possible that by the time the controller sends byte 15 (which contains the lowest 8-bits of the grouphead 1 counter) that it’s been incremented and shows 0, when in fact it just rolled over and byte 14 (which had already been sent) has also been incremented. This could lead the WiFi controller to thinking that the counter is 0, when in fact it’s actually 256. An obvious solution to this is to allocate a RAM buffer for everything that gets sent and copy it first so that it is consistent. This would use an additional 40 bytes of RAM, which can probably be spared at the moment, but considering the AVR only has 2k of RAM, I’m trying to be thrifty. Either way, I’ve not really thought of a scenario where this behavior has much in the way of consequences yet. The only case where it may matter are the resetting of the maintenance counters, but those are done atomically in the microcontroller.
Anyhow, it’s a work in progress, many more features to come! Oh, and it actually needs to be installed.