Adding a ks0212 relay board to the mqtt universe

Weatherstation with raspi
Weatherstation with raspi

Adding the 4 channel relay board ks0212 to the MQTT universe

We just hacked a trotec dehumidifier for Herwigs Observatory. The idea was to additionally activate the dehumidifier when the difference between outside and inside humidity is above 10%. Normally there is a fan taking care of it but sometimes the differents gets to high. As there is already a raspberry pi running in the observatory for the weatherstation and the flightradar24 installation we just added the 4 channel relay board ks0212 from keyestudio. Not touching the 220V part we directly used the relay to “press” the TTL switch on the board for 0.5 seconds to turn on and off the dehumidifier. Here are the code snipped we used for this. The control is completely handled via MQTT.

Installing necessary programs and libraries

sudo apt install python python-pip python-dev
sudo pip install wiringpi paho-mqtt

For the sake of simplicity we used python and the GPIO library wiringpi. Therefore we first install the python development parts and them the python libraries for wiringpi and MQTT. As this is a dedicated hardware installation we don’t use virtualenv and directly install the library as root system wide.

The python program

import time
import wiringpi
import paho.mqtt.client as mqtt

def setup():
   wiringpi.pinMode(3,  1)
   wiringpi.pinMode(7,  1)
   wiringpi.pinMode(22, 1)
   wiringpi.pinMode(25, 1)

def short(pin):

def switch_on(pin):
    wiringpi.digitalWrite(pin, 1)

def switch_off(pin):
    wiringpi.digitalWrite(pin, 0)

def on_connect(self, client, userdata, rc):

def on_message(client, userdata, msg):
    m = msg.topic.split("/")
    pin = 0
    if m[-1] == "j3": 
        pin = 3
    if m[-1] == "j2": 
        pin = 7
    if m[-1] == "j4": 
        pin = 22
    if m[-1] == "j5": 
        pin = 25
    if pin != 0:
        if msg.payload == "on":
        if msg.payload == "off":
        if msg.payload == "press":

if __name__ == "__main__":
    mqclient = mqtt.Client(clean_session=True)
    mqclient.connect("", 1883, 60)
    mqclient.on_connect = on_connect
    mqclient.on_message = on_message

Again, a very simple python script, basically attaching to a (you need to change the code, there is no config) mqtt server and subscribes itself to a certain topic. Then it waits for messages and cuts off the last part of the topic to identify the relay. The naming convention is based on the relay name printed on the ks0212 pcb. As payload you can send “on“, “off” and “press“. “press” switches the relay on for half a second in order to simulate a button press as we need it for our dehumidifier.

Adding a systemd service

In order to keep the wantabe daemon up and running and also start it automatically at system start we add this service configuration file in “/lib/systemd/system/relayboard.service“:

#cat /lib/systemd/system/relayboard.service
Description=ks0212 Relay Board

ExecStart=/usr/bin/python /home/pi/


Activating the service

The following lines activate the service:

sudo chmod 644 /lib/systemd/system/relayboard.service
sudo systemctl daemon-reload
sudo systemctl enable relayboard.service
sudo systemctl start relayboard.service

Checking the status can be done with:

sudo systemctl status relayboard.service

ks0212 Pinout

If you want to do some hacking with the ks0212 relay board on your own here is the pin mapping table. I used the very cool side for getting the numbers:

Relay WiringPi BCM GPIO Link
J2 7 4 7
J3 3 22 15
J4 22 6 31
J5 25 26 37



Setting up SDRplay remote on a raspberry pi



I recently bought myself a SDRPlay receiver to play with this technology and maybe build a ground station or meteor scatter detector. The original plan is to setup a receiver on the Motionlab roof with an raspberry pi and send the IQ data via network down to a local server and extract the interesting information. One great software to work remotely with an SDR receiver is the Soapy project.

Install the raspberry pi part

Build system

Install the latest raspberry pi lite version from

sudo apt update
sudo apt upgrade
sudo apt install cmake g++ libpython-dev python-numpy swig git

Core system

The soapy part consist of 3 parts. The Core system must be installed first.

git clone
cd SoapySDR
mkdir build
cd build
cmake ..
make -j4
sudo make install
sudo ldconfig


The SDRplay part consist of two parts one are the proprietary binary libraries from SDRplay itself the the other part is the soapy wrapper for SDRplay.

Binary Libraries

The driver can be downloaded from the SDRplay homepage

chmod 777

The SDRplay Soapy wrapper

git clone
cd SoapySDRPlay
mkdir build
cd build
cmake ..
make -j4
sudo make install

