The easiest way to get the app working without the electronics connected is to simulate the electronics in software. I’ve made a start by implementing the power and boiler interfaces as a simulation. The code for the simulation so far can be found here.
The code runs a timer at a configurable interval that will update the simulation model. It has a few constants like the boiler content, the power of the heating element and how much energy it costs to heat a kg of water by one degree. It also contains a heat loss estimation, that is very crude and linear but will do for simulating heating and cooling.
We initialize the simulation by choosing a random value for room temperature and atmospheric pressure. We define an observable that calculates pressure directly from temperature using the atmospheric pressure (more on that later).
Then we start the timer and run the main simulation loop. The main simulation loop calculates whether the heating element should be turned on or off. When this is determined and the heating is on it will calculate the temperature increase in this tick based on the content of the boiler. Determined is whether the protection is triggered and the heating should be turned off.
Finally, the heat loss to the environment by the machine is determined and subtracted from the new temperature.
Temperature pressure relation
In a closed system like the boiler and with the air out, during heating steam will form and pressure starts building inside the boiler. The water is saturated at this point (as it cannot absorb any more thermal energy without boiling) and so is the steam (it cannot lose any more thermal energy without condensing). In such a system with saturated steam, there is a direct relationship between temperature and pressure. The espresso machine exploits this relationship by controlling pressure instead of temperature. For us to simulate this behaviour we must have an accurate model of the relation between pressure and temperature. Luckily I found one with a very small error between -25 °C and 220 °C. Converting the formula to C# gives the following converter:
This converter converts temperature accurately enough to pressure to be within a 0.005% margin. Using the converter I noticed that the pressure was consistently 1bar too high when converting known temperatures to pressure. That is because any manometer measures relatively to the atmospheric pressure so that I had to subtract roughly 1013mbar from the pressure to obtain pressure relative to the atmosphere (barG). This also means that at high altitude water boils at a lower temperature, so with a classic espresso machine you need to increase the boiler pressure to obtain the same temperature at high elevations.
It also means that if I calibrate the temperature and pressure sensors accurately, the espresso machine will make for a nice barometer to predict the weather. All in all, measured pressure seems dependent on atmospheric pressure, which is why it might be better to control boiler temperature directly through the temperature sensor and leave pressure completely alone. I’ll decide on this later, based on the accuracy and responsiveness of the actual sensors.
Implementing a boiler control card in the app, based on the simulation, leads to a nice video that shows heating and controlling of the boiler through pressure. You can watch the video below: