Install ArchLinux on Hetzner Cloud

This is my protocol of an Archlinux installation on a CX11 in the Hetzner Cloud.

First Step: Create a new Server

  • create with any OS.

  • activate rescue mode (linux64)

Second Step: Install Archlinux

  • ssh as root to server (now in rescue mode)

  • run installimage and choose ArchLinux

  • I only changed the HOSTNAME in the install.conf

  • reboot

Third Step: post-install

  • ssh as root to server (now with ArchLinux)

  • add more locales in /etc/locale.gen and run locale-gen

  • set root password with passwd

  • install some packages (i.e. cronie sudo tinc git …) using pacman -S

  • enable cronie: systemctl enable cronie

  • visudo and enable wheel group

  • add user: useradd -m -G wheel -s /bin/bash myusername

  • set password for user: passwd myusername

  • setup tinc (sth for another blogpost)

Use docker ARGs for conditionals on build


Run some script on docker build but only on production and not on development.


Using ARG in Dockerfile and docker-compose.yml. The "main" is an example for production and "main_dev" for development.


FROM python:3

RUN if [ ! "$DEV" = "yes" ]; then echo "DEV is NOT set"; else echo "DEV is set"; fi

CMD [ "echo", "OK" ]


version: "3.8"
      context: .
      context: .
         DEV: "yes"

Now lets run the examples:

docker-compose build --no-cache main results in

Building main
Step 1/4 : FROM python:3
 ---> 1f88553e8143
Step 2/4 : ARG DEV
 ---> Running in a369f15bdbba
Removing intermediate container a369f15bdbba
 ---> d33834a028c4
Step 3/4 : RUN if [ ! "$DEV" = "yes" ]; then echo "DEV is NOT set"; else echo "DEV is set"; fi
 ---> Running in c47c9d3ae979
DEV is NOT set
Removing intermediate container c47c9d3ae979
 ---> d93839918c95
Step 4/4 : CMD [ "echo", "OK" ]
 ---> Running in 1280176dc942
Removing intermediate container 1280176dc942
 ---> ac023ae1b6fa

Successfully built ac023ae1b6fa
Successfully tagged dockerarg_main:latest


docker-compose build --no-cache main_dev results in

Building main_dev
Step 1/4 : FROM python:3
 ---> 1f88553e8143
Step 2/4 : ARG DEV
 ---> Running in a9bbe1cbf494
Removing intermediate container a9bbe1cbf494
 ---> f28af462f46a
Step 3/4 : RUN if [ ! "$DEV" = "yes" ]; then echo "DEV is NOT set"; else echo "DEV is set"; fi
 ---> Running in 908227c7de9c
DEV is set
Removing intermediate container 908227c7de9c
 ---> 4f4caad6140a
Step 4/4 : CMD [ "echo", "OK" ]
 ---> Running in 0f83a6b15abf
Removing intermediate container 0f83a6b15abf
 ---> 19bf8758a2ae

Successfully built 19bf8758a2ae
Successfully tagged dockerarg_main_dev:latest

This seems to work as seen with DEV is NOT set vs DEV is set.

Count Rows on an old rowing machine


At the end of 2019 a mechanical rowing machine came into my possession. This machine (a Hanseatic Rowing Machine) has no electronics at all but I want to measure the rows I am doing.


So I bought an I2C based Accelerometer: MMA7455 and soldered a raspberry-pi zero shield for it:


The recording is done with a Python script that is started on boot via systemd.

This code shows howto read the current value from the sensor:

import smbus

bus = smbus.SMBus(1)
# MMA7455L address is 0x1D
bus.write_byte_data(0x1D, 0x16, 0x01)

# read values
data = bus.read_i2c_block_data(0x1D, 0x00, 6)

# Convert the data to 10-bits
xAcc = (data[1] & 0x03) * 256 + data [0]
if xAcc > 511 :
    xAcc -= 1024
yAcc = (data[3] & 0x03) * 256 + data [2]
if yAcc > 511 :
    yAcc -= 1024
zAcc = (data[5] & 0x03) * 256 + data [4]
if zAcc > 511 :
    zAcc -= 1024

print(f"Acceleration {xAcc:5d} {yAcc:5d} {zAcc:5d}")

The full record script used on the raspberry pi is additionally logging to a csv file:


The latest version of the evaluation as a jupyter notebook:

First, we need to find the best curve for the problem. Here we see 1 row, 2 rows and 5 rows:


And only the 5 rows zoomed in:


Only in the x axis curve the rows are clearly distinguishable.

So the isolated x axis looks like this


Because it is easier to detect peaks on top we negate the curve:


And now smoothen the curve using a savgol filter:


We got the parameters for the savgol by trial and error.

The next step is the peak finding. Scipy has a find_peaks method that works after some tweaking quite good:


The (orange) line below the found peak is the prominence. This "height" helps filtering too small peaks. The complete filtering methods looks like this

def get_peaks(x):
    _ = np.negative(x)
    _ = scipy.signal.savgol_filter(_, 51, 3)
    peaks, properties = scipy.signal.find_peaks(_, prominence=5, width=40)
    return sum(map(lambda i: i>12, properties["prominences"]))

The sum in the last line filters all peaks with a prominence higher than 12 and only sums them.

Another example with 100 rows:


The full code: