12/02/2020 at 06:50 •
I had my phone handy and was putting it back where it goes, so I stopped to get a better selection of photos. You'll find them after the break!---------- more ----------
I hope you liked seeing a little more of this silly-simple thing I built. :)
11/29/2020 at 07:41 •
As of commit 8b0f3dee, both OTA updates and core dumps are working!
Core dumps were surprisingly straightforward. I just had to turn them on in menuconfig and add an HTTP route through which I could dump the core dump partition of the flash device. After that there's pretty good documentation and discussion online of the local tools for processing the dump once it's downloaded;
OTA updates were another story. Lines like this one are there due to about a half hour of adding logging, turning up existing logging too far, reading through the SDK's HTTP initialization code, and finally realizing this was a simple developer-facing parameter. My favorite was when switching the firmware upload receive buffer from the stack to the heap caused a ~10x slowdown. It turned out I was using sizeof() instead of the macro I had defined with the size in it, so the heap version was using only the first four bytes of a two-kilobyte buffer.
I checked in several of the commands I find handy in a .ps1 file. (Visual Studio Code and the ESP IDF plugin make PowerShell the most convenient, so that's what I'm using.)
11/26/2020 at 07:31 •
Adam Savage's Every Tool's a Hammer and my Geiger counter project have mostly sold me on lists as brainstorming and work-tracking tools. The next version of this project will differ pretty much only in software; here's a list of bite-sized features I want to add:
- Save core dumps to flash
- Retrieve core dumps via HTTP
- OTA updates
- Thermals tracking
- In-RAM debug logging
- Retrieve logging via HTTP
- Filesystem for HTML/CSS/JS content
- Faster local-loop UI workflow
- Mobile-friendly UI
- Prettier UI
- Current state indicator on UI
- HTTP responsiveness watchdog
- Settings in NVRAM of some kind
- Differentiate first boot after flash vs. power cycle and reboot using NVRAM to enable safe settings storage, OTA, and core dump operations
- Diagnose and fix random loss of network contactibility
11/09/2020 at 07:48 •
Over time I have noticed a few pain points: the HTML interface is only marginally usable with not-so-useful-to-me defaults, the network connection sometimes freezes and leads to a system reboot, and I have to take the device apart to re-flash it. I really do want this to be a fire-and-forget device for weeks on end, and it needs some work to get there.
As part of this I expect to learn how to save ESP32 core dumps to flash and retrieve them over the network, how to do OTA updates, how to read debug logging over the network, and how to use CSS to make the UI easier to use on mobile.
The UI would be easier to test if I were to use the filesystem to store and serve the files instead of having to transmute them into C strings before building. I could even have multiple files at that point!
07/20/2020 at 07:16 •
Most makers working with the ESP32 can easily accomplish their goals by using the ESP-IDF integration with the Arduino IDE. With my background in hardware and low-level software, I wanted to learn a bit more about the ESP32 and went with the ESP IDF (coupled with Visual Studio Code. (The blog post linked from the Project page discusses some of the work I had to do to get it working in the somewhat exotic environment I work in on a daily basis.)
This meant I was exposed to FreeRTOS resource management and multitasking, and I had some fun.
The IDF sample project starts with a 'main' function that runs in its own FreeRTOS task (thread). As peripherals are initialized and started, relevant tasks, events, and ISRs are set up. The programmer is then left to build on top of this.
Overall, the code is split into:
- Color Space APIs
- LED patterns and colors
- Configuration management
- HTTP handlers
- Alarm+Sleep state machine
I ended up with two event loops of my own: a vestigial one in the 'main' task that keeps the WiFi up and an alarm loop that tracks time and listens for configuration events.
The color APIs all rely on only function arguments and locals, making them state-free and so lock-free.
The alarm event loop is not direct-call or callback-based. Instead, it uses events and event wait timeouts to receive things like HTTP events (snooze, stop) and time changes.
Time is tracked by the onboard RTC and periodically updated via NTP. As an alarm clock on mains power, no attempt was made at power management.
Nothing makes code easier to maintain than good comments (except maybe good docs in addition to good comments :). Knowing I would probably take quite some time away from this project and seeing that I was learning a lot as I went, I put as much documentation as I could in the code.
07/20/2020 at 06:51 •
Wiring up the project got a little weird because of some aesthetic choices I made.
The LED strips came with two plain wires for ground and +5V and a 3-wire cable for ground, +5V, and data. Instead of wiring the power in a classic star topology (ESP32 and LED strips all connected to the barrel jack), I connected the two plain wires to the barrel jack and connected the ESP32 to power through one of the LED strips. It came out something like this:
07/20/2020 at 06:41 •
Over the next several weeks, I spent my spare time setting up the build environment, brainstorming desired features, and writing code. There were a number of little problems, described primarily in the Git commit history, code, and in a blog post linked from the Project page. Probably the most interesting issue was that LEDs past about number 30 would sometimes get random colors. It took a lot of forum crawling, some trial-and-error, and a little oscilloscope work to establish that one ESP32 CPU core isn't powerful enough to handle both WiFi and IR transceiver (RMT) interrupts on the same CPU if the RMT load is high enough--like when doing tight-loop animations or writing to long strands. The fix was to move the RMT ISR to the second CPU.
A second interesting problem was programming colors. The ESP32 is capable of using floating-point representations for colors at all phases (barring its terrible floating point performance), but eventually they needed to be converted to the 24-bit RGB values the LEDs would accept. I ended up with a set of layered abstractions and conversion functions moving from the most abstract color systems, like CIE x,y values and color temperatures, down past HSV to linear RGB through to gamma-corrected RGB PWM values to be sent straight to the hardware. This felt like the part where I learned the most.
The remainder of the physical build was actually straightforward: drill some holes in plastic and aluminum, bolt things together, and run some wires. It sounds simple, but I ended up only getting three of the four board-mounting holes to line up well enough to use. (I also learned that a common battery-powered drill with wood bits is, with some effort, quite capable of working 1/16" aluminum.
07/20/2020 at 06:23 •
A long time ago (2018), I had a hare-brained idea to make getting up in the morning easier: add light to the room on a timer. This led to a part-buying binge with quite a bit of back-and-forth on which 5V power supply to use and which LED strips would minimize the work I had to do, with a mix of SparkFun and AdaFruit parts sneaking off with my wallet. A local hardware store, Ace Hardware, provided a meter of angle aluminum.
This sat on my shelf for two years, right next to the ESP32 modules a family member got me for Christmas.
Well, technically I immediately used the adhesive backing on the LED strips to paste them to the angle aluminum, and that subassembly sat on my shelf forever. As is often the case with my projects, enclosure design stymied me--until I discovered Hammond Manufacturing project boxes at Vetco Electronics Store.
Around the start of the COVID-19 response in my country, I got the bug to make something and, with all the parts sitting around, this project was the clear winner.