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 , ,

How to Transfer pictures wirelessly for a Sony camera without using Playmemories on MacOS

Sony cameras can transfer photos to PC using a WIFI connection. To implement support on a host they provided software called “Playmemories”. This software was never working very well and eventually was completely abandoned by Sony. After upgrading to macOS Catalina I found that it is not working anymore, without any vendor updates available. So I started to look for alternatives.

How it works

Internally Sony using PTP/IP protocol to transfer files. The device starts sending the UDP packet to the broadcast address when the user choosing the ‘Send to Computer’ option from the camera menu. Software (Playmemories) captures this packet, connects to the camera, and start the sync process.

After the initial network configuration camera needs to be connected to the station using USB to set PTP/IP GUID. This is a one-time operation which is also handled by Playmemories.

OpenSource implementations

I have been able to find 2 working OSS implementations for that:

  • falk0069/sony-pm-alt – using gphoto2 and python wrapper and provides C program to set initial guid on camera.
  • shezi/airmtp – pure Python2 MTP implementation, includes some additional options. Does not provide any way to initialize the camera over USB.

I found that both tools are working fine.


The tool has an initial GUID setter, which using libusb-1.0 to configure GUID on camera. I have been able to compile it on macOS:

clang `pkg-config libusb-1.0 --libs --cflags` sony-guid-setter.c -o sony-guid-setter

However, found that it is useless, as macOS opens USB exclusively when you connecting the camera. To workaround that I compiled this tool in the VirtualBox Linux VM and passed the camera using USB pass-thru functionality. This was working well and I got the camera configured. In my case exact command was sudo ./sony-guid-setter.o 054c:08af -g. This operation needs to be done only once per camera network setup.

To use wireless transfer I installed gphoto2 from brew (brew install gphoto2) and changed PHOTO_DIR in the to the user folder. I found that this application is working fine and able to transfer photos from the camera.


AirMTP is a pure python2 application, so it does not have any external dependencies. Also, it supports many additional sync options, e.g. ability to skip old files, specify extensions to download, etc.

To use it with the camera you need to run it with a command-line like --ipaddress auto --outputdir and start the transfer on the camera.

I found that the performance of both tools is +- the same, so decided to use airmtp as it has no external dependencies and supports more options. To run it on startup I created such plist and places it in the ~/Library/LaunchAgents/airmtp.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">

To enable service do launchctl load -w ~/Library/LaunchAgents/airmtp.plist. Additionally, I added python code which adds macOS notification on camera connect and file transfer. I will publish my changes later.


Thanks to OSS software – i can use camera sync functionality again and this time without vendor lock.

Tagged ,

AWS Client VPN internals

About AWS Client VPN

AWS Client VPN is a managed client-based VPN service provided by AWS. With Client VPN, it is possible to access AWS resources from any location using an OpenVPN-based VPN client.

Recently AWS added ability to use SAML IdP for user authorization and authentication (see image).


However, SAML federation requires you to use proprietary AWS VPN Client which is available only for Windows and macOS. Moreover, the client is a closed source and very limited.

In this post, I will show how it works under the hood and how to connect to it using the native OpenVPN binary.

Update from 25.9.20 – AWS source code found

AWS published changes to the OpenVPN on their S3, with link available in the “About” window. Patch in my repository updated to the official one, everything else is still relevant.

What is wrong with “native AWS client”

  • Available only for the macOS and Windows. OpenVPN itself supports pretty every platform which can establish VPN connections.
  • Closed source. In combination with root access, it adds security risks.
  • VERY limited. No hooks, no timeouts, no log export, nothing at all. Just “import profile” and “Connect”. If things are not working – you have to search for the logs in the /tmp folder (??).
  • Client whitelisting only very few OpenVPN options. If you will try to add any non-whitelisted option to the config – the client will fail to start. It is including inactivity timeout settings, scripting, etc.
  • No documentation about the ability to create customized packages with pre-loaded config

Hopefully, AWS would address some of these limitations in the future and publish source code of their “client”.

How “native client” works

I was using Wireshark and LLDB tools to find out how the client really works. How user flow looks like:

  1. On the first run, you are importing a profile to the AWS VPN Client. Client detecting auth-federate keyword in it and saving config in the ~/.config/AWSVPNClient/OpenVpnConfigs. Special auth-federate keyword is removed at this stage.
  2. The user running the AWS VPN Client and using the “connect” menu to connect.
  3. AWS VPN Client opening a web browser and redirects to the SAML IdP page. After the authorization browser shows “Authentication details received, processing details. You may close this window at any time.” message.
  4. The client connects to the gateway and traffic starts to go via VPN.

