--- /dev/null
+Install statically linked qemu to allow chroot to ARM:
+$ aur sync --chroot qemu-arm-static
+$ sudo pacman -Sy
+$ sudo pacman -S qemu-arm-static
+
+Get and unpack Arch Linux ARM for Pi 2/3:
+$ cd /tmp
+$ wget http://archlinuxarm.org/os/ArchLinuxARM-rpi-2-latest.tar.gz
+$ sudo mkdir controlpi
+$ sudo mount -t tmpfs none controlpi/
+$ sudo tar -xpf ArchLinuxARM-rpi-2-latest.tar.gz -C controlpi/
+
+First upgrade:
+$ TERM=xterm sudo arch-chroot controlpi/
+# pacman-key --init
+# pacman-key --populate archlinuxarm
+# pacman -Syu
+
+Package management until:
+# pacman -Q --explicit
+base
+binutils
+fakeroot
+gcc
+git
+linux-rpi
+make
+openssh
+pacman-contrib
+parted
+python
+raspberrypi-bootloader-x
+raspberrypi-firmware
+sudo
+vim
+# pacman -Q --deps --unrequired --unrequired
+Nothing
+
+Clean up /etc/systemd/system/:
+# rm /etc/systemd/system/multi-user.target.wants/remote-fs.target
+# rm /etc/systemd/system/sysinit.target.wants/haveged.service
+
+Clean up /var/log:
+# rm -r /var/log/journal # Makes journal volatile
+# rm /var/log/lastlog
+
+Change timezone to Europe/Berlin:
+# ln -s /usr/share/zoneinfo/Europe/Berlin /etc/localtime
+
+Rename standard user:
+# mv /home/alarm /home/pi
+# sed -i 's/alarm/pi/g' /etc/passwd /etc/shadow /etc/group /etc/gshadow
+
+Change passwords to well-known password:
+# passwd
+# passwd pi
+
+Enable sudo for standard user (all in group 'wheel'):
+# echo '%wheel ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/wheel
+# chmod o-r etc/sudoers.d/wheel
+
+Safe settings for sshd:
+# sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/g' /etc/ssh/sshd_config
+# sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/g' /etc/ssh/sshd_config
+
+Install ssh keys:
+# su - pi
+$ mkdir .ssh
+Put accompanying authorized_keys file in /home/pi/.ssh/authorized_keys.
+$ chmod -R go-rx .ssh/
+$ exit
+
+Script for resizing to full SD card:
+# su - pi
+$ cat << EOF > resize-fs.sh
+#!/bin/sh
+sudo parted /dev/mmcblk0 resizepart 2 100%
+sudo partprobe /dev/mmcblk0
+sudo resize2fs /dev/mmcblk0p2
+EOF
+$ chmod +x resize-fs.sh
+$ exit
+
+Enable I2C:
+# echo 'dtparam=i2c_arm=on' >> /boot/config.txt
+# echo 'i2c-dev' >> /etc/modules-load.d/raspberrypi.conf
+
+Install pigpio from the AUR:
+# su - pi
+$ git clone https://aur.archlinux.org/pigpio.git
+$ cd pigpio/
+$ makepkg -s
+$ exit
+# pacman -U /home/pi/pigpio/pigpio-XX-X-armv7h.pkg.tar.xz
+# ln -s /usr/lib/systemd/system/pigpiod.service /etc/systemd/system/multi-user.target.wants/
+# mkdir /etc/systemd/system/pigpiod.service.d
+# cat << EOF > /etc/systemd/system/pigpiod.service.d/override.conf
+[Service]
+ExecStart=
+ExecStart=/usr/bin/pigpiod -l
+EOF
+
+Clear package cache:
+# paccache -rk0
+
+Install ControlPi system:
+# su - pi
+$ python -m venv controlpi-venv
+$ source controlpi-venv/bin/activate
+$ pip install -U pip setuptools wheel
+$ pip install git+git://git.graph-it.com/graphit/controlpi.git
+$ pip install git+git://git.graph-it.com/graphit/controlpi-pinio.git
+$ pip install git+git://git.graph-it.com/graphit/controlpi-modbus.git
+$ pip install git+git://git.graph-it.com/graphit/controlpi-wsclient.git
+$ pip install git+git://git.graph-it.com/graphit/controlpi-wsserver.git
+$ pip install git+git://git.graph-it.com/graphit/controlpi-statemachine.git
+$ exit
+# cat << EOF > /etc/systemd/system/controlpi.service
+[Unit]
+Description=ControlPi Service
+Wants=network-online.target
+After=network-online.target
+After=pigpiod.service
+StartLimitIntervalSec=300
+StartLimitBurst=5
+
+[Service]
+WorkingDirectory=/home/pi
+Environment=PYTHONUNBUFFERED=1
+ExecStart=/home/pi/controlpi-venv/bin/python -m controlpi conf.json
+Restart=on-failure
+RestartSec=5
+
+[Install]
+WantedBy=multi-user.target
+EOF
+# ln -s /etc/systemd/system/controlpi.service /etc/systemd/system/multi-user.target.wants/
+
+Change hostname to '<whatever>pi':
+# sed -i 's/alarmpi/<whatever>pi/g' /etc/hostname
+
+Create <Whatever>-specific conf.json
+# su - pi
+$ cat << EOF > conf.json
+{
+ "<Whatever>Pi-Master": {
+ "plugin": "WSClient",
+ "url": "ws://graph.example.com:8080",
+ "interface": "eth0",
+ "up filter": [ ],
+ "down filter": [ { "target": { "const": "" } } ]
+ }
+}
+EOF
+$ exit
+
+Exit chroot and build archive:
+# exit
+$ sudo tar -czf <whatever>pi.tar.gz -C controlpi/ .
+
+Create image:
+$ fallocate -l 2G 20YY-MM-DD-<whatever>pi-arch-controlpi.img
+$ sudo losetup --find --show 20YY-MM-DD-<whatever>pi-arch-controlpi.img
+$ sudo parted --script /dev/loop0 mklabel msdos
+$ sudo parted --script /dev/loop0 mkpart primary fat32 0% 100M
+$ sudo parted --script /dev/loop0 mkpart primary ext4 100M 100%
+$ sudo mkfs.vfat -F32 /dev/loop0p1
+$ sudo mkfs.ext4 -F /dev/loop0p2
+$ sudo mount /dev/loop0p2 /mnt
+$ sudo mkdir /mnt/boot
+$ sudo mount /dev/loop0p1 /mnt/boot
+$ sudo tar -xpf <whatever>pi.tar.gz -C /mnt/
+$ sudo umount /mnt/boot
+$ sudo umount /mnt
+$ sudo losetup --detach /dev/loop0
+$ scp 20YY-MM-DD-<whatever>pi-arch-controlpi.img root@graph-de-screwerk-com.castle:sdimages/
+
+Flash image to SD card:
+$ sudo dd if=20YY-MM-DD-<whatever>pi-arch-controlpi.img of=/dev/mmcblk0 bs=1M
--- /dev/null
+{
+ "Debug": {
+ "plugin": "WSServer",
+ "web": {
+ "/": {
+ "module": "controlpi_plugins.wsserver",
+ "location": "Debug"
+ }
+ }
+ },
+ "Log": {
+ "plugin": "Log",
+ "filter": [{}]
+ },
+ "Output Card 1": {
+ "plugin": "OutputCard",
+ "address": 56,
+ "pins": {
+ "A1-1": 0, "A1-2": 1, "A1-3": 2, "A1-4": 3,
+ "A1-5": 4, "A1-6": 5, "A1-7": 6, "A1-8": 7
+ }
+ },
+ "Output Card 2": {
+ "plugin": "OutputCard",
+ "address": 57,
+ "pins": {
+ "A2-1": 0, "A2-2": 1, "A2-3": 2, "A2-4": 3,
+ "A2-5": 4, "A2-6": 5, "A2-7": 6, "A2-8": 7
+ }
+ },
+ "Input Card 1": {
+ "plugin": "InputCard",
+ "address": 32,
+ "interrupt pin": 4,
+ "pins": {
+ "E1-1": 0, "E1-2": 1, "E1-3": 2, "E1-4": 3,
+ "E1-5": 4, "E1-6": 5, "E1-7": 6, "E1-8": 7
+ }
+ },
+ "Input Card 2": {
+ "plugin": "InputCard",
+ "address": 33,
+ "interrupt pin": 17,
+ "pins": {
+ "E2-1": 0, "E2-2": 1, "E2-3": 2, "E2-4": 3,
+ "E2-5": 4, "E2-6": 5, "E2-7": 6, "E2-8": 7
+ }
+ },
+ "Input Card 3": {
+ "plugin": "InputCard",
+ "address": 34,
+ "interrupt pin": 27,
+ "pins": {
+ "E3-1": 0, "E3-2": 1, "E3-3": 2, "E3-4": 3,
+ "E3-5": 4, "E3-6": 5, "E3-7": 6, "E3-8": 7
+ }
+ },
+ "Input Card 4": {
+ "plugin": "InputCard",
+ "address": 35,
+ "interrupt pin": 22,
+ "pins": {
+ "E4-1": 0, "E4-2": 1, "E4-3": 2, "E4-4": 3,
+ "E4-5": 4, "E4-6": 5, "E4-7": 6, "E4-8": 7
+ }
+ },
+ "Test Init": {
+ "plugin": "Init",
+ "messages": [{ "target": "Test On", "command": "start" }]
+ },
+ "Test Wait": {
+ "plugin": "GenericWait"
+ },
+ "Test A1-1 On": {
+ "plugin": "Alias",
+ "from": {
+ "target": { "const": "Test On" },
+ "command": { "const": "start" }
+ },
+ "to": [
+ { "target": "A1-1", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-1 On" }
+ ]
+ },
+ "Test A1-1 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-1 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-1", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-1 Off" }
+ ]
+ },
+ "Test A1-2 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-1 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-2", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-2 On" }
+ ]
+ },
+ "Test A1-2 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-2 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-2", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-2 Off" }
+ ]
+ },
+ "Test A1-3 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-2 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-3", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-3 On" }
+ ]
+ },
+ "Test A1-3 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-3 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-3", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-3 Off" }
+ ]
+ },
+ "Test A1-4 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-3 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-4", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-4 On" }
+ ]
+ },
+ "Test A1-4 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-4 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-4", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-4 Off" }
+ ]
+ },
+ "Test A1-5 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-4 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-5", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-5 On" }
+ ]
+ },
+ "Test A1-5 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-5 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-5", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-5 Off" }
+ ]
+ },
+ "Test A1-6 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-5 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-6", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-6 On" }
+ ]
+ },
+ "Test A1-6 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-6 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-6", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-6 Off" }
+ ]
+ },
+ "Test A1-7 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-6 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-7", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-7 On" }
+ ]
+ },
+ "Test A1-7 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-7 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-7", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-7 Off" }
+ ]
+ },
+ "Test A1-8 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-7 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-8", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-8 On" }
+ ]
+ },
+ "Test A1-8 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-8 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A1-8", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A1-8 Off" }
+ ]
+ },
+ "Test A2-1 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A1-8 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-1", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-1 On" }
+ ]
+ },
+ "Test A2-1 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-1 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-1", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-1 Off" }
+ ]
+ },
+ "Test A2-2 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-1 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-2", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-2 On" }
+ ]
+ },
+ "Test A2-2 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-2 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-2", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-2 Off" }
+ ]
+ },
+ "Test A2-3 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-2 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-3", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-3 On" }
+ ]
+ },
+ "Test A2-3 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-3 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-3", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-3 Off" }
+ ]
+ },
+ "Test A2-4 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-3 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-4", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-4 On" }
+ ]
+ },
+ "Test A2-4 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-4 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-4", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-4 Off" }
+ ]
+ },
+ "Test A2-5 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-4 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-5", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-5 On" }
+ ]
+ },
+ "Test A2-5 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-5 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-5", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-5 Off" }
+ ]
+ },
+ "Test A2-6 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-5 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-6", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-6 On" }
+ ]
+ },
+ "Test A2-6 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-6 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-6", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-6 Off" }
+ ]
+ },
+ "Test A2-7 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-6 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-7", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-7 On" }
+ ]
+ },
+ "Test A2-7 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-7 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-7", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-7 Off" }
+ ]
+ },
+ "Test A2-8 On": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-7 Off" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-8", "command": "set state", "new state": true },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-8 On" }
+ ]
+ },
+ "Test A2-8 Off": {
+ "plugin": "Alias",
+ "from": {
+ "sender": { "const": "Test Wait" },
+ "id": { "const": "A2-8 On" },
+ "event": { "const": "finished" }
+ },
+ "to": [
+ { "target": "A2-8", "command": "set state", "new state": false },
+ { "target": "Test Wait", "command": "wait",
+ "seconds": 0.1, "id": "A2-8 Off" }
+ ]
+ }
+}