diff --git a/.gitignore b/.gitignore index ee23282..3ada0e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ Range_Testing/Range_Test_Sensor/RangeTestSensor/src/RangeTestSensor.cpp Range_Testing/Range_Test_Hub/LoRaRangeTestHub/src/LoRaRangeTestHub.cpp +Range_Testing/Range_Test_Hub/LoRaRangeTestHub/src/LoRaRangeTestHub.cpp +arduinoSketchbook/hardware/* +arduinoSketchbook/libraries/* \ No newline at end of file diff --git a/Range_Testing/Range_Test_Hub/LoRaRangeTestHub/src/Webhook.txt b/Range_Testing/Range_Test_Hub/LoRaRangeTestHub/src/Webhook.txt new file mode 100644 index 0000000..c9578ca --- /dev/null +++ b/Range_Testing/Range_Test_Hub/LoRaRangeTestHub/src/Webhook.txt @@ -0,0 +1,9 @@ +{ + "name": "LoRaHubLogging", + "event": "LoRaHubLogging", + "template": "google-sheets", + "url": "https://script.google.com/<>/exec", + "requestType": "POST", + "noDefaults": false, + "rejectUnauthorized": true +} \ No newline at end of file diff --git a/arduinoSketchbook/Interrupt_Test2_Low_Power/Interrupt_Test2_Low_Power.ino b/arduinoSketchbook/Interrupt_Test2_Low_Power/Interrupt_Test2_Low_Power.ino new file mode 100644 index 0000000..209e8d7 --- /dev/null +++ b/arduinoSketchbook/Interrupt_Test2_Low_Power/Interrupt_Test2_Low_Power.ino @@ -0,0 +1,98 @@ + +/* Interrupt_Test2 + * Program to test powering down an ATmega328 and wake it up with an edge truggered interrupt. + * The interrupt used is interrupt 0, which is pin 2 on Arduino or pin 4 on + * the ATmega328. + * + * This is a low power test, based upon published workm by Nick Gammon (https://www.gammon.com.au/power). + * + * The microcontroller goes through setup() and blinks a D9 LED 3 times slowly. It enters loop() and sets up deep + * sleep mode (as deep as possible) and goes to sleep. Before doing so,interrupt 0 ia attached as falling edge truggered. + * A pushbutton is attached to interrupt 0 which grounds the pulled-up line when pressed. A falling edge (pressing the button) should + * then wake up the microcontroller which causes a triple fast blink of the LED, and continues with loop() to go back + * to deep sleep. + * + * In this test, any press of the button should cause one triple blink of the LED, regardless of how + * long that the button is pressed. After the triple blink and until the button is pressed again, the micontroller should + * be in deep sleep. TEST RESULT: verified. Deep sleep measures a few microamps using a ATmega328 not-P (accurate measurement + * is not possible with my cheap multimeter). + * + * NOTE: The initial tests performed on 8/1/24 used an internal pullup on the interrupt 0 line. Deep sleep current measured + * oen to two tenths of a microamp when the button was not pressed (contact open). When the button was pressed (contact closed), + * the deep sleep current went up to about 85 microamps. This is due to the internal pullup being about 40 Kohms. + * + * 8/5/24: I changed the code to not use the internal pullup (version 1.1). I used an external 1 Mohm resistor as the pullup, with a 0.1 uF + * capacitor between the switched line and ground. The capacitor is to filter out noise. Because the RC combination results in + * a long rise time when the switch is released, I wired the switched line to pin 1 of a 74HC14 schmitt trigger inverter input. + * I wired the output (74HC14 pin 2) to the input of the next inverter (74HC14 pin 3) and took the output of this second inverter + * (74HC14 pin 4) as the interrup signal (microcontroller pin 4). I connected thew inputs to the 4 remaining inverters to + * ground to minimize supply current draw. This reduced the deep sleep current draw to about 3.4 microamps + * when the contact is closed! NOTE: it is possible that external schmitt trigger gates are not required as the microcontroller + * may have schmitt triggers on the input pins. + * + * version 1.10, by: Bob Glicksman, 08/05/24 + * (c) 2024, Bob Glicksman, Jim Schrempp, Team Practical Projects. All rights reserved. + */ + +#include + +// CONSTANTS +const int BUTTON_PIN = 2; // the pushbutton is on digital pin 2 which is chip pin 4 +const int LED_PIN = 9; // the LED is on digital pin 9 which is chip pin 15 + + + +void setup() { +// pinMode(BUTTON_PIN, INPUT_PULLUP); // Interrupt 0 is pin 2 is chip pin 4 + pinMode(BUTTON_PIN, INPUT); // Interrupt 0 is pin 2 is chip pin 4, external pullup with schmitt trigger is used. + pinMode(LED_PIN, OUTPUT); // the LED pin is 9, chip pin 15. + blink3(500); // indicate that setup() is over and loop() begins + pinMode(LED_PIN, INPUT); // change the LED pin for lowest power consumption while sleeping + +} // end of setup() + +void loop() { + // disable the adc + ADCSRA = 0; + + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + sleep_enable(); + + noInterrupts(); // disable interrupts until we actually go to sleep. + attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), isr, FALLING); // ready the wakeup interrupt + EIFR = bit(INTF0); // clear flag for interrupt 0 + + // turn off brown-out enable in software + // BODS must be set to one and and BODSE must be set to zero within 4 clock cycles + MCUCR = bit(BODS) | bit (BODSE); + // the BODS bit is automatically cleared after 3 clock cycles + MCUCR = bit(BODS); + + interrupts(); // enable interrupts just before sleeping + sleep_cpu(); + + // This part of loop() happens only after an interrupt 0 + + pinMode(LED_PIN, OUTPUT); + blink3(250); // indicate that the cpu is awake + delay(10); // wait a few milliseconds before changing pinMode() + pinMode(LED_PIN, INPUT); + +} // end of loop() + +// simple function to blink the LED 3 times at interval "speed" (ms) +void blink3(int speed) { + for (int i = 0; i < 3; i++) { + digitalWrite(9, HIGH); + delay(speed); + digitalWrite(9, LOW); + delay(speed); + } +} // end of blink3() + +// the interrupt service routine that wakes up the microcontroller +void isr () { + sleep_disable(); // cancel sleep mode for now + detachInterrupt(digitalPinToInterrupt(BUTTON_PIN)); // preclude more interrupts due to bounce, or other + +} // end of isr() diff --git a/arduinoSketchbook/Low_Power_Testing_Tester/Low_Power_Testing_Tester.ino b/arduinoSketchbook/Low_Power_Testing_Tester/Low_Power_Testing_Tester.ino new file mode 100644 index 0000000..592454c --- /dev/null +++ b/arduinoSketchbook/Low_Power_Testing_Tester/Low_Power_Testing_Tester.ino @@ -0,0 +1,89 @@ +/* + * Project LowPowerTestingTx + * Author: Bob Glicksman + * Date: 5/04/24 + * + * Description: This is code for a tester of LoRa module low power operation. The tester + * is based upon a Particle Photon that is set up to not need Wifi (any Arduino can be used + * in its place). In addition to the Photon, a N.O. pushbutton switch is connected to ground + * at one end and Photon pin D0 on the other. The LoRa module (RYLR998) is connected as follows: + * * Vcc to Photon 3.3v (through an ammeter to measure supply current to the LoRa module). + * * GND to Photon GND + * * Tx to Photon Rx (Serial1) + * * Rx to Photon Tx (Serial1) + * * Reset is not connected + * + * The testing concept is to produce a companion device - the "hub". The hub uses its LoRa module + * to listen for a message from the tester. When a message is received, the hub prints the message + * to the USB serial port so that receipt of a proper message can be confirmed. NOTE: the same + * hub as used for LoRa range testing is used here, but the tester does not wait to receive a + * response message before powering down. + * + * The tester is assigned device number 0 and the hub is assigned device number 1. The network + * number used for testing is 3 and the baud rate to/from the LoRa modem is 115200. Otherwise, + * the default LoRa module values are used. NOTE: the tester code does not set up these + * values. The LoRa modules are set up using a PC and an FTDI USB-serial board. + * + * The software senses pressing of the pushbutton and powers up the LoRA module (mode = 0). + * After verifying power up ("+OK" received) the tester sends a short message to a companion LoRa + * module in the hub. After an "+OK" response is received from the LoRa module (indicating that + * the message has been sent to the hub, the LoRa module is powered down (mode = 1). + * + * version 1.0; 5/04/24 + */ + +//#include "Particle.h" + +// The following system directives are to disregard WiFi for Particle devices. Not needed for Arduino. +SYSTEM_MODE(SEMI_AUTOMATIC); +SYSTEM_THREAD(ENABLED); + +#define VERSION 1.00 +#define STATION_NUM 0 // housekeeping; not used in the code + +#define PUSHBUTTON D2 + +void setup() { + pinMode(PUSHBUTTON, INPUT_PULLUP); + + + Serial1.begin(115200); // the LoRa device + Serial1.setTimeout(10); // a full string is received after 10 ms of no new data from the serial buffer + delay(1000); +} // end of setup() + + +void loop() { + // test for button to be pressed + if(digitalRead(PUSHBUTTON) == LOW) { // button press detected + // power up the LoRa module + Serial1.println("AT+MODE=0"); + waitForOK(); + + // send the sensor message + Serial1.println("AT+SEND=1,5,HELLO"); + waitForOK(); + + // power down the LoRa module + Serial1.println("AT+MODE=1"); + waitForOK(); + + } + + while(digitalRead(PUSHBUTTON) == LOW); // wait for button to be released + delay(100); // wait a little while before sampling the button again + +} // end of loop() + +void waitForOK() { + // wait for data in the serial buffer + while(Serial1.available() <=0 ) {} + + // read out the whole string (note: setTimeout ensure that the whole string is read) + String receivedData = Serial1.readString(); + + // we could test for "+OK" but what would we do if we got an error? + // so we are satisfied that the LoRa module responsed to the command message + + return; +} // end of waitForOK() diff --git a/arduinoSketchbook/atmega_sensor_button/atmega_sensor_button.ino b/arduinoSketchbook/atmega_sensor_button/atmega_sensor_button.ino new file mode 100644 index 0000000..7b92d13 --- /dev/null +++ b/arduinoSketchbook/atmega_sensor_button/atmega_sensor_button.ino @@ -0,0 +1,264 @@ +/* + * Project RangeTestTx + * Author: Bob Glicksman + * Date: 4/25/24 + * + * Description: This is code for a tester of LoRa signal range. The tester is based upon + * a Particle Photon that is set up to not need Wifi (any Arduino can be used in its place). + * In addition to the Photon, a N.O. pushbutton switch is connected to ground at one end + * and Photon pin D0 on the other. The LoRa module (RYLR998) is connected as follows: + * * Vcc to Photon 3.3v + * * GND to Photon GND + * * Tx to Photon Rx (Serial1) + * * Rx to Photon Tx (Serial1) + * * Reset is not connected + * + * The testing concept is to produce a companion device - the "hub". The hub uses its LoRa module + * to listen for a message from the tester. When a message is received, the hub responds with a + * message of its own. If the tester receives the response message, it is still in range of the + * hub. + * + * The tester is assigned device number 0 and the hub is assigned device number 1. The network + * number used for testing is 3 and the baud rate to/from the LoRa modem is 115200. Otherwise, + * the default LoRa module values are used. NOTE: the tester code does not set up these + * values. The LoRa modules are set up using a PC and an FTDI USB-serial board. + * + * The software senses pressing of the pushbutton and sends a short message to a companion LoRa + * module in the hub. The message is also printed on the Photon's USB serial port for debugging + * purposes. The unit then waits 3 seconds for a response. If a response is received + * (data received from the hub, beginning with +RCV), the D7 LED is flashed three times. If no + * data is received from the hub (distance too far), the D7 LED is flashed once. + * + * version 1.0; 4/25/24 + */ + + +#define PARTIClEPROCESSOR 0 + +#if PARTIClEPROCESSOR +#include "Particle.h" +#define DEBUG_SERIAL Serial +#define TPP_LORA_DEBUG 1 + +#else // ATmega238 +#include "Arduino.h" +#include +#define BUTTON_PIN_DIGITAL 2 +#define LED_PIN_DIGITAL 10 +#define txPin 11 +#define rxPin 12 +//#define DEBUG_SERIAL Serial +//#define DEBUG_SERIAL softSerial +#define DEBUG_SERIAL myDebugSerial +NeoSWSerial DEBUG_SERIAL(rxPin, txPin); +//extern SoftwareSerial DEBUG_SERIAL = SoftwareSerial(rxPin, txPin); +#define SENSOR_DEBUG 0 // always 0 for Arduino + +#endif + +#include "tpp_LoRa.h" + +#if PARTIClEPROCESSOR +// The following system directives are to disregard WiFi for Particle devices. Not needed for Arduino. +SYSTEM_MODE(SEMI_AUTOMATIC); +SYSTEM_THREAD(ENABLED); +#endif + +#define VERSION 1.00 +#define STATION_NUM 0 // housekeeping; not used ini the code + +//#define THIS_LORA_SENSOR_ADDRESS -1 // the address of the sensor + +//Jim's addresses +#define THIS_LORA_SENSOR_ADDRESS 12648 // the address of the sensor LoRaSensor +//#define THIS_LORA_SENSOR_ADDRESS 11139 // the address of the sensor lora3 + + +#if PARTICLEPROCESSOR +// Show system, cloud connectivity, and application logs over USB +// View logs with CLI using 'particle serial monitor --follow' +SerialLogHandler logHandler(LOG_LEVEL_INFO); +#else + +#endif + +tpp_LoRa LoRa; // create an instance of the LoRa class + + +void debugPrint(String message) { + if (SENSOR_DEBUG == 1) { + DEBUG_SERIAL.print("sensor: " + message); + } +} +void debugPrintNoHeader(String message) { + if (SENSOR_DEBUG == 1) { + DEBUG_SERIAL.print(message); + } +} +void debugPrintln(String message) { + if (SENSOR_DEBUG == 1) { + DEBUG_SERIAL.println("sensor: " + message); + } +} + +void setup() { + + pinMode(LED_PIN_DIGITAL, OUTPUT); + pinMode(BUTTON_PIN_DIGITAL, INPUT_PULLUP); + +#if PARTICLEPROCESSOR + blinkTimes(2, 50); + delay(1000); + DEBUG_SERIAL.begin(9600); // the USB serial port +#else + blinkTimes(4, 50); + delay(1000); + pinMode(rxPin, INPUT); + pinMode(txPin, OUTPUT); + if (SENSOR_DEBUG == 1) { + DEBUG_SERIAL.begin(9600); + delay(250); + DEBUG_SERIAL.println("Sensor booted and running"); + } + + DEBUG_SERIAL.begin(9600); //xxx + delay(250); + DEBUG_SERIAL.println("Sensor booted and running"); + +#endif + + digitalWrite(LED_PIN_DIGITAL, HIGH); + + int error_code = LoRa.initDevice(THIS_LORA_SENSOR_ADDRESS); + if (error_code != 0) { // initialize the LoRa device + debugPrintln("error initializing LoRa device - Stopping"); + debugPrintln("hint: did you change the LoRaSensorAddress?"); + while (1) { + blinkTimes(error_code, 100); + delay(1000); + }; + }; + + if (LoRa.readSettings() != 0) { // read the settings from the LoRa device + debugPrintln("error reading LoRa settings - Stopping"); + while (1) { + blinkTimes(error_code, 100); + delay(1000); + }; + }; + + debugPrintln("Sensor ready for testing ...\n"); + digitalWrite(LED_PIN_DIGITAL, LOW); + +} // end of setup() + + +void loop() { + + String receivedData = ""; // string to hold the received LoRa data + String cmd = ""; // string to hold the command to send to the LoRa + static bool awaitingResponse = false; // when waiting for a response from the hub + static unsigned long startTime = 0; + static String lastRSSI = ""; + static String lastSNR = ""; + static int msgNum = 0; + + // test for button to be pressed and no transmission in progress + if (digitalRead(BUTTON_PIN_DIGITAL) == LOW && !awaitingResponse) { // button press detected + digitalWrite(LED_PIN_DIGITAL, HIGH); + debugPrintln(""); + debugPrintln("--------------------"); + msgNum++; + String payload = ""; + switch (msgNum) { + case 1: + payload = "HELLO m: " + String(msgNum) + " uid: " + LoRa.UID; + break; + case 2: + payload = "HELLO m: " + String(msgNum) + " p: " + LoRa.parameters; + break; + default: + payload = "HELLO m: " + String(msgNum) + " rssi: " + lastRSSI + " snr: " + lastSNR; + break; + } + LoRa.transmitMessage(String(TPP_LORA_HUB_ADDRESS), payload); + awaitingResponse = true; + startTime = millis(); + digitalWrite(LED_PIN_DIGITAL, LOW); + } + + + if (awaitingResponse) { + + if (millis() - startTime > 5000) { // wait 5 seconds for a response from the hub + awaitingResponse = false; // timed out + blinkTimes(1); + debugPrintln("timeout waiting for hub response"); + msgNum++; // bump the message number so the hub knows we missed a response + } + LoRa.checkForReceivedMessage(); + switch (LoRa.receivedMessageState) { + case RECEIVE_ERROR_MISSING_RCV: // error + awaitingResponse = false; // error + blinkTimes(7); + debugPrintln("error while waiting for response, no RCV"); + msgNum++; + break; + case RECEIVE_ERROR_COMMA_COUNT: // error + awaitingResponse = false; // error + blinkTimes(8); + debugPrintln("error while waiting for response, bad comma count"); + msgNum++; + break; + case 0: // no message + delay(5); // wait a little while before checking again + break; + case 1: // message received + debugPrintln("received data = " + LoRa.receivedData); + lastRSSI = LoRa.RSSI; + lastSNR = LoRa.SNR; + + // test for received data from the hub (denoted by "+RCV") + if (LoRa.receivedData.indexOf("+RCV") >= 0) { // will be -1 of "+RCV" not in the string + + awaitingResponse = false; // we got a response + debugPrintln("response received"); + DEBUG_SERIAL.println("response: " + LoRa.receivedData); + + if (LoRa.receivedData.indexOf("TESTOK") >= 0) { + debugPrintln("response is TESTOK"); + blinkTimes(3, 150); + } else if (LoRa.receivedData.indexOf("NOPE") >= 0) { + debugPrintln("response is NOPE"); + blinkTimes(4); + msgNum++; + } else { + debugPrintln("response is unrecognized"); + blinkTimes(5); + msgNum++; + } + } + break; + + } // end of if(Serial1.available()) + + } // end of if(awaitingResponse) + + //while(digitalRead(D0) == LOW); // wait for button to be released + //delay(1); // wait a little while before sampling the button again + +} // end of loop() + +void blinkTimes(int number) { + blinkTimes(number, 250); +} + +void blinkTimes(int number, int delayTimeMS) { + for (int i = 0; i < number; i++) { + digitalWrite(LED_PIN_DIGITAL, HIGH); + delay(delayTimeMS); + digitalWrite(LED_PIN_DIGITAL, LOW); + delay(delayTimeMS); + } + return; +} // end of blinkTimes() diff --git a/arduinoSketchbook/atmega_sensor_button/tpp_LoRa.cpp b/arduinoSketchbook/atmega_sensor_button/tpp_LoRa.cpp new file mode 100644 index 0000000..0ddbed9 --- /dev/null +++ b/arduinoSketchbook/atmega_sensor_button/tpp_LoRa.cpp @@ -0,0 +1,310 @@ +/* + tpp_LoRa.h - routines for communication with the LoRa module + created by Bob Glicksman and Jim Schrempp 2024 + as part of Team Practical Projects (tpp) + +*/ + +#include "tpp_LoRa.h" + +bool mg_LoRaBusy = false; + +void tpp_LoRa::debugPrint(String message) { + if(TPP_LORA_DEBUG == 1) { + LORA_DEBUG_SERIAL.print("tpp_LoRa: " + message); + } +} +void tpp_LoRa::debugPrintNoHeader(String message) { + if(TPP_LORA_DEBUG == 1) { + LORA_DEBUG_SERIAL.print(message); + } +} +void tpp_LoRa::debugPrintln(String message) { + if(TPP_LORA_DEBUG == 1) { + LORA_DEBUG_SERIAL.println("tpp_LoRa: " + message); + } +} + +void tpp_LoRa::clearClassVariabels() { + receivedData = ""; + loraStatus = ""; + deviceNum = ""; + payload = ""; + RSSI = ""; + SNR = ""; + receivedMessageState = 0; +} + + +// Initialize the LoRa module with settings +// rtn 0 if successful +// rtn code if failure + +int tpp_LoRa::initDevice(int deviceAddress) { + + int rtn_code = 0; + bool error = 0; + + LORA_SERIAL.begin(38400); // the LoRa device + + // check that LoRa is ready + if(sendCommand("AT") != 0) { + debugPrintln("LoRa reply bad, trying again"); + + if(sendCommand("AT") != 0) { // try again for photon 1 + //debugPrintln("LoRa is not ready"); + error = true; + rtn_code = 5; + } + } + + if(!error) { + + debugPrintln("LoRa is ready"); + + // Set the network number + if(sendCommand("AT+NETWORKID=" + String(LoRaNETWORK_NUM)) != 0) { + debugPrintln("Network ID not set"); + error = true; + rtn_code = 6; + } else if(sendCommand("AT+ADDRESS=" + String(deviceAddress)) != 0) { + debugPrintln("Device number not set"); + error = true; + rtn_code = 7; + } else if(sendCommand("AT+PARAMETER=" + String(LoRaSPREADING_FACTOR) + "," + + String(LoRaBANDWIDTH) + "," + String(LoRaCODING_RATE) + "," + String(LoRaPREAMBLE)) != 0) { + debugPrintln("Parameters not set"); + rtn_code = 8; + error = true; + } else if (sendCommand("AT+MODE=0") != 0) { + debugPrintln("Tranciever mode not set"); + error = true; + rtn_code = 9; + } else if (sendCommand("AT+BAND=915000000") != 0) { + debugPrintln("Band not set"); + error = true; + rtn_code = 10; + } else if (sendCommand("AT+CRFOP=22") != 0) { + debugPrintln("Power not set"); + error = true; + rtn_code = 11; + } else { + debugPrintln("LoRo module is initialized"); + } + } + + thisDeviceNetworkID = deviceAddress; + + return rtn_code; + +} + +// Read current settings and print them to the serial monitor +// If error then the D7 will blink twice +bool tpp_LoRa::readSettings() { + + // READ LoRa Settings + debugPrintln(""); + debugPrintln(""); + debugPrintln("-----------------"); + debugPrintln("Reading back the settings"); + + bool error = false; + + if(sendCommand("AT+UID?") != 0) { + debugPrintln("error reading UID"); + error = true; + } else { + UID = receivedData.substring(5, receivedData.length()); + UID.trim(); + } + + if(sendCommand("AT+CRFOP=22?") != 0) { + debugPrintln("error reading radio power"); + error = true; + } else if (sendCommand("AT+NETWORKID?") != 0) { + debugPrintln("error reading network id"); + error = true; + } else if(sendCommand("AT+ADDRESS?") != 0) { + debugPrintln("error reading device address"); + error = true; + } else if(sendCommand("AT+PARAMETER?") != 0) { + debugPrintln("error reading parameters"); + error = true; + } else { + // replace commas with backslashes in the parameters string + parameters = receivedData; + parameters.trim(); + parameters.replace(",", ":"); + parameters = "[" + parameters + "]"; + } + + return error; +} + +// function to send AT commands to the LoRa module +// returns 0 if successful, 1 if error, -1 if no response +// prints message and result to the serial monitor +int tpp_LoRa::sendCommand(String command) { + + if (mg_LoRaBusy) { + debugPrintln("LoRa is busy"); + return 1; + } + mg_LoRaBusy = true; + + int retcode = 0; + system_tick_t timeoutMS = 1000; + receivedData = ""; + + //debugPrintln(""); + //debugPrintln("cmd: " + command); + LORA_SERIAL.println(command); + + // wait for data available, which should be +OK or +ERR + system_tick_t starttimeMS = millis(); + int dataAvailable = 0; + //debugPrint("waiting "); + do { + dataAvailable = LORA_SERIAL.available(); + delay(10); + //debugPrintNoHeader("."); + } while ((dataAvailable == 0) && (millis() - starttimeMS < timeoutMS)) ; + //debugPrintNoHeader("\n"); + + //delay(100); // wait for the full response + + // Get the response if there is one + if(dataAvailable > 0) { + receivedData = LORA_SERIAL.readString(); + // received data has a newline at the end + //receivedData.trim(); + debugPrintln("received data = " + receivedData); + if(receivedData.indexOf("+ERR") >= 0) { + debugPrintln("LoRa error"); + retcode = 1; + } else { + debugPrintln("command worked"); + retcode = 0; + } + } else { + debugPrintln("No response from LoRa"); + retcode = -1; + } + mg_LoRaBusy = false; + return retcode; +}; + +// function to transmit a message to another LoRa device +// returns 0 if successful, 1 if error, -1 if no response +// prints message and result to the serial monitor +int tpp_LoRa::transmitMessage(String devAddress, String message){ + + String cmd = "AT+SEND=" + devAddress + "," + String(message.length()) + "," + message; + + return sendCommand(cmd); + +} + + +// If there is data on Serial1 then read it and parse it into the class variables. +// Set receivedMessageState to 1 if successful, 0 if no message, -1 to -99 if error +// If there is no data on Serial1 then clear the class variables. +void tpp_LoRa::checkForReceivedMessage() { + + if (mg_LoRaBusy) { + //debugPrintln("LoRa is busy"); + receivedMessageState = 0; + return; + } + mg_LoRaBusy = true; + + clearClassVariabels(); + + if(LORA_SERIAL.available()) { // data is in the Serial1 buffer + + //debugPrintln(""); + // debugPrintln("--------------------"); + delay(1000); // wait a bit for the complete message to have been received + receivedData = LORA_SERIAL.readString(); + // received data has a newline at the end + receivedData.trim(); + debugPrintln("received data = " + receivedData); + + + if ((receivedData.indexOf("+OK") == 0) && receivedData.length() == 3) { + + // this is the normal OK from LoRa that the previous command succeeded + debugPrintln("received data is +OK"); + receivedMessageState = 1; + + } else { + + if (receivedData.indexOf("+RCV") < 0) { + // We are expecting a +RCV message + debugPrintln("received data is not +RCV"); + receivedMessageState = RECEIVE_ERROR_MISSING_RCV; + } else { + // find the commas in received data + unsigned int commas[5]; + bool commaCountError = false; + + // find first comma + for(unsigned int i = 0; i < receivedData.length(); i++) { + if(receivedData.charAt(i) == ',') { + commas[0] = i; + break; + } + } + + // find other commas from the end to the front + int commaCount = 5; + for(unsigned int i = receivedData.length()-1; i >= commas[0]; i--) { + if(receivedData.charAt(i) == ',') { + commaCount--; + if (commaCount < 1) { + // should never happen + debugPrintln("ERROR: received data from sensor has weird comma count"); + break; + commaCountError = true; + } + commas[commaCount] = i; + } + } + + if (commaCountError) { + + // error in the received data + debugPrintln("ERROR: received data from sensor has odd comma count"); + + receivedMessageState = RECEIVE_ERROR_COMMA_COUNT; + + } else { + + // create substrings from received data + deviceNum = receivedData.substring(5, commas[0]); // skip the "+RCV=" + //charCount = receivedData.substring(commas[1] + 1, commas[2]); + payload = receivedData.substring(commas[2] + 1, commas[3]); + RSSI = receivedData.substring(commas[3] + 1, commas[4]); + SNR = receivedData.substring(commas[4] + 1, receivedData.length()); // -1 to remove the newline + + receivedMessageState = 1; + + } // end of if (commaCount != 4) + } // end of if(receivedData.indexOf("+RCV") < 0) + } // end of if ((receivedData.indexOf("+OK") == 0) && receivedData.length() == 5) + + } else { + + // no data in the Serial1 buffer + clearClassVariabels(); + } + + mg_LoRaBusy = false; + + return; +} + + + + diff --git a/arduinoSketchbook/atmega_sensor_button/tpp_LoRa.h b/arduinoSketchbook/atmega_sensor_button/tpp_LoRa.h new file mode 100644 index 0000000..8613d56 --- /dev/null +++ b/arduinoSketchbook/atmega_sensor_button/tpp_LoRa.h @@ -0,0 +1,111 @@ +/* + tpp_LoRa.h - routines for communication with the LoRa module + created by Bob Glicksman and Jim Schrempp 2024 + as part of Team Practical Projects (tpp) + +*/ +/* + The block below was recommended by CoPilot. It has nothing to do with our libary. + tpp_LoRa.h - Library for LoRa communication with the Things Plus Plus board. + Created by Bennett Marsh, 2021. + Released into the public domain. + +*/ +#ifndef tpp_LoRa_h +#define tpp_LoRa_h + +#define PARTICLEPROCESSOR 0 + +#if PARTIClEPROCESSOR +#include "Particle.h" +#define DEBUG_SERIAL Serial +#define LORA_SERIAL Serial1 +#define TPP_LORA_DEBUG 1 // set to 1 for debugging output; 0 for no debugging + +#else //ATmega328 +#include "Arduino.h" +//#include +//#include +#define LORA_SERIAL Serial +#define LORA_DEBUG_SERIAL Serial +//#define DEBUG_SERIAL softSerial +//#define DEBUG_SERIAL myDebugSerial +//NeoSWSerial DEBUG_SERIAL(rxPin, txPin); +//extern SoftwareSerial DEBUG_SERIAL; +//extern NeoSWSerial DEBUG_SERIAL; + +#define TPP_LORA_DEBUG 0 // Always set to 0 when running on Arduino + +#endif + +#define VERSION 1.00 + +#define TPP_LORA_HUB_ADDRESS 57248 // arbitrary 0 - 65535 + +typedef uint32_t system_tick_t; + +#define LoRaNETWORK_NUM 18 + +#define LoRaBANDWIDTH 9 // default 7; 7:125kHz, 8:250kHz, 9:500kHz lower is better for range but requires better + // frequency stability between the two devices + +#define LoRaSPREADING_FACTOR 11 // default 9; 7 - 11 larger is better for range but slower + // SF7 - SF9 at 125kHz, SF7 - SF10 at 250kHz, and SF7 - SF11 at 500kHz + +#define LoRaCODING_RATE 4 // default 1; 1 is faster; [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] This can result in + // small signal gains at the limit of reception, but more symbols are sent for each character. + +#define LoRaPREAMBLE 24 // 12 max unless network number is 18; + +#define RECEIVE_ERROR_MISSING_RCV -1 +#define RECEIVE_ERROR_COMMA_COUNT -2 + +// class for the LoRa module +class tpp_LoRa +{ +private: + /* data */ + void clearClassVariabels(); + void debugPrint(String message); + void debugPrintNoHeader(String message); + void debugPrintln(String message); + +public: + + // Initialize the LoRa module with settings found in the tpp_LoRa.h file + int initDevice(int devAddress); + + // Read current settings and print them to the serial monitor + // If error then return false + bool readSettings(); + + // check for a received message from the LoRa module. status in receivedMessageState + // if successful, the received data is stored in the receivedData variable + // and other class variables. If not, the class variables are set to default + void checkForReceivedMessage(); + + // function to send AT commands to the LoRa module + // returns 0 if successful, 1 if error, -1 if no response + // prints message and result to the serial monitor + int sendCommand(String command); + + // function to transmit a message to another LoRa device + // returns 0 if successful, 1 if error, -1 if no response + // prints message and result to the serial monitor + int transmitMessage(String devAddress, String message); + + int receivedMessageState = 0; // 0 = no message, 1 = message received, -1 = error + String UID = ""; + String thisDeviceNetworkID = ""; + String parameters = ""; + String receivedData = ""; + String loraStatus = ""; + String deviceNum = ""; + String payload = ""; + String RSSI = ""; + String SNR = ""; + +}; + + +#endif \ No newline at end of file