This commit is contained in:
Terra 2022-04-24 03:37:42 +02:00
commit 2355f290de
14 changed files with 1480 additions and 0 deletions

180
Data Format.svg Normal file
View File

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg5"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
sodipodi:docname="Data Format.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="1.3336319"
inkscape:cx="412.78255"
inkscape:cy="344.17292"
inkscape:window-width="1600"
inkscape:window-height="863"
inkscape:window-x="0"
inkscape:window-y="37"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
inkscape:snap-bbox="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-text-baseline="true">
<inkscape:grid
type="xygrid"
id="grid844"
spacingx="1.0583333"
spacingy="1.0583333"
empspacing="4" />
</sodipodi:namedview>
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';opacity:0.991674;fill:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
x="54.60339"
y="98.907654"
id="text1937"><tspan
sodipodi:role="line"
id="tspan1935"
style="font-size:4.23333px;stroke-width:0.264583"
x="54.60339"
y="98.907654">TX ID 16</tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';opacity:0.991674;fill:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
x="117.6166"
y="98.534546"
id="text8299"><tspan
sodipodi:role="line"
id="tspan8297"
style="stroke-width:0.264583"
x="117.6166"
y="98.534546">Length 8</tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';opacity:0.991674;fill:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
x="144.06046"
y="98.534538"
id="text11065"><tspan
sodipodi:role="line"
id="tspan11063"
style="stroke-width:0.264583"
x="144.06046"
y="98.534538">Data 8 * Length</tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';opacity:0.991674;fill:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
x="186.03824"
y="98.944862"
id="text12091"><tspan
sodipodi:role="line"
id="tspan12089"
style="stroke-width:0.264583"
x="186.03824"
y="98.944862">Hash 64</tspan></text>
<rect
style="opacity:0.991674;fill:none;stroke:#000000;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="rect12767"
width="25.4"
height="8.4666662"
x="50.799999"
y="93.133331" />
<rect
style="opacity:0.991674;fill:none;stroke:#000000;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="rect12835"
width="25.4"
height="8.4666662"
x="114.3"
y="93.133331" />
<rect
style="opacity:0.991674;fill:none;stroke:#000000;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="rect12837"
width="42.333332"
height="8.4666662"
x="139.7"
y="93.133331" />
<rect
style="opacity:0.991674;fill:none;stroke:#000000;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="rect12839"
width="25.4"
height="8.4666662"
x="182.03333"
y="93.133331" />
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';opacity:0.991674;fill:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
x="28.811686"
y="98.907654"
id="text1937-3"><tspan
sodipodi:role="line"
id="tspan1935-6"
style="font-size:4.23333px;stroke-width:0.264583"
x="28.811686"
y="98.907654">RX ID 16</tspan></text>
<rect
style="opacity:0.991674;fill:none;stroke:#000000;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="rect12767-7"
width="25.4"
height="8.4666662"
x="25.4"
y="93.133331" />
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';opacity:0.991674;fill:none;stroke:#000000;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
x="131.23332"
y="80.433334"
id="text18838"><tspan
sodipodi:role="line"
id="tspan18836"
style="stroke-width:1.05833"></tspan></text>
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';opacity:0.991674;fill:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
x="95.25412"
y="98.497345"
id="text12091-5"><tspan
sodipodi:role="line"
id="tspan12089-3"
style="text-align:center;text-anchor:middle;stroke-width:0.264583"
x="95.25412"
y="98.497345">Message ID 32</tspan></text>
<rect
style="opacity:0.991674;fill:none;stroke:#000000;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
id="rect20031"
width="38.099998"
height="8.4666662"
x="76.199997"
y="93.133331" />
<text
xml:space="preserve"
style="font-size:4.23333px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';opacity:0.991674;fill:#000000;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;paint-order:markers stroke fill"
x="71.53672"
y="65.040993"
id="text1937-5"><tspan
sodipodi:role="line"
id="tspan1935-62"
style="font-size:4.23333px;stroke-width:0.264583"
x="71.53672"
y="65.040993">Max 251 Bytes</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,313 @@
#include <ArduinoLowPower.h>
#include <RHReliableDatagram.h>
#include <RH_RF95.h>
#include <SPI.h>
#include <TemperatureZero.h>
#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2
#define MODEM_CONFIG RH_RF95::Bw125Cr45Sf128
#define MODEM_POWER 13
#define MODEM_FREQ 868.0
const int RF_SS_PIN = 1;
const int RF_IRQ_PIN = 0;
const int BATMON_PIN = A5;
const int BOOSTEN_PIN = 27;
const float DIVIDER_mA = 183.677;
const float DIVIDER_V = 408.5;
byte LoopState = 0;
char inByte;
unsigned long nextTick;
unsigned int msTick = 500;
// RFM95 connected to Pin 1 for SS an Pin 0 for interrupt
RH_RF95 rfm95(RF_SS_PIN, RF_IRQ_PIN);
// Class to manage message delivery and receipt, using the rfm95 declared above
RHReliableDatagram rfManager(rfm95, CLIENT_ADDRESS);
// Internal on-chip Temperature sensor
TemperatureZero TempZero = TemperatureZero();
void setup()
{
// Pins with internal functions
pinMode(BOOSTEN_PIN, OUTPUT);
digitalWrite(BOOSTEN_PIN, LOW);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
// Acivating Motor and Digital Outputs, but at LOW level
pinMode(10, OUTPUT);
digitalWrite(10, LOW);
pinMode(12, OUTPUT);
digitalWrite(12, LOW);
pinMode(6, OUTPUT);
digitalWrite(6, LOW);
pinMode(7, OUTPUT);
digitalWrite(7, LOW);
// explcitly activate inputs, to avoid conflicts
pinMode(38, INPUT);
pinMode(2, INPUT);
pinMode(5, INPUT);
pinMode(11, INPUT);
// Configuring ADC
analogReference(AR_INTERNAL2V23);
analogReadResolution(12);
SerialUSB.begin(9600);
//while (!SerialUSB) ; // Wait for serial port to be available
if (!rfManager.init())
SerialUSB.println("init failed");
rfm95.setModemConfig(MODEM_CONFIG);
// Defaults after init are 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
// The default transmitter power is 13dBm, using PA_BOOST.
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
// you can set transmitter powers from 2 to 20 dBm:
rfm95.setTxPower(MODEM_POWER, false);
rfm95.setFrequency(MODEM_FREQ);
// You can optionally require this module to wait until Channel Activity
// Detection shows no activity on the channel before transmitting by setting
// the CAD timeout to non-zero:
// rfm95.setCADTimeout(10000);
// on-chip teperature sensor
TempZero.init();
// activate all five "outputs" for a short period during startup (left to right)
digitalWrite(BOOSTEN_PIN, HIGH);
delay(1000);
digitalWrite(BOOSTEN_PIN,LOW);
digitalWrite(10, HIGH);
delay(1000);
digitalWrite(10,LOW);
digitalWrite(12, HIGH);
delay(1000);
digitalWrite(12,LOW);
digitalWrite(6, HIGH);
delay(1000);
digitalWrite(6,LOW);
digitalWrite(7, HIGH);
delay(1000);
digitalWrite(7,LOW);
// Print Status Report
PrintSR();
// switch of LED after startup
digitalWrite(LED_BUILTIN, LOW);
}
// from RadioHead sample
uint8_t data[] = "Hello World!";
// Dont put this on the stack:
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
void loop()
{
/*
SerialUSB.println("Sending to rf95_reliable_datagram_server");
// Send a message to manager_server
if (rfManager.sendtoWait(data, sizeof(data), SERVER_ADDRESS))
{
SerialUSB.println("...got an ACK");
// Now wait for a reply from the server
uint8_t len = sizeof(buf);
uint8_t from;
if (rfManager.recvfromAckTimeout(buf, &len, 2000, &from))
{
SerialUSB.print("got reply from : 0x");
SerialUSB.print(from, HEX);
SerialUSB.print(": ");
SerialUSB.println((char*)buf);
}
else
{
SerialUSB.println("No reply, is rf95_reliable_datagram_server running?");
}
}
else
SerialUSB.println("sendtoWait failed");
*/
// TICK-ROUTINE
if(LoopState && millis() > nextTick)
{
switch (LoopState){
case 1: // Read and print Currents
SerialUSB.print(float(analogRead(A1))/DIVIDER_mA, 3);
SerialUSB.print("\t");
SerialUSB.print(float(analogRead(A2))/DIVIDER_mA, 3);
SerialUSB.print("\t");
SerialUSB.println(float(analogRead(A3))/DIVIDER_mA, 3);
break;
case 2: // Read and print Voltages
SerialUSB.print(float(analogRead(A1))/DIVIDER_V, 3);
SerialUSB.print("\t");
SerialUSB.print(float(analogRead(A2))/DIVIDER_V, 3);
SerialUSB.print("\t");
SerialUSB.println(float(analogRead(A3))/DIVIDER_V, 3);
break;
case 3: // Read and print all GPIO and DigINPUTs
SerialUSB.print(digitalRead(8));
SerialUSB.print("\t");
SerialUSB.print(digitalRead(9));
SerialUSB.print("\t \t");
SerialUSB.print(digitalRead(4));
SerialUSB.print("\t");
SerialUSB.print(digitalRead(3));
SerialUSB.print("\t \t \t");
SerialUSB.print(digitalRead(38));
SerialUSB.print("\t");
SerialUSB.print(digitalRead(2));
SerialUSB.print("\t \t");
SerialUSB.print(digitalRead(5));
SerialUSB.print("\t");
SerialUSB.println(digitalRead(11));
break;
case 4: // Read and Print Battery Voltage
SerialUSB.println(float(analogRead(BATMON_PIN))/918, 3);
break;
case 5: // Read and Print internal Temperature
SerialUSB.println(TempZero.readInternalTemperature());
break;
}
nextTick = millis() + msTick;
}
// SERIAL CLI
if (SerialUSB.available() > 0) {
inByte = SerialUSB.read();
SerialUSB.read();
if(LoopState == 1)
digitalWrite(BOOSTEN_PIN, LOW);
//else if(LoopState == 4)
// msTick = 500;
switch (inByte) {
case 'c':
SerialUSB.println("Currents in mA (switches in lower pos)");
LoopState = 1;
digitalWrite(BOOSTEN_PIN, HIGH);
break;
case 'v':
SerialUSB.println("Voltages in V (switches in upper pos)");
LoopState = 2;
break;
case 'd':
SerialUSB.println("GPIO and Digital INPUTS");
LoopState = 3;
break;
case 'b':
SerialUSB.println("Battery Voltage in V");
//msTick = 5000;
LoopState = 4;
break;
case 't':
SerialUSB.println("Searching for DS18B20-Sensors, connected to the four 1-Wire pins...");
SerialUSB.println("On-Chip Temperature in °C");
LoopState = 5;
break;
case 's':
SerialUSB.println("Setting Radio Module and MCU in sleep mode for 10 seconds...");
rfm95.sleep();
LowPower.deepSleep(10000);
break;
default:
LoopState = 0;
PrintSR();
break;
}
}
/*
delay(5000);
rfm95.sleep();
delay(5000);
LowPower.deepSleep(10000);
*/
}
void PrintSR(){
// Status Report: Dumps a bunch of usefull information to SerialUSB
// Toggle LED
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
SerialUSB.println("**************************************");
SerialUSB.println("********** NetMon MultiNode **********");
SerialUSB.println("**************************************");
SerialUSB.println();
SerialUSB.println(__FILE__);
SerialUSB.print(__DATE__);
SerialUSB.print(" / ");
SerialUSB.println(__TIME__);
SerialUSB.println();
SerialUSB.println("********** RFM95 LoRa Module *********");
SerialUSB.print("Client-ID: "); SerialUSB.print(rfManager.thisAddress());
SerialUSB.print("\tServer-ID: "); SerialUSB.print(SERVER_ADDRESS);
SerialUSB.print("\tRFM-Version: "); SerialUSB.println(rfm95.getDeviceVersion());
SerialUSB.print("\tRFM-Message Length: "); SerialUSB.println(rfm95.maxMessageLength());
SerialUSB.print("Modem-Config: "); SerialUSB.print(MODEM_CONFIG);
SerialUSB.print("\tFrequency: "); SerialUSB.print(MODEM_FREQ);
SerialUSB.print("\tPower: "); SerialUSB.println(MODEM_POWER);
SerialUSB.print("Last RSSI: "); SerialUSB.print(rfm95.lastRssi());
SerialUSB.print("\tFreq-Error: "); SerialUSB.print(rfm95.frequencyError());
SerialUSB.print("\tLast SNR: "); SerialUSB.println(rfm95.lastSNR());
SerialUSB.println();
SerialUSB.println("********** ATSAMD21G18A MCU *********");
SerialUSB.print("Internal Temperature (°C): ");
SerialUSB.println(TempZero.readInternalTemperature());
SerialUSB.print("Battery Voltage (V): ");
SerialUSB.println(float(analogRead(BATMON_PIN))/918);
if(digitalRead(LED_BUILTIN))
SerialUSB.println("Built-In LED ON");
else
SerialUSB.println("Built-In LED OFF");
if(digitalRead(BOOSTEN_PIN))
SerialUSB.println("LoopSupply ENABLED (12...15 V)");
else
SerialUSB.println("LoopSupply DISABLED");
SerialUSB.println();
SerialUSB.println("Send c, v, d, b or t to plot Analog and Digital readings.");
SerialUSB.println("Send s for 10 seconds of sleep mode.");
// Toggle LED back to previous state
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}

81
MultiNode/Configurator.py Normal file
View File

@ -0,0 +1,81 @@
import serial, struct
from glob import glob
port = serial.Serial(port=glob("/dev/serial/by-id/usb-Arduino_LLC_Arduino_Zero_*-if00")[0], baudrate=115200)
port.write(b"c")
config_info = {val.split(b":")[0] : int(val.split(b":")[1]) for val in port.read_until(b"END\r\n").split(b"\r\n")[:-2]}
mn_config_size = config_info[b"MNConfiguration"]
mn_cfgmem_size = config_info[b"CFGMEM"]
n_devices = config_info[b"N_DEVICES"]
mn_device_size = config_info[b"Device"]
mn_analog_input_size = config_info[b"AnalogInput"]
print(config_info)
port.write(b"r")
config = port.read(mn_config_size)
print(config)
port.write(b"R")
cfgmem = port.read(mn_cfgmem_size)
print(cfgmem)
modem_frequency = struct.unpack("<f", config[0:4])[0]
modem_power = config[4]
client_address = int.from_bytes(config[5:7], byteorder="little")
server_address = int.from_bytes(config[7:9], byteorder="little")
pointers = list(config[9 : 9 + n_devices])
modem_frequency = 868.0
modem_power = 0
client_address = 0x1234
server_address = 0x0001
def pack_device():
data = struct.pack("<bbb", 0, 0xFF, 0xFF)
assert len(data) == mn_device_size
return data
def pack_analog_input(sensor_type, pin, min_threshold, max_threshold, multiplier, offset, is_current):
data = struct.pack("<bbbffff?", 1, sensor_type, pin, min_threshold, max_threshold, multiplier, offset, is_current)
assert len(data) == mn_analog_input_size
return data
cfgmem = list(cfgmem)
cfgmem_pointer = 0x0
pointer_counter = 0
def add_to_config(entry):
global cfgmem_pointer, pointer_counter
cfgmem[cfgmem_pointer : cfgmem_pointer + len(entry)] = entry
pointers[pointer_counter] = cfgmem_pointer
cfgmem_pointer += len(entry)
pointer_counter += 1
assert cfgmem_pointer < 256
add_to_config(pack_analog_input(5, 15, -1000, 1000, 1, 0, False))
add_to_config(pack_analog_input(6, 16, 3.8, 20.5, 1, 0, True))
config = list(config)
config[0:4] = struct.pack("<f", modem_frequency)
config[4] = modem_power
config[5:7] = int.to_bytes(client_address, 2, byteorder="little")
config[7:9] = int.to_bytes(server_address, 2, byteorder="little")
config[9 : 9 + n_devices] = pointers
port.write(b"W")
port.write(bytearray(cfgmem))
port.write(b"w")
port.write(bytearray(config))
server_key = 0x2e29b257521dc792
client_key = 0x0#0x7ed64cce5b5d8e85
port.write(b"k")
port.write(struct.pack("<QQ", client_key, server_key))
#port.write(b"s")

Binary file not shown.

Binary file not shown.

Binary file not shown.

333
MultiNode/MNLib.cpp Normal file
View File

@ -0,0 +1,333 @@
#include "MNLib.h"
#define FLASH_DEBUG 0
#define EEPROM_EMULATION_SIZE 1024
#include <FlashStorage_SAMD.h>
MNConfiguration configuration;
RH_RF95 radio(RF_SS_PIN, RF_IRQ_PIN);
TemperatureZero temperature_sensor = TemperatureZero();
BLAKE2s hash_generator;
uint32_t message_id = 0;
bool _is_client;
uint8_t configuration_memory[CFGMEM];
DeviceBase* devices[N_DEVICES];
//void init_mn(bool is_client = true)
void init_mn()
{
//_is_client = is_client;
EEPROM.setCommitASAP(false); // Don't unnecessarily write to the EEPROM
pinMode(LOOPEN_PIN, OUTPUT);
digitalWrite(LOOPEN_PIN, LOW);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
pinMode(MOTOR10, OUTPUT);
digitalWrite(MOTOR10, LOW);
pinMode(MOTOR12, OUTPUT);
digitalWrite(MOTOR12, LOW);
pinMode(DOUT6, OUTPUT);
digitalWrite(DOUT6, LOW);
pinMode(DOUT7, OUTPUT);
digitalWrite(DOUT7, LOW);
// explicitly activate inputs, to avoid conflicts
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);
pinMode(DIGIN38, INPUT);
pinMode(DIGIN2, INPUT);
pinMode(DIGIN5, INPUT);
pinMode(DIGIN11, INPUT);
pinMode(GPIO8, INPUT);
pinMode(GPIO9, INPUT);
pinMode(GPIO4, INPUT);
pinMode(GPIO3, INPUT);
pinMode(BATMON_PIN, INPUT);
// Activate LED output
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, OFF);
// Configuring ADC
analogReference(AR_INTERNAL2V23);
analogReadResolution(12);
while (!radio.init())
{
SerialUSB.println("Radio init failed");
errorBlink(5);
}
loadMemory();
/*if (configuration.client_address == 0xFFFF) // Check for uninitialized flash
{
configuration.modem_frequency = 868.0;
configuration.modem_power = 10;
configuration.client_address = 0x1FFF;
configuration.server_address = 0x0001;
configuration.client_secret_key = 0;
saveMemory();
loadMemory();
}*/
// Defaults after init are 868.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
radio.setModemConfig(MODEM_CONFIG);
// You can optionally require this module to wait until Channel Activity
// Detection shows no activity on the channel before transmitting by setting
// the CAD timeout to non-zero:
radio.setCADTimeout(1000);
//radio.setHeaderFrom(1);
//radio.setHeaderTo(255);
// on-chip teperature sensor
temperature_sensor.init();
initializeDevices();
}
void test()
{
digitalWrite(LED_BUILTIN, ON);
// activate all five "outputs" for a short period during startup (left to right)
digitalWrite(LOOPEN_PIN, HIGH);
delay(1000);
digitalWrite(LOOPEN_PIN, LOW);
digitalWrite(10, HIGH);
delay(1000);
digitalWrite(10, LOW);
digitalWrite(12, HIGH);
delay(1000);
digitalWrite(12, LOW);
digitalWrite(6, HIGH);
delay(1000);
digitalWrite(6, LOW);
digitalWrite(7, HIGH);
delay(1000);
digitalWrite(7, LOW);
// switch of LED after startup
digitalWrite(LED_BUILTIN, OFF);
// Print Status Report
printStatusReport();
}
void printStatusReport()
{
// Status Report: Dumps a bunch of usefull information to SerialUSB
// Toggle LED
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
SerialUSB.println("**************************************");
SerialUSB.println("********** NetMon MultiNode **********");
SerialUSB.println("**************************************");
SerialUSB.println();
SerialUSB.println(__FILE__);
SerialUSB.print(__DATE__);
SerialUSB.print(" / ");
SerialUSB.println(__TIME__);
SerialUSB.println();
SerialUSB.println("********** RFM95 LoRa Module *********");
SerialUSB.print("Client-ID: "); SerialUSB.print(configuration.client_address);
SerialUSB.print("\tServer-ID: "); SerialUSB.print(configuration.server_address);
//SerialUSB.print("\tRFM-Version: "); SerialUSB.println(radio.getDeviceVersion());
SerialUSB.print("Modem-Config: "); SerialUSB.print(MODEM_CONFIG);
SerialUSB.print("\tFrequency: "); SerialUSB.print(MODEM_FREQ);
SerialUSB.print("\tPower: "); SerialUSB.println(MODEM_POWER);
SerialUSB.print("Last RSSI: "); SerialUSB.print(radio.lastRssi());
SerialUSB.print("\tFreq-Error: "); SerialUSB.print(radio.frequencyError());
SerialUSB.print("\tLast SNR: "); SerialUSB.println(radio.lastSNR());
SerialUSB.println();
SerialUSB.println("********** ATSAMD21G18A MCU *********");
SerialUSB.print("Internal Temperature (°C): ");
SerialUSB.println(temperature_sensor.readInternalTemperature());
SerialUSB.print("Battery Voltage (V): ");
SerialUSB.println(float(analogRead(BATMON_PIN)) / 918);
if (digitalRead(LED_BUILTIN))
SerialUSB.println("Built-In LED ON");
else
SerialUSB.println("Built-In LED OFF");
if (digitalRead(LOOPEN_PIN))
SerialUSB.println("LoopSupply ENABLED (12...15 V)");
else
SerialUSB.println("LoopSupply DISABLED");
SerialUSB.println();
// Toggle LED back to previous state
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
void setLoopPower(bool state)
{
digitalWrite(LOOPEN_PIN, state);
}
void saveMemory() // Use sparingly https://github.com/khoih-prog/FlashStorage_SAMD
{
EEPROM.put(CONFIGURATION_ADDRESS, configuration);
EEPROM.put(CONFIGURATION_MEMORY_ADDRESS, configuration_memory);
EEPROM.commit();
}
void loadMemory()
{
EEPROM.get(CONFIGURATION_ADDRESS, configuration);
EEPROM.get(CONFIGURATION_MEMORY_ADDRESS, configuration_memory);
refreshConfig();
}
void refreshConfig()
{
radio.setFrequency(configuration.modem_frequency);
// The default transmitter power is 13dBm, using PA_BOOST.
// If you are using RFM95/96/97/98 modules which uses the PA_BOOST transmitter pin, then
// you can set transmitter powers from 2 to 20 dBm:
radio.setTxPower(configuration.modem_power, false);
initializeDevices();
}
float batteryVoltage()
{
return float(analogRead(BATMON_PIN)) * 2.0 * 2.23 / 4096.0;
}
void errorBlink(int n)
{
for (int i = 0; i < n; i++)
{
delay(ERROR_BLINK_HALF_INTERVAL);
digitalWrite(LED_BUILTIN, HIGH);
delay(ERROR_BLINK_HALF_INTERVAL);
digitalWrite(LED_BUILTIN, LOW);
}
}
bool send(uint8_t data[], uint8_t len)
{
if (2 + 2 + 4 + 1 + len + HASH_LENGTH > RH_RF95_MAX_MESSAGE_LEN)
return false;
// Full data composed of RX ID, TX ID, Message ID, Length, Data, Hash
uint8_t full_data[2 + 2 + 4 + 1 + len + HASH_LENGTH];
uint8_t hash[HASH_LENGTH];
//if (_is_client)
//{
full_data[0] = configuration.server_address & 0xFF;
full_data[1] = (configuration.server_address >> 8) & 0xFF;
full_data[2] = configuration.client_address & 0xFF;
full_data[3] = (configuration.client_address >> 8) & 0xFF;
/*} else {
full_data[0] = configuration.client_address & 0xFF;
full_data[1] = (configuration.client_address >> 8) & 0xFF;
full_data[2] = configuration.server_address & 0xFF;
full_data[3] = (configuration.server_address >> 8) & 0xFF;
}*/
full_data[4] = message_id & 0xFF;
full_data[5] = (message_id >> 8) & 0xFF;
full_data[6] = (message_id >> 16) & 0xFF;
full_data[7] = (message_id >> 24) & 0xFF;
full_data[8] = len;
for (size_t i = 0; i < len; i++)
{
full_data[i + 2 + 2 + 4 + 1] = data[i];
}
hash_generator.reset(&configuration.client_secret_key, sizeof(configuration.client_secret_key), HASH_LENGTH); // Does sizeof() work here?
hash_generator.update(full_data, 2 + 2 + 4 + 1 + len);
hash_generator.finalize(hash, HASH_LENGTH);
for (size_t i = 0; i < HASH_LENGTH; i++)
{
full_data[i + 2 + 2 + 4 + 1 + len] = hash[i];
}
message_id++;
bool success = true;
success = success & radio.send(full_data, 2 + 2 + 4 + 1 + len + HASH_LENGTH);
radio.waitPacketSent(); // What does the returned value here indicate?
return success;
}
void initializeDevices()
{
for (int i = 0; i < N_DEVICES; i++)
{
devices[i] = getDevice(configuration.devices[i]);
devices[i]->initialize();
}
}
DeviceBase* getDevice(uint8_t pointer)
{
uint8_t class_type = configuration_memory[pointer];
if (class_type == 0)
{
DeviceBase* device = new Device;
device->class_type = 0;
device->sensor_type = 0xFF;
device->pin = 0xFF;
return device;
}
if (class_type == 1)
{
DeviceBase* device = new AnalogInput;
//memcpy(device + 4, &configuration_memory[pointer], sizeof(AnalogInput) - 4); // There is some 4 bytes extra at the beginning
memcpy(reinterpret_cast<uint8_t*>(device) + 4, &configuration_memory[pointer], sizeof(AnalogInput) - 4); // There is some 4 bytes extra at the beginning
return device;
}
return new Device;
}
void loopSensors()
{
for (int i = 0; i < N_DEVICES; i++)
{
devices[i]->loop();
}
}
void sendSensorData()
{
uint8_t buffer[N_DEVICES * (sizeof(float) + sizeof(uint8_t) * 2)];
uint16_t send_register = 0;
static_assert(N_DEVICES <= 16, "N_DEVICES too large");
int cnt = 0;
for (int i = 0; i < N_DEVICES; i++)
{
if (devices[i]->doSend())
{
send_register |= 1 << i;
float value = devices[i]->getValue();
memcpy(&buffer[cnt * (sizeof(float) + sizeof(uint8_t) * 2)], &devices[i]->sensor_type, sizeof(uint8_t));
memcpy(&buffer[cnt * (sizeof(float) + sizeof(uint8_t) * 2) + sizeof(uint8_t)], &devices[i]->pin, sizeof(uint8_t));
memcpy(&buffer[cnt * (sizeof(float) + sizeof(uint8_t) * 2) + sizeof(uint8_t) * 2], &value, sizeof(float));
cnt++;
}
}
uint8_t data[N_DEVICES * (sizeof(float) + sizeof(uint8_t) * 2) + sizeof(uint16_t) + sizeof(uint8_t)];
data[0] = MT_SensorStatus;
memcpy(&data[1], &send_register, sizeof(send_register));
memcpy(&data[3], &buffer, cnt * (sizeof(float) + sizeof(uint8_t) * 2));
send(data, sizeof(uint8_t) + sizeof(uint16_t) + cnt * (sizeof(float) + sizeof(uint8_t) * 2));
}

173
MultiNode/MNLib.h Normal file
View File

@ -0,0 +1,173 @@
#pragma once
#ifndef MNLib_h
#define MNLib_h
#include <RH_RF95.h>
#include <TemperatureZero.h>
#include <BLAKE2s.h>
#define ON HIGH
#define OFF LOW
#define MODEM_CONFIG RH_RF95::Bw125Cr45Sf128
#define MODEM_POWER 13
#define MODEM_FREQ 868.0
#define HASH_LENGTH 8 // 8 bytes
#define RF_SS_PIN 1
#define RF_IRQ_PIN 0
#define BATMON_PIN A5
#define LOOPEN_PIN 27
#define DIVIDER_mA_per_LSB 2.23 / 4096 / 100 * 1000 // Adding 0.5 LSB and dividing by 4095 would be more accurate (approx. 0.01 % error currently)
#define DIVIDER_V_per_LSB 2.23 / 4096 * (180000 + 51100) / 51100
// The following block is kinda unnecessary
#define A1 A1
#define A2 A2
#define A3 A3
#define GPIO8 8
#define GPIO9 9
#define GPIO4 4
#define GPIO3 3
#define DIGIN38 38
#define DIGIN2 2
#define DIGIN5 5
#define DIGIN11 11
#define MOTOR10 10
#define MOTOR12 12
#define DOUT6 6
#define DOUT7 7
#define N_DEVICES 15
#define CFGMEM 256
#define ERROR_BLINK_HALF_INTERVAL 100 // 5 Hz
#define CONFIGURATION_ADDRESS 0
#define CONFIGURATION_MEMORY_ADDRESS 256
struct MNConfiguration
{
float modem_frequency;
int8_t modem_power;
uint16_t client_address;
uint16_t server_address;
uint8_t devices[N_DEVICES];
uint64_t client_secret_key;
uint64_t server_secret_key;
} __attribute__ ((packed));
static_assert(sizeof(MNConfiguration) == 40, "MNConfiguration has the wrong size! Please edit this in the configurator too");
struct DeviceBase
{
uint8_t class_type;
uint8_t sensor_type;
uint8_t pin;
virtual void loop() = 0;
virtual float getValue() = 0;
virtual void setValue(float value) = 0;
virtual void reset() = 0;
virtual bool doSend() = 0;
virtual void initialize() = 0;
virtual void printStatus() = 0;
} __attribute__ ((packed));
static_assert(sizeof(DeviceBase) == 7, "Device size wrong");
struct Device : DeviceBase
{
void loop() {}
float getValue()
{
return NAN;
}
void setValue(float value) {}
void reset() {} // For counters / ratemeters and such
bool doSend()
{
return false;
}
void initialize() {}
void printStatus()
{
SerialUSB.println("This is a dummy device");
}
} __attribute__ ((packed));
static_assert(sizeof(Device) == 7, "Device size wrong");
struct AnalogInput : Device // 20 Bytes, each device gets about 17.06 bytes on average
{
float min_threshold;
float max_threshold;
float multiplier;
float offset;
bool is_current;
void initialize()
{
Device::initialize();
pinMode(pin, INPUT);
}
float getValue()
{
int value = analogRead(pin);
float converted_value;
if (is_current)
converted_value = value * DIVIDER_mA_per_LSB;
else
converted_value = value * DIVIDER_V_per_LSB;
if (converted_value < min_threshold || converted_value > max_threshold)
return NAN;
else
return converted_value * multiplier + offset;
};
bool doSend() {
return true;
}
void printStatus()
{
SerialUSB.print("AnalogInput on pin A");
SerialUSB.println(pin - 14);
}
} __attribute__ ((packed));
static_assert(sizeof(AnalogInput) == 24, "AnalogInput size wrong");
enum MessageType
{
MT_DeviceStatus = 1,
MT_SensorStatus = 2,
};
extern MNConfiguration configuration;
extern uint8_t configuration_memory[CFGMEM];
extern RH_RF95 radio;
extern TemperatureZero temperature_sensor;
extern BLAKE2s hash_generator;
extern uint32_t message_id;
extern DeviceBase* devices[N_DEVICES];
//void init_mn(bool is_client);
void init_mn();
void test();
void printStatusReport();
void setLoopPower(bool state);
void saveMemory();
void loadMemory();
void refreshConfig();
float batteryVoltage();
void errorBlink(int n); // Quickly blink n times
bool send(uint8_t data[], uint8_t len);
void initializeDevices();
DeviceBase* getDevice(uint8_t pointer);
void loopSensors();
void sendSensorData();
#endif

156
MultiNode/MultiNode.ino Normal file
View File

@ -0,0 +1,156 @@
#include <ArduinoLowPower.h>
#include <SPI.h>
#include "MNLib.h"
//#define IS_CLIENT
//#define IS_SERVER
byte LoopState = 0;
char inByte;
unsigned long nextTick = 0;
unsigned int msTick = 2000;
// Class to manage message delivery and receipt, using the rfm95 declared above
//RHReliableDatagram rfManager(rfm95, CLIENT_ADDRESS);
// Internal on-chip Temperature sensor
TemperatureZero TempZero = TemperatureZero();
void setup()
{
SerialUSB.begin(115200);
//while (!SerialUSB);
/*for (int i = 0; i < 5; i++)
{
delay(1000);
SerialUSB.println("owo");
}*/
init_mn();
/*for (int i = 0; i < 2; i++)
{
delay(1000);
SerialUSB.println("awa");
}
printStatusReport();*/
nextTick = millis();
}
void loop()
{
if (SerialUSB.available())
{
size_t config_size = sizeof(MNConfiguration) - sizeof(configuration.client_secret_key) - sizeof(configuration.server_secret_key); // Don't leak the secret key
char command = SerialUSB.read();
switch (command)
{
case 'C': // Print configuration for in the field debugging
SerialUSB.println("Configuration:");
SerialUSB.print("Frequency: ");
SerialUSB.print(configuration.modem_frequency);
SerialUSB.println(" MHz");
SerialUSB.print("Power: ");
SerialUSB.print(configuration.modem_power);
SerialUSB.println(" dBm");
SerialUSB.print("Client address: ");
SerialUSB.println(configuration.client_address);
SerialUSB.print("Server address: ");
SerialUSB.println(configuration.server_address);
for (int i = 0; i < N_DEVICES; i++)
{
SerialUSB.print("Pointer ");
SerialUSB.print(i);
SerialUSB.print(": ");
SerialUSB.println(configuration.devices[i]);
if (configuration.devices[i] != 255)
{
for(int j = 0; j < sizeof(AnalogInput); j++)
{
SerialUSB.print(reinterpret_cast<uint8_t*>(devices[i])[j]);
SerialUSB.print(" ");
}
SerialUSB.println();
devices[i]->printStatus();
}
}
SerialUSB.println();
SerialUSB.print("Battery voltage: ");
SerialUSB.println(batteryVoltage());
SerialUSB.println();
SerialUSB.println(sizeof(uint16_t) + 1 * (sizeof(float) + sizeof(uint8_t) * 2));
SerialUSB.println(sizeof(uint16_t));
SerialUSB.println(1 * (sizeof(float) + sizeof(uint8_t) * 2));
SerialUSB.println("END");
break;
case 'c':
SerialUSB.print("N_DEVICES: ");
SerialUSB.println(N_DEVICES);
SerialUSB.print("CFGMEM: ");
SerialUSB.println(CFGMEM);
SerialUSB.print("MNConfiguration: ");
SerialUSB.println(sizeof(MNConfiguration) - sizeof(configuration.client_secret_key) - sizeof(configuration.server_secret_key));
SerialUSB.print("Device: ");
SerialUSB.println(sizeof(Device) - 4);
SerialUSB.print("AnalogInput: ");
SerialUSB.println(sizeof(AnalogInput) - 4);
SerialUSB.println("END");
break;
case 'r': // Read configuration (Frequency, which sensors etc.)
SerialUSB.write(reinterpret_cast<char*>(&configuration), config_size);
break;
case 'w':
SerialUSB.readBytes(reinterpret_cast<char*>(&configuration), config_size);
refreshConfig();
break;
case 'R': // Read configuration memory (extended configuration for each sensor)
SerialUSB.write(reinterpret_cast<char*>(&configuration_memory), sizeof(configuration_memory));
break;
case 'W':
SerialUSB.readBytes(reinterpret_cast<char*>(&configuration_memory), sizeof(configuration_memory));
refreshConfig();
break;
case 'k':
char data[sizeof(MNConfiguration)];
SerialUSB.readBytes(reinterpret_cast<char*>(&configuration) + config_size, sizeof(configuration.client_secret_key) + sizeof(configuration.server_secret_key));
SerialUSB.write(reinterpret_cast<char*>(&configuration) + config_size, sizeof(configuration.client_secret_key) + sizeof(configuration.server_secret_key));
refreshConfig();
break;
case 's':
saveMemory();
}
}
loopSensors();
// TICK-ROUTINE
if (millis() > nextTick)
{
sendSensorData();
SerialUSB.println("s");
uint8_t data[5];
data[0] = MT_DeviceStatus;
float bv = batteryVoltage();
memcpy(&data[1], &bv, 4);
send(data, 5);
nextTick = nextTick + msTick;
digitalWrite(LED_BUILTIN, HIGH);
delay(2);
digitalWrite(LED_BUILTIN, LOW);
}
}

View File

@ -0,0 +1,224 @@
#include <ArduinoLowPower.h>
#include <SPI.h>
#include "MNLib.h"
#define IS_CLIENT
//#define IS_SERVER
byte LoopState = 0;
char inByte;
unsigned long nextTick;
unsigned int msTick = 500;
// RFM95 connected to Pin 1 for SS an Pin 0 for interrupt
RH_RF95 rfm95(RF_SS_PIN, RF_IRQ_PIN);
// Class to manage message delivery and receipt, using the rfm95 declared above
//RHReliableDatagram rfManager(rfm95, CLIENT_ADDRESS);
// Internal on-chip Temperature sensor
TemperatureZero TempZero = TemperatureZero();
void setup()
{
initialize(true);
}
// from RadioHead sample
uint8_t data[] = "Hello World!";
// Dont put this on the stack:
uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
void loop()
{
/*
SerialUSB.println("Sending to rf95_reliable_datagram_server");
// Send a message to manager_server
if (rfManager.sendtoWait(data, sizeof(data), SERVER_ADDRESS))
{
SerialUSB.println("...got an ACK");
// Now wait for a reply from the server
uint8_t len = sizeof(buf);
uint8_t from;
if (rfManager.recvfromAckTimeout(buf, &len, 2000, &from))
{
SerialUSB.print("got reply from : 0x");
SerialUSB.print(from, HEX);
SerialUSB.print(": ");
SerialUSB.println((char*)buf);
}
else
{
SerialUSB.println("No reply, is rf95_reliable_datagram_server running?");
}
}
else
SerialUSB.println("sendtoWait failed");
*/
// TICK-ROUTINE
if (LoopState && millis() > nextTick)
{
switch (LoopState) {
case 1: // Read and print Currents
SerialUSB.print(float(analogRead(A1)) / DIVIDER_mA, 3);
SerialUSB.print("\t");
SerialUSB.print(float(analogRead(A2)) / DIVIDER_mA, 3);
SerialUSB.print("\t");
SerialUSB.println(float(analogRead(A3)) / DIVIDER_mA, 3);
break;
case 2: // Read and print Voltages
SerialUSB.print(float(analogRead(A1)) / DIVIDER_V, 3);
SerialUSB.print("\t");
SerialUSB.print(float(analogRead(A2)) / DIVIDER_V, 3);
SerialUSB.print("\t");
SerialUSB.println(float(analogRead(A3)) / DIVIDER_V, 3);
break;
case 3: // Read and print all GPIO and DigINPUTs
SerialUSB.print(digitalRead(8));
SerialUSB.print("\t");
SerialUSB.print(digitalRead(9));
SerialUSB.print("\t \t");
SerialUSB.print(digitalRead(4));
SerialUSB.print("\t");
SerialUSB.print(digitalRead(3));
SerialUSB.print("\t \t \t");
SerialUSB.print(digitalRead(38));
SerialUSB.print("\t");
SerialUSB.print(digitalRead(2));
SerialUSB.print("\t \t");
SerialUSB.print(digitalRead(5));
SerialUSB.print("\t");
SerialUSB.println(digitalRead(11));
break;
case 4: // Read and Print Battery Voltage
SerialUSB.println(float(analogRead(BATMON_PIN)) / 918, 3);
break;
case 5: // Read and Print internal Temperature
SerialUSB.println(TempZero.readInternalTemperature());
break;
}
nextTick = millis() + msTick;
}
// SERIAL CLI
if (SerialUSB.available() > 0) {
inByte = SerialUSB.read();
SerialUSB.read();
if (LoopState == 1)
digitalWrite(LOOPEN_PIN, LOW);
//else if(LoopState == 4)
// msTick = 500;
switch (inByte) {
case 'c':
SerialUSB.println("Currents in mA (switches in lower pos)");
LoopState = 1;
digitalWrite(LOOPEN_PIN, HIGH);
break;
case 'v':
SerialUSB.println("Voltages in V (switches in upper pos)");
LoopState = 2;
break;
case 'd':
SerialUSB.println("GPIO and Digital INPUTS");
LoopState = 3;
break;
case 'b':
SerialUSB.println("Battery Voltage in V");
//msTick = 5000;
LoopState = 4;
break;
case 't':
SerialUSB.println("Searching for DS18B20-Sensors, connected to the four 1-Wire pins...");
SerialUSB.println("On-Chip Temperature in °C");
LoopState = 5;
break;
case 's':
SerialUSB.println("Setting Radio Module and MCU in sleep mode for 10 seconds...");
rfm95.sleep();
LowPower.deepSleep(10000);
break;
default:
LoopState = 0;
PrintSR();
break;
}
}
/*
delay(5000);
rfm95.sleep();
delay(5000);
LowPower.deepSleep(10000);
*/
}
void PrintSR() {
// Status Report: Dumps a bunch of usefull information to SerialUSB
// Toggle LED
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
SerialUSB.println("**************************************");
SerialUSB.println("********** NetMon MultiNode **********");
SerialUSB.println("**************************************");
SerialUSB.println();
SerialUSB.println(__FILE__);
SerialUSB.print(__DATE__);
SerialUSB.print(" / ");
SerialUSB.println(__TIME__);
SerialUSB.println();
SerialUSB.println("********** RFM95 LoRa Module *********");
//SerialUSB.print("Client-ID: "); SerialUSB.print(rfManager.thisAddress());
SerialUSB.print("\tServer-ID: "); SerialUSB.print(configuration.server_address);
//SerialUSB.print("\tRFM-Version: "); SerialUSB.println(rfm95.getDeviceVersion());
SerialUSB.print("Modem-Config: "); SerialUSB.print(MODEM_CONFIG);
SerialUSB.print("\tFrequency: "); SerialUSB.print(MODEM_FREQ);
SerialUSB.print("\tPower: "); SerialUSB.println(MODEM_POWER);
SerialUSB.print("Last RSSI: "); SerialUSB.print(rfm95.lastRssi());
SerialUSB.print("\tFreq-Error: "); SerialUSB.print(rfm95.frequencyError());
SerialUSB.print("\tLast SNR: "); SerialUSB.println(rfm95.lastSNR());
SerialUSB.println();
SerialUSB.println("********** ATSAMD21G18A MCU *********");
SerialUSB.print("Internal Temperature (°C): ");
SerialUSB.println(TempZero.readInternalTemperature());
SerialUSB.print("Battery Voltage (V): ");
SerialUSB.println(float(analogRead(BATMON_PIN)) / 918);
if (digitalRead(LED_BUILTIN))
SerialUSB.println("Built-In LED ON");
else
SerialUSB.println("Built-In LED OFF");
if (digitalRead(LOOPEN_PIN))
SerialUSB.println("LoopSupply ENABLED (12...15 V)");
else
SerialUSB.println("LoopSupply DISABLED");
SerialUSB.println();
SerialUSB.println("Send c, v, d, b or t to plot Analog and Digital readings.");
SerialUSB.println("Send s for 10 seconds of sleep mode.");
// Toggle LED back to previous state
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}

1
PyRH Symbolic link
View File

@ -0,0 +1 @@
/mnt/ssh/multinode/PyRH

1
RasPi Symbolic link
View File

@ -0,0 +1 @@
/mnt/ssh/multinode/RasPi

12
notes.txt Normal file
View File

@ -0,0 +1,12 @@
Setup:
Install SAMD boards in Arduino IDE
Install Arduino Low Power
Install TemperatureZero
Install libraries from MultiNode/Libraries
RasPi:
# Install https://www.airspayce.com/mikem/bcm2835/ on Raspberry Pi
sudo apt-get install pip
pip install numpy pyLoraRFM9x pyblake2 toml
C++ programs need to be executed with sudo, or set permissions to use bcm2835 as user

6
protocol.txt Normal file
View File

@ -0,0 +1,6 @@
Each node has a private key shared with the base station (each node its own key or shared between all?)
Each packet has a running number to prevent replay attacks
Public key is sent by the base station to a node during initialization
Hash is determined from RX ID + TX ID + Message ID + Length + Data + Public key + Private key