Candle at the Pool

LoRa Washing Machine

Sometimes you cannot choose your project, but the project chooses you. This one is about sending the status of a Home Connect clothes washer to MQTT using LoRa radio communication.

The project can be found at GitHub.

The Problem

It all started when my clothes washer broke down. I replaced it with a new one, one that is also IoT capable by using the Home Connect network. I liked the idea because the machine is located in a shared laundry room at the basement of the building. If I knew about the progress and the remaining time, I could go to the basement and swap the laundry right on time, not too soon and not too late.

However, my WLAN does not reach the basement, so I couldn't connect the washer to the Home Connect cloud. I tried PLC, but that made my DSL connection instable, so it wasn't a solution either. I pondered about buying an LTE router, but the data tariff would cause monthly costs that I wasn't really willing to pay.

Then I discovered LoRa, which is a radio communication technique that is specially designed for Long Range (hence its name) communication, with a range of up to several kilometers (on optimal conditions). It should be easy for LoRa to send data from the basement to my flat, and indeed, a first test was successful.

LoRa solves the problem of transporting the data. However, it comes with a price: The individual data packages are very small (about 50 bytes worst case), and in Europe there is also a 1% duty cycle restriction that needs to be respected. So it wasn't possible to just connect the washer to the Home Connect cloud using LoRa as some kind of WLAN repeater.

Instead of that, I would have to connect to the washer directly, read its state and compress the information to what I actually need, before sending it. The problem is now that the connection between the appliance and the Home Connect cloud is proprietary and encrypted.

I found the solution to that problem in a blog post "hacking your dishwasher" by Trammell Hudson. By smart reverse engineering, Trammell was able to find a way to directly connect to his home appliances, without having to go through the Home Connect cloud. This was the last part of the puzzle that I needed.

Concept

The LoRa32 sender and receiver in their case. With Trammell's work, I was able to connect to my washer and read its current state. Basically, the washer is sending key-value pairs via JSON, where the key seems to be a 16 bit integer, and the value is mostly also an integer, but could also be a boolean or a string. This information can be easily compressed into small LoRa packages, as I mostly need to transport numeric key-value pairs.

So there is a LoRa "sender" at the basement. It spawns a WLAN access point that the washer connects to. It then communicates with the washer, retrieves its state change events, compresses them, and sends them out via LoRa.

In my flat, a LoRa "receiver" uncompresses the information. From it, JSON bodies are generated and sent to my home automation's MQTT queue. The generated JSON bodies resemble those sent by Home Connect. A display that is connected to MQTT shows the current progress and the remaining time of the washer. I will also get a message on my phone when the washer is completed, or if an error has occured.

Implementation

The LoRa32 sender in the basement. For the implementation, I bought two Heltec LoRa32 V2 modules. They are based on an ESP32, with a LoRa module and an OLED on board. With a few modifications to the source, any other Semtech SX1276 based LoRa module can be used. For a proper housing, I created a 3D printed minimal Heltec LoRa32 V2 case.

Thanks to Trammell's hcpy source code, it was surprisingly simple to write a C++ class for the ESP32 that opens a web socket connection to the washer and starts communicating with it.

As mentioned above, the washer is sending JSON messages that contain mostly integer based key-value pairs. To stuff as much information as possible into a single LoRa packet, I came up with a simple compression. The first byte is stating the type of information, followed by a 16-bit integer key, optionally follwed by the value. These are the possible types:

  • 0: Represents the constant 0 (so no value needs to be transported)
  • 1: Represents an unsigned 8-bit integer (so the value consumes 1 byte)
  • 2: Represents a negative unsigned 8-bit integer (the positive value is transported, and then negated on the receiver side)
  • 3,4: The same, but for 16-bit integers (the value consumes 2 bytes)
  • 5,6: The same, but for 32-bit integers (the value consumes 4 bytes)
  • 7: A boolean constant false (so no value needs to be transported)
  • 8: A boolean constant true (so no value needs to be transported)
  • 9: A string (followed by the null-terminated string as value)

These key-value pairs are collected until the LoRa package is full or the sender is flushed. A length byte is added that contains the total length of the pairs, so the receiver is able to unpack all of them again.

