From Zero to Cloud: Building a Smart Meter Network Bridge with NetBSD

Now to the first practical example. We have had two smart meters from the energy supply company installed in our metering station for around two years. These meters have an optical interface on which a read head can be placed magnetically and without contact. The data stream supplied by the meter can be transferred via this read head, which provides information on meter readings and current consumption. This is a prerequisite for the storage, analysis and optimization of consumption data. Read heads are available as a kit with USB connection. These are essentially USB serial adapter logic. In my case, the read data is not to be processed on site but made available for collection by a separate system that is connected via a network. We will therefore configure a Raspberry Pi here, which will act as a smart meter to network bridge. It will be shown how such a simple requirement can be implemented using only NetBSD’s built-in capabilities.

Hardware Setup

A Raspberry Pi 2 Model B is used, which has been lying unused in the cupboard for a few years. Two USB-IEC adapters are connected to it via a USB interface each. The Raspberry Pi is also connected to the LAN via the built-in network interface.

setup setup

Writing the NetBSD OS Image to the SD Card

The operating system of the Raspberry Pi - NetBSD - is loaded from an SD memory card. Consequently, the corresponding image must first be written to a suitable card. On Linux or any other Unix-like operating system, this can be done easily with on-board resources:

$ sudo dd if=armv7.img of=/dev/sdd bs=4M
319+1 records in
319+1 records out
1339031552 bytes (1,3 GB, 1,2 GiB) copied, 320,436 s, 4,2 MB/s

In our example, the SD-Card is assigned to the device /dev/sdd. This will probably different on your system. Please check before proceeding!

The memory card prepared in this way is in principle already bootable. If an HDMI screen and a keyboard are connected to the Raspberry Pi, you can boot from it and log in with the root user.

System Access Considerations

In our case, we want to operate the Raspberry Pi headless, i.e. apart from the network cable and our sensors, no other peripherals are present. Logging into the system will therefore take place via the network using SSH. For this to work, a login user must be created in the system.

Anatomy of the NetBSD OS Image

The image just written to the memory card has two partitions. A small FAT32 partition with the boot loader and a larger one with the actual NetBSD file system (FFSv2).

$ sudo sfdisk -l /dev/sdd
Festplatte /dev/sdd: 3,72 GiB, 3991928832 Bytes, 7796736 Sektoren
Festplattenmodell: USB    CRW-SD   
Einheiten: Sektoren von 1 * 512 = 512 Bytes
Sektorgröße (logisch/physikalisch): 512 Bytes / 512 Bytes
E/A-Größe (minimal/optimal): 512 Bytes / 512 Bytes
Festplattenbezeichnungstyp: dos
Festplattenbezeichner: 0x00000000

Gerät      Boot Anfang    Ende Sektoren Größe Kn Typ
/dev/sdd1  *     32768  196607   163840   80M  c W95 FAT32 (LBA)
/dev/sdd2       196608 2615295  2418688  1,2G a9 NetBSD

The size of the NetBSD partition corresponds to the size of the partition with which the image was created during the build. When booting from the SD card for the first time, the partition is automatically enlarged to the full size of the memory card, creating additional free space for user data.

Creating a Login User

A login user is created indirectly. This means that we will first create a special file in the boot partition in which we record instructions on how the login user is to be created. This change will then be made in the NetBSD partition on the first boot.

First, the SD card must be mounted:

$ sudo mount /dev/sdd1 /mnt

A look at the table of contents shows that not only the boot loader and its configuration files are stored in the FAT32 partition, but also the NetBSD kernel. This means that the entire startup up to the mounting of the root file system takes place from this partition:

$ ls -lah /mnt/
insgesamt 24M
drwxr-xr-x  4 root root 2,0K Jan  1  1970 ./
drwxr-xr-x 22 root root  250 Mai 10 09:21 ../
drwxr-xr-x  3 root root 1,0K Mai  9 11:33 EFI/
-rwxr-xr-x  1 root root 1,6K Mai  9 11:33 LICENCE.broadcom*
-rwxr-xr-x  1 root root  129 Mai  9 11:33 boot.cmd*
-rwxr-xr-x  1 root root  227 Mai  9 11:33 boot.ini*
-rwxr-xr-x  1 root root  201 Mai  9 11:33 boot.scr*
-rwxr-xr-x  1 root root  52K Mai  9 11:33 bootcode.bin*
-rwxr-xr-x  1 root root  115 Mai  9 11:33 cmdline.txt*
-rwxr-xr-x  1 root root  350 Mai  9 11:33 config.txt*
drwxr-xr-x  2 root root  59K Mai  9 11:33 dtb/
-rwxr-xr-x  1 root root 7,1K Mai  9 11:33 fixup.dat*
-rwxr-xr-x  1 root root 3,2K Mai  9 11:33 fixup_cd.dat*
-rwxr-xr-x  1 root root 9,9M Mai  9 11:33 netbsd-GENERIC.img*
-rwxr-xr-x  1 root root 9,9M Mai  9 11:33 netbsd-GENERIC.ub*
-rwxr-xr-x  1 root root 2,9M Mai  9 11:33 start.elf*
-rwxr-xr-x  1 root root 790K Mai  9 11:33 start_cd.elf*

