DIY Connected Espresso Machine: Toggle (Part 4)
Following the third part about controlling the Boiler from the microcontroller, this one is to connect another mechanical component of the espresso machine, but as input.
The toggle is on the front panel and has four states:
- off (neutral)
- make steam (left)
- boil (right)
- pour water (far right)
Originally it controls all the components directly as a regular switch, but for “connected” purposes we would like to wire it to the microcontroller as input to pass particular signals based on the toggle state and delegate control of other components to the microcontroller itself.
Theory
So the goal is to pass information about the toggle state to the microcontroller. Contacts between pins are closing depending on the toggle state.
Option 1. We could wire all of them right to the microcontroller and derive the resulting state based on the combinations, but having 6 pins used would be quite costly.
Option 2. The solution I chose is to manage this in hardware-way (despite I have not much hardware skills :) using voltage dividers and a single analog input pin.
We can solder them with resistors of different resistance and left only two wires with resistance depending on the toggle state, something like a rheostat. Having such kind of potentiometer will allow us to use a single analog input to determine the toggle state.
Hardware
The first step is to solder toggle wires with resistors and leave only two wires needed to connect the toggle to the microcontroller. For that, I used one 4.7KΩ and one 10 KΩ resistors.
The second step is to connect the wires to an analog input pin of the microcontroller. There is an article on the Arduino website that clarifies the usage of analog input:
The circuit based on a photoresistor uses a resistor divider to allow the high impedence Analog input to measure the voltage. These inputs do not draw almost any current, therefore by Ohm’s law the voltage measured on the other end of a resistor connected to 5V is always 5V, regardless the resistor’s value. To get a voltage proportional to the photoresistor value, a resistor divider is necessary. This circuit uses a variable resistor, a fixed resistor and the measurement point is in the middle of the resistors.
Another 4.7KΩ resistor is needed here independently on the microcontroller. With Arduino Uno, I used an A2 pin (yellow wire):
With NodeMCU it was A0 pin (yellow wire):
It doesn't matter how the toggle wires (+/–)are connected to the pin, since it’s essentially a resistor.
Firmware
Now to the hardest part in this episode: the challenge is to encapsulate the toggle behavior programmatically in an object-oriented approach.
Similar to the Boiler implementation we need to abstract the toggle component as a Toggle class and provide a set of self-explanatory states with ToggleState enum.
Obviously, it can’t give a programmatic way to change the toggle state without the mechanical capability to do so. However, we need to understand the current toggle state as well as whether the toggle was switched or not so then the needed operation can be executed by the main program.
The straightforward solution is to keep the current state and read analog input in iterations detecting whether the value deviated for a tangible magnitude to consider another state. If the state changes the main program can understand this using a separate getter and react accordingly.
Properties offReading, boilReading, makeSteamReading, and pourWaterReading keep average analog input readings reflecting the actual toggle state.
readingDeviation helps to soften the thresholds which are almost never accurate when reading the analog values.
debounceTimeout is a timeout in milliseconds needed to prevent chatter in reading with the help of time.
All these properties have default values adjusted during testing, however, we lay down getters and setters to be able to control them from the main program in runtime.
The private method inBounds() helps to encapsulate the check for value deviation.
The Toggle() constructor accepts a single value of the pin used and configures it as an analog input pin.
Magic happens in the getState() method which is expected to be run in the main program loop to return results in a timely manner.
- First of all, it checks when the last time the reading happened and returns the last value if it was obtained recently.
- Otherwise, it reads the analog input value to understand what is the actual state of the toggle and remembers the timestamp of the reading. If the value is not in bounds of any known state, we return the last one not to fall into an unknown state.
- Finally, the current state is compared to the last one and if it changed the flag isToggled remembers this fact and the state returned.
Another method getIsToggled() is not purely a getter as it is expected to be used one-time and drops the isToggled flag. So the main program should react right after getting the toggled state as it won’t be detected as changed later. This point is important to consider when consuming the Toggle API, however, it’s just a single drawback of using such an approach.
Test
Everything is wired and prepared to be tested.
The main program checks the initial state of the components implemented and detects the toggle state change if it happens.
Next Steps
As most of the hardware components are wired and have their own API in place the next step will be to collect them in a single entity — EspressoMachine!
The project code is available here: https://github.com/loginov-rocks/Connected-Espresso-Machine — it’s not finished by the moment I write this article so I will work on this during the next episodes.
That’s all for today, stay tuned!