Now, let’s take a look at what is going on internally.

  1. The wrapper on Mono starting OpenVPN binary (part of the package) and starts the HTTP server at address.
  2. Using the OpenVPN management interface it is asking to connect to the provided gateway with the username N/A and password ACS::35001. This (of course) fails with an authentication failure, but as failure reason VPN server sends SAML redirect URL.
  3. Wrapper using this URL and opening it in the browser. If SAML flow succeeds – IdP will redirect the browser with POST data to the HTTP POST data contains SAMLResponse field. On this URL mono wrapper capturing it.
  4. Mono wrapper asks OpenVPN to establish connection second time, but now with N/A as username and SAMLResponse + some session data as a password.
  5. AWS VPN server validates them, and if they are looking valid (e.g. signed by corresponding IdP, etc) start the session.

How to connect with OSS OpenVPN to the AWS Client VPN using SAML

I decided to emulate this flow. I started with writing small HTTP server on golang, which listens on and saving SAMLResponse POST form field to the file. The next step was to write a shell wrapper which emulates the activity of the Mono wrapper in the AWS Client. I decided not to use the management interface but to run the OpenVPN binary directly.

Surprisingly I been able to get the connection up, but only with acvc-openvpn binary from the AWS VPN package. So I decided to build OpenVPN myself to debug why it is not working with OSS binary. After some experiments reason was found:

  • Password length in the OSS OpenVPN is up to 128 bytes. SAML response is ~11Kb. I extended this size but got another problem related to the TLS error.
  • After all, I found that the password block is not fitting into TLS_CHANNEL_BUF_SIZE limit in the OpenVPN, so I had to extend it as well.
  • I been able to connect. Eventually i found OpenVPN modified source code and it shows that my changes are similar to AWS one, but they set much higher limits (up to 256Kb). My repo was updated to include AWS patch instead of mine.

Patch is available here. At this point, I was able to connect and use a VPN.


So far my PoC can connect to the VPN Server. After connect, it is working the same way as AWS client. I already tested both TCP and UDP setup, with 443 and 1194 ports. Some things to do (if I will have some time)

  • Make golang wrapper smarter and replace shell wrapper entirely
  • Think how to integrate this with tunnelblick or other OSS UI for the OpenVPN

As usual – patches and contributions are welcome, repository URL is

Tagged , ,

Official MacOS NVME Smart header found

Finally Apple published header of the NVME Smart header (NVMeSMARTLibExternal.h). It was found in the latest XCode update by Harry Mallon, who also provided initial version of the Smartmontools patch to use it. I adopted and fixed this patch, so latest smartmontools version got Log Pages support on macOS. Original Apple header could be found on my gist.

Good news is that my effort to reconstruct API was mostly correct. I also found some functions (GetFieldCounters, GetSystemCounters, GetAlgorithmCounters) not exported officially. Also mystery with non-working GetLogPage function resolved – second parameter is size (as i expected) but in DWORD-s + starting from 0 (e.g. 1 == 2 DWORDS == 64 bits) and there is a strict validation of it.

Tagged , , ,

Migration to Let’s Encrypt V2 API with acmetool

Why acmetool?

A long time ago i migrated from certbot to the acmetool due to its simplicity and much better design. It is still working perfectly, managing many certificates without any headache. The only problem was new (V2) ACME API, which will be mandatory to use starting from July 2020. The development of acmetool is not very active, but at some point, the author provided a new (beta) release with V2 protocol support. The migration process is not documented, so I decided to make this blog post.

How to migrate

I would recommend starting with the backup of the ACME_STATE_DIR directory first. It should be located on /var/lib/acme on Linux and /var/db/acme on the FreeBSD. During migration content of the directory will be changed.

Next thing is to install the new binary. I already updated acmetool FreeBSD port and found that it is also updated in the Debian SID. If your OS does not have it updated yet – binary could be easily build using a recent golang compiler. When the binary upgrade is done – you can run acmetool status and it will show you your existing domains. Now run acmetool quickstart and choose Let's Encrypt (Live v2) server. Continue with configuration. When done – run acmetool status – all your existing domains should use V2 API from now. Last step is to go to the /var/lib/acme/accounts and remove directory started with acme-v01. Run acmetool status again to validate that only the V2 account is now available.

I did it on a number of the Linux and FreeBSD servers and everything went just fine.

Tagged , ,