Now we come to the creation of the login user. To do this, we create a new “creds.txt” file:

$ sudo vi /mnt/creds.txt

  useradd user sm4rt!m3ter

The example above causes a user to be created with the name “user” and the password behind it.

Afterwards, only the SD card needs to be unmounted. It can then be inserted into the Raspberry Pi.

$ sudo umount /mnt

First Boot Preparations

When booting up for the first time, it is recommended to connect the HDMI monitor and keyboard for checking purposes.

setup

First Boot

The first boot takes a little longer as various one-off tasks are running, such as creating the SSH host key and enlarging the NetBSD partition. An automatic reboot also takes place in between. Once all this has been completed, it should be possible to log in with the predefined login user.

setup

First Network Login

The device should now be accessible via SSH in the network. As NetBSD has a built-in multicast DNS service (mDNS), this is usually possible via the host name “armv7.local”. The host name can of course be changed later.

$ ssh user@armv7.local
(user@armv7.local) Password for user@armv7:
Last login: Thu May  9 11:36:38 2024
NetBSD 10.0_STABLE (GENERIC) #0: Thu May  9 13:33:17 CEST 2024

Welcome to NetBSD!

armv7$ 

setup

Identify and Verify Sensors

Now it’s time to connect the USB read heads. Once this has been done, they can be identified via a command in the system:

armv7$ su
armv7# usbdevs
addr 1: DWC2 root hub, NetBSD
 addr 2: product 9514, vendor 0424
  addr 3: product ec00, vendor 0424
  addr 4: CP2102N USB to UART Bridge Controller, Silicon Labs
  addr 5: CP2102N USB to UART Bridge Controller, Silicon Labs

If these are the only two USB serial interfaces, they can be accessed via the device files /dev/ttyU0 and /dev/ttyU1. A first test is to use the cu command to access them read-only and consume the data stream:

armv7# cu -l /dev/ttyU0 -s 9600 | hexdump

Similar data records should now be displayed every second or so.

Hint: You can use escape sequence ~~. to break out of cu!

Providing a Network Service

So far, we have only accessed the USB serial interfaces locally. This is now set to change. We use the so-called Internet Superserver from the NetBSD base system to bind the cu command to a network socket, which can then be used to access the same data from any computer in the network.

As our Smart Meter Network Bridge is not a standard service, there is no service definition for it yet. No problem - we can simply create it ourselves:

armv7# vi /etc/services

  ttyU0-srv       4000/tcp
  ttyU1-srv       4001/tcp

The service name “ttyU0-srv” can be freely chosen. This is followed by the port, e.g. port 4000 TCP.

The cache of the service definitions must then be updated:

armv7# services_mkdb

Now we come to the configuration of the Internet Superserver (inetd for short). Put simply, this enables any programs that normally communicate via stdin/stdout to be made network-capable. To do this, inetd listens to the configured service ports and starts an instance of the configured program when a connection is established and communicates with stdin/stdout via the socket until it is terminated.

armv7# vi /etc/inetd.conf

  ttyU0-srv       stream  tcp     nowait:600      root    /usr/bin/cu     cu -l /dev/ttyU0 -s 9600
  ttyU1-srv       stream  tcp     nowait:600      root    /usr/bin/cu     cu -l /dev/ttyU1 -s 9600

The inetd must then be activated in the runconf and started for the first time:

armv7# echo "inetd=YES" >> /etc/rc.conf
armv7# service inetd start

inetd=YES seems to be the default - so not necessarily needed to be set here

Verification of the Network Service

Last but not least, let’s test the new network service. To do this, we connect to one of the two ports from any other computer in the network:

$ nc armv7.local 4000 | hexdump

This should now give the same results as our previous local experiment with cu.

Security Consideration

The solution presented operates at a very low level and shows how easy it is to create integrated solution modules with basic NetBSD functionalities. For production-ready use within a closed network (LAN), perimeter security should be provided as a minimum. This includes the use of the mechanisms of /etc/hosts.allow and /etc/hosts.deny as well as an appropriate configuration of the IP packet filter (npf). Furthermore, the encryption of communication paths - for example via IPSec or Wireguard - could be decisive for secure use beyond the boundaries of local networks. NetBSD provides all you need as part of its base system.

Admittedly, the Raspberry Pi is somewhat overpowered for this relatively simple interface technology alone. An ESP8266-based microcontroller - for example with NodeMCU - could also perform the same task. However, if pre-processing or parsing of the data is to take place directly on the interface, the Raspberry Pi would be preferable. It would be conceivable to implement an SML parser in Python that stores time series of the measured values in a local, rotating cache on an embedded database. From there, they could be offered to client systems via an authenticated web service using a REST API. It would also be possible to implement a complete and standalone energy monitoring system for home applications.