The place where random ideas get written down and lost in time.

2025-09-28 - ESP-RS: Rust on the ESP32-CAM (again)

Category Esp32

I looked at using Rust a few years ago on ESP32, but quite frankly the platform did not seem mature enough. It was really bleeding edge. Like razor sharp. I can barely stand the “better-than-thou” hype around Rust, so that was too much. Then I checked again last year when I did the SDB project and it still wasn’t looking stable enough for my needs.

This time it looks like I could at least do what I want: use Rust with ESP-RS on the ESP32-CAM and actually capture image frames. There’s now some good ESP Camera component circulating around with some tangible support:

To use this in an ESP-RS project, we need 3 changes:

First, we need a git checkout of the https://github.com/espressif/esp32-camera project.
In a serious project, that would be a git submodule, but for a throw-away test that can be just good ol’ boring
git clone:

$ mkdir components
$ pushd components
$ git clone
https://github.com/espressif/esp32-camera.git

In the main Cargo.toml, we need the esp-idf-sys crate and we need to use (import?) that esp32-camera component:

$ cargo add esp-idf-sys
$ vim Cargo.toml
[[package.metadata.esp-idf-sys.extra_components]]
component_dirs = "components/esp32-camera"
bindings_header = "components/bindings.h"
bindings_module = "camera"

Now the first I compiled this, it failed at runtime in the esp_camera::init:

I (657) cam_hal: Allocating 384000 Byte frame buffer in PSRAM
E (657) cam_hal: cam_dma_config(509): frame buffer malloc failed

That sounds like “esp_psram” is not enabled on the project. That’s done in the sdkconfig.defaults file:

CONFIG_ESP32_SPIRAM_SUPPORT=y

My own “webcam sample” for the ESP32-CAM is located here:

https://github.com/ralfoide/arduino/tree/main/ESP32-CAM/Rust/esp-rs-std-webcam


2025-04-12 - GA4 Stats from an ESP32?

Category Esp32

At Randall, I’ll soon have the Distant Signal panel installed. This connects to the local wifi and gets the turnout state from the local MQTT broker. I want to track the “health” of that wifi connection, as that has been an issue in the past. The simplest way is to reuse my existing Google Analytics dashboards, thus I want the CircuitPython script on the ESP32 to send pings to GA4.

The goal is to measure the “uptime” of the display. The display is working when it is able to receive messages from the MQTT server. It can receive them when it can connect to the wifi. Thus we want to track the wifi state, or some kind of proxy for it.

Since this is CircuitPython, we have AdaFruit libraries to already deal with JSON and network.

So really “all” I need is something similar to the Analytics class in Conductor 2:

  • Queue stats to be sent.
  • Stats are sent as JSON payloads.
  • POSTs can fail, in which case they need to be retried with a backoff delay.
    • ⇒ Obviously a “wifi lost” event would have no wifi to send the stat.
  • Do I need to put a real date/timestamp in the events?
    • ⇒ These ESP32 don’t have a “date” clock, so it’s nice if I don’t have to.
    • There’s an NTP library which I’ve used on the LitterTimer experiment but it makes the logic more complicated since we need to expect that the device may not have wifi.

So what do we need to do custom pings to Google Analytics 4?

  • A “GA4_CLIENT_ID”, typically configured in settings.toml
  • A “GA4_MEASUREMENT_ID”, also configured in settings.toml
  • A “GA4_API_SECRET”, also configured in settings.toml
    • The feature is disabled if either of these settings is missing.
    • Placing them in setting.toml avoids leaking these in any GIT source, and it makes it easier to configure a new panel.
    • See below on where to find these.
  • A base URL:
    • https://www.google-analytics.com/debug/mp/collect    -- debugging version
    • https://www.google-analytics.com/mp/collect          -- real pings version
  • The POST URL is:
    • %(base)s?api_secret=%(GA4_API_SECRET)s&measurement_id=%(GA4_MEASUREMENT_ID)s
  • POST mime type: text/plain
  • Payload is JSON, and is used as such in Conductor:

{
        'client_id': GA4_CLIENT_ID,
        'events': [ {
                'name': event_action ,
                'params': {
                        'items': [ ] ,
                        'value' : value_int ,
                        'currency' : USD
                }
        } ]
}

  • The AdaFruit “requests” library is the simplest way to send a POST request. It accepts either a JSON (using a Python dictionary) or a string payload.
  • When POSTing to the debug URL, print the response.status_code and the response.content.decode() to get valuable information on why requests fail.
  • When POSTing to the real ping version, the GA server replies with 204 (Success w/ No Content) and a complete lack of response body.

Click here to continue reading...


2025-04-10 - CircuitPython on ESP32: “pystack exhausted”

Category Esp32

Well, that escalated quickly.

I’ve been battling with this in Distant Signal:

