RSS Feed
okt 28

Fantus-button part 2: the physical button build and the network communication

Posted on l√łrdag, oktober 28, 2023 in Hal9k, Planets

First part of this series is here, covering the reverse engineering of the DRTV Chromecast App.

I wanted the physical appearance to be extremely minimalistic, with slight references to various cubes from videogames. Because it is a remote control, it of course has to be wireless and battery-powered.

The box is lasercut from 6 mm MDF, and with a giant red arcade button on top with a red LED inside.

The electronics inside is a battery-powered Wemos D1, along with 4 x 18650 Lithium battery cells. After some experimentation on the response time, which is primarily dominated by the time it takes to reconnect to the WiFi network, I initially only used “light sleep”. This resulted in a battery time of just over a week, which is okay, but not great.

In order to preserve battery deep sleep would be really nice. The problem is deep sleep on the Wemos can only be interrupted by a reset. The idea was to use a MOSFET (in this case an N-channel logic level mosfet, IRFZ44N) for the Wemos to be able to select whether a press of the button should reset it, or it should just register on a pin as normal.

This circuit allows RST to be pulled low by the button, as long as D0 is high. Luckily, D0 is high during deep sleep, so as long as the Arduino code keeps D0 low button presses will not reset — but can still be registered by reading pin D1.

This works out “responsively enough” because the initial start has some delay due to the Chromecast initializing the app and loading media. Any subsequent button presses within the 30 seconds the Arduino stays awake are instant though. With this setup the battery life is not a problem – I’ve only had to charge it once. As a bonus feature/bug whenever the battery gets low the Wemos will trigger a bit sporadically: this causes “Fantus-bombing” where Fantus will just randomly start; quite quickly thereafter the Fantus-button is being charged ūüėČ

