Browse Source

Add user manuals as appendices

Starbeamrainbowlabs 1 year ago
3 changed files with 355 additions and 0 deletions
  1. +37
  2. +203
  3. +115

+ 37
- 0
build View File

@ -46,6 +46,7 @@ if [[ "$#" -lt 1 ]]; then
echo -e " ${CACTION}client-watch${RS}${LC} - Auto-rebuild the client-side code on modification.";
echo -e " ${CACTION}render-initial${RS}${LC} - Render the initial report";
echo -e " ${CACTION}render-final${RS}${LC} - Render the final report";
echo -e " ${CACTION}render-manuals${RS}${LC} - Render the user manuals";
echo -e " ${CACTION}geojson-debug${RS}${LC} - Generate some GeoJSON from the raw readings for debugging purposes (paste into";
echo -e "";
@ -92,6 +93,8 @@ task_setup() {
if [[ "$?" -ne "0" ]]; then echo "${HC}pdflatex${RS} is required to render the reports."; fi
check_command bibtex true optional;
if [[ "$?" -ne "0" ]]; then echo "${HC}bibtex${RS} is required to render the reports."; fi
check_command weasyprint true optional;
if [[ "$?" -ne "0" ]]; then echo "${HC}weasyprint${RS} is required to render the user manuals."; fi
task_end $?;
task_begin "Initialising submodules";
@ -238,13 +241,47 @@ task_geojson-debug() {
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
# ██ ██ ███████ ██ ██████ ██ ██ ██ ███████
task_render-manuals() {
task_begin "Rendering README";
_render-markdown-pdf "" "README.pdf" "LoRaWAN Signal Mapping - User Manual";
task_end "$?";
task_begin "Rendering HARDWARE";
_render-markdown-pdf "" "HARDWARE.pdf" "LoRaWAN Signal Mapping - Hardware Guide";
task_end "$?";
task_render-initial() {
_render-latex-pdf "Reports/Initial-Report/Initial-Report.tex";
task_render-final() {
tasks_run render-manuals;
_render-latex-pdf "Reports/Final-Report/Final-Report.tex";
# $1 - Source file
# $2 - Destination file
# $3 - Title
_render-markdown-pdf() {
tmpfile="$(mktemp msc-project-md-pdf.XXXXXXX)";
subtask_begin "Markdown -> HTML";
pandoc -s "${source}" -o "${tmpfile}" --metadata "title=${title}";
task_end "$?";
subtask_begin "HTML -> PDF";
weasyprint "${tmpfile}" "${dest}" --stylesheet print.css;
task_end "$?";
subtask_begin "Cleanup";
rm "${tmpfile}";
task_end "$?";
# $1 - Location of top-level LaTeX file
_render-latex-pdf() {
if [[ ! -f "$1" ]]; then

+ 203
- 0
msc-project-md-pdf.ikopCXS View File

@ -0,0 +1,203 @@
<!DOCTYPE html>
<html xmlns="" lang="" xml:lang="">
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>LoRaWAN Signal Mapping - User Manual</title>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
<style type="text/css">
a.sourceLine { display: inline-block; line-height: 1.25; }
a.sourceLine { pointer-events: none; color: inherit; text-decoration: inherit; }
a.sourceLine:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode { white-space: pre; position: relative; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
@media print {
code.sourceCode { white-space: pre-wrap; }
a.sourceLine { text-indent: -1em; padding-left: 1em; }
pre.numberSource a.sourceLine
{ position: relative; left: -4em; }
pre.numberSource a.sourceLine::before
{ content: attr(title);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; pointer-events: all; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
{ }
@media screen {
a.sourceLine::before { text-decoration: underline; }
code { color: #ff0000; font-weight: bold; } /* Alert */
code { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code { color: #7d9029; } /* Attribute */
code { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code { color: #007020; font-weight: bold; } /* ControlFlow */
code { color: #4070a0; } /* Char */
code { color: #880000; } /* Constant */
code { color: #60a0b0; font-style: italic; } /* Comment */
code { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code { } /* Import */
code { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code { color: #4070a0; } /* SpecialChar */
code { color: #bb6688; } /* SpecialString */
code { color: #4070a0; } /* String */
code { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
<h1 class="title">LoRaWAN Signal Mapping - User Manual</h1>
<h1 id="msc-summer-project">Msc-Summer-Project</h1>
<p>My Msc Summer Project</p>
<p>This repository contains my Masters-level summer project. The title is <em>LoRaWAN Signal Mapping</em>. The software contained here provides a complete system for mapping and visualising the signal coverage of <a href="">The Things Network</a>.</p>
<h2 id="structure">Structure</h2>
<p>The system is split up into 2 primary parts:</p>
<ol type="1">
<li>An Arduino program that collects the data.</li>
<li>A Node.js program that stores and analyses the data</li>
<p>It’s best explained with the aid of a diagram or two:</p>
<p><img src="images/Manual%20Diagrams-Workflow.png" /></p>
<p>The above flowchart describes the workflow when using the system:</p>
<ol type="1">
<li>First, the IoT device is turned on and the TTN Listener is launched.</li>
<li>Then, the IoT device is taken around the target area that needs mapping. This can be done by anyone - the device does not require operation beyond turning it on and off once provisioned.</li>
<li>Once the device has been carried around the target area, the <code>DATA.TSV</code> file on the IoT device’s microSD card is copied off and placed on the server.</li>
<li>The server data processor is then run to fold the data into the database.</li>
<li>The AI trainer is now run on the collected data</li>
<li>The data is displayed in a web browser, with the help of a web server</li>
<p>The flow of data during use can be shown using a diagram:</p>
<p><img src="images/Manual%20Diagrams-Data%20Flow.png" /></p>
<h2 id="system-requirements">System requirements</h2>
<p>This software has a number of requirements in order to function properly:</p>
<li>A Linux-based server (<em>Ubuntu Server LTS</em> is recommended), with the following installed:
<li>Node.js v10+ with npm 6+ - see <a href="">this website</a> for instructions on how to install a recent version of Node.js (for the various application server stuff)</li>
<li>Git (for cloning the code repository)</li>
<li><code>awk</code> (for text processing by the build script)</li>
<li>Bash v4+ (for the build script)</li>
<li>PHP (for the temporary test web server; not otherwise used any static web server will do)</li>
<li>Arduino IDE (for programming the IoT device)</li>
<li>Git (for cloning the code repository)</li>
<h2 id="getting-started">Getting Started</h2>
<h3 id="step-0-initial-setup">Step 0: Initial setup</h3>
<p>Before doing anything, clone this git repository to both the server (for the TTN listener etc.) and your local machine (for programming the IoT device):</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb1-1" title="1"><span class="fu">git</span> clone</a></code></pre></div>
<p><em>If you have somehow obtained a static copy of the code (e.g. through the University’s marking system), then skip the above and use that instead.</em></p>
<p>Next, <code>cd</code> to the root of the repository and then run the setup task of the build script:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb2-1" title="1"><span class="ex">./build</span> setup</a></code></pre></div>
<p>On Windows, this should be run in <em>Git Bash</em> (accessible from the start menu when <em>Git</em> is installed - don’t forget to <code>cd</code> to the root of the repository).</p>
<h3 id="step-1-build-a-device">Step 1: Build a device</h3>
<p>First, a device must be built and provisioned. See <code></code> in this repository for detailed instructions on how to do this.</p>
<p>Once built, copy <code>iot/main/settings.custom.cpp.example</code> to <code>iot/main/settings.custom.cpp</code> and follow the instructions fill in the fields there. To do this, you’ll need to register the device using ABP (Activation By Personalisation) on <em>The Things Network</em>. <a href="">This guide</a> tells you how to do this.</p>
<p>It is suggested that the following Bash one-liner be used to generate a new encryption key in the right formats:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb3-1" title="1"><span class="fu">head</span> -c16 /dev/urandom <span class="kw">|</span> <span class="fu">od</span> -tx1 <span class="kw">|</span> <span class="fu">awk</span> <span class="st">&#39;{ gsub(/^0+\s+/, &quot;&quot;, $0); toml=$0; gsub(/\s+/, &quot;&quot;, toml); print(&quot;settings.toml format: &quot; toml); arduino=toupper($0); gsub(/\s+/, &quot;, 0x&quot;, arduino); print(&quot;settings.custom.cpp format: 0x&quot; arduino); exit }&#39;</span></a></code></pre></div>
<p><em>(Paste it into a terminal and hit enter)</em></p>
<p>Then, review <code>iot/main/settings.h</code> to make sure it matches your setup (e.g. all the pin numbers are correct)</p>
<p>Next, copy the folders in <code>iot/libraries</code> to your Arduino IDE libraries folder.</p>
<p>Finally, open <code>iot/main/main.ino</code> in the Arduino IDE and program the IoT device itself.</p>
<h3 id="step-2-the-things-network-setup">Step 2: The Things Network Setup</h3>
<p><em>This step should be completed on the server.</em></p>
<p>Once a device has been constructed, running the <em>The Things Network</em> listener is next. This requires giving the system the <em>The Things Network</em> credentials.</p>
<p>Edit the file called <code>settings.toml</code> in the root of this repository (or create it if it doesn’t exist), and make sure it contains the following:</p>
<pre class="toml"><code>[ttn]
app_id = &quot;{APPLICATION_ID}&quot;
access_key = &quot;{TTN_ACCESS_ID}&quot;
encryption_key = &quot;{ENCRYPTION_KEY_FROM_STEP_1}&quot;
port = 8883
tls = true
devices = [ &quot;{DEVICE_NAME_FROM_TTN}&quot; ]</code></pre>
<p>The <code>app_id</code> and <code>access_key</code> can be obtained from <em>The Things Network Console</em>:</p>
<p><img src="images/TTN-Main.png" /></p>
<p>The device name can be obtained from step #1, when you registered the device with <em>The Things Network</em>. Alternatively, if the device is already registered, it can be obtained from the device list if you click the “X registered devices” text.</p>
<p>With <code>settings.toml</code> filled in, you can now start TTN Listener:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb5-1" title="1"><span class="ex">./build</span> ttn-listener</a></code></pre></div>
<p>….it will display an error message if you forgot a value.</p>
<p>Now that the TTN listener is running, the IoT device can be carried around and data collected.</p>
<h3 id="step-3-processing-the-data">Step 3: Processing the data</h3>
<p><em>This step should be completed on the server.</em></p>
<p>Once data has been collected by the IoT device, it can then be processed by the Node.js server application.</p>
<p>Copy the <code>DATA.TSV</code> file from the IoT device’s microSD card to the root of the repository on the server. It is suggested that <code>scp</code> (Linux) or <a href="">WinSCP</a> be used to do this.</p>
<p>Next, fold <code>DATA.TSV</code> into the database like so:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb6-1" title="1"><span class="ex">./build</span> process-data</a></code></pre></div>
<p>Then, train the AIs on the collected data like this:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb7-1" title="1"><span class="ex">./build</span> train-ai</a></code></pre></div>
<p>The architecture of the neural networks trained can be customised by editing <code>settings.toml</code>. Check <code>server/settings.default.toml</code> for a guide on the different options available.</p>
<h3 id="step-4-viewing-the-ai-output">Step 4: Viewing the AI output</h3>
<p>With the AIs trained, the browser-based web interface can be used to display the output as a map. Any static web server will do, but the build script has one built-in (if PHP is installed) for convenience:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb8-1" title="1"><span class="ex">./build</span> server</a></code></pre></div>
<p>If an alternative server is to be used, it should serve the <code>app/</code> directory that can be found in the root of this repository.</p>
<h2 id="credits">Credits</h2>
<li>Build Engine: <a href="">Lantern Build Engine</a> (written by me; proof available upon request)</li>
<h3 id="iot-device">IoT Device</h3>
<li>Random number generation: <a href="">Entropy</a></li>
<li>LoRa Modem Driver: <a href="">LMiC</a></li>
<li>MicroSD card access: <a href="">SdFat</a></li>
<li>GPS Decoder: <a href="">TinyGPS</a></li>
<li>Free memory analyser: <a href="">Arduino-MemoryFree</a></li>
<h3 id="node.js-server">Node.js Server</h3>
<li>AI: <a href="">Brain.js</a></li>
<li>Database access: <a href="">better-sqlite3</a></li>
<li>Encryption: <a href="">aes-js</a></li>
<li>The Things Network Access: <a href="">async-mqtt</a></li>
<li>Dependency Injection: <a href="">Awilix</a></li>
<h3 id="web-interface">Web Interface</h3>
<li>Mapping Library: <a href="">Leaflet</a></li>
<li><a href="">Loading Animation</a></li>
<li>Packaging system: <a href="">Rollup</a></li>
<h2 id="useful-links">Useful Links</h2>
<li>Entropy extraction from the watchdog timer vs the internal clock:</li>

+ 115
- 0
print.css View File

@ -0,0 +1,115 @@
/* Base CSS */
* This CSS file contains (for me) logical style defaults that are easy to read.
* This file is quite often used as a starting point for other projects.
* The original version of this stylesheet lives at
* Proof that I wrote this is, as always, available upon request.
* Todo:
* <button>
* <inputs>
* <progress>
* <meter>
/* rem is relative to the html element, and em is relative to the current element. */
html { font-size:100%; }
font-family: sans-serif; /* Serif is awful :( */
background: #f3f3f3; /* Don't forget to update the @page one too for pages media */
color: #232323;
title { string-set: page-title content(text); }
/* Special tweaks for paged media (e.g. PDFs) */
@page {
font-family: sans-serif;
background: #f3f3f3; /* Set the background colour to cover the whole page */
@top-right {
content: "By ***REMOVED*** ***REMOVED***";
opacity: 0.6;
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
opacity: 0.6;
/* A small tweak to things more responsive */
iframe, object, embed, img, table {
max-width: 100%;
th, td {
margin: 4px 6px;
padding: 4px 6px;
pre { page-break-inside: avoid; break-inside: avoid; }
pre, code {
white-space: pre-wrap;
/* -moz-tab-size: 4; */
tab-size: 4;
/* todo add the rest of the textbox like inputs here */
input[type=text], input[type=number], textarea
margin: 3px 5px;
padding: 5px 8px;
background: white;
border: 0;
border-radius: 5px;
/* Utility / layout aids */
.float.left { float: left; }
.float.right { float: right; }
/* Not supported by weasyprint yet */
.flex { display: flex; }
.flex-1 { flex: 1; }
.flex-2 { flex: 2; }
.flex-3 { flex: 3; }
.flex-4 { flex: 4; }
.flex-5 { flex: 5; }
.flex-6 { flex: 6; }
.small-spacing { margin: 2px 4px; padding: 2px 4px; }
.med-spacing { margin: 5px 8px; padding: 5px 8px; }
.high-spacing { margin: 8px 10px; padding: 8px 10px; }
.text-left { text-align: left; }
.text-centre { text-align: center; }
.text-right { text-align: right; }
.small-text { font-size: 0.8rem; }
.medium-text { font-size: 1rem; }
.large-text { font-size: 1.3rem; }
.bold-text { font-weight: bolder; }
.block { display: block; }
.inline { display: inline; }
.inline.block { display: inline-block; }
.invisilink { text-decoration: none; color: inherit; }
.invisilist { list-style-type: none; margin: 5px; padding: 5px; }
.tiny-image { max-width: 100px; max-height: 100px; }
.small-image { max-width: 250px; max-height: 250px; }
.medium-image { max-width: 450px; max-height: 450px; }
.large-image { max-width: 650px; max-height: 650px; }
.img-text-middle{ vertical-align: middle; }