Integration of the gas leakage detection to the smart home

As I am (still) using a gas boiler I need to monitor gas leakage to ensure that if it happens I am aware before it caused any damage.

In the past, I was using "dumb" sensors with only sound/led indication of the leak. Now, as it was time to replace the old sensor anyway, I decided to try something more intelligent.

Some of the criteria I was using to find the device:

  • It should be able to work autonomously, with a sound leak alarm no matter what.
  • It should use standard WIFI. I already have coverage of the entire house with WIFI and I see very little value in using other wireless protocols and their gateways.
  • It should use open standards and should be able to operate without any kind of cloud connectivity. It is very important to me to have the device fully functional even if the vendor will decide to shut down its own cloud servers or "end of life" the current protocol.

After all, I found that the "Shelly Gas" sensor meets all the criteria – according to specification it supports MQTT, REST and can work without vendor app/cloud connectivity. So, I decided to give it a try.

Device experience

The device seems to be built on the Expressif platform (using Mongoose-OS) and once turned on provides an Access Point to connect. Once connected it is possible to configure the device using a web browser and switch it to client mode. I have a dedicated SSID for home IoT devices, without internet connectivity, so I decided to use it. After configuration device successfully acquired an IP address using DHCP and was available on it for the configuration.

There is not too much to configure – "eco mode", buzzer volume, and 3 actions. Actions are webhooks with user-defined URLs triggered on alarm status change (mild/heavy/gone). There are also settings to enable and configure MQTT and CoIoT protocols. It is also possible to control a smart gas valve with it, however, I do not have such.

Getting data from the device

I decided to integrate the device with Grafana, to build real-time graphs and use its alerting functionality. To push data in the InfluxDB I am already using Telegraf, so it was logical to integrate it with Shelly Gas.

The device itself has an HTTP server that serves UI and API. Vendor provides a very detailed description of the device API.