The Wemos itself is not powerful enough to do all the pyChromecast communication needed, so I setup a small Raspberry Pi to handle that part. Since I didn’t want to spend too much time and effort setting up the communication between them, I ended up using a trick from my youth: UDP broadcasting. Because UDP is datagram-oriented you can send a UDP packet to the broadcast address ( and then it will be received by all hosts on the local area network: no configuration needed. In Arduino code it looks like:

Udp.beginPacket("", 31337);

(Full Arduino code available here.)

At this point I had a UDP packet that I could receive on the Raspberry Pi, and it was just a matter of writing a small server program to listen, receive and process those UDP commands. However, at this point a thought entered my mind, that derailed the project for a while:

netcat | bash

Why write my own server to parse and execute commands, when Bash is already fully capable of doing exactly that with more flexibility than I could ever dream of? And netcat is perfectly capable of receiving UDP packets? This is a UNIX system, after all, and UNIX is all about combining simple commands in pipelines — each doing one thing well.

The diabolical simplicity of just executing commands directly from the network was a bit too insecure though. This is where Bash Restricted mode enters the project: I wouldn’t rely on it for high security (since it is trying to “enumerate badness“), but by locking down the PATH of commands that are allowed to execute it should be relatively safe from most of the common bypass techniques:

netcat -u -k -l 31337 | PATH=./handlers/ /bin/bash -r

The project was now fully working: press the button, Fantus starts. Press it while Fantus is playing: Fantus pauses. Press it while Fantus is paused: Fantus resumes. The little human was delighted about his new powers over the world, and pressed the button to his hearts content (and his parents slight annoyance at times).

(Full code for handler available here.)

But wouldn’t it be cool if the little human had a (limited) choice in what to view?…

jul 18

Fantus-button part 1: Reverse engineering the DRTV Chromecast App

Posted on tirsdag, juli 18, 2023 in Hal9k, Planets

I want to build a physical giant red button, that when pressed instantly starts a children’s TV-show, in my case Fantus on DRTV using a Chromecast.

The first part of the build is figuring out how to remotely start a specific video on a Chromecast. Initially I thought this would be pretty simple to do from an Arduino, because back in the day you could start a video just using a HTTP request. Very much not so anymore: the Chromecast protocol has evolved into some monster using JSON inside Protobuf over TLS/TCP, with multicast DNS for discovery. Chance of getting that working on a microcontroller is near-zero.

But remote control is possible using e.g. pychromecast which has support for not only the usual app of YouTube, but also a couple of custom ones like BBC. Let’s try and add support for DRTV to pychromecast, starting at the hints given on adding a new app.

Using the netlog-viewer to decode the captured net-export from Chrome, and looking at the unencrypted socket communication, the appId of the DRTV app is easily found.

However, one of the subsequent commands has a lot more customData than I expected, since it should more or less just be the contentId that is needed:

  "items": [
      "autoplay": true,
      "customData": {
        "accountToken": {
          "expirationDate": "2022-07-02T00:48:35.391Z",
          "geoLocation": "dk",
          "isCountryVerified": false,
          "isDeviceAbroad": false,
          "isFallbackToken": false,
          "isOptedOut": false,
          "profileId": "c4e0...f3e",
          "refreshable": true,
          "scope": "Catalog",
          "type": "UserAccount",
          "value": "eyJ0eX...Dh8kXg"
        "chainPlayCountdown": 10,
        "profileToken": {
          "expirationDate": "2022-07-02T00:48:35.389Z",
          "geoLocation": "dk",
          "isCountryVerified": false,
          "isDeviceAbroad": false,
          "isFallbackToken": false,
          "isOptedOut": false,
          "profileId": "c4e0a...f3e",
          "refreshable": true,
          "scope": "Catalog",
          "type": "UserProfile",
          "value": "eyJ0eXAi...IkWOU5TA"
        "senderAppVersion": "2.211.33",
        "senderDeviceType": "web_browser",
        "sessionId": "cd84eb44-bce0-495b-ab6a-41ef125b945d",
        "showDebugOverlay": false,
        "userId": ""
      "media": {
        "contentId": "278091",
        "contentType": "video/hls",
        "customData": {
          "accessService": "StandardVideo"
        "streamType": "BUFFERED"
      "preloadTime": 0,
      "startTime": 0
  "repeatMode": "REPEAT_OFF",
  "requestId": 202,
  "sessionId": "81bdf716-f28a-485b-8dc3-ac4881346f79",
  "startIndex": 0,
  "type": "QUEUE_LOAD"

Here I spent a long time trying without any customData, and just using the appId and contentId. Initially it seemed to work!

However, it turned out it only worked if the DRTV Chromecast app was already launched from another device. If launched directly from pychromecast the app would load, show a spinner, and then go back to idle. Here much frustration was spent; I guess the customData is actually needed. And indeed, putting that in works! But where do these tokens come from, and how do we get those tokens from Python?

Using Chrome’s developer tools (F12) on the DRTV page, and then searching globally (CTRL-SHIFT-f) for various terms (“expirationDate”, “customData”, “profileToken”, “accountToken” etc.) revealed some interesting code, that was as semi-readable as any pretty-printed minifyed Javascript. Eventually I found the tokens in local storage:

Using these tokens work really well, and allows starting playback!

Some further exploration proceeded: using the showDebugOverlay flag reveals that the DRTV player is just a rebranded Shaka Player. The autoplay functionality can be disabled by setting chainPlayCountdown to -1, which is honestly a real oversight that it cannot be disabled officially, to not have to rush to stop the playback of the item before the next autoplays.

With all the puzzle pieces ready, I prepared a pull request (still open) to add support for DRTV to pychromecast.

Fantus-button part 2 will follow, detailing the hardware build and network integration with the support from pychromecast.

feb 20

Floating Solid Wood Alcove Shelves

Posted on mandag, februar 20, 2023 in Hal9k, Planets, Woodworking

I have an alcove where I wanted to put in some floating shelves. I wanted to use some solid wood I had lying around, to match the rest of the interior; this ruled out most of the methods described online: (i) building up the shelf around a bracket, and (ii) using hidden mounting hardware would be hard to get precise and would not provide support on the sides.

So inspired by some of the options instead I tried to get by with just brackets on the three sides, in a solid wood shelf. I ended up with 12mm brackets of plywood in a 26mm solid wood shelf, and that was plenty sturdy.

Step 1 was to cut out the rough shelves, with plenty of extra width, and rough fitting the plywood bracket pieces. It makes sense to leave as much on the top of the slit as possible, as this will be the failure point if overloaded. The excellent wood workshop at Hal9k came in very handy!

Step 2 was to mount the plywood brackets in the alcove. Pretty easy to do using a laser level, biggest problem was getting the rawplugs in precise enough for the level to be kept.

Step 3 was fitting the shelves individually, accounting for the crookedness of the 3 walls. The scribing method used by Rag’n’Bone Brown was pretty useful, just doing it in multiple steps to make sure not to cut off too much.

Finally, all the shelves in final mounting. Getting them in took a bit of persuasion with a hammer, and minor adjustments with a knife to the plywood brackets, as it was a tight fit. The key again was small adjustments.

One concern with such a tight fit would be wood movement; however most of the wood movement is “across the grain” which in this application means “in and out” from the alcove, where the wood is basically free to move as the shelves are not fastened to the brackets in any way.

Another concern would be if the relatively small brackets (12x12mm) can handle the load of the relatively wide shelves (60cm wide, 35cm deep, and 2.6cm high). There are two failure scenarios: (i) the wood could split above the slit, (ii) or the bracket could deform or be pulled out. Neither seems likely as (i) applying a static (or even dynamic) load large enough to split the wood seems implausible, even at the weakest point in the middle of the front, and (ii) the tight fit counteracts the brackets ability to be pulled out since pulling out in one side would have the shelf hitting the wall on the opposite side.

All in all a very satisfying project to work on and complete!

dec 15

Quick and dirty guide to Lithium battery-powered Wemos D1 Mini

Posted on torsdag, december 15, 2022 in Hal9k

The Wemos D1 Mini is an ESP8266 based prototyping board with WiFi connectivity and countless applications. It becomes even more useful in battery-powered applications, where with the proper setup, it can run low-powered for months at a time — or only hours if done incorrectly.

This is the quick and dirty guide to running a Wemos D1 Mini powered by Lithium-Ion batteries: We will be blatantly ignoring several design specifications, so double check everything before using in a critical project. Several things will vary, and since there is plenty of clones of the board some boards will work better than others.

Warning: Lithium-Ion batteries always command healthy respect, due to the energy they store! Do not use bad cells, and do not leave batteries unattended in places where a fire can develop, especially while charging. That being said, the setup given here should be as safe as most other Lithium-Ion battery projects.

Why run off a battery?

You chose a Wemos D1 because you want to do some WiFi connectivity. This narrows down the useful modes from the overwhelming large table of possibilities. The approach will be slightly different depending on why you want to run off a battery. There are 3 main usecases:

  • Periodically wake up on a timer, do some work, connect to WiFi, and go back to sleep. Here we can utilize the deep sleep mode of the ESP8266, and get lifetimes in months.
  • Wake up based on an external pin trigger, do some work, connect to WiFi, and go back to sleep. Here we can also utilize deep sleep, and get lifetimes in weeks/months.
  • React with low latency to an external pin, do some work, and go to sleep while still connected to WiFi. Here we can utilize light sleep, but only get lifetimes in hours/days.

Hardware setup

The hardware needed is:

  • Wemos D1 Mini
  • TP4056 module with “discharge protection”, most modules with more than one chip has this, but be careful!
  • Lithium-Ion battery, e.g. a 18650 cell, and probably a holder for the battery

What you don’t want is anything resembling a power bank or battery shield with a regulated output (5V or 3V). These are practically useless, simply a more expensive battery holder! Two reasons: poorly built (I have several where standby is prevented by pulling 100 mA through a resistor!), and you don’t want a switching mode power supply. The keyword here is “quiescent current”: an SMPS can easily consume 5-10 mA continuously, which could very likely be the majority of the current draw.

Wiring diagram.

Waking on a timer – deep sleep

Full code example for deep sleeping on a timer.

To start deep sleep for a specified period of time:

//Sleep for some time; when waking everything will be reset and setup() will run again

Note that you can’t safely sleep for more than approximately 3 hours. Power usage is approx 0.3–0.4mA when deep sleeping.

Keep in mind that after waking from the timer the chip will be reset, meaning no state is available, and WiFi will have to reconnect. Reconnecting to WiFi can be anything from 3–10 seconds or even longer, meaning that will be a delay before the program can resume.

Waking on an pin trigger (reset)

Full code example for deep sleeping waiting for a pin trigger.

The code is exactly the same as waking on a timer, with one exception:

//Sleep until RESET pin is triggered

The chip will be effectively comatose, sleeping until a RESET is triggered. Same caveats apply: waking up the program is restarted, and reconnecting to WiFi will be a delay.

Stay connected – low latency

Full code example for light sleeping connected to WiFi waiting for a pin trigger. Note that the button should be connected to D3 for this example, not RST.

The key parts are:

void setup() {
  WiFi.setSleepMode(WIFI_LIGHT_SLEEP, 3);  // Automatic Light Sleep

void loop() {
  delay(350); // Any value between 100--500 will work, higher value more power savings
    // but also slower wakeup!

Simply delaying will bring power savings — simple and easy!

When awake power consumption is around 75mA. Average power consumption when light sleeping with delay(200) is around 45 mA, with delay(350) and larger is around 30–40mA.

Measuring battery depletion

The ESP can measure it’s internal VCC supply voltage, and because the battery will start dropping below the rated 3.3V before it is depleted, this allows to get an warning when the battery starts to deplete.


void loop() {
  if (ESP.getVcc() < 2800) {
    //Do something to warn of low battery

In my experience the Vcc reading will drop below 2800 when the battery starts to be depleted.

ADC readings vs. battery voltage

Note that measuring the VCC while connected with USB is not possible, as the USB connection will pull up the battery and the 5V rail to 5V!

Calculating battery life

Here is a quick calculator for how long your Wemos D1 Mini can stay powered

Deep sleep

(conservatively assumes base load 1mA, 10 secs burst of 100mA for every wakeup), resulting in

Light sleep

Of course the consumption can be brought even lower: some chips are unused but partly connected and will have some leakage (LEDs, USB chip on the Wemos). Making it even leaner is outside the scope of quick and dirty.

okt 4

Olimex A20-OLinuXino-LIME2 ‚Äď 8 years in service, 2 PSUs and 1 SD-card down

Posted on tirsdag, oktober 4, 2022 in Hal9k, Planets, Sysadmin'ing

4 years ago I posted a 4 year review of the Olimex LIME2. It seems that the lifetime of power supplies is approximately 4 years as now another power supply died, and this time also the SD-card was expiring. The LIME2 lives on however!

It was a bit hard to notice, because the battery pack of the LIME2 kept it running pretty well even with the poor power supply. So, better monitoring of the battery pack is also on the todo list.

Recovering the bad SD-card

Recovering the SD-card was relatively easy with minimal dataloss, when out of the LIME2:

$ sudo ddrescue /dev/mmcblk0 backup.img
# Put in a new SD-card
$ sudo dd if=backup.img of=/dev/mmcblk0 bs=16M

I have done this a couple of times with other SD-cards from Raspberry PIs, and though there is the potential for dataloss it is usually minimal. This time a few blocks were lost.

Upgrading Debian from Stretch to Bullseye

I took the opportunity to upgrade the Debian install while the system was offline anyway. Upgrading was generally painless, following the usual Debian method. I went through the Buster release just to be sure:

$ vim /etc/apt/sources.list
# replace all "stretch" with "buster" :%s/stretch/buster
$ apt update && apt upgrade && apt full-upgrade
$ reboot

$ vim /etc/apt/sources.list
# replace all "buster" with "bullseye" :%s/buster/bullseye
$ apt update && apt upgrade && apt full-upgrade
$ reboot

The only tricky part is booting the new kernel. Since that always fails for me on the first try, I always hookup the serial console. For future reference, this is how to hookup the serial console (which is TTL 3.3V):

Pinout from left as labelled on the LIME2: TX, RX, GND

Now, of course the boot failed. I tried getting the flash-kernel package to work for my setup, but for historical reasons I have a separate boot partition. In the end I derived a simple bootscript from that package, that boots from p1 but loads the kernel, fdt and initrd from p2:

setenv bootargs  ${bootargs} console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait panic=10

#setenv fk_kvers '4.19.0-21-armmp-lpae'
setenv fk_kvers '5.10.0-18-armmp-lpae'
setenv fdtpath dtb-${fk_kvers}

load mmc 0:2 ${kernel_addr_r} /boot/vmlinuz-${fk_kvers}
load mmc 0:2 ${fdt_addr_r} /boot/${fdtpath}
load mmc 0:2 ${ramdisk_addr_r} /boot/initrd.img-${fk_kvers}
bootz ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}

The script can be manually input over the serial terminal, and thereby tested out.

The only downside is it needs to be manually updated after each kernel upgrade. To activate the uboot bootscript:

$ mount /dev/mmcblk0p1 /mnt/
$ cd /mnt
# ensure boot.cmd is as above
$ mkimage -C none -A arm -T script -d boot.cmd boot.scr

Monitoring the LIME2 battery pack

After upgrading to a recent 5.X mainline Linux kernel the battery pack is exposed in the sysfs filesystem:

$ cat /sys/class/power_supply/axp20x-battery/voltage_now 
4070000 # 4.07 V
$ cat /sys/class/power_supply/axp20x-ac/voltage_now 
4933000 # 4.93 V

I setup a couple of alerting rules for these in my home monitoring setup, so hopefully the next time the LIME2 defeats a power supply I’ll get notified.


I can still warmly recommend the LIME2. It is still available, and even a bit cheaper nowadays at 40 EUR + VAT, and still a little workhorse that just keeps on going.

aug 29

Grundfos Alpha 2 pumpe går i stykker og flimrer: reparer den med en kondensator til nogle få kr

Posted on mandag, august 29, 2022 in Danish, Hal9k, Planets

Som med s√• mange andre huse fulgte der en Grundfos Alpha 2 cirkulationspumpe med da vi k√łbte et hus. Den pumpede og pumpede, indtil den var blevet 13 √•r gammel: s√• begyndte den at flimre n√•r den skulle starte op. Det er jo som s√•dan en rimelig h√¶derlig levetid, men ogs√• lidt mist√¶nkeligt at det ikke virkede til at v√¶re et mekanisk problem.

Flimmer, men ingen pumpning.

Symptomerne er:

  • Pumpen kan k√łre fint i l√¶ngere tid
  • Ved l√¶ngere tids stop kan den ikke starte; nogen gange starter den efter noget tid
  • Ved opvarmning starter pumpen, f.x. med en varmepistol

Det sidste punkt har gjort at der flere steder bliver spekuleret i at der er “kondens” i pumpen.

Det er dog ikke problemet. Problemet er en lille kondensator der holder str√łm til lavsp√¶ndingselektronikken:

47 uF 16 V kondensatoren er problemet.

I Hal9k eksperimenterede vi en smule for at verificere: hvis man k√łler den ned med f.x. sprit opst√•r problemet med det samme. Hvis man varmer den op starter pumpen med det samme.

For en udf√łrlig vejledning i hvordan pumpen skilles ad og kondensatoren skiftes har Simon lavet en video:

Men hvad er kilden til problemet s√•? Kondensatoren f√•r over tid en alt for stor indre modstand, og sp√¶ndingstabet bliver for stort. Her et par m√•linger uden og med lidt sprit til ekstra afk√łling:

En helt ny kondensator måler under 1 ohms modstand, altså 100 gange så lille indre modstand:

En ny kondensator kan findes ved at s√łge p√• “47 uf 16 v smd electrolytic capacitor”, f.x., eller endnu mere lokalt fra

Efter erstatning pumper pumpen lystigt.

Så hvad kan man lære af hele denne historie?
Grundfos laver mekanisk gode pumper, men sparer p√• deres elektronik. Det er trist at t√¶nke p√• hvor mange pumper der mon er smidt ud lang tid f√łr tid. Man kan nok ikke beskylde Grundfos for “planned obsolence” efter 13 √•r, men man kunne dog √łnske at produktet fejlede i en mere brugbar konfiguration: f.x. at pumpen k√łrer ved et minimum hvis elektronikken fejler.

jan 31

Reparation af Aduro-tronic II

Posted on s√łndag, januar 31, 2021 in Danish, Hal9k

Vi har en Aduro 1-2 brændeovn med Aduro-tronic, som vi generelt er rigtig glade for. Den har nu været i drift i 5 år, og har haft omkring 4500 optændinger. Generelt er designet rigtig fornuftigt, og med Aduro-tronic og Smart Response er det rigtig nemt at fyre korrekt.

Den eneste anke må være at vi nu 2 gange har oplevet at Aduro-tronic stemplet har givet op:

F√łrste gang k√łbte jeg et nyt, men det viste sig at v√¶re ret nemt at reparere. S√• da problemet opstod igen reparerede jeg bare det gamle stempel.

Aduro-tronic er basalt set en ut√¶t luftcylinder med en fjeder. Stemplet trykkes ind, fjederen bliver sp√¶ndt og som lufter langsomt tr√¶kker ud af cylinderen k√łrer stemplet op igen. Hvor ut√¶t cylinderen er justeres med den lille skrue, og dette s√¶tter s√•ledes tiden spj√¶ldet holdes √•bent.

Problemet opst√•r n√•r aske, st√łv og lignende s√¶tter sig inde i cylinderen, og over tid f√•r foringen til at blive ut√¶t. Derved er stemplet for ut√¶t.

L√łsningen er simpel:

Tag forsigtigt fjederen af ved at trykke holderpladen ned og dreje den 90 grader.
Den √łverste plade kan forsigtig tages ud. Jeg brugte en spidstang, og lidt vrikken fra side til side. Derefter kan selve stemplet med gummi-foring tages ud.

Reng√łr nu cylinderen, og sm√łr stempel og cylinder med en lille smule silikone-spray der hj√¶lper med at forsegle.

Saml hele mekanikken igen, tryk stemplet ned og check at det nu bliver nede af sig selv. Når mekanikken igen er monteret på brændeovnen skal tiden nok indstilles forfra.

mar 25

Sniffing Philips Hue Zigbee traffic with Wireshark

Posted on mandag, marts 25, 2019 in Hal9k, Planets

I have a Philips Hue gateway at home that is connected to a number of Philips Hue lights, as well as some IKEA trådfri light bulbs, and a couple of OSRAM Lightify light strips. Most of the time the network works quite well, but some of the time a few of the lights become unreachable. I read a rumor online that the Hue lights and the other lights are actually on two different Zigbee networks. Of course, if only I had a way of sniffing the Zigbee traffic I could diagnose these problems. And thus began this quest.

USB TI CC2531 Zigbee sniffer dongle.

I started by buying a Zigbee sniffer, I found that the Texas Instruments CC2531 chip is widely used, and available in a cheap USB package. I purchased this USB CC2531 Zigbee sniffer, but others are probably equally good. After the dongle arrived I spent quite a while thinking that I need to replace the stock firmware, because of various old projects on GitHub (Sensniff, ccsniffpiper, etc.). Fortunately, you do not need to change the stock firmware. The best software package seems to be KillerBee which supports both sniffing and injection; however only sniffing with the CC2531. Installing KillerBee on Ubuntu is quite easy. You need to install scapy, and a few dependencies. The installation instructions are probably more up to date than this blog post.

Starting the sniffing is really easy, if you know the channel the Philips Hue is operating at. I think channel 11 is the default, but it is displayed in the Hue app, under info for the bridge:

sudo zbwireshark -c 11

This will launch a background process, and an instance of Wireshark that is monitoring the channel. At this point you can see the traffic; but everything is encrypted…

Encryption… Encryption everywhere!

A very incomplete intro to Zigbee encryption

Zigbee traffic can be encrypted with AES-128, which is a symmetric encryption scheme. This means the key to encrypt and decrypt is the same. There is a number of keys that can be used to encrypt a single packet payload:

  1. The Network Key, which is unique to this Zigbee network. This is what we will ultimately need to find. It is generated by the gateway, and shared by all the devices on the network. How does a new device join the network then? It uses the…
  2. The Key-Transport Key which is a pre-shared secret. Apparently there is a number of these, depending on the class of devices and type of network. These are apparently a well-kept secret or something, although widely available on the internet:
    1. “default global trust center link key” which is 5A:69:67:42:65:65:41:6C:6C:69:61:6E:63:65:30:39
    2. “light link master key” which is 9F:55:95:F1:02:57:C8:A4:69:CB:F4:2B:C9:3F:EE:31
    3. “light link commissioning key” which is 81:42:86:86:5D:C1:C8:B2:C8:CB:C5:2E:5D:65:D1:B8

You can add these keys to Wireshark, and the Zigbee dissector will then try to decrypt traffic using them. Go to Edit -> Preferences -> Protocols -> ZigBee and edit the pre-configured keys:

The Key-Transport Key is used whenever a new device joins the network with the sole purpose of encrypting the network key. So, to find the network key we need to know the Key-Transport Key, and observe the traffic when a device joins. So this is what I did: I found an IKEA Trådfri lightbulb and spent the frustrating time needed to get it to join the Philips Hue gateway (resetting the bulb, searching for new lights). Finally, it suceeded!

Hitting gold!

Now, by adding the transport key to the list of keys in Wireshark all the traffic on the network was able to be decrypted!

Decrypted traffic

The next step will be to analyze the traffic, and understand the routing. Very initial probes using zigbee-viewer indicates that there is indeed three distinct routings:

Zigbee routing.
feb 1

D√łr jeg af partikelforurening fra min moderne br√¶ndeovn?

Posted on fredag, februar 1, 2019 in Danish, Hal9k, Planets

Vi har en fin moderne br√¶ndeovn derhjemme (en Aduro 1-2), som vi bruger ret intensivt til opvarmning af vores gamle stuehus. Et meget relevant sp√łrgsm√•l er derfor: hvor meget bidrager s√•dan en moderne br√¶ndeovn til partikelforureningen i vores stue?

Partikelforurening er sm√• partikler af st√łv og sod, der bl.a. fremkommer ved afbr√¶nding af fossile br√¶ndsler, som olie og tr√¶. De kan for√•rsage forskellige slags sundhedsproblemer, bl.a. kr√¶ft. P√• et interaktivt partikelkort kan man se hvilke niveauer der (beregnet) var i Danmark i 2012, og f.x. forskellen mellem land og by; √•rsgennemsnittet for PM2.5 l√• p√• 5.3 – 11.9 őľg/m3.

Det er et ganske egoistisk projekt jeg har gang i: jeg har ingen data for hvor stor partikelforureningen er udenfor huset, men kun inde i selve stuen. Der er en del kilder til partikelforurening som jeg kender til, eller har observeret:

  • Vi har et pillefyr, der st√•r i n√¶rheden, der ogs√• k√łrer i den kolde tid
  • Vi bor i kort afstand fra en lettere bef√¶rdet vej
  • Madlavning, specielt med en gammel emh√¶tte, kan bidrage betydeligt
  • Den generelle baggrundsvariation kan v√¶re betydelig

For at unders√łge det har jeg opsat en partikel sensor (en Honeywell HPMA-1150S0) i stuen, ca. 3 m fra br√¶ndeovnen. Samtidig registrerer jeg br√¶ndeovnens temperatur, via en Aduro Smart Response sensor. Dette har jeg nu gjort i lidt over et √•r, og kan dermed lave en data analyse p√• et √•rs data.

Til brug for analysen er der registreret PM10 og PM2.5 v√¶rdier, if√łlge databladet i őľg/m3. Sensoren skulle desuden v√¶re “fully calibrated”, og kunne k√łre i mindst 20.000 timer, s√• et √•rs data burde man kunne stole p√•. Usikkerheden er dog angivet til +/- 15 őľg/m3, eller +/-15% alt efter m√•lingen; i praksis virker den dog til at v√¶re ret stabil i v√¶rdierne. Sensoren beregner PM10 v√¶rdier ud fra PM2.5 v√¶rdier, s√• jeg vil prim√¶rt fokusere p√• analyse af PM2.5 v√¶rdierne. Data er optaget med et interval p√• 5 minutter, men med sensor l√¶sninger ca. hvert 6 sekund der s√• er aggregeret ved gennemsnit (Der er brugt HPMA-1150S0 sensorens “auto-send”).

Sensoren opfanger partikler mindre end 2.5 őľg med en laser.

Br√¶ndeovnens temperatur er m√•lt som foreskrevet af Aduro Smart Response, dvs. i den √łvre del af br√¶ndkammeret p√• vej mod r√łgr√łret. Aduro sensoren sender data i ca. 4 timer. Jeg har defineret at br√¶ndeovnen er i brug, hvis temperaturen er registreret, dvs. afk√łling ogs√• er talt med.

Data der er opsamlet er bl.a. PM10, PM2.5, br√¶ndeovnens temperatur, og str√łmforbrug p√• de 3 faser.

Vi bruger vores brændeovn en hel del i de kolde måneder. Faktisk helt op til halvdelen af tiden:

Det passer meget godt med at vi bruger brændeovnen næsten alt tid vi er hjemme, i de kolde måneder.

Vi t√¶nder op efter forskrifterne og bedste evne; genindfyring sker typisk ved 175C eller 150C ved at l√¶gge 2-3 stykker br√¶nde ind, og √•bne spj√¶ldet (der s√• ved Adurotronic lukker over ca. 6 minutter). Der er naturligvis stor variation i pr√¶cis hvorn√•r der lige bliver genindfyret. Og en sj√¶lden gang imellem glipper opt√¶ndingen, og giver r√łg i stuen. Men generelt opleves fyringen som ganske uproblematisk.

Gennem året har jeg lavet lidt observationer, og min subjektive vurdering for partikelforureningen er ca.:

  • Der er normalt meget lille partikelforurening, 2-3 őľg/m3
  • Ved god opt√¶nding stiger forureningen med 1-2 őľg/m3
  • I nogle perioder er baggrundsforureningen h√łjere, lige under 20 őľg/m3
  • Ved uheldig opf√łrsel stiger partikelforureningen drastisk – helt op til 900 őľg/m3; det kan f.x. v√¶re ved d√•rlig opt√¶nding, eller ved madlavning.


PM2.5 koncentrationer, ifht. årets måneder.

Som det kan ses er der en del variation imellem m√•nederne. Der er ogs√• en hel del outliers, der tr√¶kker gennemsnittet op, mens medianen for alle m√•neder ligger under 5 őľg/m3.

PM2.5 koncentrationer, ifht. br√¶ndeovnens temperatur; r√łde cirkler angiver gennemsnit, r√łd linje angiver kubisk tendenslinje.

Mere interessant er det om partikelforureningen p√•virkes af br√¶ndeovnens temperatur, og dermed dens brug. Det ser det bestemt ud til! Selvom median v√¶rdierne ikke stiger meget stiger specielt 3. kvartil. Gennemsnitsv√¶rdierne stiger ogs√•, helt op til 12.37 őľg/mfor intervallet [250, 300). En tolkning af dette kunne v√¶re at der normalt (median) ikke er ret meget mere partikelforurening, men det sker hyppigere at der er store koncentrationer til stede.

Det b√łr noteres at der ikke er s√¶rlig mange m√•linger over 350C, som det kan ses af histogrammet for hvilke br√¶ndeovnstemperaturer der er registreret:


Der er et par fejlkilder i målingerne:

  • Der mangler en uges data i september, hvor en str√łmforsyning stod af mens vi var p√• ferie.
  • Partikelsensoren giver nogle meget h√łjere m√•linger i et enkelt punkt, engang imellem. Checksummen fra sensoren ser ud til at passe, s√• hvad pr√¶cist problemet er ved jeg ikke. Jeg har f√łrst filtreret √•benlyst forkerte m√•linger (<0 eller >1000) fra i databehandlingen, men pga. gennemsnittet over de 5 min kan nogle √•benlyst forkerte m√•linger stadig v√¶re talt med.
  • Br√¶ndeovnssensor har nok manglet batteri en dag eller to, det kan jeg ikke helt huske.


√Örligt gennemsnit5.44 őľg/m3
– √Örligt gennemsnit, br√¶ndeovn i brug9.28 őľg/m3
– √Örligt gennemsnit, br√¶ndeovn ikke i brug4.49 őľg/m3

Alle v√¶rdier er under EU’s gr√¶nsev√¶rdi, p√• 25 őľg/m3 PM2.5. Hvis vi antager at m√•lingerne mens br√¶ndeovnen ikke er i brug er repr√¶sentative for hele √•ret, s√• har br√¶ndeovnen bidraget med 0.95 őľg/m3 PM2.5 til √•rs gennemsnittet.

Hvor farligt er det så?

Et studie fra 2013 af sammenh√¶ngen mellem partikelforurening og lungekr√¶ft fandt (eftersigende, jeg har ikke adgang til artiklen men kun til resum√©et p√• at selv sm√• stigninger i partikelforurening giver √łget risiko for lungekr√¶ft.

For sm√•kornet luftforurening [PM2.5] stiger risikoen for lungekr√¶ft med 18 procent per fem ekstra mikrogram sv√¶vest√łv, men det resultat var ikke statistisk signifikant. Det var alle resultaterne for risikostigning under det tilladte niveau heller ikke. Sm√• m√¶ngder forurening √łger faren for kr√¶ft

Hvis vi antager at det resultat holder, og at virkningen er line√¶r, vil den √łgede forurening p√• 0.95 őľg/m3 PM2.5 √łge risikoen for lungekr√¶ft med 3.42%.

Enkeltstående tilfælde

Et andet problem kunne v√¶re hvis enkeltst√•ende tilf√¶lde af h√łj luftforurening var specielt sundhedsskadeligt, som indikeret af at EU for PM10 ogs√• har en daglig gr√¶nsev√¶rdi (50 őľg/m3), og et antal tilladte overskridelser per √•r (35). Der er 0 dage hvor den daglige PM10 gr√¶nsev√¶rdi har v√¶ret overskredet. Jeg har alligevel analyseret de 35 dage med det h√łjeste gennemsnit, og fors√łgt at klassificere de √•rsager (prim√¶r og sekund√¶re) til de h√łje v√¶rdier. Det har jeg gjort ved at kigge p√• br√¶ndeovnstemperaturen, str√łmforbruget, tidspunket p√• dagen, osv. Disse tal m√• derfor siges at v√¶re min subjektive vurdering.

Primær årsagSekundær årsag

De prim√¶re √•rsager til h√łje m√•linger ser ud til at v√¶re madlaving og baggrund, mens br√¶ndeovnen bidrager til halvdelen af de h√łje dagsgennemsnit.


Vores moderne br√¶ndeovn bidrager med 0.95 őľg/m3 PM2.5 til √•rs gennemsnittet, og √łger dermed vores risiko for lungekr√¶ft med 3.42%. Hvis vi f.x. flyttede til en st√łrre by som K√łbenhavn ville vi opleve en v√¶sentlig h√łjere for√łgelse til m√•ske 10 őľg/m3, if√łlge modelberegningen, hvilket ville √łge risikoen for lungekr√¶ft med 16%.

Hvis man ser p√• PM2.5 koncentrationer ifht. br√¶ndeovnens temperatur, ser det ud til at br√¶ndeovnen for det meste (m√•lt p√• medianen) ikke udleder ret mange partikler, men bidrager til at h√łje forureningskoncentrationer optr√¶der oftere (som set p√• de √łgede gennemsnitsv√¶rdier, og for√łgede 3. kvartil).

Br√¶ndeovnen bidrager til 18 af de 35 h√łjeste dagsm√•linger, mens de prim√¶re √•rsager til h√łje dagsm√•linger er madlavning og baggrundsforurening.

sep 29

18650 Lithium-ion battery packs ‚Äď 1S80P

Posted on l√łrdag, september 29, 2018 in Hal9k, Planets

This is the considerations I did when building 1S80P 18650 battery packs, for a DIY powerwall.

My design will go for 14 of these packs in series, for a nominal 48V system.

I wanted a design that was:

  • Very hard to short circuit, individual cell fuses, and generally as safe as possible
  • Mechanically stable
  • Balanced as much as possible
  • Expandable

The design is basically 4 4×5 18650 holders for the top and bottom. The cells I used were all tested for capacity (all above 2000 mAh) and self-discharge (all above 4,1V after several weeks/months), and are all Samsung cells. When assembling the packs I tried to mix the cells as much as possible: this should mean that on average the packs will be approximately the same capacity.

The packs have all the positive metal on the top, and the negative on the bottom. This means that any metal would have to touch both the top and the bottom, to short circuit the pack; this is not possible with a straight piece of metal. The connectors are going out on each side: if they went out the same side it would be possible to short-circuit them. Also, this will ensure that all the cells are discharged at the same rate: if they went out the same side the cells closest to the connectors would be loaded harder than the ones further away. This layout will not be a problem when they are put in series, they will just be alternating up-down. The busbars are shrink-wrapped on both ends, so only the connector is connected.

This means that the packs are impossible to short-circuit by themselves.

The packs are held together by 6 zip-ties: 2 at each end, and 2 in the middle. 5mm holes are drilled in the holders. The zip-ties go through the packs and around the busbars on each side.

The busbars are 4 wires of 2.5mm² wires, that are extracted from a standard AC cable. They are twisted together using a bench vise, and a cordless drill. They are then pre-bent using a template.

The connectors are 25mm² cable lugs. The two ends of the busbar go into the lug, meaning 8 wires of 2.5mm², or 20mm² in total. Depending on the exact calculations, this should be good up to 80A-160A. I intend to load the packs with at most 80A, and normally much less, so this should be fine.

The cells are connected to the busbars by fuse-wires. I used legs from 1/8W resistors, from a batch I tested beforehand. The resistor legs blows at 5A after some time, and in a few seconds at 6A. This should be well within spec, since the fuse-wires are mainly intended to isolate cells that go short-circuit: in this case the other 79 cells will be delivering current to the one bad cell, and the fuse wire should blow very quickly. This is another reason to not build too small packs: you need enough current available that the fuses will blow quickly.

The fuse wire is soldered to the cells, and soldered to the busbars. I used good lead-based solder, I tried crappier and lead-free solder but the results were poor. The positive side is soldered at about 340C, while the negative needs a bit more heat at 350C. For soldering to the busbars I go up to 380C, and move around in a circle since heat management is very much needed.

One concern I have heard from several people is that the cells are losing capacity by soldering. I did a test by soldering a few cells, and leaving a few control cells unsoldered. Then I capacity tested all the cells for a few cycles to check if any capacity is lost. I was unable to find any capacity loss on the soldered or unsoldered cells, so for me that is “myth busted”.

The packs are prepared for a future extension to 1s160P or similar. The holders are all oriented in the same way, and in such a way that 2 80P packs should be able to click together side by side:

Each pack (or set of 2 packs if expanded) will get one Batrium LongMon. It should be fully capable of balancing such a system.

If the hivemind has any ideas or things I missed, I’m very interested in hearing about it!