To secure the communication, a SHA256 based HMAC is generated. A random 16 bit package number is added as well, which is used by the receiver for acknowledgement. Finally, the package is encrypted using AES256.

The receiver side will unencrypt the package and generate an HMAC, using a shared secret. If the HMAC matches, an acknowledge package with the package number is sent back to the sender. After that, the payload is uncompressed and converted to JSON strings that are sent to MQTT.

It is important to know that the transport encryption is not state-of-the-art. There are several sacrifices that had to be made to keep the LoRa transport small and simple:

  • Only the first 4 bytes of the MAC are used, for space reasons.
  • The RSA256 encryption does not use a mode of operation, mainly because it would be hard to re-synchronize the LoRa connection if a package was lost. On the other hand, we are only sending the washer state. If someone would want to find out whether the washer is running or not, they could just check if a package has been sent within the past minute.
  • The transport is not secured against replay attacks. The receiver should provide a random nonce, which is then used by the sender for the next package. This is something that should definitely be addressed.

So the LoRa connection provides an acceptable encryption, and is also protected against lost packages, since the sender will reattempt to send the package if there was no acknowledgement from the receiver.

Configuration

My MQTT display is showing the current progress (80%) and the remaining time (0:40) of the washer. The trickiest part of the project is probably the configuration.

To directly connect to the Home Connect appliance, an encryption key and (depending on the protocol) an initialization vector is required. Both parts cannot be retrieved by the public Home Connect API, but you need to trick the API into thinking that you are connecting from the Home Connect app. This is where Trammell's hcpy project comes into play. It will let you log into your Home Connect account, and then extract a profile of your appliance and writes it into a config.json file. This file is required for setting up my project.

The config-converter.py in my project will take this config.json file and extract all the necessary parts from it. It will print the appliance's key and iv values for your sender/config.h. It will also create a new random shared secret for the LoRa encryption. And last but not least, it will create a receiver/mapping.cpp file, which is used to convert the integer keys and values to strings similar to the Home Connect API.

If you came this far, you made the hardest part. After that, the LoRa transceivers need to be configured. Unfortunately the parameters depend on the country where the sender is used, so there are no general default settings.

The following values are proposals and are only valid for countries of the EU. You are responsible to find the correct settings for your country. Failure to do so may result in legal problems, claims for damages, and even imprisonment.

  • LORA_BAND: This is the frequency used for LoRa transmissions. For EU countries this is usually 867E6.
  • LORA_POWER: The power of the LoRa sender, in dB. For EU countries this must be 14 or less.
  • LORA_PABOOST: true for EU countries.
  • LORA_SPREADING: The spreading factor. For EU countries, values between 7 and 12 are allowed. Higher values span longer distances, but also exhaust the permitted 1% duty cycle sooner. You should use the lowest possible value that gives a stable LoRa connection, and rather try to enhance reception by finding a better place for the LoRa devices or by using better antennas. The value should be 9 or less, as the duty cycle limit is likely to be exceeded with higher spreading factors.
  • LORA_BANDWIDTH: The bandwidth, must be 125E3 in EU countries.
  • LORA_SYNCWORD: A sync word. You can choose basically any values, or just use the default 0x12.

Make sure that the sender and the receiver are using the same settings, otherwise the transmission will fail.

The other settings are mainly about the WLAN access point for your appliance, the WLAN settings of your home network, and the credentials to access your MQTT server.

And that's it! Actually it was quite a fun project, and I learned a lot about ESP32 programming and LoRa networks. I also spent way too much time with it, but maybe it will pay off because I get the laundry done sooner now.

ZX Spectrum Plus Repair

Let's have a look inside a ZX Spectrum Plus this time. It's basically the same as a ZX Spectrum, but with a (somewhat) better keyboard and a reset button. However, this machine caused a few surprises.

The first surprise was that instead of a serial number, the words "Upgraded" were printed on the case. Next to it there was a (broken) warranty seal from Sinclair Germany. It was completely new to me that Sinclair had actually sold upgrade packages for the ZX Spectrum.

Instead of a serial number, "upgraded" is printed on the case.

Inside the case, I found an Issue Two board, which is a rare sight in a ZX Spectrum Plus. For an obvious reason: The Issue Two heatsink is too big for the Plus case, so the board was somewhat crammed into the case.

