init
This commit is contained in:
commit
2355f290de
180
Data Format.svg
Normal file
180
Data Format.svg
Normal 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 |
313
MultiNode-Tester/MultiNode-Tester.ino
Normal file
313
MultiNode-Tester/MultiNode-Tester.ino
Normal 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
81
MultiNode/Configurator.py
Normal 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")
|
BIN
MultiNode/Libraries/Crypto.zip
Normal file
BIN
MultiNode/Libraries/Crypto.zip
Normal file
Binary file not shown.
BIN
MultiNode/Libraries/FlashStorage_SAMD.zip
Normal file
BIN
MultiNode/Libraries/FlashStorage_SAMD.zip
Normal file
Binary file not shown.
BIN
MultiNode/Libraries/RadioHead.zip
Normal file
BIN
MultiNode/Libraries/RadioHead.zip
Normal file
Binary file not shown.
333
MultiNode/MNLib.cpp
Normal file
333
MultiNode/MNLib.cpp
Normal 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
173
MultiNode/MNLib.h
Normal 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
156
MultiNode/MultiNode.ino
Normal 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);
|
||||
}
|
||||
}
|
224
MultiNodeTest/MultiNodeTest.ino
Normal file
224
MultiNodeTest/MultiNodeTest.ino
Normal 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));
|
||||
}
|
12
notes.txt
Normal file
12
notes.txt
Normal 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
6
protocol.txt
Normal 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
|
Loading…
Reference in New Issue
Block a user