Test the Soapy access

SoapySDRUtil --info

Soapy Server for Remote Access

git clone
cd SoapyRemote
mkdir build
cd build
cmake ../ # -DCMAKE_BUILD_TYPE=Debug
make -j4
sudo make install

Run the server

SoapySDRServer --bind

If you want to run it as a service have a look here on how to autostart stuff in linux.

Dev-Ops with OtA update for ESP8266

Over the Air update (Ota) for ESP8266


Thanks to the esp8266 project on github there is a very convenient way how an ESP can be updated over the air. There are three different ways available.

  1. The first one is via the arduino IDE itself where the esp opens a port and is available for firmware upload just like with a serial connection. Very convenient if you are in the same network.
  2. The second one is via http upload. So the esp provides a web server to upload the bin file. In this case there is no need to be in the same network but it is still a push and for each installed esp individual necessary.
  3. The third and most convenient way for a bigger installation base or in case the devices are behind a firewall (as they always should be) and no remote access is possible. In this case the device can download the firmware itself via http(s) download from a web server somewhere in the internet.

For a complete dev-ops pipeline from pushing to a repository to flashing a device the third scenario it the easiest one. So we need a place to store the binary files. For convenience I use amazon s3 to host my binary files as travis easily supports s3 upload. But it can be every internet platform where files can be stored and downloaded via http(s). The necessary code on arduino side looks like this:

#define ULR_FIRMWARE_BIN     ""