@@ MQTT: Failed with  pystack exhausted
Traceback (most recent call last):
 File "code.py", line 498, in <module>
 File "code.py", line 354, in _mqtt_loop
 File "adafruit_minimqtt/adafruit_minimqtt.py", line 956, in loop
 File "adafruit_minimqtt/adafruit_minimqtt.py", line 1027, in _wait_for_msg
 File "adafruit_minimqtt/adafruit_minimqtt.py", line 381, in _handle_on_message
 File "code.py", line 328, in _mqtt_on_message
 File "script_loader.py", line 27, in newScript
 File "script_parser.py", line 316, in parseJson
 File "script_parser.py", line 289, in _parseGroup
 File "script_parser.py", line 214, in _parseInstructions
 File "script_parser.py", line 278, in _parseInstructions
 File "adafruit_display_text/label.py", line 88, in __init__
 File "adafruit_display_text/__init__.py", line 273, in __init__
 File "adafruit_display_text/__init__.py", line 294, in _get_ascent_descent
RuntimeError: pystack exhausted

It turns out that CircuitPython is built with a max stack depth of 15-16 calls (*), at least for this version of the MatrixPortal S3.

It’s entirely possible to rebuild it with a different stack depth, if one is inclined to do so.

The stack size is controlled by CIRCUITPY_PYSTACK_SIZE which is defined globally here:
https://github.com/adafruit/circuitpython/blob/HEAD/py/circuitpy_mpconfig.h#L455
and is also architecture dependent -- e.g. the SAM arch overrides the default.

Documentation on CIRCUITPY_PYSTACK_SIZE is here:
https://github.com/adafruit/circuitpython/blob/HEAD/docs/environment.rst#circuitpy_pystack_size 

(*) As can be seen above, the “stack size” is not really a number of calls but really a size in bytes. Thus I expect calls with more arguments will exhaust the stack faster, or whatever else needs to be pushed on the stack for each call.

At runtime, the “ustack” module can give the current max and used size:
https://docs.circuitpython.org/en/latest/shared-bindings/ustack/index.html 

Well, not on the MatrixPortal S3 port at least.

So how does one solve that? Since I do not want to rebuild CircuitPython, it means I can just amend the code to reduce stack usage.

For example the main starts with this:

if __name__ == "__main__":
   setup()
   loop()

This conveniently mimics an Arduino sketch setup. But we don’t need that “loop()” function. Just place everything under the “if main” and save one call stack entry.

The MQTT error above happened when a message was received -- I’d parse the script right there in the MQTT handler. Instead, what I do is save the script in a temporary global variable, and process it from the main loop, after the MQTT handler has returned. That way we’re “saving” about 5 calls depth on the stack.

The parser is about 5 calls deep. I could probably save one call in there, if I really have to.

Another easy strategy is to create lambdas, and queue them for processing in the main loop.

That’s more or less what I do with the delayed processing of the script, except since there’s only one thing to do, I keep the string reference around rather than wrap the processing in a lambda. Delayed processing can quickly become hard to follow, it’s harder to understand and debug.


2025-04-07 - The HUB75 Protocol for LED Matrix Displays

Category Esp32

Over there in the trains/electronics blog, I have a little write up about the HUB75 Protocol for LED Matrix Displays, as used in the Distant Signal project. Go read it there.


2025-02-09 - Wifi APIs on Android 13

Category Android

I’m still in the process of updating RTAC to work properly on Android 13.

RTAC uses a number of APIs which have been deprecated since Android 10. Since it was running on 2017 hardware with Android 9, that wasn’t much of a problem. Now I want to move to newer hardware that runs Android 13 so I need to deal with it.

WifiLock:

  • The WifiManager.WifiLock API is still present and usable.
  • I’ve added a handler class for it in TCM.
  • However it’s not absolutely clear what it really provides.

WifiManager#enableNetwork is reserved to “Device Owners” and system apps.

  • It seems that ConnectivityManager#requestNetwork could be an adequate replacement?
  • A first try of the ConnectivityManager#requestNetwork API wasn’t conclusive.
  • The app suddenly displayed a model dialog “connecting to device” with a spinner, which has the undesirable side-effect of pausing the main TCM activity.
  • Configuring the request for a known SSID did… nothing. It stayed on that spinner and did not switch to that wifi network.
  • Configuring the request for a known SSID and its WPA password did something weird where the wifi list showed a second entry of the SSID “for this particular app”.
  • So far that didn’t work as expected. My goal is to switch the wifi to an already known SSID that has already been configured in the Android wifi setting and already set up with password et al.

To be continued.


2025-02-05 - Here we go again…

Category DEV

Somehow the same wishes always circle back:

“Aww, wouldn't it be cool if <insert random unfinished project name> had a built-in editor and I could code the app on my {phone, PDA} whilst I'm using it?”

A few decades ago that's how the Hint project started, which quickly got nowhere -- mostly because I was trying to get everywhere at the same time.

And of course later I realized the goal is its own anti-goal -- it's already hard to finish one project, so adding another project inside that project pretty much guarantees that neither gets anywhere, fast.


2025-01-29 - Moving Again…

Category DEV

Another month, another problem… I’ll spare you 2 days of internal discussion and I’ll just summarize it in one sentence:

I pretty much have decided to move all my open source repositories from BitBucket to GitHub.

