What's up, D?#
Fri, 30 Apr 2021 17:27:22 +0000
One of the nice things about side projects (as opposed to the ones I get paid for) is not having to be accountable to anybody for timely delivery, and the freedom to head off on tangents or side investigations. I have recently picked up NixWRT again, but I regret to inform you that "off piste" is where I am right now.
Backstory: monit is not a great service monitor for my needs, mostly because it isn't the parent process of the service daemons it starts, so can't tell when they exit without polling/pid files etc - which is slow and unreliable. So I decided that the world does not have enough init replacements already, and because of the risk that anyone else might otherwise find it useful I decided to write it in Fennel. Then a little while later, I decided to do something else for about five months, and now I have no mental context of what I was doing.
So why not start again? This time it's called upd and although it shares some ideas with swarm, I am attempting to use tests to drive more of its design.
I started by sketching out the shape of a plausible service monitor for a pppoe daemon. You can see that the state logic is complicated and it has many collaborators, so I install mock versions of them by setting entries in the
package.loaded table which
require checks before loading a file. The most complicated bit is probably the fake event loop: the test setup provides an array
my-events of functions which may update state, replace mocks etc, perform assertions etc, and and the mock event loop runs them one by one. This lets me write my first test, to see that the daemon is started when the interface is present. A followup refactor replaces the longwinded calls to
tset package.loaded with more intention-revealing names
The second test checks that we observe an exponential backoff when the process fails to start. We don't have any visibility into the actual backoff state in the system under test, so have to observe from the outside. First we stop the process from an event function, and then poll the process state repeatedly while incrementing a counter until it restarts. 0e966459
This is convoluted and might suggest that we should make that state visible. But it also prompted me to try making backoff state be a property of the process monitor itself, not the overall system under test - suppose we want to write a script that watches two or more processes? The next few commits
Of course, making the backoff state a property of the process has the side effect of making it visible. The next test is that the process is stopped if the underlying link is lost, and it's actually quite handy to be able to reset the backoff interval in the test setup: 0350c8aed6a
Something was still bothering me here, though. In order to move the backoff testing from the script to the process, I'd done
- (when (and (not (process.running? pppd))
- (nil? pppd.backoff-until))
+ (when (not (process.running? pppd))
This is logically correct, as
pppd:backoff does nothing anyway if
pppd.backoff-until is nil. But it's really quite non-obvious. What we actually want to say here is "if the process died, back off" - so let's say it! Add a process.died? method and rewrite the test to use it.
The last interesting change in this sequence was triggered by looking at the event loop, when I realised that we don't anywhere use the value of the event that comes back from
next-event - we wait for something to happen, but then we discern what happened by testing various bits of state. To say the same thing again in fewer words: we're don't receive events, we wait for state changes. So let's change the code to use better names
What's the conclusion? Is there a conclusion? A few thoughts -
- for the TDD purists, it wasn't "real" TDD because I wrote quite a lot of implementation code first instead of writing the test first.
- by listening to the pain of testing I was able to improve the micro-level design in a few aspects: I am happier with the result than with the initial code
- by having the tests I was able to make those changes while being reasonably confident that I hadn't broken anything
- the next thing on my list (which is in progress already) is writing the service monitor for a hardware ethernet device. When it's in slightly better shape I will write another post full of netlink and qemu trivia, but until then, goodbye.
First steps in Colemak#
Thu, 15 Apr 2021 14:29:26 +0000
This is more of a placeholder than anything else, but I decided at the weekend that it was time to learn to touchtype, and further, that it would make sense to switch to Colemak at the same time. I don't yet know if it actually will prove correct, it's too early too say.
I'm using Colemak Academy and have achieved 97% on the home keys at an astonishingly speedy 18 words per minute, but the top and bottom rows are still strangers to me. And only after trying to use Emacs did I learn that the Colemak layout rebound Caps lock (which I bind to Ctrl) as Backspace).
The ANT is my friend, it's blowing in the wind#
Sat, 06 Mar 2021 14:46:16 +0000
A few weeks ago I won some bike rollers on Ebay, and once I'd mastered the basic skills (pedalling without falling over, stopping without falling over, starting without falling over without holding onto the door jamb), I started looking for some way to measure what I was doing. Another Ebay purchase later and I was the owner of a Garmin GSC10 speed/cadence sensor. (There are two standards for wireless fitness equipment: ANT+, which is the older one, and probably obsolescent, and Bluetooth Low Energy, which is newer but is based on Bluetooth, therefore probably unreliable. Also, in terms of what I could find on Ebay, BTLE is double the price for any brand I'd heard of.)
But the sensor is only half of the solution: I also need something to display the values, and as I already have a handlebar mount for my phone I decided that an Android app would be the thing.
There is more than one route to persuading an Android phone to receive ANT readings: you can
- use a USB OTG adaptor and an ANT+ USB stick, if you don't mind having lumps of daisychained plastic hanging off the bottom of your phone. Probably reserve this for indoor use - or dry days - only
- make your own micro-USB ANT stick which is conceptually simple but practically fiddly, and a good way to level-up your soldering skills. Remove the case from a miniature ANT+ stick, solder four wires to the traces (comparatively easy) and then solder onto a micro-usb connector (comparatively not easy). Connect pin 4 of the MicroUSB conector to ground to make it act in Host mode.
- use a phone that has an ANT+ transceiver built in, and acquire or build a version of Android with drivers that support it. This might not be the one it was shipped with! Using a Moto G5 Plus, I had to build my own (it took several attenpts)
Then I needed an app.
As a cyclist
I want speed and cadence displayed in big numbers
So I can see them even when the wind is making my eyes water
and the sun is shining on the phone display
Apps for ANT+ sensors also seem to be comparatively thin on the ground. IpBike seems to be the de facto standard, but after trying it a few times I was struck by acute NIH, and decided it would be more fun to write my own than to figure out whether I could configure it for my needs ...
Three weeks later I'm not sure it is more fun, but it's certainly a learning experience. https://github.com/telent/biscuit
Her name is Oreo#
Fri, 12 Feb 2021 13:47:14 +0000
I am at a point somewhere along the way of a long yak-shaving journey ("I wonder if the MSM8953 SoC supports ANT+ in hardare and it just needs enabling in the Android software?") which involves rebuilding Android for my phone. There seem to be very few ROMs for the Moto G5 Plus ("potter") that are still being maintained, and even fewer of them that have build instructions that (a) work as described; (b) comprise fewer than 30 manual steps. Do Android ROM developers not do CI builds? If this was all GPL I could make a strong case it's in breach. ("The source code for a work means the preferred form of the work for making modifications to it [...] all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable."). Yes, I know, volunteers, spare time, teeth of gift horses, etc etc. End rant
Anyway, so far I am doing the following and it has not yet blown up
1) using the docker image from LineageOS for Microg
2) invoking it as follows
docker run \
-e "BRANCH_NAME=lineage-15.1" \
-e "DEVICE_LIST=potter" \
-e "SIGN_BUILDS=false" \
-e "SIGNATURE_SPOOFING=restricted" \
-e "INCLUDE_PROPRIETARY=false" \
-e "WITH_SU=true" \
-e "CUSTOM_PACKAGES=GmsCore GsfProxy FakeStore MozillaNlpBackend NominatimNlpBackend com.google.android.maps.jar FDroid FDroidPrivilegedExtension " \
-v "/$HOME/src/android/l4mg/lineage:/srv/src" \
-v "/$HOME/src/android/l4mg/zips:/srv/zips" \
-v "/$HOME/src/android/l4mg/logs:/srv/logs" \
-v "/$HOME/src/android/l4mg/cache:/srv/ccache" \
-v "/$HOME/src/android/l4mg/keys:/srv/keys" \
-v "/$HOME/src/android/l4mg/manifests:/srv/local_manifests" \
3) with this manifest in
<?xml version="1.0" encoding="UTF-8"?>
<project name="LineageOS/android_external_bson" path="external/bson" remote="github" revision="cm-14.1" />
<project name="LineageOS/android_device_qcom_common" path="device/qcom/common" remote="github" revision="lineage-15.1" />
<project name="LineageOS/android_packages_resources_devicesettings" path="packages/resources/devicesettings" remote="github" revision="staging/lineage-15.1" />
<project name="boulzordev/android_device_motorola_potter" path="device/motorola/potter" remote="github" revision="lineage-15.1-64" />
<project name="boulzordev/android_vendor_motorola_potter" path="vendor/motorola/potter" remote="github" revision="lineage-15.1-64" />
<project name="boulzordev/android_kernel_motorola_msm8953" path="kernel/motorola/msm8953" remote="github" revision="lineage-15.1-64" />
Worthy of note:
- I found the manifest on the Interwebz by use of a search engine. I had to edit the revision attribute of
- because I have a (presumed working) example manifest for 15.1 I am not attempting to build any newer version. One change at a time, hey
- in my limited understanding, the manifest tells the build system where to find Moto proprietary files, and the
INCLUDE_PROPRIETARY=false build argument tells the L4microg build scripting not to try and download them from "TheMuppets". This is important because the muppets don't have them for my device.
- getting F-droid and Microg baked in is probably no bad thing
As I write this, it has been building for ~ 30 minutes and not fallen over yet, so things are looking vaguely promising
[ 8% 8343/95970] target thumb C++: libv8src_32 <= external/v8/src/builtins/builtins-constructor.cc
Apparently it's done 8% of something
Update: Fri Feb 12 19:57:48 GMT 2021
It works! It built a buch of stuff in
zips/potter/ including the file
lineage-15.1-20210212-UNOFFICIAL-potter.zip which I installed from Recovery using the instructions at https://wiki.lineageos.org/devices/peregrine/install Yes, that's not the same phone, but it's close enough to use the same procedure
Sat, 30 Jan 2021 13:46:06 +0000
There are probably better ways to satisfy the need for a wall light than
- cutting up a 150 LED Neopixel strip, sticking the pieces to a piece of aluminum, then soldering wires from each row to the next
- building a ESP8226 circuit on stripboard to drive it
- sending 10x15 arrays of RGB over MQTT to specify colours
The led strip came from my Christmas lights, as did the circuit - now transferred from breadboard to a more permanent soldered stripboard. The Arduino sketch, known as Dolores ("do lo-res") is all new, though, and parts of it (the bits that are plain C++ not Arduino code) are even unit-tested.
Perhaps worth noting
- 10x15 pixels with a byte each for R G B is 450 bytes of data, which is more than the default max packet size of the Arduino MQTT library I'm using. Fix this with a call to
client.setBufferSize with a more appropriate value
- after the first time I'd put it all together and installed it on the wall, I realised that software updates were going to be considerable faff, given they require unplugging the Wemos D1 from its circuit and attaching it to USB. OTA updates to the rescue! This makes the device run a HTTP server which new images can be POSTed to. There are better ways to do this - I'd rather it were an HTTP client which fetched images from a known URL, instead of accepting anything from anyone that can reach its port 80 - but that can wait until I have some kind of CI setup.
- the Nix environment I'm using for Arduino IDE only works with
nix-build. I'd like it to behave as a regular derivation so I can have a CI setup
But now I can send it any picture I like. Here's a digital clock -
while sleep 2 ; do \
( convert -support .5 -depth 8 -fill red \
-font DejaVu-Sans-Condensed -pointsize 7 -size 15x10! \
-draw "text 0,7 '$(date +%H%M)'" -sharpen 8 'xc:#333' RGB:- | \
tee out.rgb | \
nix run nixos.mosquitto -c mosquitto_pub -h mymqttserver.lan \
-s --username username --pw password \
-t effects/84F3EBE5E57C/image ) ; \