A small one this time. The code has had another review, and it turned up a handful of bugs. None of them are serious, and most only show up if something unusual happens - a typo in a setting, or the internet going down - but they were all worth fixing. There are no new features, and none of your settings are changed by the update.
Profiles
A fermenter's profile can no longer be changed while a profile is running or paused. Choosing a different one used to carry the current step and hour over into the new profile, and choosing Standard temperature control left the profile "running" with its temperature stuck where it was. In the WebUI the Fermentation profile box is greyed out while a profile is running, and the same change from Home Assistant or the API is refused. Stop the profile first, then choose another.
Sensor calibration
The temperature and gravity adjustments for probes, Tilts and iSpindels are now limited to ±10 °C (±18 °F) and ±0.050. They used to take any number, so a typo like 10 instead of 1.0 would have the fermenter controlled to the wrong temperature without any warning.
An iSpindel that sends a report without a gravity no longer shows its gravity adjustment as the gravity reading.
A Tilt's temperature reading can only be set to Beer temperature or Not used, as the WebUI already offered. Any other value sent to the API used to be saved, then quietly changed back at the next restart.
Brewfather and Brewer's Friend
If the internet goes down, the controller now gives up on a service for that 15 minutes after the first report that gets no answer, rather than trying every fermenter in turn. Each try can hold the controller up for as long as 15 seconds, so during an outage the WebUI could stop responding for a while every 15 minutes. Reports carry on as normal once the connection is back.
In Fahrenheit mode, the hysteresis sent with each report was in Celsius. It's now in Fahrenheit, like the temperatures.
Firmware uploads and settings
If a firmware upload fails, the reason is now written to the log, so it shows up in syslog and the WebUI's log viewer, and the error page gives the reason in words rather than a number. An upload that's cut off part way through is now logged too.
Saving syslog settings with a port number out of range is refused as before, but the server and the on/off switch from the same save are no longer changed anyway.
Other changes
The Dashboard item in the WebUI's menu now shows the number of fermenters that are switched on, rather than always showing 4.
An update to the mDNS library that the controller uses for its .local name. It now sends each answer the way the device asking for it expects, either to the whole network or straight back to that device.
The release builds now run the same tests as every other build, including the WebUI tests and the memory error checks, so a release can't be published without passing them.
If you use the API
Two requests that used to be accepted are now refused with an error:
POST /fermenter with a different ProfileNo while that fermenter's profile is running or paused. Sending the profile number it already has is fine, so a script that sends every field won't be affected.
A probe, Tilt or iSpindel tempAdjust outside ±10 °C (±18 °F in Fahrenheit mode), or an sgAdjust outside ±0.050.
The main change in this release is a brand new admin WebUI. The old one had grown a tab at a time since the early days, and it showed - it was cramped, it didn't really work on a phone, and it asked the controller for a lot more than it needed to. So it's been redesigned from scratch.
Everything you could do in the old WebUI is still there, the API is the same apart from one new check on Tilts and iSpindels (see the end of this post), and none of your settings are touched by the update. It's all built into the firmware as before, so there's nothing extra to upload.
A new layout
The admin page now opens on a Dashboard with a card for each fermenter, showing its readings, what it's doing and its temperature control, with the profile and batch details underneath. Everything else is grouped into a handful of sections:
Dashboard - your fermenters and their controls.
Profiles - with a chart of the temperature plan as you build it.
Sensors - temperature probes, Tilts and iSpindels in one place.
Smart plugs
Reporting - Brewfather, Brewer's Friend, MQTT and Home Assistant.
System & diagnostics - controller information, settings, files and backups, the RF sniffer, the BLE console and firmware updates.
Each section has its own address (eg /admin#profiles), so you can bookmark the one you use most, and the browser's back button works as you'd expect. On a phone the sections are in a menu, the forms are one column, and the buttons are big enough to tap. On a bigger screen the cards sit side by side. The other pages - firmware update, WiFi, reset, reboot and the RF and BLE sniffers - have had a tidy up too, so they fit on a phone.
Saving changes
What you type stays put. The readings carry on updating every 10 seconds, but they no longer overwrite a field you're in the middle of changing, and your changes are kept until you Save or Discard them - even if you move to another section and back. The old 30 second time limit on unsaved changes has gone.
Each form shows whether it has unsaved changes, Save only lights up when something has actually changed, and the browser warns you if you try to leave the page with changes that haven't been saved.
If the controller refuses a save, the message stays on the screen along with your changes, so you can fix them and try again.
If the temperature unit is changed while you have unsaved changes, they're held back and you're asked to discard them and enter them again in the new unit, so a temperature can't be saved in the wrong one.
Other new bits
Copy profile - copy a profile into another slot as a starting point for a new one. Nothing is saved until you press Save.
Log viewer - the log drawer from v0.8.2 now has a search box and a Download button for the lines it has collected, and it jumps to the newest lines whenever you open it.
Stale readings - each section shows how long ago it was last updated, and says so if the controller stops answering, so you're not left looking at old numbers without realising.
Configuration backup - a backup is only downloaded if every settings file was read. If any of them couldn't be read, you're told which, instead of getting a backup with bits missing. Remember a backup can contain passwords, so keep it somewhere safe.
Test a plug - the RF sniffer page can now switch a configured plug on and off using its saved settings, to check it's working. This operates the real plug, so be careful if something is plugged into it!
Easier on the controller
The ESP8266 doesn't have a lot of memory to spare, so the new WebUI tries to ask less of it:
The pages are stored compressed, and your browser keeps a copy of the styles and scripts, so after the first visit they don't have to be sent again. The firmware is about 66 KB smaller as a result, even with the extra features.
The old Fermenters tab sent five requests at once every 10 seconds. The new Dashboard sends one, a third of the size, and requests are sent one at a time rather than all together. Each section only asks for what it shows, and nothing is requested while the page is hidden.
In my testing, the lowest free memory while a browser had the WebUI open was about 3 KB higher than with the old one, and the occasional failed request I'd seen with the old one didn't happen. Memory use with no browser open is about the same as before.
I've tested it on my own controller, on a PC and an Android phone, and with Safari's engine on a PC, but not yet on a real iPhone, so if something looks wrong on yours please let me know.
Home Assistant dashboard
The example Home Assistant dashboard (HomeAssistant/dashboard.yaml in the repo) has been reworked to use more of Home Assistant's own tile cards, with fewer custom ones. It still needs Mushroom, Plotly Graph Card and card-mod from HACS. OG and target SG are now shown to 3 decimal places next to the readings.
Also in this release
One Tilt or iSpindel per fermenter - a fermenter only ever uses one of each, and a second one was silently ignored. The controller now refuses to put a second one on a fermenter, and tells you which one is already there. If you already have two on one fermenter they'll carry on as before, and the Sensors page will warn you so you can unassign the spare. A Tilt and an iSpindel on the same fermenter is still fine, and the Tilt takes priority.
The MQTT library (PubSubClient3) has been updated to v3.3.2.
A small one this time, with one new feature in the WebUI and a fix for the admin page on phones.
Live log viewer
There's now a Log bar at the bottom of the admin page. Click it and a drawer opens showing the controller's log as it happens, so you can see what it's doing without setting up a syslog server or an MQTT broker.
It starts with the last few seconds of log lines, and keeps collecting while the admin page is open, even with the drawer closed.
Lines are coloured by how important they are - errors in red, warnings in amber - and you can hide the less important ones, to show only notices and worse, or only warnings and worse.
Pause stops the view moving so you can read it, and Clear empties it.
It's a live view, not a stored log. The controller only keeps a few seconds of lines in memory, so if your browser falls behind, or the page is hidden (eg your phone goes to sleep), some lines will be missed. When that happens the drawer says so, with a line like -- 25 lines missed --, rather than leaving a silent gap. It also marks where the controller has restarted. If you need a complete log, syslog is still the way to go.
Admin page on phones
The Profiles and Probes tabs were too wide for a phone screen, so most of each table was off to the side, and you had to scroll sideways to reach it. On a narrow screen each profile step and each probe is now shown as a small card of its own, with the fields stacked underneath each other. The step move and delete buttons are bigger, so they're easier to tap, and the step type info box now stays on the screen.
On a wider screen - a tablet, a computer, or most phones turned on their side - you'll get the usual tables as before.
The biggest change is that the controller now writes far less to its flash memory. The rest are small bugs that only show up in unusual situations - a bad value typed in somewhere, a flash read that fails, or two things going wrong at once - but they were all worth fixing. There are no new features in this one.
Far fewer writes to the flash
Every 10 minutes the controller saves its settings, so that things like a running profile's progress survive a power cut. Until now, that rewrote every config file and its backup each time, around 2,880 file writes a day, even when nothing had changed. Flash memory only takes so many writes before it wears out, so that was a lot of wear for nothing.
It now only writes the files that have actually changed. Normally that's just one small file, plus the fermenter settings while a profile is running. Any copy found missing or damaged at start-up is saved again, and a file that fails to save is tried again 10 minutes later.
Temperature control and profiles
A profile step that has no Start Temp (Specific Gravity, Attenuation and Temp Reached steps don't show one) now holds its End Temp. Before, if you changed a new step to one of those types, its hidden Start Temp of 0 was kept, and the step ramped up from 0 °C.
In the WebUI, a new step now starts with the same Start and End Temp, and while they're the same, changing End Temp moves Start Temp with it.
A step's length in days is no longer rounded down to whole hours. 0.1 days is 2 hours 24 minutes, but it used to be cut short to 2 hours. Ramps now move the target every 10 minutes, rather than every hour.
An MQTT command with a value like nan or 21abc is now refused. nan used to get through, and a fermenter given it would never stop heating or cooling.
If a firmware upload fails, a fridge that was cooling now waits out its compressor delay before it starts again.
Settings
If the filesystem can't be read at start-up, it's tried again before it's formatted. Before, a single bad read would wipe all of your settings.
If a setting can't be saved, the WebUI now tells you, rather than saying it was saved. It's tried again at the next 10-minute save.
When a save is refused, eg a value is out of range, the error now stays on screen along with what you typed. Before, the page reloaded half a second later and the message disappeared.
A factory reset now clears the Tilt settings too.
Tilts you've set up are no longer pushed out by ones that have only been heard nearby, and setting up a fifth Tilt is refused rather than one being dropped silently.
Smart plug RF settings that would take so long to send that the controller restarts are now refused.
Installing over an original MyBrewbot
If the converted settings can't be saved, the controller restarts and tries again. After three failed tries it starts on default settings rather than on a mix of converted and unconverted files.
An original settings file that can't be read is now saved on its default settings, and orig/conversion.txt says so. Before, it could be left in the original's format.
Firmware uploads
An upload is now checked against the firmware's own length and checksum before it's installed, so a file that's been cut short or damaged is refused, and the controller carries on with the firmware it has.
MQTT and Home Assistant
Changing the MQTT broker, port, login or base topic now takes effect straight away, so the Test button checks the new settings. Before, the old connection carried on until it dropped.
Turning HA Discovery on now adds the controller to Home Assistant straight away, instead of at the next reconnect.
If you have more than one controller on the same broker, give each one its own Base Topic. They all start as ourbrewbot, and two with the same base topic publish over each other's readings. The README now covers this.
Security
The controller's API has no login, so it should only be on a network you trust. But a web page on another website could also send commands to it through your browser. Those requests are now refused, firmware uploads included. The WebUI, curl, scripts, Home Assistant and iSpindels aren't affected.
Builds
The exact versions of the ESP8266 core, the compiler and every library are now fixed for each build, so a new release of one of them can't change the firmware without being tested first.
I've done some more analysis work on the original MyBrewbot firmware, module by module, comparing what it did with what OurBrewbot currently does. The biggest result is that an original MyBrewbot's settings are now properly carried across when you install OurBrewbot over it. There are also a few smaller fixes that came out of the comparison.
The original firmware keeps its settings in the same files as OurBrewbot, and they survive installing OurBrewbot as long as the flash isn't erased. Until now OurBrewbot read them as they were, but several of them mean something different in OurBrewbot, due to some assumptions I made when starting out. Eg, the fermenter power switch was the other way round, plug roles come from the slot they're in, Tilt colours are numbered differently, and so on - so the result wasn't quite right.
On its first start, OurBrewbot now converts them as follows:
Fermenters - the one(s) you were using are switched on. Your floor and ceiling are widened by the hysteresis, so heating and cooling start at the same temperatures as before. Settings saved in Fahrenheit are converted.
Smart plugs - each plug gets the job its original slot had, and "Dial" plugs get the codes the original sent for them.
Probes, Tilts and iSpindels - keep their fermenter and role. The probe addresses the original saved are now recognised, so your probes aren't found as new, unassigned ones.
Profiles - steps are converted to OurBrewbot's step types.
Brew service - Brewfather or Brewer's Friend keeps its ID. Press its Test button to check it still works.
WiFi - your network name and password are moved into the controller's own WiFi store, so it joins your WiFi straight away. The original's WiFi settings file, which held your password in plain text, is deleted.
The untouched original files are kept in the orig folder, and orig/conversion.txt lists every change that was made. You can read both in the file browser on the System Settings tab, which now shows files in folders too. Settings for Probe names, Plaato, MyBrewBuddy and monitor.beer aren't carried across.
Double reset clears the WiFi
As on the original, pressing the reset button twice, the second press within 10 seconds of the first, clears the saved WiFi and opens the OurBrewbot-XXXXXX setup hotspot.
Setup hotspot
With no WiFi saved - a new controller, or one you've just cleared - the hotspot now stays open until WiFi is set up, and the fermenters are controlled in the background. Before, the controller waited in the hotspot controlling nothing and restarted every 3 minutes.
If WiFi is lost for 5 minutes after the controller has connected, it now opens the hotspot too, taking turns with trying to reconnect, so you can enter new details after changing your router or WiFi password. Before, that only happened if the WiFi was down when it started.
Both are an improvement over the original firmware, which couldn't control the fermenters without WiFi for more than about 20 minutes, and not at all while its setup hotspot was open.
Smart plugs
Every plug is switched off when a firmware upload starts, and held off until the controller restarts on the new firmware. If the upload fails, control carries on as before.
Plugs are also switched off before a reboot or a config reset that you ask for, from the WebUI, the API or MQTT.
Other smaller changes...
A probe reading of exactly 85.0 °C is treated as a failed read, as the original did.
If the Bluetooth module doesn't answer at start-up, other baud rates are tried, and the module is reset back to the configured one.
When a fermenter has no beer temperature and is controlled on its ambient probe, the temperature source now says "Ambient" instead of "None", and the WebUI shows "controlling on Ambient".
The original's SG Control setting has been removed from the logic. It only ever chose where the gravity came from, which OurBrewbot does automatically (a Tilt, then an iSpindel, then an estimate).
GET /mqtt no longer shows your MQTT password, and the WebUI's password field can be left empty to keep it. It's still in the config files, so keep exported config files private.
GET /controller no longer returns the original's Blynk auth code.
A small release, prompted by a crash report from a controller that had started up while its WiFi was down. Thanks to everyone who has crash reporting switched on - this one would have been hard to find without it!
Setup hotspot crash
Since 0.6.4, a controller that can't reach its WiFi when it starts opens its OurBrewbot-XXXXXX setup hotspot, so new WiFi details can be entered. The controller crashed and restarted when that hotspot closed:
about 8 to 10 minutes after starting, and every 10 minutes after that for as long as the WiFi stayed down;
straight after new WiFi details were saved in the hotspot.
Each restart also starts the compressor delay again, so a long WiFi outage could hold off cooling for much of the time. This is now fixed.
A quicker setup hotspot
While the WiFi is down, the hotspot now opens after 1 minute instead of 5. It then stays up for 2 minutes at a time, taking turns with 1 minute of trying to reconnect, so the controller gets back onto your network sooner once it's working again. While a phone is connected to the hotspot it stays open for up to 5 minutes, so you aren't cut off while typing in the new WiFi details.
Crash reports wait for WiFi
Until now, a crash report was only held in memory. If the WiFi was down, or the controller restarted before the report was sent, it was lost - which is exactly what happens when a controller keeps crashing while it's offline.
Crash reports are now saved on the controller and sent once WiFi has been connected for about 2 minutes, oldest first. Up to 5 are kept; a newer one replaces the oldest. Switching Crash Reports off, or a factory reset, deletes any reports still waiting.
I've done an audit of the code and turned up a few bugs, mostly ones you'd only hit after weeks of uptime or in an unlucky moment, plus a fair bit of old code that wasn't doing anything.
One change to be aware of:/reboot and /reset now only work with a POST. If you have a script or bookmark that uses them, see below.
Temperature control
A smart plug with only its ON code learnt could be switched on and then never switched off again. A plug is now only switched on if it has an OFF code as well.
After a reboot, the last saved probe temperatures are no longer treated as live readings. Before, a probe that was missing at startup kept its old reading for about 30 seconds, which could switch the heater on for a beer that wasn't being measured. The probes are now read once while the controller starts up, so control starts from real readings.
Controllers left running for weeks
The processor's millisecond counter goes back to zero every 49.7 days, and a few things were using it to measure long periods of time. After 49.7 days of uptime:
the reported uptime went back to 0;
the compressor delay could wrongly hold off cooling;
an iSpindel that had stopped reporting could look fresh again, and its old reading was used for control;
the alarm delay, the "last update check" time and the log timestamps went wrong.
These now all use a clock that doesn't wrap.
Config import
Your current settings are now backed up before an import starts. If any file in the import is refused, the previous settings are put back, so the controller can't end up running on a mix of old and new settings.
The controller restarts as soon as the import is done. Before, the "Import complete" message waited for you to click OK before restarting, and if it was left open the controller's regular save could write the old settings back over the imported ones.
/reboot and /reset now need a POST
A plain GET to /reboot or /reset?target=config, which is what a browser does when it opens a link, used to restart the controller or wipe all its settings. Link previews and browser prefetching can do that without anyone clicking anything.
Opening either address in a browser now just shows a page asking you to confirm. The WebUI buttons work as before. If you use these from a script, send a POST instead, for example curl -X POST http://<your controller>/reboot.
Everything else
If the syslog server can't be found when the controller starts (e.g. the router is still starting up after a power cut), it now tries again every 10 minutes instead of leaving syslog off until the next reboot.
Sending badly formed JSON to /fermenter or /controller now gets the same "Invalid JSON" reply as every other endpoint.
A tidy-up of old code that wasn't being used, which frees about half a kilobyte of memory.
After 0.6.3 I went back through the control logic and found a fair few things that weren't quite right. None of them are new, but some could affect a brew, so I'd recommend updating.
Temperature control
The controller now controls your fermenters even if WiFi isn't available when it starts up. Before, it did nothing until WiFi connected.
The compressor delay is always honoured, including when cooling stops because of a lost reading, the fermenter being turned off or temperature control being turned off.
A profile step with a temperature band narrower than the hysteresis no longer makes the heater and fridge fight each other.
Up to an hour of profile time is no longer lost every time the controller reboots.
The estimated gravity no longer resets to the OG at every profile step change.
"Next" on the last step of a finished profile no longer reports an error.
iSpindels
A missing or rejected iSpindel temperature is no longer used for control as 0 °C.
Once an iSpindel stops reporting, its last reading stops being used for control, rather than being used forever.
A newly seen iSpindel is no longer assigned to Fermenter 1 automatically.
Smart plugs
A plug no longer stays on forever if it's reassigned, unassigned or re-coded while it's switched on.
Commands are resent every 60 seconds instead of every 10, so there's a lot less radio chatter.
MQTT and Home Assistant
The All Off command is no longer undone by the controller.
Switching profile no longer restarts a running or paused profile from step 0.
Everything else
A fermenter's alarm now clears when it's turned off or loses its temperature reading.
An unplugged probe no longer repeats its warnings (and a slow retry) every 21 minutes.
The Bluetooth module is restarted if it stops answering.
Out-of-range values in a config file are checked before they're used.
Thanks to users who have crash reporting enabled! A few reports have pointed me straight at a bug I've not seen on my own controller.
Bluetooth crash fix. If the stored baud rate for the Bluetooth (HM-10) module was 0 or one it doesn't support, the controller could crash while starting the module. It now falls back to 9600.
RF receiver - the 433 MHz receiver used to learn smart plug codes was left listening all the time, interrupting the processor for every bit of radio noise. It's now only switched on while the RF sniffer page is open.
Better crash reports. They now include the uptime, free memory, WiFi signal strength and the first fault in a crash, as the first one is usually the cause and the rest are just knock-on effects.
Home Assistant - the example fermenter graphs are updated for plotly-graph-card v4.
A quick follow-up to 0.6.1, tidying up how the controller stores text.
Names with accents or emoji are stored properly now. A long name used to be cut off half way through a character, leaving a garbled bit at the end. Names that older firmware (or an imported config file) already mangled are repaired automatically when the controller starts.
MQTT base topic - the controller now refuses a base topic your broker can't use (wildcards like + or #, a leading $, or one that's too long) when you save it. If you already have one saved, it's repaired at startup, and working topics are left alone. All the MQTT settings are checked before any of them are applied, so a bad value can't leave you with half a config.
The same topic rules now apply to iSpindel IDs stored in the config.
A small one. If you left the WebUI open on your phone, switched to another app and came back later, the page could be left blank until you reloaded it. The phone puts the page to sleep in the background, and any request it was waiting on just never came back.
Each tab now gives up on a request after 8 seconds, rather than waiting forever.
The auto refresh stops while the page is hidden, and the tab you're on reloads as soon as you come back to it.
A tab shows "Loading..." until its data arrives, and tells you if a load failed rather than just sitting there empty.
A slow reply can no longer overwrite newer data, or a form you're half way through editing.
This release is mostly about stability. Since May my own controller has been rebooting on its own every week or two, always with a crash somewhere in the network code. It took a few weeks of debug builds to pin down, but I've found at least one of the causes.
Network memory bug. The ESP8266's network library could free the same piece of memory twice when a lot of recently closed web connections built up, which slowly corrupted memory until the controller crashed. OurBrewbot now works around it. I've also reported it upstream with a fix (esp8266/Arduino#9327), so hopefully it gets fixed properly.
More breathing room. The controller's working memory (the stack) was getting dangerously close to running out while it handled iSpindel reports. I've freed up well over 1 KB of it across the web, logging and MQTT code.
Long profiles are no longer cut off at 1 KB when you view them in the WebUI or the API.
Names with quotes, < or & now show properly in the WebUI, instead of breaking the page.
iSpindel IDs are made safe for MQTT and Home Assistant, and a long ID no longer cuts off the Home Assistant unique ID.
Crash reports now include how low the stack got and where, which makes them a lot more useful to me when something does go wrong.
If you've had the odd unexplained reboot, this one's worth installing.
This is the first release since 0.4.10, so it rolls up everything from 0.4.11 to 0.5.0. It also goes out alongside this new website.
New
Update check. Once a day the controller checks ourbrewbot.com for a newer release, and tells you on the admin page, in syslog and as a Home Assistant update entity. It never downloads or installs anything by itself.
Crash reports. If the controller crashes or is reset by its watchdog, it sends a report to ourbrewbot.com a couple of minutes after it restarts, so I can see what went wrong. Nothing is sent after a normal restart.
The ESP8266's mDNS library (the part that makes ourbrewbot-XXXXXX.local work) was behind a lot of the watchdog resets I was seeing. I've replaced it with a much smaller one of my own that does its work in the main loop, which also frees up about 16 KB of flash. It's published as a separate library, MicroMDNS, if it's any use to anyone else.
Everything else
Syslog lines logged while the controller was starting up could go missing, including the one that says why it last crashed. They all arrive now.
An extra memory corruption check is switched on, to help track down the occasional random crash.