diff --git a/MultiNode-Tester/MultiNode-Tester.ino b/MultiNode-Tester/MultiNode-Tester.ino index 0abaa3a..9ede24e 100644 --- a/MultiNode-Tester/MultiNode-Tester.ino +++ b/MultiNode-Tester/MultiNode-Tester.ino @@ -2,7 +2,7 @@ #include #include #include -#include +#include ´ #define CLIENT_ADDRESS 1 @@ -36,7 +36,7 @@ RHReliableDatagram rfManager(rfm95, CLIENT_ADDRESS); TemperatureZero TempZero = TemperatureZero(); -void setup() +void setup() { // Pins with internal functions pinMode(BOOSTEN_PIN, OUTPUT); @@ -44,75 +44,84 @@ void setup() 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); + /*// Acivating Motor and Digital Outputs, but at LOW level + pinMode(10, OUTPUT); + digitalWrite(10, LOW); + pinMode(12, OUTPUT); + digitalWrite(12, LOW); - // Configuring ADC - analogReference(AR_INTERNAL2V23); - analogReadResolution(12); + pinMode(6, OUTPUT); + digitalWrite(6, LOW); + pinMode(7, OUTPUT); + digitalWrite(7, LOW); - - SerialUSB.begin(9600); - //while (!SerialUSB) ; // Wait for serial port to be available + // explcitly activate inputs, to avoid conflicts + pinMode(38, INPUT); + pinMode(2, INPUT); + pinMode(5, INPUT); + pinMode(11, INPUT); - - if (!rfManager.init()) + // 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 + 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); + // 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); + // 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); + // on-chip teperature sensor + TempZero.init(); - // Print Status Report - PrintSR(); + // 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); - // switch of LED after startup + // Print Status Report + PrintSR(); + + // switch of LED after startup + digitalWrite(LED_BUILTIN, LOW); + rfm95.sleep();*/ digitalWrite(LED_BUILTIN, LOW); + for (int i = 0; i < 10; i++) + { + digitalWrite(LED_BUILTIN, HIGH); + delay(100); + digitalWrite(LED_BUILTIN, LOW); + delay(100); + } } @@ -125,17 +134,40 @@ uint8_t buf[RH_RF95_MAX_MESSAGE_LEN]; void loop() { + while (!USBDevice.connected()) + { + LowPower.sleep(1000); + USBDevice.init(); + } + for (int i = 0; i < 10; i++) + { + digitalWrite(LED_BUILTIN, HIGH); + delay(100); + digitalWrite(LED_BUILTIN, LOW); + delay(100); + } + /*if (USBDevice.connected()) + digitalWrite(LED_BUILTIN, HIGH); + else + digitalWrite(LED_BUILTIN, LOW);*/ + return; + digitalWrite(BOOSTEN_PIN, HIGH); + delay(2000); + digitalWrite(BOOSTEN_PIN, LOW); + delay(2000); + LowPower.deepSleep(4000); + return; /* - SerialUSB.println("Sending to rf95_reliable_datagram_server"); + SerialUSB.println("Sending to rf95_reliable_datagram_server"); - // Send a message to manager_server - if (rfManager.sendtoWait(data, sizeof(data), SERVER_ADDRESS)) - { + // 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; + uint8_t from; if (rfManager.recvfromAckTimeout(buf, &len, 2000, &from)) { SerialUSB.print("got reply from : 0x"); @@ -147,29 +179,29 @@ void loop() { SerialUSB.println("No reply, is rf95_reliable_datagram_server running?"); } - } - else + } + else SerialUSB.println("sendtoWait failed"); */ - + // TICK-ROUTINE - if(LoopState && millis() > nextTick) + if (LoopState && millis() > nextTick) { - switch (LoopState){ + switch (LoopState) { case 1: // Read and print Currents - SerialUSB.print(float(analogRead(A1))/DIVIDER_mA, 3); + SerialUSB.print(float(analogRead(A1)) / DIVIDER_mA, 3); SerialUSB.print("\t"); - SerialUSB.print(float(analogRead(A2))/DIVIDER_mA, 3); + SerialUSB.print(float(analogRead(A2)) / DIVIDER_mA, 3); SerialUSB.print("\t"); - SerialUSB.println(float(analogRead(A3))/DIVIDER_mA, 3); + 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(float(analogRead(A1)) / DIVIDER_V, 3); SerialUSB.print("\t"); - SerialUSB.print(float(analogRead(A2))/DIVIDER_V, 3); + SerialUSB.print(float(analogRead(A2)) / DIVIDER_V, 3); SerialUSB.print("\t"); - SerialUSB.println(float(analogRead(A3))/DIVIDER_V, 3); + SerialUSB.println(float(analogRead(A3)) / DIVIDER_V, 3); break; case 3: // Read and print all GPIO and DigINPUTs @@ -191,11 +223,11 @@ void loop() break; case 4: // Read and Print Battery Voltage - SerialUSB.println(float(analogRead(BATMON_PIN))/918, 3); + SerialUSB.println(float(analogRead(BATMON_PIN)) / 918, 3); break; case 5: // Read and Print internal Temperature - SerialUSB.println(TempZero.readInternalTemperature()); + SerialUSB.println(TempZero.readInternalTemperature()); break; } @@ -208,7 +240,7 @@ void loop() inByte = SerialUSB.read(); SerialUSB.read(); - if(LoopState == 1) + if (LoopState == 1) digitalWrite(BOOSTEN_PIN, LOW); //else if(LoopState == 4) // msTick = 500; @@ -234,7 +266,7 @@ void loop() 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; @@ -246,24 +278,24 @@ void loop() default: LoopState = 0; PrintSR(); - break; - } + break; + } } -/* -delay(5000); -rfm95.sleep(); -delay(5000); -LowPower.deepSleep(10000); -*/ + /* + delay(5000); + rfm95.sleep(); + delay(5000); + LowPower.deepSleep(10000); + */ } -void PrintSR(){ +void PrintSR() { // Status Report: Dumps a bunch of usefull information to SerialUSB - // Toggle LED + // Toggle LED digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); SerialUSB.println("**************************************"); @@ -290,20 +322,20 @@ void PrintSR(){ SerialUSB.println("********** ATSAMD21G18A MCU *********"); SerialUSB.print("Internal Temperature (°C): "); - SerialUSB.println(TempZero.readInternalTemperature()); + SerialUSB.println(TempZero.readInternalTemperature()); SerialUSB.print("Battery Voltage (V): "); - SerialUSB.println(float(analogRead(BATMON_PIN))/918); + SerialUSB.println(float(analogRead(BATMON_PIN)) / 918); - if(digitalRead(LED_BUILTIN)) + if (digitalRead(LED_BUILTIN)) SerialUSB.println("Built-In LED ON"); else SerialUSB.println("Built-In LED OFF"); - - if(digitalRead(BOOSTEN_PIN)) + + 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."); diff --git a/MultiNode/Configurator.py b/MultiNode/Configurator.py index 7480d43..47412e2 100644 --- a/MultiNode/Configurator.py +++ b/MultiNode/Configurator.py @@ -36,7 +36,7 @@ client_address = 0x1234 server_address = 0x0001 # In ms -sensor_update_interval = 3000 +sensor_update_interval = 5000 device_update_interval = 7000 jitter = 1000 diff --git a/MultiNode/MNLib.cpp b/MultiNode/MNLib.cpp index 6034b97..eb79d64 100644 --- a/MultiNode/MNLib.cpp +++ b/MultiNode/MNLib.cpp @@ -2,6 +2,7 @@ #define FLASH_DEBUG 0 #define EEPROM_EMULATION_SIZE 1024 #include +#include MNConfiguration configuration; RH_RF95 radio(RF_SS_PIN, RF_IRQ_PIN); @@ -16,6 +17,9 @@ uint32_t last_server_message_id = 0; //void init_mn(bool is_client = true) void initMN() { + if (batteryVoltage() < 3.5 && !USBDevice.connected()) // Shut off below this voltage, down to 3.1 V should be OK if the regulator has a dropout of 400 mV and conducts fully if its output is below 3.3 V + LowPower.deepSleep(); + //_is_client = is_client; EEPROM.setCommitASAP(false); // Don't unnecessarily write to the EEPROM @@ -90,6 +94,7 @@ void initMN() temperature_sensor.init(); initializeDevices(); + initRTC(); } void test() @@ -266,6 +271,8 @@ bool send(uint8_t data[], uint8_t len) 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? + radio.setModeIdle(); + radio.sleep(); return success; } @@ -276,6 +283,8 @@ bool receive() if (radio.recv(buffer, &len)) { + radio.setModeIdle(); + radio.sleep(); uint16_t target_address = buffer[0] + ((uint16_t)buffer[1] << 8); if ((target_address == configuration.client_address || target_address == 0xFFFF) && buffer[2] == (configuration.server_address & 0xFF) && buffer[3] == (configuration.server_address >> 8 & 0xFF)) { @@ -344,9 +353,7 @@ DeviceBase* getDevice(uint8_t pointer) device->pin = 0xFF; } else - { memcpy(reinterpret_cast(device) + 4, &configuration_memory[pointer], device->size()); - } return device; } @@ -401,3 +408,41 @@ void sendDeviceData() memcpy(&data[5], &temperature, 4); send(data, 9); } + +void initRTC() // https://github.com/arduino-libraries/RTCZero/blob/master/src/RTCZero.cpp +{ + PM->APBAMASK.reg |= PM_APBAMASK_RTC; +#ifndef CRYSTALLESS + SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_ONDEMAND | + SYSCTRL_XOSC32K_RUNSTDBY | + SYSCTRL_XOSC32K_EN32K | + SYSCTRL_XOSC32K_XTALEN | + SYSCTRL_XOSC32K_STARTUP(6) | + SYSCTRL_XOSC32K_ENABLE; +#endif + + GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(4); + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); +#ifdef CRYSTALLESS + GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(2) | GCLK_GENCTRL_DIVSEL ); +#else + GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_ID(2) | GCLK_GENCTRL_DIVSEL ); +#endif + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); + GCLK->CLKCTRL.reg = (uint32_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK2 | (RTC_GCLK_ID << GCLK_CLKCTRL_ID_Pos))); + while (GCLK->STATUS.bit.SYNCBUSY); + + RTC->MODE0.CTRL.reg &= ~RTC_MODE0_CTRL_ENABLE; // disable RTC + + RTC->MODE0.CTRL.reg |= RTC_MODE0_CTRL_SWRST; // software reset + + RTC->MODE0.CTRL.reg = RTC_MODE0_CTRL_PRESCALER_DIV1024 | RTC_MODE0_CTRL_MODE_COUNT32; + RTC->MODE0.CTRL.reg &= ~RTC_MODE0_CTRL_MATCHCLR; // disable clear on match + + RTC->MODE0.CTRL.reg |= RTC_MODE0_CTRL_ENABLE; // enable RTC +} + +uint32_t getRTC() +{ + return RTC->MODE1.COUNT.reg; +} diff --git a/MultiNode/MNLib.h b/MultiNode/MNLib.h index 4bafcfc..ffb9586 100644 --- a/MultiNode/MNLib.h +++ b/MultiNode/MNLib.h @@ -189,4 +189,6 @@ void storeDevice(uint8_t pointer, DeviceBase* device); // Stores a device at the void loopMN(); void sendSensorData(); void sendDeviceData(); +void initRTC(); +uint32_t getRTC(); #endif diff --git a/MultiNode/MultiNode.ino b/MultiNode/MultiNode.ino index 3e866da..1cc92a1 100644 --- a/MultiNode/MultiNode.ino +++ b/MultiNode/MultiNode.ino @@ -5,48 +5,39 @@ //#define IS_CLIENT //#define IS_SERVER - unsigned long tick_tracker_sensors = 0; unsigned long tick_tracker_device = 0; unsigned long next_tick_sensors = 0; unsigned long next_tick_device = 0; - - - -// Class to manage message delivery and receipt, using the rfm95 declared above -//RHReliableDatagram rfManager(rfm95, CLIENT_ADDRESS); - -// Internal on-chip Temperature sensor - +unsigned long offset = 0; void setup() { SerialUSB.begin(115200); //while (!SerialUSB); /*for (int i = 0; i < 5; i++) - { + { delay(1000); SerialUSB.println("owo"); - }*/ + }*/ initMN(); /*for (int i = 0; i < 2; i++) - { + { delay(1000); SerialUSB.println("awa"); - } - printStatusReport();*/ + } + printStatusReport();*/ tick_tracker_sensors = millis(); tick_tracker_device = 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 @@ -70,7 +61,7 @@ void loop() SerialUSB.println(configuration.devices[i]); if (configuration.devices[i] != 255) { - for(int j = 0; j < sizeof(AnalogInput); j++) + for (int j = 0; j < sizeof(AnalogInput); j++) { SerialUSB.print(reinterpret_cast(devices[i])[j]); SerialUSB.print(" "); @@ -79,15 +70,13 @@ void loop() 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.print("RTC time: "); + SerialUSB.println(RTC->MODE1.COUNT.reg); SerialUSB.println("END"); break; @@ -104,24 +93,24 @@ void loop() SerialUSB.println(sizeof(AnalogInput) - 4); SerialUSB.println("END"); break; - + case 'r': // Read configuration (Frequency, which sensors etc.) SerialUSB.write(reinterpret_cast(&configuration), config_size); break; case 'w': - SerialUSB.readBytes(reinterpret_cast(&configuration), config_size); + SerialUSB.readBytes(reinterpret_cast(&configuration), config_size); refreshConfig(); tick_tracker_sensors = millis(); tick_tracker_device = millis(); break; - + case 'R': // Read configuration memory (extended configuration for each sensor) SerialUSB.write(reinterpret_cast(&configuration_memory), sizeof(configuration_memory)); break; case 'W': - SerialUSB.readBytes(reinterpret_cast(&configuration_memory), sizeof(configuration_memory)); + SerialUSB.readBytes(reinterpret_cast(&configuration_memory), sizeof(configuration_memory)); refreshConfig(); break; @@ -129,7 +118,7 @@ void loop() char data[sizeof(MNConfiguration)]; SerialUSB.readBytes(reinterpret_cast(&configuration) + config_size, sizeof(configuration.client_secret_key) + sizeof(configuration.server_secret_key)); SerialUSB.write(reinterpret_cast(&configuration) + config_size, sizeof(configuration.client_secret_key) + sizeof(configuration.server_secret_key)); - + refreshConfig(); break; @@ -140,7 +129,9 @@ void loop() loopMN(); - if (millis() >= next_tick_sensors) + long now_time = millis() + offset; + + if (now_time >= next_tick_sensors) { sendSensorData(); tick_tracker_sensors = tick_tracker_sensors + configuration.sensor_update_interval; @@ -150,7 +141,7 @@ void loop() digitalWrite(LED_BUILTIN, LOW); } - if (millis() >= next_tick_device) + if (now_time >= next_tick_device) { sendDeviceData(); tick_tracker_device = tick_tracker_device + configuration.device_update_interval; @@ -159,4 +150,25 @@ void loop() delay(2); digitalWrite(LED_BUILTIN, LOW); } + + long next_tick_device_dt = next_tick_device - now_time; + long next_tick_sensors_dt = next_tick_sensors - now_time; + long min_delay = min(next_tick_device_dt, next_tick_sensors_dt); + + if (min_delay > 1000 && !USBDevice.connected()) + { + power_sleep(min_delay); + offset += min_delay; + //USBDevice.init() + } +} + +void power_sleep(uint32_t milliseconds) +{ + radio.setModeIdle(); + //radio.sleep(); Doesn't work + setLoopPower(OFF); + LowPower.sleep(milliseconds); + radio.setModeIdle(); + delay(100); } diff --git a/Readme.md b/Readme.md index 26d5307..e5219db 100644 --- a/Readme.md +++ b/Readme.md @@ -1,18 +1,24 @@ Setup: - Install SAMD boards in Arduino IDE - Install Arduino Low Power - Install TemperatureZero - Install libraries from MultiNode/Libraries - Upload MultiNode/MultiNode.ino - Execute MultiNode/Configurator.py while it is attached + Install SAMD boards in Arduino IDE + Install Arduino Low Power + Install TemperatureZero + Install libraries from MultiNode/Libraries + Upload MultiNode/MultiNode.ino + Execute MultiNode/Configurator.py while it is attached RasPi: - # Install https://www.airspayce.com/mikem/bcm2835/ on Raspberry Pi (maybe not) - sudo apt-get install pip - pip install numpy pyLoraRFM9x pyblake2 toml + # Install https://www.airspayce.com/mikem/bcm2835/ on Raspberry Pi (maybe not) + 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 + C++ programs need to be executed with sudo, or set permissions to use bcm2835 as user - Execute RasPi/RXTest.py \ No newline at end of file + Execute RasPi/RXTest.py + +Reconfiguring a node: + + Attach it via USB + Press the reset button (left of USB connector) + It will not enter sleep until it is unplugged \ No newline at end of file