void checkForNewFirmware(void){

    HTTPClient http;
    int httpCode = http.GET();

    if(httpCode == HTTP_CODE_OK) {
        String payload = http.getString();
        int newVersion = payload.toInt();

        if (BUILD_VERSION < newVersion){
            Serial.println("I need to update");
            t_httpUpdate_return ret = ESPhttpUpdate.update(ULR_FIRMWARE_BIN);

            if (ret == HTTP_UPDATE_FAILED){
                Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());

This arduino function can be called from time to time (at startup or on constant running systems every now and then) to check for a new firmware version and in case there is a new version available automatic flash it and restart.

  • Line 1 is a #define with a placeholder for the current version of the installed firmware. This placeholder is replaced in the build pipeline at travis with an increasing number. So the compiled code has something like 23 or 42 instead of REPLACE_WITH_CURRENT_VERSION.
  • Line 2 is the URL for a latest version of a firmware.
  • Line 3 is the URL to a file with only one line with the latest build number in it.
  • Line 7-9 loads the version file from s3.
  • Line 12-13 converts the file into a number which can be compared with the define from line 1.
  • Line 17 is the firmware update itself. A detailed description of the ESPhttpUpdate class can be found here.

There are two ways to check if there is a new version available and only flash if there is something new. The one we use here is to have an own mechanism for it. I do it because on s3 I can only host static files and therefore I place the latest build number in a static file next to the firmware itself. The other way is build in into ESPhttpUpdate. The update function can be called with a build number which will be compared on the server and the return code will reflect if there is a new version or not. In this case we would need a script on the server to check for it.

Get an increasing build version number

With a little bash script we could load the last build number from s3 and then increase it in order to have the current number for our build.

#!/usr/bin/env bash

let oldversion=`curl`
let newversion=oldversion+1

echo "============="
echo "New Version:"
echo $newversion
echo "============="

sed -i "s/REPLACE_WITH_CURRENT_VERSION/$newversion/g" src/main.cpp

echo $newversion > upload/blanked.version

This script loads the version file (line 3), increases the number (line 4) and patches our source code file (line 11) with this number instead of REPLACE_WITH_CURRENT_VERSION. After running this script the current source code contains the latest number and also the upload folder for s3 has a new file with the newest number in order to inform the polling ESPs.

Travis config file

Travis-ci is incredible easy to use and very reliable for continuous integration. In combination with platformio it is very easy to compile arduino code for several types of hardware. Simply configure the hardware in the platformio.ini file:

platform  = espressif8266
board     = huzzah
framework = arduino

In this case we use the esp8266 feather board aka Huzzah. Just set the framework to your kind of esp.

Travis itself is configured by the .travis file in the root directory of your repository on github:

language: python
- '2.7'
sudo: false
  - "~/.platformio"

- pip install -U platformio

- mkdir upload
- ./
- sed -i "s/WLANSSID/$WLANSSID/g"     src/main.cpp
- sed -i "s/WLANPASSWD/$WLANPASSWD/g" src/main.cpp
- platformio run
- cp .pioenvs/huzzah/firmware.bin upload/blanked.bin

  provider: s3
  bucket: 'feelflight'
    secure: amzqBC+rs+S860Z6ABQNAseKYL+7UgNnJGhF7jGkc6Aq/e8JmPqRSYHrEESM4S1jkOXYR5WouX04ytZqoXnrt0E625LT0+rLUGjyZ1QpGlrI5dwhOP4TagT+A90DtRI77TGf4qgnAkX+wAOufKehMKNms8jL6M64vwR5mIg3veiewZFRBtpvlkqCS55+rdWmYbFuT+UYNdq5UItJkfY1HNunafDvS1qfCTwBzoa5Yro/pyGA5cSdKDEZrJ+WfgCm03PZHVMKARm07lOcAZTstp5qQCbG8S4jE0OA8Q+AQ/mcwzB+JRHrJZdoQmpNjAsREnRDvv/Zz88V4JluPVrgk1B3mWw7tAPGnxT+N/Kwj+f455AMjsEcJ3z3YdGeJtftqYtr9kbcECWt7puPILpRhSKkAMGEPAQhOQAdLqQvfL1qZQbunexDShKkpMbpmVvyTYQXXmixoc26dB7MJpbw4UHNui3zpb5fWDHuJ3EIEvHvuoMDT2Dk2GTpStBqACrbo74Orsfah6DvEuJXXbmBIChfDufalNA5CNkjhIfBSDQpu5HE6UEylPDYcwgXwvhIl9zSXljYcH6LBP18axwheCmyeolVse3a3h9GF3tSfcJrlMshZ0oZ0WuwvLflE4ZzDWMT1XX8kgHrvaYklagwKbgltMYkq7R04kD++h32J8s=
    secure: jZiEn7PTFRrwFu8ZmDEkUGjYuKSWmP+kI6biVKaRwhcqA+WeYjKOH3r5NgR5V+xoYcZA47Qm41pl6gi71aMEU2Xil4+HUdmLM6pXLU87Q0NG974cLesccaDy2/rkADmLP/jaqN66Pavd4l2tRxGOP+p1QQRXQOccFEW6j95PdPOyzppPZc6h8yzqmxerIgDSDFQuF4pRWjEtJSPrEyw79p834wvVVahlXRJ6jrqy5X4CiqabYmaR3QuT0W9tBHHtfMfgPJBCooTxqT0uqDnOSN0wU6TmQ8ZHg9y7d4ChOWLbpHwHhOk3UrDrTllbTSr7zRjqzwW69yivZX2e0XR89X8PFcLg8jIcZxgKyIKGo+BpCnaLlVQ1dxmIrDfcComino+3ZWC4lZDLgaw/uTfcAapn1sPBNhnxed7kr7u/RkZIfdXWZn4GSO1aDAJWXMF6lC2lq1JN7FlfpyGJuvsN6FQcIrq0W8jghZ0+8AAgwOzzNPG5bY34+8R3Qtp1d4hwqkan4peF0vVfeVRldtkissmup+bQRyU7xyYUPqL7EdtjJXBXwP3ChTv/FGu2eQhjgweLHsyrkcFBeqpKwjHnG0jUSV3QQPq9hpO8mk3eSjSbM91cY9S5t2BKtIR0ALCyyAn+B40P6OiJ+4v4d0ZdyXGjL3aRyOSg4jIOl5Awa10=
  skip_cleanup: true
  acl: public_read
  local_dir: upload/
  upload-dir: firmware
  • Line 1: Platformio is based on python so the build environment (although the code is c++) is python for maintaining platformio.
  • Line 3: Right now platformio is only available for python 2.7 so this line gets the latest stable version of python 2.7.
  • Line 5-7: Gets the latest cache files from the last build in order to save compile time and reduce the costs for travis. As this service is for free for open source projects it is always nice to save some money for the cool guys.
  • Line 10: Installs the latest version of platformio itself.
  • Line 13: Creates the upload directory which we will upload to s3 later on.
  • Line 14: Calls the build number increase and patch script.
  • Line 15-16: Patches the wireless lan config in case it is not handled inside the arduino code itself.
  • Line 17: Calls platformio to download all libraries and compile the arduino code itself.
  • Line 18: Platformio generates a lot of files for the linker and several other files. We only need the bin file later on, so we copy it here to the upload folder.
  • Line 20: Travis has a build in functionality to upload files after compilation. This is the part where we upload the files to s3.
  • Line 22: Defines the s3 bucket to upload the files.
  • Line 23-26: Provides the encrypted s3 credentials. See travis documentation on how to create these lines.
  • Line 29: Defines the local folder to be uploaded. Otherwise travis will upload everything from the current run.
  • Line 30: Defines the s3 folder in the bucket where the files will be stored.

With this files in place travis monitors your github repository and creates / uploads new firmware versions each time you push changes to your repository. The arduino code checks for new versions and patches itselfs as soon as there is a new version available. A complete project can be found here in my github repository.


BC95 Board

NB-Iot with the BC95 with arduino

Hardware hacking with the mobile c-lab

Last weekend the c-base and friends team had a great time doing some hacking with Narrow Band IoT (NB-IoT) from Deutsche Telekom at the nbiot-hackathon at hub:raum. We could put our hands on the BC95 chip, a dev board and access to the Telekom test network. Beside hacking we had a great time with funny hats and our obligatory overalls.

The BC95 Board

The board we could use was the BC95-B8 board which we mounted on a development board with a support controller and serial converter. Beside the board setup we also soldered a PCB with some sensors and a Teensy board to control the BC95 and the sensors.



The dev board itself has a RS232 converter to give access via “normal” RS232 connectors. This is convenient for older laptops or desktops but luckily they also give you access via 3.3V pegel to the same UART interface. The pins are pre soldered on a 10 pin header so it’s easy to connect this to arduino or Raspberry PI via the serial connection. As you can see in the picture it is pin 1,2 and 6. No level converter necessary.

AT- Command Set

AT      // First handshake will be used for out speed detection may be result in ERROR
AT      // Repeat sending "AT" until you receive an "OK"
AT+NRB  // Reboot the device in order to have a clean start

//wait for the reboot

AT+NBAND=8                        // Set the communication band in this case 900Mhz
AT+CGDCONT=1,"IP","NBIOT.Telekom" // Sets the APN (Access Point Name)
AT+CEREG=2                        // Connect to the IoT Core
AT+CFUN=1                         // Power on the module
AT+COPS=1,2,"12345"               // Force the module to connect to the network

// wait for the network connection (some seconds)

AT+NPING=                          // Ping a server to check if it works
AT+NSOCR=DGRAM,17,16666                   // Open a UDP socket
AT+NSOST=0,,16666,4,414E5349  // Send out the data

Line 1,2

The serial protocol can be any speed at 8N1, the board auto detects the speed at first communication, therefore you need to send the AT command several times (normally 2) to set the speed. Normally the first AT command is answered with ERROR and the second one with AT. Make sure you get an OK before you continue.

Line 3

In order to get a clean setup we first reboot the board with “NRB”. It takes some seconds and it will come back with OK.

Line 7

Depending on your network you need to set the band with “NBAND”. The Telekom test network is on 900 MHz so we go with 8. Other bands are 5 (850 Mhz) and 20 (800 MHz).

Line 8

Depending on your setup and provider you need to set the APN with the CGDCONT command.

Line 9

Connect to the IoT Core

Line 10

Power on the module

Line 11

Connect to the network. This can take several seconds even a minute. You can always check the connection with at+cgatt?” and check for “+CGATT:1”. You can double check the existing connection by asking for the IP address which is assigned to you by sending “at+cgpaddr=1” to get for example “+CGPADDR:1,”.

Line 15

Ping a server to test everything is fine. (In this case the google DNS server)

Line 16

Open an UDP socket to receive answers. In our case the DGRAM and 17 are mandatory but the port you are using (in our case 16666) is up to you.

Line 17

Send your UDP data package. First parameter is the out port (0). The second one is the address you want to send the data to (ip or name). Third one is the receivers port (16666). Fours is the amount of data you want to send (keep it below 100 bytes) and the last one is your data in hex notification. I recommend to convert a string you want to send.


uint8_t sensordata[10];

void setup() {
  while (!Serial) {}
  while (!Serial1) {}
  Serial.println("c-base starts");

void sensorfake(){
  for (int i = 0; i < 10; i++){
    uint8_t d = sensordata[i];
    sensordata[i] = d+1;

void sendit(String in) {
  while(Serial1.available() > 0){
    if(Serial1.available() > 0){
      byte c =;

void initModem(){

void sendData(){

  String datastring = "AT+NSOST=0,,16666,11,01";

  for(uint8_t i = 0; i < 10; i++){
    String hexstring = String(sensordata[i], HEX);
    if(hexstring.length() < 2){
      hexstring = "0" + hexstring;


void loop() {

This arduino code is really a fast and ugly hack for the hackathon in order to send out the data. It does not listen for the AT returns or anything else. So this is only an example on how NOT to do coding but it worked for the hackathon.