Lindenblad Antenna for 2 Meters DIY

We need an Antenna


There was need for an antenna for our SatNogs (Satellite Ground Station Network). As serious hackers there was no other option than to build one of our own. After several more or less unsuccessful experiments with several antenna types we decided to build a Lindenblad antenna for the 2 meters (144 Mhz frequency) range. We are Ronny (DL7ROX) and myself (DM1AS). There are several papers and discussions available on how to build such an antenna, most of them vom Amsat and US in general.

So I only focus here on the “translation” into the metric system and the DIY parts to assemble one antenna.  For a very good paper and the magic background please have a look at

Dipole Dimensions

Dimension Length / Distance
Length on one dipol element 373 mm
Space between the dipoles 19 mm
Total length of the dipole 765 mm

In order to make your life easier and the spacing hopefully very accurate I create this T-connector with Fusion360.

and this plug

The cross connection in between the 4 dipoles is the same aluminium tube with a length of 584 mm.

The wires

As we have 4 dipoles of 50 Ohm impedance in parallel and the wire typically as 50 Ohm we need to match it. The solution in the paper is to use an 75 Ohm TV wire with a defined range so it will match the 200 Ohm to the 50 Ohm of the wire impedance.

impedance matching wire
impedance matching wire
A 584 mm
B 5 mm
C 8 mm


Put it all together

Each dipol will be connected to one impedance matching wire and all 4 wires to the antenna wire. Don’t forget a cable ferrite on each of the impedance matching wires very close to the dipol side. The 4 dipoles will then be connected opposite to each other and each dipole rotated by 30 degrees clockwise to the horizon.

Measure the SWR


We measured the dimensions with an AA-1400 and where very proud to get such a great result of 1 at the center frequency.

G199 or how to 3d print a logo on existing STL files

The problem

Logo with 2 different Filaments
Logo with 2 different Filaments

Sometime you want to print you logo or some text on your 3D object with different filament but you only have a single head printer and don’t want to spend all the time sitting next to your printer to wait for the right moment to manually pause the print and change the filament. Like the Motionlab logo on the picture. For sure you could print it separately and glue it on the main printed part but specially with text it’s a lot of tiny parts to take care of and align. If you are lucky and have a dual print head it’s not a problem but there is also a way to do it very simple with a singe print head by editing the G-Code file and add G-Codes by hand.

The solution

There is a G-Code named G199. Regarding to Craftware the purpose of the code is “G199 pauses the print immediately, and moves the head to X0, Y100. (this is the command the LCD screen uses)”. So by adding this code by hand the printer stops printing and moved the head to the side. After changing the filament (and also extrude some more by hand to make sure the printer is ready) you can press “continue” on the printer display.

Prepare the SVG file

If your logo is already in SVG you are lucky. Otherwise try to convert it to SVG and make sure it’s in connected objects. If you need some geeky stuff I can recommend Geeksvgs.

Use Fusion360 to create the STL logo file

Fusion360 insert SVG
Fusion360 insert SVG

In Fusion360 use Insert -> Insert SVG -> Select SVG File to open the SVG file on a Sketch. Resize and stretch it as you like or the dimensions dictate.

Next step is to extrude the logo to a 3D object. This can be done simply by “Stop Sketching” and then press “e” for extrude. Select everything by drawing a frame with your mouse. Unfortunately fusion has no idea witch part of the logo should be extruded and witch not. Press and hold CTRL and deselect the inner parts of the logo. For example the circle in the “o”. I recommend extracting 10 mm even if you only want to rise the logo by 4 mm.

As the single objects are not connected fusion creates several bodies instead of one.

Save single STL
Save single STL

A single STL file with all Bodies included at the right position can be exported by pressing the Component name and press the right mouse button.

Combine both STL in your slicer

Now as we have two STL files we can load them both at the same time in our slicer (no matter witch one). Position your logo at the right place, scale it and change the z access offset accordingly to your needs.

Combine STLs
Combine STLs

As we extruded the logo 10 mm there is enough space to play around. Make sure at least one mm is submerged in your main body.


Manually edit the gcode to add the pause sequence

Find the right Layer
Find the right Layer

Now we need to find the right place in the G-Code itself. Our slicer can help us with the preview mode. The best layer is the second one after the main body is done and the logo starts to be printed.

Mark down this layer and open the G-Code in your favorite text editor. All slicers I used always make nice comments in the code to find the light position. Search for “layer nnn” and add the “G199” statement.

G1 X120.290 Y97.291 E0.0407
G1 X117.699 Y96.872 E0.0884
G1 X115.696 Y96.511 E0.0685
G1 X113.803 Y94.618 E0.0901
G1 X113.662 Y94.477 F2400
G1 E-1.5000 F1800
; layer 156, Z = 39.000
; inner perimeter
G1 X114.840 Y96.357 F4800
G1 Z39.000 F1000
G1 E1.5000 F1800
G1 X111.737 Y95.763 E0.1064 F2400
G1 X110.227 Y95.491 E0.0517
G1 X108.129 Y95.141 E0.0716
G1 X106.311 Y94.873 E0.0619
G1 X105.294 Y94.746 E0.0345
G1 X104.465 Y94.666 E0.0281
G1 X103.842 Y94.638 E0.0210



Just print the G-Code as you always do. As soon as the printer reads and process the G199 comment it stops printing and moves the head to the left side. All heating settings remain the same and you can easily replace your filament and press “Continue” or “GO” on your printers screen. Happy printing.

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



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.