An Issue Two board is crammed into the case. The heatsink is twisted to its maximum angle.

As I was going to replace the 7805 with a DC/DC converter anyway, this ugly sight of the twisted heatsink would be solved soon though.

I also found that the keyboard membrane got brittle over the years, and needed replacement with a modern replica.

The keyboard connectors are brittle and broke off.

Technical Check

The very first thing I do is the composite mod. It just takes a bit of wire and a few minutes of soldering, so it's worth to invest the time even if the Speccy should turn out to be irrepairably broken. A first check showed the start screen, so everything seemed to be allright first.

The machine seems to be working.

But then I connected the diagnostics cart, and the trouble started.

This does not look good at all. All RAMs are reported as broken, and there are strange horizontal lines.

The diagnostics reported that all eight lower RAM chips were broken, and the LEDs on the cart showed that the -5V and 12V power lines were missing. A voltmeter confirmed that -5V was gone, and there were only 7V on the 12V line, so the onboard power converter was broken. Strange enough: When I disconnected the diagnostics cart, the system started again, although both voltages were still bad.

I checked the coil, but it showed no short circuit between the windings. So I replaced the usual suspects of a broken power converter: TR4, D15 and D16. I also replaced the 7805 with a DC/DC converter as planned, and recapped all the electrolytic caps. The -5V and 12V were good after that, and all diagnostic tests passed.

Green tint, pale colors, horizontal disturbances. What has happened here?

But now the screen had a strong green tint, the colors were pale, and there were visible horizontal lines.

On Issue Two boards, there are two pots for calibrating the white balance. One just needs to connect a scope to the composite line, and then adjust both pots until the signal noise is reduced to a minimum. However, I only managed to get the green tint a bit better, but it didn't disappear. Also the disturbing horizontal lines stayed. This was the best I could get out of the signal.

But why was the white balance perfect before I fixed the power converter? I later found out that the 12V are necessary for generating the color signal. While the power converter was broken, the 12V were missing, and so the display was presented in a perfect black and white. Since the start screen does not use colors, it just looked good on the first sight. When I restored the 12V line, the color signal was generated again, and the screen got tinted.

I first suspected the ULA, but the problem stayed when I swapped it with a known-good one. Then I swapped the LM1889N. The strange horizontal lines disappeared after that, and overall the colors got better, but were still not perfect.

The pots still couldn't restore a clean white. But then I found that VR1 wasn't really working well, and the signal was crackling when I was turning it. So I replaced it with a Piher one, which almost didn't fit because it is encapsulated. After that, I could finally calibrate the signal to have a minimum noise.

The result was a perfectly white-balanced picture, with the only green things being the passed diagnostics tests.

Much better: Nice colors, perfect white balance, overall good image quality.

For a test, I reinstalled the previous LM1889N, and the color issues and the horizontal lines came back. So the problem with the green screen was a combination of a broken LM1889N and a broken pot.

The repaired board: New capacitors, repaired power converter, U?? and VR? replaced.

The hardware part is done. Let's have a look at the keyboard next.

The Keyboard

The keyboard of the ZX Spectrum Plus is a bit special. On the connector side, there is no difference to the ZX Spectrum keyboard. However, the ZX Spectrum Plus has some more special keys, like cursor keys or a dedicated delete key. These keys need to do two simultaneous keypresses in the correct order. This is done by two membrane layers that are connected to each other. A keypress then closes the contacts on both layers.

These layers are interconnected at the top of the membrane connectors. So it is crucial to do a clean work there, and to make sure that the layers are properly aligned and securely fastened under the brackets. Do not overtighten the screws though. Remember that the plastic is almost 40 years old. ๐Ÿ˜‰

The different layers are interconnected at the top end of the connector. It is crucial to properly align the layers and secure them firmly.

After a bit of cleaning, I could then reassemble the machine.

And that's it. Now I finally also have a ZX Spectrum Plus in my collection.

A ZX Spectrum Plus

New ZX Spectrum 48K

The original Issue 3 board, with some labels explaining the functions of the components. I got this board of a Sinclair ZX Spectrum. It must have been a ZX Spectrum Plus model before, because there was this reset wire attached to it. There were also a few labels that were explaining the functionality of the components in German language, maybe for educational purposes.

