Implement and document Arduino software

This commit is contained in:
Philipp Matthias Schaefer 2020-12-11 14:18:46 +01:00
parent 28de70e732
commit 001c7cd568
17 changed files with 648 additions and 70 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/documentation/build/
/source/arduino/build/

116
README.md
View file

@ -21,24 +21,126 @@ includes a field indicating whether the space is open or closed.
## Arduino
## Python Script
### Arduino: Hardware
We measure the door's lock state using a reed switch. The switch is installed at
the bottom of hole for the deadbolt and gets triggered by a magnet glued to the
tip of the deadbolt.
The reed switch is connected to pin 0 and ground on an Arduino MKR1000. The
software sets up pin 0 (by default) with a pull up resistor. This prevents
currents induced into the long wire between switch an Arduino to trigger the pin
state.
A second component connected to the MKR1000 is an Arduino MKR RGB Shield that
provides a 7x12 RGB LED matrix. It is used to give visual feedback on the
software's current state.
While trying to connect to the configured WLAN, a vertical yellow line
oscillates between both sides of the display. When connecting to the WLAN fails,
a red cross is displayed. When the room is open all LEDs are green, and when it
is closed all LEDs are turned off (nobody should be there to see it anyway).
The following images is a schematic showing the connections between the three
components mentioned.
![a schematic of Arduino MKR1000 and all components physically connected to it, as described in the text](documentation/arduino.png)
The Arduino is powered by a USB charger.
If you need to change the schematic, adapt `documentation/arduino.tex` and run
`make`. This requires the following software and TeX libraries to be installed
on your systems:
- pdflatex
- convert from ImageMagick
- tikz
- circuitikz
### Arduino: Setup and Configuration
The sketch for our Arduino is located in `sources/arduino`. Before we flash the
Arduino with the sketch, we should adapt `config.h`. We can use either the
[Arduino IDE](https://www.arduino.cc/en/software) or the provided Makefile to
verify or flash the sketch.
For the Makefile to work,
[Arduino CLI](https://arduino.github.io/arduino-cli/latest/) has to be
installed. Use `make` for verification and `make install` to flash the board,
which needs to be connected to our PC via USB cable.
Verifying and flashing the sketch requires the installation of several
libraries.
First we need the core for the family of boards Arduino MKR1000
belongs to: Arduino SAMD Boards (or `arduino:samd`). Installation instructions
for [Arduino IDE](https://www.arduino.cc/en/guide/cores) and
[Arduino CLI](https://arduino.github.io/arduino-cli/latest/commands/arduino-cli_core_install/)
can be found in the respective documentation.
Then we need to make sure the following libraries are installed:
- ArduinoGraphics
- Arduino_MKRRGB
- uTimerLib
- WiFi101
Installed instructions for
[Arduino IDE](https://www.arduino.cc/en/guide/libraries) and
[Arduino CLI](https://arduino.github.io/arduino-cli/latest/commands/arduino-cli_lib_install/)
can be found in the respective documentation.
We assign a fixed IP address in the space's WiFi network to the Arduino's MAC
address. The MAC address gets printed to `Serial` on each start up. Once
connected to the configured WLAN, the IP address is also printed to `Serial`.
(TODO: MENTION WHERE TO FIND DOCUMENTATION FOR OUR DHCPD.)
### Arduino: UDP API
Once the Arduino has started successfully, it listens on the configured port
(see [sources/arduino/config.h](sources/arduino/config.h) for the default port)
for UDP packets and answers each incoming packet with the state of the door. The
state is encoded as a one byte integer, where `0` stands for closed and `1`
stands for open.
In `scripts` is the Python 3 script `test_udp_api.py`, which we use to test the
UDP API. It takes an IP address and optionally a port, queries the Arduino, and
prints out the result.
## In-Space Service
## Internet Service
## API
## License
The authors of the Arduino code license the code under the AGPL 3.0 (see
`LICENSE.AGPL`). The respective authors are listed in the in the preambles of
each source file.
`LICENSE.AGPL`). The respective authors are listed in the preambles of each
source file.
## Dependencies
The creation of the schematic image depends on the following libraries:
*TikZ* published by the copyright holders under the GPL 2, the Free
Documentation License, and the LaTeX Project Public License 1.3c
*Circuitikz* published by the copyright holders under the GPL 2 and the LaTeX
Project Public License
The Arduino code depends on the following libraries:
_Arduino Core_ published by the copyright holders under the LGPL 2.1 or later.
*Arduino SAMD Boards* published by the copyright holders under the LGPL 2.1 or later.
_SPI Master Library for Arduino Zero_ published by the copyright holders under
*SPI Master Library for Arduino Zero* published by the copyright holders under
the LGPL 2.1 or later.
_Library for Arduino WiFi Shield_ published by the copyright holders under the
*ArduinoGraphics* published by the copyright holders under the LGPL 2.1 or
later.
*Arduino_MKRRGB* published by the copyright holders under the LGPL 2.1 or later.
*uTimerLib* published by the copyright holders under the LGPL 3.
*WiFi101* published by the copyright holders under the
LGPL 2.1 or later.

33
documentation/Makefile Normal file
View file

@ -0,0 +1,33 @@
# Copyright (c):
#
# Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
#
# This file is part of the KrautStatus' Arduino code.
#
# The Clean CommonMark library is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# The Clean CommonMark library is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License along
# with the Clean CommonMark library. If not, see
# <https://www.gnu.org/licenses/>.
.PHONY: all clean
all: arduino.png
clean:
rm --recursive build
arduino.png: arduino.tex build
pdflatex -output-directory build arduino
convert -density 300 build/arduino.pdf -trim -colorspace RGB arduino.png
build:
mkdir --parents build

BIN
documentation/arduino.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

55
documentation/arduino.tex Normal file
View file

@ -0,0 +1,55 @@
\documentclass[border=1mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usepackage{circuitikz}
\begin{document}
\begin{circuitikz}[
chip/.style = {
dipchip,
hide numbers,
no topmark,
external pins width=0,
num pins=14,
}]
\ctikzset{multipoles/dipchip/width=1.6}
% Arduino MKR1000
\node [chip] (MKR1000) {};
% align=center enables line breaks in node text
\node [align=center, rotate=90] at (MKR1000.center){Arduino\\MKR1000};
\draw (MKR1000.n) -- ++(0,1) node[vcc]{};
\draw (MKR1000.s) -- ++(0,-1) node[ground] {};
\node [right, font=\tiny] at (MKR1000.bpin 4) {0};
\node [left, font=\tiny] at (MKR1000.bpin 12) {VIN};
\node [left, font=\tiny] at (MKR1000.bpin 11) {A3};
\node [left, font=\tiny] at (MKR1000.bpin 10) {A4};
% Arduino MKR RGB Shield
\node[chip, right=of MKR1000] (RGB) {};
% align=center enables line breaks in node text
\node [align=center, rotate=90] at (RGB.center){Arduino\\MKR RGB Shield};
\draw (RGB.s) -- ++(0, -1) node[ground]{};
\node [right, font=\tiny] at (RGB.bpin 3) {VIN};
\node [right, font=\tiny] at (RGB.bpin 4) {A3};
\node [right, font=\tiny] at (RGB.bpin 5) {A4};
\draw (MKR1000.bpin 12) -- (RGB.bpin 3);
\draw (MKR1000.bpin 11) -- (RGB.bpin 4);
\draw (MKR1000.bpin 10) -- (RGB.bpin 5);
% Reed switch
\draw (MKR1000.bpin 4) -- ++(-1, 0) to [normal open switch, name=R] ++(0, -2.96) node[ground]{};
\node [rotate=90, above] at (R) {Reed Switch};
\end{circuitikz}
\end{document}

62
scripts/test_udp_api.py Executable file
View file

@ -0,0 +1,62 @@
#!/usr/bin/env python3
"""
Test KrautStatus's UDP API for a given IP and (optionally) port.
"""
import argparse
import ipaddress
import socket
def port(string):
"Convert string to an unsigend integer that is a valid port number"
port = int(string)
if port < 0 or port > 65535:
raise ValueError()
return port
def main():
parser = argparse.ArgumentParser(__doc__)
parser.add_argument("ip",
metavar="IP",
type=ipaddress.ip_address,
help="IP address of the KrautStatus Arduino")
parser.add_argument("port",
nargs="?",
metavar="PORT",
default=12345,
type=port,
help="port that the KrautStatus Arduino listens to")
args = parser.parse_args()
receiver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
receiver.bind(('0.0.0.0', args.port))
print(f'Listening on 0.0.0.0:{args.port}.')
sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# We do not use \0 or \1 here, so that we do not trigger ourselves when
# testing the script locally (127.0.0.1)
sender.sendto(b'\2', (str(args.ip), args.port))
print(f'Sent null byte to {args.ip}:{args.port}.')
while True:
status, address = receiver.recvfrom(1)
if not address[0] == str(args.ip):
continue
if status[0] == 0:
print('Responded: door closed')
return
if status[0] == 1:
print('Resondend: door open')
return
if __name__ == "__main__":
main()

33
source/arduino/Makefile Normal file
View file

@ -0,0 +1,33 @@
# Copyright (c):
#
# Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
#
# This file is part of the KrautStatus' Arduino code.
#
# The Clean CommonMark library is free software: you can redistribute it and/or
# modify it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# The Clean CommonMark library is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License along
# with the Clean CommonMark library. If not, see
# <https://www.gnu.org/licenses/>.
.PHONY: all clean install
all:
arduino-cli compile --fqbn arduino:samd:mkr1000
clean:
rm --recursive build
install:
PORT=$(arduino-cli board list | \
grep arduino:samd:mkr1000 | \
sed -e 's/ .*//g'); \
arduino-cli upload --fqbn arduino:samd:mkr1000 --port $$PORT

View file

@ -0,0 +1,70 @@
/*
Copyright (c):
Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
This file is part of the KrautStatus' Arduino code.
The Clean CommonMark library is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
The Clean CommonMark library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
for more details.
You should have received a copy of the GNU Affero General Public License along
with the Clean CommonMark library. If not, see <https://www.gnu.org/licenses/>.
*/
#include <SPI.h>
#include <uTimerLib.h>
#include "config.h"
#include "matrix.h"
#include "sensor.h"
#include "serial.h"
#include "wifi.h"
const unsigned long int MATRIX_UPDATE_FREQUENCY = 1;
void setup() {
serial_setup();
matrix_setup();
boolean wifi_result = matrix_show_scan_and_run(wifi_setup);
if (!wifi_result) {
matrix_show_failure();
while(true);
}
sensor_setup();
start_matrix_update();
}
void loop() {
int packetSize = Udp.parsePacket();
if (packetSize) {
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(uint8_t(sensor_get_door_state()));
Udp.endPacket();
}
}
void start_matrix_update() {
TimerLib.setInterval_us(matrix_update, 1000000 / MATRIX_UPDATE_FREQUENCY);
}
void matrix_update() {
switch (sensor_get_door_state()) {
OPEN:
matrix_fill(GREEN);
break;
CLOSED:
matrix_fill(BLACK);
break;
}
}

View file

@ -19,14 +19,15 @@ You should have received a copy of the GNU Affero General Public License along
with the Clean CommonMark library. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdint.h>
// SSID and password of the WiFi network to which we broadcast the door lock's
// status.
char SSID[] = "";
char PASSWORD[] = "";
const char SSID[] = "";
const char PASSWORD[] = "";
// Port on which to listen for status requests
unsigned int PORT = 12345;
const unsigned int SERVER_PORT = 12345;
// Pin to which the reed switch is connected
uint8_t REED_PIN = 0;
const uint8_t SENSOR_PIN = 0;

87
source/arduino/matrix.cpp Normal file
View file

@ -0,0 +1,87 @@
/*
Copyright (c):
Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
This file is part of the KrautStatus' Arduino code.
The Clean CommonMark library is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
The Clean CommonMark library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
for more details.
You should have received a copy of the GNU Affero General Public License along
with the Clean CommonMark library. If not, see <https://www.gnu.org/licenses/>.
*/
#include <ArduinoGraphics.h>
#include <Arduino_MKRRGB.h>
#include <uTimerLib.h>
#include "matrix.h"
const uint32_t RED = COLOR(255, 0, 0);
const uint32_t YELLOW = COLOR(255, 255, 0);
const unsigned long int SCAN_UPDATE_FREQUENCY = 25;
void matrix_setup() {
MATRIX.begin();
}
void matrix_show_failure() {
MATRIX.stroke(RED);
MATRIX.beginDraw();
MATRIX.clear();
MATRIX.line(0, 0, MATRIX.width() - 1, MATRIX.height() - 1);
MATRIX.line(0, MATRIX.height() - 1, MATRIX.width() - 1, 0);
MATRIX.endDraw();
}
uint8_t scan_timer = 0;
void matrix_paint_scan() {
// We paint a vertical line that oscillates between the left and right border
// of the matrix. Going back and forth one pixel at a time gives us as period
// of
// 2 * width - 2
// because we do not want to remain at the borders for one tick.
scan_timer += 1;
scan_timer %= 2 * MATRIX.width() - 2;
uint8_t position = scan_timer;
if(position >= MATRIX.width())
position = 2 * MATRIX.width() - position - 2;
MATRIX.beginDraw();
MATRIX.clear();
MATRIX.line(position, 0, position, 6);
MATRIX.endDraw();
}
bool matrix_show_scan_and_run(bool(*thunk)()) {
MATRIX.stroke(YELLOW);
TimerLib.setInterval_us(matrix_paint_scan, 1000000 / SCAN_UPDATE_FREQUENCY);
bool result = thunk();
TimerLib.clearTimer();
MATRIX.beginDraw();
MATRIX.clear();
MATRIX.endDraw();
return result;
}
void matrix_fill(uint32_t color) {
MATRIX.fill(color);
MATRIX.beginDraw();
MATRIX.clear();
MATRIX.endDraw();
}

33
source/arduino/matrix.h Normal file
View file

@ -0,0 +1,33 @@
/*
Copyright (c):
Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
This file is part of the KrautStatus' Arduino code.
The Clean CommonMark library is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
The Clean CommonMark library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
for more details.
You should have received a copy of the GNU Affero General Public License along
with the Clean CommonMark library. If not, see <https://www.gnu.org/licenses/>.
*/
#define COLOR(r, g, b) (r << 16 | g << 8 | b)
const uint32_t BLACK = COLOR( 0, 0, 0);
const uint32_t GREEN = COLOR( 0, 255, 0);
void matrix_setup();
void matrix_show_failure();
bool matrix_show_scan_and_run(bool(*)());
void matrix_fill(uint32_t color);

35
source/arduino/sensor.cpp Normal file
View file

@ -0,0 +1,35 @@
/*
Copyright (c):
Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
This file is part of the KrautStatus' Arduino code.
The Clean CommonMark library is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
The Clean CommonMark library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
for more details.
You should have received a copy of the GNU Affero General Public License along
with the Clean CommonMark library. If not, see <https://www.gnu.org/licenses/>.
*/
#include <Arduino.h>
#include "sensor.h"
#include "config.h"
void sensor_setup() {
pinMode(SENSOR_PIN, INPUT_PULLUP);
}
door_state sensor_get_door_state() {
if (digitalRead(SENSOR_PIN) == HIGH)
return OPEN;
return CLOSED;
}

29
source/arduino/sensor.h Normal file
View file

@ -0,0 +1,29 @@
/*
Copyright (c):
Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
This file is part of the KrautStatus' Arduino code.
The Clean CommonMark library is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
The Clean CommonMark library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
for more details.
You should have received a copy of the GNU Affero General Public License along
with the Clean CommonMark library. If not, see <https://www.gnu.org/licenses/>.
*/
typedef enum {
CLOSED = 0,
OPEN = 1
} door_state;
void sensor_setup();
door_state sensor_get_door_state();

26
source/arduino/serial.cpp Normal file
View file

@ -0,0 +1,26 @@
/*
Copyright (c):
Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
This file is part of the KrautStatus' Arduino code.
The Clean CommonMark library is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
The Clean CommonMark library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
for more details.
You should have received a copy of the GNU Affero General Public License along
with the Clean CommonMark library. If not, see <https://www.gnu.org/licenses/>.
*/
#include <SPI.h>
void serial_setup() {
Serial.begin(9600);
}

22
source/arduino/serial.h Normal file
View file

@ -0,0 +1,22 @@
/*
Copyright (c):
Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
This file is part of the KrautStatus' Arduino code.
The Clean CommonMark library is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
The Clean CommonMark library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
for more details.
You should have received a copy of the GNU Affero General Public License along
with the Clean CommonMark library. If not, see <https://www.gnu.org/licenses/>.
*/
void serial_setup();

View file

@ -25,82 +25,43 @@ with the Clean CommonMark library. If not, see <https://www.gnu.org/licenses/>.
#include "config.h"
uint8_t OPEN = 1;
uint8_t CLOSED = 0;
WiFiUDP Udp;
void setup() {
pinMode(REED_PIN, INPUT_PULLUP);
Serial.begin(9600);
while (!Serial);
void print_mac_address() {
uint8_t mac[6];
WiFi.macAddress(mac);
Serial.print("MAC: ");
for(unsigned int i = 5; i > 0; --i) {
Serial.print(mac[i], HEX);
Serial.print(":");
}
Serial.println(mac[0], HEX);
}
boolean wifi_setup() {
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("No WiFI shield present");
// TODO: Create noShieldLoop with visual indication.
while (true);
return false;
}
print_mac_address();
while (true) {
// TODO: Visually indicate that we are trying to connect
Serial.print("Connecting to SSID: ");
Serial.println(SSID);
status = WiFi.begin(SSID, PASSWORD);
if (status == WL_CONNECTED) {
if (WiFi.begin(SSID, PASSWORD) == WL_CONNECTED) {
break;
}
// TODO: Visually indicate that we waiting for trying to connect again
delay(10000);
}
printNetworkInfo();
}
void loop() {
int reed_state = digitalRead(REED_PIN);
int packetSize = Udp.readPacket();
if (packetSize) {
Udp.beginPacket(udp.remoteIP(), Udp.remotePort());
if (reed_state == HIGH) {
Udp.write(CLOSED);
} else {
Udp.write(OPEN);
}
Udp.endPacket();
}
// TODO: Visually indicate open/closed state.
}
void printNetworkInfo() {
Serial.print("Connect to WiFi");
IPAddress ip = WiFi.localIP();
Serial.println("Connect to WiFi");
Serial.print("IP Address: ");
Serial.println(ip);
Serial.println(WiFi.localIP());
byte mac[6];
WiFi.macAddress(mac);
Serial.print("MAC address: ");
for (int i = 5; i >= 0; --i) {
if (mac[i] < 16) {
Serial.print("0");
}
Serial.print(mac[i], HEX);
if (i > 0) {
Serial.print(":");
}
}
Serial.println();
Udp.begin(SERVER_PORT);
return true;
}

27
source/arduino/wifi.h Normal file
View file

@ -0,0 +1,27 @@
/*
Copyright (c):
Philipp Matthias Schäfer (philipp.matthias.schaefer@posteo.de), 2020
This file is part of the KrautStatus' Arduino code.
The Clean CommonMark library is free software: you can redistribute it and/or
modify it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.
The Clean CommonMark library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
for more details.
You should have received a copy of the GNU Affero General Public License along
with the Clean CommonMark library. If not, see <https://www.gnu.org/licenses/>.
*/
#include <WiFi101.h>
#include <WiFiUdp.h>
extern WiFiUDP Udp;
bool wifi_setup();