Historically I had all my code on Google Code. When that service shut down, a lot of folks went to GitHub and I had my reservations about that service. Instead I chose BitBucket. I still have some philosophical issues with GitHub, but right now I have even more of them with BitBucket so… a lengthy migration is in order.


2025-01-06 - Motivation to Write Games

Category DEV

Recently I’ve started two game projects at home, none of which are in a good enough state to be published. One of them is TD1 which I’ve discussed here in the previous years, and I finally built a prototype, originally to have a concrete way to explore the issues in a specific defense-vs-attack gameplay. There were some interesting challenges. Then I’ve discussed that with some friends. Now that this is done, I have had little motivation to actually implement the discussed gameplay changes. Instead I have thoughts on other projects.

Then I see a lot of little short games on Itch.io that are “playable” -- small scopes and essentially “mostly” finished. But I honestly don’t feel like playing them much besides the initial intro. Each time I think I’m maybe jealous because these games are finished and mine aren’t… but is that really the case?

Well, let’s analyze this a bit more.

1- I’m jealous these games are “finished” and I don’t finish mine.

  • Well, it turns out the first part is only the appearance of it. I fully understand some of the games I see in the various itch.io Jams are only finished “on the surface” -- good enough for a Jam deadline. Each time I try some, I feel like there’s a lack of depth.
  • It’s not true that I don’t finish my games. My Labs has Nerdkill and Asqare which, also on the surface, can appear “finished” -- and in fact these 2 are actually finished from my point of view.

2- These games lack gameplay depth. Well, yeah. Totally. All of them.

  • Nerdkill is a good example as I never intended for the “game” to have a scoring system because then it would become the game I don’t want it to be (gratification of violence). Not having a scoring system is a core part of this gameplay (“violence is pointless”).
  • Asqare could do more, but really what for? It’s a me-too kind of gameplay and the more I add to it, the more it would be clear it’s not Bejeweled -- when I started, I was trying to not be that game, as part of the original implementation was to try different gameplays -- but the problem is that no matter what, it would be compared to that baseline.

3- I do have a number of totally unfinished games. Or maybe it’s better to see them as “not even really started”. They are not even on the Labs page or anywhere to be found.

  • Cangrejo is one. It’s 200% me-too and not even clearly defined where it was going anyway. Jump! is another one such. They are essentially technological demos with a complete lack of interest in producing even a finished MVP. I only started them because I saw some other game and I thought “hey how would I implement this”. But once I found the core engine implementation, I didn't really care about all the work needed to make it a playable game.
  • In a way TD1 falls into that category as well. I had a vague idea, but I see a huge gap between what the prototype is and where I’d want it to be, and it’s not clear to me the end result would be interesting. Thus, yeah, I have little motivation to put in the effort to bridge that gap, only to end up with something I don’t care for at the end.

In essence, that last part is key: I measure the cost of the project in terms of involvement versus result, and I don’t find it motivation compared to other stuff I may be doing. For example stuff like Conductor, RTAC, or even SDB have a high involvement cost, yet on the other hand I can see the tangible benefits to me of having them working -- the Randall Museum visitors may not see the software behind the train automation, yet I know what drives that automation and I’m proud of it.

The other aspect of these game projects is that I tend to focus on the wrong thing. I find it more interesting to focus on the engine and the framework than the gameplay or the rendering itself. I’m not really a game designer, and I’m definitely not a graphic artist. Eventually implementing the gameplay is the least interesting part of game development so far.


2024-12-29 - Kotlin Web

Category Kotlin

It's time to look again at Kotlin for web development. Last time I tried it, a few years ago, it was embryonic and really not suitable for usage. Since then things have changed.

The entry point is Kotlin Multi Platform. It has now a version of Jetpack Compose: https://www.jetbrains.com/lp/compose-multiplatform/

The goal is to evaluate that against my use of Dart / Flutter on Firebase GCP.

There's currently some uncertainty in the future of Flutter. Dart should survive a bit longer as it still seems to be used internally by core projects, however the same can't be said of Flutter according to the water tank hearsay.

Evaluation criteria:

  • How to build a Kotlin MP project.
  • What can Compose do for the web.
  • Overall app architecture.
  • Integration with Firebase authentication.
  • Integration with Firebase DB.
  • Ecosystem of 3rd party libraries.
  • Ease of maintenance over time.

These are all points where Flutter more or less excels. Dart seemed like a barrier at first, until I understood it as basically “Java/Kotlin meets Typescript”. After that, it just made sense and it's easy to work with and pick it up again when I only tweak a project once a year.

The “web UI” aspect of Flutter is familiar -- it's basically ReactJS, but easier to use, and better documented.

The Flutter doc is very strong, and the IJ plugin is a delight to use.

Click here to continue reading...


2024-11-11 - ESP32 with Arduino-esp32, IDF 5.1.x, and C++20

Category Esp32

Time to try again if this combo finally works.

My main motivation is to use C++20 Modules.

(TL;DR Summary: it works… and yet it doesn’t. C++ Modules require a very specific compilation ordering due to cross module dependencies, and the Arduino CLI simply cannot do that. However with ESP-IDF and CMake, it should be fine.)


  Generated on 2025-12-27 by Rig4j 0.1-Exp-78c9166