To grab device data I was using /status HTTP endpoint. Here is a sample reply:

  "wifi_sta": {
    "connected": true,
    "ssid": "IOT",
    "ip": "",
    "rssi": -54
  "cloud": {
    "enabled": false,
    "connected": false
  "mqtt": {
    "connected": false
  "time": "18:22",
  "unixtime": 1675617720,
  "serial": 59,
  "has_update": false,
  "mac": "10521CF2AAAA",
  "cfg_changed_cnt": 0,
  "actions_stats": {
    "skipped": 0
  "gas_sensor": {
    "sensor_state": "normal",
    "self_test_state": "not_completed",
    "alarm_state": "none"
  "concentration": {
    "ppm": 0,
    "is_valid": true
  "valves": [
      "state": "not_connected"
  "update": {
    "status": "unknown",
    "has_update": false,
    "new_version": "",
    "old_version": "20221027-111245/v1.12.1-ga9117d3"
  "ram_total": 52528,
  "ram_free": 39640,
  "fs_size": 233681,
  "fs_free": 92368,
  "uptime": 187828

What I like is that device provides all required data in a single HTTP call and uses JSON format, so it was very easy to integrate it.

Building Grafana dashboard

To get data from the device in the InfluxDB I used the following configuration:

  urls = [
  tagexclude = ["url", "host"]
  data_format = "json_v2"
      measurement_name = "shellygas"
          path = "wifi_sta.ip"
          rename = "ip"
          path = "mac"
          path = "concentration.ppm"
          rename = "concentration"
          type = "int"
          path = "gas_sensor.sensor_state"
          rename = "sensor_state"
          path = "gas_sensor.alarm_state"
          rename = "alarm_state"
          path = "ram_total"
          type = "int"
          path = "ram_free"
          type = "int"
          path = "fs_size"
          type = "int"
          path = "fs_free"
          type = "int"

Once restarted Telegraf started to poll the device every 10s and push data to InfluxDB. Here is a sample dashboard I created:

The exported version could be downloaded from my GitHub.


To test the device I used dust-off compressed gas aerosol. Once the concentration is high enough device starts sound/audio indication and the PPM counter is also increasing. Here is a Grafana output of the test:

More things to do

Connect the device to the Domoticz using the MQTT protocol

  • Configure alerts for the device status and availability
  • Think about integration of the device alerts with SMS (using AWS SNS?) and email


I like the way Shelly built the device – it has cloud integration but it is optional. Support of open and well-documented protocols allowed me to easily integrate it into my setup.



Recently my energy counter was upgraded to ZPA ZE314. I found that reading data with IEC 62056-61 (OBIS) still works as expected, the only difference is that now I need to send not only '\x2F\x3F\x21\x0D\x0A' but also '\x06\x30\x30\x30\x0D\x0A' to start the transfer, so I will have to change my Domoticz integration.

Below is the meaning of the attributes, from different sources:

C.1.0(1234567890)                # Meter serial number
0.0.0(1234567890123456)          # Device address 1 (contract number)
0.3.0(10000*imp/kWh)             # Active energy meter constant (LED)
0.3.3(00100*imp/kWh)             # Active energy meter constant (S1)
F.F(000000)                      # Fatal error meter status
1.8.0(0000003.436*kWh)           # Positive active energy (A+) total [kWh]
1.8.1(0000001.165*kWh)           # Positive active energy (A+) in tariff T1 [kWh]
1.8.2(0000002.271*kWh)           # Positive active energy (A+) in tariff T2 [kWh]
1.8.1-0*(0000001.011*kWh)        # Historical data for the last 14 months
21.8.0(0000001.488*kWh)          # Positive active energy (A+) in phase L1 total [kWh]
41.8.0(0000000.980*kWh)          # Positive active energy (A+) in phase L2 total [kWh]
61.8.0(0000000.973*kWh)          # Positive active energy (A+) in phase L3 total [kWh]
2.8.0(0000001.010*kWh)           # Negative active energy (A+) total [kWh]
2.8.0-0*(0000001.010*kWh)        # Historical data for the last 14 months
22.8.0(0000000.336*kWh)          # Negative active energy (A-) in phase L1 total [kWh]
42.8.0(0000000.336*kWh)          # Negative active energy (A-) in phase L2 total [kWh]
62.8.0(0000000.337*kWh)          # Negative active energy (A-) in phase L3 total [kWh]
1.6.1(0000.30*kW,2301241630)     # Positive active maximum demand (A+) in tariff T1 [kW]
                                 # with date/time of the event (YYMMDDHHMM)
1.6.2(0000.48*kW,2301241745)     # Positive active maximum demand (A+) in tariff T2 [kW]
                                 # with date/time of the event (YYMMDDHHMM)
1.6.1-*(0000.00*kW,0000000000)   # Historical data for the last 14 months
                                 # with date/time of the event (YYMMDDHHMM)
2.6.0(0000.00*kW,0000000000)     # Negative active maximum demand (A-) total [kW]
                                 # with date/time of the event (YYMMDDHHMM)
2.6.0-*(0000.00*kW,0000000000)   # Historical data for the last 14 months
C.8.1(00000038)                  # total time T1
C.8.2(00000128)                  # total time T1
C.8.0(00000201)                  # total operation time
C.82.0(00000001)                 # Operating period in total (RRMMDDhh)
C.7.1(00000000)                  # PowerOutage_L1
C.7.2(00000000)                  # PowerOutage_L2
C.7.3(00000000)                  # PowerOutage_L3
C.7.1-*(00000000)                # Historical data for the last 14 months
C.7.2-*(00000000)                # Historical data for the last 14 months
C.7.3-*(00000000)                # Historical data for the last 14 months
0.2.1(ver.01, 180717, 3231)      # Parameters scheme ID
C.2.1(2206070830)                # Event parameters change - timestamp. 
C.2.9(2206070236)                # Date and time of the last read-out. 
0.9.2(230124)                    # Date (YY.MM.DD or DD.MM.YY)
0.9.1(180202)                    # Current time (hh:mm:ss)
C.51.15(0000000000)              # Event RTC (Real Time Clock) set - counter
82.8.1-*(2301241611)             # Terminal cover removal timestamps history (??)
C.6.0(0007260911)                # Time on battery
C.6.3(3.68V)                     # RTC battery voltage

Tagged ,

Converting TD-W8970B + Huawei e3372h to the WIFI LTE Router

Why to do that way

I had an old and long-time unused DSL router TP-Link TD-W8970B v1 collecting dust on my shelf. And USB LTE dongle from Huawei, E3372H which I rarely used as a backup connection. As TP-Link also had 2 USB ports I decided to convert it from DSL to LTE using this dongle.

Initially, I tried with stock firmware (the last update from 2014) and it was not working. It supports only old, 3G modems and AT based conversations. New USB modems are emulating network cards. So I decided to give the device the last chance and switch it to OpenWRT. It does not make a lot of sense from the practical point of view (routers are cheap) but it was a great practice 🙂

Broken serial port

To start the experiment I decided to start with the soldering serial port on the router. That was a bad surprise, none of my UART TTL adapters have been able to get any output from it. Led on the UART was blinking but nothing was on the terminal. I tried with 3 different UART/TTL to USB adapters with the same outcome, and as the loop test was passing just fine it was clear that the problem is with board hardware. So I decided to give it a last chance with a cheap USB Logic analyzer and PulseView as a UART decoder. Surprisingly it was working! Using 500Khz sample rate and default (115200/8N1) decoder settings I was able to read from the port. Likely analog circuit in the serial is kind of broken and not generating TTL levels expected by the UART adapter, but still readable with Logic Analyzer. So at least we have now the read-only port, hooray.

Hacking the router

As I had read only access to console I decided to use web interface hack method from the OpenWRT wiki. Idea is to modify the backup configuration which will force the router to run telnetd. I was not able to get it working with Python code, but the java app was working just fine. And I was able to telnet to the stock firmware and login as a root user. So I can try to install OpenWRT firmware to it 🙂

Bricking and debricking

As the article suggests I backed up all mtd (mtd0-mtd6) partitions to the FAT32 flash using the telnet interface. I decided to install OpenWRT by using hack from a similar deviceby writing part of the firmware to mtd1 and rest of it to the mtd2 using dd and cat command. Loader is living in mtd0, so we are not overriding it. For me the result was a broken router – it seems that this is not a reliable way to flash it. From UART I saw that u-boot fails to unpack the kernel image, which makes me think that flash was written with errors.

To de-brick the router I used the ch340 programmer. Initially, I desoldered flash (don’t do that :)) but later found that it perfectly works with a clip when soldered on the board. I flashed old firmware (by doing cat mtd0 mtd1 mtd2 mtd3 mtd4 mtd5 mtd6 > fw.bin) to the flash, soldered it back and the router started normally.

Finally flashing OpenWRT

As the router (with the telnet hack) was online again, I decided to flash OpenWRT using CH340. To generate an image I replaced mtd1 and mtd2 with OpenWrt squashfs image. Partition mtd2 was padded by zeros to ensure that size matched the original one. So I flashed it with fw-new.bin, enabled serial console monitoring and in a minute OpenWRT was started! I used the version from the article (15.05) as it was tested already and I have no idea if this ancient device is not broken in the recent builds. When the device started I was able to telnet to the from the one port. I had to install some packages from the OpenWRT RNDIS user guide to make OS aware of the USB LTE Dongle. After reboot dongle was recognized as eth1, so I was able to assign it as a WAN device using Luci. I tested performance and it seems that I can get dongle speed (~30Mbit on this plan) using wifi, so the problem is solved and the device got a new life. Was it easier to just buy a new router? Likely yes. But here I had much more fun )

Tagged , ,

Booting FreeBSD 13.1/arm64 on MacOS/arm64 using QEMU

After upgrading to Mac M1 I decided to run FreeBSD virtually. As VirtualBox is not supported on this host (and likely will never be) QEMU was the only possible choice.

I found great gist post on how to do this with patched QEMU 6. The good news is that now QEMU 7 is released and it includes all proposed patches + some other related fixes. This note just updating the gist above for the latest QEMU and FreeBSD versions.

Also, I removed the custom edk2 download as it seems that supplied one is just good enough now.

Setup instructions

The easiest way to set up QEMU now is to use Homebrew. When done:

  • Install qemu with brew install qemu command

  • Create some directory to place the FreeBSD image and unpack it:

    mkdir -p ~/local/FreeBSD13 && cd ~/local/FreeBSD13
    unxz -k FreeBSD-13.1-RELEASE-arm64-aarch64.raw.xz
  • Finally create a shell script to run it:

    qemu-system-aarch64 \
      -M virt \
      -drive file=/opt/homebrew/share/qemu/edk2-aarch64-code.fd,format=raw,if=pflash,readonly=on \
      -accel hvf \
      -cpu host \
      -smp 4 \
      -m 8G \
      -drive file=FreeBSD-13.1-RELEASE-arm64-aarch64.raw,if=virtio,cache=writethrough,format=raw \
      -serial mon:stdio \

That should be it. Running should bring you into the FreeBSD console with a working network and SMP 🙂 Login is root, and the password is empty by default.

Resizing the root disk

If you want to add some disk space to the image it could be done easily. Stop FreeBSD and run this command on the host to add 2G to the image:

qemu-img resize FreeBSD-13.1-RELEASE-arm64-aarch64.raw +2G

Now boot FreeBSD normally. To grow root partition run such commands inside it:

gpart recover vtbd0
gpart resize -i 3 vtbd0
growfs /

Trying to build world and kernel

To check that system is fast and reliable enough I decided to rebuild the kernel/world on it.

tar -C / -xvf src.txz
make -j4 buildkernel
>>> Kernel(s)  GENERIC built in 209 seconds, ncpu: 4, make -j4
make -j4 buildworld
>>> World built in 2364 seconds, ncpu: 4, make -j4

As you could see both steps succeed in a reasonable time, which makes this option suitable for the development/testing.

Reducing host CPU usage

Only issue found is a high host CPU usage when FreeBSD guest is running. I was able to find solution for this in the freebsd-arm mail list. Line kern.hz=100 should be added to the /boot/loader.conf.

Tagged , ,

Using AM2320B sensor with a FreeBSD/Raspberry Pi

Some time ago my DHT22 sensor, connected to the RPi1, died (humidity always was showing 99%), so I decided to replace it. I found a cheap AM2320 sensor that was in the same case as my old DHT, so I ordered it. When it arrived I found that its used 4 PINS instead of 3 and I2C protocol instead of simple 1-Wire in DHT. So I had to spend some time learning how to get the data from it.

Enabling I2C bus and connecting sensor.

In the config.txt located on FAT partition dtparam=i2c_arm=on line if its not there and reboot RPi. You should see something like that in the dmesg output:

root@rpi:/home/freebsd/ # dmesg | grep -i i2c
iicbus0:  on iichb0
iic0:  on iicbus0

If you see these lines it means that FreeBSD enabled RPi I2C bus on pins 3 (SDA) and 5 (SCL). You will also need to connect GND and VCC pins, I was using 3.3V, the chip should accept both according to the spec. See pinout on the picture:

Once connected you can run the scan command. This chip has an automatic sleep feature and does not respond to commands in this state, so the scan needs to be run twice:

root@rpi:~ # i2c -s ; i2c -s
Hardware may not support START/STOP scanning; trying less-reliable read method.
Scanning I2C devices on /dev/iic0: 
Hardware may not support START/STOP scanning; trying less-reliable read method.
Scanning I2C devices on /dev/iic0: 5c

As you could see – on the second run we got 0x5c address used by AM2320B.

Finally getting data from the sensor

As chip requires very strict timing before requests we cant use the i2c tool to get actual values from it. Also, the protocol has a checksum which we should calculate and data is collected as 16-bit integers. To get it in the human-readable form code needs to be written. And I found one for Linux at repository. FreeBSD uses a slightly different way to work with I2C from userland (we need to use I2CRDWR ioctl) so I ported it to BSD and left all the logic the same. And it now finally works as expected:

root@rpi:~freebsd/libi2c # ./am2320
Temperature 16.1 [C]
Humidity    58.8 [%]

Domoticz integration

To work with domoticz i am using LUA scripting. I think I am going to port to the FreeBSD to avoid calling external binary on every reading. It is also possible to do a tiny kernel driver for that, but it is not something I like, IMO userspace is a better space for it.

Update: 1-Wire mode and datasheet

After finishing this article and i2c code I found a datasheet for this chip. Chip support both i2c and old, legacy 1-wire when SCL is connected to Ground and SDA used as "data" line. Anyway, i2c seems to me more standard solution.

Tagged , ,

Replacing faulty power supply in the DGS-1210-10P D-Link switch

Recently one of my house switches with PoE devices connected decided to die. Status LED was blinking and no PoE was supplied to devices. I replaced it (as it is hard to live without internet these days) but then decided to see if it is fixable. The switch is 1Gb 8+2 Port with PoE, and not that old to just recycle it.

After opening, I found that there are 2 boards: logical one and PSU. On PSU there was a label that it should supply 54V (used in PoE) and 12V (used for the mainboard itself). 54V line was faulty, it was unstable and likely caused this cycled reboot.

I was not able to find the PSU schematic and it had a lot of SMD elements and compound all-around. Also, I found that it’s a typical problem for this switch to have PSU failure, so after all, I decided that it would be better just to replace it.

After all i replaced it with 54V 6.5A PSU and DC/DC 48/12V converter connected to the 54V output. It supports up to 60V, so 54V is in range. It is enough space to put a new PSU instead of original one:

After wiring all together switch started to work again, here is a test with a Cisco phone connected:

Now PSU is much more powerful compared to the original one and hopefully will last longer. So if your D-Link is failing like mine there is an easy way to fix it.


Adding web based remote control to my Marantz amplifier

In one of my previous blog posts I explained how to emulate Marantz remote control with Arduino using "remote in" socket. Now I decided to do something useful about it. My final goal is:

  • Allow managing a device from any mobile or tablet
  • Automatically power off the device if needed (e.g. at 4 AM)
  • Could also be used for alarm functionality
  • Better integrate with other devices, e.g. to turn on the radio if "Tuner" input is selected


To support WIFI and be able to run a web server I decided to use the ESP32 platform, which is widely available and supported by Arduino IDE. Initially, I tried to connect GPIO12 output directly to the "Remote In" socket. This was working fine but completely blocked IR. So I had to add a diode, to avoid current from the amplifier to the ESP32. This solved the problem. As a power supply, I am using an old mobile charger with micro-USB output.

To implement HTTP i am using ESPAsyncWebServer project. Web UI is done using simple HTML/CSS/JS.

One of the early problems I found was that connection latency was very unstable, from 10 to 100ms. It was solved when I disabled power saving on the wireless interface, now it is within 1-5ms.


Source code

Source code is published in my github repo. To compile it – add spressif/arduino-esp32 and ESPAsyncWebServer manually.



To do

Something I may add in the future:

  • Web interface for the sleep timer
  • Maybe also wire device status to the ESP32. This would require some modification of the amplifier, so not sure if I want to do it.
  • MQTT for the better IoT integration
  • May be OTA, just to learn how it should work

Comments are welcome

Tagged , ,

Using smartmontools with LSI raid on FreeBSD

Working on 5 years old issue

A long time ago I got a report that mrsas driver does not allow to get SMART disk data with smartctl/FreeBSD. With previous driver generation (mfi) it was not an issue as it was exporting all physical devices as /dev/passX using undocumented mfip.ko kernel module. However, it was not the case for the mrsas which does not have this functionality. Smartmontools on Linux was using proprietary LSI ioctl calls to access the data, so I was curious if something like that could be possible on FreeBSD. As my first, quick, and dirty attempt I tried to use the same ioctl structures as on Linux. The server silently rebooted after the disk IDENTIFY command, not too much fun. So I abandoned it.

Second attempt – dtrace helps

Recently I received a few more comments about problems with disks on mrsas controller, so decided to take a look one more time. There are 3 utilities to configure these adapters on FreeBSD: mfiutil (opensource, part of the base), MegaCLI (proprietary, FreeBSD/amd64) and StorCLI (proprietary, FreeBSD/amd64). As mfiutil was supporting only MFI devices I focused on StorCLI and MegaCLI.

Thanks to dtrace ability to trace userland and kernel code to fetch ioctl structures – I was able to reverse the protocol. It has some significant differences with Linux code, but the principles are the same. There is a way to issue path-thru SCSI commands to disks and also controller-related commands to get configuration, disk id-s, etc. I also found that both tools use the same ioctl call (MFI_CMD) and that mrsas and mfi logic seems to be 100% the same. So I implemented it in smartmontools. This time server was not rebooting (however, while experimenting I was able to hang the RAID card) and eventually it started to work as expected. Also, I got confirmation from the mrsas owner that it also works for him. Code is now landed to the main branch.

Some examples

Getting a list of the devices:

# ./smartctl --scan-open
/dev/cd0 -d atacam # /dev/cd0, ATA device
/dev/mfi0 -d sat+megaraid,9 # /dev/mfi0 [megaraid_disk_09] [SAT], ATA device
/dev/mfi0 -d sat+megaraid,10 # /dev/mfi0 [megaraid_disk_10] [SAT], ATA device
/dev/mfi0 -d sat+megaraid,11 # /dev/mfi0 [megaraid_disk_11] [SAT], ATA device
/dev/mfi0 -d sat+megaraid,12 # /dev/mfi0 [megaraid_disk_12] [SAT], ATA device

Accessing information about one of the drives:

# ./smartctl  -d megaraid,10 /dev/mfi0  -q noserial  -i
smartctl 7.3 (build date Nov 23 2021) [FreeBSD 13.0-RELEASE-p4 amd64] (local build)
Copyright (C) 2002-21, Bruce Allen, Christian Franke,

Model Family:     Western Digital Scorpio Blue Serial ATA (AF)
Device Model:     WDC WD10TPVT-00U4RT1
Firmware Version: 01.01A01
User Capacity:    1,000,204,886,016 bytes [1.00 TB]
Sector Sizes:     512 bytes logical, 4096 bytes physical
Rotation Rate:    5200 rpm
Device is:        In smartctl database
ATA Version is:   ATA8-ACS (minor revision not indicated)
SATA Version is:  SATA 2.6, 3.0 Gb/s (current: 3.0 Gb/s)
Local Time is:    Tue Nov 23 13:12:41 2021 UTC
SMART support is: Available - device has SMART capability.
SMART support is: Enabled

Running self-test:

# ./smartctl  -d megaraid,10 /dev/mfi0  -t short
smartctl 7.3 (build date Nov 23 2021) [FreeBSD 13.0-RELEASE-p4 amd64] (local build)
Copyright (C) 2002-21, Bruce Allen, Christian Franke,

Sending command: "Execute SMART Short self-test routine immediately in off-line mode".
Drive command "Execute SMART Short self-test routine immediately in off-line mode" successful.
Testing has begun.
Please wait 2 minutes for test to complete.
Test will complete after Tue Nov 23 13:20:00 2021 UTC
Use smartctl -X to abort test.


Code seems to work as expected, but as usual – use it at your own risk. Feedback, as always, is welcome. This functionality will be released in the next smartmontools version, so build from source or use CI builds if you want to try it now.

Tagged , ,

Controlling Marantz amplifier using Arduino via “Remote” socket

Recently I decided to control my old Marantz PM-62 programmatically. There were a few reasons to do so:

  1. Original remote control died and "compatible replacements" were not good enough.
  2. I want to build more complex integration and routing for audio/video, so amplifier input switching would be a part of it.
  3. It is fun 🙂

Playing with "Remote" socket

I found that there are 2 standard RCA sockets named "Remote IN/OUT" on the back panel. So I connected with the oscilloscope to the OUT plug and tried to use different buttons on the remote control. Soon I understood that this output shows signals received from the IR sensor, after amplifier (impulses are about 6V) and without carrier frequency. An interesting fact is that it sends here all commands, including one unit cant understand. Also, I found that there are 2 very different types of codes, with different lengths and impulse counts. I was also able to use a cheap Saleae clone (8ch, 24MHz) with a PulseView instead of the oscilloscope.

Native format and scan codes

I identified that Marantz using 2 different ways to encode the signal.

  • Standard RC-5 encoding with 14 bits of data. Pulseview can decode such data using the IR RC-5 decoder. It has 2 fields – address to choose the device and command to send.
  • Marantz RC-5 extension – this adds one more field, called extension. Also, this format adds pause after the first 8 bits, likely to simplify the identification of it. This format is not supported by PulseView protocol decoders.
  • Both formats require at least 2 packets sent with Toggle bit flipped. I spent a lot of time trying to understand why it is not working for me until found that.
Sample of the decoded RC-5 command (“CD Input”)
Same from oscilloscope

After all, I have been able to identify all scan codes from the original RC:

Name Address Command Extension
Standby 16 12
Phono 21 63
CD 20 63
Tuner 17 63
AUX 1 16 00 06
AUX 2 16 00 07
DCC ON/OFF 23 63
Volume + 16 16
Volume – 16 17

Finally making hardware to control it

I had a few Arduino mini clones, so decided to start with it. Initially, I started with Arduino IRremote library but soon found that impulses it generating are not something I am looking for:

  • By default, it is re-creating carrier PWM impulses needed for real (wireless) IR transmit. This could be disabled by the USE_NO_SEND_PWM definition, but that causing an inverted signal with active HIGH. The amplifier was not able to recognize such a signal.
  • There is no code for 20bit RC-5 anyway

So I decided to quickly write my replacement, without using this library. It is done and published on my gist. This code does not require any external libraries and can send RC5 and RC5-20bit to the amplifier in the way it understands it. Some code is taken from Arduino-IRremote. I also did a patch to PulseView decoder to understand Marantz’s format, which I will PR to the upstream later.

To control the device you need to connect the RCA jack to GND and D5 pins. Once the program is loaded it will switch amplifier inputs in a loop. I found that TTL levels from ATmega168 are enough to control the device without the need to use any TTL converters or additional transistors

Next steps

I think I will try to use ESP32 for the same purpose and will find an Android application to control the device from the smartphone if needed. Also will look for the replacement of the original remote control which is smart enough to learn or program all codes I found. There are power sockets on the backside, so it should be trivial to add some 5v PSU to control have it running. Maybe later will also do some XMBC integration to make it feel more native. Maybe I will also add decoding functionality to my gateway to understand commands from other RC-s. I tried to use decode functions from the Arduino-IRremote but they are not working properly, likely due to the same difference as with sending.

Tagged , , ,

ESP32 based old clock controller, with NTP sync

I have been able to find an old “slave” clock from 1960 (according to the serial number).
It is Pragotron PJ 27, 12V version. They have been produced in Czechoslovakia during
196x years and using the PS-1 mechanism.

This type of clocks usually been used in organizations with central time management systems, e.g. schools, factories, etc. To drive them initially big mechanical “master” clock has been used, later such systems been replaced by digital one.

The clock mechanism is very simple and expects 1m impulse with different polarity on every run to move the arrows. There are also 6V, 24V, and 60V versions.

Making new clock controller hardware

After cleaning up clocks from the dust I tested them with a 12V power supply and found
that they are still working. As I don`t have any master clock to drive them – I
decided to build my controller. I had already an ESP32 controller with an OLED screen
on-board, so decided to utilize it. Also, I found a 220V → 12V/1A power supply left
from another project.

To generate 12V impulses with different polarities i decided to buy L298N DC Motor Driver Module.
It should be more reliable compared to a set of relays, polarity could be set using
TTL inputs and the module itself is very cheap (1-2$). Also, it provides a 12v → 5v converter,
so we can power our ESP32 board from it. I am currently using only one channel to drive clocks,
the second channel could be used for the alarm or clock in a different timezone.

Wiring, in this case, was very simple: 12V is wired directly to the L298N controller, 5V L298N
output and GND → to the ESP32 5V and GND, and L298N IN1 and IN2 are connected to the GPIO
pins 12 and 13. Clocks are connected to the L298N OUT1 motor output.

Software part

As ESP32 does not have real RTC I decided to use NTP over WIFI as a precise time source. This way I can avoid using an additional RTC module with battery.
To store slave clock status I am using ESP32 flash. I built software for the controller
using the Arduino IDE. Some of the features implemented:

  • On boot, it connects to WIFI and using NTP to get actual time. After initial sync
    time is updated from NTP every 5 minutes.
  • Timezone support is implemented using Timezone.
  • Actual time, slave clock status, wifi status, and NTP sync status is displayed on the OLED screen.
  • There is a special “init” mode which is enabled by touching GPIO15 and reboot.
    In this mode, impulses are generated every second. When the slave clock set to 12:00
    the pin needs to be released. This mode is useful for the initial setup or testing of the slave.
  • State is saved every minute to the ESP32 Flash using the “Preferences” library. It is requesting
    “nvs” partition which implements basic wear leveling. To make it more efficient I changed the NVS partition size to 1Mb.
  • To avoid OLED degradation in a 10m screen goes to the “screen-saver” mode. To exit screensaver – touch GPIO15 pin.

Code is available on the samm-git/clock-controller-esp github repo, comments are welcome.


It was an interesting journey to add NTP and wifi support to the device from 1960.
I was able to find a case from the old-time relay which now hosts my controller.
Maybe in the future, I will add more devices to this master.

Tagged , ,