I tried to run the diagnostics, but the module didn't even start, and the D0 LED was permanently dark. There must have been a short circuit somewhere on the data bus. But instead of repairing it, my plan was to make a completely new ZX Spectrum from as many new components as possible, with reusing only the ULA, CPU, LM1889N, the coil, and the RAM chips.

So I first removed the valuable components. The stripped original board was a sad sight, but the prospect of making a new Speccy from it made it less painful.

The board, with all valuable components stripped.

I checked the ULA in another Speccy, and it turned out to be fine. From the 16 RAM chips however, only 9 were still functional. This was much less than I expected. I'm having some of those old RAM chips in my stock, but they are precious and hard to find.

A New Board

The new board and some of the components. The new replica board is made by PABB and can be ordered from PCBWay.

For the required components, I assembled a bill of materials. It contains as many new components as I could find, but some rare parts are long out of production. They can still be found as NOS parts at online marketplaces, or they can be replaced with replacement types or replicas (like the Retroleum Nebula or vRetro vLA82).

There are four wire bridges that configure the type of the upper RAM chips, and the brand of the ROM chip manufacturer. The correct configuration can be found in my bill of materials as well.

Instead of the modulator, I decided to use an S-Video mod and a 3D printed base plate. A simple alternative is to just solder an RCA connector to COMP and GND, and use it as a composite output.

After a lot of soldering, the assembly was almost completed. But before seating the valuable chips, I first checked that all three voltages (+5V, +12V, -5V) were present and within their acceptable tolerance.

The replica board, with all components soldered in, but the chips are not seated into their sockets yet.

The S-Video mod takes the place of the original modulator, but is not soldered in, but held by two screws. The screws also provide ground, so they must not be isolating. Three wires then connect the board with +5V, and the composite signal as luma. The chroma signal is connected to the positive end of C65, which must be removed first so the luma and chroma signals won't mix.

The S-Video mod mounted in place.

After that, the new board was finally completed and ready for a first test.

The completed ZX Spectrum replica board with S-Video mod.

Bugfixing

But alas, this is what I was seeing when I powered it up for the first time.

This is what we don't want to see: black and white columns.

The diagnostics showed no action on the CPU bus controls. My suspicion was confirmed when I checked the clock input of the CPU with a scope. It was just a flat line:

The CPU clock is generated by the ULA, but the clock signal was present there.

A look into the schematics shows that between the ULA clock output and the CPU clock input there is the transistor TR3, probably for amplifying the signal. Strange enough, the signal was still present at the right of R24, which is directly connected to the clock output, but at the left of R24 (which is connected to the base of the transistor) the signal was missing already. When I removed TR3, the clock signal appeared there too, so TR3 must have been the cause.

After a longer search, I found out that the Spectrum is very picky about the type used for TR3. The original ZTX313 is not in production anymore, so I used a BC548 first, which is said to be a replacement type, however not at this position. For TR3, the only recommended replacement type is the MPS2369, which is also a bit hard to find now. With that type, the clock signal was finally good (cyan: ULA clock output, yellow: CPU clock input).

And to my joy, the new Spectrum finally started up and showed the famous start screen.

Hello there, Speccy!

The next step was to run a full diagnostics check. Now I got an error that the M1 signal was missing.

Diagnostics complains that the hardware was not found.

The M1 signal is generated by the CPU, and indicates the first of four machine cycles, which is the cycle where the next instruction is read from memory. The Spectrum itself does not use the M1 signal, but a few expansions like the ZX Interface 1 need it.

After replacing the CPU, all diagnostics checks finally passed.

We are green!

So at the bottom line, all I could reuse from the old ZX Spectrum was the ULA, the ROM, the LM1888N and the coil. I was hoping for the RAM chips and the CPU, but I haven't been really lucky with them.

Test Run

Anyway, it was finally time for a test run. I connected the new Speccy to my computer, and used tzxplay to play the tape file of my favorite game, Starquake. It was loading and running fine. Also, the image quality of the S-Video output is excellent, and probably the best one can get from this old design. Only the ZX Spectrum Next has a better quality with its native, pixel perfect HDMI output.

I bought the original board without any case. But luckily, there are replica cases, keymats, membranes, and faceplates on the market, so I could assemble a brand new outerior. Of course, I chose a transparent case, so the nice black mainboard could be seen from the outside. Well, at least a bit.

And there it is, an (almost) new ZX Spectrum in mint condition.

Amiga Debugging with Linux

A "zero modem" adapter between the serial-to-USB and DB9-to-DB25 adapters The AmigaOS offers a debug console as a simple way for debugging. Log data can be written via the linkable Debug.lib, which is also used by all kind of tools like MuForce, Mungwall, or PatchWork. AmigaOS provides a simple internal debugger called ROMWack (which has been replaced by the even simpler SAD in later versions). But also DiagROM is writing diagnostics data via the serial port, which comes in handy when a RAM chip or something in the video area is broken.

The log output is sent to the serial port and can be read by a terminal connected to it. Back in the good old days, not so many hobbyists could afford an actual terminal or a second computer for that, so we used tools like Sushi or Sashimi to redirect the debug output into a Shell window, which worked fine unless the system has crashed too hard.

Today, I assume that almost all of the Amiga owners also have a second computer at home, and if it's just a second Amiga. ๐Ÿ˜‰ This blog article is about how to connect your Amiga to your Linux PC, and get the debug output.

On the hardware side, you will need a construction with a DB25 female port on the one end, and an USB connector on the other end. I use one of those USB-to-Serial converters that can be found on hardware shops for little money. They are often equipped with a DB9 male connector, and are supposed to be connected to peripheral devices (like modems). To connect them to a computer, a so-called zero modem (or null modem) is required, which is just a small adapter that enables to connect two computers directly together by crossing the transmit and receive lines. Finally, we need a DB9-to-DB25 connector with the correct genders, to connect the other end of the zero modem to the Amiga.

This hardware stack is connected to the Amiga's serial port on the one end, and to a USB port of the PC on the other end. Remember to turn off the Amiga before connecting something to the serial port. Unlike USB, the ports of old computers are not designed for connecting or disconnecting devices while the system is powered. It could actually damage the system to do so!

On the software side, we don't need to install drivers on the Amiga. The debug or diagnostics output is just sent to the serial port. On Linux, we can use any terminal emulator. The most prominent is certainly minicom.

The default serial port settings are 9600-8N1 (9,600 bps, 8 bits per character, no parity, 1 stop bit). However, the debug output is just directly sent to the serial port. If you changed the serial parameters on Amiga side, and used the serial.device for something else, the debug output will use the current settings. Handshake must be turned off in any case, though.

Maybe the easiest way is to create a file called ~/.minirc.amiga with the following content (change the pu port value to your actual TTY USB device):

pu port             /dev/ttyUSB0
pu baudrate         9600
pu bits             8
pu parity           N
pu stopbits         1
pu rtscts           No
pu xonxoff          No

On many Linux distributions, the user also needs to be added to the dialout group in order to access a serial device:

sudo usermod -aG dialout $(whoami)

After that, just start minicom with the amiga profile:

minicom amiga

Now you should see all the debug output generated by AmigaOS on your minicom screen. For interactive debuggers like ROMWack, you can also type commands into the console.

To leave minicom, press CTRL-A and then Q. ๐Ÿ˜‰

Amiga CD32

A TTL-to-DB9 converter board and a PS/2 cable connected to it Unlike other Amiga models, the CD32 has no dedicated RS-232 port. Instead of that, it provides a simple serial interface at the Aux port that is connected to Paula's UART pins internally.

To build an adapter, you need a PS/2 cable (e.g. from an extension cord or an old PS/2 input device) and a MAX3232 based TTL-to-DB9 level converter. These converters can be found at online marketplaces for a few Euro.

Cut one end of the cable and connect the wires to the converter like that:

  • Pin 2: TXD
  • Pin 3 (and the shield): GND
  • Pin 4: VCC
  • Pin 6: RXD

Leave the remaining two wires unconnected, and check for correct polarity before connecting the wires to the converter!

The CD32 does not provide any control and handshake signals, but fortunately they are not needed for debugging and diagnostics purposes.