From 9f3ffaba3079be89019e3da068c5d73d5ea45beb Mon Sep 17 00:00:00 2001 From: Vlad Utkin Date: Tue, 11 May 2021 20:43:42 +0300 Subject: [PATCH] first commit --- .gitignore | 5 + .vscode/extensions.json | 7 + .vscode/settings.json | 12 + include/README | 39 + lib/README | 46 + lib/SX12XX-LoRa/ReadMe.md | 442 ++ lib/SX12XX-LoRa/Updates.md | 20 + lib/SX12XX-LoRa/What is LoRa.md | 136 + .../12_ATmel_Sleep_with_Switch_Wakeup.ino | 112 + .../26A_GPS_Echo_Simple.ino | 46 + .../26B_GPS_Echo_Hardware_Serial.ino | 43 + .../26C_GPS_Echo_UBLOXI2C.ino | 45 + .../26_GPS_Echo/26_GPS_Echo.ino | 48 + .../28_GPS_Checker/28_GPS_Checker.ino | 272 ++ .../29_GPS_Checker_With_Display.ino | 335 ++ .../30_I2C_Scanner/30_I2C_Scanner.ino | 69 + .../31_SSD1306_SH1106_OLED_Checker.ino | 122 + .../32_GPS_Checker_Time.ino | 153 + .../34_ATmel_Sleep_with_Watchdog_Wakeup.ino | 109 + .../37_Servo_Sweep_Tester.ino | 78 + .../43_SD_Card_Test/43_SD_Card_Test.ino | 267 ++ .../45_Battery_Voltage_Read_Test.ino | 115 + .../46_I2C_FRAM_Memory_Test.ino | 154 + .../46_I2C_FRAM_Memory_Test/I2C_Scanner.h | 84 + .../48_DS18B20_Test/48_DS18B20_Test.ino | 84 + .../57_NRF24L01_Is_It_There.ino | 103 + .../61_Bluetooth_Test/61_Bluetooth_Test.ino | 48 + .../63_QuectelGPS_Firmware_Checker.ino | 62 + .../72A_GPS_Speedo_With_Display.ino | 145 + .../72_GPS_Speedo/72_GPS_Speedo.ino | 105 + .../77_ILI9341_Display_Checker.ino | 134 + .../79_Serial_Terminal/79_Serial_Terminal.ino | 55 + .../81_BME280_Test/81_BME280_Test.ino | 120 + .../88_ATMEGA328P_TEST/88_ATMEGA328P_TEST.ino | 28 + .../89_ArduinoUniqueID/89_ArduinoUniqueID.ino | 26 + .../ESP32/30_I2C_Scanner/30_I2C_Scanner.ino | 69 + .../31_SSD1306_SH1106_OLED_Checker_ESP32.ino | 86 + .../43_SD_Card_Test_ESP32.ino | 272 ++ .../44_SD_Card_Test_With_FS_ESP32.ino | 110 + .../ESP32_LoRa_Micro_Node.h | 21 + .../testfile.txt | 1062 ++++ .../45_Battery_Voltage_Read_Test_ESP32.ino | 111 + .../47_DeepSleep_Timed_Wakeup_ESP32.ino | 90 + .../50_LightSleep_Timed_Wakeup_ESP32.ino | 92 + .../51_DeepSleep_Switch_Wakeup_ESP32.ino | 87 + .../64_WiFi_Scanner_Display_ESP32.ino | 131 + .../ESP32_LoRa_Micro_Node.h | 35 + .../81_BME280_Test_ESP32.ino | 125 + .../31_SSD1306_OLED_Checker_STM32.ino | 86 + lib/SX12XX-LoRa/examples/ReadMe.md | 636 +++ .../103_LoRa_Transmitter_Detailed_Setup.ino | 188 + .../Settings.h | 46 + .../104_LoRa_Receiver_Detailed_Setup.ino | 254 + .../Settings.h | 49 + .../14_LoRa_Structure_TX.ino | 145 + .../Basics/14_LoRa_Structure_TX/Settings.h | 41 + .../15_LoRa_Structure_RX.ino | 194 + .../Basics/15_LoRa_Structure_RX/Settings.h | 44 + .../Basics/1_LED_Blink/1_LED_Blink.ino | 69 + .../2_Register_Test/2_Register_Test.ino | 402 ++ .../3_LoRa_Transmitter/3_LoRa_Transmitter.ino | 123 + .../4_LoRa_Receiver/4_LoRa_Receiver.ino | 170 + .../Basics/58_FM_Tone/58_FM_Tone.ino | 95 + .../Basics/58_FM_Tone/Settings.h | 36 + .../10_LoRa_Link_Test_Transmitter.ino | 266 + .../10_LoRa_Link_Test_Transmitter/Settings.h | 52 + .../11_LoRa_Packet_Logger_Receiver.ino | 223 + .../11_LoRa_Packet_Logger_Receiver/Settings.h | 42 + .../16_LoRa_RX_Frequency_Error_Check.ino | 190 + .../Settings.h | 47 + .../20_LoRa_Link_Test_Receiver.ino | 317 ++ .../20_LoRa_Link_Test_Receiver/Settings.h | 42 + .../33_LoRa_RSSI_Checker_With_Display.ino | 283 ++ .../Settings.h | 48 + ..._LoRa_Data_Throughput_Test_Transmitter.ino | 282 ++ .../Settings.h | 43 + .../60_LoRa_Packet_Logger_Receiver_SD.ino | 252 + .../SD_Logger_Library.h | 346 ++ .../Settings.h | 41 + ..._LoRa_Transmitter_Detailed_Setup_ESP32.ino | 189 + .../Settings.h | 46 + ...104_LoRa_Receiver_Detailed_Setup_ESP32.ino | 254 + .../Settings.h | 52 + .../3_LoRa_Transmitter/3_LoRa_Transmitter.ino | 127 + .../Pictures/ESP32_Bare_Bones_Schematic.jpg | Bin 0 -> 38883 bytes .../ESP32/Pictures/ESP32_Micro_Node.jpg | Bin 0 -> 89760 bytes .../ESP32/Pictures/ESP32_Shield.jpg | Bin 0 -> 156483 bytes .../examples/SX126x_examples/ESP32/ReadMe.md | 111 + .../8_LoRa_LowMemory_TX.ino | 154 + .../LowMemory/8_LoRa_LowMemory_TX/Settings.h | 41 + .../9_LoRa_LowMemory_RX.ino | 195 + .../LowMemory/9_LoRa_LowMemory_RX/Settings.h | 44 + .../examples/SX126x_examples/ReadMe.md | 44 + .../21_On_Off_Transmitter.ino | 307 ++ .../21_On_Off_Transmitter/Settings.h | 52 + .../22_On_Off_Receiver/22_On_Off_Receiver.ino | 297 ++ .../22_On_Off_Receiver/Settings.h | 48 + .../35_Remote_Control_Servo_Transmitter.ino | 205 + .../Settings.h | 53 + .../36_Remote_Control_Servo_Receiver.ino | 259 + .../Settings.h | 48 + .../STM32/Basics/1_LED_Blink/1_LED_Blink.ino | 69 + .../2_Register_Test/2_Register_Test.ino | 401 ++ .../3_LoRa_Transmitter/3_LoRa_Transmitter.ino | 123 + .../4_LoRa_Receiver/4_LoRa_Receiver.ino | 170 + .../17_Sensor_Transmitter.ino | 315 ++ .../Sensor/17_Sensor_Transmitter/Settings.h | 47 + .../18_Sensor_Receiver/18_Sensor_Receiver.ino | 358 ++ .../Sensor/18_Sensor_Receiver/Settings.h | 49 + .../59_Play_Star_Wars_Tune.ino | 176 + .../Silly/59_Play_Star_Wars_Tune/Settings.h | 35 + .../Silly/59_Play_Star_Wars_Tune/pitches.h | 94 + .../5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino | 242 + .../Settings.h | 45 + .../62_LoRa_Wake_on_RX_Atmel.ino | 224 + .../Sleep/62_LoRa_Wake_on_RX_Atmel/Settings.h | 44 + .../6_LoRa_RX_and_Sleep_Atmel.ino | 248 + .../6_LoRa_RX_and_Sleep_Atmel/Settings.h | 44 + .../23_GPS_Tracker_Transmitter.ino | 407 ++ .../23_GPS_Tracker_Transmitter/Settings.h | 68 + .../24_GPS_Tracker_Receiver.ino | 322 ++ .../24_GPS_Tracker_Receiver/Settings.h | 37 + ..._Tracker_Receiver_With_Display_and_GPS.ino | 624 +++ .../Settings.h | 67 + .../Tracker/38_lora_Relay/38_lora_Relay.ino | 173 + .../Tracker/38_lora_Relay/Settings.h | 53 + .../67_Balloon_Tracker_Transmitter.ino | 744 +++ .../67_Balloon_Tracker_Transmitter/Settings.h | 141 + .../68_Balloon_Tracker_Receiver.ino | 1038 ++++ .../68_Balloon_Tracker_Receiver/Settings.h | 97 + .../71_FSKRTTY_Transmitter_Test.ino | 194 + .../71_FSKRTTY_Transmitter_Test/Settings.h | 46 + .../103_Lora_Transmitter_Detailed_Setup.ino | 183 + .../Settings.h | 39 + .../104_Lora_Receiver_Detailed_Setup.ino | 250 + .../Settings.h | 44 + .../14_LoRa_Structure_TX.ino | 145 + .../Basics/14_LoRa_Structure_TX/Settings.h | 39 + .../15_LoRa_Structure_RX.ino | 194 + .../Basics/15_LoRa_Structure_RX/Settings.h | 39 + .../Basics/1_LED_Blink/1_LED_Blink.ino | 72 + .../2_Register_Test/2_Register_Test.ino | 266 + .../3_LoRa_Transmitter/3_LoRa_Transmitter.ino | 121 + .../4_LoRa_Receiver/4_LoRa_Receiver.ino | 168 + .../Basics/58_FM_Tone/58_FM_Tone.ino | 93 + .../Basics/58_FM_Tone/Settings.h | 32 + .../73_LoRa_Receiver_AFC.ino | 200 + .../Basics/73_LoRa_Receiver_AFC/Settings.h | 41 + .../80_Direct_DIO2_Tone.ino | 106 + .../Basics/80_Direct_DIO2_Tone/Settings.h | 31 + .../10_LoRa_Link_Test_Transmitter.ino | 266 + .../10_LoRa_Link_Test_Transmitter/Settings.h | 46 + .../11_LoRa_Packet_Logger_Receiver.ino | 223 + .../11_LoRa_Packet_Logger_Receiver/Settings.h | 39 + .../13_Frequency_and_Power_Check_TX.ino | 138 + .../Settings.h | 41 + .../16_LoRa_RX_Frequency_Error_Check.ino | 183 + .../Settings.h | 43 + .../20_LoRa_Link_Test_Receiver.ino | 298 ++ .../20_LoRa_Link_Test_Receiver/Settings.h | 44 + .../33_LoRa_RSSI_Checker_With_Display.ino | 283 ++ .../Settings.h | 44 + ..._LoRa_Data_Throughput_Test_Transmitter.ino | 282 ++ .../Settings.h | 42 + ...a_Data_Throughput_Acknowledge_Receiver.ino | 174 + .../Settings.h | 39 + .../60_LoRa_Packet_Logger_Receiver_SD.ino | 252 + .../SD_Logger_Library.h | 410 ++ .../Settings.h | 41 + .../66_FSK_Carrier_Generator.ino | 98 + .../66_FSK_Carrier_Generator/Settings.h | 28 + .../1_LED_Blink_ESP32/1_LED_Blink_ESP32.ino | 63 + .../2_Register_Test_ESP32.ino | 300 ++ .../3_LoRa_Transmitter_ESP32.ino | 193 + .../3_LoRa_Transmitter_ESP32/Settings.h | 44 + .../4_LoRa_Receiver_ESP32.ino | 258 + .../Basics/4_LoRa_Receiver_ESP32/Settings.h | 49 + ...Data_Throughput_Transmitter_Test_ESP32.ino | 277 ++ .../Settings.h | 46 + .../Pictures/ESP32_Bare_Bones_Schematic.jpg | Bin 0 -> 38883 bytes .../ESP32/Pictures/ESP32_Micro_Node.jpg | Bin 0 -> 89760 bytes .../ESP32/Pictures/ESP32_Shield.jpg | Bin 0 -> 214109 bytes .../examples/SX127x_examples/ESP32/ReadMe.md | 111 + .../17_Sensor_Transmitter_ESP32.ino | 308 ++ .../17_Sensor_Transmitter_ESP32/Settings.h | 49 + .../18_Sensor_Receiver_ESP32.ino | 358 ++ .../18_Sensor_Receiver_ESP32/Settings.h | 49 + .../5_LoRa_TX_Sleep_Timed_Wakeup_ESP32.ino | 268 ++ .../Settings.h | 43 + ...3_Simple_GPS_Tracker_Transmitter_ESP32.ino | 408 ++ .../Settings.h | 71 + ...er_Receiver_With_Display_and_GPS_ESP32.ino | 624 +++ .../Settings.h | 60 + .../71_FSKRTTY_Transmitter_Test_ESP32.ino | 192 + .../Settings.h | 43 + ...oon_Tracker_Transmitter_SDLogger_ESP32.ino | 898 ++++ .../Settings.h | 142 + .../40_LoRa_Transmitter_ImplicitPacket.ino | 192 + .../Settings.h | 40 + .../41_LoRa_Receiver_ImplicitPacket.ino | 258 + .../Settings.h | 45 + .../8_LoRa_LowMemory_TX.ino | 99 + .../LowMemory/8_LoRa_LowMemory_TX/Settings.h | 30 + .../9_LoRa_LowMemory_RX.ino | 168 + .../LowMemory/9_LoRa_LowMemory_RX/Settings.h | 28 + .../21_On_Off_Transmitter.ino | 306 ++ .../21_On_Off_Transmitter/Settings.h | 47 + .../22_On_Off_Receiver/22_On_Off_Receiver.ino | 297 ++ .../22_On_Off_Receiver/Settings.h | 44 + .../35_Remote_Control_Servo_Transmitter.ino | 229 + .../Settings.h | 47 + .../36_Remote_Control_Servo_Receiver.ino | 275 ++ .../Settings.h | 43 + .../86_Buffer_Transmit_Controller.ino | 127 + .../87_Buffer_Receive_Controller.ino | 176 + .../1_LED_Blink_STM32/1_LED_Blink_STM32.ino | 69 + .../2_Register_Test_STM32.ino | 300 ++ .../3_LoRa_Transmitter_STM32.ino | 182 + .../3_LoRa_Transmitter_STM32/Settings.h | 39 + .../4_LoRa_Receiver_STM32.ino | 247 + .../Basics/4_LoRa_Receiver_STM32/Settings.h | 44 + .../STM32/Pictures/Arduino_IDE_for_STM32.jpg | Bin 0 -> 44524 bytes .../STM32/Pictures/STM32_and_Shield.jpg | Bin 0 -> 237505 bytes .../examples/SX127x_examples/STM32/ReadMe.md | 38 + .../17_Sensor_Transmitter.ino | 332 ++ .../Sensor/17_Sensor_Transmitter/Settings.h | 55 + .../18_Sensor_Receiver/18_Sensor_Receiver.ino | 361 ++ .../Sensor/18_Sensor_Receiver/Settings.h | 45 + .../39_LoRa_SX127x_Temperature_Read.ino | 103 + .../59_Play_Star_Wars_Tune.ino | 175 + .../Silly/59_Play_Star_Wars_Tune/Settings.h | 32 + .../Silly/59_Play_Star_Wars_Tune/pitches.h | 94 + .../5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino | 242 + .../Settings.h | 43 + .../62_LoRa_Wake_on_RX_Atmel.ino | 224 + .../Sleep/62_LoRa_Wake_on_RX_Atmel/Settings.h | 43 + .../6_LoRa_RX_and_Sleep_Atmel.ino | 247 + .../6_LoRa_RX_and_Sleep_Atmel/Settings.h | 44 + .../7_LoRa_TX_Sleep_Switch_Wakeup_Atmel.ino | 260 + .../Settings.h | 43 + .../Sleep/Atmel Watchdog Sleep Time.jpg | Bin 0 -> 43600 bytes .../Sleep/Bare_Bones_Arduino_ATMega328.pdf | Bin 0 -> 35195 bytes .../23_GPS_Tracker_Transmitter.ino | 419 ++ .../23_GPS_Tracker_Transmitter/Settings.h | 65 + .../24_GPS_Tracker_Receiver.ino | 323 ++ .../24_GPS_Tracker_Receiver/Settings.h | 35 + ..._Tracker_Receiver_With_Display_and_GPS.ino | 634 +++ .../Settings.h | 59 + .../Tracker/38_lora_Relay/38_lora_Relay.ino | 173 + .../Tracker/38_lora_Relay/Settings.h | 51 + .../67_Balloon_Tracker_Transmitter.ino | 807 ++++ .../67_Balloon_Tracker_Transmitter/Settings.h | 143 + .../68_Balloon_Tracker_Receiver.ino | 1048 ++++ .../AFSKRTTY2_DL-Fldigi_Settings.jpg | Bin 0 -> 45618 bytes .../68_Balloon_Tracker_Receiver/Settings.h | 103 + ...Balloon_Tracker_Transmitter_GenericGPS.ino | 786 +++ .../Settings.h | 137 + .../70_AFSKRTTY_Upload_Test.ino | 98 + .../AFSKRTTY2_DL-Fldigi_Settings.jpg.jpg | Bin 0 -> 45618 bytes .../71_FSKRTTY_Transmitter_Test.ino | 183 + .../71_FSKRTTY_Transmitter_Test/Settings.h | 45 + .../103_LoRa_Transmitter_Detailed_Setup.ino | 182 + .../Settings.h | 42 + .../104_LoRa_Receiver_Detailed_Setup.ino | 247 + .../Settings.h | 44 + .../14_LoRa_Structure_TX.ino | 145 + .../Basics/14_LoRa_Structure_TX/Settings.h | 39 + .../15_LoRa_Structure_RX.ino | 194 + .../Basics/15_LoRa_Structure_RX/Settings.h | 38 + .../Basics/1_LED_Blink/1_LED_Blink.ino | 69 + .../2_Register_Test/2_Register_Test.ino | 375 ++ .../3_LoRa_Transmitter/3_LoRa_Transmitter.ino | 183 + .../Basics/3_LoRa_Transmitter/Settings.h | 38 + .../4_LoRa_Receiver/4_LoRa_Receiver.ino | 247 + .../Basics/4_LoRa_Receiver/Settings.h | 40 + .../52_FLRC_Transmitter.ino | 180 + .../Basics/52_FLRC_Transmitter/Settings.h | 40 + .../53_FLRC_Receiver/53_FLRC_Receiver.ino | 240 + .../Basics/53_FLRC_Receiver/Settings.h | 42 + .../10_LoRa_Link_Test_Transmitter.ino | 266 + .../10_LoRa_Link_Test_Transmitter/Settings.h | 47 + .../11_LoRa_Packet_Logger_Receiver.ino | 223 + .../11_LoRa_Packet_Logger_Receiver/Settings.h | 40 + .../16_LoRa_RX_Frequency_Error_Check.ino | 183 + .../Settings.h | 44 + .../20_LoRa_Link_Test_Receiver.ino | 317 ++ .../20_LoRa_Link_Test_Receiver/Settings.h | 46 + .../33_LoRa_RSSI_Checker_With_Display.ino | 283 ++ .../Settings.h | 40 + ..._LoRa_Data_Throughput_Test_Transmitter.ino | 282 ++ .../Settings.h | 42 + ..._Data_Throughput_Test_Transmitter_FLRC.ino | 294 ++ .../Settings.h | 42 + ..._LoRa_Transmitter_Detailed_Setup_ESP32.ino | 183 + .../Settings.h | 46 + ...104_LoRa_Receiver_Detailed_Setup_ESP32.ino | 247 + .../Settings.h | 51 + .../Pictures/ESP32_Bare_Bones_Schematic.jpg | Bin 0 -> 38883 bytes .../ESP32/Pictures/ESP32_Micro_Node.jpg | Bin 0 -> 89760 bytes .../examples/SX128x_examples/ESP32/ReadMe.md | 111 + .../8_LoRa_LowMemory_TX.ino | 154 + .../LowMemory/8_LoRa_LowMemory_TX/Settings.h | 38 + .../9_LoRa_LowMemory_RX.ino | 195 + .../LowMemory/9_LoRa_LowMemory_RX/Settings.h | 40 + .../Pictures/Calibration_Values.jpg | Bin 0 -> 20046 bytes .../Pictures/IMG_2531_Reduced.jpg | Bin 0 -> 227865 bytes .../Pictures/SX128XLT_Ranging_100m.jpg | Bin 0 -> 22942 bytes .../SX128X_Ranging_Calibration_Values.jpg | Bin 0 -> 18784 bytes .../54_Ranging_Master/54_Ranging_Master.ino | 212 + .../Ranging/54_Ranging_Master/Settings.h | 47 + .../55_Ranging_Slave/55_Ranging_Slave.ino | 147 + .../Ranging/55_Ranging_Slave/Settings.h | 38 + .../56_Ranging_Calibration_Checker.ino | 204 + .../56_Ranging_Calibration_Checker/Settings.h | 48 + .../Ranging/Pictures/Calibration_Values.jpg | Bin 0 -> 20046 bytes .../Ranging/Pictures/IMG_2531_Reduced.jpg | Bin 0 -> 227865 bytes .../Pictures/SX128XLT_Ranging_100m.jpg | Bin 0 -> 22942 bytes .../SX128X_Ranging_Calibration_Values.jpg | Bin 0 -> 18784 bytes .../SX128x_examples/Ranging/ReadMe.md | 113 + .../examples/SX128x_examples/ReadMe.md | 88 + .../21_On_Off_Transmitter.ino | 307 ++ .../21_On_Off_Transmitter/Settings.h | 49 + .../22_On_Off_Receiver/22_On_Off_Receiver.ino | 297 ++ .../22_On_Off_Receiver/Settings.h | 49 + .../35_Remote_Control_Servo_Transmitter.ino | 190 + .../Settings.h | 49 + .../36_Remote_Control_Servo_Receiver.ino | 256 + .../Settings.h | 46 + .../17_Sensor_Transmitter.ino | 315 ++ .../Sensor/17_Sensor_Transmitter/Settings.h | 45 + .../18_Sensor_Receiver/18_Sensor_Receiver.ino | 358 ++ .../Sensor/18_Sensor_Receiver/Settings.h | 46 + .../5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino | 242 + .../Settings.h | 40 + .../23_GPS_Tracker_Transmitter.ino | 407 ++ .../23_GPS_Tracker_Transmitter/Settings.h | 67 + .../24_GPS_Tracker_Receiver.ino | 322 ++ .../24_GPS_Tracker_Receiver/Settings.h | 44 + ..._Tracker_Receiver_With_Display_and_GPS.ino | 624 +++ .../Settings.h | 63 + .../Tracker/38_lora_Relay/38_lora_Relay.ino | 173 + .../Tracker/38_lora_Relay/Settings.h | 46 + lib/SX12XX-LoRa/keywords.txt | 191 + lib/SX12XX-LoRa/library.properties | 9 + .../pictures/Atmel Watchdog Sleep Time.jpg | Bin 0 -> 43600 bytes .../pictures/ESP32_Bare_Bones_circuit.pdf | Bin 0 -> 34095 bytes lib/SX12XX-LoRa/src/AFSKRTTY.h | 75 + lib/SX12XX-LoRa/src/AFSKRTTY2.h | 180 + lib/SX12XX-LoRa/src/AtmelSleep.h | 101 + lib/SX12XX-LoRa/src/EEPROM_Memory.h | 88 + lib/SX12XX-LoRa/src/FRAM_FM24CL64.h | 462 ++ lib/SX12XX-LoRa/src/FRAM_MB85RC16PNF.h | 447 ++ lib/SX12XX-LoRa/src/LICENSE.txt | 25 + lib/SX12XX-LoRa/src/ProgramLT_Definitions.h | 123 + lib/SX12XX-LoRa/src/QuectelSerialGPS.h | 608 +++ lib/SX12XX-LoRa/src/Quectel_HWSerialGPS.h | 474 ++ lib/SX12XX-LoRa/src/SX126XLT.cpp | 3494 ++++++++++++++ lib/SX12XX-LoRa/src/SX126XLT.h | 229 + lib/SX12XX-LoRa/src/SX126XLT_Definitions.h | 305 ++ lib/SX12XX-LoRa/src/SX127XLT.cpp | 4272 +++++++++++++++++ lib/SX12XX-LoRa/src/SX127XLT.h | 263 + lib/SX12XX-LoRa/src/SX127XLT_Definitions.h | 314 ++ lib/SX12XX-LoRa/src/SX128XLT.cpp | 2759 +++++++++++ lib/SX12XX-LoRa/src/SX128XLT.h | 197 + lib/SX12XX-LoRa/src/SX128XLT_Definitions.h | 388 ++ lib/SX12XX-LoRa/src/UBLOXI2CGPS.h | 569 +++ lib/SX12XX-LoRa/src/UBLOXSerialGPS.h | 690 +++ lib/SX12XX-LoRa/src/UBLOX_HWSerialGPS.h | 580 +++ platformio.ini | 22 + src/bmp180.h | 45 + src/bmp388.h | 40 + src/bno055.h | 160 + src/gps.h | 28 + src/lora.h | 93 + src/main copy.old | 214 + src/main.cpp | 132 + src/main.cpp.old | 150 + src/sdcard.h | 242 + src/sht31.h | 52 + src/wifi_setup.h | 47 + test/README | 11 + 381 files changed, 69596 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 include/README create mode 100644 lib/README create mode 100644 lib/SX12XX-LoRa/ReadMe.md create mode 100644 lib/SX12XX-LoRa/Updates.md create mode 100644 lib/SX12XX-LoRa/What is LoRa.md create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/12_ATmel_Sleep_with_Switch_Wakeup/12_ATmel_Sleep_with_Switch_Wakeup.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/26A_GPS_Echo_Simple/26A_GPS_Echo_Simple.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/26B_GPS_Echo_Hardware_Serial/26B_GPS_Echo_Hardware_Serial.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/26C_GPS_Echo_UBLOXI2C/26C_GPS_Echo_UBLOXI2C.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/26_GPS_Echo/26_GPS_Echo.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/28_GPS_Checker/28_GPS_Checker.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/29_GPS_Checker_With_Display/29_GPS_Checker_With_Display.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/30_I2C_Scanner/30_I2C_Scanner.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/31_SSD1306_SH1106_OLED_Checker/31_SSD1306_SH1106_OLED_Checker.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/32_GPS_Checker_Time/32_GPS_Checker_Time.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/34_ATmel_Sleep_with_Watchdog_Wakeup/34_ATmel_Sleep_with_Watchdog_Wakeup.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/37_Servo_Sweep_Tester/37_Servo_Sweep_Tester.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/43_SD_Card_Test/43_SD_Card_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/45_Battery_Voltage_Read_Test/45_Battery_Voltage_Read_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/46_I2C_FRAM_Memory_Test/46_I2C_FRAM_Memory_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/46_I2C_FRAM_Memory_Test/I2C_Scanner.h create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/48_DS18B20_Test/48_DS18B20_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/57_NRF24L01_Is_It_There/57_NRF24L01_Is_It_There.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/61_Bluetooth_Test/61_Bluetooth_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/63_QuectelGPS_Firmware_Checker/63_QuectelGPS_Firmware_Checker.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/72A_GPS_Speedo_With_Display/72A_GPS_Speedo_With_Display.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/72_GPS_Speedo/72_GPS_Speedo.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/77_ILI9341_Display_Checker/77_ILI9341_Display_Checker.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/79_Serial_Terminal/79_Serial_Terminal.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/81_BME280_Test/81_BME280_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/88_ATMEGA328P_TEST/88_ATMEGA328P_TEST.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/89_ArduinoUniqueID/89_ArduinoUniqueID.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/30_I2C_Scanner/30_I2C_Scanner.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/31_SSD1306_SH1106_OLED_Checker_ESP32/31_SSD1306_SH1106_OLED_Checker_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/43_SD_Card_Test_ESP32/43_SD_Card_Test_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/44_SD_Card_Test_With_FS_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/ESP32_LoRa_Micro_Node.h create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/testfile.txt create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/45_Battery_Voltage_Read_Test_ESP32/45_Battery_Voltage_Read_Test_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/47_DeepSleep_Timed_Wakeup_ESP32/47_DeepSleep_Timed_Wakeup_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/50_LightSleep_Timed_Wakeup_ESP32/50_LightSleep_Timed_Wakeup_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/51_DeepSleep_Switch_Wakeup_ESP32/51_DeepSleep_Switch_Wakeup_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/64_WiFi_Scanner_Display_ESP32/64_WiFi_Scanner_Display_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/64_WiFi_Scanner_Display_ESP32/ESP32_LoRa_Micro_Node.h create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/81_BME280_Test_ESP32/81_BME280_Test_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/Hardware_Checks/STM32/31_SSD1306_OLED_Checker_STM32/31_SSD1306_OLED_Checker_STM32.ino create mode 100644 lib/SX12XX-LoRa/examples/ReadMe.md create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/103_LoRa_Transmitter_Detailed_Setup.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/104_LoRa_Receiver_Detailed_Setup.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/14_LoRa_Structure_TX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/15_LoRa_Structure_RX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/1_LED_Blink/1_LED_Blink.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/2_Register_Test/2_Register_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/58_FM_Tone/58_FM_Tone.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Basics/58_FM_Tone/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/60_LoRa_Packet_Logger_Receiver_SD.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/SD_Logger_Library.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/103_LoRa_Transmitter_Detailed_Setup_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/104_LoRa_Receiver_Detailed_Setup_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Pictures/ESP32_Bare_Bones_Schematic.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Pictures/ESP32_Micro_Node.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Pictures/ESP32_Shield.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/ReadMe.md create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/ReadMe.md create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/22_On_Off_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/1_LED_Blink/1_LED_Blink.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/2_Register_Test/2_Register_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/17_Sensor_Transmitter/17_Sensor_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/17_Sensor_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/18_Sensor_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/59_Play_Star_Wars_Tune.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/pitches.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/62_LoRa_Wake_on_RX_Atmel.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/6_LoRa_RX_and_Sleep_Atmel.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/23_GPS_Tracker_Transmitter/23_GPS_Tracker_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/38_lora_Relay/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/67_Balloon_Tracker_Transmitter/67_Balloon_Tracker_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/67_Balloon_Tracker_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/68_Balloon_Tracker_Receiver/68_Balloon_Tracker_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/68_Balloon_Tracker_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/71_FSKRTTY_Transmitter_Test/71_FSKRTTY_Transmitter_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/71_FSKRTTY_Transmitter_Test/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/103_Lora_Transmitter_Detailed_Setup/103_Lora_Transmitter_Detailed_Setup.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/103_Lora_Transmitter_Detailed_Setup/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/104_Lora_Receiver_Detailed_Setup/104_Lora_Receiver_Detailed_Setup.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/104_Lora_Receiver_Detailed_Setup/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/14_LoRa_Structure_TX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/15_LoRa_Structure_RX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/1_LED_Blink/1_LED_Blink.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/2_Register_Test/2_Register_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/58_FM_Tone/58_FM_Tone.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/58_FM_Tone/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/73_LoRa_Receiver_AFC/73_LoRa_Receiver_AFC.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/73_LoRa_Receiver_AFC/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/80_Direct_DIO2_Tone/80_Direct_DIO2_Tone.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Basics/80_Direct_DIO2_Tone/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/13_Frequency_and_Power_Check_TX/13_Frequency_and_Power_Check_TX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/13_Frequency_and_Power_Check_TX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/43_LoRa_Data_Throughput_Acknowledge_Receiver/43_LoRa_Data_Throughput_Acknowledge_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/43_LoRa_Data_Throughput_Acknowledge_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/60_LoRa_Packet_Logger_Receiver_SD.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/SD_Logger_Library.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/66_FSK_Carrier_Generator/66_FSK_Carrier_Generator.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/66_FSK_Carrier_Generator/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/1_LED_Blink_ESP32/1_LED_Blink_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/2_Register_Test_ESP32/2_Register_Test_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/3_LoRa_Transmitter_ESP32/3_LoRa_Transmitter_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/3_LoRa_Transmitter_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/4_LoRa_Receiver_ESP32/4_LoRa_Receiver_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/4_LoRa_Receiver_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Diagnostic_and_Test/42_LoRa_Data_Throughput_Transmitter_Test_ESP32/42_LoRa_Data_Throughput_Transmitter_Test_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Diagnostic_and_Test/42_LoRa_Data_Throughput_Transmitter_Test_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Pictures/ESP32_Bare_Bones_Schematic.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Pictures/ESP32_Micro_Node.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Pictures/ESP32_Shield.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/ReadMe.md create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/17_Sensor_Transmitter_ESP32/17_Sensor_Transmitter_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/17_Sensor_Transmitter_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/18_Sensor_Receiver_ESP32/18_Sensor_Receiver_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/18_Sensor_Receiver_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/23_Simple_GPS_Tracker_Transmitter_ESP32/23_Simple_GPS_Tracker_Transmitter_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/23_Simple_GPS_Tracker_Transmitter_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/71_FSKRTTY_Transmitter_Test_ESP32/71_FSKRTTY_Transmitter_Test_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/71_FSKRTTY_Transmitter_Test_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/75_Balloon_Tracker_Transmitter_SDLogger_ESP32/75_Balloon_Tracker_Transmitter_SDLogger_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/75_Balloon_Tracker_Transmitter_SDLogger_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/40_LoRa_Transmitter_ImplicitPacket/40_LoRa_Transmitter_ImplicitPacket.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/40_LoRa_Transmitter_ImplicitPacket/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/41_LoRa_Receiver_ImplicitPacket/41_LoRa_Receiver_ImplicitPacket.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/41_LoRa_Receiver_ImplicitPacket/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/22_On_Off_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/86_Buffer_Transmit_Controller/86_Buffer_Transmit_Controller.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/87_Buffer_Receive_Controller/87_Buffer_Receive_Controller.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/1_LED_Blink_STM32/1_LED_Blink_STM32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/2_Register_Test_STM32/2_Register_Test_STM32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/3_LoRa_Transmitter_STM32/3_LoRa_Transmitter_STM32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/3_LoRa_Transmitter_STM32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/4_LoRa_Receiver_STM32/4_LoRa_Receiver_STM32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/4_LoRa_Receiver_STM32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Pictures/Arduino_IDE_for_STM32.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Pictures/STM32_and_Shield.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/STM32/ReadMe.md create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/17_Sensor_Transmitter/17_Sensor_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/17_Sensor_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/18_Sensor_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/39_LoRa_SX127x_Temperature_Read/39_LoRa_SX127x_Temperature_Read.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/59_Play_Star_Wars_Tune.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/pitches.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/62_LoRa_Wake_on_RX_Atmel.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/6_LoRa_RX_and_Sleep_Atmel.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/Atmel Watchdog Sleep Time.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/Bare_Bones_Arduino_ATMega328.pdf create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/23_GPS_Tracker_Transmitter/23_GPS_Tracker_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/38_lora_Relay/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/67_Balloon_Tracker_Transmitter/67_Balloon_Tracker_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/67_Balloon_Tracker_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/68_Balloon_Tracker_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/AFSKRTTY2_DL-Fldigi_Settings.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/69_Balloon_Tracker_Transmitter_GenericGPS/69_Balloon_Tracker_Transmitter_GenericGPS.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/69_Balloon_Tracker_Transmitter_GenericGPS/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/70_AFSKRTTY_Upload_Test/70_AFSKRTTY_Upload_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/70_AFSKRTTY_Upload_Test/AFSKRTTY2_DL-Fldigi_Settings.jpg.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/71_FSKRTTY_Transmitter_Test/71_FSKRTTY_Transmitter_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/71_FSKRTTY_Transmitter_Test/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/103_LoRa_Transmitter_Detailed_Setup.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/104_LoRa_Receiver_Detailed_Setup.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/14_LoRa_Structure_TX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/15_LoRa_Structure_RX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/1_LED_Blink/1_LED_Blink.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/2_Register_Test/2_Register_Test.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/3_LoRa_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/4_LoRa_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/52_FLRC_Transmitter/52_FLRC_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/52_FLRC_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/53_FLRC_Receiver/53_FLRC_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Basics/53_FLRC_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter_FLRC/42_LoRa_Data_Throughput_Test_Transmitter_FLRC.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter_FLRC/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/103_LoRa_Transmitter_Detailed_Setup_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/104_LoRa_Receiver_Detailed_Setup_ESP32.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Pictures/ESP32_Bare_Bones_Schematic.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Pictures/ESP32_Micro_Node.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/ReadMe.md create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Pictures/Calibration_Values.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Pictures/IMG_2531_Reduced.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Pictures/SX128XLT_Ranging_100m.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Pictures/SX128X_Ranging_Calibration_Values.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/54_Ranging_Master/54_Ranging_Master.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/54_Ranging_Master/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/55_Ranging_Slave/55_Ranging_Slave.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/55_Ranging_Slave/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/56_Ranging_Calibration_Checker/56_Ranging_Calibration_Checker.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/56_Ranging_Calibration_Checker/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/Pictures/Calibration_Values.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/Pictures/IMG_2531_Reduced.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/Pictures/SX128XLT_Ranging_100m.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/Pictures/SX128X_Ranging_Calibration_Values.jpg create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/ReadMe.md create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/ReadMe.md create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/22_On_Off_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/17_Sensor_Transmitter/17_Sensor_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/17_Sensor_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/18_Sensor_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/23_GPS_Tracker_Transmitter/23_GPS_Tracker_Transmitter.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino create mode 100644 lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/38_lora_Relay/Settings.h create mode 100644 lib/SX12XX-LoRa/keywords.txt create mode 100644 lib/SX12XX-LoRa/library.properties create mode 100644 lib/SX12XX-LoRa/pictures/Atmel Watchdog Sleep Time.jpg create mode 100644 lib/SX12XX-LoRa/pictures/ESP32_Bare_Bones_circuit.pdf create mode 100644 lib/SX12XX-LoRa/src/AFSKRTTY.h create mode 100644 lib/SX12XX-LoRa/src/AFSKRTTY2.h create mode 100644 lib/SX12XX-LoRa/src/AtmelSleep.h create mode 100644 lib/SX12XX-LoRa/src/EEPROM_Memory.h create mode 100644 lib/SX12XX-LoRa/src/FRAM_FM24CL64.h create mode 100644 lib/SX12XX-LoRa/src/FRAM_MB85RC16PNF.h create mode 100644 lib/SX12XX-LoRa/src/LICENSE.txt create mode 100644 lib/SX12XX-LoRa/src/ProgramLT_Definitions.h create mode 100644 lib/SX12XX-LoRa/src/QuectelSerialGPS.h create mode 100644 lib/SX12XX-LoRa/src/Quectel_HWSerialGPS.h create mode 100644 lib/SX12XX-LoRa/src/SX126XLT.cpp create mode 100644 lib/SX12XX-LoRa/src/SX126XLT.h create mode 100644 lib/SX12XX-LoRa/src/SX126XLT_Definitions.h create mode 100644 lib/SX12XX-LoRa/src/SX127XLT.cpp create mode 100644 lib/SX12XX-LoRa/src/SX127XLT.h create mode 100644 lib/SX12XX-LoRa/src/SX127XLT_Definitions.h create mode 100644 lib/SX12XX-LoRa/src/SX128XLT.cpp create mode 100644 lib/SX12XX-LoRa/src/SX128XLT.h create mode 100644 lib/SX12XX-LoRa/src/SX128XLT_Definitions.h create mode 100644 lib/SX12XX-LoRa/src/UBLOXI2CGPS.h create mode 100644 lib/SX12XX-LoRa/src/UBLOXSerialGPS.h create mode 100644 lib/SX12XX-LoRa/src/UBLOX_HWSerialGPS.h create mode 100644 platformio.ini create mode 100644 src/bmp180.h create mode 100644 src/bmp388.h create mode 100644 src/bno055.h create mode 100644 src/gps.h create mode 100644 src/lora.h create mode 100644 src/main copy.old create mode 100644 src/main.cpp create mode 100644 src/main.cpp.old create mode 100644 src/sdcard.h create mode 100644 src/sht31.h create mode 100644 src/wifi_setup.h create mode 100644 test/README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..e80666b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e3c18a0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "files.associations": { + "*.old": "cpp", + "array": "cpp", + "deque": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "initializer_list": "cpp" + } +} \ No newline at end of file diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/lib/SX12XX-LoRa/ReadMe.md b/lib/SX12XX-LoRa/ReadMe.md new file mode 100644 index 0000000..7f2dff0 --- /dev/null +++ b/lib/SX12XX-LoRa/ReadMe.md @@ -0,0 +1,442 @@ +# SX12XX Library + +### Library installation + +To install the library select the 'Clone or download' button on the main Github page, then select 'Download Zip'. In the Arduino IDE select 'Sketch' then 'Include Library'. Next select 'Add .ZIP library' and browse to and select the ZIP file you downloaded, it's called 'SX12xx-master.zip'. + + +### Warning +**The Semtech devices that this library supports are all 3.3V logic level devices so do not use directly with 5V logic level Arduinos, some form of logic level conversion is needed.** There are no specific logic level converters I could recommend. + +Buy Me A Coffee + +This library supports the SX126x, SX127x and SX128x Semtech LoRa devices. There is a wide range of example programs for these devices. These Semtech devices are used to manufacture a range of LoRa modules sold by companies such as Hope, Dorji, NiceRF and others. The library does not support LoRa modules with a UART based interface such as those from Ebyte and Microchip. + +The objective of the library was to allow the same program sketches to be used across the range of UHF lora modules SX126x and SX127x (UHF) as well as the 2.4Ghz SX128x modules. A sketch written for the SX1278 (for example) should then run with very minor changes on the SX1262 or SX1280. However, whilst the SX126x and SX128x modules use the same style of device programming, the SX127x programming is completely different. The function style used for the SX126x and SX128x devices has been copied to create a matching style for the SX127x. + +A conventional lora library normally uses a buffer of data created within the Arduino sketch to contain the data that is sent as a packet. This library has those functions, see the example programs 3 and 4 in the 'Basics' folder. There are examples for sending\receiving a simple character buffer ("Hello World") and for sending\receiving a data structure which can also include a character buffer. + + +###Direct access to lora device internal data buffer + +An additional library feature has been implemented to enable variables or character data to be written direct to the lora devices internal buffer. This has the benefit of not requiring a memory buffer in the Arduino and also lends itself to a simple way of sending and receiving packets. For instance this is the routine to create a packet for transmission taken from the 'LowMemory' folder; + + LT.startWriteSXBuffer(0); //start the write at location 0 + LT.writeBuffer(trackerID, sizeof(trackerID)); //= 13 bytes (12 characters plus null (0) at end) + LT.writeUint32(TXpacketCount); //+4 = 17 bytes + LT.writeFloat(latitude); //+4 = 21 bytes + LT.writeFloat(longitude); //+4 = 25 bytes + LT.writeUint16(altitude); //+2 = 27 bytes + LT.writeUint8(satellites); //+1 = 28 bytes + LT.writeUint16(voltage); //+2 = 30 bytes + LT.writeInt8(temperature); //+1 = 31 bytes total to send + len = LT.endWriteSXBuffer(); + +This is the matching code for the receiver; + + LT.startReadSXBuffer(0); //start buffer read at location 0 + LT.readBuffer(receivebuffer); //read in the character buffer + txcount = LT.readUint32(); //read in the TXCount + latitude = LT.readFloat(); //read in the latitude + longitude = LT.readFloat(); //read in the longitude + altitude = LT.readUint16(); //read in the altitude + satellites = LT.readUint8(); //read in the number of satellites + voltage = LT.readUint16(); //read in the voltage + temperature = LT.readInt8(); //read in the temperature + RXPacketL = LT.endReadSXBuffer(); + + +Clearly as with other methods of sending data the order in which the packet data is created in the transmitter has to match the order that it is read in the receiver. + + + +###Considerations for pin usage + +Pins settings and usage must be set up the the 'Settings.h' file that is include in each sketch folder. Program **2\_Register\_Test** does not use a 'Settings.h' file however. + +The library supports the SPI based LoRa modules and these all require that the SPI bus pins, SCK, MOSI and MISO are connected. All modules also need a NSS (chip select pin) and NRESET (reset) pin. In theory the NRESET pin could be omitted, but the programs would loose the ability to reset the device. All devices need the RFBUSY pin to be used also. + +Of the DIO pins the library in standard form only uses DIO0 (SX127X) and DIO1 (SX126X and SX128X). The pin definitions for DIO1 and DIO2 (SX127x) and DIO2 and DIO3 (SX126x and SX128x) are not currently used by the library or examples so can be defined as -1 meaning they will not be configured. + +The Dorji DRF1262 and DRF1268 modules has a SW pin which must be configured, it provides power to the antenna switch used on these modules. +Some SX126x modules may have RX or TX enable pins, these are currently not supported by the library. + +Some of the SX128x modules do have RX or TX enable pins, such as the Ebyte modules, these are supported by the library, and you need to define the pins RX_EN and TX_EN pins used, otherwise leave unused by defining them as -1. + +### Testing +For testing the library and the example programs I used a board of my own design, it uses a 3.3V/8Mhz Arduino Pro Mini which is soldered with a minimum amount of other components onto a board to which you can plug in a LoRa device as a Mikrobus style module. The board is small enough to be used for a GPS tracker application using the connections for a GPS and display as shown in the picture. The Pro Mini used includes a supply reverse protection diode and a fuse, so the board does not need these components. See the [**Easy Pro Mini**](https://github.com/StuartsProjects/Devices/tree/master/Easy%20Pro%20Mini) folder for details. +
+ +All example programs were checked against version 1.8.10 of the Arduino IDE, and the latest copies of any external libraries, as of 16/12/19. The operating system was Windows 10. + +### Program examples + +The Examples folder contains a number of practical working applications. There is an example for a very low sleep current sensor transmitter and matching receiver. There are examples for remote control of outputs and servos. There is a GPS tracker transmitter and receiver application. These applications utilise LoRa for the communications so even at low powers they can operate over several kilometres. + +There are demonstrations on how to send data as a plain character array, as a structure and by writing variables direct to the LoRa devices internal buffer. + +There are additional program examples for testing devices, antennas and long distance links. + +The Settings.h file contains the settings for the LoRa device such as frequency, spreading factor, bandwidth and coding rate. The example programs use a frequency of 434.000Mhz or 2.445Ghz for the SX128x, you will need to check if that frequency is permitted in your part of the World. The radio frequency spectrum is not a free for all, which frequencies, transmitter powers and duty cycles you are permitted to use varies by region and country. By default CRC checking is added to transmitted packets and used to check for errors on reception. + +The first program to test a layout and connections would be the Example program in the Basics folder **2\_Register_Test**, this just does a simple register print of the LoRa device. If this program does not work, then the rest of the example programs wont either. This program is self contained, it does not need the library installed to operate. + +With an example program written and tested on this SX127x library the example should work with some minor changes with the SX126x and SX128x devices. Many of the example programs have already been tested and are working on SX126x, conversion typically takes less than a minute. + +There are still some issues to attend to and changes to be made, see the section 'Changes Required to Library' at the bottom of this document. + + +# Library Functions + +All of the library functions are public and can be accessed from users sketches. + +The basic functions will be described in the order that example program **3\_LoRa\_Transmitter** uses them. + +**SPI.begin()** + +Standard Arduino library function. Sets up SPI. The library then internally uses; + +**SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode))** + +before every use of the SPI and this function after it; + +**SPI.endTransaction()** + +The parameters used are LTspeedMaximum, LTdataOrder and LTdataMode are defined in SX12XXLT_Definitions.h as; + + + LTspeedMaximum 8000000 + LTdataOrder MSBFIRST + LTdataMode SPI_MODE0 + +The use of SPI.beginTransaction and SPI.endTransaction can be disabled by commenting out this define at the top of the relevant SX12XXLT.cpp file; + + #define USE_SPI_TRANSACTION + + +**begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA\_DEVICE)** (SX127X library) + +**begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, SW, LORA\_DEVICE)** (SX126X library) + +**begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX\_EN, TX\_EN, LORA\_DEVICE)** (SX128X library) + +The begin function initialises the hardware pins used by the device. The begin functions are slightly different for the SX127X, SX126X and SX128X libraries due to the different pins used. NSS, NRESET and DIO0 (SX127X) or DIO1 (SX126x and SX128X) are required, other DIOs are optional and when not used define as -1. The SX126X and SX128X devices have an RFBUSY pin. To ensure compatibility with Dorji SX1262 and SX1268 devices the SW pin needs to be defined. This pin turns on\off the antenna switch on Dorji devices. Set to -1 if not used. Some of the SX128X devices for example from eByte require TX and RX enable pins, set to -1 if your not using them. + +The library examples for the SX128x do use the long form of the begin command (begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX\_EN, TX\_EN, LORA\_DEVICE) but if your device does not have TX and RX enable pins you can use the short form of the begin command; + +**begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, LORA\_DEVICE)** (SX128X library) + + +**LoRA\_DEVICE** tells the library which actual LoRa RF IC is being used and in the case of the SX127x devices how the antenna is connected. The choices are for the SX127x part of the library are; + + DEVICE_SX1272 + DEVICE_SX1276 + DEVICE_SX1277 + DEVICE_SX1278 + DEVICE_SX1279 + + DEVICE_SX1272_PABOOST + DEVICE_SX1277_PABOOST + DEVICE_SX1278_PABOOST + DEVICE_SX1279_PABOOST + + DEVICE_SX1276_RFO + DEVICE_SX1277_RFO + DEVICE_SX1278_RFO + DEVICE_SX1279_RFO + + +Note that in the above list for the SXX127x part of the library only and there are new device types that allow the library to differentiate between devices that have the transmit output connected to the **PA\_BOOST** pin (which is the vast majority of SX127x devices) and those that use the **RFO LF\_ANT** or **RFO HF\_ANT** pins for RF output. These RFO connected devices are in the minority and are limited to 14dBm output. There is no library support for a SX1272 using the RFO outputs as I dont have one to test. + +The old device types; DEVICE\_SX1272, DEVICE\_SX1276, DEVICE\_SX1277, DEVICE\_SX1278 and DEVICE\_SX1272 are retained for compatibility with old sketches and are the same device as those with the \_PABOOST identity. + +Devices using the PABOOST output can be set to power levels from 2dBm to 20dBm, and operation at 20dBm is limited to a 1% duty cycle. Divices using the RFO outputs can be set to power levels from 0dBm to 14dBm. + +The device types for the SX126X and SX128X part of the library are; + + DEVICE_SX1261 //SX126X library + DEVICE_SX1262 //SX126X library + DEVICE_SX1268 //SX126X library + DEVICE_SX1280 //SX128X library + DEVICE_SX1281 //SX128X library + +**setMode(MODE\_STDBY\_RC)** + +Sets the operation mode of the LoRa device. Choices are; + + MODE_SLEEP + MODE_STDBY + MODE_STDBY_RC + +**setPacketType(PACKET\_TYPE\_LORA)** + +Set the type of packet to use, currently only LORA is supported, choices are; + + PACKET_TYPE_LORA + PACKET_TYPE_GFSK (not yet implemented) + PACKET_TYPE_FLRC (SX128X library only) + PACKET_TYPE_RANGING (SX128X library only) + PACKET_TYPE_BLE (SX128X library only) (not yet implemented) + PACKET_TYPE_NONE + +**setRfFrequency(Frequency, Offset)** + +Sets the operating frequency in hertz. A calibration offset also in hertz can be used if there is a calibration value known for a particular module. + +**calibrateImage(0)** + +Carries out an internal device calibration, normally carried out after setting the initial operating frequency. + +**setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO)** + +Sets the LoRa modem parameters for Spreading factor, Bandwidth, CodeRate and Optimisation. The options are; + + //LoRa Spreading factors + LORA_SF5 (SX126X and SX128X libraries only) + LORA_SF6 + LORA_SF7 + LORA_SF8 + LORA_SF9 + LORA_SF10 + LORA_SF11 + LORA_SF12 + + //LoRa Bandwidths SX127X and SX126X libraries + LORA_BW_500 //actual 500000hz + LORA_BW_250 //actual 250000hz + LORA_BW_125 //actual 125000hz + LORA_BW_062 //actual 62500hz + LORA_BW_041 //actual 41670hz + LORA_BW_031 //actual 31250hz + LORA_BW_020 //actual 20830hz + LORA_BW_015 //actual 15630hz + LORA_BW_010 //actual 10420hz + LORA_BW_007 //actual 7810hz + + //LoRa Bandwidths SX128X library + LORA_BW_0200 //actually 203125hz + LORA_BW_0400 //actually 406250hz + LORA_BW_0800 //actually 812500hz + LORA_BW_1600 //actually 1625000hz + + //LoRa Coding rates + LORA_CR_4_5 + LORA_CR_4_6 + LORA_CR_4_7 + LORA_CR_4_8 + +The SX126X and SX127X devices have an low data rate optimisation setting that needs to be set when the symbol time is greater than 16mS. You can manually turn it on or off or set it to LDRO\_AUTO and the library does the calculation for you. + + //Low date rate optimisation, need to be set when symbol time > 16mS + LDRO_OFF + LDRO_ON + LDRO_AUTO //automatically calculated and set + +**setBufferBaseAddress(0x00, 0x00)** + +This sets the default address for the locations in the LoRa device buffer where transmitted and received packets start. The defaults of these locations are set in the transmit and receive functions, so this function is not normally required. + +**setPacketParams(PreAmblelength, LORA\_PACKET\_VARIABLE\_LENGTH, PacketLength, LORA\_CRC\_ON, LORA\_IQ\_NORMAL)** + +Set the packet parameters. PreAmblelength is normally 8. There is a choice of LORA\_PACKET\_VARIABLE\_LENGTH for variable length explicit packets or LORA\_PACKET\_FIXED\_LENGTH for implicit packets. PacketLength is 1 to 255, it can be set here but is normally handled within the transmitter and receiver functions. There is the option of using a packet CRC with LORA\_CRC\_ON or not using a CRC with LORA\_CRC\_OFF. IQ can be set to LORA\_IQ\_NORMAL or LORA\_IQ\_INVERTED. + +**setSyncWord(LORA\_MAC\_PRIVATE\_SYNCWORD)** + +You can define the syncword here, either a 8 bit value of your own choice or the standard values of LORA\_MAC\_PRIVATE\_SYNCWORD (0x12) or LORA\_MAC\_PUBLIC\_SYNCWORD (0x34). Take care with setting your own syncwords, some values may not be compatible with other LoRa devices or can give reduced sensitivity. There is no configurable syncword for SX128x devices. + +**setHighSensitivity()** + +Sets LoRa device for the highest sensitivity at expense of slightly higher LNA current. The alternative is setLowPowerReceive() for lower sensitivity with slightly reduced current. + + +**setDioIrqParams(MASK, DIO0\_MASK, DIO1\_MASK, DIO2\_MASK)** (SX127X library) + +**setDioIrqParams(MASK, DIO0\_MASK, DIO1\_MASK, DIO2\_MASK)** (SX126X and SX128X libraries) + +Sets up the how the device responds to internal events. This function is written for the SX127X to match the style used by the SX126X and SX128X devices. MASK is applied to the IRQ settings for DIO0, DIO1 and DIO2 (SX127X) and DIO1, DIO2 and DIO3 (SX126X and SX128X), its normally set to IRQ\_RADIO\_ALL (0xFFFF). Whilst the SX127X only has an 8 bit IRQ register the library has extended the function to provide additional IRQ detections that are used for the SX126X and SX127X. + +In the case of the SX127X, the function maps the internal interrupts to the DIO0, DIO1 and DIO2 pins according to this table; + + IRQ_RADIO_NONE 0x00 + IRQ_CAD_ACTIVITY_DETECTED 0x01 //active on DIO1 + IRQ_FSHS_CHANGE_CHANNEL 0x02 //active on DIO2 + IRQ_CAD_DONE 0x04 //active on DIO0 + IRQ_TX_DONE 0x08 //active on DIO0 + IRQ_HEADER_VALID 0x10 //read from IRQ register only + IRQ_CRC_ERROR 0x20 //read from IRQ register only + IRQ_RX_DONE 0x40 //active on DIO0 + IRQ_RADIO_ALL 0xFFFF + + IRQ_TX_TIMEOUT 0x0100 //so that readIrqstatus can return additional detections + IRQ_RX_TIMEOUT 0x0200 //so that readIrqstatus can return additional detections + IRQ_NO_PACKET_CRC 0x0400 //so that readIrqstatus can return additional detections + +The SX126X library has this table; + + IRQ_RADIO_NONE 0x0000 + IRQ_TX_DONE 0x0001 + IRQ_RX_DONE 0x0002 + IRQ_PREAMBLE_DETECTED 0x0004 + IRQ_SYNCWORD_VALID 0x0008 + IRQ_HEADER_VALID 0x0010 + IRQ_HEADER_ERROR 0x0020 + IRQ_CRC_ERROR 0x0040 + IRQ_CAD_DONE 0x0080 + + IRQ_CAD_ACTIVITY_DETECTED 0x0100 + IRQ_RX_TX_TIMEOUT 0x0200 + IRQ_TX_TIMEOUT 0x0200 + IRQ_RX_TIMEOUT 0x0200 + IRQ_RADIO_ALL 0xFFFF + +And the SX128X has this one; + + IRQ_RADIO_NONE 0x0000 + IRQ_TX_DONE 0x0001 + IRQ_RX_DONE 0x0002 + IRQ_SYNCWORD_VALID 0x0004 + IRQ_SYNCWORD_ERROR 0x0008 + IRQ_HEADER_VALID 0x0010 + IRQ_HEADER_ERROR 0x0020 + IRQ_CRC_ERROR 0x0040 + IRQ_RANGING_SLAVE_RESPONSE_DONE 0x0080 + + IRQ_RANGING_SLAVE_REQUEST_DISCARDED 0x0100 + IRQ_RANGING_MASTER_RESULT_VALID 0x0200 + IRQ_RANGING_MASTER_RESULT_TIMEOUT 0x0400 + IRQ_RANGING_SLAVE_REQUEST_VALID 0x0800 + IRQ_CAD_DONE 0x1000 + IRQ_CAD_ACTIVITY_DETECTED 0x2000 + IRQ_RX_TX_TIMEOUT 0x4000 + IRQ_TX_TIMEOUT 0x4000 + IRQ_RX_TIMEOUT 0x4000 + IRQ_PREAMBLE_DETECTED 0x8000 + IRQ_RADIO_ALL 0xFFFF + + +**setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation)** + +As an alternative to setting up the LoRa device with separate functions (as described above) you can use this function. The function first sets the Frequency of operation, the frequency is in hertz as a 32 bit unsigned integer. The actual programmed operating frequency is the sum of Frequency and Offset (also 32 bit integer). + +SpreadingFactor, Bandwidth and CodeRate are the LoRa modem parameters and the choices are as given for the setModulationParams() described above. + +When using setupLoRa() that library function then calls the following functions using these defaults; + + setMode(MODE_STDBY_RC) + setPacketType(PACKET_TYPE_LORA); + setRfFrequency(Frequency, Offset); + calibrateImage(0); + setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); + setBufferBaseAddress(0x00, 0x00); + setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); + setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); + LORA_MAC_PUBLIC_SYNCWORD = 0x34 + setHighSensitivity(); + setDioIrqParams(IRQ_RADIO_ALL, IRQ_TX_DONE, 0, 0); + + +**printModemSettings()** + +Prints the current modem settings to the serial monitor for diagnostic purposes. The parameters printed for lora are ; device\_type, frequency, spreading factor, bandwidth, coding\_rate, syncword, IQ\_Status, preamble\_length. + +**printOperatingSettings()** + +Prints the current operating settings to the serial monitor for diagnostic purposes. The settings printed are; device\_type, version\_number, packet\_mode, header\_mode, packet\_CRC\_on\_off, AGC\_auto\_on\_off, LNA\_gain, LNA\_boostHF, LNA\_boostLF. + +**printRegisters(start, end)** + +Print the device registers from start address to end address. + +**printASCIIPacket(buff, length)** + +Print as ASCII characters to the serial monitor the contents of the buffer name given, for the given length. + +**transmit(buff, TXPacketL, timeout, TXpower, WAIT_TX)** + +Transmit the contents of the buffer name given, for the given length. With a timeout in mS, with a TXpower in dBm and wait for the transmit to complete (a blocking command). To have the LoRa device start transmitting and continue as a no blocking command use NO_WAIT. + +With **transmit** and WAIT\_TX the function returns the packet length if transmit detected no errors and 0 if errors were detected. If NO_WAIT is used you will need to check when pin DIO0 (SX127X) or DIO1 (SX126X and SX128X) goes high indicating transmission is completed. + +**CRCCCITT(buff, TXPacketL, 0xFFFF)** + +Calculates the 16 bit CRC of the buffer given for the length given. Specify the initialise value for the CRC, 0xFFFF in this case. + +**readIrqStatus()** + +Reads the value of the IRQ register flags. + +## Receiving Packets + +The **4\_LoRa\_Receiver** sketch is very similar, with the following differences; + +**LT.receive(RXBUFFER, RXBUFFER\_SIZE, timeout, WAIT\_RX)** + +Copy the received packet into the buffer address given with a maximum buffer size. If the RXBUFFER\_SIZE is smaller than the actual received packet then the packet will be truncated. If WAIT\_RX is selected then the program will wait for the time-out period (in mS) for a packet to arrive before signalling a timeout, this is a blocking command. To have the receiver wait continuously for a packet set the timeout to 0. To use the receiver in non-blocking mode set NO\_WAIT in which case you will need to check DIO0 (SX127X) or DIO1 (SX126X and SX128X) going high to indicate a packet has arrived. + +**readPacketRSSI()** + +Read the signal strength in dBm of the received packet. + +**readPacketSNR()** + +Read the signal to noise ratio in dB of the received packet. Typical values are +10dB for strong signals and -20dB for reception at the limit. + +**printIrqStatus()** + +Prints to the serial monitor the interrupt flags set. + + +
+ + +## Packet Addressing + +LoRa is a two way technology, each device is a transceiver. Most often on a particular frequency there will be one transmitter and one receiver. However, this may not always be the case and there could be several nodes in use on the same frequency. + +In order to keep the software simple and allow for the receipt of signals from multiple receivers or directed commands to a particular node, a basic addressing scheme can be used and is implemented by some example programs, see '17_Sensor_Transmitter' for an example. There are library routines to send and receive packets in addressed and non-addressed format so you choose which to send. When using addressed mode regardless of the data content of the actual payload each packet sent has 3 control bytes at the beginning of the packet. In the case of the sensor example mentioned above, the use of the addressing allows the receiver to know from which sensor transmitter the packet came. + +In general the control bytes have been restricted to ASCII printable characters so that they can be shown directly on a terminal monitor. The 3 bytes are; + +**Packet type**. This either describes the content of the packet, which could be a GPS location payload or is a command to do something and there is no payload. Details of the packet types defined are in the library file 'ProgramLT_Definitions.h' + +**Packet Destination**. The node number that the packet is destined for. + +**Packet Source**. The node number that the packet was sent from. + +The destination and source packet bytes mean that node ‘2’ (could be your base station receiver) can send a command that only station ‘3’ will respond to. This command could be a reset command, a request to turn on and off certain transmission types etc. Node ‘3’ can be set-up so that it only accepts commands from a particular node. + +In addressed mode the 3 control bytes are automatically stripped from each received packet. + +An example of the 3 control bytes from a tracker would be; + +T*2 + +Which means there is a test packet (T) its been sent as a broadcast (*) and its from node 2. + +### Compatibility + +**Fully tested on 3.3V 8Mhz ATMega328P and ATMega1284P only**. + +It was not the intention to specifically support non-Atmel platforms with the library but several programs have been tested and work on an ESP32 WROOM board and an STM32 Xnucleo board. See the Readme for ESP32 and STM32 in the ESP32 and STM32 examples folders. + + +### Support +The examples do work, so if for you they do not, assume there is a problem with how you have wired the modules or that your modules are faulty or that your Arduino set-up or LoRa module is faulty or unsupported. You are best placed to diagnose these issues. + +If you find a bug, or other error in the SX12xx library or examples, please let me know. + + +### Future Changes and Enhancements to Library + +Add reliable packet send\receive code. + +Add file transfer to and from SD card code. + +
+ + + +### Stuart Robinson + +### December 2020 + diff --git a/lib/SX12XX-LoRa/Updates.md b/lib/SX12XX-LoRa/Updates.md new file mode 100644 index 0000000..5bcf984 --- /dev/null +++ b/lib/SX12XX-LoRa/Updates.md @@ -0,0 +1,20 @@ +## Updates - 30/12/20 + +A number of updates have been applied to the SX127X part of the library, in part to cure some known issues and also to add additional functionality. A list of the changes made is below; + +1. Correct a problem with LORA\_IQ\_INVERTED mode not working when configured. +2. Apply some of the SX1276\_77\_8\_ErrataNote\_1\_1 fixes. '2.1 Sensitivity Optimization with a 500 kHz Bandwidth'. Note that '2.3 Receiver Spurious Reception of a LoRa Signal' has not yet been implemented, although the code is present, further testing of this 'fix' is required. +3. Apply the revised RSSI and SNR value calculations to match the latest data sheet. The RSSI value was changed to be int16_t type since it can (now) go lower than -127dBm. +4. Remove the private variables _PacketRSSI and _PacketSNR, they are not needed since they can be recovered direct from a SX127x register. +5. Changes to the FSKRTTY transmit routines so that they automatically cope with transmission during a micros() roll-over. +6. Make changes to several of the example programs that use a GPS to be able to cope automatically with a millis() roll-over +7. Added support for SX127x devices that use the RFO\_LF or RFO\_HF ports for the transmit antenna. No support yet for an SX1272 using the RFO ports. +8. Added 'AFSKRTTY2.h' library file to be used as the standard AFSKRTTY library for upload of HAB payloads into a PC. This version uses micros() to toggle a pin for audio and has been tested on ATMega328, DUE and ESP32. The use of tone() would be preferred but some Arduino platforms don't support it. +9. Changed the GPS library programs 'UBLOXSerialGPS.h', 'UBLOXI2CGPS.h' and 'QuectelSerialGPS.h' so that they have compatible functions and that the same library file can be used for both softwareserial and hardware serial. +10. A function was added, doAFCPPM(); which adjusts the LoRa devices internal oscillator PPM offset. +11. Adjusted the calibrateImage(); function to operate correctly with fast processors. +12. Add function to read current RSSI. +13. Add an overloaded begin(int8\_t pinNSS, uint8\_t device); function for use with ESP32CAM. +14. Change Receive and Transmit functions to allow for millis() roll-over at 50days. + +### Stuart Robinson diff --git a/lib/SX12XX-LoRa/What is LoRa.md b/lib/SX12XX-LoRa/What is LoRa.md new file mode 100644 index 0000000..d9da98e --- /dev/null +++ b/lib/SX12XX-LoRa/What is LoRa.md @@ -0,0 +1,136 @@ +## What is LoRa? + +LoRa is a type of chirp spread spectrum transmission that has been implemented by Semtech for use in low cost Industrial Scientific and Medical (ISM) band radio devices. The SX127x devices became available in 2014\2015. LoRa is short for Long Range. + +As a general guide a LoRa device will have up to 10 times (or maybe more) the range \distance of other typical radio modules, when using the same frequencies, data rates, antennas and transmit powers. + +The first LoRa devices were for the UHF bands and modules for 169Mhz, 434Mhz, 868Mhz and 915Mhz are available to match the ISM band requirements of a particular global location. + +LoRa devices are programmed over an SPI interface. The older SX127x range of devices are the ones in common use and are register centric, they are programmed by changing the contents of 128 seperate single byte registers. Newer LoRa devices such as SX126x (UHF) and SX128x (2.4Ghz) are programmed through a command interface, you send a command byte followed by a series of parameter bytes across the SPI bus. + +LoRa devices are packet based, you load a first in first out (FIFO) buffer with bytes of data and tell the device to transmit that packet of data. The packets can be up to 256 bytes long and can have an integral cyclic redundancy check (CRC) automatically added. When a packet is received it is loaded into the FIFO from where the bytes of data can be read out. + +There are now several software libraries that allow LoRa devices to be used with a range of Arduino devices. Note that a library specifically for the SX1276 and SX1278 devices will not correctly drive an SX1272. + + +#### Comparison to FSK devices + +With frequency shift keying (FSK) style transmitters and receivers such as the Hope RFM22B or RFM69 for reception to work the receiver needs to see a signal that is strong enough to be above the local radio frequency (RF) noise level. Noise levels in the ISM bands are a performance limiter and each device in use adds to the local RF noise level. The total noise a receiver sees is a combination of local RF noise and noise generated by the electronics inside the receiver. + +A typical FSK receiver needs to see a signal that is at least 5dB above the receiver's noise level in order to decode it. In comparison LoRa technology allows for signals to be received that are as much as 20dB below noise level, thus the LoRa signal could be up to 25dB (5dB + 20dB) weaker than the FSK one and still be received correctly. If LoRa can decode a signal that is 25dB weaker, then the transmitter can be further away than in the FSK example. + +So due in the main to the below noise level performance, LoRa devices are able to receive signals that are very much weaker and thus much further away than with previous technology ISM band modules. + + +#### Configuring a LoRa module + +LoRa modules are straight forward to drive and flexible, the internal firmware takes care of internal adjustments and optimisations, although there is an extra optimisation flag to set for long range slow to transmit packets. + +The parameters that need to be configured for LoRa are, Spreading Factor, Coding Rate, Bandwidth, frequency and transmit power. These parameters can be easily changed so that you can set-up for low data rate long range or high data rate shorter range. + +The LoRa device itself is a single surface mount integrated circuit, these are built onto small, typically 15mm x 15mm, modules that contain the crystal (or oscillator) and antenna matching components the modules need. Examples of LoRa modules are the Hope RFM98 and Dorji DRF1278F. + +LoRa is a two way technology, the transmitter device is a receiver too, so if you can pick-up signals from your transmitter you can send it commands or exchange data with it also. + + +#### Bandwidth + +The LoRa bandwidth is the amount of frequency spread the signal occupies. The bandwidths LoRa can be set to are; + +7.8kHz, 10.4kHz, 15.6kHz, 20.8kHz, 31.25kHz, 41.7kHz, 62.5kHz, 125kHz, 250kHz, 500kHz. + +A low bandwidth signal, such as 7.8kHz will give the best (longest distance) performance, but with the low cost components used in the Hope and Dorji LoRa modules a transmitter and receiver may not talk to each other at this bandwidth. The receiver has a frequency capture range, this is the percentage difference allowable in relation to the bandwidth that the transmitter and receiver can be apart in frequency in order for the receiver to pick up the transmitters signal. This percentage is typically 25% of the bandwidth. + +Lets assume the bandwidth in use is 10kHz, the allowable variation is 25% or 2.5kHz and the transmitter is on 434.000Mhz. For the link to work the receiver needs to be on or between 433.9975Mhz and 434.0025Mhz. This is an exacting requirement for a low cost crystal, even if transmitter and receiver are at the same temperature. + +Low bandwidths of 20.8kHz have been used for long range trackers and they do give better performance, but care is needed as devices used at this bandwidth may not talk to each other unless a frequency calibration factor is measured and applied. Once communication is established at low bandwidth a form of automatic frequency control can be used to adjust the receivers base frequency to keep it within the capture range. + +A bandwidth of 62.5kHz has been found to be reliable and allows for differences in manufacturing tolerance and temperature between transmitter and receiver devices. Applications such as The Things Network (see later) use a bandwidth of 125kHz. + +Newer LoRa devices such as the SX1262 are designed to use temperature compensated crystal oscillators (TCXO). The TCXO is very stable in frequency and can allow LoRa devices to be used at the lowest bandwidth (7.8kHz) even when there are significant temperature differences between transmitting and receiving LoRa devices. + + +#### Spreading factor + +This controls the amount of signal processing gain. The spreading factor can be set between 6 and 12. A setting of 6 can only be used with fixed length packets, so is not often used in applications. + +The spreading factor defines the below noise performance. SF6 gives a -7.5dB below noise performance and SF12 a -20dB below noise performance. + +The larger spreading factors therefore give more range, as it allows the reception of weaker signals, the trade off is that higher spreading factor packets take longer to transmit. + + +#### Coding rate + +Additional data can be added to the packet to allow for error correction. This can result in small signal gains at the limit of reception. The coding rate can be varied between 4:5 and 4:8, the higher 4:8 rate will result in longer packets. + +#### How the LoRa settings affect range + +Having established a practical LoRa bandwidth, say 62.5kHz, we can use this base to look at how the spreading factor affects range and the time to transmit a 20 byte payload. + +The lower spreading factors will give the highest data rates and the shortest packets in time terms which in turn is more battery efficient. The lowest spreading factor we will consider is 7. + +Assume we are using a bandwidth of 62.5kHz and a spreading factor of 7, that will give an equivalent data rate of 2734bps, according Semtech's 'LoRa Calculator' tool. The time taken to transmit our 20 bytes is 98mS. Assume that is our base and that in a typical urban area the limit of reception is 500m. + +If we switch to spreading factor 12 then the data rate drops to 146bps and the transmit time goes up to 2.2 seconds so this is a slow packet. The benefit of the higher spreading factor is significant, we now have an approximate 14dB of extra signal gain. This is equivalent to 5 times further range, so our 500m now extends to 2.5km. The 'LoRa Calculator' tool provides the signal gain figures for different combinations of spreading factor and bandwidth. + +5 times longer distance may not seem a lot, but appreciate how much difference it can make to the transmit power required to cover the extra distance. If we were transmitting at 10mW and that gave us the 500m range, to cover 5 times further range just by increasing transmit power would require a transmitter putting out 25 times the 10mW or 250mW. + +#### Data rates + +The fastest data rate a SX127x UHF LoRa device is capable of is 37500bps. The lowest data rate is 18bps, but the bandwidth at this rate, 7.8kHz, would be difficult to use. +Low data rate optimisation + +When receiving a packet of data the receiver locks onto the packet preamble in order to synchronise the decoding of the following data. For the duration of packet reception only a small frequency variation in the transmitted signal is permitted. If the transmitter frequency drifts by more than circa 1.5% of the LoRa bandwidth in use, then there can be packet reception errors. + +Unfortunately there will be a small amount of frequency drift during transmission caused by the LoRa device in the transmitter heating as the device dissipates the power used by the transmitter. The heating causes a slight drift in the frequency of the quartz crystal oscillator. The higher the transmit power the more the heating and the more the frequency drift. In long range mode, where packets may take a second or more to send, then there is a greater heating effect. + +Similar problems could occur when the frequency difference between transmitter and receiver occurs due to Doppler shift which can happen in communications between fast moving vehicles for instance. +To work around these issues, for packets that are slow to transmit and which have a symbol time of 12mS or longer, an optimisation flag is set. If you were using a bandwidth of 62.5kHz the optimisation flag needs to be set at spreading factor 10 and above. + +#### RSSI versus SNR + +LoRa devices can provide both the received signal strength indication (RSSI) and signal to noise ratio (SNR) of a received packet. Whilst you might be tempted to use RSSI as an indication of the strength of the received signal, for weak signals, i.e. those approaching failure, RSSI is a poor indicator. + +In practice the SNR reported is a far more useful indicator of impending link failure. If the SNR limit for +the spreading factor you are using is -10dB then a received packet reporting as SNR -4dB, is within 6dB of failure. Simple to do distance measurements can show that with 6dB (10-4) of signal link margin remaining, you can expect to receive a packet at approximately twice distance. + +SNR Limits for Spreading factor. + +SF7 -7.5dB +SF8 -10dB +SF9 -12.5dB +SF10 -15dB +SF11 -17.5dB +SF12 -20dB + + +#### Phantom Packets + +It is an artefact of a LoRa receiver that data and packets can appear as if from nowhere, they come from within the device itself. The rate of phantom packets depends on the bandwidth in use by the receiver and can be at the rate of around 5 to 10 phantom packets per hour. Care is needed in software libraries to ensure that these phantoms are identified and rejected. + + +#### 2.4Ghz LoRa + +More recently 2.4Ghz LoRa modules have been available, based on the SX128x. Whilst they don't have the extreme range of the UHF modules, they do allow for higher data rates, up to 200kbps and no duty cycle restrictions,. + +The 2.4Ghz LoRa modules include a ranging function which allows a suitably configured pair of modules to measure the distance between them. The ranging works best in clear open areas and can be used to measure distances from circa 50m to beyond 40km. Recent endeavours by the author have shown that 2.4GHz LoRa can work at up to 89km LOS, and the ranging at 85km LOS. + +#### How far does LoRa go? + +LoRa devices are capable of extreme long range at low data rates. The current distance record for standard modules is 766km, a ground based receiver tracking a high altitude balloon. This distance was achieved using only basic omnidirectional antennas, at 868Mhz and only 25mW transmit power. See **[here](https://www.hackster.io/news/a-new-lorawan-distance-record-577c8bc11d7b)**. Its interesting to note that at 434MHz and the full device power of 100mW (when permitted!), the distance covered could be up to 4 times greater than the 766km. + +At the time of writing, June 2019, there are small Earth orbiting satellite launches planned that will utilise LoRa, so perhaps the 766km will indeed be beaten. + + +#### LoRa and LoRaWAN are different + +LoRa devices are used for simple point to point communications. LoRaWAN is an extra software based network communication layer added to basic LoRa that allows sensors or nodes to communicate with gateway devices in a structured manner. These gateways can forward data received from nodes across the Internet to applications and data collection services running on remote servers. Internet services such as Cayenne allow you to view data from sensors on-line. + +The Things Network (TTN) is a community based free to use network of LoRaWAN gateways. If you want to have a sensor monitor temperatures, water levels, or track the location of assets, you can use the TTN to do this, assuming a gateway is within range. + +There is a fair access policy for using TTN, your restricted to 30 seconds of transmit time per day and can send 10 messages per day to the node from the gateway. If your node is close to a gateway and your node is using spreading factor 7 then you could transmit up to 14,000 bytes of data per day. Its best to plan for worst case where your node is a long distance from the gateway and spreading factor 12 needs to be used, in this case you can send only 600 bytes per day. TTN is not for applications that require large amounts of data to be sent. + +
+ +### Stuart Robinson +### December 2019 \ No newline at end of file diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/12_ATmel_Sleep_with_Switch_Wakeup/12_ATmel_Sleep_with_Switch_Wakeup.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/12_ATmel_Sleep_with_Switch_Wakeup/12_ATmel_Sleep_with_Switch_Wakeup.ino new file mode 100644 index 0000000..f406504 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/12_ATmel_Sleep_with_Switch_Wakeup/12_ATmel_Sleep_with_Switch_Wakeup.ino @@ -0,0 +1,112 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 26/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program tests the deep sleep mode and wakeup with a switch of an Atmel 328P or + 1284P processor. The program starts, flashes the LED and then puts the processor into permanent sleep. + It can be woken up with a switch press. Used as a base test routine for checking the sleep current of + a board. + + Tested on a 'bare bones' ATmega328P board, the current in sleep mode was 1.7uA with a 3.3V MCP1700 + regulator being used. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include + +#define LED1 8 //on board LED, high for on +#define SWITCH1 2 //switch used to wake processor up, switch pin connected to + //ground to activate. Define as -1 if switch not used. + +uint32_t sleeps; + + +void loop() +{ + digitalWrite(LED1, HIGH); + delay(2000); + sleeps++; + Serial.print(sleeps); + Serial.println(F(" Sleeping zzzzz....")); + Serial.println(); + Serial.flush(); //make sure serial out buffer is empty + digitalWrite(LED1, LOW); + + attachInterrupt(digitalPinToInterrupt(SWITCH1), wakeUp, FALLING); //This is a hardware interrupt + + sleep_permanent(); //goto sleep till woken up by switch press + + detachInterrupt(digitalPinToInterrupt(SWITCH1)); + + Serial.println(F("Awake !")); + digitalWrite(LED1, HIGH); +} + + +void sleep_permanent() +{ + ADCSRA = 0; //disable ADC + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + + //turn off brown-out enable in software + MCUCR = bit (BODS) | bit (BODSE); //turn on brown-out enable select + MCUCR = bit (BODS); //this must be done within 4 clock cycles of above + interrupts (); //guarantees next instruction executed + + sleep_cpu (); //sleep within 3 clock cycles of above + + /* wake up here */ + + sleep_disable(); +} + + +void wakeUp() +{ + //handler for the interrupt +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(SWITCH1, INPUT_PULLUP); //setup switch pin, ground to activate + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + pinMode(SWITCH1, INPUT_PULLUP); //setup switch pin, connect to ground to activate + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("12_ATmel_Sleep_with_Switch_Wakeup Starting")); + + +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/26A_GPS_Echo_Simple/26A_GPS_Echo_Simple.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/26A_GPS_Echo_Simple/26A_GPS_Echo_Simple.ino new file mode 100644 index 0000000..2e23315 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/26A_GPS_Echo_Simple/26A_GPS_Echo_Simple.ino @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 24/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a simple program to test a GPS. It reads characters from the GPS using + software serial and sends them (echoes) to the IDE serial monitor. If your ever having problems with + a GPS (or just think you are) use this program first. + + If you get no data displayed on the serial monitor, the most likely cause is that you have the receive + data pin into the Arduino (RX) pin connected incorrectly. + + GPS baud rate set at 9600 baud, Serial monitor set at 115200 baud. If the data displayed on the serial + terminal appears to be random text with odd symbols its very likely you have the GPS serial baud rate + set incorrectly for the GPS. + + Note that not all pins on all Arduinos will work with software serial, see here; + + https://www.arduino.cc/en/Reference/softwareSerial + + Serial monitor baud rate is set at 115200. + +*******************************************************************************************************/ +#define RXpin A3 //this is the pin that the Arduino will use to receive data from the GPS +#define TXpin A2 //this is the pin that the Arduino can use to send data (commands) to the GPS - not used + +#include +SoftwareSerial GPS(RXpin, TXpin); + +void loop() +{ + while (GPS.available()) + { + Serial.write(GPS.read()); + } +} + +void setup() +{ + GPS.begin(9600); + Serial.begin(115200); + Serial.println("GPS_Echo Starting"); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/26B_GPS_Echo_Hardware_Serial/26B_GPS_Echo_Hardware_Serial.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/26B_GPS_Echo_Hardware_Serial/26B_GPS_Echo_Hardware_Serial.ino new file mode 100644 index 0000000..137f41b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/26B_GPS_Echo_Hardware_Serial/26B_GPS_Echo_Hardware_Serial.ino @@ -0,0 +1,43 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 14/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a simple program to test a GPS. It reads characters from the GPS using + a hardware serial port, set to Serial1 as default, and then sends them (echoes) to the Arduino IDE + serial monitor. If your ever having problems with a GPS (or just think you are) use this program first. + + If you get no data displayed on the serial monitor, the most likely cause is that you have the receive + data pin into the Arduino (RX) pin connected incorrectly. + + At program start you should see '26A_GPS_Echo_Hardware_Serial Starting' in the serial monitor, if you + dont the serial monitor baud rate is probably incorrectly set. If you then see data displayed on the + serial terminal which appears to be random text with odd symbols its very likely you have the GPS + serial baud rate set incorrectly. + + Change 'Serial1' in the program to match the hardware serial port you are using, for an Arduino Mega + this would normally be Serial1, Serila2 or Serial3. + + Serial monitor baud rate is set at 115200. + +*******************************************************************************************************/ + +void loop() +{ + while (Serial3.available()) + { + Serial.write(Serial3.read()); + } +} + + +void setup() +{ + Serial3.begin(9600); + Serial.begin(115200); + Serial.println(); + Serial.println("26B_GPS_Echo_Hardware_Serial Starting"); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/26C_GPS_Echo_UBLOXI2C/26C_GPS_Echo_UBLOXI2C.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/26C_GPS_Echo_UBLOXI2C/26C_GPS_Echo_UBLOXI2C.ino new file mode 100644 index 0000000..5453d70 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/26C_GPS_Echo_UBLOXI2C/26C_GPS_Echo_UBLOXI2C.ino @@ -0,0 +1,45 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 14/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a simple program to test a UBLOX GPS. It reads characters from the GPS using + the I2C interface and sends them (echoes) to the IDE serial monitor. If your ever having problems with a + GPS (or just think you are) use this program first. + + Serial monitor baud rate is set at 115200. + +*******************************************************************************************************/ + +#include +const uint16_t GPSI2CAddress = 0x42; + + +void loop() +{ + uint8_t GPSByte; + + do + { + Wire.requestFrom(GPSI2CAddress, 1); + GPSByte = Wire.read(); + + if (GPSByte != 0xFF) + { + Serial.write(GPSByte); + } + } + while (true); +} + + +void setup() +{ + Wire.begin(); + Serial.begin(115200); + Serial.println(); + Serial.println("26C_GPS_Echo_UBLOXI2C Starting"); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/26_GPS_Echo/26_GPS_Echo.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/26_GPS_Echo/26_GPS_Echo.ino new file mode 100644 index 0000000..74760cf --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/26_GPS_Echo/26_GPS_Echo.ino @@ -0,0 +1,48 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 14/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a simple program to test a GPS. It reads characters from the GPS using + software serial and sends them (echoes) to the IDE serial monitor. If your ever having problems with + a GPS (or just think you are) use this program first. + + If you get no data displayed on the serial monitor, the most likely cause is that you have the receive + data pin into the Arduino (RX) pin connected incorrectly. + + If the data displayed on the serial terminal appears to be random text with odd symbols its very + likely you have the GPS serial baud rate set incorrectly. + + Note that not all pins on all Arduinos will work with software serial, see here; + + https://www.arduino.cc/en/Reference/softwareSerial + + Serial monitor baud rate is set at 115200. + +*******************************************************************************************************/ + +#define RXpin A3 //this is the pin that the Arduino will use to receive data from the GPS +#define TXpin A2 //this is the pin that the Arduino can use to send data (commands) to the GPS - not used + +#include + +SoftwareSerial GPS(RXpin, TXpin); + +void loop() +{ + while (GPS.available()) + { + Serial.write(GPS.read()); + } +} + + +void setup() +{ + GPS.begin(9600); + Serial.begin(115200); + Serial.println("26_GPS_Echo Starting"); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/28_GPS_Checker/28_GPS_Checker.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/28_GPS_Checker/28_GPS_Checker.ino new file mode 100644 index 0000000..a8bff7b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/28_GPS_Checker/28_GPS_Checker.ino @@ -0,0 +1,272 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 24/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a portable GPS checker. It reads the GPS for 5 seconds and copies + the characters from the GPS to the serial monitor, this is an example printout from a working GPS that + has just been powered on; + + 28_GPS_Checker Starting + Wait GPS Fix 5 seconds + Timeout - No GPS Fix 5s + Wait GPS Fix 5 seconds + $PGACK,103*40 + $PGACK,105*46 + $PMTK011,MTKGPS*08 + $PMTK010,001*2E + $PMTK010,00æ*2D + $GPGGA,235942.800,,,,,0,0,,,M,,M,,*4B + $GPGSA,A,1,,,,,,,,,,,,,,,*1E + $GPRMC,235942.800,V,,,,,0.00,0.00,050180,,,N*42 + $GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32 + $GPGSV,1,1,03,30,,,43,07,,,43,05,,,38*70 + + Timeout - No GPS Fix 5s + Wait GPS Fix 5 seconds + + That printout is from a Meadiatek GPS, the Ublox ones are similar. The data from the GPS is also fed into + the TinyGPS++ library and if there is no fix a message is printed on the serial monitor. + + When the program detects that the GPS has a fix, it prints the Latitude, Longitude, Altitude, Number + of satellites in use, the HDOP value, time and date to the serial monitor. + + The program has the option of using a pin to control the power to the GPS, if the GPS module being used + has this feature. To use the option change the define; '#define GPSPOWER -1' from -1 to the pin number + being used. Also set the GPSONSTATE and GPSOFFSTATE to the appropriate logic levels. + + There is a set of GPS co-ordinates defined, TestLatitude and TestLongitude, when the GPS has its location + fix the distance and direction to the Test location is calculated and shown on the serial monitor. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#define Program_Version "V1.2" +#define authorname "Stuart Robinson" + +#include //get library here > http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS +#define LED1 8 //pin number for LED, turns on when printing Fix data + +#define GPSPOWER -1 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#include +SoftwareSerial GPSserial(RXpin, TXpin); + + +float GPSLat; //Latitude from GPS +float GPSLon; //Longitude from GPS +float GPSAlt; //Altitude from GPS +uint8_t GPSSats; //number of GPS satellites in use +uint32_t GPSHdop; //HDOP from GPS +uint8_t hours, mins, secs, day, month; +uint16_t year; +uint32_t startGetFixmS; +uint32_t endFixmS; + +//GPS co-ordinates to use for the test location transmission +const float TestLatitude = 51.48230; //Cardiff castle keep, used for location testing purposes +const float TestLongitude = -3.18136; + + +void loop() +{ + if (gpsWaitFix(5)) + { + digitalWrite(LED1, HIGH); //LED on to indicate fix + Serial.println(); + Serial.println(); + Serial.print(F("Fix time ")); + Serial.print(endFixmS - startGetFixmS); + Serial.println(F("mS")); + + GPSLat = gps.location.lat(); + GPSLon = gps.location.lng(); + GPSAlt = gps.altitude.meters(); + GPSSats = gps.satellites.value(); + GPSHdop = gps.hdop.value(); + + hours = gps.time.hour(); + mins = gps.time.minute(); + secs = gps.time.second(); + day = gps.date.day(); + month = gps.date.month(); + year = gps.date.year(); + + printGPSfix(); + startGetFixmS = millis(); //have a fix, next thing that happens is checking for a fix, so restart timer + digitalWrite(LED1, LOW); + } + else + { + Serial.println(); + Serial.println(); + Serial.print(F("Timeout - No GPS Fix ")); + Serial.print( (millis() - startGetFixmS) / 1000 ); + Serial.println(F("s")); + } +} + + +bool gpsWaitFix(uint16_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for updated fix + + uint32_t startmS, waitmS; + uint8_t GPSchar; + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.println(F(" seconds")); + + waitmS = waitSecs * 1000; //convert seconds wait into mS + + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < waitmS) //allows for millis() overflow + { + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + Serial.write(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated() && gps.date.isUpdated()) + { + endFixmS = millis(); //record the time when we got a GPS fix + return true; + } + } + return false; +} + + + +void printGPSfix() +{ + float tempfloat; + uint32_t distance; + uint16_t direction; + + Serial.print(F("New GPS Fix ")); + + tempfloat = ( (float) GPSHdop / 100); + + Serial.print(F("Lat,")); + Serial.print(GPSLat, 6); + Serial.print(F(",Lon,")); + Serial.print(GPSLon, 6); + Serial.print(F(",Alt,")); + Serial.print(GPSAlt, 1); + Serial.print(F("m,Sats,")); + Serial.print(GPSSats); + Serial.print(F(",HDOP,")); + Serial.print(tempfloat, 2); + Serial.print(F(",Time,")); + + if (hours < 10) + { + Serial.print(F("0")); + } + + Serial.print(hours); + Serial.print(F(":")); + + if (mins < 10) + { + Serial.print(F("0")); + } + + Serial.print(mins); + Serial.print(F(":")); + + if (secs < 10) + { + Serial.print(F("0")); + } + + Serial.print(secs); + Serial.print(F(",Date,")); + + Serial.print(day); + Serial.print(F("/")); + Serial.print(month); + Serial.print(F("/")); + Serial.print(year); + + distance = gps.distanceBetween(GPSLat, GPSLon, TestLatitude, TestLongitude); + direction = gps.courseTo(GPSLat, GPSLon, TestLatitude, TestLongitude); + + Serial.println(); + Serial.print(F("Distance to Test Location (")); + Serial.print(TestLatitude, 6); + Serial.print((",")); + Serial.print(TestLongitude, 6); + Serial.print((") ")); + Serial.print(distance); + Serial.print(("m")); + Serial.println(); + Serial.print(F("Direction to Test Location (")); + Serial.print(TestLatitude, 6); + Serial.print((",")); + Serial.print(TestLongitude, 6); + Serial.print((") ")); + Serial.print(direction); + Serial.print(("d")); + Serial.println(); + Serial.println(); +} + + +void GPSON() +{ + if (GPSPOWER) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + GPSON(); + } + + GPSserial.begin(9600); + + Serial.begin(115200); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("28_GPS_Checker Starting")); + Serial.println(); + + startGetFixmS = millis(); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/29_GPS_Checker_With_Display/29_GPS_Checker_With_Display.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/29_GPS_Checker_With_Display/29_GPS_Checker_With_Display.ino new file mode 100644 index 0000000..30b58a1 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/29_GPS_Checker_With_Display/29_GPS_Checker_With_Display.ino @@ -0,0 +1,335 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This program is a portable GPS checker with display option. It uses an SSD1306 or + SH1106 128x64 I2C OLED display. The program reads the GPS for 5 seconds checking for a fix and copies + the characters from the GPS to the serial monitor so you can see if the GPS is working. This is an example + printout from a working GPS with the program having been just been powered on; + + 29_GPS_Checker_Display Starting + + Wait GPS Fix 5 seconds + $GPGGA,235945.020,,,,,0,0,,,M,,M,,*46 + $GPGLL,,,,,235945.020,V,N*74 + $GPGSA,A,1,,,,,,,,,,,,,,,*1E + $GPGSV,1,1,01,11,,,33*78 + $GPRMC,235945.020,V,,,,,0.00,0.00,050180,,,N*4F + $GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32 + $GPGGA,235946.020,,,,,0,0,,,M,,M,,*45 + $GPGLL,,,,,235946.020,V,N*77 + $GPGSA,A,1,,,,,,,,,,,,,,,*1E + $GPGSV,1,1,02,22,,,36,11,,,33*7E + $GPRMC,235946.020,V,,,,,0.00,0.00,050180,,,N*4C + + Timeout - No GPS Fix 5s + Wait GPS Fix 5 seconds + + That printout is from a Meadiatek GPS, the Ublox ones are similar. The data from the GPS is also fed into + the TinyGPS++ library and if there is no fix a message is printed on the serial monitor. + + When the program detects that the GPS has a fix, it prints the Latitude, Longitude, Altitude, Speed, Number + of satellites in use, the HDOP value, time and date to the serial monitor. If the I2C OLED display is + attached that is updated as well. Display is assumed to be on I2C address 0x3C. + + The program has the option of using a pin to control the power to the GPS, if the GPS module being used + has this feature. To use the option change the define; '#define GPSPOWER -1' from -1 to the pin number + being used. Also set the GPSONSTATE and GPSOFFSTATE to the appropriate logic levels. + + Serial monitor baud rate is set at 115200. + + Changes: + 290920 - Add speed to serial monitor output and display + +*******************************************************************************************************/ + +#define Program_Version "V1.2" +#define authorname "Stuart Robinson" + +#include //get library here > http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER -1 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#include +SoftwareSerial GPSserial(RXpin, TXpin); + + +#include //get library here > https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 +#define DEFAULTFONT u8x8_font_chroma48medium8_r //font used by U8X8 Library + + +float GPSLat; //Latitude from GPS +float GPSLon; //Longitude from GPS +float GPSAlt; //Altitude from GPS +uint8_t GPSSats; //number of GPS satellites in use +uint32_t GPSHdop; //HDOP from GPS +float GPSSpeed; //Speed of GPS, mph + +uint8_t hours, mins, secs, day, month; +uint16_t year; +uint32_t startGetFixmS; +uint32_t endFixmS; + + +void loop() +{ + if (gpsWaitFix(5)) + { + Serial.println(); + Serial.println(); + Serial.print(F("Fix time ")); + Serial.print(endFixmS - startGetFixmS); + Serial.println(F("mS")); + + GPSLat = gps.location.lat(); + GPSLon = gps.location.lng(); + GPSAlt = gps.altitude.meters(); + GPSSats = gps.satellites.value(); + GPSHdop = gps.hdop.value(); + GPSSpeed = gps.speed.mph(); + + hours = gps.time.hour(); + mins = gps.time.minute(); + secs = gps.time.second(); + day = gps.date.day(); + month = gps.date.month(); + year = gps.date.year(); + + printGPSfix(); //print GPS data to serial monitor + displayscreen1(); //print GPS data on display + startGetFixmS = millis(); //have a fix, next thing that happens is checking for a fix, so restart timer + } + else + { + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("No GPS Fix ")); + disp.print( (millis() - startGetFixmS) / 1000 ); + Serial.println(); + Serial.println(); + Serial.print(F("Timeout - No GPS Fix ")); + Serial.print( (millis() - startGetFixmS) / 1000 ); + Serial.println(F("s")); + } +} + + +bool gpsWaitFix(uint16_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for updated fix + + uint32_t startmS, waitmS; + uint8_t GPSchar; + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.println(F(" seconds")); + //Serial.print(F("Current millis() ")); + //Serial.println(millis()); + + waitmS = waitSecs * 1000; //convert seconds wait into mS + + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < waitmS) //allows for millis() overflow + { + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + Serial.write(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated() && gps.date.isUpdated()) + { + endFixmS = millis(); //record the time when we got a GPS fix + return true; + } + } + return false; +} + + +void printGPSfix() +{ + float tempfloat; + + Serial.print(F("New GPS Fix ")); + + tempfloat = ( (float) GPSHdop / 100); + + Serial.print(F("Latitude,")); + Serial.print(GPSLat, 6); + Serial.print(F(",Longitude,")); + Serial.print(GPSLon, 6); + Serial.print(F(",Altitude,")); + Serial.print(GPSAlt, 1); + Serial.print(F("m,Speed,")); + Serial.print(GPSSpeed, 1); + Serial.print(F("mph,Sats,")); + Serial.print(GPSSats); + Serial.print(F(",HDOP,")); + Serial.print(tempfloat, 2); + Serial.print(F(",Time,")); + + if (hours < 10) + { + Serial.print(F("0")); + } + + Serial.print(hours); + Serial.print(F(":")); + + if (mins < 10) + { + Serial.print(F("0")); + } + + Serial.print(mins); + Serial.print(F(":")); + + if (secs < 10) + { + Serial.print(F("0")); + } + + Serial.print(secs); + Serial.print(F(",Date,")); + + Serial.print(day); + Serial.print(F("/")); + Serial.print(month); + Serial.print(F("/")); + Serial.print(year); + + Serial.println(); + Serial.println(); +} + + +void displayscreen1() +{ + //show GPS data on display + float tempfloat; + tempfloat = ( (float) GPSHdop / 100); + + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(GPSLat, 6); + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(GPSLon, 6); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(GPSAlt,0); + disp.print(F("m")); + disp.clearLine(3); + disp.setCursor(0, 3); + disp.print(GPSSpeed,0); + disp.print(F("mph")); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(F("Sats ")); + disp.print(GPSSats); + disp.clearLine(5); + disp.setCursor(0, 5); + disp.print(F("HDOP ")); + disp.print(tempfloat); + disp.clearLine(6); + disp.setCursor(0, 6); + + if (hours < 10) + { + disp.print(F("0")); + } + + disp.print(hours); + disp.print(F(":")); + + if (mins < 10) + { + disp.print(F("0")); + } + + disp.print(mins); + disp.print(F(":")); + + if (secs < 10) + { + disp.print(F("0")); + } + + disp.print(secs); + disp.print(F(" ")); + + disp.clearLine(7); + disp.setCursor(0, 7); + + disp.print(day); + disp.print(F("/")); + disp.print(month); + disp.print(F("/")); + disp.print(year); +} + + +void GPSON() +{ + if (GPSPOWER) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void setup() +{ + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + GPSON(); + } + + GPSserial.begin(9600); + + Serial.begin(115200); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + disp.begin(); + disp.setFont(DEFAULTFONT); + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Display Ready")); + + Serial.println(F("29_GPS_Checker_With_Display Starting")); + Serial.println(); + + startGetFixmS = millis(); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/30_I2C_Scanner/30_I2C_Scanner.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/30_I2C_Scanner/30_I2C_Scanner.ino new file mode 100644 index 0000000..79656a0 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/30_I2C_Scanner/30_I2C_Scanner.ino @@ -0,0 +1,69 @@ +// -------------------------------------- +// i2c_scanner +// from: https://playground.arduino.cc/Main/I2cScanner/ + +// This sketch tests the standard 7-bit addresses +// Devices with higher bit address might not be seen properly. + + +#include +uint16_t counter; + +void setup() +{ + Wire.begin(); + + Serial.begin(9600); + Serial.println(F("I2C Scanner starting")); + Serial.println(); +} + + +void loop() +{ + uint8_t error, address; + int16_t nDevices; + + counter++; + Serial.print(counter); + Serial.println(F(" Scanning...")); + + nDevices = 0; + for (address = 1; address < 127; address++ ) + { + // The i2c_scanner uses the return value of + // the Write.endTransmisstion to see if + // a device did acknowledge to the address. + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) + { + Serial.print(F("I2C device found at address 0x")); + if (address < 16) + Serial.print(F("0")); + Serial.println(address, HEX); + nDevices++; + } + else if (error == 4) + { + Serial.print(F("Unknown error at address 0x")); + if (address < 16) + Serial.print(F("0")); + Serial.println(address, HEX); + } + } + + if (nDevices == 0) + { + Serial.println(F("No I2C devices found")); + } + else + { + Serial.println(); + Serial.println(F("Done")); + Serial.println(); + } + + delay(5000); // wait 5 seconds for next scan +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/31_SSD1306_SH1106_OLED_Checker/31_SSD1306_SH1106_OLED_Checker.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/31_SSD1306_SH1106_OLED_Checker/31_SSD1306_SH1106_OLED_Checker.ino new file mode 100644 index 0000000..9a05d5d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/31_SSD1306_SH1106_OLED_Checker/31_SSD1306_SH1106_OLED_Checker.ino @@ -0,0 +1,122 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a simple test program for the SSD1306 and SH1106 OLEDs running on + a 3.3V 8Mhz bare bones ATmega328P. The program prints a short message on each line, pauses, clears the + screen, turns off the screem, puts the processor to sleep and starts again. Sleep current with display + off is circa 8uA. + + OLED address defaults to 0x3C. + + Screen write on 8Mhz Atmel 196mS. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include + + +#include //get library here > https://github.com/olikraus/u8g2 +//U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 +#define DEFAULTFONT u8x8_font_chroma48medium8_r //font for U8X8 Library + + +uint16_t writecount; +uint32_t startwritemS, endwritemS, timemS; + + +void loop() +{ + writecount++; + Serial.print(writecount); + Serial.print(F(" Writing to display")); + + startwritemS = millis(); + disp.clear(); + screen1(); + endwritemS = millis(); + timemS = endwritemS - startwritemS; + disp.setCursor(8, 4); + disp.print(timemS); + disp.print(F("mS")); + + Serial.print(F(" - done ")); + Serial.print(timemS); + Serial.println(F("mS")); + + delay(2000); + Serial.println(F("PowerSave display")); + Serial.flush(); //pending serial output affects sleep mode + disp.setPowerSave(1); //power save display, turns off, sleep current circa 8uA + sleep1seconds(2); //sleep in units of 1 second + disp.setPowerSave(0); //display back to normal +} + + +void sleep1seconds(uint32_t sleeps) +{ + uint32_t index; + + for (index = 1; index <= sleeps; index++) + { + ADCSRA = 0; //disable ADC + MCUSR = 0; //clear various "reset" flags + WDTCSR = bit (WDCE) | bit (WDE); //allow changes, disable reset + WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1); //set interrupt mode and an interval, set WDIE, and 1 seconds sleep + wdt_reset(); //pat the dog + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + MCUCR = bit (BODS) | bit (BODSE); //turn off brown-out enable in software + MCUCR = bit (BODS); + interrupts (); //guarantees next instruction executed + + sleep_cpu (); + //awake here + sleep_disable(); //cancel sleep as a precaution + } +} + +ISR (WDT_vect) +{ + //watchdog interrupt + wdt_disable(); // disable watchdog +} + + +void screen1() +{ + disp.setCursor(0, 0); + disp.print(F("Hello World !")); + disp.setCursor(0, 1); + disp.print(F("Line 1")); + disp.setCursor(0, 2); + disp.print(F("Line 2")); + disp.setCursor(0, 3); + disp.print(F("Line 3")); + disp.setCursor(0, 4); + disp.print(F("Line 4")); + disp.setCursor(0, 5); + disp.print(F("Line 5")); + disp.setCursor(0, 6); + disp.print(F("Line 6")); + disp.setCursor(0, 7); + disp.print(F("0123456789012345")); //display is 8 lines x 16 charaters when using the +} + + +void setup() +{ + Serial.begin(9600); + Serial.println(F("31_SSD1306_SH1106_OLED_Checker starting")); + disp.begin(); + disp.setFont(DEFAULTFONT); +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/32_GPS_Checker_Time/32_GPS_Checker_Time.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/32_GPS_Checker_Time/32_GPS_Checker_Time.ino new file mode 100644 index 0000000..83b182e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/32_GPS_Checker_Time/32_GPS_Checker_Time.ino @@ -0,0 +1,153 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 05/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is GPS Time checker. It reads the GPS NMEA output and displays the time + on the Arduino IDE serial monitor. If you have the serial monitor output on screen as the same time as + observing a web site displaying the current time you can check how close your GPS is reporting to real time. + + At power up a GPS will not normally display the time accurate to the exact second until it receives the + navigation message that is the updated value of current leaps seconds which is sent out every 12.5 minutes. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#define Program_Version "V1.0" +#define authorname "Stuart Robinson" + +#include //get library here > http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER -1 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#include +SoftwareSerial GPSserial(RXpin, TXpin); + +uint8_t hours, mins, secs; + + +void loop() +{ + if (gpsWaitFix(5)) + { + hours = gps.time.hour(); + mins = gps.time.minute(); + secs = gps.time.second(); + + printGPSfix(); + + } + else + { + Serial.println(F("Timeout - No GPS Fix ")); + } +} + + +bool gpsWaitFix(uint16_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for updated fix + + uint32_t endwaitmS; + uint8_t GPSchar; + + endwaitmS = millis() + (waitSecs * 1000); + + while (millis() < endwaitmS) + { + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + //Serial.write(GPSchar); + } + + if (gps.location.isUpdated() && gps.time.isUpdated()) + { + return true; + } + } + + return false; +} + + +void printGPSfix() +{ + Serial.print(F("Time,")); + + if (hours < 10) + { + Serial.print(F("0")); + } + + Serial.print(hours); + Serial.print(F(":")); + + if (mins < 10) + { + Serial.print(F("0")); + } + + Serial.print(mins); + Serial.print(F(":")); + + if (secs < 10) + { + Serial.print(F("0")); + } + + Serial.print(secs); + + Serial.println(); +} + + +void GPSON() +{ + if (GPSPOWER) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void setup() +{ + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + GPSON(); + } + + GPSserial.begin(9600); + + Serial.begin(115200); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("32_GPS_Checker_Time Starting")); + Serial.println(); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/34_ATmel_Sleep_with_Watchdog_Wakeup/34_ATmel_Sleep_with_Watchdog_Wakeup.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/34_ATmel_Sleep_with_Watchdog_Wakeup/34_ATmel_Sleep_with_Watchdog_Wakeup.ino new file mode 100644 index 0000000..be156e7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/34_ATmel_Sleep_with_Watchdog_Wakeup/34_ATmel_Sleep_with_Watchdog_Wakeup.ino @@ -0,0 +1,109 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 03/09/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program tests the sleep mode of an Atmel ATMega328P processor. + + At power up the flashes an LED 4 times, then turns on the LED for 5 seconds. Then the processor is put + to sleep for 16 seconds. On wakeup the LED flashes twice, then is on for 2 seconds and the board goes to + sleep again. And the sequence repeats. + + Sleep current for a 'bare bones' ATmega328 with a MCP1700 regulator @ 3.3V and using an external event + such as a switch to wakeup from sleep should be around 2uA. Using the watchdog timer to wakeup raises + the deep sleep current to circa 6.2uA. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + + +//basic code from here - https://www.gammon.com.au/forum/?id=11497 + +#include +#include + +#define LED1 8 + + +void loop () +{ + Serial.println("LED Off"); + digitalWrite(LED1, LOW); + digitalWrite(13, LOW); + Serial.println(F("Now Sleeping... ")); + Serial.flush(); + + sleep8seconds(2); //sleep for about 16 seconds + + Serial.println("Awake "); + led_Flash(2, 125); + Serial.println("LED On "); + digitalWrite(LED1, HIGH); + digitalWrite(13, HIGH); + delay(2000); +} + + +void sleep8seconds(uint32_t sleeps) +{ + uint32_t index; + + for (index = 1; index <= sleeps; index++) + { + ADCSRA = 0; //disable ADC + MCUSR = 0; //clear various "reset" flags + WDTCSR = bit (WDCE) | bit (WDE); //allow changes, disable reset + WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0); //set interrupt mode and an interval, set WDIE, and 8 seconds delay + wdt_reset(); //pat the dog + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + MCUCR = bit (BODS) | bit (BODSE); //turn off brown-out enable in software + MCUCR = bit (BODS); + interrupts (); //guarantees next instruction executed + + sleep_cpu (); + //awake here + sleep_disable(); //cancel sleep as a precaution + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + digitalWrite(13, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + digitalWrite(13, LOW); + delay(delaymS); + } +} + + +void setup () +{ + pinMode(LED1, OUTPUT); + pinMode(13, OUTPUT); + Serial.begin(9600); + led_Flash(4, 125); + Serial.println("LED On "); + digitalWrite(LED1, HIGH); + digitalWrite(13, HIGH); + delay(5000); +} + + +ISR (WDT_vect) +{ + //watchdog interrupt + wdt_disable(); // disable watchdog +} + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/37_Servo_Sweep_Tester/37_Servo_Sweep_Tester.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/37_Servo_Sweep_Tester/37_Servo_Sweep_Tester.ino new file mode 100644 index 0000000..83f1525 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/37_Servo_Sweep_Tester/37_Servo_Sweep_Tester.ino @@ -0,0 +1,78 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 30/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program sweeps two servos from one end of their travel to the other. Useful to + check servos are connected correctly and working. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + + +#define pinservoX1 2 //pin for controlling servo X 1 +#define pinservoY1 4 //pin for controlling servo Y 1 + + +#define LED1 8 + +#include + +Servo ServoX1; //create the servo object +Servo ServoY1; //create the servo object + + +void loop() +{ + uint16_t index; + + for (index = 900; index <= 2100; index++) + { + //Serial.print(F("Microseconds1 ")); + //Serial.println(index); + ServoX1.writeMicroseconds(index); + ServoY1.writeMicroseconds(index); + } + + delay(1000); + + for (index = 2100; index >= 900; index--) + { + //Serial.print(F("Microseconds1 ")); + //Serial.println(index); + ServoX1.writeMicroseconds(index); + ServoY1.writeMicroseconds(index); + } + + delay(1000); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + + Serial.begin(9600); + ServoX1.attach(pinservoX1); //using pin servoX for servo object + ServoY1.attach(pinservoY1); //using pin servoX for servo object + + Serial.println(F("Servo sweep test starting")); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/43_SD_Card_Test/43_SD_Card_Test.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/43_SD_Card_Test/43_SD_Card_Test.ino new file mode 100644 index 0000000..9c78ee2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/43_SD_Card_Test/43_SD_Card_Test.ino @@ -0,0 +1,267 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 04/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This test program has been written to check that a connected SD card adapter, Micro + or standard, is functional. + + When the program runs it will attempts to create a file that is next in sequence to Log0000.txt, thus + if this is the first time the program has run on the SD card it will create file Log0001.txt. If the + file Log0001.txt exists it will create Log0002.txt etc. This ensures that everytime the program starts + it creates a new file for testing. + + Next the program checks if the Logxxxx.txt file exists and if so attempts to delete it. + + Then the program starts a loop. First the same file is again opened for append, it will be created if it + does not exist and then the line '1 Hello World' is writtent to the file. The file is closed and the + contents of the file are dumped to the serial monitor. The loop restarts, and this time the line + '2 Hello World' is appended to the file. + + As the program progresses the file will grow in size and after 4 iterrations of the open,write,close + and dump loop the file dump on serial monitor will look like this + + 1 Hello World + 2 Hello World + 3 Hello World + 4 Hello World + + This file dump will grow if you let the program run. If an error with the SD card is detected at any + time the LED will rapid flash continuously and the message 'X Card failed, or not present' is printed + to serial monitor. The number X will allow you to check the program listing for where the error occured. + + 1 Card failed = SD card did not initialise + 2 Card failed = Could nt setup logFile for new name + 3 Card failed = Could not open file for append + 4 Card failed = Failure to dump file to serial monitor + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ +#define Program_Version "V1.1" + + +#include +#include + + +#define LED1 8 //pin number for LED +#define SDCS 4 //pin number for device select on SD card module + +File logFile; + +char filename[] = "/LOG0000.TXT"; //filename used as base for creating logfile, 0000 replaced with numbers + +uint16_t linecount; + + +void loop() +{ + Serial.println(); + Serial.println("Initializing SD card"); + + if (!SD.begin(SDCS)) + { + cardFail(1); + } + + Serial.println("Card initialized"); + + logFile = SD.open("/"); + Serial.println("Card directory"); + Serial.println(); + printDirectory(logFile, 0); + Serial.println(); + Serial.println(); + + if (!setupSDLOG(filename)) //setup logfile name for writing + { + cardFail(2); + } + + Serial.print(F("logFile name is ")); + Serial.println(filename); + + if (SD.exists(filename)) + { + Serial.print(filename); + Serial.println(" exists - delete"); + SD.remove(filename); + } + + if (!SD.exists(filename)) + { + Serial.print(filename); + Serial.println(" does not exist"); + } + + while (1) + { + logFile = SD.open(filename, FILE_WRITE); + + if (!logFile) + { + cardFail(3); + } + + linecount++; + logFile.print(linecount); + logFile.println(" Hello World"); + logFile.close(); + + Serial.println(); + Serial.print("Dump file "); + Serial.println(filename); + Serial.println(); + + digitalWrite(LED1, HIGH); + + if (dumpFile(filename)) + { + Serial.println(); + Serial.println("Finished File Dump"); + } + else + { + cardFail(4); + } + + digitalWrite(LED1, LOW); + delay(2000); + } +} + + +void printDirectory(File dir, int numTabs) +{ + while (true) + { + + File entry = dir.openNextFile(); + if (! entry) + { + //no more files + break; + } + for (uint8_t i = 0; i < numTabs; i++) { + Serial.print('\t'); + } + Serial.print(entry.name()); + if (entry.isDirectory()) + { + Serial.println("/"); + printDirectory(entry, numTabs + 1); + } + else + { + //files have sizes, directories do not + Serial.print("\t\t"); + Serial.println(entry.size(), DEC); + } + entry.close(); + } +} + + +bool dumpFile(char *buf) +{ + + if (SD.exists(buf)) + { + Serial.print(buf); + Serial.println(" found"); + } + else + { + Serial.print(filename); + Serial.println(" not found"); + return false; + } + + logFile = SD.open(buf); + + if (logFile) //if the file is available, read from it + { + while (logFile.available()) + { + Serial.write(logFile.read()); + } + logFile.close(); + return true; + } + + return false; +} + + +uint8_t setupSDLOG(char *buf) +{ + //creats a new filename + + uint16_t index; + + for (index = 1; index <= 9999; index++) { + buf[4] = index / 1000 + '0'; + buf[5] = ((index % 1000) / 100) + '0'; + buf[6] = ((index % 100) / 10) + '0'; + buf[7] = index % 10 + '0' ; + if (! SD.exists(filename)) { + // only open a new file if it doesn't exist + logFile = SD.open(buf, FILE_WRITE); + break; + } + } + + if (!logFile) + { + return 0; + } + + return index; //return number of logfile created +} + + +void cardFail(uint8_t num) +{ + while (1) + { + Serial.print(num); //so we can tell where card failed + Serial.println(" Card failed, or not present"); + led_Flash(100, 25); + } +} + + +void led_Flash(unsigned int flashes, unsigned int delaymS) +{ + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //for PCB LED + led_Flash(4, 125); + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("43_SD_Card_Test Starting")); +} + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/45_Battery_Voltage_Read_Test/45_Battery_Voltage_Read_Test.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/45_Battery_Voltage_Read_Test/45_Battery_Voltage_Read_Test.ino new file mode 100644 index 0000000..ed7fb13 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/45_Battery_Voltage_Read_Test/45_Battery_Voltage_Read_Test.ino @@ -0,0 +1,115 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This test program has been written to check that hardware for reading the battery + voltage has been assembled correctly such that it is funtional. The value defined as 'ADMultiplier' + in settings.h is used to adjust the value read from the 91K\11K resistor divider and convert into mV. + + There is also an option of using a logic pin to turn the resistor divider used to read battery voltage on + and off. This reduces current used in sleep mode. To use the feature set the define for pin BATVREADON + in 'Settings.h' to the pin used. If not using the feature set the pin number to -1. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ +/* +****************************************************************************************************** + Program operation - + +****************************************************************************************************** +*/ + +#define ADMultiplier 6.36 //adjustment to convert AD value read into mV of battery voltage +#define BATVREADON 8 //used to turn on the resistor divider to measure voltage, //this pin turns on the MOSFET that switches in the resistor divider +#define LED1 8 //pin for PCB LED +#define SupplyAD A0 //Resitor divider for battery connected here + +void loop() +{ + Serial.println(F("LED Flash")); + led_Flash(4, 125); + printSupplyVoltage(); + Serial.println(); + delay(1500); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void printSupplyVoltage() +{ + //get and display supply volts on terminal or monitor + Serial.print(F("Supply Volts ")); + Serial.print(readSupplyVoltage()); + Serial.println(F("mV")); +} + + +uint16_t readSupplyVoltage() +{ + //relies on internal reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t volts = 0; + byte index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn MOSFET connection resitor divider in circuit + } + + analogReference(INTERNAL); + temp = analogRead(SupplyAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(SupplyAD); + volts = volts + temp; + } + volts = ((volts / 5) * ADMultiplier); + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn MOSFET connection resitor divider in circuit + } + + return volts; +} + + +void setup() +{ + Serial.begin(9600); //setup Serial console ouput + Serial.println(); + Serial.println(__FILE__); + Serial.print(F("Compiled ")); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println("45_Battery_Voltage_Read_Test Starting"); + + pinMode(LED1, OUTPUT); //for PCB LED + + if (BATVREADON >= 0) + { + pinMode (BATVREADON, OUTPUT); //for turning on resistor divider + } +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/46_I2C_FRAM_Memory_Test/46_I2C_FRAM_Memory_Test.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/46_I2C_FRAM_Memory_Test/46_I2C_FRAM_Memory_Test.ino new file mode 100644 index 0000000..9c0502f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/46_I2C_FRAM_Memory_Test/46_I2C_FRAM_Memory_Test.ino @@ -0,0 +1,154 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is suitable + for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program tests that an MB85RC16PNF or FM24CL64 FRAM is working and retaining + memory contents when power is removed. FRAMs are non-voltatile memory devices with a typical write + endurance of from 10,000,000,000 (MB85RC16PNF) to 100,000,000,000,000 (FM24CL64B) write cycles. + + When the program starts the serial monitor will first display the results of an I2C Device scan which + should list 8 devices found from 0x50 to 0x57 for a MB85RC16PNF and 0x50 for a FM24CL64 with the default + pin connections. The MB85RC16PNF has eight 256 byte pages, FM24CL64 is one page of 8kbytes. The program + then will print the contents of the memory. Next all the variable types will be written to successive + addresses will print the area of memory, then read back each variable from memory. + + To check if the FRAM is reating memory you could make some changes to the variables written, run the + program and then power off. On restart the changed variables should be displayed. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ +#define Program_Version "V1.0" + + +//#include //SX12xx library file for FM24CL64 FRAM, 64kbit, 8kbyte, , I2C addresse 0x50 +#include //SX12xx library file for MB85RC16PNF FRAM, 16kbit, 2kbyte, I2C addresses 0x50 to 0x57 + +#include +#include "I2C_Scanner.h" + +#define Serial_Monitor_Baud 9600 //this is baud rate used for the Arduino IDE Serial Monitor + +int16_t Memory_Address = 0x50; //default I2C address of MB85RC16PNF and FM24CL64 FRAM + +void loop() +{ + Serial.println(F("Printing memory")); + Serial.flush(); + printMemory(0, 31); + Serial.println(); + Serial.flush(); + + while (true) + { + Serial.println(); + writeMemory(); + Serial.println(); + readMemory(); + Serial.println(); + delay(5000); + } +} + + +void writeMemory() +{ + Serial.println(F("Writing variables")); + Serial.flush(); + Serial.println(F("Write char A")); + writeMemoryChar(0, 'A'); + + Serial.println(F("Write int8_t 0x55")); + writeMemoryInt8(1, 0x55); + + Serial.println(F("Write uint8_t 0xAA")); + writeMemoryUint8(2, 0xAA); + + Serial.println(F("Write int16_t 0xFEDC")); + writeMemoryInt16(3, 0xFEDC); + + Serial.println(F("Write uint16_t 0x1234")); + writeMemoryUint16(5, 0x1234); + + Serial.println(F("Write int32_t 0xFEDCBA98")); + writeMemoryInt32(7, 0xFEDCBA98); + + Serial.println(F("Write uint32_t 0x12345678")); + writeMemoryUint32(11, 0x12345678); + + Serial.println(F("Write float 0.12345678")); + writeMemoryFloat(15, 0.12345678); +} + + +void readMemory() +{ + char var1; + int8_t var2; + uint8_t var3; + int16_t var4; + uint16_t var5; + int32_t var6; + uint32_t var7; + float var8; + char buf[9]; + + Serial.println(F("Reading variables")); + Serial.flush(); + var1 = readMemoryChar(0); + Serial.print(F("Read char ")); + Serial.write(var1); + Serial.println(); + + var2 = readMemoryInt8(1); + Serial.print(F("Read int8_t 0x")); + Serial.println(var2,HEX); + + var3 = readMemoryUint8(2); + Serial.print(F("Read uint8_t 0x")); + Serial.println(var3,HEX); + + var4 = readMemoryInt16(3); + Serial.print(F("Read int16_t 0x")); + sprintf(buf, "%04X", var4); + Serial.println(buf); + + var5 = readMemoryUint16(5); + Serial.print(F("Read uint16_t 0x")); + Serial.println(var5,HEX); + + var6 = readMemoryInt32(7); + Serial.print(F("Read int32_t 0x")); + Serial.println(var6,HEX); + + var7 = readMemoryUint32(11); + Serial.print(F("Read uint32_t 0x")); + Serial.println(var7,HEX); + + var8 = readMemoryFloat(15); + Serial.print(F("Read float ")); + Serial.println(var8, 7); +} + + +void setup() +{ + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + setup_I2CScan(); + run_I2CScan(); + + memoryStart(Memory_Address); //optional command to start memory with I2C address, if required + +} + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/46_I2C_FRAM_Memory_Test/I2C_Scanner.h b/lib/SX12XX-LoRa/examples/Hardware_Checks/46_I2C_FRAM_Memory_Test/I2C_Scanner.h new file mode 100644 index 0000000..3ec460a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/46_I2C_FRAM_Memory_Test/I2C_Scanner.h @@ -0,0 +1,84 @@ + // -------------------------------------- + // i2c_scanner + // + // Version 1 + // This program (or code that looks like it) + // can be found in many places. + // For example on the Arduino.cc forum. + // The original author is not know. + // Version 2, Juni 2012, Using Arduino 1.0.1 + // Adapted to be as simple as possible by Arduino.cc user Krodal + // Version 3, Feb 26 2013 + // V3 by louarnold + // Version 4, March 3, 2013, Using Arduino 1.0.3 + // by Arduino.cc user Krodal. + // Changes by louarnold removed. + // Scanning addresses changed from 0...127 to 1...119, + // according to the i2c scanner by Nick Gammon + // http://www.gammon.com.au/forum/?id=10896 + // Version 5, March 28, 2013 + // As version 4, but address scans now to 127. + // A sensor seems to use address 120. + // Version 6, November 27, 2015. + // Added waiting for the Leonardo serial communication. + // + // + // This sketch tests the standard 7-bit addresses + // Devices with higher bit address might not be seen properly. + // + + + //#include + + + void setup_I2CScan() + { + Wire.begin(); + + //Serial.begin(9600); + Serial.println("I2C Scanner"); + } + + + void run_I2CScan() + { + byte error, address; + int nDevices; + + Serial.println("Scanning for I2C Devices..."); + + nDevices = 0; + for(address = 1; address < 127; address++ ) + { + // The i2c_scanner uses the return value of + // the Write.endTransmisstion to see if + // a device did acknowledge to the address. + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) + { + Serial.print("I2C device found at address 0x"); + if (address<16) + Serial.print("0"); + Serial.println(address,HEX); + + nDevices++; + } + else if (error==4) + { + Serial.print("Unknown error at address 0x"); + if (address<16) + Serial.print("0"); + Serial.println(address,HEX); + } + } + if (nDevices == 0) + Serial.println("No I2C devices found"); + else + Serial.println("done"); + + Wire.endTransmission(); + } + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/48_DS18B20_Test/48_DS18B20_Test.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/48_DS18B20_Test/48_DS18B20_Test.ino new file mode 100644 index 0000000..d32de64 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/48_DS18B20_Test/48_DS18B20_Test.ino @@ -0,0 +1,84 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program reads a single DS18B20 temperature sensor and prints the result to the + serial monitor. + + The program also has the option of using a logic pin to control the power to the lora and SD card + devices, which can save power in sleep mode. If the hardware is fitted to your board then these devices are + assumed to be powered on by setting the VCCPOWER pin low. If your board does not have this feature set + VCCPOWER to -1. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + + +#define programversion "V1.0" + +#define ONE_WIRE_BUS 4 //pin the DS18B20 is connected to +#define LED1 8 //LED, on when reading temperature + + +#include //get library here > https://github.com/PaulStoffregen/OneWire +OneWire oneWire(ONE_WIRE_BUS); //create instance of OneWire library + +#include //get library here > https://github.com/milesburton/Arduino-Temperature-Control-Library +DallasTemperature sensor(&oneWire); //create instance of dallas library + + +void loop() +{ + float DS18B20temperature; + + digitalWrite(LED1, HIGH); + sensor.requestTemperatures(); + digitalWrite(LED1, LOW); + + DS18B20temperature = sensor.getTempCByIndex(0); + Serial.print(F("DS18B20 Temperature ")); + Serial.print(DS18B20temperature, 2); + Serial.println(F("c")); + delay(5000); +} + + +void led_Flash(unsigned int flashes, unsigned int delaymS) +{ + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, LOW); + delay(delaymS); + digitalWrite(LED1, HIGH); + delay(delaymS); + } +} + + +void setup() +{ + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(programversion)); + Serial.println(); + Serial.println("48_DS18B20_Test Starting"); + + pinMode(LED1, OUTPUT); + + led_Flash(4, 125); //one second of flashes + + sensor.begin(); + sensor.requestTemperatures(); //do a null temperature read +} + + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/57_NRF24L01_Is_It_There/57_NRF24L01_Is_It_There.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/57_NRF24L01_Is_It_There/57_NRF24L01_Is_It_There.ino new file mode 100644 index 0000000..a5d8890 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/57_NRF24L01_Is_It_There/57_NRF24L01_Is_It_There.ino @@ -0,0 +1,103 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 05/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is stand alone, it is not necessary to install a NRF2401 library to + check the NRF2401 can be written to and read from over the SPI bus. + + The contents of the NRF2401 registers from 0x00 to 0x1F are read and printed to the serial monitor. + If the connections are OK then the printout should look like this; + + 57_NRF24L01_Is_It_There ? + + Print registers 0x00 to 0x1F + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00 00 3F 02 03 5F 4C 27 42 00 00 E7 52 C3 C4 C5 C6 + 0x10 E7 00 20 00 00 00 00 12 00 00 FF FF FF FF FF FF + + Note that this is just a 'is it there type check' the CE pin is not used or needed for this simple check. + If the device is faulty, not present or wired incorrectly the register contents will likely be all 00s or + FFs. The program makes no attempt to turn on the RF transmitter or receiver. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include + +#define CSN_PIN 10 + +#define NUM_REGISTERS 26 +#define CMD_R_REGISTER 0x00 +#define CMD_W_REGISTER 0x20 + + +void loop() +{ + Serial.println(F("Print registers 0x00 to 0x1F")); + printRegisters(0, 0x1F); + Serial.println(); + delay(5000); +} + + +void printRegisters(uint16_t Start, uint16_t End) +{ + uint16_t Loopv1, Loopv2, RegData; + + Serial.print(F("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F")); + Serial.println(); + + for (Loopv1 = Start; Loopv1 <= End;) //32 lines + { + Serial.print(F("0x")); + if (Loopv1 < 0x10) + { + Serial.print(F("0")); + } + Serial.print((Loopv1), HEX); //print the register number + Serial.print(F(" ")); + for (Loopv2 = 0; Loopv2 <= 15; Loopv2++) + { + RegData = readRegister(Loopv1); + if (RegData < 0x10) + { + Serial.print(F("0")); + } + Serial.print(RegData, HEX); //print the register number + Serial.print(F(" ")); + Loopv1++; + } + Serial.println(); + } +} + + +uint8_t readRegister(uint8_t reg) +{ + uint8_t result = 0xFF; + + if (reg < NUM_REGISTERS) { + digitalWrite(CSN_PIN, LOW); + SPI.transfer(CMD_R_REGISTER | reg); + result = SPI.transfer(0xff); + digitalWrite(CSN_PIN, HIGH); + } + + return result; +} + + +void setup() +{ + Serial.begin(9600); + pinMode(CSN_PIN, OUTPUT); + Serial.println(); + Serial.println(F("57_NRF24L01_Is_It_There ?")); + Serial.println(); + SPI.begin(); +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/61_Bluetooth_Test/61_Bluetooth_Test.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/61_Bluetooth_Test/61_Bluetooth_Test.ino new file mode 100644 index 0000000..35cc291 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/61_Bluetooth_Test/61_Bluetooth_Test.ino @@ -0,0 +1,48 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a simple program to test a Serial Bluetooth device is working. The program + uses software serial to send a Hello World message to a serial port. If the Bluetooth device is paired + with an Android device you should see Hello World in an Andriod Bluetooth terminal application. + + Bluetooth baud rate is set at 9600, an HC-06 Bluetooth device is assumed. + Note that not all pins on all Arduinos will work with software serial, see here; + + https://www.arduino.cc/en/Reference/softwareSerial + + Serial monitor baud rate is set at 115200. + +*******************************************************************************************************/ + + +#define BlueToothBAUD 9600 //this is the serial baud rate that will be used for the Bluetooth, a common default +#define MONITORBAUD 9600 //this is the serial baud rate that will be used for the serial monitor + +#define RXpin A3 //this is the pin that the Arduino will use to receive data from the Bluetooth +#define TXpin A2 //this is the pin that the Arduino can use to send data (commands) to the Bluetooth + +#include + +SoftwareSerial BlueTooth(RXpin, TXpin); + +uint32_t counter; + +void loop() +{ + BlueTooth.print(counter++); + BlueTooth.println(F(" Hello World")); + delay(1000); +} + + +void setup() +{ + BlueTooth.begin(BlueToothBAUD); + Serial.begin(MONITORBAUD); + Serial.println("61_Bluetooth_Test Starting"); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/63_QuectelGPS_Firmware_Checker/63_QuectelGPS_Firmware_Checker.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/63_QuectelGPS_Firmware_Checker/63_QuectelGPS_Firmware_Checker.ino new file mode 100644 index 0000000..1dd673b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/63_QuectelGPS_Firmware_Checker/63_QuectelGPS_Firmware_Checker.ino @@ -0,0 +1,62 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/09/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program sends a command to a Quectel GPS to return its firmware revision. + The output from the GPS is copied to the Serial Monitor. + + If you get no data displayed on the serial monitor, the most likely cause is that you have the receive + data pin into the Arduino (RX) pin connected incorrectly. + + If the data displayed on the serial terminal appears to be random text with odd symbols its very + likely you have the GPS serial baud rate set incorrectly. + + Note that not all pins on all Arduinos will work with software serial, see here; + + https://www.arduino.cc/en/Reference/softwareSerial + + Serial monitor baud rate is set at 115200. + +*******************************************************************************************************/ + + +#define GPSBAUD 9600 //this is the serial baud rate that will be used for the GPS, a common default +#define MONITORBAUD 115200 //this is the serial baud rate that will be used for the serial monitor + +#define RXpin A3 //this is the pin that the Arduino will use to receive data from the GPS +#define TXpin A2 //this is the pin that the Arduino can use to send data (commands) to the GPS - not used +#define GPSPOWER 4 //When high this pin turns GPS on +#define LED1 8 //when high this pin turns LED on + +#include + +SoftwareSerial GPS(RXpin, TXpin); + +void loop() +{ + while (GPS.available()) + { + Serial.write(GPS.read()); + } +} + + +void setup() +{ + pinMode(GPSPOWER, OUTPUT); + digitalWrite(GPSPOWER, HIGH); + + pinMode(LED1, OUTPUT); + digitalWrite(LED1, HIGH); + + GPS.begin(GPSBAUD); + Serial.begin(MONITORBAUD); + Serial.println("63_QuectelGPS_Firmware_Checker"); + delay(2000); + GPS.println("$PMTK605*31"); + +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/72A_GPS_Speedo_With_Display/72A_GPS_Speedo_With_Display.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/72A_GPS_Speedo_With_Display/72A_GPS_Speedo_With_Display.ino new file mode 100644 index 0000000..820aaa2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/72A_GPS_Speedo_With_Display/72A_GPS_Speedo_With_Display.ino @@ -0,0 +1,145 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 12/10/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This program uses a GPS to indicate speed, should work with most GPSs. Speed update + rate approx once per second. GPS characters are output to serial monitor when checking for update from + GPS. Prints speed in large text on an SSD1306 or SH1106 OLED. + + Display and serial monitor updatesto speed only occur when the a new update has been decoded from the + GPS. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#include //get library here > http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#include +SoftwareSerial GPSserial(RXpin, TXpin); + +#include //get library here > https://github.com/olikraus/u8g2 +#include + +U8G2_SSD1306_128X64_NONAME_1_HW_I2C disp(U8G2_R0, U8X8_PIN_NONE); //use this line for 0.96" SSD1306 OLED +//U8G2_SH1106_128X64_NONAME_1_HW_I2C disp(U8G2_R0, U8X8_PIN_NONE); //use this line for 1.3" SH1106 OLED + +float GPSSpeed; //Speed of GPS, mph + +uint32_t startGetFixmS; +uint32_t endFixmS; + + +void loop() +{ + if (gpsWaitFix(3)) + { + Serial.println(); + Serial.println(); + Serial.print(F("Fix time ")); + Serial.print(endFixmS - startGetFixmS); + Serial.println(F("mS")); + + GPSSpeed = gps.speed.mph(); + + Serial.print(F("Speed,")); + Serial.print(GPSSpeed, 1); + Serial.println(); + + displayscreen1(); //print GPS data on display + startGetFixmS = millis(); //have a fix, next thing that happens is checking for a fix, so restart timer + } + else + { + displayscreen2(); + Serial.println(); + Serial.println(); + Serial.print(F("Timeout - No GPS Fix ")); + Serial.print( (millis() - startGetFixmS) / 1000 ); + Serial.println(F("s")); + } +} + + +bool gpsWaitFix(uint16_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for good fix + + uint32_t startmS, waitmS; + uint8_t GPSchar; + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.println(F(" seconds")); + + waitmS = waitSecs * 1000; //convert seconds wait into mS + startmS = millis(); + + while ((uint32_t) (millis() - startmS) < waitmS) + { + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + Serial.write(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated() && gps.speed.isUpdated()) + { + endFixmS = millis(); //record the time when we got a new GPS update + return true; + } + } + + return false; +} + + +void displayscreen1() +{ + disp.setFont(u8g2_font_logisoso62_tn); + disp.firstPage(); + do { + disp.setCursor(0, 63); + disp.print(GPSSpeed, 0); + } while ( disp.nextPage() ); +} + + +void displayscreen2() +{ + disp.setFont(u8g2_font_lubB14_tr); + disp.firstPage(); + do { + disp.setCursor(35, 15); + disp.print(F("Speedo")); + disp.setCursor(15, 45); + disp.print(F("No GPS fix")); + } while ( disp.nextPage() ); +} + + +void setup() +{ + delay(1000); + Serial.begin(115200); + GPSserial.begin(9600); + + disp.begin(); + disp.clear(); + displayscreen2(); + disp.print(F("Display Ready")); + + Serial.println(F("72A_GPS_Speedo_With_Display Starting")); + Serial.println(); + + startGetFixmS = millis(); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/72_GPS_Speedo/72_GPS_Speedo.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/72_GPS_Speedo/72_GPS_Speedo.ino new file mode 100644 index 0000000..9b93ebf --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/72_GPS_Speedo/72_GPS_Speedo.ino @@ -0,0 +1,105 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 30/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This program uses a GPS to indicate speed, should work with most GPSs. Speed update + rate approx onece per second. GPS characters are output to serial monitor when checking for update from + GPS. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#include //get library here > http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#include +SoftwareSerial GPSserial(RXpin, TXpin); + +float GPSSpeed; //Speed of GPS, mph + +uint32_t startGetFixmS; +uint32_t endFixmS; + + +void loop() +{ + if (gpsWaitFix(5)) + { + Serial.println(); + Serial.println(); + Serial.print(F("Fix time ")); + Serial.print(endFixmS - startGetFixmS); + Serial.println(F("mS")); + + GPSSpeed = gps.speed.mph(); + + Serial.print(F("Speed,")); + Serial.print(GPSSpeed, 1); + Serial.println(); + + startGetFixmS = millis(); //have a fix, next thing that happens is checking for a fix, so restart timer + } + else + { + Serial.println(); + Serial.println(); + Serial.print(F("Timeout - No GPS Fix ")); + Serial.print( (millis() - startGetFixmS) / 1000 ); + Serial.println(F("s")); + } +} + + +bool gpsWaitFix(uint16_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for good fix + + uint32_t startmS, waitmS; + uint8_t GPSchar; + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.println(F(" seconds")); + + waitmS = waitSecs * 1000; //convert seconds wait into mS + startmS = millis(); + + while ((uint32_t) (millis() - startmS) < waitmS) + { + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + Serial.write(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated() && gps.speed.isUpdated()) + { + endFixmS = millis(); //record the time when we got a new GPS update + return true; + } + } + + return false; +} + + +void setup() +{ + delay(1000); + Serial.begin(115200); + GPSserial.begin(9600); + + Serial.println(F("72_GPS_Speedo Starting")); + Serial.println(); + + startGetFixmS = millis(); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/77_ILI9341_Display_Checker/77_ILI9341_Display_Checker.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/77_ILI9341_Display_Checker/77_ILI9341_Display_Checker.ino new file mode 100644 index 0000000..47a9d62 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/77_ILI9341_Display_Checker/77_ILI9341_Display_Checker.ino @@ -0,0 +1,134 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 30/09/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a simple test program for the LIL9341 TFT display that comes in a + range of sizes. The program prints a short message on each line, pauses, clears the screen, and starts + again. Program uses Adafruit ILI9341 library. + + Screen write time on Arduino DUE 332mS, screen clear 268mS + Screen write time on Teensy 4.1 207mS, screen clear 180mS + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include "SPI.h" +#include "Adafruit_GFX.h" //get library here > https://github.com/adafruit/Adafruit-GFX-Library +#include "Adafruit_ILI9341.h" //get library here > https://github.com/adafruit/Adafruit_ILI9341 + +int8_t DISPCS = 10; +int8_t DISPDC = 8; + +//int8_t DISPCS = 43; //for DUE shield +//int8_t DISPDC = 45; //for DUE shield + +Adafruit_ILI9341 disp = Adafruit_ILI9341(DISPCS, DISPDC); + +#define textscale 2 //default text scale + +uint16_t writecount; +uint32_t startmS, endmS, timemS; + + +void loop() +{ + setCursor(0, 0); + disp.setTextColor(ILI9341_WHITE); + disp.setTextSize(textscale); + + writecount++; + Serial.print(writecount); + Serial.print(F(" Writing to display")); + + startmS = millis(); + disp.fillScreen(ILI9341_BLACK); //clear screen + screen1(); + endmS = millis(); + timemS = endmS - startmS; + setCursor(8, 4); + disp.print(timemS); + disp.print(F("mS")); + + Serial.print(F(" - done ")); + Serial.print(timemS); + Serial.println(F("mS")); + + delay(2000); + + startmS = millis(); + disp.fillScreen(ILI9341_BLACK); //clear screen + endmS = millis(); + timemS = endmS - startmS; + setCursor(0, 0); + disp.print(F("Screen clear ")); + disp.print(timemS); + disp.print(F("mS")); + + delay(2000); + +} + + +void setCursor(uint8_t lcol, uint8_t lrow) +{ + disp.setCursor((lcol * 6 * textscale), (lrow * 10 * textscale)); +} + + +void screen1() +{ + setCursor(0, 0); + disp.print(F("ILI9341_Checker")); + + setCursor(0, 1); + disp.print(F("Line 1")); + + setCursor(0, 2); + disp.print(F("Line 2")); + + setCursor(0, 3); + disp.print(F("Line 3")); + + setCursor(0, 4); + disp.print(F("Line 4")); + + setCursor(0, 5); + disp.print(F("Line 5")); + + setCursor(0, 6); + disp.print(F("Line 6")); + + setCursor(0, 7); + disp.print(F("Line 7")); + + setCursor(0, 8); + disp.print(F("Line 8")); + + setCursor(0, 9); + disp.print(F("Line 9")); + + setCursor(0, 10); + disp.print(F("Line 10")); + + setCursor(0, 11); + disp.print(F("01234567890123456789012")); //display is 12 lines x 23 charaters +} + + +void setup() +{ + Serial.begin(9600); + Serial.println(F("77_ILI9341_Display_Checker starting")); + SPI.begin(); + disp.begin(); + //disp.setFont(Arial_16); + disp.fillScreen(ILI9341_BLACK); + disp.setTextColor(ILI9341_WHITE); + disp.setRotation(1); + disp.setTextSize(textscale); +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/79_Serial_Terminal/79_Serial_Terminal.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/79_Serial_Terminal/79_Serial_Terminal.ino new file mode 100644 index 0000000..2d24860 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/79_Serial_Terminal/79_Serial_Terminal.ino @@ -0,0 +1,55 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 14/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a simple terminal to allow you to use a Serial terminal program, such as Teraterm + or CoolTerm to talk to a serial device such as a Bluetooth module connected to your Arduino. + + Note that not all pins on all Arduinos will work with software serial, see here; + + https://www.arduino.cc/en/Reference/softwareSerial + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + + +#define DEVICEBAUD 9600 //serial baud rate that will be used for the device conncted to your Arduino +#define MONITORBAUD 9600 //serial baud rate that will be used for the serial monitor + +#define RXpin A3 //this is the pin that the Arduino will use to receive data from the serial device +#define TXpin A2 //this is the pin that the Arduino can use to send data (commands) to the serial device + + +#include + +SoftwareSerial device(RXpin, TXpin); + +void loop() +{ + while (device.available()) + { + Serial.write(device.read()); + } + + while (Serial.available()) + { + device.write(Serial.read()); + } + +} + + +void setup() +{ + device.begin(DEVICEBAUD); + Serial.begin(MONITORBAUD); + Serial.println("79_Serial_Terminal Starting"); + + delay(1000); + +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/81_BME280_Test/81_BME280_Test.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/81_BME280_Test/81_BME280_Test.ino new file mode 100644 index 0000000..ad1cbaf --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/81_BME280_Test/81_BME280_Test.ino @@ -0,0 +1,120 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 03/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a simple test for a Bosch BME280 sensor. Readings are sent to the serial + monitor. The Seeed library assumes the BME280 is at address 0x76. It can be changed to 0x77 by an edit + in the Seeed_BME280.h librsry file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include //get library here; https://github.com/Seeed-Studio/Grove_BME280 +BME280 bme280; //create an instance of the BME280 senosor + +#define BME280_REGISTER_CONTROL 0xF4 //BME280 register number for power control + +#include + +#define LED1 8 //on board LED, high for on + +float temperature; //the BME280 temperature value +float pressure; //the BME280 pressure value +uint16_t humidity; //the BME280 humididty value + + +void loop() +{ + Serial.print(F("Reading > ")); + + readSensors(); //read the sensor values + printSensorValues(); //print the sensor values + Serial.println(); + + sleepBME280(); + delay(500); + normalBME280(); //BME280 sensor to normal mode +} + + +void readSensors() +{ + //read the sensor values into the global variables + temperature = bme280.getTemperature(); + pressure = bme280.getPressure(); + humidity = bme280.getHumidity(); + +} + + +void printSensorValues() +{ + Serial.print(F("Temperature,")); + Serial.print(temperature, 1); + Serial.print(F("c,Pressure,")); + Serial.print(pressure, 0); + Serial.print(F("Pa,Humidity,")); + Serial.print(humidity); + Serial.print(F("%")); + Serial.print(F(" ")); + Serial.flush(); +} + + +void sleepBME280() +{ + //write this register value to BME280 to put it to sleep + writeBME280reg(BME280_REGISTER_CONTROL, B01111100); +} + + +void normalBME280() +{ + //write this register value to BME280 to put it to read mode + writeBME280reg(BME280_REGISTER_CONTROL, B01111111); +} + + +void writeBME280reg(uint8_t reg, uint8_t regvalue) +{ + //write a register value to the BME280 + Wire.beginTransmission((uint8_t) BME280_ADDRESS); + Wire.write((uint8_t)reg); + Wire.write((uint8_t)regvalue); + Wire.endTransmission(); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //for PCB LED + led_Flash(2, 125); + + Serial.begin(9600); + Serial.println(); + Serial.println(F("81_BME280_Test starting")); + + bme280.init(); + Serial.println(F("Initialised BME280")); + Serial.println(); + + readSensors(); //do an initial sensor read +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/88_ATMEGA328P_TEST/88_ATMEGA328P_TEST.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/88_ATMEGA328P_TEST/88_ATMEGA328P_TEST.ino new file mode 100644 index 0000000..8ebf110 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/88_ATMEGA328P_TEST/88_ATMEGA328P_TEST.ino @@ -0,0 +1,28 @@ +//see the video here > https://www.youtube.com/watch?v=eeDC1m7ANJI&t=100s +//code here > https://gist.github.com/speters/f889faec42b510052a6ab4be437d38ca + +//Purpose is to simply run a memory check on ATMEGA238P to test for counterfeit parts + +#include +#define SIGRD 5 +void setup() { + // put your setup code here, to run once: + Serial.begin(115200); + Serial.println(""); + Serial.println("boot sig dump"); + int newLineIndex = 0; + for (uint8_t i = 0; i <= 0x1F; i += 1) { + Serial.print(boot_signature_byte_get(i), HEX); + Serial.print("\t"); + newLineIndex++; + if (newLineIndex == 8) { + Serial.println(""); + newLineIndex = 0; + } + } + Serial.println(); +} + +void loop() { + +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/89_ArduinoUniqueID/89_ArduinoUniqueID.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/89_ArduinoUniqueID/89_ArduinoUniqueID.ino new file mode 100644 index 0000000..a7a2401 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/89_ArduinoUniqueID/89_ArduinoUniqueID.ino @@ -0,0 +1,26 @@ +// +// ArduinoUniqueID.ino +// +// Example shows the UniqueID on the Serial Monitor. +// + +#include //get library here > https://github.com/ricaun/ArduinoUniqueID + +void setup() +{ + Serial.begin(115200); + UniqueIDdump(Serial); + Serial.print("UniqueID: "); + for (size_t i = 0; i < UniqueIDsize; i++) + { + if (UniqueID[i] < 0x10) + Serial.print("0"); + Serial.print(UniqueID[i], HEX); + Serial.print(" "); + } + Serial.println(); +} + +void loop() +{ +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/30_I2C_Scanner/30_I2C_Scanner.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/30_I2C_Scanner/30_I2C_Scanner.ino new file mode 100644 index 0000000..79656a0 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/30_I2C_Scanner/30_I2C_Scanner.ino @@ -0,0 +1,69 @@ +// -------------------------------------- +// i2c_scanner +// from: https://playground.arduino.cc/Main/I2cScanner/ + +// This sketch tests the standard 7-bit addresses +// Devices with higher bit address might not be seen properly. + + +#include +uint16_t counter; + +void setup() +{ + Wire.begin(); + + Serial.begin(9600); + Serial.println(F("I2C Scanner starting")); + Serial.println(); +} + + +void loop() +{ + uint8_t error, address; + int16_t nDevices; + + counter++; + Serial.print(counter); + Serial.println(F(" Scanning...")); + + nDevices = 0; + for (address = 1; address < 127; address++ ) + { + // The i2c_scanner uses the return value of + // the Write.endTransmisstion to see if + // a device did acknowledge to the address. + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) + { + Serial.print(F("I2C device found at address 0x")); + if (address < 16) + Serial.print(F("0")); + Serial.println(address, HEX); + nDevices++; + } + else if (error == 4) + { + Serial.print(F("Unknown error at address 0x")); + if (address < 16) + Serial.print(F("0")); + Serial.println(address, HEX); + } + } + + if (nDevices == 0) + { + Serial.println(F("No I2C devices found")); + } + else + { + Serial.println(); + Serial.println(F("Done")); + Serial.println(); + } + + delay(5000); // wait 5 seconds for next scan +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/31_SSD1306_SH1106_OLED_Checker_ESP32/31_SSD1306_SH1106_OLED_Checker_ESP32.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/31_SSD1306_SH1106_OLED_Checker_ESP32/31_SSD1306_SH1106_OLED_Checker_ESP32.ino new file mode 100644 index 0000000..92795c1 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/31_SSD1306_SH1106_OLED_Checker_ESP32/31_SSD1306_SH1106_OLED_Checker_ESP32.ino @@ -0,0 +1,86 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a simple test program for the SSD1306 and SH1106 OLEDs. The program + prints a short message on each line, pauses, clears the screen, and starts again. + + OLED address is defined as 0x3C. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include //get library here > https://github.com/olikraus/u8g2 +//U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 +#define DEFAULTFONT u8x8_font_chroma48medium8_r //font for U8X8 Library + +#define OLED_Address 0x3C + +uint16_t writecount; +uint32_t startwritemS, endwritemS, timemS; + + +void loop() +{ + writecount++; + Serial.print(writecount); + Serial.print(F(" Writing to display")); + + disp.setI2CAddress(OLED_Address<<1); //I2C address multiplied by 2, the lowest bit must be zero + disp.setFont(DEFAULTFONT); + + startwritemS = millis(); + disp.clear(); + screen1(); + endwritemS = millis(); + timemS = endwritemS - startwritemS; + disp.setCursor(8, 4); + disp.print(timemS); + disp.print(F("mS")); + + Serial.print(F(" - done")); + Serial.print(timemS); + Serial.println(F("mS")); + + delay(2000); + Serial.println(F("PowerSave display")); + disp.setPowerSave(1); //power save display, turns off + delay(2000); + disp.setPowerSave(0); //display back to normal +} + + +void screen1() +{ + disp.setCursor(0, 0); + disp.print(F("Hello World !")); + disp.setCursor(0, 1); + disp.print(F("Line 1")); + disp.setCursor(0, 2); + disp.print(F("Line 2")); + disp.setCursor(0, 3); + disp.print(F("Line 3")); + disp.setCursor(0, 4); + disp.print(F("Line 4")); + disp.setCursor(0, 5); + disp.print(F("Line 5")); + disp.setCursor(0, 6); + disp.print(F("Line 6")); + disp.setCursor(0, 7); + disp.print(F("0123456789012345")); //display is 8 lines x 16 charaters when using the + //u8x8_font_chroma48medium8_r font +} + + +void setup() +{ + Serial.begin(9600); + Serial.println(F("31_SSD1306_SH1106_OLED_Checker_ESP32 starting")); + disp.begin(); +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/43_SD_Card_Test_ESP32/43_SD_Card_Test_ESP32.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/43_SD_Card_Test_ESP32/43_SD_Card_Test_ESP32.ino new file mode 100644 index 0000000..2b49bb1 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/43_SD_Card_Test_ESP32/43_SD_Card_Test_ESP32.ino @@ -0,0 +1,272 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 04/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This test program has been written to check that a connected SD card adapter, Micro + or standard, is functional. + + When the program runs it will attempts to create a file that is next in sequence to Log0000.txt, thus + if this is the first time the program has run on the SD card it will create file Log0001.txt. If the + file Log0001.txt exists it will create Log0002.txt etc. This ensures that everytime the program starts + it creates a new file for testing. + + Next the program checks if the Logxxxx.txt file exists and if so attempts to delete it. + + Then the program starts a loop. First the same file is again opened for append, it will be created if it + does not exist and then the line '1 Hello World' is writtent to the file. The file is closed and the + contents of the file are dumped to the serial monitor. The loop restarts, and this time the line + '2 Hello World' is appended to the file. + + As the program progresses the file will grow in size and after 4 iterrations of the open,write,close + and dump loop the file dump on serial monitor will look like this + + 1 Hello World + 2 Hello World + 3 Hello World + 4 Hello World + + This file dump will grow if you let the program run. If an error with the SD card is detected at any + time the LED will rapid flash continuously and the message 'X Card failed, or not present' is printed + to serial monitor. The number X will allow you to check the program listing for where the error occured. + + 1 Card failed = SD card did not initialise + 2 Card failed = Could nt setup logFile for new name + 3 Card failed = Could not open file for append + 4 Card failed = Failure to dump file to serial monitor + + Note: At the time of writing, 04/06/20, the function that the dumpFile() routine uses to check if an SD + card is still present, SD.exists(filename), does not work on the SD.h file included in the current Expressif + core for Arduino. If the SD card is removed, the SD.exists(filename) function returns true so the following + dump of the file locks up the ESP32. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ +#define Program_Version "V1.1" + + +#include +#include + +//pin definitions. You also need to connect the card SCK to pin 18, MISO to pin 19 and MOSI to pin 23 +#define LED1 2 //pin number for LED +#define SDCS 13 //pin number for device select on SD card module + +File logFile; + +char filename[] = "/LOG0000.TXT"; //filename used as base for creating logfile, 0000 replaced with numbers + +uint16_t linecount; + + +void loop() +{ + Serial.println(); + Serial.println("Initializing SD card"); + + if (!SD.begin(SDCS)) + { + cardFail(1); + } + + Serial.println("Card initialized"); + + logFile = SD.open("/"); + Serial.println("Card directory"); + Serial.println(); + printDirectory(logFile, 0); + Serial.println(); + Serial.println(); + + if (!setupSDLOG(filename)) //setup logfile name for writing + { + cardFail(2); + } + + Serial.print(F("logFile name is ")); + Serial.println(filename); + + if (SD.exists(filename)) + { + Serial.print(filename); + Serial.println(" exists - delete"); + SD.remove(filename); + } + + if (!SD.exists(filename)) + { + Serial.print(filename); + Serial.println(" does not exist"); + } + + while (1) + { + logFile = SD.open(filename, FILE_APPEND); + + if (!logFile) + { + cardFail(3); + } + + linecount++; + logFile.print(linecount); + logFile.println(" Hello World"); + logFile.close(); + + Serial.println(); + Serial.print("Dump file "); + Serial.println(filename); + Serial.println(); + + digitalWrite(LED1, HIGH); + + if (dumpFile(filename)) + { + Serial.println(); + Serial.println("Finished File Dump"); + } + else + { + cardFail(4); + } + + digitalWrite(LED1, LOW); + delay(2000); + } +} + + +void printDirectory(File dir, int numTabs) +{ + while (true) + { + + File entry = dir.openNextFile(); + if (! entry) + { + //no more files + break; + } + for (uint8_t i = 0; i < numTabs; i++) { + Serial.print('\t'); + } + Serial.print(entry.name()); + if (entry.isDirectory()) + { + Serial.println("/"); + printDirectory(entry, numTabs + 1); + } + else + { + //files have sizes, directories do not + Serial.print("\t\t"); + Serial.println(entry.size(), DEC); + } + entry.close(); + } +} + + +bool dumpFile(char *buf) +{ + //Note, this function will return true if the SD card is remove. See note at program start. + if (SD.exists(buf)) + { + Serial.print(buf); + Serial.println(" found"); + } + else + { + Serial.print(filename); + Serial.println(" not found"); + return false; + } + + logFile = SD.open(buf); + + if (logFile) //if the file is available, read from it + { + while (logFile.available()) + { + Serial.write(logFile.read()); + } + logFile.close(); + return true; + } + + return false; +} + + +uint8_t setupSDLOG(char *buf) +{ + //creats a new filename + + uint16_t index; + + for (index = 1; index <= 9999; index++) { + buf[4] = index / 1000 + '0'; + buf[5] = ((index % 1000) / 100) + '0'; + buf[6] = ((index % 100) / 10) + '0'; + buf[7] = index % 10 + '0' ; + if (! SD.exists(filename)) { + // only open a new file if it doesn't exist + logFile = SD.open(buf, FILE_WRITE); + break; + } + } + + if (!logFile) + { + return 0; + } + + return index; //return number of logfile created +} + + +void cardFail(uint8_t num) +{ + while (1) + { + Serial.print(num); //so we can tell where card failed + Serial.println(" Card failed, or not present"); + led_Flash(100, 25); + } +} + + +void led_Flash(unsigned int flashes, unsigned int delaymS) +{ + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //for PCB LED + led_Flash(4, 125); + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("43_SD_Card_Test Starting")); +} + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/44_SD_Card_Test_With_FS_ESP32.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/44_SD_Card_Test_With_FS_ESP32.ino new file mode 100644 index 0000000..e569dd6 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/44_SD_Card_Test_With_FS_ESP32.ino @@ -0,0 +1,110 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This test program has been written to check that a connected SD card adapter, Micro + or standard, is funtional with the FS functions. To use the program first copy the file (in this programs + directory) called testfile.txt to the root directory of the SD card. + + When the program runs it will attempt to open 'testfile.txt' and spool the contents to the Arduino IDE + serial monitor. The testfile is part of the source code for the Apollo 11 Lunar Lander navigation and + guidance computer. There are LED flashes at power up or reset, then at start of every loop of the test. + The LED is on whilst the testfile is being read. If the LED flashes very rapidly then there is a problem + accessing the SD card. + + The program also has the option of using a logic pin to control the power to the lora and SD card + devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are + powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" +#include "ESP32_LoRa_Micro_Node.h" + +#include "FS.h" +#include "SD.h" +#include "SPI.h" + + +void loop() +{ + Serial.println(F("LED Flash")); + Serial.println(); + led_Flash(2, 50); + readFile(SD, "/testfile.txt"); + delay(1000); +} + + +void readFile(fs::FS &fs, const char * path) { + Serial.printf("Reading file: %s\n", path); + + File file = fs.open(path); + if (!file) { + Serial.println(); + Serial.println("Failed to open file for reading"); + Serial.println(); + return; + } + + Serial.print("Read from file: "); + digitalWrite(LED1, HIGH); + while (file.available()) { + Serial.write(file.read()); + } + file.close(); + digitalWrite(LED1, LOW); +} + + +void led_Flash(unsigned int flashes, unsigned int delaymS) +{ + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //for PCB LED + led_Flash(4, 125); + + if (VCCPOWER >= 0) + { + pinMode(VCCPOWER, OUTPUT); //For controlling power to external devices + digitalWrite(VCCPOWER, LOW); //VCCOUT on. lora device on + } + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("44_SD_Card_Test_With_FS_ESP32 Starting")); + Serial.print("Initializing SD card..."); + + if (!SD.begin(SDCS)) { + Serial.println("Card failed, or not present."); + led_Flash(100, 25); + return; //loop if no card found + } + + Serial.println("Card initialized."); +} + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/ESP32_LoRa_Micro_Node.h b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/ESP32_LoRa_Micro_Node.h new file mode 100644 index 0000000..06e4e0f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/ESP32_LoRa_Micro_Node.h @@ -0,0 +1,21 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of the Tracker boards, the ESP32_Micro_Node, be sure to change +//them to match your own setup. You will also need to connect up the pins for the SPI bus, which on the +//ESP32_Micro_Node are SCK on pin 13, MISO on pin 19 and MOSI on pin 23. Some pins such as DIO1, DIO2 and +//BUZZER may not be in used by this sketch so they do not need to be connected and should be set to -1. + +#define SWITCH1 0 //pin number to attach switch +#define LED1 2 //pin number for LED +#define SDCS 13 //ESP32 pin number for device select on SD card module +#define VCCPOWER 14 //pin controls power to external devices, such as the SD card + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/testfile.txt b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/testfile.txt new file mode 100644 index 0000000..3f10610 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/44_SD_Card_Test_With_FS_ESP32/testfile.txt @@ -0,0 +1,1062 @@ +# Copyright: Public domain. +# Filename: BURN_BABY_BURN--MASTER_IGNITION_ROUTINE.agc +# Purpose: Part of the source code for Luminary 1A build 099. +# It is part of the source code for the Lunar Module's (LM) +# Apollo Guidance Computer (AGC), for Apollo 11. +# Assembler: yaYUL +# Contact: Ron Burkey . +# Website: www.ibiblio.org/apollo. +# Pages: 731-751 +# Mod history: 2009-05-19 RSB Adapted from the corresponding +# Luminary131 file, using page +# images from Luminary 1A. +# 2009-06-07 RSB Corrected 3 typos. +# 2009-07-23 RSB Added Onno's notes on the naming +# of this function, which he got from +# Don Eyles. +# +# This source code has been transcribed or otherwise adapted from +# digitized images of a hardcopy from the MIT Museum. The digitization +# was performed by Paul Fjeld, and arranged for by Deborah Douglas of +# the Museum. Many thanks to both. The images (with suitable reduction +# in storage size and consequent reduction in image quality as well) are +# available online at www.ibiblio.org/apollo. If for some reason you +# find that the images are illegible, contact me at info@sandroid.org +# about getting access to the (much) higher-quality images which Paul +# actually created. +# +# Notations on the hardcopy document read, in part: +# +# Assemble revision 001 of AGC program LMY99 by NASA 2021112-61 +# 16:27 JULY 14, 1969 + +# Page 731 +## At the get-together of the AGC developers celebrating the 40th anniversary +## of the first moonwalk, Don Eyles (one of the authors of this routine along +## with Peter Adler) has related to us a little interesting history behind the +## naming of the routine. +## +## It traces back to 1965 and the Los Angeles riots, and was inspired +## by disc jockey extraordinaire and radio station owner Magnificent Montague. +## Magnificent Montague used the phrase "Burn, baby! BURN!" when spinning the +## hottest new records. Magnificent Montague was the charismatic voice of +## soul music in Chicago, New York, and Los Angeles from the mid-1950s to +## the mid-1960s. +# BURN, BABY, BURN -- MASTER IGNITION ROUTINE + + BANK 36 + SETLOC P40S + BANK + EBANK= WHICH + COUNT* $$/P40 + +# THE MASTER IGNITION ROUTINE IS DESIGNED FOR USE BY THE FOLLOWING LEM PROGRAMS: P12, P40, P42, P61, P63. +# IT PERFORMS ALL FUNCTIONS IMMEDIATELY ASSOCIATED WITH APS OR DPS IGNITION: IN PARTICULAR, EVERYTHING LYING +# BETWEEN THE PRE-IGNITION TIME CHECK -- ARE WE WITHIN 45 SECONDS OF TIG? -- AND TIG + 26 SECONDS, WHEN DPS +# PROGRAMS THROTTLE UP. +# +# VARIATIONS AMONG PROGRAMS ARE ACCOMODATED BY MEANS OF TABLES CONTAINING CONSTANTS (FOR AVEGEXIT, FOR +# WAITLIST, FOR PINBALL) AND TCF INSTRUCTIONS. USERS PLACE THE ADRES OF THE HEAD OF THE APPROPRIATE TABLE +# (OF P61TABLE FOR P61LM, FOR EXAMPLE) IN ERASABLE REGISTER `WHICH' (E4). THE IGNITION ROUTINE THEN INDEXES BY +# WHICH TO OBTAIN OR EXECUTE THE PROPER TABLE ENTRY. THE IGNITION ROUTINE IS INITIATED BY A TCF BURNBABY, +# THROUGH BANKJUMP IF NECESSARY. THERE IS NO RETURN. +# +# THE MASTER IGNITION ROUTINE WAS CONCEIVED AND EXECUTED, AND (NOTA BENE) IS MAINTAINED BY ADLER AND EYLES. +# +# HONI SOIT QUI MAL Y PENSE +# +# *********************************************** +# TABLES FOR THE IGNITION ROUTINE +# *********************************************** +# +# NOLI SE TANGERE + +P12TABLE VN 0674 # (0) + TCF ULLGNOT # (1) + TCF COMFAIL3 # (2) + TCF GOCUTOFF # (3) + TCF TASKOVER # (4) + TCF P12SPOT # (5) + DEC 0 # (6) NO ULLAGE + EBANK= WHICH + 2CADR SERVEXIT # (7) + + TCF DISPCHNG # (11) + TCF WAITABIT # (12) + TCF P12IGN # (13) + +P40TABLE VN 0640 # (0) + TCF ULLGNOT # (1) + TCF COMFAIL4 # (2) + TCF GOPOST # (3) + TCF TASKOVER # (4) + TCF P40SPOT # (5) +# Page 732 + DEC 2240 # (6) + EBANK= OMEGAQ + 2CADR STEERING # (7) + + TCF P40SJUNK # (11) + TCF WAITABIT # (12) + TCF P40IGN # (13) + TCF REP40ALM # (14) + +P41TABLE TCF P41SPOT # (5) + DEC -1 # (6) + EBANK= OMEGAQ + 2CADR CALCN85 # (7) + + TCF COMMON # (11) + TCF TIGTASK # (12) + +P42TABLE VN 0640 # (0) + TCF WANTAPS # (1) + TCF COMFAIL4 # (2) + TCF GOPOST # (3) + TCF TASKOVER # (4) + TCF P42SPOT # (5) + DEC 2640 # (6) + EBANK= OMEGAQ + 2CADR STEERING # (7) + + TCF P40SJUNK # (11) + TCF WAITABIT # (12) + TCF P42IGN # (13) + TCF P42STAGE # (14) + +P63TABLE VN 0662 # (0) + TCF ULLGNOT # (1) + TCF COMFAIL3 # (2) + TCF V99RECYC # (3) + TCF TASKOVER # (4) + TCF P63SPOT # (5) + DEC 2240 # (6) + EBANK= WHICH + 2CADR SERVEXIT # (7) + + TCF DISPCHNG # (11) + TCF WAITABIT # (12) +# Page 733 + TCF P63IGN # (13) + +ABRTABLE VN 0663 # (0) + TCF ULLGNOT # (1) + TCF COMFAIL3 # (2) + TCF GOCUTOFF # (3) + TCF TASKOVER # (4) + NOOP # (5) + NOOP # (6) + NOOP # (7) + NOOP + TCF DISPCHNG # (11) + TCF WAITABIT # (12) + TCF ABRTIGN # (13) + +# ********************************* +# GENERAL PURPOSE IGNITION ROUTINES +# ********************************* + +BURNBABY TC PHASCHNG # GROUP 4 RESTARTS HERE + OCT 04024 + + CAF ZERO # EXTIRPATE JUNK LEFT IN DVTOTAL + TS DVTOTAL + TS DVTOTAL +1 + + TC BANKCALL # P40AUTO MUST BE BANKCALLED EVEN FROM ITS + CADR P40AUTO # OWN BANK TO SET UP RETURN PROPERLY + +B*RNB*B* EXTEND + DCA TIG # STORE NOMINAL TIG FOR OBLATENESS COMP. + DXCH GOBLTIME # AND FOR P70 OR P71. + + INHINT + TC IBNKCALL + CADR ENGINOF3 + RELINT + + INDEX WHICH + TCF 5 + +P42SPOT = P40SPOT # (5) +P12SPOT = P40SPOT # (5) +P63SPOT = P41SPOT # (5) IN P63 CLOKTASK ALREADY GOING +P40SPOT CS CNTDNDEX # (5) +# Page 734 + TC BANKCALL # MUST BE BANKCALLED FOR GENERALIZED + CADR STCLOK2 # RETURN +P41SPOT TC INTPRET # (5) + DLOAD DSU + TIG + D29.9SEC + STCALL TDEC1 + INITCDUW + BOFF CALL + MUNFLAG + GOMIDAV + CSMPREC + VLOAD MXV + VATT1 + REFSMMAT + VSR1 + STOVL V(CSM) # CSM VELOCITY -- M/CS*2(7) + RATT1 + VSL4 MXV + REFSMMAT + STCALL R(CSM) # CSM POSITION -- M*2(24) + MUNGRAV + STODL G(CSM) # CSM GRAVITY VEC. -- M/CS*2(7) + TAT + STORE TDEC1 # RELOAD TDEC1 FOR MIDTOAV. +GOMIDAV CALRB + MIDTOAV1 + TCF CALLT-35 # MADE IT IN TIME. + + EXTEND # TIG WAS SLIPPED, SO RESET TIG TO 29.9 + DCA PIPTIME1 # SECONDS AFTER THE TIME TO WHICH WE DID + DXCH TIG # INTEGRATE. + EXTEND + DCA D29.9SEC + DAS TIG + +CALLT-35 DXCH MPAC + DXCH SAVET-30 # DELTA-T UNTIL TIG-30 + EXTEND + DCS 5SECDP + DAS SAVET-30 # DELTA-T UNTIL TIG-35 + EXTEND + DCA SAVET-30 + TC LONGCALL + EBANK= TTOGO + 2CADR TIG-35 + + TC PHASCHNG + OCT 20254 # 4.25SPOT FOR TIG-35 RESTART. +# Page 735 + TC CHECKMM + DEC 63 + TCF ENDOFJOB # NOT P63 + CS CNTDNDEX # P63 CAN START DISPLAYING NOW. + TS DISPDEX + TC INTPRET + VLOAD ABVAL + VN1 + STORE ABVEL # INITIALIZE ABVEL FOR P63 DISPLAY + EXIT + TCF ENDOFJOB + +# ******************************** + +TIG-35 CAF 5SEC + TC TWIDDLE + ADRES TIG-30 + + TC PHASCHNG + OCT 40154 # 4.15SPOT FOR TIG-30 RESTART + + CS BLANKDEX # BLANK DSKY FOR 5 SECONDS + TS DISPDEX + + INDEX WHICH + CS 6 # CHECK ULLAGE TIME. + EXTEND + BZMF TASKOVER + CAF 4.9SEC # SET UP TASK TO RESTORE DISPLAY AT TIG-30 + TC TWIDDLE + ADRES TIG-30.1 + + CAF PRIO17 # A NEGATIVE ULLAGE TIME INDICATES P41, IN + TC NOVAC # WHICH CASE WE HAVE TO SET UP A JOB TO + EBANK= TTOGO # BLANK THE DSKY FOR FIVE SECONDS, SINCE + 2CADR P41BLANK # CLOKJOB IS NOT RUNNING DURING P41. + + TCF TASKOVER + +P41BLANK TC BANKCALL # BLANK DSKY. + CADR CLEANDSP + TCF ENDOFJOB + +TIG-30.1 CAF PRIO17 # SET UP JOB TO RESTORE DISPLAY AT TIG-30 + TC NOVAC + EBANK= TTOGO + 2CADR TIG-30A + + TCF TASKOVER +# Page 736 +TIG-30A CAF V16N85B + TC BANKCALL # RESTORE DISPLAY. + CADR REGODSP # REGODSP DOES A TCF ENDOFJOB + +# ******************************** + +TIG-30 CAF S24.9SEC + TC TWIDDLE + ADRES TIG-5 + + CS CNTDNDEX # START UP CLOKTASK AGAIN + TS DISPDEX + + INDEX WHICH # PICK UP APPROPRIATE ULLAGE -- ON TIME + CA 6 # Was CAF --- RSB 2009. + EXTEND + BZMF ULLGNOT # DON'T SET UP ULLAGE IF DT IS NEG OR ZERO + TS SAVET-30 # SAVE DELTA-T FOR RESTART + TC TWIDDLE + ADRES ULLGTASK + + CA THREE # RESTART PROTECT ULLGTASK (1.3SPOT) + TS L + CS THREE + DXCH -PHASE1 + CS TIME1 + TS TBASE1 + + INDEX WHICH + TCF 1 + +WANTAPS CS FLGWRD10 # (1) FOR P42 ENSURE APSFLAG IS SET. IF IT + MASK APSFLBIT # WASN'T SET, DAP WILL BE INITIALIZED TO + ADS FLGWRD10 # ASCENT VALUES BY 1/ACCS IN 2 SECONDS. + +ULLGNOT EXTEND # (1) + INDEX WHICH + DCA 7 # LOAD AVEGEXIT WITH APPROPRIATE 2CADR + DXCH AVEGEXIT + + CAF TWO # 4.2SPOT RESTARTS IMMEDIATELY AT REDO4.2 + TS L + CS TWO # AND ALSO AT TIG-5 AT THE CORRECT TIME. + DXCH -PHASE4 + + CS TIME1 + TS TBASE4 # SET TBASE4 FOR TIG-5 RESTART + +REDO2.17 EXTEND +# Page 737 + DCA NEG0 # CLEAR OUT GROUP 2 SO LAMBERT CAN START + DXCH -PHASE2 # IF NEEDED. + +REDO4.2 CCS PHASE5 # IF SERVICER GOING? + TCF TASKOVER # YES, DON'T START IT UP AGAIN. + + TC POSTJUMP + CADR PREREAD # PREREAD END THIS TASK + +# ********************************* + +ULLGTASK TC ONULLAGE # THIS COMES AT TIG-7.5 OR TIG-3.5 + TC PHASCHNG + OCT 1 + TCF TASKOVER + +# ********************************* + +TIG-5 EXTEND + DCA NEG0 # INSURE THAT GROUP 3 IS INACTIVE. + DXCH -PHASE3 + + CAF 5SEC + TC TWIDDLE + ADRES TIG-0 + + TC DOWNFLAG # RESET IGNFLAG AND ASINFLAG + ADRES IGNFLAG # FOR LIGHT-UP LOGIC. + TC DOWNFLAG + ADRES ASTNFLAG + + INDEX WHICH + TCF 11 + +P40SJUNK CCS PHASE3 # (11) P40 AND P42. S40.13 IN PROGRESS? + TCF DISPCHNG # YES + + CAF PRIO20 + TC FINDVAC + EBANK= TTOGO + 2CADR S40.13 + + TC PHASCHNG # 3.5SPOT FOR S40.13 + OCT 00053 +DISPCHNG CS VB99DEX # (11) + TS DISPDEX + +# Page 738 +COMMON TC PHASCHNG # RESTART TIG-0 (4.7SPOT) + OCT 40074 + TCF TASKOVER + +# ********************************* + +TIG-0 CS FLAGWRD7 # SET IGNFLAG SINCE TIG HAS ARRIVED + MASK IGNFLBIT + ADS FLAGWRD7 + + TC CHECKMM # IN P63 CASE, THROTTLE-UP IS ZOOMTIME + DEC 63 # AFTER NOMINAL IGNITION, NOT ACTUAL + TCF IGNYET? + CA ZOOMTIME + TC WAITLIST + EBANK= DVCNTR + 2CADR P63ZOOM + + TC 2PHSCHNG + OCT 40033 + + OCT 05014 + OCT 77777 + +IGNYET? CAF ASTNBIT # CHECK ASTNFLAG: HAS ASTRONAUT RESPONDED + MASK FLAGWRD7 # TO OUR ENGINE ENABLE REQUEST? + EXTEND + INDEX WHICH + BZF 12 # BRANCH IF HE HAS NOT RESPONDED YET + +IGNITION CS FLAGWRD5 # INSURE ENGONFLG IS SET. + MASK ENGONBIT + ADS FLAGWRD5 + CS PRIO30 # TURN ON THE ENGINE. + EXTEND + RAND DSALMOUT + AD BIT13 + EXTEND + WRITE DSALMOUT + EXTEND # SET TEVENT FOR DOWNLINK + DCA TIME2 + DXCH TEVENT + + EXTEND # UPDATE TIG USING TGO FROM S40.13 + DCA TGO + DXCH TIG + EXTEND + DCA TIME2 + DAS TIG + +# Page 739 + CS FLUNDBIT # PERMIT GUIDANCE LOOP DISPLAYS + MASK FLAGWRD8 + TS FLAGWRD8 + + INDEX WHICH + TCF 13 + +P63IGN EXTEND # (13) INITIATE BURN DISPLAYS + DCA DSP2CADR + DXCH AVGEXIT + + CA Z # ASSASSINATE CLOKTASK + TS DISPDEX + + CS FLAGWRD9 # SET FLAG FOR P70-P71 + MASK LETABBIT + ADS FLAGWRD9 + + CS FLAGWRD7 # SET SWANDISP TO ENABLE R10. + MASK SWANDBIT + ADS FLAGWRD7 + + CS PULSES # MAKE SURE DAP IS NOT IN MINIMUM-IMPULSE + MASK DAPBOOLS # MODE, IN CASE OF SWITCH TO P66 + TS DAPBOOLS + + EXTEND # INITIALIZE TIG FOR P70 AND P71. + DCA TIME2 + DXCH TIG + + CAF ZERO # INITIALIZE WCHPHASE, AND FLPASS0 + TS WCHPHASE + TS WCHPHOLD # ALSO WHCPHOLD + CA TWO + TS FLPASS0 + + TCF P42IGN +P40IGN CS FLAGWRD5 # (13) + MASK NOTHRBIT + EXTEND + BZF P42IGN + CA ZOOMTIME + TC WAITLIST + EBANK= DVCNTR + 2CADR P40ZOOM + +P63IGN1 TC 2PHSCHNG + OCT 40033 # 3.3SPOT FOR ZOOM RESTART. + OCT 05014 # TYPE C RESTARTS HERE IMMEDIATELY + OCT 77777 + +# Page 740 + TCF P42IGN +P12IGN CAF EBANK6 + TS EBANK + EBANK= AOSQ + + CA IGNAOSQ # INITIALIZE DAP BIAS ACCELERATION + TS AOSQ # ESTIMATES AT P12 IGNITION. + CA IGNAOSR + TS AOSR + + CAF EBANK7 + TS EBANK + EBANK= DVCNTR + +ABRTIGN CA Z # (13) KILL CLOKTASK + TS DISPDEX + + EXTEND # CONNECT ASCENT GYIDANCE TO SERVICER. + DCA ATMAGADR + DXCH AVGEXIT + + CS FLAGWRD7 # ENABLE R10. + MASK SWANDBIT + ADS FLAGWRD7 + +P42IGN CS DRIFTBIT # ENSURE THAT POWERED-FLIGHT SWITCHING + MASK DAPBOOLS # CURVES ARE USED. + TS DAPBOOLS + CAF IMPULBIT # EXAMINE IMPULSE SWITCH + MASK FLAGWRD2 + CCS A + TCF IMPLBURN + +DVMONCON TC DOWNFLAG + ADRES IGNFLAG # CONNECT DVMON + TC DOWNFLAG + ADRES ASTNFLAG + TC DOWNFLAG + ADRES IDLEFLAG + + TC PHASCHNG + OCT 40054 + + TC FIXDELAY # TURN ULLAGE OFF HALF A SECOND AFTER + DEC 50 # LIGHT UP. + +ULLAGOFF TC NOULLAGE + +WAITABIT EXTEND # KILL GROUP 4 + DCA NEG0 +# Page 741 + DXCH -PHASE4 + + TCF TASKOVER + +TIGTASK TC POSTJUMP # (12) + CADR TIGTASK1 + +# ******************************** + + BANK 31 + SETLOC P40S3 + BANK + COUNT* $$/P40 + +TIGTASK1 CAF PRIO16 + TC NOVAC + EBANK= TRKMKCNT + 2CADR TIGNOW + + TC PHASCHNG + OCT 6 # KILL GROUP 6 + + TCF TASKOVER + +# ******************************** + +P63ZOOM EXTEND + DCA LUNLANAD + DXCH AVEGEXIT + + TC IBNKCALL + CADR FLATOUT + TCF P40ZOOMA + +P40ZOOM CAF BIT13 + TS THRUST + CAF BIT4 + + EXTEND + WOR CHAN14 + +P40ZOOMA TC PHASCHNG + OCT 3 + TCF TASKOVER + + EBANK= DVCNTR +LUNLANAD 2CADR LUNLAND + +# Page 742 +ZOOM = P40ZOOMA + BANK 36 + SETLOC P40S + BANK + COUNT* $$/P40 + +# ******************************** + +COMFAIL TC UPFLAG # (15) + ADRES IDLEFLAG + TC UPFLAG # SET FLAG TO SUPPRESS CONFLICTING DISPLAY + ADRES FLUNDISP + CAF FOUR # RESET DVMON + TS DVCNTR + CCS PHASE6 # CLOCKTASK ACTIVE? + TCF +3 # YES + TC BANKCALL # OTHERWISE, START IT UP + CADR STCLOK1 + +3 CS VB97DEX + TS DISPDEX + TC PHASCHNG # TURN OFF GROUP 4. + OCT 00004 + TCF ENDOFJOB + +COMFAIL1 INDEX WHICH + TCF 2 + +COMFAIL3 CA Z # (15) KILL CLOKTASK USING Z + TCF +2 + +COMFAIL4 CS CNTDNDEX + TS DISPDEX + + TC DOWNFLAG # RECONNECT DV MONITOR + ADRES IDLEFLAG + TC DOWNFLAG # PERMIT GUIDANCE LOOP DISPLAYS + ADRES FLUNDISP + TCF ENDOFJOB + +COMFAIL2 TC PHASCHNG # KILL ZOOM RESTART PROTECTION + OCT 00003 + + INHINT + TC KILLTASK # KILL ZOOM IN CASE IT'S STILL TO COME + CADR ZOOM + TC IBNKCALL # COMMAND ENGINE OFF + CADR ENGINOF4 + TC UPFLAG # SET THE DRIFT BIT FOR THE DAP. + ADRES DRIFTDFL +# Page 743 + TC INVFLAG # USE OTHER RCS SYSTEM + ADRES AORBTFLG + TC UPFLAG # TURN ON ULLAGE + ADRES ULLAGFLG + CAF BIT1 + INHINT + TC TWIDDLE + ADRES TIG-5 + TCF ENDOFJOB + +# *********************************** +# SUBROUTINES OF THE IGNITION ROUTINE +# *********************************** + +INVFLAG CA Q + TC DEBIT + COM + EXTEND + RXOR LCHAN + TCF COMFLAG + +# *********************************** + +NOULLAGE CS ULLAGER # MUST BE CALLED IN A TASK OR UNDER INHINT + MASK DAPBOOLS + TS DAPBOOLS + TC Q + +# *********************************** + +ONULLAGE CS DAPBOOLS # TURN ON ULLAGE. MUST BE CALLED IN + MASK ULLAGER # A TASK OR WHILE INHINTED. + ADS DAPBOOLS + TC Q + +# *********************************** + +STCLOK1 CA ZERO # THIS ROUTINE STARTS THE COUNT-DOWN +STCLOK2 TS DISPDEX # (CLOKTASK AND CLOKJOB). SETTING +STCLOK3 TC MAKECADR # SETTING DISPDEX POSITIVE KILLS IT. + TS TBASE4 # RETURN SAVE (NOT FOR RESTARTS). + EXTEND + DCA TIG + DXCH MPAC + EXTEND + DCS TIME2 +# Page 744 + DAS MPAC # HAVE TIG -- TIME2, UNDOUBTEDLY A + NUMBER + TC TPAGREE # POSITIVE, SINCE WE PASSED THE + CAF 1SEC # 45 SECOND CHECK. + TS Q + DXCH MPAC + MASK LOW5 # RESTRICT MAGNITUDE OF NUMBER IN A + EXTEND + DV Q + CA L # GET REMAINDER + AD TWO + INHINT + TC TWIDDLE + ADRES CLOKTASK + TC 2PHSCHNG + OCT 40036 # 6.3SPOT FOR CLOKTASK + OCT 05024 + OCT 13000 + + CA TBASE4 + TC BANKJUMP + +CLOKTASK CS TIME1 # SET TBASE6 FOR GROUP 6 RESTART + TS TBASE6 + + CCS DISPDEX + TCF KILLCLOK + NOOP + CAF PRIO27 + TC NOVAC + EBANK= TTOGO + 2CADR CLOKJOB + + TC FIXDELAY # WAIT A SECOND BEFORE STARTING OVER + DEC 100 + TCF CLOKTASK + +KILLCLOK EXTEND # KILL RESTART + DCA NEG0 + DXCH -PHASE6 + TCF TASKOVER + +CLOKJOB EXTEND + DCS TIG + DXCH TTOGO + EXTEND +# Page 745 + DCA TIME2 + DAS TTOGO + INHINT + CCS DISPDEX # IF DISPDEX HAS BEEN SET POSITIVE BY A + TCF ENDOFJOB # TASK OR A HIGHER PRIORITY JOB SINCE THE + TCF ENDOFJOB # LAST CLOKTASK, AVOID USING IT AS AN + COM # INDEX. + RELINT # ***** DISPDEX MUST NEVER B -0 ***** + INDEX A + TCF DISPNOT -1 # (-1 DUE TO EFFECT OF CCS) + +VB97DEX = OCT35 # NEGATIVE OF THIS IS PROPER FOR DISPDEX + + -35 CS ZERO # INDICATE VERB 97 PASTE + TS NVWORD1 + CA NVWORD +2 # NVWORD+2 CONTAINS V06 & APPROPRIATE NOUN + TC BANKCALL + CADR CLOCPLAY + TCF STOPCLOK # TERMINATE CLOKTASK ON THE WAY TO P00H + TCF COMFAIL1 + TCF COMFAIL2 + + # THIS DISPLAY IS CALLED VIA ASTNCLOK + -25 CAF V06N61 # IT IS PRIMARILY USED BY THE CREW IN P63 + TC BANKCALL # TO RESET HIS EVENT TIMER TO AGREE WITH + CADR REFLASH # TIG. + TCF STOPCLOK + TCF ASTNRETN + TCF -6 + +CNTDNDEX = LOW4 # OCT17: NEGATIVE PROPER FOR DISPDEX + + -17 INDEX WHICH # THIS DISPLAY COMES UP AT ONE SECOND + # Was CAF --- RSB 2009 + CA 0 # INTERVALS. IT IS NORMALLY OPERATED + TC BANKCALL # BETWEEN TIG-30 SECONDS AND TIG-5 SECONDS + CADR REGODSP # REGODSP DOES ITS OWN TCF ENDOFJOB + +VB99DEX = ELEVEN # OCT13: NEGATIVE PROPER FOR DISPDEX + +V99RECYC EQUALS + + -13 CS BIT9 # INDICATE VERB 99 PASTE + TS NVWORD1 + INDEX WHICH # THIS IS THE "PLEASE ENABLE ENGINE" + # Was CAF --- RSB 2004 + CA 0 # DISPLAY; IT IS INITIATED AT TIG-5 SEC. + TC BANKCALL # THE DISPLAY IS A V99NXX, WHERE XX IS + CADR CLOCPLAY # NOUN THAT HAD PREVIOUSLY BEEN DISPLAYED + TCF STOPCLOK # TERMINATE GOTOP00H TURNS OFF ULLAGE. + TCF *PROCEED + TCF *ENTER + +# Page 746 +BLANKDEX = TWO # NEGATIVE OF THIS IS PROPER FOR DISPDEX + + -2 TC BANKCALL # BLANK DSKY. THE DSKY IS BLANKED FOR + CADR CLEANDSP # 5 SECONDS AT TIG-35 TO INDICATE THAT +DISPNOT TCF ENDOFJOB # AVERAGE G IS STARTING. + +STOPCLOK TC NULLCLOK # STOP CLOKTASK & TURN OFF ULLAGE ON THE + TCF GOTOP00H # WAY TO P00 (GOTOP00H RELINTS) + +NULLCLOK INHINT + EXTEND + QXCH P40/RET + TC NOULLAGE # TURN OFF ULLAGE ... + TC KILLTASK # DON'T LET IT COME ON, EITHER ... + CADR ULLGTASK + TC PHASCHNG # NOT EVEN IF THERE'S A RESTART. + OCT 1 + CA Z # KILL CLOKTASK + TS DISPDEX + TC P40/RET + +ASTNRETN TC PHASCHNG + OCT 04024 + CAF ZERO # STOP DISPLAYING BUT KEEP RUNNING + TS DISPDEX + CAF PRIO13 + TC FINDVAC + EBANK= STARIND + 2CADR ASTNRET + + TCF ENDOFJOB + +*PROCEED TC UPFLAG + ADRES ASTNFLAG + + TCF IGNITE + +*ENTER INHINT + INDEX WHICH + TCF 3 + +GOPOST CAF PRIO12 # (3) MUST BE LOWER PRIORITY THAN CLOKJOB + TC FINDVAC + EBANK= TTOGO + 2CADR POSTBURN + +# Page 747 + INHINT # SET UP THE DAP FOR COASTING FLIGHT. + TC IBNKCALL + CADR ALLCOAST + TC NULLCLOK + TC PHASCHNG # 4.13 RESTART FOR POSTBURN + OCT 00134 + + TCF ENDOFJOB + +GOCUTOFF CAF PRIO17 # (3) + TC FINDVAC + EBANK= TGO + 2CADR CUTOFF + + TC DOWNFLAG + ADRES FLUNDISP + + INHINT # SET UP THE DAP FOR COASTING FLIGHT. + TC IBNKCALL + CADR ALLCOAST + TC NULLCLOK + TC PHASCHNG + OCT 07024 + OCT 17000 + EBANK= TGO + 2CADR CUTOFF + + TCF ENDOFJOB + +IGNITE CS FLAGWRD7 # (2) + MASK IGNFLBIT + CCS A + TCF IGNITE1 + CAF BIT1 + INHINT + TC TWIDDLE + ADRES IGNITION + + CAF OCT23 # IMMEDIATE RESTART AT IGNITION + TS L + COM + DXCH -PHASE4 + +IGNITE1 CS CNTDNDEX # RESTORE OLD DISPLAY. + TS DISPDEX + + TCF ENDOFJOB + +# Page 748 +# ******************************** + +P40ALM TC ALARM # PROGRAM SELECTION NOT CONSISTENT WITH + OCT 1706 # VEHICLE CONFIGURATION + +REP40ALM CAF V05N09 # (14) + TC BANKCALL + CADR GOFLASH + + TCF GOTOP00H # V34E TERMINATE + TCF +2 # PROCEED CHECK FOR P42 + TCF REP40ALM # V32E REDISPLAY ALARM + + INDEX WHICH # FOR P42, ALLOW CREW TO PROCEED EVEN + TCF 14 # THOUGH VEHICLE IS UNSTAGED. + +# ******************************** + + BANK 31 + SETLOC P40S2 + BANK + + COUNT* $$/P40 + +P40AUTO TC MAKECADR # HELLO THERE. + TS TEMPR60 # FOR GENERALIZED RETURN TO OTHER BANKS. +P40A/P TC BANKCALL # SUBROUTINE TO CHECK PGNCS CONTROL + CADR G+N,AUTO # AND AUTO STABILIZATION MODES + CCS A # +0 INDICATES IN PGNCS, IN AUTO + TCF TURNITON # + INDICATES NOT IN PGNCS AND/OR AUTO + CAF APSFLBIT # ARE WE ON THE DESCENT STAGE? + MASK FLGWRD10 + CCS A + TCF GOBACK # RETURN + CAF BIT5 # YES, CHECK FOR AUTO-THROTTLE MODE + EXTEND + RAND CHAN30 + EXTEND + BZF GOBACK # IN AUTO-THROTTLE MODE -- RETURN +TURNITON CAF P40A/PMD # DISPLAYS V50N25 R1=203 PLEASE PERFORM + TC BANKCALL # CHECKLIST 203 TURN ON PGNCS ETC. + CADR GOPERF1 + TCF GOTOP00H # V34E TERMINATE + TCF P40A/P # RECYCLE +GOBACK CA TEMPR60 + TC BANKJUMP # GOODBYE. COME AGAIN SOON. + +P40A/PMD OCT 00203 + +# Page 749 + BANK 36 + SETLOC P40S + BANK + + COUNT* $$/P40 + +# ********************************** +# CONSTANTS FOR THE IGNITION ROUTINE +# ********************************** + +SERVCADR = P63TABLE +7 + +P40ADRES ADRES P40TABLE + +P41ADRES ADRES P41TABLE -5 + +P42ADRES ADRES P42TABLE + + EBANK= DVCNTR +DSP2CADR 2CADR P63DISPS -2 + + EBANK= DVCNTR +ATMAGADR 2CADR ATMAG + +? = GOTOP00H + +D29.9SEC 2DEC 2990 + +S24.9SEC DEC 2490 + +4.9SEC DEC 490 + +OCT20 = BIT5 + +V06N61 VN 0661 + +# Page 750 +# KILLTASK +# MOD NO: NEW PROGRAM +# MOD BY: COVELLI +# +# FUNCTIONAL DESCRIPTION: +# +# KILLTASK IS USED TO REMOVE A TASK FROM THE WAITLIST BY SUBSTITUTING A NULL TASK CALLED `NULLTASK' (OF COURSE), +# WHICH MERELY DOES A TC TASKOVER. IF THE SAME TASK IS SCHEDULED MORE THAN ONCE, ONLY THE ONE WHICH WILL OCCUR +# FIRST IS REMOVED. IF THE TASK IS NOT SCHEDULED, KILLTASK TAKES NO ACTION AND RETURNS WITH NO ALARM. KILLTASK +# LEAVES INTERRUPTS INHIBITED SO CALLER MUST RELINT +# +# CALLING SEQUENCE +# L TC KILLTASK # IN FIXED-FIXED +# L+1 CADR ???????? # CADR (NOT 2CADR) OF TASK TO BE REMOVED. +# L+2 (RELINT) # RETURN +# +# EXIT MODE: AT L+2 OF CALLING SEQUENCE. +# +# ERASABLE INITIALIZATION: NONE. +# +# OUTPUT: 2CADR OF NULLTASK IN LST2 +# +# DEBRIS: ITEMP1 - ITEMP4, A, L, Q. + + EBANK= LST2 + BLOCK 3 # KILLTASK MUST BE IN FIXED-FIXED. + SETLOC FFTAG6 + BANK + COUNT* $$/KILL +KILLTASK CA KILLBB + INHINT + LXCH A + INDEX Q + CA 0 # GET CADR. + LXCH BBANK + TCF KILLTSK2 # CONTINUE IN SWITCHED FIXED. + + EBANK= LST2 +KILLBB BBCON KILLTSK2 + + BANK 27 + + SETLOC P40S1 + BANK + COUNT* $$/KILL + +KILLTSK2 LXCH ITEMP2 # SAVE CALLER'S BBANK +# Page 751 + INCR Q + EXTEND + QXCH ITEMP1 # RETURN 2ADR IN ITEMP1,ITEMP2 + + TS ITEMP3 # CADR IS IN A + MASK LOW10 + AD BIT11 + TS ITEMP4 # GENADR OF TASK + + CS LOW10 + MASK ITEMP3 + TS ITEMP3 # FBANK OF TASK + + ZL +ADRSCAN INDEX L + CS LST2 + AD ITEMP4 # COMPARE GENADRS + EXTEND + BZF TSTFBANK # IF THEY MATCH, COMPARE FBANKS +LETITLIV CS LSTLIM + AD L + EXTEND # ARE WE DONE? + BZF DEAD # YES -- DONE, SO RETURN + INCR L + INCR L + TCF ADRSCAN # CONTINUE LOOP. + +DEAD DXCH ITEMP1 + DTCB + +TSTFBANK CS LOW10 + INDEX L + MASK LST2 +1 # COMPARE FBANKS ONLY. + EXTEND + SU ITEMP3 + EXTEND + BZF KILLDEAD # MATCH -- KILL IT. + TCF LETITLIV # NO MATCH -- CONTINUE. + +KILLDEAD CA TCTSKOVR + INDEX L + TS LST2 # REMOVE TASK BY INSERTING TASKOVER + TCF DEAD + +LSTLIM EQUALS BIT5 # DEC 16 + + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/45_Battery_Voltage_Read_Test_ESP32/45_Battery_Voltage_Read_Test_ESP32.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/45_Battery_Voltage_Read_Test_ESP32/45_Battery_Voltage_Read_Test_ESP32.ino new file mode 100644 index 0000000..edeb8e0 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/45_Battery_Voltage_Read_Test_ESP32/45_Battery_Voltage_Read_Test_ESP32.ino @@ -0,0 +1,111 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This test program has been written to check that hardware for reading the battery + voltage has been assembled correctly such that it is funtional. The value defined as 'ADMultiplier' + in settings.h is used to adjust the value read from the 91K\11K resistor divider and convert into mV. + + There is also an option of using a logic pin to turn the resistor divider used to read battery voltage + on and off. This reduces current used in sleep mode. To use the feature set the define for pin BATVREADON + in 'Settings.h' to the pin used. If not using the feature set the pin number to -1. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + + +#define ADMultiplier 10.872 //adjustment to convert AD value read into mV of battery voltage +#define BATVREADON 25 //used to turn on the resistor divider to measure voltage //this pin turns on the MOSFET that switches in the resistor divider +#define LED1 2 //pin for PCB LED +#define SupplyAD 36 //Resitor divider for battery connected here + +void loop() +{ + Serial.println(F("LED Flash")); + led_Flash(4, 125); + printSupplyVoltage(); + Serial.println(); + delay(1500); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void printSupplyVoltage() +{ + //get and display supply volts on terminal or monitor + Serial.print(F("Supply Volts ")); + Serial.print(readSupplyVoltage()); + Serial.println(F("mV")); +} + + +uint16_t readSupplyVoltage() +{ + //ESP32 uses the 3.3V VCC as reference + + uint16_t temp; + uint16_t volts = 0; + byte index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn on MOSFET connecting resitor divider in circuit + } + + temp = analogRead(SupplyAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(SupplyAD); + volts = volts + temp; + } + volts = ((volts / 5) * ADMultiplier); + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn off MOSFET connecting resitor divider in circuit + } + + return volts; +} + + +void setup() +{ + Serial.begin(9600); //setup Serial console ouput + Serial.println(); + Serial.println(__FILE__); + Serial.print(F("Compiled ")); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println("45_Battery_Voltage_Read_Test_ESP32 Starting"); + + pinMode(LED1, OUTPUT); //for PCB LED + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); //controls MOSFET connecting resitor divider in circuit + } + +} + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/47_DeepSleep_Timed_Wakeup_ESP32/47_DeepSleep_Timed_Wakeup_ESP32.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/47_DeepSleep_Timed_Wakeup_ESP32/47_DeepSleep_Timed_Wakeup_ESP32.ino new file mode 100644 index 0000000..368769d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/47_DeepSleep_Timed_Wakeup_ESP32/47_DeepSleep_Timed_Wakeup_ESP32.ino @@ -0,0 +1,90 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program flashes a LED connected to the pin defined by LED1, and puts the ESP32 + to deep_sleep for a period determined by the TIME_TO_SLEEP variable (in seconds). + + The program also has the option of using a logic pin to control the power to the lora and SD card + devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are + powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + + Current in deep_sleep for a bare bones ESP32 with regulator and no other devices was 27uA. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define LED1 2 //pin number for LED +#define VCCPOWER 14 //pin may control power to external devices, -1 if not used + +#define uS_TO_S_FACTOR 1000000 //Conversion factor for micro seconds to seconds +#define TIME_TO_SLEEP 15 //Time ESP32 will go to sleep (in seconds) + +RTC_DATA_ATTR int32_t bootCount = 0; +RTC_DATA_ATTR uint32_t sleepcount = 0; + + +void loop() +{ + Serial.print(F("Bootcount ")); + Serial.println(bootCount); + Serial.print(F("Sleepcount ")); + Serial.println(sleepcount); + Serial.println(F("LED Flash")); + led_Flash(4, 125); + Serial.println(F("LED On")); + digitalWrite(LED1, HIGH); + delay(2500); + Serial.println(F("LED Off")); + digitalWrite(LED1, LOW); + + esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); + Serial.println(F("Start Sleep")); + Serial.flush(); + sleepcount++; + esp_deep_sleep_start(); + Serial.println(); + Serial.println(); + Serial.println(F("Awake !")); +} + + +void led_Flash(unsigned int flashes, unsigned int delaymS) +{ + //flash LED to show tracker is alive + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, LOW); + delay(delaymS); + digitalWrite(LED1, HIGH); + delay(delaymS); + } +} + + +void setup() +{ + Serial.begin(9600); //setup Serial console ouput + Serial.println(F("47_DeepSleep_Timed_Wakeup_ESP32 - Starting")); + + if (bootCount == 0) //Run this only the first time + { + bootCount = bootCount + 1; + } + + pinMode(LED1, OUTPUT); //for PCB LED + + if (VCCPOWER >= 0) + { + pinMode(VCCPOWER, OUTPUT); + digitalWrite(VCCPOWER, HIGH); //VCCOUT off + } + +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/50_LightSleep_Timed_Wakeup_ESP32/50_LightSleep_Timed_Wakeup_ESP32.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/50_LightSleep_Timed_Wakeup_ESP32/50_LightSleep_Timed_Wakeup_ESP32.ino new file mode 100644 index 0000000..7e6c4bc --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/50_LightSleep_Timed_Wakeup_ESP32/50_LightSleep_Timed_Wakeup_ESP32.ino @@ -0,0 +1,92 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 03/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ +/* + +**************************************************************************************************************** + Program operation - The program flashes a LED connected to the pin defined by LED1, and puts the ESP32 to + light_sleep for a period determined by TIME_TO_SLEEP (in seconds). + + The program also has the option of using a logic pin to control the power to the lora.SD card and DS18B20 + devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are + powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + + Current in light_sleep mode was 1500uA +**************************************************************************************************************** +*/ + + +#define VCCPOWER 14 //when low supplies VCC power to external devices. Set to -1 if not used +#define LED1 2 //On board LED, high for on + + +#define uS_TO_S_FACTOR 1000000 //Conversion factor for micro seconds to seconds +#define TIME_TO_SLEEP 15 //Time ESP32 will go to sleep (in seconds) + +RTC_DATA_ATTR int16_t bootCount = 0; +RTC_DATA_ATTR uint16_t sleepcount = 0; + + +void loop() +{ + Serial.print(F("Bootcount ")); + Serial.println(bootCount); + Serial.print(F("Sleepcount ")); + Serial.println(sleepcount); + Serial.println(F("LED Flash")); + led_Flash(4, 125); + Serial.println(F("LED On")); + digitalWrite(LED1, HIGH); + delay(2500); + Serial.println(F("LED Off")); + digitalWrite(LED1, LOW); + + esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); + Serial.println(F("Start Sleep")); + Serial.flush(); + sleepcount++; + esp_light_sleep_start(); + Serial.println(); + Serial.println(); + Serial.println(F("Awake !")); +} + + +void led_Flash(unsigned int flashes, unsigned int delaymS) +{ + //flash LED to show tracker is alive + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, LOW); + delay(delaymS); + digitalWrite(LED1, HIGH); + delay(delaymS); + } +} + + +void setup() +{ + Serial.begin(9600); //setup Serial console ouput + Serial.println(F("50_LightSleep_Timed_Wakeup_ESP32 - Starting")); + + if (bootCount == 0) //Run this only the first time + { + bootCount = bootCount + 1; + } + + pinMode(LED1, OUTPUT); //for PCB LED + + if (VCCPOWER >= 0) + { + pinMode(VCCPOWER, OUTPUT); //For controlling power to external devices + digitalWrite(VCCPOWER, HIGH); //VCCOUT off, lora device and SD card off + } + +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/51_DeepSleep_Switch_Wakeup_ESP32/51_DeepSleep_Switch_Wakeup_ESP32.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/51_DeepSleep_Switch_Wakeup_ESP32/51_DeepSleep_Switch_Wakeup_ESP32.ino new file mode 100644 index 0000000..a557fb3 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/51_DeepSleep_Switch_Wakeup_ESP32/51_DeepSleep_Switch_Wakeup_ESP32.ino @@ -0,0 +1,87 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* +Program Operation - The program flashes a LED connected to the pin defined by LED1, and puts the ESP32 +to deep_sleep. Pressing BOOT switch should wake up the ESP32 from sleep. + +Only the specific RTC IO pins can be used as a source for external wakeup. +These are pins: 0,2,4,12-15,25-27,32-39. + +Current in deep_sleep for a bare bones ESP32 with regulator and no other devices was 27uA. + +Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define LED1 2 //pin number for LED +#define SWITCH1 0 //pin number wakeup switch + + +RTC_DATA_ATTR int16_t bootCount = 0; +RTC_DATA_ATTR uint16_t sleepcount = 0; + + +void loop() +{ + Serial.print(F("Bootcount ")); + Serial.println(bootCount); + Serial.print(F("Sleepcount ")); + Serial.println(sleepcount); + Serial.println(F("LED Flash")); + led_Flash(4,125); + Serial.println(F("LED On")); + digitalWrite(LED1, HIGH); + delay(2500); + Serial.println(F("LED Off")); + digitalWrite(LED1, LOW); + + //esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); + + esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, 0); //wakeup on pin GPIO0 going low + + Serial.println(F("Start Sleep")); + Serial.flush(); + sleepcount++; + esp_deep_sleep_start(); + Serial.println(); + Serial.println(); + Serial.println(F("Awake ?")); //should not really see this, deep sleep wakeup causes reset .... +} + + +void led_Flash(unsigned int flashes, unsigned int delaymS) +{ + //flash LED to show tracker is alive + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, LOW); + delay(delaymS); + digitalWrite(LED1, HIGH); + delay(delaymS); + } +} + + +void setup() +{ + Serial.begin(9600); //setup Serial console ouput + Serial.println(); + Serial.println(); + Serial.println(F("51_DeepSleep_Timed_Wakeup_ESP32 - Starting")); + + if(bootCount == 0) //Run this only the first time + { + bootCount = bootCount+1; + } + + pinMode(LED1, OUTPUT); //for PCB LED + pinMode(SWITCH1, INPUT_PULLUP); //for wakeup switch + +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/64_WiFi_Scanner_Display_ESP32/64_WiFi_Scanner_Display_ESP32.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/64_WiFi_Scanner_Display_ESP32/64_WiFi_Scanner_Display_ESP32.ino new file mode 100644 index 0000000..ca83477 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/64_WiFi_Scanner_Display_ESP32/64_WiFi_Scanner_Display_ESP32.ino @@ -0,0 +1,131 @@ +/******************************************************************************************************* + lora Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - When the ESP32 turns on the WiFi function, there is a short high current pulse that + can cause the ESP32 brownout detect to operate. + + This test program at startup flashes an LED, leaves it on and then starts the WiFi. If the Wifi initiates + a brownout, you will see the LED flash again. The LED stays on when scanning, the program reports the + networks found to the serial console and displays them on an attached SSD1306 OLED. + + Thus if you see the LED continually doing short bursts of flashing the turn on\off the WiFi is causing + the ESP32 to reset. There will also be a message on the serial monitor that the brownout detector operated. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#include "WiFi.h" +#define LED1 2 //Arduino pin number for LED, when high LED should be on. + +#include //get library here > https://github.com/olikraus/u8g2 +//U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 + + +void loop() +{ + Serial.println("Set WiFi to Station mode"); //Set WiFi to station mode + Serial.flush(); + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(1000); + + Serial.println("Setup done"); + Serial.flush(); + + digitalWrite(LED1, HIGH); + Serial.println("WiFi scan start"); + Serial.flush(); + int n = WiFi.scanNetworks(); //WiFi.scanNetworks will return the number of networks found + digitalWrite(LED1, LOW); + delay(500); + disp.clear(); + disp.setCursor(0, 0); + + if (n == 0) { + Serial.println("No WiFi"); + disp.println("No WiFi"); + } else { + Serial.print(n); + disp.print(n); + Serial.println(" WiFi found"); + disp.println(" WiFi found"); + led_Flash(n, 500); + + if (n > 16) //only want to display first 16 networks + { + n = 16; + } + + for (int i = 0; i < n; ++i) { + //Print SSID and RSSI for each network found + Serial.print(i + 1); + Serial.print(": "); + Serial.print(WiFi.SSID(i)); + + if (i > 7) + { disp.clearLine(i - 8); + disp.setCursor(0, i - 8); + } + else + { + disp.clearLine(i); + disp.setCursor(0, i); + } + + disp.print(WiFi.SSID(i)); + Serial.print(" ("); + Serial.print(WiFi.RSSI(i)); + Serial.print(")"); + Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*"); + if (i == 7) //check if 8 lines have already been sent to display + { + delay(2000); //leave last 8 on display for a while + disp.clear(); + } + } + } + Serial.println(); + disp.println(); + + + // Wait a bit before scanning again + delay(5000); +} + + +void led_Flash(unsigned int flashes, unsigned int delaymS) +{ + //flash LED + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(5, 50); + digitalWrite(LED1, LOW); + delay(1000); + Serial.begin(9600); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Scanner Ready")); +} diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/64_WiFi_Scanner_Display_ESP32/ESP32_LoRa_Micro_Node.h b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/64_WiFi_Scanner_Display_ESP32/ESP32_LoRa_Micro_Node.h new file mode 100644 index 0000000..73abaf9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/64_WiFi_Scanner_Display_ESP32/ESP32_LoRa_Micro_Node.h @@ -0,0 +1,35 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of the Tracker boards, the ESP32_Micro_Node, be sure to change +//them to match your own setup. You will also need to connect up the pins for the SPI bus, which on the +//ESP32_Micro_Node are SCK on pin 18, MISO on pin 19 and MOSI on pin 23. Some pins such as DIO1, DIO2 and +//BUZZER may not be in used by this sketch so they do not need to be connected and should be set to -1. + + +#define SWITCH1 0 //pin number for switch, the boot switch also +#define LED1 2 //pin number for LED +#define NSS 5 //pin number where the NSS line for the LoRa device is connected +#define SDCS 13 //ESP32 pin number for device select on SD card module +#define VCCPOWER 14 //pin controls power to external devices +#define DIO2 15 //pin number for DIO2 pin on LoRa device +#define TonePin 15 //pin number for LoRa radio tone generation, connects to LoRa device pin DIO2 +#define GPSTX 16 //pin number for TX output from Arduino - RX into GPS +#define GPSRX 17 //pin number for RX input into Arduino - TX from GPS +#define BATREAD 25 //pin that switches on supply measure resistor devider +#define GPSPOWER 26 //pin controls power to external devices +#define NRESET 27 //pin where LoRa device reset line is connected +#define ONEWIREBUS 33 //pin for one wire bus devices +#define DIO1 34 //pin connected to DIO1 on LoRa device +#define DIO0 35 //pin number for DIO0 pin on LoRa device +#define SupplyAD 36 //pin for reading supply voltage + +#define ADMultiplier 10 //multiplier for supply volts calculation, default + + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/81_BME280_Test_ESP32/81_BME280_Test_ESP32.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/81_BME280_Test_ESP32/81_BME280_Test_ESP32.ino new file mode 100644 index 0000000..f1aeeda --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/ESP32/81_BME280_Test_ESP32/81_BME280_Test_ESP32.ino @@ -0,0 +1,125 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 27/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a simple test for a Bosch BME280 sensor. Readings are sent to the serial + monitor. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include //get library here; https://github.com/Seeed-Studio/Grove_BME280 +BME280 bme280; //create an instance of the BME280 senosor +#define BME280_ADDRESS 0x76 //I2C bus address of BME280 +#define BME280_REGISTER_CONTROL 0xF4 //BME280 register number for power control + +#include + +#define LED1 2 //on board LED, high for on + +float temperature; //the BME280 temperature value +float pressure; //the BME280 pressure value +uint16_t humidity; //the BME280 humididty value + + +void loop() +{ + Serial.print(F("Reading > ")); + + readSensors(); //read the sensor values + printSensorValues(); //print the sensor values + Serial.println(); + + sleepBME280(); + delay(5000); + normalBME280(); //BME280 sensor to normal mode +} + + +void readSensors() +{ + //read the sensor values into the global variables + temperature = bme280.getTemperature(); + pressure = bme280.getPressure(); + humidity = bme280.getHumidity(); + +} + + +void printSensorValues() +{ + Serial.print(F("Temperature,")); + Serial.print(temperature, 1); + Serial.print(F("c,Pressure,")); + Serial.print(pressure, 0); + Serial.print(F("Pa,Humidity,")); + Serial.print(humidity); + Serial.print(F("%")); + Serial.print(F(" ")); + Serial.flush(); +} + + +void sleepBME280() +{ + //write this register value to BME280 to put it to sleep + writeBME280reg(BME280_REGISTER_CONTROL, B01111100); +} + + +void normalBME280() +{ + //write this register value to BME280 to put it to read mode + writeBME280reg(BME280_REGISTER_CONTROL, B01111111); +} + + +void writeBME280reg(uint8_t reg, uint8_t regvalue) +{ + //write a register value to the BME280 + Wire.beginTransmission((uint8_t) BME280_ADDRESS); + Wire.write((uint8_t)reg); + Wire.write((uint8_t)regvalue); + Wire.endTransmission(); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //for PCB LED + led_Flash(2, 125); + + Serial.begin(9600); + Serial.println(); + Serial.println(F("81_BME280_Test_ESP32 starting")); + + bme280.init(); //intialise BME280 library + Wire.begin(); //default for ESP32 is SDA on 21, SCL on 22 + + //alternative pins for I2C, ESP32 allows re-direction of I2C, format is Wire.begin(SDA,SCL); + //The SeedBME280 library does do a Wire.begin(); so this will overide it + Wire.begin(16,17); + + Serial.println(F("Initialised BME280")); + Serial.println(); + + readSensors(); //do an initial sensor read +} + diff --git a/lib/SX12XX-LoRa/examples/Hardware_Checks/STM32/31_SSD1306_OLED_Checker_STM32/31_SSD1306_OLED_Checker_STM32.ino b/lib/SX12XX-LoRa/examples/Hardware_Checks/STM32/31_SSD1306_OLED_Checker_STM32/31_SSD1306_OLED_Checker_STM32.ino new file mode 100644 index 0000000..5fa0ce1 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/Hardware_Checks/STM32/31_SSD1306_OLED_Checker_STM32/31_SSD1306_OLED_Checker_STM32.ino @@ -0,0 +1,86 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a simple test program for the SSD1306 and SH1106 OLEDs. The program + prints a short message on each line, pauses, clears the screen, and starts again. + + OLED address is defined as 0x3C. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include //get library here > https://github.com/olikraus/u8g2 +//U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 +#define DEFAULTFONT u8x8_font_chroma48medium8_r //font for U8X8 Library + +#define OLED_Address 0x3C + +uint16_t writecount; +uint32_t startwritemS, endwritemS, timemS; + + +void loop() +{ + writecount++; + Serial.print(writecount); + Serial.print(F(" Writing to display")); + + disp.setI2CAddress(OLED_Address<<1); //I2C address multiplied by 2, the lowest bit must be zero + disp.setFont(DEFAULTFONT); + + startwritemS = millis(); + disp.clear(); + screen1(); + endwritemS = millis(); + timemS = endwritemS - startwritemS; + disp.setCursor(8, 3); + disp.print(timemS); + disp.print(F("mS")); + + Serial.print(F(" - done ")); + Serial.print(timemS); + Serial.println(F("mS")); + + delay(2000); + Serial.println(F("PowerSave display")); + disp.setPowerSave(1); //power save display, turns off + delay(2000); + disp.setPowerSave(0); //display back to normal +} + + +void screen1() +{ + disp.setCursor(0, 0); + disp.print(F("Hello World !")); + disp.setCursor(0, 1); + disp.print(F("Line 1")); + disp.setCursor(0, 2); + disp.print(F("Line 2")); + disp.setCursor(0, 3); + disp.print(F("Line 3")); + disp.setCursor(0, 4); + disp.print(F("Line 4")); + disp.setCursor(0, 5); + disp.print(F("Line 5")); + disp.setCursor(0, 6); + disp.print(F("Line 6")); + disp.setCursor(0, 7); + disp.print(F("0123456789012345")); //display is 8 lines x 16 charaters when using the + //u8x8_font_chroma48medium8_r font +} + + +void setup() +{ + Serial.begin(9600); + Serial.println(F("31_SSD1306_OLED_Checker starting")); + disp.begin(); +} + diff --git a/lib/SX12XX-LoRa/examples/ReadMe.md b/lib/SX12XX-LoRa/examples/ReadMe.md new file mode 100644 index 0000000..3d03dba --- /dev/null +++ b/lib/SX12XX-LoRa/examples/ReadMe.md @@ -0,0 +1,636 @@ +## SX12XX Library Example Programs + + +For the majority of the program examples you will need to define the pins used, the frequency and the LoRa settings in the Settings.h file. The default provided settings may not be optimised for long distance. See the 'What is LoRa' document for information on how LoRa settings affect range. + +Some of the examples use sleep mode on the processor and LoRa device to save power. Typical sleep currents may be mentioned in the description of the program. In most all cases a 'bare bones' Arduino has been used to measure the sleep currents and you may not get even close to the quoted figures using standard Arduinos such as Pro Minis or similar. Optimising particular Arduino boards for low power sleep is outside of the scope of this library and examples. + + +#### 1\_LED\_Blink             (Basics folder) + +This program blinks an LED connected the pin number defined by LED1.The pin 13 LED, fitted to some Arduinos is blinked as well. The blinks should be close to one per second. Messages are sent to the Serial Monitor also. + +#### 2\_Register\_Test             (Basics folder) +This program is stand alone, it is not necessary to install the SX12XX-LoRa library to use it. + +The program checks that a lora device can be accessed by doing a test register write and read. If there is no device found a message is printed on the serial monitor. The contents of the registers from 0x00 to 0x7F are printed, there is a copy of a typical printout below. Note that the read back changed frequency may be different to the programmed frequency, there is a rounding error due to the use of floats to calculate the frequency. + + SX1276-79 Selected + LoRa Device found + Device version 0x12 + Frequency at reset 434000000 + Registers at reset + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00 00 09 1A 0B 00 52 6C 80 00 4F 09 2B 20 08 02 0A + 0x10 FF 70 15 0B 28 0C 12 47 32 3E 00 00 00 00 00 40 + 0x20 00 00 00 00 05 00 03 93 55 55 55 55 55 55 55 55 + 0x30 90 40 40 00 00 0F 00 00 00 F5 20 82 04 02 80 40 + 0x40 00 00 12 24 2D 00 03 00 04 23 00 09 05 84 32 2B + 0x50 14 00 00 10 00 00 00 0F E0 00 0C 04 06 00 5C 78 + 0x60 00 19 0C 4B CC 0D FD 20 04 47 AF 3F F2 3F D9 0B + 0x70 D0 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 + + + Changed Frequency 434099968 + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00 00 09 1A 0B 00 52 6C 86 66 4F 09 2B 20 08 02 0A + 0x10 FF 70 15 0B 28 0C 12 47 32 3E 00 00 00 00 00 40 + 0x20 00 00 00 00 05 00 03 93 55 55 55 55 55 55 55 55 + 0x30 90 40 40 00 00 0F 00 00 00 F5 20 82 04 02 80 40 + 0x40 00 00 12 24 2D 00 03 00 04 23 00 09 05 84 32 2B + 0x50 14 00 00 10 00 00 00 0F E0 00 0C 04 06 00 5C 78 + 0x60 00 19 0C 4B CC 0D FD 20 04 47 AF 3F F2 3F D9 0B + 0x70 D0 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 + + + + +#### 3\_LoRa\_Transmitter             (Basics folder) +This is a minimum setup LoRa test transmitter. A packet containing the ASCII text "Hello World 1234567890" is sent using the frequency and LoRa settings specified in the LT.setupLoRa() command. The pins to access the lora device need to be defined at the top of the program also. + +The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with the transmit power used and the packet length. The matching receiver program, '4\_LoRa\_Receiver' can be used +to check the packets are being sent correctly, the frequency and LoRa settings (in the LT.setupLoRa() commands) must be the same for the transmitter and receiver programs. Sample Serial Monitor output; + +10dBm Packet> Hello World 1234567890 BytesSent,23 PacketsSent,6 + +For an example of a more detailed configuration for a transmitter, see program 103\_LoRa\_Transmitter. + +Serial monitor baud rate is set at 9600 + +#### 4\_LoRa\_Receiver             (Basics folder) + +This is a minimum setup LoRa test receiver. The program listens for incoming packets using the frequency and LoRa settings in the LT.setupLoRa() command. The pins to access the lora device need to be defined at the top of the program also. + +There is a printout on the Arduino IDE serial monitor of the valid packets received, the packet is assumed to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. Sample serial monitor output; + +8s Hello World 1234567890,RSSI,-44dBm,SNR,9dB,Length,23,Packets,7,Errors,0,IRQreg,50 + +If there is a packet error it might look like this, which is showing a CRC error; + +137s PacketError,RSSI,-89dBm,SNR,-8dB,Length,23,Packets,37,Errors,2,IRQreg,70,IRQ\_HEADER\_VALID,IRQ\_CRC\_ERROR,IRQ\_RX\_DONE + +If there are no packets received in a 10 second period then you should see a message like this; + +112s RXTimeout + +For an example of a more detailed configuration for a receiver, see program 104\_LoRa\_Receiver. + +Serial monitor baud rate is set at 9600. +
+ +#### 5\_LoRa\_TX\_Sleep\_Timed\_Wakeup\_Atmel             (Sleep folder) + +This program tests the sleep mode and register retention of the lora device in sleep mode, it assumes an Atmel ATMega328P processor is in use. The LoRa settings to use are specified in the 'Settings.h' file. + +A packet is sent, containing the text 'Before Device Sleep' and the LoRa device and Atmel processor are put to sleep. The processor watchdog timer should wakeup the processor in 15 seconds (approx) and register values should be retained. The device then attempts to transmit another packet 'After Device Sleep' without re-loading all the LoRa settings. The receiver should see 'After Device Sleep' for the first packet and 'After Device Sleep' for the second. + +Tested on a 'bare bones' ATmega328P board, the current in sleep mode was 6.5uA. + + +#### 6\_LoRa\_RX\_and\_Sleep\_Atmel             (Sleep folder) + + +The program listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + +When the program starts the LoRa device is set-up to receive packets with pin DIO0 set to go high when a packet arrives. The receiver remains powered (it cannot receive otherwise) and the processor (Atmel ATMega328P or 1284P) is put to sleep. When pin DIO0 does go high, indicating a packet is received, the processor wakes up and prints the packet. It then goes back to sleep. + +There is a printout of the valid packets received, these are assumed to be in ASCII printable text. The LED will flash for each packet received and the buzzer will sound,if fitted. + +Tested on a 'bare bones' ATmega328P board, the current in sleep mode was 12.26mA. + +#### 7\_LoRa\_TX\_Sleep\_Switch\_Wakeup\_Atmel             (Sleep folder) + + +This program tests the sleep mode and register retention of the lora device in sleep mode, it assumes an Atmel ATMega328P processor is in use. The LoRa settings to use are specified in the 'Settings.h' file. + +A packet is sent, containing the text 'Before Device Sleep' and the lora device and Atmel processor are put to sleep. The processor should remain asleep until the pin defined by SWITCH1 in the Settings.h file is connected to ground and the LoRa device register values should be retained. The LoRa device then attempts to transmit another packet 'After Device Sleep' without re-loading all the LoRa settings. The receiver should see 'After Device Sleep' for the first packet and 'After Device Sleep' for the second. + +Tested on a bare bones ATmega328P board, the current in sleep mode was 2.4uA. + +#### 8\_LoRa\_LowMemory\_TX             (LowMemory folder) + +The program transmits a packet without using a processor buffer, the LoRa device internal buffer is filled direct with variables. The program is a simulation of the type of packet that might be sent from a GPS tracker. Note that in this example a buffer of text is part of the transmitted packet, this does need a processor buffer which is used to fill the LoRa device internal buffer, if you don't need to transmit text then the uint8_t trackerID[] = "tracker1"; definition can be omitted. + +The matching receiving program '9\_LoRa\_LowMemory\_RX' can be used to receive and display the packet, though the program '15\_LoRa\_RX\_Structure' should receive it as well, since the packet contents are the same. + +The contents of the packet received, and printed to serial monitor, should be; + +"tracker1" (buffer) - trackerID +1+ (uint32\_t) - packet count +51.23456 (float) - latitude +-3.12345 (float) - longitude +199 (uint16\_t) - altitude +8 (uint8\_t) - number of satellites +3999 (uint16\_t) - battery voltage +-9 (int8_t) - temperature + +#### 9\_LoRa\_LowMemory\_RX             (LowMemory folder) + +The program receives a packet without using a processor buffer, the LoRa device internal buffer is read direct and copied to variables. The program is a simulation of the type of packet that might be received from a GPS tracker. Note that in this example a buffer of text is part of the received packet, this does need a processor buffer which is filled with data from the LoRa device internal buffer, if you don't need to send and receive text then the uint8_t receivebuffer[32]; definition can be omitted. + +The contents of the packet received, and printed to serial monitor, should be; + +"tracker1" (buffer) - trackerID +1+ (uint32\_t) - packet count +51.23456 (float) - latitude +-3.12345 (float) - longitude +199 (uint16\_t) - altitude +8 (uint8\_t) - number of satellites +3999 (uint16\_t) - battery voltage +-9 (int8_t) - temperature + + +#### 10\_LoRa\_Link\_Test\_Transmitter             (Diagnostics and Test folder) + +This is a program that can save you a great deal of time if your testing the effectiveness of a LoRa links or attached antennas. Simulations of antenna performance are no substitute for real world tests and this simple program allows both long distance link performance to be evaluated and antenna performance to be compared. + +The program sends short test packets that reduce in power by 1dBm at a time. The start power is defined by start\_power and the end power is defined by end\_power (see Settings.h file). Once the end\_power point is reached, the program pauses a short while and starts the transmit sequence again at start\_power. The packet sent contains the power used to send the packet. By listening for the packets with the basic LoRa receive program (4\_LoRa\_Receiver) you can see the reception results, which should look something like this; + + 11s 1*T+05,CRC,80B8,RSSI,-03dBm,SNR,9dB,Length,6, Packets,9,Errors,0,IRQreg,50 + 12s 1*T+04,CRC,9099,RSSI,-74dBm,SNR,9dB,Length,6, Packets,10,Errors,0,IRQreg,50 + 14s 1*T+03,CRC,E07E,RSSI,-75dBm,SNR,9dB,Length,6, Packets,11,Errors,0,IRQreg,50 + +Above shows 3 packets received, the first at +05dBm (+05 in printout), the second at 4dBm (+04 in printout) and the third at 3dBm (+03) in printout. + +If it is arranged so that reception of packets fails halfway through the sequence by attenuating either the transmitter (with an SMA attenuator for instance) or the receiver (by placing it in a tin perhaps) then if you swap transmitter antennas you can see the dBm difference in reception, which will be the dBm difference (gain) of the antenna. + +To start the sequence a packet is sent with the number 999, when received it looks like this; + +T*1999 + +This received packet could be used for the RX program to be able to print totals etc. + +LoRa settings to use for the link test are specified in the 'Settings.h' file. + + +#### 11\_LoRa\_Packet\_Logger\_Receiver             (Diagnostics and Test folder) + + +This is a useful packet logger program. It listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + +There is a printout of the valid packets received in HEX format. Thus the program can be used to receive and record non-ASCII packets. The LED will flash for each packet received and the buzzer will sound, if fitted. The measured frequency difference between the frequency used by the transmitter and the frequency used by the receiver is shown. If this frequency difference gets to 25% of the set LoRa bandwidth, packet reception will fail. The displayed error can be reduced by using the 'offset' setting in the 'Settings.h' file. + +#### 12\_ATmel\_Sleep\_with\_Switch\_Wakeup             (Hardware_Checks folder) + +This program tests the deep sleep mode and wakeup with a switch of an Atmel 328P or 1284P processor. The program starts, flashes the LED and then puts the processor into permanent sleep. It can be woken up with a switch press. Used as a base test routine for checking the sleep current of a board. + +Tested on an 'bare bones' ATmega328P board, the current in sleep mode was 1.7uA with a 3.3V MCP1700 regulator being used. + + +#### 13\_Frequency\_and\_Power\_Check\_TX             (Diagnostics and Test folder) + +This is a program that transmits a long LoRa packets lasting about 5 seconds that can be used to measure the frequency and power of the transmission using external equipment. The bandwidth of the transmission is only 10khz, so a frequency counter should give reasonable average result. + +The LoRa settings to use, including transmit power, are specified in the 'Settings.h' file. + + +#### 14\_LoRa\_Structure\_TX             (Basics folder) + +This program demonstrates the transmitting of a structure as a LoRa packet. The contents of the structure are the same as in the '8\_LoRa\_LowMemory\_TX' program. The packet sent is typical of what might be sent from a GPS tracker. The structure type is defined as trackerPacket and an instance called location1 is created. The structure which includes a character array (text) is filled with values and transmitted. + +The matching receiving program '15\_LoRa\_RX\_Structure' can be used to receive and display the packet, though the program '9\_LoRa\_LowMemory\_RX' should receive it as well, since the contents are the same. + +Note that the structure definition and variable order (including the buffer size) used in the transmitter need to match those used in the receiver. + +The contents of the packet transmitted should be; + +"tracker1" (buffer) - trackerID +1+ (uint32\_t) - packet count +51.23456 (float) - latitude +-3.12345 (float) - longitude +199 (uint16\_t) - altitude +8 (uint8\_t) - number of satellites +3999 (uint16\_t) - battery voltage +-9 (int8_t) - temperature + +#### 15\_LoRa\_Structure\_RX             (Basics folder) + + +This program demonstrates the receiving of a structure as a LoRa packet. The packet sent is typical of what might be sent from a GPS tracker. + +The structure type is defined as trackerPacket and an instance called location1 is created. The structure includes a character array (text). + +The matching receiving program is '15\_LoRa\_RX\_Structure' can be used to receive and display the packet, though the program '9\_LoRa\_LowMemory\_RX' should receive it as well, since the packet contents are the same. + +Not that the structure definition and variable order (including the buffer size) used in the transmitter need to match those used in the receiver. Good luck. + +The contents of the packet received, and printed to serial monitor, should be; + +"tracker1" (buffer) - trackerID +1+ (uint32\_t) - packet count +51.23456 (float) - latitude +-3.12345 (float) - longitude +199 (uint16\_t) - altitude +8 (uint8\_t) - number of satellites +3999 (uint16\_t) - battery voltage +-9 (int8_t) - temperature + + +#### 16\_LoRa\_RX\_Frequency\_Error\_Check             (Diagnostics and Test folder) + +This program can be used to check the frequency error between a pair of LoRa devices, a transmitter and receiver. This receiver measures the frequecy error between the receivers centre frequency and the centre frequency of the transmitted packet. The frequency difference is shown +for each packet and an average over 10 received packets reported. Any transmitter program can be used to give this program something to listen to, including example program '3\_LoRa\_Transmitter'. + + +#### 17\_Sensor\_Transmitter             (Sensor folder) + +The program transmits a LoRa packet without using a processor buffer, the LoRa devices internal buffer is filled directly with variables. + +The sensor used is a BME280. The pressure, humidity, and temperature are read and transmitted. There is also a 16bit value of battery mV (simulated) and and a 8 bit status value at the packet end. + +Although the LoRa packet transmitted and received has its own internal CRC error checking, you could still receive packets of the same length from another source. If this valid packet were to be used to recover the sensor values, you could be reading rubbish. To reduce the risk of this, when the packet is transmitted the CRC value of the actual sensor data is calculated and sent out with the packet. This CRC value is read by the receiver and used to check that the received CRC matches the supposed +sensor data in the packet. As an additional check there is some addressing information at the beginning of the packet which is also checked for validity. Thus we can be relatively confident when reading the +received packet that its genuine and from this transmitter. The packet is built and sent in the sendSensorPacket() function, there is a 'highlighted section' where the actual sensor data is added to the packet. + +Between readings the LoRa device, BME280 sensor, and Atmel micro controller are put to sleep in units of 8 seconds using the Atmel processor internal watchdog. + +The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. The Atmel watchdog timer is a viable option for a very low current sensor node. A bare bones ATmega328P with regulator and LoRa device has a sleep current of 6.6uA, add the LoRa devices and BME280 sensor module and the average sleep current only rises to 6.8uA. + +One of these transmitter programs is running on a long term test with a 175mAh battery, to see how long the battery actually lasts. + + +#### 18\_Sensor\_Receiver             (Sensor folder) + +The program receives a LoRa packet without using a processor buffer, the LoRa devices internal buffer is read direct for the received sensor data. + +The sensor used in the matching '17\_Sensor\_Transmitter' program is a BME280 and the pressure, humidity, and temperature are being and received. There is also a 16bit value of battery mV and and a 8 bit status value at the end of the packet. + +When the program starts, the LoRa device is set-up to set the DIO0 pin high when a packet is received, the Atmel processor is then put to sleep and will wake up when a packet is received. When a packet is received, its printed and assuming the packet is validated, the sensor results are printed to the serial monitor and screen. Between readings the sensor transmitter is put to sleep in units of 8 seconds using the Atmel +processor internal watchdog. + +For the sensor data to be accepted as valid the flowing need to match; + +The 16bit CRC on the received sensor data must match the CRC value transmitted with the packet. +The packet must start with a byte that matches the packet type sent, 'Sensor1' +The RXdestination byte in the packet must match this node ID of this receiver node, defined by 'This_Node' + +In total that's 16 + 8 + 8 = 32bits of checking, so a 1:4294967296 chance (approx) that an invalid packet is acted on and erroneous values displayed. + +The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + +With a standard Arduino Pro Mini and SSD1306 display the current consumption was 20.25mA with the display and 16.6mA without the display. + + +#### 20\_LoRa\_Link\_Test\_Receiver             (Diagnostics and Test folder) + +The program listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + +The program is a matching receiver program for the '10\_LoRa\_Link\_Test\_TX'. The packets received are displayed on the serial monitor and analysed to extract the packet data which indicates the power used to send the packet. A count is kept of the numbers of each power setting received. When the transmitter sends the test mode packet at the beginning of the sequence (displayed as 999) the running totals of the powers received are printed. Thus you can quickly see at what transmit power levels the reception fails. + + +#### 21\_On\_Off\_Transmitter             (Remote Control folder) + +This program is a remote control transmitter. When one of four switches are made (shorted to ground) a packet is transmitted with single byte indicating the state of Switch0 as bit 0, Switch1 as bit 1 and Switch2 as bit 2. To prevent false triggering at the receiver the packet contains a +32 bit number called the TXIdentity which in this example is set to 1234554321. The receiver will only act on, change the state of the outputs, if the identity set in the receiver matches that of the transmitter. The chance of a false trigger is fairly remote. + +Between switch presses the LoRa device and Atmel micro controller are put to sleep. A switch press wakes up the processor from sleep, the switches are read and a packet sent. On a 'bare bones' Arduino set-up the transmitter has a sleep current of approx 2.2uA, so it's ideal for a battery powered remote control with a potential range of many kilometres. + + +The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + +#### 22\_On\_Off\_Receiver             (Remote Control folder) + +This program is a remote control receiver. When a packet is received an 8 bit byte (SwitchByte) is read and the four outputs (defined in Settings.h) are toggled according to the bits set in this byte. If the Switch1 byte has bit 0 cleared, then OUTPUT0 is toggled. If the Switch1 byte has bit 1 cleared, then OUTPUT1 is toggled. If the Switch1 byte has bit 2 cleared, then OUTPUT2 is toggled. + +To prevent false triggering at the receiver the packet also contains a 32 bit number called the TXIdentity which in this example is set to 1234554321. The receiver will only act on, change the state of the outputs, if the identity set in the receiver matches that of the transmitter. The chance of a false trigger is fairly remote. + +The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + +#### 23\_GPS\_Tracker\_Transmitter             (Tracker folder) + +This program is an example of a basic GPS tracker. The program reads the GPS, waits for an updated fix and transmits location and altitude, number of satellites in view, the HDOP value, the fix time of the GPS and the battery voltage. This transmitter can be also be used to investigate GPS performance. At start-up there should be a couple of seconds of recognisable text from the GPS printed to the serial monitor. If you see garbage or funny characters its likely the GPS baud rate is wrong. If the transmitter is turned on from cold, the receiver will pick up the cold fix time, which is an indication of GPS performance. The GPS will be powered on for around 4 seconds before the timing of the fix starts. Outside with a good view of the sky most GPSs should produce a fix in around 45 seconds. The number of satellites and HDOP are good indications to how well a GPS is working. + +The program writes direct to the LoRa devices internal buffer, no memory buffer is used. + +The LoRa settings are configured in the Settings.h file. + +The program has the option of using a pin to control the power to the GPS, if the GPS module being used has this feature. To use the option change the define in the Settings.h file; '#define GPSPOWER -1' from -1 to the pin number being used. Also set the GPSONSTATE and GPSOFFSTATE to the appropriate logic levels. + + +#### 24\_GPS\_Tracker\_Receiver             (Tracker folder) + +This program is an basic receiver for the '23\_Simple\_GPS\_Tracker\_Transmitter' program. + +The program reads the received packet from the tracker transmitter and displays the results on the serial monitor. The LoRa and frequency settings provided in the Settings.h file must match those used by the transmitter. + +The program receives direct from the LoRa devices internal buffer. + + +#### 25\_GPS\_Tracker\_Receiver\_with\_Display\_and\_GPS             (Tracker folder) + +This program is an example of a basic portable GPS tracker receiver. The program receives the location packets from the remote tracker and displays them on an OLED display. The program also reads a local GPS and when that has a fix, will display the distance and direction to the remote tracker. + +The program writes direct to the LoRa devices internal buffer, no memory buffer is used. + +The LoRa settings are configured in the Settings.h file. + +The received information is printed to screen in this order top to bottom; + +Latitude, Longitude, Altitude, HDOP, GPS Fixtime, Tracker battery mV, Number of received packets, Distance and direction to tracker, if local GPS fix. In addition if there is a recent tracker transmitter GPS fix a 'T' is shown on line 0 right of screen and if there is a recent local (receiver) GPS fix a 'R' is displayed line 1 right of screen. + +The received information is printed to the Serial Monitor as CSV data in this order; + +Packet Address information, Latitude, Longitude, Altitude, Satellites in use, HDOP, TX status byte, GPS Fixtime, Tracker battery mV, Number of received packets, Distance and direction to tracker, if local GPS fix. + +The program has the option of using a pin to control the power to the GPS, if the GPS module being used has this feature. To use the option change the define in Settings.h; '#define GPSPOWER -1' from -1 to the pin number being used. Also set the GPSONSTATE and GPSOFFSTATE to the appropriate logic levels. + +The program by default uses software serial to read the GPS, you can use hardware serial by commenting out this line in the Settings.h file; + + #define USE\_SOFTSERIAL\_GPS + +And then defining the hardware serial port you are using, which defaults to Serial1. + +#### 26\_GPS\_Echo             (Hardware_Checks folder) + +This is a simple program to test a GPS. It reads characters from the GPS using software serial and sends them (echoes) to the IDE serial monitor. If your ever having problems with a GPS (or just think you are) use this program first. + +If you get no data displayed on the serial monitor, the most likely cause is that you have the receive data pin into the Arduino (RX) pin connected incorrectly. + +If the data displayed on the serial terminal appears to be random text with odd symbols its very likely you have the GPS serial baud rate set incorrectly. + +Note that not all pins on all Arduinos will work with software serial, see here; + +https://www.arduino.cc/en/Reference/softwareSerial + +Serial monitor baud rate is set at 115200. + + +#### 28\_GPS\_Checker             (Hardware_Checks folder) + +This program is a portable GPS checker and display. At start-up the program starts checking the data coming from the GPS for a valid fix. It checks for 5 seconds and if there is no fix, prints a message on the serial monitor. During this time the data coming from the GPS is copied to the serial monitor also. + +When the program detects that the GPS has a fix, it prints the Latitude, Longitude, Altitude, Number of satellites in use and the HDOP value to the serial monitor. + +Serial monitor baud rate is set at 115200, GPS baud rate to 9600, both are configured in setup(). + + +#### 29\_GPS\_Checker\_With\_Display             (Hardware_Checks folder) + +This program is a GPS checker with a display output. It uses an SSD1306 or SH1106 128x64 I2C OLED display. At start-up the program starts checking the data coming from the GPS for a valid fix. It reads the GPS for 5 seconds and if there is no fix, prints a message on the serial monitor and updates the seconds without a fix on the display. During this time the data coming from the GPS is copied to the serial monitor also. + +When the program detects that the GPS has a fix, it prints the Latitude, Longitude, Altitude, Number of satellites in use, the HDOP value, time and date to the serial monitor. If the I2C OLED display is attached that is updated as well. Display is assumed to be on I2C address 0x3C. + + +#### 30\_I2C\_Scanner             (Hardware_Checks folder) + +The program scans the I2C bus and displays the addresses of any devices found. Useful first check when using I2C devices. + + +#### 31\_SSD1306\_OLED\_Checker             (Diagnostics and Test folder) + +This program is a simple test program for the SSD1306 and SH1106 OLEDs. The program prints a short message on each line, pauses, clears the screen, and starts again. + +OLED address is defined as 0x3C. + +#### 33\_LoRa\_RSSI\_Checker\_With\_Display             (Diagnostics and Test folder) + +The program listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + +There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. The LED will flash for each packet received and the buzzer will sound, if fitted. + +Sample serial monitor output; + +1109s {packet contents} CRC,3882,RSSI,-69dBm,SNR,10dB,Length,19,Packets,1026,Errors,0,IRQreg,50 + +If there is a packet error it might look like this, which is showing a CRC error, + +1189s PacketError,RSSI,-111dBm,SNR,-12dB,Length,0,Packets,1126,Errors,1,IRQreg,70,IRQ\_HEADER\_VALID,IRQ\_CRC\_ERROR,IRQ\_RX\_DONE + +A summary of the packet reception is sent to the OLED display as well, useful for portable applications. + + +#### 34\_ATmel\_Sleep\_with\_Watchdog\_Wakeup             (Hardware_Checks folder) + +This program tests the sleep mode of an Atmel ATMega328P processor. + +At power up the flashes an LED 4 times, then turns on the LED for 5 seconds. Then the processor is put to sleep for 8 seconds. On wakeup the LED flashes twice, then is on for 5 seconds and the board goes to sleep again. And the sequence repeats. + +Sleep current for a 'bare bones' ATmega328 with a MCP1700 regulator @ 3.3V and using an external event such as a switch to wakeup from sleep should be around 2uA. Using the watchdog timer to wakeup raises the deep sleep current to circa 6.2uA. + +#### 35\_Remote\_Control\_Servo\_Transmitter             (Remote Control folder) + +This is a remote control transmitter that uses a LoRa link to transmit the positions from a simple joystick to a remote receiver. The receiver uses the sent joystick positions to adjust the positions of servos. The positions of the joysticks potentiometers on the transmitter are read with the analogueRead() function. + +If the joystick has a switch, often made by pressing on the joystick, then this can be used to remote control an output on the receiver. The switch is read by an interrupt, the interrupt routine sets a flag byte which is read in loop(). + +The program is intended as a proof of concept demonstration of how to remote control servos, the program is not designed as a practical remote control device for RC model cars for instance. + +It would be straight forward to make the transmitter program send packets continuously, but in most places in the world that would break a normal limitation of 10% duty cycle for unlicensed use. Therefore the program was designed to only transmit at a 10% duty cycle. Thus the fastest (lowest air time) packets are used, spreading factor 6 at a bandwidth of 500khz. This results in an air time for the 5 byte control packet of around 4mS, so there are around 25 sent per second. + +To have the transmitter program print out the values read from the joystick, comment in the line; + +//#define DEBUG + +Which is just above the loop() function. With the DEBUG enabled the transmission rate, the rate at which the control packets are transmitted will be slowed down. + +To reduce the risk of the receiver picking up LoRa packets from other sources, the packet sent contains a 'TXidentity' number, valid values are 0 - 255. The receiver must be set-up with the matching identity number or the received packets will be ignored. + +The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings are not necessarily optimised for long range. + + +#### 36\_Remote\_Control\_Servo\_Receiver             (Remote Control folder) + +This is a remote control receiver that uses a LoRa link to control the positions of servos sent from a remote transmitter. + +If the transmitter joystick has a switch, often made by pressing on the joystick, then this can be used to remote control an output on the receiver. + +The program is intended as a proof of concept demonstration of how to remote control servos, the program is not designed as a practical remote control device for RC model cars for instance. + +It would be straight forward to make the transmitter program send packets continuously, but in most places in the world that would break a normal limitation of 10% duty cycle for unlicensed use. Therefore the program was designed to only transmit at a 10% duty cycle. Thus the fastest (lowest air time) packets are used, spreading factor 6 at a bandwidth of 500khz. This results in an air time for the 5 byte control packet of around 4mS, so there are around 25 sent per second. +To have the receiver program print out the joystick values (0-255) read from the received packet, comment in the line; + +//#define DEBUG + +Which is just above the loop() function. With the DEBUG enabled then there is a possibility that some transmitted packets will be missed. With the DEBUG line enabled to servos should also sweep to and fro 3 times at program start-up. + +To reduce the risk of the receiver picking up LoRa packets from other sources, the packet sent contains a 'TXidentity' number, valid values are 0 - 255. The receiver must be set-up with the matching RXIdentity number in Settings.h or the received packets will be ignored. + +The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings are not necessarily optimised for long range. + + +#### 37\_Servo\_Sweep\_Tester             (Hardware_Checks folder) + +This program sweeps two servos from one end of their travel to the other. Useful to check servos are connected correctly and working. + + +#### 38\_lora\_Relay             (Tracker folder) + +This program will receive a lora packet and relay (re-transmit) it. The receiving and transmitting can use different frequencies and lora settings. The receiving and transmitting settings are in the 'Settings.h' file. For an example of it's use see this report; + +How to Search 500 Square Kilometres in 10 minutes.pdf in the libraries 'Test_Reports' folder. + + + +#### 40\_LoRa\_Transmitter\_ImplicitPacket             (Implicit folder) + +This is an example of the use of implicit or fixed length LoRa packets. +Implicit packets have no header so both transmitter and receiver need to be programmed with the packet length in use. The use of spreading factor 6 requires implicit packets and together with a bandwidth of 500khz, leads to the shortest possible and lowest air time packets. + +This example sends a buffer that is 23 characters long and that length must be defined in Settings.h as the constant 'PacketLength'. + +A packet containing ASCII text is sent according to the frequency and LoRa settings specified in the 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + +The details of the packet sent and any errors are shown on the Serial Monitor, together with the transmit power used, the packet length and the CRC of the packet. The matching receive program, '41\_LoRa\_Receiver\_ImplicitPackets' can be used to check the packets are being sent correctly, the frequency and LoRa settings (in Settings.h) must be the same for the Transmit and Receive program. + +Sample Serial Monitor output; + +10dBm Packet> {packet contents*} BytesSent,23 CRC,DAAB TransmitTime,8mS PacketsSent,1 + + +#### 41\_LoRa\_Receiver\_ImplicitPacket             (Implicit folder) + +This is an example of the use of implicit or fixed length LoRa packets. Implicit packets have no header so both transmitter and receiver need to be programmed with the packet length in use. The use of spreading factor 6 requires implicit packets and together with a bandwidth of 500khz, leads to the shortest possible and lowest air time packets. The program listens for incoming packets using the LoRa settings in the 'Settings.h'. + +This example receives a buffer that is 19 characters long and that length must be defined in Settings.h as the constant 'PacketLength'. + +The pins to access the lora device need to be defined in the 'Settings.h' file also. + +There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. The LED will flash for each packet received and the buzzer will sound, if fitted. + +Sample serial monitor output; + +1109s {packet contents} CRC,3882,RSSI,-69dBm,SNR,10dB,Length,19,Packets,1026,Errors,0,IRQreg,50 + +If there is a packet error it might look like this, which is showing a CRC error, + +1189s PacketError,RSSI,-111dBm,SNR,-12dB,Length,0,Packets,1126,Errors,1,IRQreg,70,IRQ\_HEADER\_VALID,IRQ\_CRC\_ERROR,IRQ\_RX\_DONE + + +#### 42\_WiFi\_Scanner\_Display\_ESP32             (Hardware Checks\ESP32 folder) + +When the ESP32 turns on the WiFi function, there is a short high current pulse that can cause the ESP32 brownout detect to operate. + +This test program at startup flashes an LED, leaves it on and then starts the WiFi. If the Wifi initiates a brownout, you will see the LED flash again. The LED stays on when scanning, the program reports the networks found to the serial console and displays them on an attached SSD1306 OLED. + +Thus if you see the LED continually doing short bursts of flashing the turn on off the WiFi is causing the ESP32 to reset. There will also be a message on the serial monitor that the brownout detector operated. + + +#### 43\_SD\_Card\_Test\_ESP32             (Hardware Checks\ESP32 folder) + +This test program has been written to check that a connected SD card adapter, Micro or standard, is functional. To use the program first copy the file (in this programs directory) called testfile.txt to the root directory of the SD card. + +When the program runs it will attempt to open 'testfile.txt' and spool the contents to the Arduino IDE serial monitor. The testfile is part of the source code for the Apollo 11 Lunar Lander navigation and guidance computer. There are LED flashes at power up or reset, then at start of every loop of the test. The LED is on whilst the testfile is being read. If the LED flashes very rapidly then there is a problem accessing the SD card. + +The program also has the option of using a logic pin to control the power to the lora and SD card devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + +#### 44\_SD\_Card\_Test\_With\_FS\_ESP322             (Hardware Checks\ESP32 folder) + +This test program has been written to check that a connected SD card adapter, Micro or standard, is functional with the FS functions. To use the program first copy the file (in this programs directory) called testfile.txt to the root directory of the SD card. + +When the program runs it will attempt to open 'testfile.txt' and spool the contents to the Arduino IDE serial monitor. The testfile is part of the source code for the Apollo 11 Lunar Lander navigation and guidance computer. There are LED flashes at power up or reset, then at start of every loop of the test. The LED is on whilst the testfile is being read. If the LED flashes very rapidly then there is a problem accessing the SD card. + +The program also has the option of using a logic pin to control the power to the lora and SD card devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + + +#### 45\_Battery\_Voltage\_Read\_Test             (Hardware_Checks folder) + + +This test program has been written to check that hardware for reading the battery voltage has been assembled correctly such that it is funtional. The value defined as 'ADMultiplier' in settings.h is used to adjust the value read from the 91K\11K resistor divider and convert into mV. + +There is also an option of using a logic pin to turn the resistor divider used to read battery voltage on and off. This reduces current used in sleep mode. To use the feature set the define for pin BATVREADON in 'Settings.h' to the pin used. If not using the feature set the pin number to -1. + +#### 47\_DeepSleep\_Timed\_Wakeup\_ESP32             (Hardware_Checks\ESP32 folder) + +This program flashes a LED connected to the pin defined by LED1, and puts the ESP32 to deep_sleep for a period determined by the TIME_TO_SLEEP variable (in seconds). + +The program also has the option of using a logic pin to control the power to the lora and SD card devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + +Current in deep_sleep for a bare bones ESP32 with regulator and no other devices was 27uA. + + +#### 48\_DS18B20\_Test             (Hardware_Checks folder) + + +The program reads a single DS18B20 temperature sensor and prints the result to the serial monitor. + +The program also has the option of using a logic pin to control the power to the lora and SD card devices, which can save power in sleep mode. If the hardware is fitted to your board then these devices are assumed to be powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + + + +#### 50\_LightSleep\_Timed\_Wakeup\_ESP32             (ESP32 folder) + +The program flashes a LED connected to the pin defined by LED1, and puts the ESP32 to light_sleep for a period determined by TIME_TO_SLEEP (in seconds). + +The program also has the option of using a logic pin to control the power to the lora device, SD card and DS18B20 devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + +#### 51\_DeepSleep\_Switch\_Wakeup\_ESP32             (ESP32 folder) + +The program flashes a LED connected to the pin defined by LED1, and puts the ESP32 to deep_sleep. Pressing BOOT switch should wake up the ESP32 from sleep. + +Only the specific RTC IO pins can be used as a source for external wakeup. +These are pins: 0,2,4,12-15,25-27,32-39. + +Current in deep sleep for a bare bones ESP32 with regulator and no other devices was 27uA. + +#### 52\_FLRC\_Transmitter             (SX128X\Examples\Basics folder) + +This is a test transmitter for the Fast Long Range Communication (FLRC) mode introduced in the SX128X devices. A packet containing ASCII text is sent according to the frequency and FLRC settings specified in the 'Settings.h' file. The pins to access the SX128X device need to be defined +in the 'Settings.h' file also. + +The details of the packet sent and any errors are shown on the Serial Monitor, together with the transmit power used, the packet length and the CRC of the packet. The matching receive program, '53_FLRC_Receiver' can be used to check the packets are being sent correctly, the frequency and FLRC settings (in Settings.h) must be the same for the Transmit and Receive program. Sample Serial Monitor output; + +10dBm Packet> {packet contents*} BytesSent,23 CRC,DAAB TransmitTime,54mS PacketsSent,1 + + +#### 53\_FLRC\_Receiver             (SX128X_Examples\Basics folder) + +This is a test receiver for the Fast Long Range Communication (FLRC) mode introduced in the SX128X devices. The program listens for incoming packets using the FLRC settings in the 'Settings.h' file. The pins to access the SX128X device need to be defined in the 'Settings.h' file also. + +There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. The LED will flash for each packet received and the buzzer will sound, if fitted. + +Sample serial monitor output; + +3s Hello World 1234567890*,CRC,DAAB,RSSI,-73dB,Length,23,Packets,1,Errors,0,IRQreg,6 + +If there is a packet error it might look like this, which is showing a CRC error, + +6s PacketError,RSSI,-103dB,Length,119,Packets,3,Errors,1,IRQreg,46,IRQ\_RX\_DONE,IRQ\_SYNCWORD\_VALID,IRQ\_CRC\_ERROR + + +#### 58\_FM\_Tone             (Basics folder) + +Transmits a FM tone using the LoRa device that can be picked up on an FM UHF handheld receiver. The tones are not true FM but the UHF receiver does not know that. + + +#### 59\_Play\_Star\_Wars\_Tune             (Silly folder) + +A silly program really, but does demonstrate that you can shift a carrier generated by the LoRa device in FSK mode fast enough to play audio tones that can be picked up on an FM UHF handheld receiver. The tones are not true FM but the receiver does not know that. + + +#### 60\_LoRa\_Packet\_Logger\_Receiver\_SD             (Diagnostics and Test folder) + + +The program listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + +There is a printout and save to SD card of the valid packets received in HEX format. Thus the program can be used to receive and record non-ASCII packets. The LED will flash for each packet received and the buzzer will sound, if fitted. The measured frequency difference between the frequency used by the transmitter and the frequency used by the receiver is shown. If this frequency difference gets to 25% of the set LoRa bandwidth, packet reception will fail. The displayed error can be reduced by using the 'offset' setting in the 'Settings.h' file. + +There will be a limit to how fast the logger can receive packets, mainly caused by the delay in writing to SD card, so at high packet rates, packets will be lost. + + + +#### 103\_LoRa\_Transmitter\_Detailed\_Setup             (Basics folder) + +This is a program that demonstrates the detailed setup of a LoRa test transmitter. A packet containing ASCII text is sent according to the frequency and LoRa settings specified in the Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + +The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with the transmit power used, the packet length and the CRC of the packet. The matching receive program, '104\_LoRa\_Receiver' can be used to check the packets are being sent correctly, the frequency and LoRa settings (in Settings.h) must be the same for the transmitter and receiver programs. Sample Serial Monitor output; + +10dBm Packet> Hello World 1234567890* BytesSent,23 CRC,DAAB TransmitTime,64mS PacketsSent,2 + +Serial monitor baud rate is set at 9600 + +#### 104\_LoRa\_Receiver\_Detailed\_Setup             (Basics folder) + +This is a program that demonstrates the detailed setup of a LoRa test receiver. The program listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + +There is a printout on the Arduino IDE Serial Monitor of the valid packets received, the packet is assumed to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. The LED will flash for each packet received and the buzzer will sound, if fitted. Sample serial monitor output; + +7s Hello World 1234567890*,CRC,DAAB,RSSI,-52dBm,SNR,9dB,Length,23,Packets,5,Errors,0,IRQreg,50 + +If there is a packet error it might look like this, which is showing a CRC error, + +968s PacketError,RSSI,-87dBm,SNR,-11dB,Length,23,Packets,613,Errors,2,IRQreg,70,IRQ\_HEADER\_VALID,IRQ\_CRC\_ERROR,IRQ\_RX\_DONE + +Serial monitor baud rate is set at 9600. + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/103_LoRa_Transmitter_Detailed_Setup.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/103_LoRa_Transmitter_Detailed_Setup.ino new file mode 100644 index 0000000..edd1db9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/103_LoRa_Transmitter_Detailed_Setup.ino @@ -0,0 +1,188 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a program that demonstrates the detailed setup of a LoRa test transmitter. + A packet containing ASCII text is sent according to the frequency and LoRa settings specified in the + 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with + the transmit power used, the packet length and the CRC of the packet. The matching receive program, + '104_LoRa_Receiver' can be used to check the packets are being sent correctly, the frequency and LoRa + settings (in Settings.h) must be the same for the transmitter and receiver programs. Sample Serial + Monitor output; + + 10dBm Packet> Hello World 1234567890* BytesSent,23 CRC,DAAB TransmitTime,64mS PacketsSent,2 + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX126XLT LT; //create a library class instance called LT + +uint8_t TXPacketL; +uint32_t TXPacketCount, startmS, endmS; + +uint8_t buff[] = "Hello World 1234567890"; + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on receiver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + startmS = millis(); //start transmit timer + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + endmS = millis(); //packet sent, note end time + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + uint16_t localCRC; + + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + localCRC = LT.CRCCCITT(buff, TXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); //print CRC of transmitted packet + Serial.print(F(" TransmitTime,")); + Serial.print(endmS - startmS); //print transmit time of packet + Serial.print(F("mS")); + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("103_LoRa_Transmitter_Detailed_Setup Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_DCDC); + LT.setPaConfig(0x04, PAAUTO, LORA_DEVICE); + LT.setDIO3AsTCXOCtrl(TCXO_CTRL_3_3V); + LT.calibrateDevice(ALLDevices); //is required after setting TCXO + LT.calibrateImage(Frequency); + LT.setDIO2AsRfSwitchCtrl(); + LT.setPacketType(PACKET_TYPE_LORA); + LT.setRfFrequency(Frequency, Offset); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, Optimisation); + LT.setBufferBaseAddress(0, 0); + LT.setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + LT.setHighSensitivity(); //set for maximum gain + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); + //*************************************************************************************************** + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/Settings.h new file mode 100644 index 0000000..8bed135 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/Settings.h @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER are not used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //busy pin on LoRa device +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define RX_EN -1 //pin for RX enable, used on some SX126X devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX126X devices, set to -1 if not used +#define SW -1 //SW pin on some Dorji LoRa devices, used to power antenna switch, set to -1 if not used +#define LED1 8 //on board LED, high for on + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +//for SX1262, SX1268 power range is +22dBm to -9dBm +//for SX1261, power range is +15dBm t0 -9dBm + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/104_LoRa_Receiver_Detailed_Setup.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/104_LoRa_Receiver_Detailed_Setup.ino new file mode 100644 index 0000000..a4146ac --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/104_LoRa_Receiver_Detailed_Setup.ino @@ -0,0 +1,254 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a program that demonstrates the detailed setup of a LoRa test receiver. + The program listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins + to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout on the Arduino IDE Serial Monitor of the valid packets received, the packet is + assumed to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect + weird things to happen on the Serial Monitor. The LED will flash for each packet received and the + buzzer will sound, if fitted. + + Sample serial monitor output; + + 7s Hello World 1234567890*,CRC,DAAB,RSSI,-52dBm,SNR,9dB,Length,23,Packets,5,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 968s PacketError,RSSI,-87dBm,SNR,-11dB,Length,23,Packets,613,Errors,2,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX126XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the device packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } + + delay(250); //gives a longer buzzer and LED flash for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("104_LoRa_Receiver_Detailed_Setup Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in the library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_DCDC); + LT.setPaConfig(0x04, PAAUTO, LORA_DEVICE); + LT.setDIO3AsTCXOCtrl(TCXO_CTRL_3_3V); + LT.calibrateDevice(ALLDevices); //is required after setting TCXO + LT.calibrateImage(Frequency); + LT.setDIO2AsRfSwitchCtrl(); + LT.setPacketType(PACKET_TYPE_LORA); + LT.setRfFrequency(Frequency, Offset); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, Optimisation); + LT.setBufferBaseAddress(0, 0); + LT.setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + LT.setHighSensitivity(); //set for maximum gain + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/Settings.h new file mode 100644 index 0000000..dd7295e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/Settings.h @@ -0,0 +1,49 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER are not used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //busy pin on LoRa device +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define RX_EN -1 //pin for RX enable, used on some SX126X devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX126X devices, set to -1 if not used +#define SW -1 //SW pin on some Dorji LoRa devices, used to power antenna switch, set to -1 if not used +#define LED1 8 //on board LED, high for on +#define BUZZER 4 //pin for buzzer, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino new file mode 100644 index 0000000..572a7b5 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino @@ -0,0 +1,145 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program demonstrates the transmitting of a structure as a LoRa packet. The + contents of the structure are the same as in the '8_LoRa_LowMemory_TX' program. The packet sent is + typical of what might be sent from a GPS tracker. + + The structure type is defined as trackerPacket and an instance called location1 is created. The struture + which includes a character array (text) is filled with values and transmitted. + + The matching receiving program '15_LoRa_RX_Structure' can be used to receive and display the packet, + though the program '9_LoRa_LowMemory_RX' should receive it as well, since the contents are the same. + + Note that the structure definition and variable order (including the buffer size) used in the transmitter + need to match those used in the receiver. + + The contents of the packet transmitted should be; + + "tracker1" (buffer) - trackerID + 1+ (uint32_t) - packet count + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Good luck. + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX126XLT LT; + +uint32_t TXpacketCount = 1; +uint32_t startmS, endmS; + +struct trackerPacket +{ + uint8_t trackerID[13]; + uint32_t txcount; + float latitude; + float longitude; + uint16_t altitude; + uint8_t satellites; + uint16_t voltage; + int8_t temperature; +}; + +struct trackerPacket location1; //define an instance called location1 of the structure trackerPacket + + +void loop() +{ + + //fill the defined structure with values + uint8_t buff[] = "tracker1"; //create the contents to be of location1.trackerID + memcpy (&location1.trackerID, &buff, sizeof(buff)); //copy the contents of buff[] into the structure + location1.txcount = TXpacketCount; + location1.latitude = 51.23456; + location1.longitude = -3.12345; + location1.altitude = 199; + location1.satellites = 8; + location1.voltage = 3999; + location1.temperature = -9; + + digitalWrite(LED1, HIGH); + startmS = millis(); + + if (LT.transmit((uint8_t *) &location1, sizeof(location1), 0, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 + { + endmS = millis(); + digitalWrite(LED1, LOW); + TXpacketCount++; + Serial.print(TXpacketCount); + Serial.print(F(" ")); + Serial.print(sizeof(location1)); + Serial.print(F(" Bytes Sent")); + Serial.print(F(" ")); + Serial.print(endmS - startmS); + Serial.print(F("mS")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/14_LoRa_Structure_TX/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/14_LoRa_Structure_TX/Settings.h new file mode 100644 index 0000000..7bc656f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/14_LoRa_Structure_TX/Settings.h @@ -0,0 +1,41 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino new file mode 100644 index 0000000..7cdc034 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino @@ -0,0 +1,194 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 17/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program demonstrates the receiving of a structure as a LoRa packet. The packet + sent is typical of what might be sent from a GPS tracker. + + The structure type is defined as trackerPacket and an instance called location1 is created. The structure + includes a character array (text). + + The matching receiving program is '15_LoRa_RX_Structure' can be used to receive and display the packet, + though the program '9_LoRa_LowMemory_RX' should receive it as well, since the packet contents are the same. + + Not that the structure definition and variable order (including the buffer size) used in the transmitter + need to match those used in the receiver. Good luck. + + The contents of the packet received, and printed to serial monitor, should be; + + "tracker1" (buffer) - trackerID + 1+ (uint32_t) - packet count + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX126XLT LT; + +uint8_t RXPacketL; //stores length of packet received +uint32_t RXpacketCount; //count of received packets +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet +uint32_t errors; //count of packet errors + + +struct trackerPacket +{ + uint8_t trackerID[13]; + uint32_t txcount; + float latitude; + float longitude; + uint16_t altitude; + uint8_t satellites; + uint16_t voltage; + int8_t temperature; +}; + +struct trackerPacket location1; //define an instance called location1 of the structure trackerPacket + + +void loop() +{ + RXPacketL = LT.receive( (uint8_t *) &location1, sizeof(location1), 0, WAIT_RX); //wait for a packet to arrive with no timeout + + digitalWrite(LED1, HIGH); //something has happened, what I wonder ? + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +void printlocation1() +{ + uint8_t buff[13]; //define a buffer to receive a copy from the structure + memcpy (&buff, &location1.trackerID, sizeof(buff)); //copy the contents of buffer in struture to buff[] + + //now print the contents of the structure + Serial.print((char*) buff); //cast to a char type for printing + Serial.print(F(",")); + Serial.print(location1.txcount); + Serial.print(F(",")); + Serial.print(location1.latitude, 5); + Serial.print(F(",")); + Serial.print(location1.longitude, 5); + Serial.print(F(",")); + Serial.print(location1.altitude); + Serial.print(F("m,")); + Serial.print(location1.satellites); + Serial.print(F("sats,")); + Serial.print(location1.voltage); + Serial.print(F("mV,")); + Serial.print(location1.temperature); + Serial.print(F("c ")); +} + + +void packet_is_OK() +{ + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(" ")); + printlocation1(); + printpacketDetails(); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + printpacketDetails(); + Serial.print(F("IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + +void printpacketDetails() +{ + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup(void) +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.print(F("Receiver ready")); + Serial.println(); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/15_LoRa_Structure_RX/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/15_LoRa_Structure_RX/Settings.h new file mode 100644 index 0000000..e5ad937 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/15_LoRa_Structure_RX/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define BUZZER 4 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/1_LED_Blink/1_LED_Blink.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/1_LED_Blink/1_LED_Blink.ino new file mode 100644 index 0000000..2865c0a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/1_LED_Blink/1_LED_Blink.ino @@ -0,0 +1,69 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This programs is supplied as is, it is up to the user of the program to decide if the programs are + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program blinks an LED connected the pin number defined below. The pin 13 LED, + fitted to some Arduinos is blinked as well. The blinks should be close to one per second. messages are + sent to the Serial Monitor also. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define LED1 8 //pin number for LED, set logic level high for on + +#define Program_Version "V1.0" + +uint16_t seconds; //used to display time elapsed on Serial Monitor + +void loop() +{ + Serial.print(seconds); + Serial.println(F(" Seconds")); //this message should print on console at close to once per second + seconds++; + digitalWrite(LED1, HIGH); + digitalWrite(13, HIGH); + delay(100); + digitalWrite(LED1, LOW); + digitalWrite(13, LOW); + delay(890); //should give approx 1 second flash +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //general purpose routine for flashing LED as indicator + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); //LED on + digitalWrite(13, HIGH); //Arduino board LED on + delay(delaymS); + digitalWrite(LED1, LOW); //LED off + digitalWrite(13, LOW); //Arduino board LED off + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + pinMode(13, OUTPUT); //setup pin as output for some Arduino boards that include an LED on pin 13 + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("1_LED_Blink Starting")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/2_Register_Test/2_Register_Test.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/2_Register_Test/2_Register_Test.ino new file mode 100644 index 0000000..0754ea4 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/2_Register_Test/2_Register_Test.ino @@ -0,0 +1,402 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is stand alone, it is not necessary to install the SX12XX-LoRa library + to use it. This test program is for the SX126X LoRa devices. + + The program checks that a SX126X LoRa device can be accessed by doing a test register write and read. + If there is no device found a message is printed on the serial monitor. The contents of the registers + from 0x00 to 0x7F are printed, there is a copy of a typical printout below. Note that the read back + changed frequency may be slightly different to the programmed frequency, there is a rounding error due + to the use of floats to calculate the frequency. + + The Arduino pin numbers that the NSS and NRESET pins on the LoRa device are connected to must be + specified in the hardware definitions section below. The LoRa device type in use, SX1261, SX1262, + or SX1268 must be specified also. + + Typical printout; + + 2_Register_Test Starting + Reset device + LoRa Device found + Reset device + Registers at reset + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x800 00 00 00 00 01 07 20 1E 00 10 19 04 0F FF 0F FF + 0x810 10 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 + 0x820 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x830 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x840 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x850 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x860 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x870 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x880 03 00 00 5F 10 08 00 00 08 05 00 39 30 00 00 0C + 0x890 00 00 00 00 00 0F 0A 07 10 00 26 01 01 53 06 07 + 0x8A0 10 00 AA 20 5A 04 F0 00 56 56 54 43 94 20 40 00 + 0x8B0 00 83 11 00 01 04 0A 4C 14 0A 2F 01 6B FF FF 00 + 0x8C0 00 A0 20 00 00 00 AC 00 1C 00 00 AB 05 30 00 00 + 0x8D0 0C 14 14 40 06 00 00 10 C8 00 00 00 00 00 31 39 + 0x8E0 90 39 0C 04 40 20 1C 18 03 00 05 04 03 02 01 01 + 0x8F0 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 + 0x900 30 00 00 00 00 00 00 00 00 00 00 00 24 04 47 04 + 0x910 14 12 12 04 00 03 0A 00 15 35 09 00 02 1F 5F 08 + 0x920 01 04 05 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x930 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x940 00 07 00 03 02 00 10 0E 0D 0C 03 04 03 70 0C 00 + 0x950 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 + 0x960 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x970 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x980 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x990 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + + Frequency at reset 915000000 + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x880 03 00 00 5F 10 08 00 00 08 05 00 39 30 00 00 0C + Change Frequency 434100000 + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x880 03 00 00 5F 10 08 00 00 08 05 00 1B 21 99 A0 0C + Changed Frequency 434100000 + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +const uint16_t REG_RFFrequency31_24 = 0x088B; +const uint16_t REG_RFFrequency23_16 = 0x088C; +const uint16_t REG_RFFrequency15_8 = 0x088D; +const uint16_t REG_RFFrequency7_0 = 0x088E; +const uint8_t RADIO_WRITE_REGISTER = 0x0D; +const uint8_t RADIO_READ_REGISTER = 0x1D; +const uint8_t RADIO_SET_RFFREQUENCY = 0x86; + +const uint8_t DEVICE_SX1261 = 0x01; +const uint8_t DEVICE_SX1262 = 0x00; +const uint8_t DEVICE_SX1268 = 0x02; + +//********* Setup hardware definitions here ! ***************** + +//These are the pin definitions for one of the Tracker boards, be sure to change them to match your +//own setup. You will also need to connect up the pins for the SPI bus, which on an Arduino Pro Mini are +//SCK pin 13, MISO pin 12, and MOSI pin 11. + +#define NSS 10 //SX126X device select +#define NRESET 9 //SX126X reset pin +#define RFBUSY 7 //SX126X busy pin +#define LED1 8 //for on board LED, put high for on +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define LORA_DEVICE DEVICE_SX1262 //define the device, DEVICE_SX1261, DEVICE_SX1262 or DEVICE_SX1268 + +//**************************************************************/ + + +#include + + +void setup() +{ + Serial.begin(9600); + Serial.println(F("2_Register_Test Starting")); + + SPI.begin(); + SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //The begin function setups the hardware pins used by device and then checks if device is found + //the DIO1, DIO2 and DIO3 are not used in this example so are set to -1 + + if (begin(NSS, NRESET, RFBUSY, -1, -1, -1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + } + else + { + Serial.println(F("No device responding")); + } +} + + +void loop() +{ + uint32_t frequency; + resetDevice(LORA_DEVICE); //reset the device + Serial.println(F("Registers at reset")); //show the all registers following a reset + printRegisters(0x800, 0x9FF); + Serial.println(); + Serial.println(); + + frequency = getFreqInt(); //read the set frequency following a reset + Serial.print(F("Frequency at reset ")); + Serial.println(frequency); + printRegisters(0x0880, 0x088F); //show the registers before the frequency change + setRfFrequency(434100000, 0); //change the frequency at reset, in hertz + frequency = getFreqInt(); //read back the changed frequency + Serial.print(F("Change Frequency ")); + Serial.println(frequency); //print the changed frequency, did the write work (allow for rounding errors) ? + printRegisters(0x0880, 0x088F); //show the registers after frequency change + frequency = getFreqInt(); //read the set frequency following a reset + Serial.print(F("Changed Frequency ")); + Serial.println(frequency); + + Serial.println(); + Serial.println(); + delay(5000); +} + + +void readRegisters(uint16_t address, uint8_t *buffer, uint16_t size) +{ + + uint16_t index; + uint8_t addr_l, addr_h; + + addr_h = address >> 8; + addr_l = address & 0x00FF; + checkBusy(); + + digitalWrite(NSS, LOW); + SPI.transfer(RADIO_READ_REGISTER); + SPI.transfer(addr_h); //MSB + SPI.transfer(addr_l); //LSB + SPI.transfer(0xFF); + for (index = 0; index < size; index++) + { + *(buffer + index) = SPI.transfer(0xFF); + } + + digitalWrite(NSS, HIGH); + + checkBusy(); +} + + +uint8_t readRegister(uint16_t address) +{ + uint8_t data; + + readRegisters(address, &data, 1); + return data; +} + + +void writeRegisters(uint16_t address, uint8_t *buffer, uint16_t size) +{ + uint8_t addr_l, addr_h; + uint8_t i; + + addr_l = address & 0xff; + addr_h = address >> 8; + checkBusy(); + + digitalWrite(NSS, LOW); + SPI.transfer(RADIO_WRITE_REGISTER); + SPI.transfer(addr_h); //MSB + SPI.transfer(addr_l); //LSB + + for (i = 0; i < size; i++) + { + SPI.transfer(buffer[i]); + } + + digitalWrite(NSS, HIGH); + + checkBusy(); +} + + +void writeRegister(uint16_t address, uint8_t value) +{ + writeRegisters( address, &value, 1 ); +} + + +uint32_t getFreqInt() +{ + //get the current set device frequency from registers, return as long integer + uint8_t MsbH, MsbL, Mid, Lsb; + uint32_t uinttemp; + float floattemp; + MsbH = readRegister(REG_RFFrequency31_24); + MsbL = readRegister(REG_RFFrequency23_16); + Mid = readRegister(REG_RFFrequency15_8); + Lsb = readRegister(REG_RFFrequency7_0); + floattemp = ( (MsbH * 0x1000000ul) + (MsbL * 0x10000ul) + (Mid * 0x100ul) + Lsb); + floattemp = ((floattemp * 0.95367431640625) / 1000000ul); + uinttemp = (uint32_t)(floattemp * 1000000); + return uinttemp; +} + + +void printRegisters(uint16_t Start, uint16_t End) +{ + //prints the contents of SX126x registers to serial monitor + + uint16_t Loopv1, Loopv2, RegData; + + Serial.print(F("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F")); + Serial.println(); + + for (Loopv1 = Start; Loopv1 <= End;) //32 lines + { + Serial.print(F("0x")); + Serial.print((Loopv1), HEX); //print the register number + Serial.print(F(" ")); + for (Loopv2 = 0; Loopv2 <= 15; Loopv2++) + { + RegData = readRegister(Loopv1); + if (RegData < 0x10) + { + Serial.print(F("0")); + } + Serial.print(RegData, HEX); //print the register number + Serial.print(F(" ")); + Loopv1++; + } + Serial.println(); + } +} + + +void setRfFrequency( uint32_t frequency, int32_t offset ) +{ + //Note RF_Freq = freq_reg*32M/(2^25)-----> freq_reg = (RF_Freq * (2^25))/32 + + uint8_t Rf_Freq[4]; + + frequency = frequency + offset; + + frequency = ( uint32_t )( ( double )frequency / ( double )0.95367431640625 ); + + checkBusy(); + + Rf_Freq[0] = (frequency >> 24) & 0xFF; //MSB + Rf_Freq[1] = (frequency >> 16) & 0xFF; + Rf_Freq[2] = (frequency >> 8) & 0xFF; + Rf_Freq[3] = frequency & 0xFF;//LSB + + writeCommand(RADIO_SET_RFFREQUENCY, Rf_Freq, 4); +} + + +void checkBusy() +{ + uint8_t busy_timeout_cnt; + busy_timeout_cnt = 0; + + while (digitalRead(RFBUSY)) + { + delay(1); + busy_timeout_cnt++; + + if (busy_timeout_cnt > 10) //wait 10mS for busy to complete + { + busy_timeout_cnt = 0; + Serial.println(F("ERROR - Busy Timeout!")); + break; + } + } +} + + +void resetDevice(uint8_t device) +{ + if ( (device == DEVICE_SX1261) | (device == DEVICE_SX1262) | (device == DEVICE_SX1268) ) + { + Serial.println(F("Reset device")); + delay(10); + digitalWrite(NRESET, LOW); + delay(2); + digitalWrite(NRESET, HIGH); + delay(25); + checkBusy(); + } +} + + + +bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinDIO2, int8_t pinDIO3, int8_t pinSW, uint8_t device) +{ + pinMode(pinNSS, OUTPUT); + digitalWrite(pinNSS, HIGH); + pinMode(pinNRESET, OUTPUT); + digitalWrite(pinNRESET, LOW); + pinMode(pinRFBUSY, INPUT); + + if (pinDIO1 >= 0) + { + pinMode( pinDIO1, INPUT); + } + + if (pinDIO2 >= 0) + { + pinMode(pinDIO2, INPUT); + } + + if (pinDIO3 >= 0) + { + pinMode(pinDIO3, INPUT); + } + + if (pinSW >= 0) + { + pinMode(pinSW, OUTPUT); //Dorji devices have an SW pin that needs to be set high to power antenna switch + digitalWrite(pinSW, HIGH); + } + + resetDevice(device); + if (checkDevice()) + { + return true; + } + + return false; +} + + +bool checkDevice() +{ + //check there is a device out there, writes a register and reads back + uint8_t Regdata1, Regdata2; + Regdata1 = readRegister(0x88e); //low byte of frequency setting + writeRegister(0x88e, (Regdata1 + 1)); + Regdata2 = readRegister(0x88e); //read changed value back + writeRegister(0x88e, Regdata1); //restore register to original value + + if (Regdata2 == (Regdata1 + 1)) + { + return true; + } + else + { + return false; + } +} + + +void writeCommand(uint8_t Opcode, uint8_t *buffer, uint16_t size) +{ + uint8_t index; + checkBusy(); + + digitalWrite(NSS, LOW); + SPI.transfer(Opcode); + + for (index = 0; index < size; index++) + { + SPI.transfer(buffer[index]); + } + digitalWrite(NSS, HIGH); + + checkBusy(); +} + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino new file mode 100644 index 0000000..ae46757 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino @@ -0,0 +1,123 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a minimum setup LoRa test transmitter. A packet containing the ASCII text + "Hello World 1234567890" is sent using the frequency and LoRa settings specified in the LT.setupLoRa() + command. The pins to access the lora device need to be defined at the top of the program also. + + The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with + the transmit power used and the packet length. The matching receiver program, '4_LoRa_Receiver' can be used + to check the packets are being sent correctly, the frequency and LoRa settings (in the LT.setupLoRa() + commands) must be the same for the transmitter and receiver programs. Sample Serial Monitor output; + + 10dBm Packet> Hello World 1234567890* BytesSent,23 PacketsSent,6 + + For an example of a more detailed configuration for a transmitter, see program 103_LoRa_Transmitter. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library + +SX126XLT LT; //create a library class instance called LT + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for sensing RX and TX done +#define SW 5 //SW pin on LoRa device, used to power antenna switch +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using +#define TXpower 10 //LoRa transmit power in dBm + +uint8_t TXPacketL; +uint32_t TXPacketCount; + +uint8_t buff[] = "Hello World 1234567890"; //the message to send + + +void setup() +{ + Serial.begin(9600); + Serial.println(); + Serial.println(F("3_LoRa_Transmitter Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1); + } + + LT.setupLoRa(434000000, 0, LORA_SF7, LORA_BW_125, LORA_CR_4_5, LDRO_AUTO); //configure frequency and LoRa settings + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on receiver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + Serial.println(); + delay(1000); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino new file mode 100644 index 0000000..3d2d920 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino @@ -0,0 +1,170 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a minimum setup LoRa test receiver. The program listens for incoming packets + using the frequency and LoRa settings in the LT.setupLoRa() command. The pins to access the lora device + need to be defined at the top of the program also. + + There is a printout on the Arduino IDE serial monitor of the valid packets received, the packet is assumed + to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect weird things to + happen on the Serial Monitor. Sample serial monitor output; + + 8s Hello World 1234567890*,RSSI,-44dBm,SNR,9dB,Length,23,Packets,7,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error; + + 137s PacketError,RSSI,-89dBm,SNR,-8dB,Length,23,Packets,37,Errors,2,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + If there are no packets received in a 10 second period then you should see a message like this; + + 112s RXTimeout + + For an example of a more detailed configuration for a receiver, see program 104_LoRa_Receiver. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library + +SX126XLT LT; //create a library class instance called LT + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define SW 5 //SW pin on LoRa device, used to power antenna switch +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using +#define RXBUFFER_SIZE 32 //RX buffer size + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet + + +void setup() +{ + Serial.begin(9600); + Serial.println(); + Serial.println(F("4_LoRa_Receiver Starting")); + Serial.println(); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1); + } + + LT.setupLoRa(434000000, 0, LORA_SF7, LORA_BW_125, LORA_CR_4_5, LDRO_AUTO); //configure frequency and LoRa settings + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + PacketRSSI = LT.readPacketRSSI(); //read the received packets RSSI value + PacketSNR = LT.readPacketSNR(); //read the received packets SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + + RXpacketCount++; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + printElapsedTime(); //print elapsed time to Serial Monitor + + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/58_FM_Tone/58_FM_Tone.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/58_FM_Tone/58_FM_Tone.ino new file mode 100644 index 0000000..5c224ed --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/58_FM_Tone/58_FM_Tone.ino @@ -0,0 +1,95 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - Transmits a FM tone using the LoRa device that can be picked up on an FM UHF + handheld receiver. The tones are not true FM but the UHF receiver does not know that. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX126XLT LT; //create a library class instance called LT + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.println(F("PlayTone> ")); + Serial.println(); + + digitalWrite(LED1, HIGH); + LT.toneFM(1000, 1000, deviation, adjustfreq, TXpower); + digitalWrite(LED1, LOW); + + delay(1000); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + pinMode(4, OUTPUT); + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("58_FM_Tone Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupDirect(Frequency, Offset); + + Serial.print(F("Tone Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/58_FM_Tone/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/58_FM_Tone/Settings.h new file mode 100644 index 0000000..f32a364 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Basics/58_FM_Tone/Settings.h @@ -0,0 +1,36 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup Direct Modem Parameters Here ! *************** + +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes +const uint16_t deviation = 10000; //deviation, total frequency shift low to high +const float adjustfreq = 0.9; //adjustment to tone frequency + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino new file mode 100644 index 0000000..45a5bb6 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino @@ -0,0 +1,266 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 01/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a program that can be used to test the effectiveness of a LoRa link or its + attached antennas. Simulations of antenna performance are no substitute for real world tests and this + simple program allows both long distance link performance to be evaluated and antenna performance to be + compared. + + The program sends short test packets that reduce in power by 1dBm at a time. The start power is defined + by start_power and the end power is defined by end_power (see Settings.h file). Once the end_power point + is reached, the program pauses a short while and starts the transmit sequence again at start_power. + The packet sent contains the power used to send the packet. By listening for the packets with the basic + LoRa receive program (4_LoRa_Receiver) you can see the reception results, which should look something + like this; + + 11s 1*T+05,CRC,80B8,RSSI,-73dBm,SNR,9dB,Length,6,Packets,9,Errors,0,IRQreg,50 + 12s 1*T+04,CRC,9099,RSSI,-74dBm,SNR,9dB,Length,6,Packets,10,Errors,0,IRQreg,50 + 14s 1*T+03,CRC,E07E,RSSI,-75dBm,SNR,9dB,Length,6,Packets,11,Errors,0,IRQreg,50 + + Above shows 3 packets received, the first at +05dBm (+05 in printout), the second at 4dBm (+04 in + printout) and the third at 3dBm (+03) in printout. + + If it is arranged so that reception of packets fails halfway through the sequence by attenuating either the + transmitter (with an SMA attenuator for instance) or the receiver (by placing it in a tin perhaps) then + if you swap transmitter antennas you can see the dBm difference in reception, which will be the dBm difference + (gain) of the antenna. + + To start the sequence a packet is sent with the number 999, when received it looks like this; + + T*1999 + + This received packet could be used for the RX program to be able to print totals etc. + + LoRa settings to use for the link test are specified in the 'Settings.h' file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include +#include "Settings.h" + +SX126XLT LT; + +int8_t TestPower; +uint8_t TXPacketL; + +void loop() +{ + Serial.println(F("Start Test Sequence")); + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("Start Packet> ")); + + SendTest1ModePacket(); + + Serial.println(); + + for (TestPower = start_power; TestPower >= end_power; TestPower--) + { + Serial.print(TestPower); + Serial.print(F("dBm ")); + Serial.print(F("Test Packet> ")); + Serial.flush(); + SendTestPacket(TestPower); + Serial.println(); + delay(packet_delay); + } + + Serial.println(F("Finished Test Sequence")); + Serial.println(); +} + + +void SendTestPacket(int8_t lpower) +{ + //build and send the test packet in addressed form, 3 bytes will be added to begining of packet + int8_t temppower; + uint8_t buff[3]; //the packet is built in this buffer + TXPacketL = sizeof(buff); + + if (lpower < 0) + { + buff[0] = '-'; + } + else + { + buff[0] = '+'; + } + + if (TestPower == 0) + { + buff[0] = ' '; + } + + temppower = TestPower; + + if (temppower < 0) + { + temppower = -temppower; + } + + if (temppower > 19) + { + buff[1] = '2'; + buff[2] = ((temppower - 20) + 0x30); + } + else if (temppower > 9) + { + buff[1] = '1'; + buff[2] = ((temppower - 10) + 0x30); + } + else + { + buff[1] = '0'; + buff[2] = (temppower + 0x30); + } + + LT.printASCIIPacket(buff, sizeof(buff)); + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitAddressed(buff, sizeof(buff), TestPacket, Broadcast, ThisNode, 5000, lpower, WAIT_TX); + digitalWrite(LED1, LOW); + + if (TXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } +} + + +void SendTest1ModePacket() +{ + //used to allow an RX to recognise the start off the sequence and possibly print totals + + uint8_t buff[3]; //the packet is built in this buffer + + buff[0] = '9'; + buff[1] = '9'; + buff[2] = '9'; + TXPacketL = sizeof(buff); + + LT.printASCIIPacket(buff, sizeof(buff)); + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitAddressed(buff, sizeof(buff), TestMode1, Broadcast, ThisNode, 5000, start_power, WAIT_TX); + delay(mode_delaymS); //longer delay, so that the start test sequence is obvious + digitalWrite(LED1, LOW); + + if (TXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes SentOK")); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + delay(packet_delay); //change LED flash so packet error visible + delay(packet_delay); + digitalWrite(LED1, HIGH); + delay(packet_delay); + delay(packet_delay); + digitalWrite(LED1, LOW); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("10_LoRa_Link_Test_Transmitter Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); + +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h new file mode 100644 index 0000000..df4699d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h @@ -0,0 +1,52 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 01/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3 may not be in used by this sketch so they do not need to be connected and should +//be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define RX_EN -1 //pin for RX enable, used on some SX126X devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX126X devices, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +//for SX1262, SX1268 power range is +22dBm to -9dBm +//for SX1261, power range is +15dBm t0 -9dBm +const int8_t TXpower = 10; //Transmit power used when sending packet starting test sequence +const int8_t start_power = 10; //link test starts at this transmit power +const int8_t end_power = -8; //and ends at this power +const uint8_t ThisNode = 'T'; //this identifies the node in transmissions + + +#define packet_delay 1000 //mS delay between packets +#define mode_delaymS 2000 //mS delay after sending start test sequence + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino new file mode 100644 index 0000000..59f62e3 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino @@ -0,0 +1,223 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 01/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout of the valid packets received in HEX format. Thus the program can be used to receive + and record non-ASCII packets. The LED will flash for each packet received and the buzzer will sound, + if fitted. The measured frequency difference between the frequency used by the transmitter and the + frequency used by the receiver is shown. If this frequency difference gets to 25% of the set LoRa + bandwidth, packet reception will fail. The displayed error can be reduced by using the 'offset' + setting in the 'Settings.h' file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" +#include //get the library here; https://github.com/PaulStoffregen/Time +time_t recordtime; //used to record the current time, preventing displayed rollover on printing + +SX126XLT LT; + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 60000, WAIT_RX); //returns 0 if packet error of some sort, timeout set at 60secs\60000mS + + digitalWrite(LED1, HIGH); //something has happened + recordtime = now(); //stop the time to be displayed rolling over + printtime(); + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + delay(50); //lets have a slightly longer beep + digitalWrite(BUZZER, LOW); + } + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + RXpacketCount++; + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + Serial.print(F(" FreqErrror,")); + Serial.print(LT.getFrequencyErrorHz()); + Serial.print(F("hz ")); + + LT.printSXBufferHEX(0, (RXPacketL - 1)); + + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void printDigits(int8_t digits) +{ + //utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(F(":")); + if (digits < 10) + Serial.print('0'); + Serial.print(digits); +} + + +void printtime() +{ + Serial.print(hour(recordtime)); + printDigits(minute(recordtime)); + printDigits(second(recordtime)); +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("11_LoRa_Packet_Logger_Receiver Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("Radio Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); + Serial.println(); + LT.printOperatingSettings(); + Serial.println(); + Serial.println(); + printtime(); + Serial.print(F(" Receiver ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h new file mode 100644 index 0000000..9ab7049 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h @@ -0,0 +1,42 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 01/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be connected +//and should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define RFBUSY 7 //SX12XX busy pin +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define LED1 8 //On board LED, high for on +#define BUZZER -1 //normally not used so set to -1 + + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 2; //LoRa TX power + +#define packet_delay 1000 //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino new file mode 100644 index 0000000..7802beb --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino @@ -0,0 +1,190 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program can be used to check the frequency error between a pair of LoRa + devices, a transmitter and receiver. This receiver measures the frequecy error between the receivers + centre frequency and the centre frequency of the transmitted packet. The frequency difference is shown + for each packet and an average over 10 received packets reported. Any transmitter program can be used + to give this program something to listen to, including example program '3_LoRa_Transmit'. + + Checked for correct operation with lora bandwidths of 500000hz, 125000 and 7800hz. At higher bandwidths + the reported frequency errors can be within 10-20hz at minimum bandwidth, 7800hz, the reported frequency + can be circa 100hz out. + + Note: Semtech appear to have stated that the frequency error function that this example uses, is not + supported for SX126X, for reasons that have not been given, so use at your own risk. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" + +SX126XLT LT; + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //a buffer is needed to receive packets + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet +int32_t totalHzError = 0; //used to keep a running total of hZ error for averaging + + +void loop() +{ + + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 0, WAIT_RX); //wait for a packet to arrive + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + RXpacketCount++; + Serial.print(F("PacketOK > ")); + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + Serial.println(); + printFrequencyError(); +} + + +void printFrequencyError() +{ + int32_t hertzerror, regdata; + regdata = LT.getFrequencyErrorRegValue(); + hertzerror = LT.getFrequencyErrorHz(); + Serial.print(F("ErrorRegValue,")); + Serial.print(regdata, HEX); + Serial.print(F(" PacketHertzError,")); + Serial.print(hertzerror); + Serial.println(F("hz")); + + totalHzError = totalHzError + hertzerror; + + if (RXpacketCount == 10) + { + Serial.print(F("******** AverageHertzerror ")); + Serial.print((totalHzError / 10)); + Serial.println(F("hz")); + RXpacketCount = 0; + totalHzError = 0; + delay(5000); + } +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + errors++; + Serial.print(F("PacketError,RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("16_LoRa_RX_Frequency_Error_Check Starting")); + Serial.println(); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h new file mode 100644 index 0000000..42b8fa5 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h @@ -0,0 +1,47 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER SWITCH1 may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define RX_EN -1 //pin for RX enable, used on some SX126X devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX126X devices, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa TX power + +#define packet_delay 1000 //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino new file mode 100644 index 0000000..61054a2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino @@ -0,0 +1,317 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 31/05/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + The program is a matching receiver program for the '10_LoRa_Link_Test_Transmitter'. The packets received + are displayed on the serial monitor and analysed to extract the packet data which indicates the power + used to send the packet. A count is kept of the numbers of each power setting received. When the transmitter + sends the test mode packet at the beginning of the sequence (displayed as 999) the running totals of the + powers received are printed. Thus you can quickly see at what transmit power levels the reception fails. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc +#include + +SX126XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + +uint32_t Test1Count[34]; //buffer where counts of received packets are stored, -9dbm to +22dBm +uint32_t Mode1_Cycles = 0; //count the number of cyles received +bool updateCounts = false; //update counts set to tru when first TestMode1 received, at sequence start + + +void loop() +{ + RXPacketL = LT.receiveAddressed(RXBUFFER, RXBUFFER_SIZE, 15000, WAIT_RX); //wait for a packet to arrive with 15seconds (15000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + delay(25); //gives a slightly longer beep + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + + if (BUZZER > 0) //turn buzzer on for a valid packet + { + digitalWrite(BUZZER, HIGH); + } + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL - 3); //print the packet as ASCII characters + + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + + processPacket(); +} + + +void processPacket() +{ + int8_t lTXpower; + uint8_t packettype; + uint32_t temp; + + packettype = LT.readRXPacketType(); //need to know the packet type so we can decide what to do + + if (packettype == TestPacket) + { + if (RXBUFFER[0] == ' ') + { + lTXpower = 0; + } + + if (RXBUFFER[0] == '+') + { + lTXpower = ((RXBUFFER[1] - 48) * 10) + (RXBUFFER[2] - 48); //convert packet text to power + } + + if (RXBUFFER[0] == '-') + { + lTXpower = (((RXBUFFER[1] - 48) * 10) + (RXBUFFER[2] - 48)) * -1; //convert packet text to power + } + + Serial.print(F(" (")); + + if (RXBUFFER[0] != '-') + { + Serial.write(RXBUFFER[0]); + } + + Serial.print(lTXpower); + Serial.print(F("dBm)")); + + if (updateCounts) + { + temp = (Test1Count[lTXpower+9]); + Test1Count[lTXpower+9] = temp + 1; + } + } + + if (packettype == TestMode1) + { + //this is a command to switch to TestMode1 also updates totals and logs + updateCounts = true; + Serial.println(); + Serial.println(F("End test sequence")); + + if (Mode1_Cycles > 0) + { + print_Test1Count(); + } + + Serial.println(); + Mode1_Cycles++; + } + +} + + +void print_Test1Count() +{ + //prints running totals of the powers of received packets + int8_t index; + uint32_t j; + + Serial.print(F("Test Packets ")); + Serial.println(RXpacketCount); + Serial.print(F("Test Cycles ")); + Serial.println(Mode1_Cycles); + + Serial.println(); + for (index = 31; index >= 0; index--) + { + Serial.print(index-9); + Serial.print(F("dBm,")); + j = Test1Count[index]; + Serial.print(j); + Serial.print(F(" ")); + } + Serial.println(); + + Serial.print(F("CSV")); + for (index = 31; index >= 0; index--) + { + Serial.print(F(",")); + j = Test1Count[index]; + Serial.print(j); + } + Serial.println(); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("20_LoRa_Link_Test_Receiver Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + //setup SPI, its external to library on purpose, so settings can be mixed and matched with other SPI devices + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //this function call sets up the device for LoRa using the settings from settings.h + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h new file mode 100644 index 0000000..f878524 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h @@ -0,0 +1,42 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 31/05/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done + +#define BUZZER -1 //pin for buzzer, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino new file mode 100644 index 0000000..37e873d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino @@ -0,0 +1,283 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 01/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, + if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. + The LED will flash for each packet received and the buzzer will sound, if fitted. + + Sample serial monitor output; + + 1109s {packet contents} CRC,3882,RSSI,-69dBm,SNR,10dB,Length,19,Packets,1026,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 1189s PacketError,RSSI,-111dBm,SNR,-12dB,Length,0,Packets,1126,Errors,1,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + A summary of the packet reception is sent to the OLED display as well, useful for portable applications. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX126XLT LT; //create a library class instance called LT + +#include //get library here > https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 + + +uint32_t RXpacketCount; +uint32_t RXpacketErrors; +uint16_t IRQStatus; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 0, WAIT_RX); //wait for a packet to arrive with no timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); //buzzer on + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t localCRC; + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(RXpacketErrors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("OK")); + dispscreen1(); +} + + +void packet_is_Error() +{ + printElapsedTime(); //print elapsed time to Serial Monitor + + RXpacketErrors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(RXpacketErrors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("Packet Error")); + dispscreen1(); + + delay(500); //gives longer buzzer and LED falsh for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void dispscreen1() +{ + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(F("RSSI ")); + disp.print(PacketRSSI); + disp.print(F("dBm")); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(F("SNR ")); + + if (PacketSNR > 0) + { + disp.print(F("+")); + } + + disp.print(PacketSNR); + disp.print(F("dB")); + disp.clearLine(3); + disp.setCursor(0, 3); + disp.print(F("Length ")); + disp.print(LT.readRXPacketL()); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(F("Packets ")); + disp.print(RXpacketCount); + disp.clearLine(5); + disp.setCursor(0, 5); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("IRQreg ")); + disp.print(IRQStatus, HEX); +} + + + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("33_LoRa_RSSI_Checker_With_Display Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Check LoRa")); + disp.setCursor(0, 1); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + disp.print(F("LoRa OK")); + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + disp.print(F("Device error")); + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //this function call sets up the device for LoRa using the settings from settings.h + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h new file mode 100644 index 0000000..ec2dd6b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h @@ -0,0 +1,48 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 01/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define RX_EN -1 //pin for RX enable, used on some SX126X devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX126X devices, set to -1 if not used +#define BUZZER -1 //pin for buzzer, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 2; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 255 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino new file mode 100644 index 0000000..0256384 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino @@ -0,0 +1,282 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 15/05/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a program that can be used to test the throughput of a LoRa transmitter. + Whilst the various LoRa calculators tell you the on air data rate, in practice the achievable data + rate will be less than that due to the overhead of the software routines to load and send a packet + and internal delays in the LoRa device itself. + + A buffer is filled with characters and that buffer is then transmitted. The total time for a number of + transmissions is recorded and the bit rate calculated. The packet size (1 - 255 bytes) and the number of + packets to send in the test are specified in the 'Settings.h' file, see the 'Setup packet parameters Here !' + section. The setting file also has the lora settings to use. A lower spreading factors and higher + bandwidths will result in higher bitrates. + + There is the option of turning on an a requirement for an acknowledgement from a remote receiver, before + the transmitter sends the next packet, set this; 'const bool waitforACK = true;' definition in the + settings file. The matching receiver program '43_LoRa_Data_Throughput_Acknowledge_Receiver' does then need + to be configured with same lora settings as this transmitter. When this option is set, the program will + keep running until the number of transmissions and acknowledgements has completed without any timeouts + in order to produce a valid average. + + The results of the test are printed out thus; + + SX1262,434000000hz,SF7,BW500000,CR4:5,LDRO_Off,SyncWord_0x12,IQNormal,Preamble_8 + Total transmit time 100 packets = 1382mS + Average 16 byte packet transmit time = 13.82mS + Packets per second 72.36 + Bits per packet sent = 128 + Data rate = 9262bps + + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX126XLT LT; //create a library class instance called LT + +uint32_t startmS, endmS, sendtimemS, bitspersecond, bitsPerpacket; +uint32_t TXPacketCount; +float averagePacketTime; +uint8_t packetNumber; +uint8_t RXPacketL; //length of received packet +uint8_t PacketType; //for packet addressing, identifies packet type received +uint32_t packetCheck; +bool loopFail = false; + + +void loop() +{ + uint16_t index, index2; + uint8_t TXBUFFER[TXPacketL + 1]; //create buffer for transmitted packet + loopFail = false; + + Serial.println(F("Start transmit test")); + + startmS = millis(); //start transmit timer + + for (index = 0; index < numberPackets; index++) + { + //fill the buffer + for (index2 = 0; index2 < TXPacketL; index2++) + { + TXBUFFER[index2] = index2; + } + + TXBUFFER[0] = TestPacket; //set first byte to identify this test packet + TXBUFFER[1] = index; //put the index as packet number in second byte of packet + Serial.print(index); //print number of packet sent + + if (waitforACK) + { + packetCheck = ( (uint32_t) TXBUFFER[4] << 24) + ( (uint32_t) TXBUFFER[3] << 16) + ( (uint32_t) TXBUFFER[2] << 8) + (uint32_t) TXBUFFER[1]; + Serial.print(F(",Checkvalue,")); + Serial.print(packetCheck, HEX); + Serial.print(F(",")); + } + + digitalWrite(LED1, HIGH); + + if (LT.transmit(TXBUFFER, TXPacketL, 10000, TXpower, WAIT_TX)) //will return 0 if transmit error + { + digitalWrite(LED1, LOW); + + if (waitforACK) + { + if (!waitAck(packetCheck)) + { + Serial.print(F("NoACKreceived,")); + loopFail = true; + break; + } + } + + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + loopFail = true; + } + + Serial.println(); + + } + + if (!loopFail) + { + endmS = millis(); //all packets sent, note end time + digitalWrite(LED1, LOW); + Serial.println(); + LT.printModemSettings(); + sendtimemS = endmS - startmS; + Serial.println(); + Serial.print(F("Total transmit time ")); + Serial.print(numberPackets); + Serial.print(F(" packets = ")); + Serial.print(sendtimemS); + Serial.println(F("mS")); + + averagePacketTime = (float) ((endmS - startmS) / numberPackets); + + if (waitforACK) + { + Serial.print(F("Average ")); + Serial.print(TXPacketL); + Serial.print(F(" byte packet transmit and acknowledge time = ")); + } + else + { + Serial.print(F("Average ")); + Serial.print(TXPacketL); + Serial.print(F(" byte packet transmit time = ")); + } + + Serial.print(averagePacketTime, 2); + Serial.println(F("mS")); + Serial.print(F("Packets per second ")); + Serial.println((float) (1000/averagePacketTime)); + bitsPerpacket = (uint32_t) (TXPacketL * 8); + Serial.print(F("Bits per packet sent = ")); + Serial.println(bitsPerpacket); + + Serial.print(F("Data rate = ")); + Serial.print((bitsPerpacket / (averagePacketTime / 1000)), 0); + Serial.print(F("bps")); + Serial.println(); + Serial.println(); + delay(10000); //have a delay between loops so we can see result + } + else + { + Serial.println(F("Transmit test failed, trying again")); + Serial.println(); + delay(1000); + } + +} + + +bool waitAck(uint32_t TXnum) +{ + uint32_t RXnum; + uint16_t IRQStatus; + + RXPacketL = LT.receiveSXBuffer(0, 1000, WAIT_RX); //returns 0 if packet error of some sort + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F("RXTimeout,")); + return false; + } + else + { + Serial.print(F("ACKRX,")); + } + + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + RXnum = LT.readUint32(); + RXPacketL = LT.endReadSXBuffer(); + + if ( (PacketType != ACK) || (RXnum != TXnum)) + { + Serial.print(F("NotValidACK,")); + return false; + } + else + { + Serial.print(RXnum, HEX); + Serial.print(F(",OK")); + return true; + } + +} + + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("42_LoRa_Data_Throughput_Test_Transmitter Starting")); + + SPI.begin(); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h new file mode 100644 index 0000000..32fbd9a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h @@ -0,0 +1,43 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 26/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER are not used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //RFBUSY pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_500; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF5; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +//******* Setup packet parameters Here ! *************** +const uint8_t numberPackets = 50; //number of packets to send in transmit loop +const uint8_t TXPacketL = 4; //length of packet to send +const bool waitforACK = false; //set to true to have transmit wait for ack before continuing + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/60_LoRa_Packet_Logger_Receiver_SD.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/60_LoRa_Packet_Logger_Receiver_SD.ino new file mode 100644 index 0000000..432e141 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/60_LoRa_Packet_Logger_Receiver_SD.ino @@ -0,0 +1,252 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout and save to SD card of the valid packets received in HEX format. Thus the program + can be used to receive and record non-ASCII packets. The LED will flash for each packet received and + the buzzer will sound, if fitted. The measured frequency difference between the frequency used by the + transmitter and the frequency used by the receiver is shown. If this frequency difference gets to 25% + of the set LoRa bandwidth, packet reception will fail. The displayed error can be reduced by using the + 'offset' setting in the 'Settings.h' file. + + There will be a limit to how fast the logger can receive packets, mainly caused by the delay in writing + to SD card, so at high packet rates, packets will be lost. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include + +#include //https://github.com/greiman/SdFat +SdFat SD; +File logFile; +char filename[] = "Log000.txt"; + +boolean SD_Found = false; //set if SD card found at program startup +uint8_t lognumber; + + +#include +SX126XLT LT; + +#include "Settings.h" +#include //get the library here; https://github.com/PaulStoffregen/Time + +uint32_t RXpacketCount; //count of good packets +uint32_t errors; //count of packet errors +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet +uint16_t IRQStatus; //used to read the IRQ status +int32_t FreqErrror; //frequency error of received packet, in hz + + +time_t recordtime; //used to record the current time, preventing displayed rollover on printing + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +#include "SD_Logger_Library.h" + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + recordtime = now(); //stop the time to be displayed rolling over + printtime(); + printtimeSD(); + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + FreqErrror = LT.getFrequencyErrorHz(); + IRQStatus = LT.readIrqStatus(); + + if (RXPacketL == 0) + { + packet_is_Error(); + packet_is_ErrorSD(); + } + else + { + packet_is_OK(); + packet_is_OKSD(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + delay(50); //lets have a slightly longer beep + digitalWrite(BUZZER, LOW); + } + + Serial.println(); +} + + +void packet_is_OK() +{ + + RXpacketCount++; + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + Serial.print(F(" FreqErrror,")); + Serial.print(FreqErrror); + Serial.print(F("hz ")); + + LT.printHEXPacket(RXBUFFER, RXPacketL); + + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + RXPacketL = LT.readRXPacketL(); //get the real packet length + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void printDigits(int8_t digits) +{ + //utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(F(":")); + if (digits < 10) + Serial.print('0'); + Serial.print(digits); +} + + +void printtime() +{ + Serial.print(hour(recordtime)); + printDigits(minute(recordtime)); + printDigits(second(recordtime)); +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("60_LoRa_Packet_Logger_Receiver_SD Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("lora device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); + } + } + + lognumber = setup_SDLOG() ; //setup SD card + Serial.print(F("Lognumber ")); + Serial.println(lognumber); + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); + Serial.println(); + printModemSettingsSD(); + logFile.println(); + LT.printOperatingSettings(); + Serial.println(); + printOperatingSettingsSD(); + logFile.println(); + printtime(); + Serial.print(F(" Receiver ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/SD_Logger_Library.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/SD_Logger_Library.h new file mode 100644 index 0000000..a8c868a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/SD_Logger_Library.h @@ -0,0 +1,346 @@ +//SD_Logger_Library.h +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +void printModemSettingsSD(); +void printOperatingSettingsSD(); +void printOperatingModeSD(uint8_t opmode); +uint8_t setup_SDLOG(); +void printDeviceSD(); +void printSXBufferHEXSD(uint8_t start, uint8_t end); +void printHEXByteSD(uint8_t temp); +void packet_is_OKSD(); +void packet_is_ErrorSD(); +void printDigitsSD(int8_t digits); +void printtimeSD(); + + +void printModemSettingsSD() +{ + uint8_t regdata; + uint8_t sf; + uint32_t bandwidth; + uint8_t cr; + uint8_t opt; + uint16_t syncword; + uint8_t invertIQ; + uint16_t preamble; + uint32_t freqint; + + //get al the data frome the lora device in one go to avoid swapping + //devices on the SPI bus all the time + regdata = LT.readsavedModParam2(); + bandwidth = LT.returnBandwidth(regdata); + + freqint = LT.getFreqInt();sf = LT.getLoRaSF(); + cr = (LT.getLoRaCodingRate() + 4); + opt = LT.getOptimisation(); + syncword = LT.getSyncWord(); + invertIQ = LT.getInvertIQ(); + preamble = LT.getPreamble(); + + printDeviceSD(); + logFile.print(F(",")); + logFile.print(freqint); + logFile.print(F("hz,SF")); + logFile.print(sf); + + logFile.print(F(",BW")); + logFile.print(bandwidth); + + logFile.print(F(",CR4:")); + logFile.print(cr); + logFile.print(F(",LDRO_")); + + if (opt) + { + logFile.print(F("On")); + } + else + { + logFile.print(F("Off")); + } + + logFile.print(F(",SyncWord_0x")); + logFile.print(syncword, HEX); + + if (invertIQ == LORA_IQ_INVERTED) + { + logFile.print(F(",IQInverted")); + } + else + { + logFile.print(F(",IQNormal")); + } + logFile.print(F(",Preamble_")); + logFile.print(preamble); + logFile.flush(); +} + + + +void printOperatingSettingsSD() +{ + //get al the data frome the lora device in one go to avoid swapping + //devices on the SPI bus all the time + + uint8_t pm = LT.getPacketMode(); + uint8_t hm = LT.getHeaderMode(); + uint8_t crcm = LT.getCRCMode(); + uint8_t lnag = LT.getLNAgain(); + uint8_t opmode = LT.getOpmode(); + + printDeviceSD(); + + printOperatingModeSD(opmode); + + logFile.print(F(",PacketMode_")); + + if (pm) + { + logFile.print(F("LoRa")); + } + else + { + logFile.print(F("FSK")); + } + + if (hm) + { + logFile.print(F(",Implicit")); + } + else + { + logFile.print(F(",Explicit")); + } + + logFile.print(F(",CRC_")); + if (crcm) + { + logFile.print(F("On")); + } + else + { + logFile.print(F("Off")); + } + + logFile.print(F(",LNAgain_")); + logFile.print(lnag); + + logFile.flush(); +} + + + +void printOperatingModeSD(uint8_t opmode) +{ + switch (opmode) + { + case 0: + logFile.print(F("SLEEP")); + break; + + case 1: + logFile.print(F("STDBY")); + break; + + case 2: + logFile.print(F("FSTX")); + break; + + case 3: + logFile.print(F("TX")); + break; + + case 4: + logFile.print(F("FSRX")); + break; + + case 5: + logFile.print(F("RXCONTINUOUS")); + break; + + case 6: + logFile.print(F("RXSINGLE")); + break; + + case 7: + logFile.print(F("CAD")); + break; + + default: + logFile.print(F("NOIDEA")); + break; + } +} + + +uint8_t setup_SDLOG() +{ + //checks if the SD card is present and can be initialised + //returns log number, 1-99, if OK, 0 if not + + uint8_t i; + + Serial.print(F("SD card...")); + + if (!SD.begin(SDCS)) + { + Serial.println(F("ERROR SD card fail")); + Serial.println(); + SD_Found = false; + return 0; + } + + Serial.print(F("Initialized OK - ")); + SD_Found = true; + + for (i = 1; i < 100; i++) { + filename[4] = i / 10 + '0'; + filename[5] = i % 10 + '0'; + if (! SD.exists(filename)) { + // only open a new file if it doesn't exist + logFile = SD.open(filename, FILE_WRITE); + break; + } + } + + Serial.print(F("Writing to ")); + Serial.println(filename); + + return i; +} + + +void printDeviceSD() +{ + + switch (LORA_DEVICE) + { + case DEVICE_SX1261: + logFile.print(F("SX1261")); + break; + + case DEVICE_SX1262: + logFile.print(F("SX1262")); + break; + + case DEVICE_SX1268: + logFile.print(F("SX1268")); + break; + + default: + logFile.print(F("Unknown Device")); + + } +} + + +void printHEXPacketSD(uint8_t *buffer, uint8_t size) +{ + uint8_t index; + + for (index = 0; index < size; index++) + { + printHEXByteSD(buffer[index]); + logFile.print(F(" ")); + } +} + + +void printHEXByteSD(uint8_t temp) +{ + + if (temp < 0x10) + { + logFile.print(F("0")); + } + logFile.print(temp, HEX); +} + + +void packet_is_OKSD() +{ + //uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + logFile.print(F(" FreqErrror,")); + logFile.print(LT.getFrequencyErrorHz()); + logFile.print(F("hz ")); + + printHEXPacketSD(RXBUFFER, RXPacketL); + + logFile.print(F(" RSSI,")); + logFile.print(PacketRSSI); + logFile.print(F("dBm,SNR,")); + logFile.print(PacketSNR); + logFile.print(F("dB,Length,")); + logFile.print(RXPacketL); + logFile.print(F(",Packets,")); + logFile.print(RXpacketCount); + logFile.print(F(",Errors,")); + logFile.print(errors); + logFile.print(F(",IRQreg,")); + logFile.println(IRQStatus, HEX); + logFile.flush(); +} + + +void packet_is_ErrorSD() +{ + //uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + RXPacketL = LT.readRXPacketL(); //get the real packet length + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + logFile.print(F(" RXTimeout")); + } + else + { + logFile.print(F(" PacketError")); + logFile.print(F(",RSSI,")); + logFile.print(PacketRSSI); + logFile.print(F("dBm,SNR,")); + logFile.print(PacketSNR); + logFile.print(F("dB,Length,")); + logFile.print(RXPacketL); + logFile.print(F(",Packets,")); + logFile.print(RXpacketCount); + logFile.print(F(",Errors,")); + logFile.print(errors); + logFile.print(F(",IRQreg,")); + logFile.println(IRQStatus, HEX); + } + logFile.flush(); +} + + +void printDigitsSD(int8_t digits) +{ + //utility function for digital clock display: prints preceding colon and leading 0 + logFile.print(F(":")); + if (digits < 10) + logFile.print('0'); + logFile.print(digits); +} + + +void printtimeSD() +{ + logFile.print(hour(recordtime)); + printDigitsSD(minute(recordtime)); + printDigitsSD(second(recordtime)); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/Settings.h new file mode 100644 index 0000000..bdd505a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/Settings.h @@ -0,0 +1,41 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, An ATmeag1284P shield base for my BBF modules. +//Be sure to change the definitions to match your own setup. Some pins such as DIO2, DIO3, BUZZER may not +//be in used by this sketch so they do not need to be connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define BUZZER 4 //pin for buzzer, on when logic high +#define SDCS 30 //CS pin for SD card + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 2; //LoRa TX power + +#define packet_delay 1000 //mS delay between packets + + +#define RXBUFFER_SIZE 255 //RX buffer size diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/103_LoRa_Transmitter_Detailed_Setup_ESP32.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/103_LoRa_Transmitter_Detailed_Setup_ESP32.ino new file mode 100644 index 0000000..7518c4c --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/103_LoRa_Transmitter_Detailed_Setup_ESP32.ino @@ -0,0 +1,189 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 04/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a program that demonstrates the detailed setup of a LoRa test transmitter. + A packet containing ASCII text is sent according to the frequency and LoRa settings specified in the + 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with + the transmit power used, the packet length and the CRC of the packet. The matching receive program, + '104_LoRa_Receiver' can be used to check the packets are being sent correctly, the frequency and LoRa + settings (in Settings.h) must be the same for the transmitter and receiver programs. Sample Serial + Monitor output; + + 10dBm Packet> Hello World 1234567890* BytesSent,23 CRC,DAAB TransmitTime,64mS PacketsSent,2 + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX126XLT LT; //create a library class instance called LT + +uint8_t TXPacketL; +uint32_t TXPacketCount, startmS, endmS; + +uint8_t buff[] = "Hello World 1234567890"; + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on reciver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + startmS = millis(); //start transmit timer + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + endmS = millis(); //packet sent, note end time + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + uint16_t localCRC; + + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + localCRC = LT.CRCCCITT(buff, TXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); //print CRC of transmitted packet + Serial.print(F(" TransmitTime,")); + Serial.print(endmS - startmS); //print transmit time of packet + Serial.print(F("mS")); + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("103_LoRa_Transmitter_Detailed_Setup Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_DCDC); + LT.setPaConfig(0x04, PAAUTO, LORA_DEVICE); + LT.setDIO3AsTCXOCtrl(TCXO_CTRL_3_3V); + LT.calibrateDevice(ALLDevices); //is required after setting TCXO + LT.calibrateImage(Frequency); + LT.setDIO2AsRfSwitchCtrl(); + LT.setPacketType(PACKET_TYPE_LORA); + LT.setRfFrequency(Frequency, Offset); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, Optimisation); + LT.setBufferBaseAddress(0, 0); + LT.setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + LT.setHighSensitivity(); //set for maximum gain + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); + //*************************************************************************************************** + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/Settings.h new file mode 100644 index 0000000..49206f1 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/Settings.h @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, a ESP32 shield base with my BBF board shield on +//top. Be sure to change the definitions to match your own setup. Some pins such as DIO2, DIO3, BUZZER +//may not be in used by this sketch so they do not need to be connected and should be included and be +//set to -1. + +#define NSS 5 //select pin on LoRa device +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NRESET 27 //reset pin on LoRa device +#define RFBUSY 25 //busy line + +#define LED1 2 //on board LED, high for on +#define DIO1 35 //DIO1 pin on LoRa device, used for RX and TX done +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define BUZZER -1 //pin for buzzer, set to -1 if not used +#define VCCPOWER 14 //pin controls power to external devices + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/104_LoRa_Receiver_Detailed_Setup_ESP32.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/104_LoRa_Receiver_Detailed_Setup_ESP32.ino new file mode 100644 index 0000000..719a09f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/104_LoRa_Receiver_Detailed_Setup_ESP32.ino @@ -0,0 +1,254 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 04/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a program that demonstrates the detailed setup of a LoRa test receiver. + The program listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins + to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout on the Arduino IDE Serial Monitor of the valid packets received, the packet is + assumed to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect + weird things to happen on the Serial Monitor. The LED will flash for each packet received and the + buzzer will sound, if fitted. + + Sample serial monitor output; + + 7s Hello World 1234567890*,CRC,DAAB,RSSI,-52dBm,SNR,9dB,Length,23,Packets,5,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 968s PacketError,RSSI,-87dBm,SNR,-11dB,Length,23,Packets,613,Errors,2,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX126XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the device packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } + + delay(250); //gives a longer buzzer and LED flash for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("104_LoRa_Receiver_Detailed_Setup_ESP32 Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in the library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_DCDC); + LT.setPaConfig(0x04, PAAUTO, LORA_DEVICE); + LT.setDIO3AsTCXOCtrl(TCXO_CTRL_3_3V); + LT.calibrateDevice(ALLDevices); //is required after setting TCXO + LT.calibrateImage(Frequency); + LT.setDIO2AsRfSwitchCtrl(); + LT.setPacketType(PACKET_TYPE_LORA); + LT.setRfFrequency(Frequency, Offset); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, Optimisation); + LT.setBufferBaseAddress(0, 0); + LT.setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + LT.setHighSensitivity(); //set for maximum gain + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/Settings.h new file mode 100644 index 0000000..2d257c7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/Settings.h @@ -0,0 +1,52 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 04/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, a ESP32 shield base with my BBF board shield on +//top. Be sure to change the definitions to match your own setup. Some pins such as DIO2, DIO3, BUZZER +//may not be in used by this sketch so they do not need to be connected and should be included and be +//set to -1. + + +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NSS 5 //select pin on LoRa device +#define NRESET 27 //reset pin on LoRa device +#define RFBUSY 25 //busy line + +#define LED1 2 //on board LED, high for on +#define DIO1 35 //DIO1 pin on LoRa device, used for RX and TX done +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define BUZZER -1 //pin for buzzer, set to -1 if not used +#define VCCPOWER 14 //pin controls power to external devices + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino new file mode 100644 index 0000000..4062747 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino @@ -0,0 +1,127 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 28/05/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a minimum setup LoRa test transmitter. A packet containing the ASCII text + "Hello World 1234567890" is sent using the frequency and LoRa settings specified in the LT.setupLoRa() + command. The pins to access the lora device need to be defined at the top of the program also. + + The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with + the transmit power used and the packet length. The matching receiver program, '4_LoRa_Receiver' can be used + to check the packets are being sent correctly, the frequency and LoRa settings (in the LT.setupLoRa() + commands) must be the same for the transmitter and receiver programs. Sample Serial Monitor output; + + 10dBm Packet> Hello World 1234567890* BytesSent,23 PacketsSent,6 + + For an example of a more detailed configuration for a transmitter, see program 103_LoRa_Transmitter. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library + +SX126XLT LT; //create a library class instance called LT + +//These are the pin definitions for used for the program. Be sure to change to match the pins for your +//own setup. You will also need to connect up the pins for the SPI bus, which are SCK on pin 18, MISO +//on pin 19 and MOSI on pin 23. + +#define NSS 5 //select pin on LoRa device +#define NRESET 27 //reset pin on LoRa device +#define RFBUSY 25 //busy line +#define DIO1 35 //DIO1 pin on LoRa device, used for RX and TX done +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +uint8_t TXPacketL; +uint32_t TXPacketCount; + +uint8_t buff[] = "Hello World 1234567890"; //the message to send + + +void setup() +{ + Serial.begin(9600); + Serial.println(); + Serial.println(F("3_LoRa_Transmitter Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1); + } + + LT.setupLoRa(434000000, 0, LORA_SF7, LORA_BW_125, LORA_CR_4_5, LDRO_AUTO); //configure frequency and LoRa settings + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on receiver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + Serial.println(); + delay(1000); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Pictures/ESP32_Bare_Bones_Schematic.jpg b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Pictures/ESP32_Bare_Bones_Schematic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19b2873de7f359235804304b780c2826347e98c1 GIT binary patch literal 38883 zcmeEubwE|!w(q795Trp;T2e}y4Js`l-Jo=LBWyxZ8a5>%-6_%y(jZ;Z-QB(STYl%< zbM(9SJLkUp-uv$%Q}$+$HP;+-j{J?Wfd7Cmf*#09%1D9`5D-AGfqx+Q4Cpxs6&VE; z1sN3;1r-eq6&>RN2FAU67zEfjSPzH^NJxkYh=?9i&{I7mqa`OIqGqM0Wng^F{Fs!A z?Fk#x6MCk{Og|?VW{R7E&OLZAU5;vyj7BEZ{0 zR3H!n3b5Lr3;z2T0wNNyUNrQ37?=P-rEO$vVrph?Vd>=T;_Bw^;TiDmec*?WLBY`InAo`ZPYH<`nOWI6xq0~o zU&|{htEy{i>%O(McXW1j_w2GZPgR?*Jg$wY7h>VPcjP{c+1VlGrAmJjTJmN&f6IVieSV#!K;UVFIz@Y1#V5T(0|MBoY z-zQ^nO<4=>MAv=KDnkdHK2(NUjk{!PNzypU1H#dFBb;KoYkQOA=Dd*&vKZDH-BEwV zYAW@Kl%Pi3?vZ`kV%e)&rG2r?8#_gA)@O^*nI}7B1XoAZ8=doxoP(aA;f!D=(Epv0 zTWD}!bY9@4sr-o)HbN&~jV_Ed4J*uNd0NDFn&nP>cdx%99KH6*S{u6{ZnVGqdfrxI zh_1obP=6lRHkr~!xYjdJw!0FQt1VeBWu~8cSIBEYWqmR;b`D{YXSl z`%;Or8?*3b?$=si`sX`DK3!Qu&SQ99|Gs5w;(d6Zx-p*l8>F{N(t_4hLcn2?@vBU8_b0g=~U(S-X%I zao+UHX^PT~J6~qhjUI%)6R8*W=c=6vTtpR)t~jTiBzVFeX4^jSr2?Au&Xz`+PAoIo zmR?MEkAHwWxZ2F={#ub6(bs#AB5kE8&`ShtD+DU#gvvIivykZwc|LYhmBwM%T7Pbd z%tg!94W2F^Kdq+_9T2jZH>Xs>ctY;)F*_$}@X2YP3CZQHgQ2C7^NoJqIWpE?|@@XE+Q5wI+-foZ4BZ*)cnMF z#3C`qQFx*U`)Y6G9&V}e9A&2=M(?Y*ft6%G)#$dwNQZSff|9rXW_(=iq=>7Z{P%p4 zjmMLEOoA5-_}uuo(JAyFkq6S;6R$+jeZVDFfG6jXv#lN%&lfGXMn~r4*t%}!NL~=> z=GTSBPx&E{XmMaZc)dZuZGDF?>bd)c>%1(#X-xNX+t)LGNPV^euSu?~veN{AFl7XT z{!a&UNu~+K?LC6;+xg70Paj|_1>m&Ne7Th~!%klo!JgjvZcfTh8bxp1gTVPY%X4gR z={{jhq0WFf33$s1a^#ZAlVrixT+;dW*e0v$1P=0j;~s_@Tf7

5~d^d;Fzlj=uPt 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 literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Pictures/ESP32_Micro_Node.jpg b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Pictures/ESP32_Micro_Node.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ff1845912bdda979f9f7f2cd3a3f57dd28a1b6c4 GIT binary patch literal 89760 zcmeFZcU%)&_b)sN0TP-iNG~CTP^5za1R-E3!O)8o3nd6hFBU{F1kglKAP@uriHI~& zA)*u;ARr(ppdwWPL6L)~sHmv$4xV#<&w1{>?|bk4PBT#QI=eaWJg|83X-S-O!V;-*r1cd9)}1?C_6_ ziu`krb&fh56zdie6_2$w0ki)>ps~Me{hjLT>g#UP)!(LTfYsCArf0lO-w410=NNz5 zWe*xq@)vdmu`2V=_MJC0W(xu^Fwxc5*X7Ip(?%b#4D2r)31UdjpJgDR-O!vrbuu>O zY@y#)oB}bF{imG0QTH$TIS}*zB`*W9Ap6gD`N8%Oe_<&Q3;k851}t-oEeM1l0DuSL z6Cj3z4wexC3vvIlQ%_eH!u^;3pxi(G2bu=({e{Ot%>Nh0fj$*0`_ncr5O4Ym`+*o) z_NN}ODM0cc7`Q0-yI-Ih08;*E84v={e_+0UU|9K|`a?jS0{@V2)Y+IHbg%(6Fckv% ze>776BiZk^!SWiQ0xX!)LEcC=Xd`bQ$XJl)CN1>o?Bg7Q32 zjs-FDuQC9j4EB@n_biVV1ONhvH|!|@F$TmNO@0Hh)P}|lrQiuB4azs*2Fv}0K?ul$ z7`Xw!nV|5ue$W&}P;L#1v7j6W;sFrXfLIwUvoVVH!T^8=<&GeB1+faKSq#KWAXWwC z8~yzWVl_~{p&ts)tbxDvgU4F`z~CX^_uDc)U}MH^cyE1u!_t4zCQv7+l>}Zpj<2sr zUzwMi=i3KN{#Sv&`^MeG-LxTGU;kb5_@DR)X!E?B24rgE@HdfxPwQMj2SWcn?te@F zJ%s=opnwi+`EAPj`s%-D|JxD8!FJm}uOe7`XKUMkl&}enjX3Nd8-(=?ij516ip1(^ z>u!wM|BPb6wu}DV6#yMPW`HyC`T9mL|D8zS;84N5@PFiihrqADjyF{OjW=p;IBVl< zISm%d2Kfxg8~Vk-Nfh=E9e?L-)Y+gv$LUY}e`4>pp>$&+gM4UheSPDu@c%}=e`PR0 zdw2e$&_-W3`u<<^m%iV6!47QPcj#apYcL%K(_pahIyiClK;FO`^@YJY8~)hvPcG=& z4WE1gnGWV{|E7fdd+3m$ zB@l3w%3ke1nSrO#Xkl--rcQ6zGc=q51~MoJuomPCtf+AOzOQd(M8Ru(BPuP860C?z8+vkoeO!1(zE_yt4+ z1w~{~n@}?U+qB*apy0p}U@sp82S8B}J``lV16(Ho*K0P`FaJg0jgenK5RTvjv$r7u z2p@P1<%7Y&l^2k}n>HT|#V>$X62$0P`omqsaVeXX^>eITV=mOU4Ngl)8jw@Vu7h=@ z!1bhmQw(e@|G~f<912td-gKdS|J5#-g;LT(yTJT0mMQw{V}J;l2SxFr01MzH*{;KL zu{1V)G1Ge5*v9N0{QF+a$4zM>zVTj!EhWaEQi3$?55Uz`I|-EuXM($?y2m~~yU_Jj z`A+(+Jzh<5opKnK?$clFeNMhR1Gj75d^&vo4D6u7ao(p-#=C}XvtFT&QOga`Ro@SO z9ge*5-tLC0>8bQH35P923by@P^f}J>D6&>1?rJQP%&hUb6k8tg&7zV`Bg1sPDTQ_StdQfnqP` z5WI|i_8(DEEf%8=hp3-r^Gsp8uRIx)OcU9=StzqPBs00qd=96lDIyiimPcpn;iZ;b z^pqa*Ht>wJvUc0abWrdTB>@&Z#Fsd-eO=){c804frl68CRA}2 z@Q$$Z`;q(7cHgRG=)n8TQ9x_wv?`}1TlbrF&&x8x;518VQ1ubG`)p6hOh|r9&_7?m z*~x4^MZLf|pm$`JhLh%ov(5R80~NP;y$OLydKnk(q1~90yX{%5*j4;+Px?pGtxxn# zyZWrGG-t*ckviWiCNO=^j+~TO&NHj1=LLv};m^uOUirQbu$*QTq&@o%W%1JnJR#x( z{+Bog)`^09zFrTH^7~_v|4Pd>+W9RH?@LMPzlNj?51U)93=caC?v(zi!mw#V67LQF z*ke8DbN%P1MUAg&2hM)@Y*`dSL3PrnYTjaYqk_2s#R(~*1h5ACjrpqPsdhwmZU_I{|6 zlbtbWDolSiyzQt)>B(@JeigGi=4|3ta~vMeesc3dy-##%C;P_C$Go(>=WMCvJMEN{ z?K!$fCRKAIQy>kiErzL5E%Dy*)=ap89^mQw;I^ZPj#7`4^MgCzPVaUf80FHZ6U@U_ zo?z~LRSLVrcqsQp$>uFjWUJ3^{|g&(&>hE|>b_5E1wGzj(fUVcXA(S=S4z9=mCe+82pF zO6Vi~!WB(*IO;7#WW4aPdDTR0+%Xus`RCS(OIOe5YuGp6ubnT4%<&}6OrA&ki%zCY z9;F|veZtVEXa^k(axD9WHeSPA?NO;rd zR*AZm?UCXQ1$PX;QANs_H!BdbS9gis%Q~J|^ka?bS>nQ;R1#K|ENmOUn?HTA;-S>5 z=-%ebp=*PYh5HZo{K&^dt~C3O?`<9*%2zR0X2%>j*JyBV(c}KCpI_Z6bGqYdx^ zo-w?^`r8d*VIQUOc^1~we_J2yxNVW|G_wYnr0>`IjkqL zO^80_^NzZ($=SoIG9>|*7^gppxY<59_A0JUOiU4ZD*v2ufzi+~y|V0LpkQw+(h|5Z z8v|_PU#V*(BW<~*>!bgQRQ``39b$XF2JwFWE#lyL)k9BiZUHgK!&?mR=PMiD=Ug`b zeGJM@<{J>(^T0e$JxLxinlx7>@@+h>S@ zv{Y4(G*GJy7+wCR{~E2|Atb*5n5PK_g8w4$^}&WcU>wM2`8yKCiO2BkTcs!)Y<3O! z_mJ@OysG-$4_fgNH!9Xxb(t~qH|lB@OUhs6F|k=+go@<=2|gwX6T&I0lg^EsPtAa> z19r2CdL zg;E!3eu4ZfhL}ZziX%UDCxn{F){|Erc<4`V()q zS`i;C5Zpb6_ZuPng6U{%Gz8A8$Q!lhd zKATcX11wWN@wj}u2jgfiH?d-*$4YiadN~|Y_S@SlRO;$sA^XfhR)49t`=FmW;{gp}b36dz%vIui_kf z9x6vHtx3O4V~ce61?gsf7q8+d@el)!w@npOsf+wrJ-?}c zv53@+U$uuHRt%Cnm53^4z;HqLj%lF!8Q9%@4}hAUSL8F{+BqdJs#^eQy&*e@PA z!sKo!MLkVkFswaZf$dqb|Mu&dL=V!Rj0~Vw8@5+nFd3&YrI}ygpWe0aw|fJ>Q0~z4 z`3t3kK>&p4`2BB@whL{&J683UwrAJKzFbV_e%wzI@hXj9>2CNr_|r{#$t^4p>FHo( z)zr9(=o6mW>n1_49?}AOhyvomV@MoA0Fx?7NJ7etnkl-+w85S5i`a}FOQ4vw;z&a^ zBv!yOez}!hm!+p5{WO?DXNI&Q{jpktLUmz0%iDJ9O$c64eR5)%3?TThWXZ`aY4b?=(F-GoDK4HRF%S1-dH3CXT!VYX)2)4M_ zs@*A(KziJR%CNUj;qI3H&P{2tTvheY5PM78k*C!9reT_bR$ZtXS+UNHzPKsi^@!c$ zNv{y8Ijy>&9TYm?ue!}>0*ANv|R^V0q*ysXzM*u=S$LKp&N@)@b@FY+e<7)W_a+REuWcd|!K z@w3vpDA=RHPwrZnG}6S8FCMxTX*R;Af4Q>7ukl|_cP!eWL zektp^n$@NS3*4oCF$I_7W!y>bRJn|5q1{enj6+#cZ!o@RRkhA=chL)HfFM|7!3`7d z0LJ;ZywG)C>5!@E69IXwSK-73rOomdJZ5i9?hB3YQW!b%nVjzcJ9C?D!5 z{-MtPp;yqpA0p|27IIXeIUgz4DM-V>GAoVjDN^869k^Ws)gtx}8WsMisbd-Mj0#NS zK*H?SLUL}dsHXVT{=G$rR~^wEMx6hFkqjij;@nvI(z$!fru6dHc^u z;495TdJo-w;TrlmC+$KcBcS6)wblfoJLVIbzdfSIfZt-UgpQ1~BIxBI183=EgiFKy zZ&$Jz^C}*7ZhpQZ=-lyDV=JAbF;QHqw{3W-3Qb`Q*gHCrJ*O z?HSsZEBhhr#us+&$JFq%qOde z@aK8Y$F`5Xs6OwRMOpBPtiBYHwB`PIuC&5*LQ2LDE}0vEM?JyMx=mO<}=h9bbg?l9=56Q4Qu;%yE-wk(jp+^c-)Web^=(oTb9aB+&H92}Nf zVP&ObWEJpMaM@AHu5GgFXfH5xo z2C@6q3r}AnY)qH)&RB|i7zNiC@+h=l#+u~$Kj7nuwl;Qa6lSS&%!EBH{qNQIfgdDZ zr7_!2qGB&=*d<;Z=ecf@a5|W)&|4RN9_9~yJu*j}_%#S@vPN6c1Mfb~m1O zv)!K3GH|-v&TD1Gov*Tx%4haCotyja{EA?XycXs3=$fC`<~5)ufKW2??yW4Y>9#k~{W6 zNV1-w6Q^W=f^LQop*@H+9&b-a+E^cFcWOWp3k#StuWx5NF!hsiuwrf)qdGgs8Dh7~ zzR^^un>H*ZMU^CnE^alBdKr;AsU zmB01hQFZ*Kot<@tjMkS-U8wnETSfI31xl{I{o~=)OAg0f4%(+bI=h$3=@|%#ApFGQ zOf?&+uQ|cj&pZj`2Nb69nm1~Ek&Oxuh+?W+teUlo4*M+2L?m}zpE%x5>2348uxP3N z^h(FR$$*#I0+{_FQbErPcirKRyxM}v+_Q6H3g7M6bvnhjm+%MbGOtSOmumX8kLP4y z49`2e*h$t;cM6sr+0uFRd{%8>_0vh~E`|1Rt5xBP2Q{|uvg&s%NOc%p+7z@bkn8+f zcJlVM-9Obn?rb})`MC@$GWvw@P4t)sM{ZXT) z71$(ayIgE3t>;dgI-*p!H_fJ7u_{GxB;Aja+pawno)mf62M2GZwq`!iJ^xm!G=2(_ zvDq`0jP@ZFsNIe&mK$@$_m!p9W-C#ee&MZR-kZNg;9AR}$8PrS`jSqvY|52R`w+M6 z;jKz5KB`=_#|5e>M9R%sb_wP=lym7$8cdXKHebUb`KQ5M9X`yITn(mHB*I<{_oBw| zLz;0$8U6+4QRojd(Xc&XCqd(z)6Fm<{1wN|(& z#C|0V-_x?6$DVq^q`Hs-GZFlzD?IiQsjuE${f7FnIt#fhT@tT?RbMdk*7DWTWjziz z|LcznvVz~NP$84~COo)_yJlc14fzI#i zky;M7D#6BtGN2(QYGRMSI6Ma&oky^(O)mZ?-Vz_vprlVgZbc-mvXBs-G9voC;V2%0 zWee37n0($*ZrDHVATf-%bHPBHsc49_TuN2q#^&!oCmjx7ywtC!tKy1-uWS|&_@+LP^8m}y&9wQD3z7J^6_sd!RoK`Sco zK;9n2mhB&Q-;UP8Xnl3|otGi0nKGx1>(9FZ9d@00zN4qF^z*9v;2DI09aqMJpCKdE zXm{X@w*qJ&V^hsVb~rR4gUcy9c0a4kR^O)(-wkjB_6Micms2JLsQlBVr;|d?SGzel zbnxh7uute+EmEPz*xlFi)&bvLUb z$Wn`O99@9C_V6&}>6XwErALz8-kd=nC&5F{k4L|8)7>kHx?ClL$Zk4y$MN#go|Xg? z&P6xRH$gvp%kC89zmt8Y*!-L+M>a0jqB6SZoS`Fz55Mm_%7#= zF)0<+`^Rk4+Qm7=gY{1q%sj@Mq;B(XF7-QNP-R;E?Xc~h*_MjQGiPQVp6AOJeO|IA zeBjK*`_JBXuPt#cT-Np-obOPu6RH^;HnLK1i&uc8S>i;0Ig9d^+Pd8Ks&S#p)= z{2|NC3SAx5JkChkzDD(*9k^u5TI~oooOL&Wdb-1DO)btqs5M2mBG{Wor?#am6k_r) z<7x%^QCl=4KU3-&P@}pSR(K}XAg=r;V<6`y!c2T@C`hjXxU*baJhxjP zuuE+sME-H%GC}J({$o5Trlm|7rAF7k^jYYKUZDh7f$s-lYMPl#>o$|Z;$zh$8_MBt z)F;TfY7Eg#H5UE_zWStsHZMn7dA4HHK|(rvT2MuAzM9*Mn}uG~=;6a>990+CGnb*k zh=^IJZe?b2seXm(F9qfVc3i_Is=h`cv~nfC$L0u-HBw(I%e9Nm?5Eb$-uK3fA!&uL zGBiX@*g`wvk=J+34RFWoGBpemlm~GW6b4%2wfi%+Whu4$OVt8c>*uBNdR0u zf8=-u4~q#9toC3_j7Co#PMZ*!Dh(pj?8#mo7ZM3Q=5L#CX1l;z+m=p>HnWTRr7nB6 zA}Ci}nN5}g+E@GOA;sJoXRTzbIuU3~ zhACP_6~j$_-g*r=p=vNj45Ds1081%`l|^Hts~94rQgQ|@9Zy`L9-0==vhWD1FKr@q`T7K93gq*?k?Go5QSDIaPCg(Hl<>tr8Ge*AOBujBF3$rFL{ zB@VObW3S`V+^Q@;ZL>38qIaGB-e`x?@R7dVB)j&9k-vMm$)kf`k6zhj_Og$r@G!I` zsY@Pfoqr=qw_>DOteIpyD0XPZjws2<$$6t=@Iy1`G-Tk&5|>BP)D;hnS=k@=P(frJ z*y<3IfW0Nz>~$aVdh)3u;#4GFcysyua|Ur754mewBR#U|23a`sKbd#gL_OoTlL<5UM~TLN$1+P|!s_fZ*GbUPBIAQ1^HZ z7Y-CL3dAz{j>_Q%CE}alq|8`cl72Goz*rh+t8kNN~tN zk_%FoJ4T?h+d1 z-b_NDMl>7oWK&H%$*{5&3wPZzYV@Vn)L)xtgP)f-+z!wSId4w=DnE*thuLdh|9t^SC z=ZnZ$EhOoBkB}R`+)rezR(ctpJ1S@h&&6{gX5M;r_Y8$d;gxcTwi#OL}dlp?P$3Mz92}4~IEsuYgQGK@m z;)ge3-DkxlLW;v}nxpx-W5ZtQ-m>zl&w>-x-TdPaXZ-mac=wOE^oPIVNqL5lM3@WRar4fj8 zxISOeF_BoK4`bSsx6wGl@i3?F{a4N>d@#(#R4?m39j(+Y9vW(63Ex!2(v!`j_=bwx<51 zw4&feZ4!376<$=;JEn&=ksWHSZpn>q**fCD8O1|dNkLb&GHb9wFTY*jQuI_Lw!?K{ zOxdQED^H?6qcf)^g09sowh);Gh_)%iV}r7kb_dUJ%RAFr<*BXI9HRpWypKP5glhyV zpEol2Ox^vVD3HEAyjTl;hy)av2r;uGQ>2v#jZ>m45bKzESsrJusNYSdw{_18!Mi;8nCUsi`O?$gcw(nb(7br)@67!MaPy)Nvlc zSOr7OD6dn3W#skkcBn1L$wNgW@2}z`h%ok*#)w|LQ;#m*O4^PQB`rV5Vy*0wtg{T2 z#Gwn6h#hw3m%zsemHIsT%zS+-96D4gAyheVjQ#52SGzIHl{WhU8c>y-%g#+>F_0%| z3`D(>Sx#J#!)G`SmQm5yof{awvXp_wPdf_$@6yT)zEF1gLw{zGTjeP5@TKh>V@2N! zr_20^@AxJu7D4-`68!B1SsdIwE4`ma^4X-E9?zL1D>s7HCp!XlZ_K2(Q40<#4-`)Z zEZ*&e(tfg4fh{p~2;1Fvi`{uxfc4#)n@mgWc=m}U-AtA-&Vaa&u%@2$)D}D$kZ}iUAt69^*nO^t6P$E*v*6+t`L3YLdVp@XCv?h)0^U3-Wl6-Y@Q?! zsrcp2D@|YZ4?pDGx*BnIuf({<-Ro^*z$5VkVJFTCwB1y)(Jd^0BX}DXYQ&3@UFxDr z>6fE-WSlIJ(|jr3e4%{D>33-*MV;p!RqP~pn`oSls24qP2Izh4eD8=BitoJV9o5Jx znH4rAi(T8xPBczxqJ95X`I+ zp17^}LqKw%txHcSjCjB_FwAbIhd$U!6{d}`6Qm%OrEk;5dbps-sGgF z@m2$F;*UKYIqW{<1Sja5pOSfG1Elg}wN3e*-(AG26DT(rBF`&i!=sd>-hUzYiQa^( zcoPavzi-DGr;)I5bpe2)q-MCgTe987qH_C_46jLtuRf%uG|C8c2hTa`{iIt4Io35Z zZaXLCa%+0Wrl$qK7%jF4GX5I)Q5}kHg&iAmcug;AQfkDDFHO%W_CQx`vWKiv@CvRC zIml`*n((43wV9GWbr#^~Ri>x}8820YYv%Rn(6+wRVVB|GOd=8Ar!9uW)pqDc(qDfx z?~XCjf=|jh5v?VN2Efe1p&TN0+nzt*r8)@i!>Zwdy>()^Jc@HwcDzi>t{~YjIYo4)*b`5D&HPqU_4!EcG`XOL(K~_36 zG#Lq4n#~6q{hFrKHTJ{0KW#P5MbX>@k6OiKi@8pO+wv~FjhASOcX*P4 zXzbxHUn^}9X?Ow=u#_a#<5q-hrl=V;*arnidkbs1+E||#)sgqWR|V3g);h3e3ig&X z9ahA_qZ2v1GAbZ|uNgk-f?=VO#WTH9_Z2qc6`QWq?&vIfWF7%Yyz6LaRfWko?*O&GLDcve7=RWP`fv9ouepH0?TK^ktuG^Iccm+_BDY6GJai zVG&~AuJ-nO_h0Xo+bs_(4os`keHI;d!uhM|q35C#r^Edp|_@OIU{XG-%vPKISMl^HpiZbs@t=MS4)!7xso zy)m`e*wShF>^Reoem&P&1k&XkZ`$I-q1Z@>q@Yh^wI7U#Y{{Ge|!wnww%-V3?o-E*IhL^KI*@eJKFYI5UQLK9s8 zX_I)A09!6!oV=QH)ofA~X|sLKgdHuOeJEpJi>Fm@$mFW~J8XMIpGMQ^Chw)5AYWu% z)9s4~Um=8xSW4~=Q(6X|1*j3~VLQ7RY9ur7VwQKFRQUe(O?TbbE~uvU;D-6_qKb@6 z5c_DQWo=lpQ<)RCm2XfsQq0*>3TbRbo@8d@_&G7r=Kh2P%R-GJQzmxfJph^E+y4v! zlRNCXxElx8-;XvYuLG#8gDyl9w}`HfpDbGn`=u$^>N}!eP?5h`c^fIhqs?g}n{9~w_ME}qkoKX5NfG@lVKaEAT%N^;F$dklK$01!}KkRKPk~VN@&P_gl(*#bwlh$st$KvT)T! z1}wiV%`j%tExn9fm7f_n@x#8Vp*;MLw6@#@6E{LGVbeGJ&5_$r4hkv4cGb6Jjs@Ix(GlsPQ0GXb063iz&|9a7nsm~VKw&`j^fEulE}tAX(xog z`h^ChOgwN@abY|((uoJ$;b2;n6?!0vuzZbPB&1M{7>Un&9aS2i*710Yp*@*(yRs?g zX76j;d%6`v%6iA$3lChUdC84WiYm?!7d_}SJ04sg?{CDwCnR)9>`ck|WXIP+!oWo* z#}e&j#cOr-l&Hu!q1l`OEDq6zT1@+4m>-@`?LedBZ$06-94S!K>LIvR&u+fy*p27= zM8?GLVC8iepn|x8hx7Kn8B{Z}pzXL8pZ3V9k9(lx)H{>*x)_V{WzLMMdG+tzE-UImK*PK|SkVqq~v;LDioaOF|V%Imx&TdvGM8S-|Q;QDmBd!?_ zDZW#<{k2*@$!e8)QqGUY|LN?j`|v+j+l4PCIv$@E(tl>qd0=ZlL1*E_lQ`x{;brvG z)?fM#X&RSA@03rY_1TyA@2HA4^@1oVdpt9{#@r%2Z)TcXpsfC+{530%Dd&ZKLJ_+x zZ2fAGKfROd9-XcOYZ0bLbPn6c2X&m?8Q^c%nfi5Wm(ANniMz#^TQj?pA8xh1f1x}D z>8WKB8p95{*mCaT0b*w7gBXAgahKbTMJZgCm3b~d`~6n!MOp&2)GAeUSZZwdfHgN@ z`BY-LY~ej#1!lF`ObtlI&Q4vD4z5oYXv)I81+eQtqGG^ZHmoCf_HOV`@x%8&5>Hxu zdG~|4Zzp{;mFRPe$l-P4V6rParo5;XWLfIk+wAFM#cJcUt>uaf)hkT(!ibUw>!@cCT6O`UUS93Q;FR3?#~$RMtkk> zDyq?E=5faK6NaqZ*c15=lz~(t&jm-b4RXPs$Sxz$I4+t~coKkvN267|C#uy|t^EXe-E=EoSrzzPvJHquDSS0zV_}GXf zLK>f$I_Vi2LV70+C}XGg7&Iwhpx<3G%P7kezr=a3RA^W$IFTvTf=gIRyd)>5k!6NV z%FW0kpSrG~EmXBVrA5x@Hi~Hpcp=Pt359q{1J-DvfVB?bZ?|37t$p(m=BdrxS=_F|QlALp#j3_8NvoJ-@RpN#+jlEuvzAbu>1Spq(z7>(j(v-A0c1e%ptVwKQy#s+akCT@a{I&S)~P+> z!sKj3L)!b?OdO;Mk)QghJzdokD?p{Y0s9RmrQ2}M5vUb!oAEM5EL*>z7%fCQ+#`o& zzMH=pv}FLikFf)^b8jvILGI&<2hreX9W!Vxu^!JwM#9dBK7WbOS!e57lRQXI2A3iF zSc)o)4jjSv6IqSt)&a8b$vp;%ki0HCSQc5DGM_V2gw4{$a~z^#!IwcRl)F9AT2PVV zYv(N#eb<4~t%CRNm;O*nLR#MTZMast4k(7&dR~*#tRQ{I_T9}AtQmNvjIO_NHlpXv z78S2ht&rT%E%Jnu!~PStG40Yt*9|n@Wq*~v*Q3D^&J3b?$w}7KW?L$ia{td4uBJ z3z2l=rArNm7pR7mA~x@-R)~ne{V&Li2a=qN$Tz;?5`;<|8pZ<~N397M=N3(et5=?O z*AhN;jA|9;u6VFSb=YpIVf3QMa1rPQ7($BLL_~xXf-n5CcKwtvHFGL6SO>1roLW{R zp9CZ&N%mQ!@>h1s5DLb^3`#vde>y~m9kSmlYh$?@>#&%z|9j?Rb>#jb`>U`C`>M$b z@J{so5_*K?2>DETu9ws166hBZ$)A_4HR=`%JUB?^%Z8s>7%-x3E&J-w0TpwGKZeI+ zh;ABt_%p5qpmhn$1+32OIPgb=@Ew~*A@zDuRwo;}Q$9HYSOs_WL@2ED6_5Y)hw4EI zyGgDr7eB%j)eHShN#{j;B1eK#HHDvNT8_S*FB@vCF3~lkVj?D;Gm48867VGTG^;P& z-XrU`BF*#jt2lLyjFE2f+x^HEU47cL{1*yM9Upc_>_y12568#tw)<`X!vo#=RMZn)H(Q5%bD* z_~F`3s~n_GoWeqPK10m?-Fppk7uKpI+wQjJ={h{#;GlH)1Lz8mH(<>oi;kbfy)78i z`Q)|*P`Z<)VaKLTy9J08$`N)@Bdw$w%OGaZRDrb`=8bQ5c7i;cG#SU_`HbvW@*I=F ztZKKP-fXw)+BMk`rs+L=&o)Um>&+KRqQdaD=fs4vw zO!tLcfI&Ac7DyE#QzRzX?sj!qv*3?FO$QM=>~o>L*OgDO6py=;R1G3~L=H)@91%+% z?k0;OmH?6qAq+3sH=DuiaJD8cL@MHZ5(fG+@iJd(y!RD7eoqNX%0&5)D93x?q*Jqt zi>~TDzw?CT-gBB_NdC$*N}f!=zRpwG5l57Hy9ljJR~J+WB(%ErSB^=ivwD|QrCM}%DB z<(yKsjp$6$kls*O6y*Fs%RZk-u&MnVMPGw8C~q6;+>WA?x3Y zxW&hN;642)nNJtqn`~=1v5DLrrW_ZZSoPF3VaEmEhc|6|hkY%Of?L~F0>Vc2^7$sD z(iYsuJ`k-Jt$Z#PJ+kLNqZ*DP*akU`C8C95M*#?)+Rl~{rszr2d6Kr3Ysd%%+@N*V zgG%g=Yt<|75GRrvgQyLRVob%|yR_<`e4(3S}qzLuN9>xSO)8H)?6&WAiAL< zy;_I*tXyHsWy^cSVD_HcuSN(HbHx64<~1z_707Omp5)R==#iXBRGW&;lRL}?-xbng z%!o!o3Wp5EwN`Dd&1sB}AkLwTl>|BpEYAF4C)FaOXVKmg($u_9QhaB?}G zjZ<z7zVyU$BU&A{oX|o;EmPKWPZ`Z0bcN3^A z=ud99bh?m(&`7U~lqA)mt~loiOrvLML~0EClIQC%r$11TZD~w03Sjb+S0`x#51v+Q ztw~APw>cLH!eJOGp__E@dpu9g3-;{}Q+w2LLzG7t2?_j<3mv4}kWtbo7D<`*;O7Wxk)KkvI*QZtpFqZ4k%(3!Z3Esoq z+porigviy#cUlGCsFl>XsNr5!=^nReDf+(6?&NkZ5X`vA$4^{hrEfhT@g(e=re|5) zXyu-c+uwukqc=xJzKzB{0MFn}O8rri`c(q&04R0FdBwy=(6G7a9Ky}z#l9g5^tpGb z$D*#R=u+g3k!7dCI@N|zTf3VpK~I`Hr}9hBb$`wNFkb5Aa6?4cmY=2#;R-li(=d4= zaCa|UaK3~xDJzmumXVo|hMF|mge6LrvJKrI+x}FGR=^JSEo}|$2>!zVPxbblP0BjzrX*l!_%h`JDyPmakfrcv7t zyYWJfYi=01HO0gu68ylk^jcXNMgC5cwRG$iwrM6_RgX4)tn76_5C6dcX@XAL2N^x8qi=PuiWi>kuYch-4oIg9ATTU^V^9o~ivKgXJ#oKPNK2thAN;`D4($NaH zKXAM0m@vFPT*XULxAd)4%V}kWXmpo}ryR{;&P)~}r2h^Letk_Z#IyZH@TJz9z0z_3 z+ZzA+;xan{r$N*Hr=e~VcCKWpq8e;2M9JW_q6h!H-y{WpKIv35JzP8^WA{;Y?2$lB zcb)5WtX0q>`3E&S`lu-fH8+oH6-e}6H7O;xp6E6C>LNsIedGd*fp|jf_Yg*$Um&2f zw$F?J`Lx!832o>7qIn7wjR5ew8=9Y`MkZRd1@G|G6q$QWY4Te9WK?9Y?Ok^YhoOW$ za^Be~Cca`&aJEL^|Doty{F(g!H$F3LPVL~B)6C5IEQg#nhK)H>PL;!)%9*5unL}H| zY!ponBc~{qB%P3Qh;m4oLlH@mO2sFo?{B|wUeh>v?G=QQW9X@g!{^ z77CIYAE9spM+_qeYkGr=CzGKE-HJ0X0y1v$?t>o*Ta|RcSmK2+u%_IaXR2QvHKR_4 zZzOVv?f_NN@$x_9d==h)oOCIU=>k2=A~Mq0F1kyV$l2tAf>}<#ai4fupBboy8f4t6 z(~%qg2LmwE&;)(C-Bu*9!3?8HLJbn5R*UElKz%khr3&lhHMAIp2BY{9nt<1B+{a09 z*a(d6(Q9D@Xxi=>O$>k45L=Pbl(z{2SC5dOCL661-)gfakrnJ(aoG4YP#tuxK1f9s z2PjpU0qmlDn4A8{yU*Dz@-O;uxGQKLaAfs2en`u#BOjE-pa#29YVadg4PrZZWbFE2#=qz||6-)Fu_bQ$JdTGQyxfnIb`< z=4L{*YPrgvN~R*jhkh10eX*$ia8~61zwaNK%+!`(2);CRd~j{+rj(F7HTftPB;>BSdDbQ3%lv z%{|tYotFOZT1ald+JqU*xUp*|8;{E$YJ*|NFV zQmD(NTU6r2% zPelqgRFGz~(Rte;=P&|lyErc-k)dvZ={E$xfIbgr!Z<}_nTjncK~3xO$jcc`fq`vC z`tU8`DGx~h{HKjpuuNA9#5V2t@(~-c4kK+kVCk4^T=+2Q841dn;D&*BJ)#jYn%mWC zi%`c+&u>6DQ8A-|0197iMJJ1J6af9Xn+Qt7NHiKf|L2C@rpv(zmwa?JJ}O{?+x z9MGEt6Bc!~0YgB27}(l_lGb=kSQ1Zz6$(B?G3ol^Q*iu)*FKU`S{2T7Jf1JuxmV{~ zJ?vJwtvl9_`Q$Gq$2Uus^fC=q@kodz7S!`^IoR#rI^}FhD$L3uB~=(vgb`XP@jXt+ z6)Y!vf60k`KMWAg1125}^#qjo0g2qS{<2!ur~VJY1>emGIX!r4?G~f%iL6U(GxJz9 z7MgLcdCG0mU86|#nJ|&jGVp|pC5Z?c*X#LHI*JiM!(_@tB+>WvF7oeIqmG!cu{imI zS58XmOOsJSGyy4XzF95Je5PO@+USwW5Ag*f&&YaRFLTB94{cSNnCA|n{$xmwfHkzm z*1<)sz-b-`+`*(Am~p(B7ptMFq5Lj&C8_f+nF0u|ESL1}D-Bl~z%|HM;$TKKcs$M4 zPrkM=cr+vccDK^y*?;_T&2FX?RKoLG(KLaI>-@poz(W=iwvYS#x9c|45aW6_0++!2 zmHs30-}C4H11QD(53r^!`AAt~6~?<#cD}tCG%T{&VZ3Cy$~3-x(z&9?tw_$_?9L1X zmHGPkx0F$n!!4)TXt873iOumTww9BT?diX2Ntxx3n zF_VEyH#QgN#BvN)*xEwHG8#sq`8lt0Se^?{@9Wl4%(fFEy4&6}H|s#T_t$P)$jUDW z6#i+`4ad%DOw$8`g9Pcl64ux5e*5ogW?;eQ@Z<5!vIz&Q@JjE0(|v=IH}DIKnJkwt z3C$v#jXFZX=5~Gz99`C&B84lYHUqLwq;}p4H#`tc9AD8K4Pk`o@{RHE3Cxhvv_j(n ztz5}pgl=Ntj@JcP+@jKN_kqMp6<-S&(AUjB@2x1fEBVH2^f}c*if*g9^ht`D<3JVo zm&Y5;=zGZd8GJcBNS`EW$0X-$WRv5roY?g=AaAPmh-h>Aigta$T`Jq2_Ex+4aF&*wQ-^_3>r{QKbI5KYD>ST1@$j+@=kE@IKW}sD zgx}f=;W!7bnbe0qUCC{+)A}g*|qsR0SFzrrNxL9mh#KcI90UT1X~QcrNU)Q zC&r07{-mR~fU9S~_iTfu@n^ARbHXdBT*3ud|AXz)7+ICxjsxZ;zIm*|J*09P5oB(@ zi)9JD-u3H>r|;E<`Wx(^vu>q6NsQXO)NoJO?PhrXK{r)g+&@|h*)`ZcB+lHKTLe}R zz(7;v-uvO%(CU!~)V4J9Ttx>`=7hjfdB#~ffu5U_FN*Iv(}kpXm~2)-6#2=>e1tC^ zOd&V&5N9=d>y#hkVLXSUr8rt8p#;wXvs<+x2C<2JcNSUqgsEoR3yrGpsWMxDVIc|Q z*jEdaYSe;e!*V259132R5v<%L;fFUb#~L%gZw4g9koe}c!d?(OPRjxY$Sm(9wND6G zDlv_T56)@yCzvkwx3$uB?iVk(i1oEW$gh>n(UnW2sxOH5rG8zPkoJ#k~ zYmQX(NF5YM2I7NvTzJ5Z!`k76LA&10jKo$O7Wz24gj;#}q1@o=Xp6k{F$+UOdu(y| zdL6A~qdJ*7IqD>qQ|T)ev8gY_)acjgo%;SiMANjYNpIR3EM=B%XnbBhA{Q~kg7xOLGcSQ*A(MyWU%^o{+-Ne;^U1QkZ(b41%d^WGH$t69@OwZPNlJK z?~n4%Mag<`j=^ILD`A)Ozc*1yzg*Pf=qDmRkz!f|!{jD+pj{`mf{aT;!O{0f@&ifn z5zemQG5&UNq$20<3%uYI>XH%Kng49zcK5B&DR=8^0|iWf(dpAz+$V6 z*jcvY3O3P19@fi)tqIcc9=3d0&9%K8>kG>oJ~=8$V9L*K$IVHARPwDje*J-_NzQeNB;Qb{&aDu)t%05K9$}p zckdj!i&%&W`Y^ulBA%27L39OJ8kfpI^{ax%N3)Y@#ZMN^4i8sJEFH#RnV{Q+z*%f*1R2n}f?2WRTD zDY*hV+4zR$K+Uc*;2b%UMM#-agIHy`m4GRY&MlRcDIGokb~`tb1OcNm!+>%8oW(Pn zE$aMP+LyeGvq}vxbG!uitP2Xe>nbbnjWei&u|~-!G=(9|9AONUX!u(d>~%{j0(y|7 z#R&BpM3Ke9`C%1p3L7149?bJ8Dy5O=a*tajG)lwXFQd+LfF^({ZNbqfh%k-a1Smx= z`%qwE0w4G`_#21pW&8803<`H~G5=txIC|b>iRxWLC!gCXB|M$Olc_429Xn-$(s$)F z69M?qqfnSpwO{9?Pb{ciHfOV?U~=Gi2tJ7?1{hTOusr~(EY;vjjJ;cLCZ5rP2crv< zb3@lC%}hlydcGe8%)dTi+W%Fn6!m^&d|w^#vHSoXsCbqeAFU?y3oDx&VZZd=Q=UYf zwD`4I@sz4Dnp35c%IqwavE$}OX&5FY_>QnL{%zQQ5Olxo;{SPvOLb*pYui1Ev zQ8d3;J?~T;RjB>GbKYpCS>}}X1;gQ_<)jNC`lTs5$38y|EGWL-eCnVQ$!1>BDF~yn z+WX}C>iuQGx4^?4Cl{-H3O=8GsN4C#Jhh-SV#MtW2|!1vQ*BsSmb0Ao8@j~+Zv2!H z@=S2PGejX~U&h$dbWA~ahLyUxa>5fYv*$u)d>JFpb63r#+1=qCuIYAr^)G%_`d(M8iUn6;L0SO%u9(ayK9|j1nUL}m*auSUU6dtRn2xy*4xh4T>W7l;G6Z?jb zx(!&rM%a%ASalG#J&n^Lplo8}PG1j)&=Yv?Yaxr@B7!BHxIWCkPa;r2KHmT7 z&3a!9Q(&`-CNmllz|BdtvehI;9%I&&IG+0UwFoY*RjYc}5lk$dh7RB{C2_i;|@QKU`0J5*# zO(DHEM%v7QhA|?8=fl-K6(UD)i7&bN=A7fPmhQjj$whZM5;OfE?^p6YO-;I=ZDht* zPn$1$i6k_8Htf((&v_{GY4LsXVj2$_CK<)RZu21YIhswWAk)5CUchj|WWAP$5}+0i zaqgc0G{1;%XZh+D1sH`^sGBY1l!4V?5Ha9`SZQG&TKzHc zr;eZp&mFM#_S0^Wb_OKTB&2WzwvE*jUGi5@th<|931#B|Zx3u$$pOlNsUzoQA$yIO z$o~P1FZeqVNe8ryZybu>1S~?Ge-Zm~K~TS3;AmuKDbLar>9k_Ds$Gn1*vy3+Qr!N4 z@|VmQDcQajJF?iAUcnUM{Z}U@Wsx3}W+hqx+>xNqCyD-EVLG zl6gchvM2x9wV**)hxYujbXMaA--?LKG%vASnK`|8sr8N1t4TVha_r2oa1W%uc_@nY)x+O`3`sZ!9GktXwFFBEy+b>?hZEm+FssJYW>;3$cfiD zl|}46U*!1CtY16a{&((VWcIH8@1Sqfb>=PqU2twV zbWq7}H7E!1_sZ*Y+Imy#>oK1MxrKrUMM6~du09#5kjK3CgQebdCa)<*X`9D@32@ML z`sIOIgM>v{4HeUhfG{+Hk)3J*kLUe5BD54?@t}6S14{v{><}2l((|M3xA{3zvjI{@ z!}Hl(hejb*^t}f>>s&hnKYf#sSsW(#@^1>Gp{J@()$9U5PpPyk5bJr2{%kO71QB{x zH5eY2V6Bz@!=13C0h~b{Gd=*Jv&H_jA`56AkcyGcllVLyO7T*ou5>^0ihmjbsDS+Y zN}tX=2!@QY*=Gpy=mX{7ZVHmR$@%ih9D&ZoYH0{?5`0Cz156)&Y{>EG=w}y?o=F20 zk1Qpnh7eY`1^W`?g3L<`$(1HGsSP=?+C@`s#X~}lFM&4p@fF*Js*Ne>M=nx@?m{3I z9RqL>9vnl}RO|Rtq~w`2XFwhfx$W2e?6+vD)ClGo56}PXyWp9hWU)WQ1AsN* zCf!&rgM=97KIEnkP{2Pu7mLyK(er&PTNg+_z&`>7)Mg0duQjXO#m_jjYd8EI#Vg%) zCF>wSNV-bnr!K_451Z6FIMgLLQsRdvh(uQnV5OY_HH*Mmt z22 zA@4mnqI3`#v@Z>HxSdL2D!uEi+as?=enPpgjAb-ILG0F;!tsBt!Pal=(B!KPur=E6 zmPhN$#w4-PIJru~sW1>7tsxT4DtmF3;MiRz4U);n5c-b%LzZ>ZUKpgZZ%nj_v(zAF zLz9+(wJd+P*-Ddgd3*RL#oG9uGKdAn`^V}ZWHqk#S;9tDpc3FZjLF7}vo@H-G!$5E zEYvVUGO;QE#LilFSaqwH#eoD2r7aHc_AP@T0?*#I>im@Gsl=HSHNlD+@Kw;f+of@g zc|z3=$?am#1FG(YKzm%%ZA4C0er`}endu!JU#pnun3R#FGeHXR5lPB=p!BF#_}6`$ z=b<7Mqx6rhf8%f5Y{5~C+oK**E%Y~gj|JXp{q!H!BVzc!)^?{~hc6vKirFta)xEk# zm)CwE7vpzZv)*fS`?hkS;N4`DUgF)ZW+qfuQ*Am8bO#hk;N<4 zLc)C(M%RZXBU^5q!v8hSylgi{+(&0LZL;>z?$dfI_~(iQlj$$c~(b=%2<(B zJw4W*g8%n4%GT@r8j|Xug~ZcSS#g8c+5|k?b|E0KM5zgd*D`lo$KJTXhh703A0Cxa z{0I@51OWv!rg;$0`UmMMEi<1oeLPOMdK%w8E{1j+*js7XaJt(VbJ|#-op|ea>FMvz z7a)<{IgdZfod{1?{~rJ`a7?d%+O&S1@Q=6|QeL%ML3?b51-%!I!+_H=8Q|yvHvsM7 zK1^u9je*6iNkd6EZ|zuYRm!43L!}Hs_RIi!?Bygr;LaCv{!nWO0d9{H5E4Afh%J&r z^iC(ua&TS4o5cw`o?UI8A!&T+?Cl6m4@u&inN6zmh;sX^Xbf=_S&esx*@i_zJ%GiL zsuGC|ar|iL?JM@edh%Z2)deI3v%AH<*0r+U5dV|VQUyUVD$3LWld2uHj{KA#?qjzwC@#p78WoXOWdgJR@q#x{uMOCz@V(#`AEH zJ)8g>Ar*=>PGtNo{{r$*iI9FMHz&>>M7zDvZw5*tIhG%LqiY&5C7M0uUa7cLk4c*j z%OgV3l;C4B;KH@P6@J2zAsK3?I*}F20CCxS#DV>b%|>=yCS=W~baoN^G;e`ph4@J- zR@GE9mg$bz>Wc?Ky-j+~-B1o)zy6Fj02le`1qwdFBVtG=R?GhkkyZ3^N;ez0Cnrq( zJ$muDhahf)6GvZaxu>ye|br8qvC-a`g*rChi{Bws!f^* ze=+}HSr#XZd5T414P%{cnDDdOUtn+-EL1wuXUEcJ3m)D1@Q)NZP-I*d5axMu8^v1_ zMQ*X6l;y9Bew^13)0TC*990;x_Cx^g?$)DJYDD+S%Z;U%ZiDTf0I=Muv2Gi6FMuMC z7=>XU6AZ~Uro4VSSY|%Gj*zhW7HZlnbSd{jSi?f_b{j?faHx*iIH;{%=oX&AS1*=r z8syDzHhEGA+Xv0K`YTb32;jhz84DQbKr0?}HQHM|WrL~!M!jVebjd8h>kT&DEnxSO zvb3VJ3-;KAaS{LhY~}|soUxu*@ppt^uZCyb$0a-EQKkA751Fpw+g_Zw#napKm3EfL z*w2atZ@fxfxY_gFu2sLWGxzeh(3G?Xl|}>1v?X@2->VBiaKE?zkVzr{MpnHIQyAjk znJb6MnXiu=DqetqC}H0&n7!^S>~{zWNr}@l!^8EX*yVU(D&Nz2HceteoL239DFpc? z7J(+WUI#J6oze-0vS&$x8!dJBDKE2q$>=Xj-2i@kew%x!7NYTd;A&jtij4Age~I%; z2kgrZT4qtR-+qFJzmR?N?txXzKc4l7?z31{3zI&Mca9JYfW{U1 zr=;eFs1Cd_tn8p@tg?|32o0NZnK64MK!B=vBba@ThPlv@TLDc;GC6HNRH zVVLt@Wu^q95i^J*{t;F3EX1@KTNf4(k>uy^(bBrX-Jz< zv(j+U3Vh_JUJ%8i(~V_B`=Z6b0;R=izsoE}286t<0@clNG02s?T=E{0l5BIc-i{|Q zRCPo7v9IHQa0;^0c99h(EcD}VSyzy{Zw3;-0^$AvD=}K37Y$%lOXzIhQq>>|(9(r` z>6tTkgdZ3*`h`=dt#R~5Elw>`jN#;BiLYjUIeeS7Sg`6my`o^p`v~bW3p4TE4VpPI; z6?tc|m3LDm)JvhLkc<|L^W_Nhto#hDyh|#c2=#aYJ^ya45~W*lXY3euDiA7yhK#eS zrmw{0_oib1cV8TOSBsDOH@_Y%pZ}ZH_qIViDZ6#!0J1PNcC?ADy38@r zlA%ublsIC$Rur|81h}6{e+EF*?;BO`AS?3XC6YSkGHQKe00PznETKQNyE+PyhT8eJ zfggEB03cy_#QWo+k6hfh1pVlrfrP@0ie2DqN_$QR;U(vCY^C7%?2Yie*&jUn3+`r1 z&Jk{Um@hq>*DS7x1p3hL$89xnqSPjb5B8py7~g;M=6`?!cai?9u+n?~1N?PeRc2ye z8J3EqguT7-`Q5-rk#s8;$GWd7sSibY^9K{6f9E@xnJBRy3lKz-SZf24SJuzU_mB{I z7i4-P?Jj6(lnPjv%^4*09t7MOXUTY7UdJZ4VnJwd;TG}Us!k-U?qvd35j3B-fHk|z zrCN<;G}6m-&+}ZzWdM@vzvO+#`Z~FCa2)W3$fWP_l{K!MH&Vq{H&=ZE4{=fTM1{l1 zw6*v$9UWk*IK@R*sByns0GUQAg@P!a-(n0R&3af1X})P6Kjb^haFt|0*9A~(I2afP z6`Ho_>2=dz)JK$2E^VuVx_=}$_kZ2WSwpE3OZ7l(5G-1+@Y~HvP&s;V$2kN5fh|h@ zu%ISZlz44p71>&wF(`KothNT)e=XPm&xpN6vQFiy6^W*eLWh z4`?hJiNXEWyvilMJB1}37J6n?3o}lJu5_{fH2b9)B&ndyqt!Mk*go8@@(9ly_pW+b zm^bYScgRScWfc@AY_dh(SItn_U#9|C6Sc?srR5~F2+fa z5ydhuYFzZ)O;)7AADz(BPm~54vWI}XNvUzIPwIyZ+vb>9#d7v!>)@rr#Jsup)3eG? zK@}`?^HqC9_kxsDYy{9DziF+eBIFq9O5LH`&`?9d1IK?Y){MmPNv~9<8U!9MQrA`X z0<_NrVuX5dPn%X-b;R7kgz9w#wjsoAG6p2AD;LvA$&f-Qs&H_9c|@}>sRsjxgDYJW zGiT`kP*IB-awc#$rhBPAVxibG1dP)+C}DDRty=swA}zQ3Fu*vFWYnaWS(k_Q`Js}l zu`ZbR_<9t!7xPBefy{2Jy_~FeKQ?HUeg3wg-BmCu<6D_Re_8oYNk*a#6@tu3tOf(r zWVNNE+11i+PnpKX$=7eH4?gXn3LrG+hCfS?d`I1Z4t9?OiAkO8B) z&vrc3#GZX5Lh+1Wq!s&S}y~*gIPZ~Np#naT_ zOz&`|q+5`;ephKv$>bje<7uhKdgJN7BGBRaZf_ z_4rK5bXQ2`t4kWrs#6P&X%(vmH|-#+|C!~t>G-N7?H?Up)X%a&JLJHAoe0W;UV3{V znl1Vw$96>h@|h6uSMAjxl;?!IVu29eFwl|8Evko|%4mGe<}*47qlGp!2oe^* z!67he3A;zE6fi}151wD9!*v8Ka8hg(JS7ywZ z1OW-b4l`jVM9l92h)t=H(!;4-6mo2By~hID@}>g@LGX*r9!D|-frC(QAfIp;@~%qT zq>I3fZegaewc_%d7CmA7sE4oFz>{_6M8lBDs6ujlPSWh2HnMMjKrWhP>8z*TTDTu_*lhON9az{wDA{UrPohcd9e z7&Rhe^A zwxb4C7<~Ddni}w1rO{a30D)ULV#8lcN<7)RAQlA~JXeuRrhFYk&djn=IQb7=D86-d zQ1Y7%Ja>eq#kYQCX+xu|WitM&lb}f#iJkjujquv3L!=50BPB9Y}1MMZ^ry0X(Ox ze$*<1ucJkwzHrhrn1b_f&F878tQfJovJi&EiaEib_(y}|#--EdvQ8=*MAK!Dg63?W zOE%t*_R0*rIrq$z>2}%8tmI0ztf_SKX`2&F)cpk;9Qu}Cdg{pg&u_EgHEo}-IsR(d z6vzz+#5*M{p3Tgw@zsvUj~ zN*i!?a=Qx$vUQa!Z6zH`WD?y^k|CMMMnl9^Cv9GTj_#q@D*RdY`0y_2TXmMpwo|s^{=u>jYhls_%<0AW+eA-O z8P8M&y@K^aN5wf=_Ff;QyX^|HiY*TPh2=A^Y216D@a>*cYVmcSgeSGxo9&}n%5SI= zCy-`Ex{spupC*n}9G0Qa+|fIf) zJjn^P-?1Ag?p}VSF!|)}WJd4~>n|T^&^mUu-kdP~K5^u;dgC{!0DxGHh+lyvC&Lqx zarujrY)t}R^>$C{0yb<%Sj>qvdX@_8V?u#oYavgwq1l#u6+pXGo~XdR5@s{C%t8)h zAC^2{ZumzTQl*Jtg)}os?|4X!8TF1NqYnda{bq*Ea*$rqU?x-t#3Tde2LMQS>56=- zq>O)~w=FZ|%$ClnuhG#`Kmqfb)fyr3W1C0s8EwK(_uc)C!IMd~FU|4_thCV{+6ECm z6wwU)ztQ;7TbkE8l72X3`weOJTWA&I(XrJE)&1f;b1$$(YqTt`su0Q!niNw6ZYrXwu_6Q@C zRdViZp5&S|%$(aM55R-I7)N-*&82EAE)2tsWB#8AS=)cklRR&FP@$T zq)ry)YK-NVS=htP3J$L1SsWke_mx*Oq*qE*Y;|aBx0l|1#Tl4tMF)>Sg0i)7nWBGD zVtoF^qB+2^-(uiWECcyY>7vu>w`NmnHu)M|)Odo1e&Vb&bCz8yr1dr0FDxbg6G@i} zNv^YIBZs-h&YWsTI98){ZlvZ_$b8-lw+2wHoa~P0q*+mLW00T`-)CuVwpn(9nWvNo z#DRt(X_7q-MUg%^$dQs%?#Z97_+u7Z@idT%YaZ0l(koW!RM&{h^ziSd$hYKho*u`I zc|b4`u~|K(RDqMTZb&FE{U4x~rm9ed3bS-^jG}_dn^#P+ z=v`A!eBk$>7BdM*;9V~d!2vm(Aw%{-u%X?zlNtbO?!hLqeUuT?4M9I z+q3_>W`Wl#2#zuqtY{R*d-mc{cU%ur1N{#q`nE=+svmhxV=9 zH%b(m?_H5luJ3)7O`A>q5~rjjd?e6L3cu-CHn^Oliahz zxP7*7RWL4vi(EV>yR3Nk**Tl4qqV7y=~6*QC|o+uwAYiMwFo#NR$Qc$r>Zwd4c zE{?J?W3(`+*&O-ojra1}_TL$|HNCAuW~> zYNWYyDgXldSM3v-GC{8>>qEPckE6?=Sj_BDirif|%Pl5ouLRb(}Udj^b$?>Ie%74MexOb>a{|+-mzfb4JZR zxLp#SLM8Dmtm2?-$6MNQy_vA^S^(?(Xvmg%-%pRYA|9-Gv_S!3Ae$o^CUmj(?Fip5 zktgk0m3!W$y1wrrJa2-#6C^p99ZGD?I`}vo0r^< z*FOZH=JWwwNNG9)VF*Gm%L0cW-Mf~=G*ZSs^tSWgsExdTcw5aOz%0#X+K3LJNQ%tp zfP2Tqq$Gdv%yM4ZloJXweJ`~Ss`w#bop+U6dM5$)K0ULbs<)6M1N)MIUxZ+4tQ89R za<(bjP-CkdZZBe;aXqsG0R-=tgMm`l1kX?aP$P9bkYIe1u$BpzjIBIc+QrC;j<&8{ zMIbRD--R@=_x2fM_p!4HXMZijx+myQU$&AnUh=o-m1_6oo#y=uF*byO<3Sm_UV-bL z$VTK4-zOVebOwuxcS)0h9uPLjqvf0xrNctJ?le>mkp%+~07hXb$T9Els2c37XP2^n zXU{SUUD-ntkdbxoQ{GT9u)izw|C}wsXv|YJYAkL~)65vNT`Iv(hOap{ogEd{)vs?5;^W;@~T&>^w66ph2*}#X+q{XA|GchA2mx!-EcF}^;WHdB%P6VjAHk0seA43Y zQDVxa4@r-~Ht&A5i>-|w*IU}v)QVj>sdTHLD_8bc;IEjP-2K^O#l@Fi&P#?j$;bv> znkcyQ#gq>@FAF&PVcR+}^@P%)m}{<{eQxH6HHzrKlAU3kF`%4x`mpfkwhz>D?oMlr z;`~*tvuaDp;d;BAKNcq?gi2y;HcJX@0mCT0(bfL}^!E}<|KF4+dU>kl0rd6oN_5Z_ z?Zw>p^_SAs$G4XyA}5xn@NRE(zCn^Mf7CwS&vgwFWP09%iwLP*FdgQ>qaN$1T^(O# zrPG2+8eeLqRA>l=nrdE=zpMe9!3mKD|^=5xObRzTmcX|LbzT!$027%e1+ZhZmftdhN5jiw+ej{{<9^AM?EPZ;AmB?^^}{ z46{zHE2bXj-IMhOAhNgpj2HU7cNP#Yku8G07Zs8duQwnD`5-V6V<^~IYOK*5m|o!< z9)k>2CBztJW;AZT-0z+F@T z??>n~OTWouopG@zz$!O>Yk%cwi7XzGEEi1x$BjfY5OV^LA2McXGJaQ24t0^TMOEwi zA17DlL=(G(2rqvMMxTB!(u&G_I1{Lf0)+_0CId~#!+ac@Hx}fq#Xu>*i=W!&VXezn zK#MZ{%SLdFPpJJ+FRf}dEkN=0z1OndNqJY3q{P_8%ND}l%esHZ0(@~4Ls&=$2{+m# zysb~Wg24_pbd%sVOg&h%FJQ|C=&4lZ=~-LwN3{VVJJ3U`(gMusN)2gE$f@5|F|D8e zI3O;Y_N&}m*pSib?;q$bPjety!1kK7X%+hetp#84aKSwMxuy@)<{UcZ@~my;0K*4a zz)DOg_L0b0m%h}2KX4U)=42b4_dft!n(#k>*dv~EoXUHGzF|<($6K2i~o&=3g zCeC&MFiQ|dsmijf2Y<^eC?OIzSrzIV)X4@xBrus%fwN&mQAa+iBtNLIf7I47NcO7}TPjoZnOPSMbA-wj`M%*=>_}c;Q@>)?GMmv#h9ri1J~{X!WsMkmJ-2 z_jLtH?VFQ{5Enp~y*|laULD_u!TEwEtjiQBxs~H(kX0Z32YjLr1+mDNiOK`H=&9i88^+kgWnyO)ww0I zQXvWa{aatZP2|Da=cl^2CnN1HuK7Z@-w2>Eqd~V`iTb_OnUNMNVytK^9v(c{&_O6F z&#C`&r3-9)Zz{v7yxwx=sA){W>#QNMOT&jZw%R*q6#aEU?amfThYYal{zlCrvGaN9 z&XZ?t6eM0BY4LiIY5#s8d=fWvvLz~=S08S6}Y85h??v+eR4=(>u0pEOZjhP z96IxW$19*{l)21S0X}HaoWy5j{*;v6al2qK5W@FN`zDIJC<@qCdifVw3~js5$063M z{!$I*A{>vn3z9JFbZ#1r1<5^?FY*)LPh(UJ7diRR?!gPFHX6Oro{i#xh*N2Fl8O;# zeSut5<7a?tW3D}Y8Q~C19B~q_1`0fFYius39sdaV$I9p%#E2NlE~AM0{0Pn!ZsZiH z6{VI$ZMkg0ty_|B0Bkv z3#iv_on2BK|Je&*1`Y|(&>EJzOO{O&B|zZmIP_Ahc|ZPuuK7AEaKywy&TTarE1DbE zBbZubQ3;g^u+E#s5^@wl{i>E*lUjlDNAbiWmDDE<6jhr7feJjhdZR&R#N@tQzn{>A z#B_Amc2$3l`Ermy2A3NK1POjx^sn1jB@KKc<-50G2BLu{R708g&<*tzJ)8J*)g3Wzmt+a>)FE*3B!#Ejaq+S-)Okd)ylU^0Ga^7*}O~ z#dLLS$2Vcq8%fnzZScJcKo&YMmX;BzyNPa7NLta2Kf)bCwOMsZqx`@h{g=DAvNyZX z`84oR9ucMPSz$Ir=J2P}Z&W~qi#f?Vr8}Lw@_?Y^Q~OJ%c**gXd3$_Ua{K4 z_|6{fUF|CxGsaG3yUh5m@A;-zz4^x!cHaE^&IuXQKeQNliO5)ck)c;2XSN0kE}y=3 zAC9pIIBytfhrSXOo#0a!rUaPV@pE|y-|d)boccON?3VSGJ}C_8;|^~;9jD?HT?QYS z*JoF@U%PrO&`g<&Y-^G{$lVHwKvipvURT^Rcn@ zm6?l)Ie^P4Oh_T^R zWJFxK=fBu;-5^n~csjZFx=TCy+PyI;(V;|EqSf`k5gmUwzewLX7a8+x=*|mcf^3QX zoz`C`#aq8ndJT^XJRBG#{OY~(;K5ex`O9yB{{xg>8Gc(iZg*oQ6RYVhIFn`|e%dBe zzy5klQ|pG`ykEyoRj_`+Tsfq&eEr0QuBXSZbEE+pE7LaoFp>Ub-ce@*EpyV|@{?1; zg@XA$dXIUM@ZElP?d;R<1F)|A>~we6Ce3?tkEF%L!e`8ERlnv%Q5|Ol@8|0aYAA1L zoU)$_7JNME5rf9Me23&L*hiZ|*VuV|s!i$&x3XJrnrKem;y_G9wtSSvDzL!qB8sXm zg*NQIdPb`fy}|>*O+*8*&Y$=MhhReP4@=Ue+ox&;%`5XDhKagRfqnj-I@oQvcsNne zNUQej1Pu7jl%5;H|0HR~J5G$2lOrYGBj6zQq#nA4F_k0`(klE6y(cXpe12fr^O1d0 zxIRqB50?gh0$qI$CFjp+`hhE--me>vE{@X7a)wad9)0563ZvSM zKtTj8#@U*yVLK;9l;DovLNTR=INxQ!*<0(4-%H80<03sIiNT?=Z@Go%CzHvT%lMk;Xl$&Gm;qgI0gV{jz_<`e|#=7re#!>8`5LFarOgyQ=mtV*!kY17*?) zu>z;9+^$Qc1xUrgg&0Ch@RotUAew!?`Qs!G&2+T(vlP+(1fM@@&+sdY8+us4m54&jqw@VM0No1i8i{e!t5VmUcQ6)`p(KdcU^(r3-~X0 zxo_UYA9i{FKfo_?5HD6XO8JIT$T8yk$Uw(3iGXeM!v6n?;X-dn&ET3tO*#+%n<|ZL zyj%ZrtM`zX0p0k>i)<$#CV@XNX_UvuQU$aRzDd}UUcBfVp7fJa`wjQ_^wk@r<9$LG zVnDfD%xtpELgnQ~>&`46;Jb0X`u|F}T05e>Q1k(ZXm`~q5m{3An=^{&vE#7_?vWYp z{{UWUYu7KCNLV)t01Z?|+~*gcD%+0uIPoEn^y4M}opODDI#bEyuU|{apZ>ECp{}>% zpHK4-Jr&N!Kdln&l&k&+m;)!N-`?0|-J5XAYAzfb-t_^&HL3l^QPGhX!Ic4CfOBJFYb?!f}kMw~?d4*8I}UgGVmg z|FD@ttrj+-dqwM2p4dz~og5@#^W6r&k6yk23kkKF`zEy=5Ci}XuZ@V|l0+tL(egMZ z00lKY@Z&_@{D`{d_H3n#8J(>YfhASQSeETPC(o_ZWoTvYE2+$|pUeWPrDOk3Uxgn& zVjX~Y^j|(1tX>fD`PvYC87yWc4=4OV2RQ{e@N&@;t++p>2cq?Y z1=vQEXD&TPs0b%g`N2-=nZ6IU_)x)d5n<2Ia(5?#B#KxWa=!@f;8I0o zymgZdOA|f7Cv)gEL!fzp&DFL*NNKq8Q^5Er zO58r=_-l8lmoSkV=5ga;dA4}tByg~;lY4%AME1Q@r7-^dWw)N(QK#u7b;4x;AhX!_ zM>+{oscxv252%k}GB7`?5h?D=XLmwZHy*nz=6m~bE z)4u&Czlp(qrnV6KgCqJhkNGFJ?a6pa|r=Hh!|9%OlsG0d5 zT;z)SsPV{<)FkrFkHOC479u^nS^}rkLzs#JqfX;xa6QY-=03oW*_Mt34j5zbgH)oo zCYs?qf=U9srz;#69&^lU1|d*spV*;w8bg{X3dS z5~A4CO6s^9GqH!u0l`V<%jmpQjT2@zwWZ`aOUtru)+S71X%Y0JR1r?xalqEBRm$}| z@-E!df;(u!6ewyB8Zl@dw)39Xkz{is5aZ1zlAwt$VaW3Ah%_fYX*Fvj#9y~I@VO$y zq+DSrYV?uK^}9iBF%{Cmyx+I%n#W%PXGe-rG4@3K#eaNM<$~RvB9{}V)8^1pYbQN`7dqUcD+*;b8y#D}&pxl&L-cP4?kkTaBs%VfS z-2y=_w4TLKk7bKG#4Q)M%^@D+_e@70bYddGNLnOc1MaJ6i8J*`0VLz*t{SiGD3}w1 zQM5$d@d=OsfNwk?6Z%Dyuu;Hm;AfdY1Pt4{ZNCzUwf@Lp5+W|8!Z4|(NwNk=Fh{Zx zE6@R9KU6r*a-P^0kthIO_)QI=L6MLV1jqv3sYS#}5BQA2Rtdg}Xn}ltsRHpZQ)6Yq z{{S==wsLcllvMfxCIMC&8fO0h(Mbb`DzFiB77(8X_dS!xDnV#5fK^^hiz0wP0upN* zxLpK@u(x@_CAK`JB6f;sk8f0fx$dmG1Op0W3-X#O0stVAN|TWW%DlF<$0(3;ps;gH zSm4SA44D~{6CIGz_eGjQz_JC0bLxWD-pTCjQ}i9+h&&6b+ah^Z&@;hOK)-CI(EDNl z#p1~faq>*S?wbg>0SL>nKnEoPONx});9hO5VQV%?);W)Y=L9%2> zOJtSofqvg5OmqyAm7@{rjzBp;zoMY_A5d53V+a=gRz=H*x4I~T(J}K#mXV*LxPw1fP+$WRiWKNlJI4yP&lXB&fdvG4L_q@S z0a!*bm?HKC6MG9J51bGcLt}s&b0IMp34a%%P0@G=WJ$A$k;<86gK|v$N=2a0a;&(v03k^N zZz@>^f(U2=_dx=}(go!>A0)3nilP~a$oEKK-aXT7#}a}+;e<6piG74v`btGJ9F!ZG z7mn$mumaZmBra}$%~NmYA+V9R2x!-q1V#Sn+yI^x43h%|55X?AN`EV< z(P~Gd%MbnG{MXS2#QAQxZxnTgbZ(bYyNLLX8(0q~{FMIyR4z&X0J1(=@i&2b$A^ng zQKLg?4@3vN(T~V-H4Y0b$nfTetTzSF9jTJ+2O#H&- z`ZtC;pH?7fw5FqbhyZ-a35`H&2`zDP&6i{Fr%`a-r^J{O?vKkaM4=V4V@3E!;*C%a zW{QYp{-da;{G3-|>ED3;UaTEvyFsS}+yd&3A3+~=>;C|U3~er#sG{lJPM{Az@esRc z0#9YtNwQ5v;T}oRe*`)L%-6BNF z4aAfPf-x$=09@bMC=o5YMWMc`tYk&IsS<5JE)|YNf$obm%we|VC$w$4qdn1LFDkH* zgjz71eMfW%8CB&xrGg+`;y@))0J`JshFs>+l+EyhA-Iu+Q76o(3H0U?IqsVzOMr+> zd2HBB?GqQWEfEL=ors7qNcj~aMTghAup!t9;A9^_6tFq$hTkP*1XyGw0T(=k^Vvk0 zSV;ouJ`MYDhY(I77Ue@?ccv3)fP0%JOo0|em_7YMfRWtTBGCf>0MS-+2>}GaP-KAw z%mr<1Hu05PU{28kgqabaqK#>Y#k+dlB1GU2tT<$ks)z;$uVk;Va`aBto0(3)@P`2~ zE}4NAl<9|;urT3!BzBI{P%~vf7`N3FN=%XZV+fl;P6;A-L<|VZtJz0lP7fFLTDmjXS-B91k$-ep$uT@8;{ttFHf=Iw#iGc_kUbLf z59pkj_2p~C%VdvRLST7Fi>5cDy5|NWRS$iZa z#AP5r5JI4T#WItP>?5+aI6C)>{70wo?uwm#LwCBPi&)u>#0gy`{{V{`cF+5#%i{k4 z`>B3D5;}K95J&g5zJ6A(Ebz~VHU1;ld<_E$IeV}eJCeP;JX~%bb7#!?9K5fW!Uw4j z#mzAP0PH6CivIxq>SytTNG<)I@?ykS{{Z(_1Frld>J+q_^!mL$YM@@21;2IWU1LkA z>DroW{`JhOd4k2*;Y26yGk4gQPN~JwO1XeC~;Os#ime)$q^xbzys>&gs z+Mp6C)~eR9fNv8Q`L7oz`snt>e%9I0A5q2b4>db`3^~m=z1O#67dQvTS{q zSgF-E)pHxAWD5g->b$&he2?`z%3eD%12%%7z6@XJwt7Wzs?~fdx^}QZHj&4=#np8! z(pL>;nT`Za$)DY7s(w9#mMp;1W-o7|+3C8Ir`FT01T~Loa2$YEkk`n*k-v4=2A+d- zrOaS!oXO|1Xa4|?WqxZ1r+9PmTcdQkn%{|}){&^F9ve?-009FecUqq?8%>MwFtyfD^RitCzrKaH5ybCmuFi#6l^DY)9 z{)K6ALRw)iPE>tPKNP$>v>%J90&(z9^;{=k{8H1@)zLi~vztM!CZVZJd7Z$6#4nu2 zrPhKc3O_RQ-eEnv7hXC6PhAb#o(;;&GV6!iZ9_D}U+KMg~} zXK}}LJ5y6_r@WGnGR<&03ZA&RO0jB2C zbWP@BSIke;@*br^*uBW?_VbAwHnIr45a$5J+~6Mn*A5VT5T`Jt)th|q(tw1H#NUfhw-5o znHyg(np%nV1AI}@9?~1tG;({@1#-Gt)U`BQT7#QXldjh^$RT`lOQ`DT)o67`f;c3v zslN?sy03uxHAlwh(I$WQ+_`h`{wimGjq+#7CmAEN0TUJ&M`)6e7rs@I%q|DCM*g0O zwqTPtR9Xoehq7FW22K`Ip~qy}4%?>^37?_>hew(YLZV3H=8_f7&5$f{?I;%$iAN?4 zlR};{GA|atDoFP*oM30nAT+5}B8ciX@`q@<7HF zaefmm1bIM(GS-NLpP*FNMY$<7-`gs?8^Q&&$*Ff4xKb`L$Fiac8%Rzg(KchUI}e~t zS>BPyDV*U+K9T6F!Ka5qgFmFk0ye}*&j|N2eN!?nTOfL>pGBbnWJWoJMb992M98qR z0lagYe^5*VF)7|pWl-1xO`v@egPv2#&5{kVH}*p$gwqC4XR#j1fwtu{JlJswVv?do z!GYx+(#_yXqS@sk5o2W|IRIcsIZuLTz)t`K1 zC9zN0i~=UsPU6F!6_3(kOF=)X@r9A0I}ylI4|_%xxC3!@A~AUjO94pofBHZNe`R!2 z+fh(Yd0_sFL+~S6LY}9p>0U?2txlCnW4l^kyZ-=*tC9Zzq!=ThbaQk5_fQ}I01bG* zOX}K=nXIL#(^WL9QZmOkA$s8#E*eMAoWCD6pIbZhk|c=Zh2UTGh@(P>h&pXbEds55 zA$3p%$q#@}%l2Dz{{R@ei!{4xX!Mn8#0s07-V+++*k2G-3-Qn();(obJ z)p|aXwxO)Eol2Wve9e}wroUF`-Bpd(Qgu4io(t*JWEkv03)(*c`rd-4h1=5pH8yJK z)pNcZpgF*qk==1$^on+c^m;4knqS%ts`fks=J;?3-RU^_uMPgyN4Yqt+S$W+mY#!A z)9CBy4~JH@An;pYK^TSSJ{0PjPL1NtHn*v0*{;!3d%i6fG#%iAFl=IG>zS{tt5-** zW%f0Jpm4fItP!lL0gpHHT=>#VT$@LF@dr}q{Zqz|)%bTwQ%Rt87R@I#HXnH)0&H#c zU0cK?4M;E?(j&Ur@Y6}E_Yq;)Veu6s@t6kOTmrD;`zC2gsM9-dR`Dl>`iJ4hpQnzH z${r!07}n}3QD|%6`JCqql1Bw`jn`FYP85S%@XIf0I9)xAAdajGpRiR&a1_mT;_BRlf{8W2UI%TcK-Vf=LZ) zfcb(~H#D|-`1n-aBX97}#EuJ7)2M5x^#I1bmE1#G8V_Nc<#kT3!B16C(QBJc z&LyCkEfd{yT{)i$g@E;3Ct7iBV%HIM=i&Jhmm^&tYv+H8;o$hIlm14J#G6PZGD#zY z9NA)QwgEPkLU$XlqveZ7<-9U_FM~W4rtvRAuc@V|eLY<#h87qgj{VmgkVup`ZRM2Ht27E}6(s4eLy(XvOUYAXztzTc&J{hIEb44!Qi8o$XpsDZ) zs$LpEkt?4bE7of1idwB*SGEISyDMCdN?3Rpzt=}w)PHxYsZNPb8&QVlj>6+3KAdhKr(paBEooL3nTF zz%m+th?tL(>&&R;#sFORU)EC8^wzbtT52`I!;re$99$rg*>&NYYbPFlG@ZoQ_=Ma< zD&Az?52Dosk$b|($3YT(*P+Q#<#9F}RD%PuLZhZgzaFY2ShV}fO(eV5veIOtk@P?P z9(+5bsnN9^cD**$)c#@ADH12)hPASN$#V9GQ}~T=b7iDK2J#ooKZE{19u;+MCbh4r z(`&nXgKlZQBXAtxO7@*c;)jKqq%~=E16{;Oq{=k|y{^y#@$x6-XSairDhW1t{{Z86 zLelhK7qDm=Dbv)@ZlToMhB%T8dRkYt{u770@Z+Iwi1<#AZ;0jd-{L=tAL6eNJ684v zQ)2}hO$K(bNfX@3E9h^+P(Si-;pwx#E>HdUE%M*vifqpul)0rxZVou%GC(O7xB(}W z;%y7gdoGa4JQ#$WcH-AnB$9nl{{YiJx`&|%b7RuR5Cm9X=7w|Y%Bb7vY$yccMdECe zw>aE=6C8`mm~ecAqM?~Bh>uj@hM3_oBF6;wRzqWzmT4H^%z%SDCIFmIzX`g;OoXCp z$N|$Zn~8z|`zkH__dsMji1)gzGtnc17bpONNdi>~Jf2e8Nj>;gmcWM`jHQzTEo2Z# z21;Y}lY{J(v4HHCjtry+rPaHEwtY%_c8PA;Qf8=)ux>q2P6)rDMb1Q&MTpEBBJ8Pv zyb#!*R9ttS5YsWZn|4SX%rYvl&z!Nf-FFC!G*?~iO{$J4v?({!YDve** zh>LKQgJS;bqs!$a`YD2LZG0e`5fU+UgxmKj%nN2v=JF5KSTmL&0m1pDeh8IOz((6z zN%ROxhpHi#p7%;QpQ2<*Fh0siyZ~WqW=89f1&?%KKo%VLLqQFb3H;S84S*R92^OB| zx@Oo2Hy*yIL$VwU4iN@&sJ1?SsK@Mt%%)AleYrzGI7|-r;SCWSwQr+V4g}ub$wcHw zqGStP`z8X-0z`(1+J5Qnyl@cUX8fc94uL3Q&^SjY&>%<{Lxkj@Si_|#76e^;lk!0l zJj5V_C^pUtY|>&ak=*(q4Ti@l#5PGVkbq>8iZBpkBX1<8N%?n1(GcN3%?$z~2!)eG zl6K^&;DUZZWN@~9ln@)qg&Iedf6@t#J{svHU~612{{XWq=lbEzu=ig@{yS3N;m(*1 z&*$I+{{XXh`7aqgCbf4xp|r!>!<`G!50K#$k0&P&l&tCpaoqsV2bGD;AV({?Y5Gk7 zYByN%N~2g@*a5-Bah1mFDQTHroj-=)PY!^u`VN|IaQUZOSyro>>W*`xf@H39 zUDCfNNWQx$01Ot2fVh0JriV-X&YV=D%1*I6cYx>~iyxVGjaE)mqIiR*(&=>c`szo0 zIV~Ve%IRAA+x2xG$Q#^TJ(pr#et<$B_PMVc@6&vPh?+b+d zl`{VTD_)y)bb%y;@+y>+wo&Ad{8`617t}5my2xDE{UDLb?ceNmM`)>4X{LT7ceSk~ zxcs4UTAqgOF=($!t0ch$N6BB7Hf{0tnZUTqjH!SsHtFLCLAj+%$)s?zGcIkmM_80HG} zY6giQjm4yV>{p4`qJ9Z6yw4c(w0jJGA#=m^bFPN4(CJ(ULF}J@-y{6_KbE+S$|;e&S=$Sdk`&&ns{UhN2OE{T!cswZn;5=Uiaf|!GG61%Nm!w$bo zq0{P>H0%^=P#UH;;$rrIwqr})@40lyTr7%;Dw-Q z4V#?k0P&Kvxf3aIIGFak56IPUL3K|>)-aQvF;olB*4-o4r>H1!trFxW_UsG-v-sJGH z_=Cgx{)b((dW{EBbB5;596OHdQRR!5i)S~(FCRXRI^o34l>&SFD5l#R%I36w80y;H zHEP;j4SI~C-S67j+R!AQXjA1;?96zjETnOi-dO86-EFqLY1(QKvRO6)G_>7Psjz6tU0ma~WkKl`M3+9~kiNbACIo&P_t&43DEb~q4@g8cx4jyoxVkt&D;#Qf89Z){;k zn;=+Ugs&cH3ntM9@^X_>>5O0x>f;WERq}|9EdLj@U5e`K#j3rR&I&pklmv=N?;U0GdNFgmPM0E6}C>> zTjqykeRbd`Jpn*o36VMbpyuLdvOr=YQ-Ws-A($ir zVF9)m%3?N-{)jhqxU!8b7}_HsK=ejntIYS5pkmp|jI1(CPVY|An}*rMq}efx1yL7q z%0|$R1oz4a25ml_k^mWtbCpxIw#h(jlfgldENoN%0Nt`e6c#5mMqva2%4XsLgb@IP zc~TiQWPyP{Q~_xhgvc9!L#%&S(#oqLN0b#34|R}`L`BHTM2p!#fHa&YTN(cV#RqoL zl*Zl(Lq|b6046M?TtEP)ej*Y(M(7vyNt;xx1SQ$|zWIRtMcu}@N zG-`zxsvIbvk`_UMkUszhkGL8tv*;bd?`RQ2LCL8XUo#cUpQ* zmU@GA4e7c%kJf3wyr|kz@YW2H3c9S*Xu96bF1gjT`%c=fhUd@|A617z@Y}A|Rc}*n zi*uklCwJcpt21f5&~N%Oc1tu8ecV|J+L^m^yk(A4SmY3-#%0kzrPd;8sJ_K(G|!_!wHWQCfw2zk7Oz{;)1vy-x4FgdA`K@rU$G(&$!?u1>g&dx z&!*apAco#taU!Nx295MxMXo+aKB;gZG?{~oD!rJNPC4BMy*f|$#nh_Pr}0fH zjdL7CX5#CI{WT=o_+8UX=Ig~u)io^LPe}TEJkBD(Ns-58)vIZn`KnZDIqz>TEFwOr zXyGnA{-=A<)Tu{7p!j$H0Cz0h@jHp&u0K_x)YqowI5FpT^2T02n&mo9r%zu*%^ec@ z^+C9iH7d#`~1&y61qdq5BZl0qK`i=rU zC3ih;pGBw*WojY8xb$62()`DYG|i^c!@aTz?7PK!ZE-$P6WMX*!&H3#0GBrwl4lV9 zo6{;VhtdV4%w1)nrqI!BQf}7ZiCd1NOl6`Eq6HdpJ3{AjeV$dzHmS2fbk3Sw=Xtjr z`K*&VPJ)}mn)-s*lV!-Zg)(HcBl7ggoxgRPXz2cLJ$U)LI;V{rMuwV&DlW~;oUW-) zPw@nY0SmKu?weauh-d%?-F5z(Ow@P7ZGkf@>2Nn5J3TyiC?T^*;Q+a$0=<8u_`a$9 zJgw8U6{(F*mYJic1d`{{G3EJ3-E>VChv?R$zP7N_al8s!znEN;IvqAqEpJkJ^!&L3 zJ@+nLoTE&PsB`F-M)t+T@RNK! zMeZB*;ggbRlOIEMwWjdKn0cx7hP3uNcmCk2eCevNUmuss+QE!K?8r10?bq;+n04Dcc z@tzwr^zLJNwwc2K3Uw#$7P#Dp`lXo_`d3_ZAlImv{6Ds>v2d#p9VrL?I4#%{6N0+x7P^sCeOJzqrQ79i9BM|o;` zj||qV;$PLeadF_iq}8A*u-YdJ)jDU5>(TJVT{S&4 zR{`|?KJWcoga+-71l@HH_EM;6o~6^tMWvZ(${qI#~eDiY<)mSCoE+A zi;s_yVWX$&bWS>lx|?N^V(Yj58+Ae9&k)eonhdQc;km9GypFMtQNq^Lcyn7xhIM*3 z{{ZZ(2mZ?iK9jDcP&%8MTnyREq`B0iO_*F9v6a!TUf;5c=c)4FM>$KfV%KGwQw)1ZLuc`k44!r&Y@c!cf=-6($MoqyZZY^@< zbap@_5KX<4G4K48xx2vH{kP+lKu5Y$J8cLOW-y$_H$|06`>M)kQC2h`A;L903CV07TE~0)6E;nTvlkrb{fFdH`^l;@3!sIJ%-C*TS0{ zdKJS)5KM>wDU6fyPSbb@td;-}N#PH0jP-bw250}vKXbIYH z$0z~~q<6YR&vJQDk0MRoH~AtOMZixah=c$xCm2W*00ZgDP8TtF9hOUOgM}aoF<^ic zhPx7nUQOXJB=QQK#~7G{$_!19vbY?B1G+(= zI&bn$!?6%HkU{rS;RF$nRb-Qt=GPx2B9rt(iE%q-%3EvT*<8Y5wARAf?urCQgbYKQ#0~Zn@Z80NaAa0 zTtlnSy(%7OIE?WDYxOM#nbm6n{oP-PVDTP{cc}Ghl=PLfnwPSpYc?A82)y8h!*%MK zJua@ckkr%~lwBL0Qb8l+xzlDNFWV;d26Vc5?wzNlLBmqkJL9?Mb6!JSL=HGudJ`&Y z>1u1!(Nfktjh_qQG?syK;3MuXxGi?RgSE8FTz(5jnbgT^iTSQZmaE`&%5UgZD>G6m z5=&2*0ys;K0$=Zgs`Uy{tg30Y8q@S!-4Eb3(O#LX`L!|Iy58v&Ut3qG{M|D8juJzL zd!^E;290TJK`n6r#sc1SZT|rIJ1>W6=_vksr%;Ads?h2TX|pmRadfo${{ZYNmBy$u zLgMM5ByPX5*U(kgKSM-*QI*?$gtWTSdlk8(rK_(}ofq_`HP11)(s*lc&DS@EVYcw{ zVXvvCbd1#<9@kY6+Jdu-iN|l|xauwHm35kGPIKDnCW$tSUjEB>sg|~lI!-L|)dOh8 zdv;WMlk^(5nq5ATH06z`;`q3U7gAOiNuqia++D3`bUD;al5k`vsMYFexy(93{_YDG zKo6~3S55FY(I7kgCvdvSsXF#O!J?C?0zoUAjl7Qo{cRY!JzCm*S=MiJDbjck*nG{A zkh>38)oC>CA^la_vr)U4K_ImDcMFN|^Xamdv9+4l?|Vddve@bxQAV3;9UTzY7z9=h zC*+pt(b6d5x(he>w@e;COAE=*2l}q~_R$8;KQ+?21AqEd3u$~9qDIlm<~oL?)-{+M zLs%jQb>!rXr#eT?`8+OssYRUQsnH_O!k1E_KaNN_Tn|ierJ@ZaiiPI$^p&!8p}I!b z;sw1?J}F1DhbIK!>ds%_u(r-e!DL8yU2(eVk1lrOw02d}dW}l@hf}K4f6{p+GT@X| z*{OV4DEV2yej!ve`hyxjPS7)D!t^i>zLQOXBjmUIa_1hZM`(|-(C9zrAodHd-*Z@S%RzvVi0TG3%(kZu9)HJjzPRg&^WBqzBLmdO73p-3Nf?nzU z*UFE_l=7MSC(jNzq|woS6&W%6tESp*2eDoCo*C&bjk;qUx4ZhJd@a(6C*ag=_^49; zEd7G{oM%FND7cJDMvX9JWp}suOQn!QTd5KM0P}DAEq8cVqz7#^H7$5<;tgRj-yd{; z5`N8pGmg%nJ{g${8>lj7S8b=^&WTlyeH~Vzz&mLoyGRF*;Z6P%>BNBDMx+^oFKo}z zYxtatMt_&W-Wk;?)27=AGuv+;8eCfDi=+75h<}m8 zEi>Vx3TI(@ntr3i>m^LvMk+ zJ~;f1D|@sR^gb(YEi=RN(u~*H&jKb450as`o1`rVWTzmDh3TWt*)zC^9Ot^A4&W_o zq5&o*0to{GOsyGKwl{4y;H7~ZK}dI3U?aMUiKa**!2Fd7l3=6^G!EXV76+J6>~bzn zN|PjJAyEh4xe909LxHviN@o_rAViULngBNnXar_-JKQyx=nAl6Bl0DADBt>q%)v}SQ3Hjh$0)j*x# zk^@{b=!-{~4$6y1sO*Dc0g@ACk^lj+sAO6_5E}-BVk=nx4X$t>igX$TWD7f6#orb=!Tds_BQ!T^r(=ded4hg` zttRe+O4-)G7rax_bzK1T?H;zBZF6;0Y8F!^&>Cdl9haX~*L*PF%=Fr?rsnPy9LOY| zS3}di&(^d_CQP1Jc&(_{vrkOBR^v-(+UBrfYH_5JIYqYZoM%wd{&t;RODMjT3JupIG0yp_gHNO`!O@6zi%m!44LjYahg6%iMtE4xmZpH|YWyNaR$5Gl zO4y;K(>kxl)|b+zY7Nb8$PvP&lCMVX4Kq%=RL_s`Ucu$4+ivc#Q~aGBKgATP7u9>1 z;M$~Py}4MlFVeHAY8yhX-8pBG&&`(?sSl*b)ek;*xx0M_D>TeD(!HGamq=>#yCEcP zbvnxg9ub#Mqp3qttJ)t@ohP(Bmi~zuAzxVOx?<*?7gp119_z;K zfFGG#{U@kV(7UO+n|?Jqk6>h-(qQxTSn72L2bVvm>1Fo>2rB66r0CJ`&&B@$Xtg4) z#}PpJnsEn(vDGM6qh6(2Y;)@&+;ij8a*%bE^SIejYmnbBF$W;uS$Qi#-5xPv7!n7BC^oxl&ZF&V|ML! z(mqrC*0=V(0{0E1!OfOr=O(aXZe#CB=SmaRRpKX@S=228Mwc#){`T5b%&tR-K}I? zjuu}MkyotLtx6ozf0E7NfY$W~6K%|QJjIu>Mz~K`3TedeJ{br5d#Cwi{9ZkZ>>er5 z>b1RNOw!7yL|oG6FqsYHd(GDY;j(|XI)2-`;s{=grRthm`se8>RNYymf+Mo>{Cp=z z(dU+puMW_wr|FcbY72|R#^Z3=F+V6=tP^5BVQbQ}iMd-G3oef=#BwJAX^HH-$*N17-^@R0ocU%7V$#^s zJkzUD(`h^#r|Nn)J564>(~6of?#6y4k1_WuydkgZ75r<`DRlt$vr($4tJ+Pr)L}Lq ztO#9?sPz8;33Sa>UXiS3rO&AL2h}blQ~v-9OZW$}=JZs2J6~MhuSTOqUX;T_cI!2) zZrpQjLg{a37fTY^w_j1>VX1hd()1N52N2hOY=;rvHm@O;)C9JUAtG}KAENIl(qFWE&R1;*jgmIaRV~AJ#Y3WjB2ug>UyFoQ6%Z9RM^+F z8Qwa= zS+3ISv^p(0-a|uQNRuNUs0*&|Tf+S(#B8ap)9Upsqo}6JH3cU_YKmY04X+Ru&a=U~ z8l6G<>e`Bo(udQnTTO3<_fu>K6P!=ftMXP2Mlov#Lhyc)w^-}6v~_iQ+Ep6L-`WqS zS-`o$=Vr^qU>IG$!YqILIpS&+YgMi^+O16%KD9#f8pa6E`nt!`^j!N z6x@Ex>5l}EJRi{K$_9#vfzOmLnke~b)FuG7NKR{{XUm5SxFv(I58$D20>>?0c;v|;+)9{&Ep<|Mj_N9Cf?OiWk}(MqXU!?$*cahKD0KQO5(k>$5=+0~^-M{(d@Q#? zUWkL}g;M^(3sx4$BE=vHi*TzX#tWl2b02+GX%%7(VOT((+D_#XVq2@bhfy(X2_&8 zN&PTQvX*?Z2YahO!(}8B77|vrRYRCImM&=WE?@0%9HmBPRN5Z1W&TKEfX*k5deae0^HhV zLX+AN5KjXtXKcZdoGWqf3Qju{bpt1x9P+5lPUB=jBFV-8C)rGZL`T#h0?-c~kPZ7M z+lX1wV1ns%G!gEh(m8|OWOUkYeGou6yefrBBNL`Wq{v=#`1aE8!)oq1ZnI8j-Nb)2 z=>!2V?~>?WjqDXa40^Hu00Zbh9jdf{G{<2lX`eA^97eH*$GY!2YMo0>MbxSE=TxHh zg9OjaE{msc``bt-`Y!M)>Zwzt(W=xrj&2%k7Wr_I$_d*B^sQ#HvDIoc-<3br%vTNd_!d_fFLGx@PMcgAQ~?FeG70)D%HKwlNY>Hnn$fO(0S*jq&oBH- zu4=1RHL3@f1GvcSS7j}tKQT~hD74i8Ky;S*ftX!iT_aswnxdzi`Ne~;m|Kgpnu>1I zALq4fs^^N%4|8@G19-$NZPip@Ee`>sg@l(l8{;W3Ng({D>wYNdR%{J+qx;e-pYH@Z z{{X0Zq|TvUo2faK3r#Q4f79%>-rp!YujsLfwD44-ka*Lxkfqf1O>{P?xs&R-D%!7) zs4v~RqeY1H?7Hh(>0Gng{{V~}_X6?+2CTJ4XRIENsnVTCMbK)SOm1y0)iwwu9#;=i zqbNfLp{`*T9D~^QT;(pI`EH4$u-x`EHHVunFIitu+QHQR1Fgd%4`s#S;Ohk3a&+#a zR6AQ+<5VONOG|Gjg}>k`BU9oEG%7cEcr7iSRP}8gOYrMgLt}>N9 zadhu<4CQQ8(rR}X)YOL;H0?QJ6N@S@7!*`$5y={{X%IMRpFm)I}b*S4FA#Z=~v)c2jLZHhG7U?Qg2_{C1vC zcgR%E#o?5u?6^-0>gelwV5_I5DN=1(l!F_OBg;&E zC3B6jFuF@;D4>HkKRHZ-J^8XWf<)&kqE92y0(j?(>6)Eq#X35Gokq7;p)?uIMz(jy z$!m1)3+eS*zY`Zw1547ztKm97Tg3b^^4s@b%~wT1^_yArX}+s}OUnUseTuQCqYr4N z*nF1={ZpClx93PaQ#>=IbY^M#cZd4rdP0Rdolj3q{hLj_z}!UL)Ni6nb3a<8G%*V3IeS`?~j{{Z`^Fcy!Jf7$vor)3A}sG1Bi))oTR zp5Snw$iu-Mf2VXEZkxkBCqtp?2SHa)Kzvs=lWK2qxy`dHX0DH2qVV@t8tr-4xan#Z zxYBJszC*xV{zO2!Ph)D+bvLvYIi;n%5ONkgnWY62$LKniyTU#M(dq;L057N4HioIr zX$=z6a%~sR7h~}ALwq^o6F`Hc{Qj-9I2(3>Z@TU}&Xu9n^x>h^>1ou`3{oP(G3>N@ z#*a_ad?~NfXz4PA2I<~f#)|?%)8w**5|TW_Z0M_ib4dQn>CXy?@XtjK7|_rk{##ef zw8ZJP7q`N99*gPU47869beTW>ijV&Qrq#>i-cG+S{gKIrkWUE$Oi8?|Yhxx-oB3pa zHR?y4A2MN7SQlH?QZfV*E{&vT39{G>for9-Pd?JB4aEJEA_SEpBuOv@yDA4Vpzh%? z4%spR(*)5}s`>7R! zn;?;uAnhJfW4Tn&eJ$vNa7i~%UFcKUJ3E;nBGD4m;0zNbEsDU6=NCY@kKz|Y!SDA{ zNsC0NwW6JPjLpzSab+~gHa01_ppZm@ve45UJGv6uf6zj#d{_?Ocu?la^i&B0D3B&Z z`lKm6lW7xxnE-%h5JiC_DqSI@!7;`qCNHANu-*Dp8vt5Lb}bVC`=P@UaH>SX!v`f1 zFmQuALT!*Oa*VB#NRxk5OkQK;obrABl7T$gD5^xPGj8W92|(;0+q5)KJY#&3Sgc|0C2t0Y64v_Sc; zmH5w3cj2dAkKv@p$F){2@rhu8=g5HtDsTIhDwQ98JIl-U@xAe9FTa@)Rx@M-GdQr5H*RUA`8!79Q zv`l?QQKPD;SpBi_M%m^qWyPnnrPI-^)-_cr^<6WXTSU2qvg-?om7%KXjygBgW}{72 ztQ(71TGpAb#_#5!*F>8gZND0a+F0N;qiAhwvxktlH+=h!uHT}MahkD||rDAL|rH@bR#R+6LJPQ4oYnjr0>Nd;AHWAuF&rwRmoYP7*?P6P!t z^}3K78n@J`5?Uz_v@bW4v#fvA>K$uT?KZPfO|5bGzG6s%IKf&S9gcTcp#YFx+3vS| zLS0v>@72G1{uBb+LG@Z)IMkz5SE5bmDBWJOU;r@VGYh zt!-T^>(kRVr8;D8a0UeTT4%+MZJ(uRJo%Tz{ue4fg849eIgND+e-5E&g18+njPi2niA31Dm32w z<#V1AYrj0w$3YqO3xRNPbcm6blDiLs$U37i3BvYsDuX=GuO!*&9vo;sBI(nERtfnP z&-jy|=)6VMD$y0)r`GA1!skSYH)`PgHqd-W)4;#?lLP)zxsMQdS6$Tl{eGdM)w@dI z#tLq0h$aLaFBiqCZ1?<>_U0ccKOvup=xxxTBl4Pa!`KZ)tsOjHHwO-b5aBk;e zqn95*E`ep1?KULB3%h;F;5B|H=oEF%Wh$LZSln+VtYP(QMgFT>sCE4_YKr|W#rk%E zzLiOl>Iqt_=sc0y8tuoKRg9ahO%A4>t4~g~IoRs~)p33(kM?h>5+-O7Z*aLsPiY<35#sMJhI)aLKfAa6pcM3GGJZ#t>gpOj zKUC(4CXYfn_v`?Aczo5W)%yF=g}e~ zkt4FEfTE!m%Tt544TwrEhm)QZOna|$CNP7RnfZi(2G`=@GXg?|4YEm}s**ggJIZ1& zV<8p}z9Q-iw}cTKrXYbAB}PkfIm)KC;GBgH0s~2&P}!A2=EB3eQy_?fpuUB)bAD58 z!H<%5Bm)Qn0glQARx55Nj$sGpo%tviCTu-aYXTs8k7SGzK+g)D0~7T@jv$p7W-IR; zl*gb>41>y|(ZnQEp+Nu`LrhqRJfec7@7ewBGw6zcN08B zaECY%2ZYGBa*UkU*&G=*{gN=|`|~Qf#junTEy6~Y#0*#ud!|SxOnU^wMA)VJMsSd7 zg2LS4EzuST%ZgY&ivGy0qc%hwm`2hw%55OPBp-E~tqMq4zdR)Ze97|&0yA)uO2kg% z4dE-cCKKIx;X6SjT}AzpM3H%v$PfXG4mm{H$N^d;tWo|bgf+4Vi_8G(Ko!3$bUPy6 zicN^@i)DqdF#tHBu{jBn2{GCw4E6eVOG9_C08T9`NfK{v5FI4iVNr?C$yCr^qD)7k zMXk7rlBwiICkjRJ9?BKGh@H0s(MyPs05Ewf#B=gbGY!9VG*x;efE#CUlo^pEY4~ZHwdVOpT&7b7EBd*x9dj-OHy`laR>#ZVZL8t@& z0K;-<9*>aN0ItHjg-s@{`dVV0eK=uYc>~IC#medQ91TiSX#sa#4@er;wzi$S!&~O2 z{5Y+0#rUI)T}amH-6+)f&eT+6nNFLZwWoBDC2DmFTB=O=<&;{@QTX)1s0o971(&P3 zw@a!WOH3%b?JaNVHn>|FV#n!efYv(SsEJ;7eVIj|mGV;{QX`k8aJ^_^6;@7#Ad3lrUL?jW?0=7^QX>O+l6go6vvk>%oR zNW?<`v`I5)27W&CYgbhGoK;o8l5l2>0LijrEdC~UwHM>lh@9v(rS4Gtew6E$dfrssIpPPSW z=BMO!PZV?y5jE6$kN1qI$!_4+*xhmZeN9?kN>(0&3z+dWM?0uF`hqJ|_+SYU6UCPe zL_#AmH?yB3tsR%b9wT>#pQ?Q)OEq-<4O%b@YB#fi1Qo&cy82ySx%Ft!6j4kPf*cU&}tv2)zf`CtRy+E5JJOn^2W`_ z$t;+3nuh7>yb61_2mmfqSjzRZlnW^q(_>Cd$Qdh+(CP&$&Ho$4dIXf0ExPMm;uh?{t~%g5$ZG=zOAaz^(`xEidFRVX_mWd{{X!ourc*s zABq0CK9|gSSEHTqS5U3g{5Pi7YFr&v@2Cmx1Bf58<2+%l)&Br(XnKB^sMG0`-KMFj zeP_ftw1{o{u4lsU(7&P39W9P#k(-#%?AgYC@~f?C-9e@{wn?xz;IBo~ zx;&MNI1dDMKBdad*e3OgZginC6lCnsPqMlgLH; zc6mwD?hOYZ;c~hy2z@74dnTB3SR27GNh_IqIxBSq>Z-PbfxS06A!_w*v!>LxpHA@h z-Cc1ZqL7v{L=sA_)(JG3%lIo=bwAov$NgOj&1*+uTO=-WpIFdUr(N{*RG!T{_Ejx( z?05K2Vz><_N~h6$BdVvXt5vRZT~|?7fb*Agx7V`i`uzv{F6}o_(^qwFnXB4fH6|aq z;DF~eW&xStsxiHqZ?t-wx}J@dH58qwq}rN#Vy$MEeuf-4`dt+IhK{S~)1_XUpIw{j zG&}XTxb7EJrQs&%dT(4hO=WFPx2R~&oEoGExFAp3>!^4iuIRcYU0pu9=bdhx{5>%Z zFWArsEggiX!95vrYb~C(-ld~`I#ns^XuXwR@Yvw+L*4!mX6Mmyek;_`tKpueD88Ha z0N3p^wC)lQW9YgUU+X%JCW}X<>KzwN(==78nwo0#>K8hbj1y6ya~;af;Yy;Xfc#YP zS=IMz!EHwfFCI@Z_mEL2RBY5Al#)jW)G0c*Ow|?`Ev8>S%j({nACl3LA-@5U{{Un! zma2g6mDD7W6dnB+)Y<_WEilAVV2<8Ux%_2@&i?=_{{UgMA=CF%PS7k$jABUb%AsbP z`r&#R=Oi}Dn+|UY{{Rit*`xs@x@cOg01z#IOKl6c8j4d!hs+8e$`8wa>Dk zZe=3-dZ!m9CeU7H5$nk1GE7|kQn?@w5MV?Dc$=nsr;g=T;T}+;p;;0KDU+EQP)G(0 zC~~v}lLo*b!-0z+84yJFO}NF>4SEw=APXYW2r0MAi=swAP^XF)Bw-_BBn$l$CLs4x z^LWTAqNMg4TmBRyVC;n0czqJ%kb9=ra0K$47zF&%00$r)iN(eU2p)rhBG#MVsu6)MBnNCO&aGM8$8hsDdQ6b*Gfg&9oT~QkafI$BMWPk{XA<0hLd4iy1 zf^wV(nT#tdVS+3^c|#ADe|1c0nZWi;&l6-eDKc8wCiXlj0B$%{az`n;V8Fi$PK{De zB%GL-_DB+biGvNaPqoqwl0=YIpnE1qGaR5|Z}U$aNaZI2NZKHjwnQH>z3nTG>$II) zq4hRN^XV%$>rKm*A_Q2idi2doRjGh{%y5qK(h+zW9~S9Pnu*1S?7P;BQd^>;DfMGS zxq@0)*q+_jL(>-q(cr)f&^m6A*XYgE(P?dZU#K)j@&1bK%cIK*Y}R(3m2pz8qg>rt zrqWu*jiIFT3kHGV`dVeML_)*X8&;N-Y8ZC9c7e(CS#$-g(=uExCOa=PyR*?7$~JF| zt^=yH5)i}C4k1ERjQ>szc(5mq2H0YAc57oE<1$DM~KN^ejv#ve6H2_@g5epvD=Cl(4 zx>nOF?WRF&lWCl(sKCGjw+SiodL=UjX1TRoL3EbP##Vlrr5d-glbyC2=lp<^3P2MA z?lU573v)q2v#OuVS?ysY1N+S3^td$-n(j zCO)fUsIb3Vq74lII0J=j+}^U$uvvu91!c?pa}3hc>N*;4(mn01riR=_*7l9v zTsPrX(C%#R-0Fd$-o)E1jzTfbDg6(r;R#-Ne(T z+P4FSlRtHo{T*oY?-#bFpRBx8rLoRvcf%)iPA(S>OK-zFfs(sdQKmIrV!LWF?|5i{ z^SQuXaeWg(OQ>~fI!gMgT3U4-2T@vf(;duRdbs7|lZ@Av4Q`dy>U74u>Hh#(5LOGi9V=4$RO{2O){_+&Wv&zYhoa#1bswlLE&>_=N9!kMtE(60 zLp1`cLr)LNkv}D<&`C6?;wuu? z0VBLFZ^4_tv>sa|7T?iu4tada?0yL`uc>JA7+CoW^ku_-X!KtT-^98IjJr7f1!nQr zOsA{sVz!4}Pfyf_(rLb@!@GY;1leNnm@e@?ig?~f{gStQZ`G)2wUzYx?ud0X3t_sD zcDiHRg1jFMVER8XRF+Q9;SQNorFeHsq^#z;t3#aUHsVPqBRO0jkIhDFv@X;;T~DNZ zH+*+#Cx6770~Za2%Xk8%TAmTCj*s5W8j3iPK^$>pacYIr^EJ$;PmwK}C0bTWj^YRj9U;-=$%D zNF2mtg~#-s*0(f2%7pvu+S9|EODmrX}s^+nd>mrTWWX!w7_JxEg0 zYU}B1KDMFzbd7suwCVyp)mlgBiCu%B3Y7c_;&#^!6@RqLU$A+1ZeTI#A#z?f)^&c5 z;*OVJrD`kcH2S)st?BJB!?X)}gO$g0KZ@_e?+{h0(#O{7rS1oUT*61nVH1C?wrh!g z=^RC4{{Wh`;~S`vj^r<@bmRM)O%?%-kNY8fvi5`1b)<|_v>tZ_^;35c+0NGf%a;fG zoxVc<0Lorxu}Fiu(_}JlA2O^77d-I0Bb5<>%Bztn+;b=0B5n4CBvuZw7ERX&KUCll z1@G#lx&*iU(r2(}^8+|kZ!ia{nKO*096^}5QKZ(8%Yex-=!utnlXRFRt(6TV0(&I9 zh5>NI6yyMcEh0UTX9Wb&BiUabsR#izFc5$yWdcFW0VJj* znB^fPnSebM!m<)vUd6qGYR06cszDh7K=bAjFOOwp$VU4HezN{ zbDg?i`zipDf(bcQ7MP@!Ap68CGjO8=_D;xqy%44y*nLo{K}bY832`u~p|J)h(MXsQ zp4-iW0lbpt!3Jc1J2MgmnXN~ zGB0vIiAAJ+l$r@4xuwPk9*H!#10a0U0{ovu2LAwvk}{elfD=8Z)du#mCSo#{-NUfq z2+~wbNp}O?QN&E92GA@8(lRUvDeN);%mQo>LHef_o)GCHAsZOA*&(7u?vsg}CR#wg zPyr*=QTry)pv26@l7qX5R%N_k-5C0Q5TzsB1hg)y;ZT>N%P^&t~?6wZVjfCm3HRYJX6?mF?k*7vplgY=|>MztWpB2U1aUd!^LaXSSVaIEP7Jx3FQ|Vg1nOM=*(=_S~gMVdj z(&?GgSy*a8tV|XFQR1bba7$*WYn~mkj*e`l#-m&&o8~zSW|e=1(<(8KEUfy191Y+F z#u~MYrS)D&^;xXZc&!;*sHn+vX*;$S-jAwP(^oZHKuHkmWDybcC1|y}s-h?wj(Ik2 zev6#b_*1Ubq90P5s0Rrj9m1dg02C~>6HA1`{;#GDZ~kkfKlkcS{{RxidcK?g0QJq2 z$g5J{b-4ck3v2M*MF$^B?4R9nI{t~&G-1WvLP9YxJ(v=|zD7?Kxpsx+hSt()ym8TR`ot zMcbI*E(++?M9OL8#jk#>HNTj1@g8EeLrx?Qb=iDT;wrs)!ltS9s%i)&?|ZiwAElRA zGFma{RugY#)oh^B;sZdrU6(;dl@>JWn97vg+lINq2Xh>*wN0mc4YEEb*xp@GH$L(|nf6%H;eXxNDvw`%Q}7@xyeK8`5<8HwRRw zrau^u@e^;d)$n{BbI34DeycpTxSM0f3-td05V%&T_tUHZ z{_=?uLh$@qFB9ML;c`sT@PAd*b$+K+;i8KMjvY){t*8hR2i0>ubHqJ2p_)|srl;XL zpxoJMJBBzOi!X=zK9{3uMIAQh6Jq!Nr!D%lY1Kd9(_`dYdqKC&c8=|t`5cjzcq%%7 zO7RYttN7PJOQP0gO)ur^J5{lfxxx!(%e)6klL2be(&^~9niMHhBxQ^P_FDkS77(=B zc|6J2WspG}N<<5(ZZ3Yvoy{U|?1ZS*oOg*B+PxE1PNs^cxsDKY^o%+8z$>B9Z&7DT z)abeoLa)&Q53N$Zt=q$z8fBoyU_$gxrKLLU1zj<(Q%)*J_q1IOY#u#VR)2ykIfj|> zH(D)l+F0tKw2sAYEh8@{Rq%gM>m4t{-8ZRzb4y>RR(W+j4$t?B@aQ3?E^N5{MJ}P! z`pdd^P^VU**Ejn?jG_MkTTRXfWVnd{8Ig-F)X&3#OwesEx2ywBNumQA{n}_*QZw>7`vFN%fqaG~P>OD8b-Cs#pMPFCZp6~!6f+{rRTnjB9 z4z={Z3w7;znCfn%?$XyY`L1G*fx4cXQ(sr5@jjutokR+?1t1+${$fql^=^thJ>V*x zKl2|PzgR1mmhCQch?`HR-E*5>EhDcCCFG8-@PD#3rr?mlZ~5B3sd0ypAb%zDg?&q? z=$)r%-T9i7*nNY6{!8lT{{YrFNr_nCU!{-9{{Y)Xh#UY)QzW)N=@Ae+rX=8qUD3@v zWG+m!!Q>_hfj?CuONkbhN3&rj5^qItZv%xS4mj+G=J0)x+Y&}pYOoC=A`JSb>k>h? zz7e)gQW+Nm-9e#?1NZkyqTuE@?5(ci$RgtY=^DU+GaGD&3tBPCE)Nh;NF-o{74!f| z5f<#6!7>e!_DDK2bkR`Tcq9o_ace1Pke&hM2C2{)ObO@ckdiEx0pOlg1ru)JDYjAo zWS$jmhTauaLUV*R0zi|5SR5dqFCR4p3_2haaG5>OOpo$QA2kYA1ZDt850?x6CTNy@#T(J&8j>X~RF;Lo8 z1lc|bRA7^2y%Bv5Nu8v6r5Tem*;t7ao)rB4XweZ+L6Au?*+^+7%E`|jiUf#)J;GL~ z6UoK+m8AG@tTrs|6>f3#kxSa zT39cnvv#{rE`jkIKULl}{UO?#jeE6KT-J@PYusJ6ya7BekJ3*5p-4oHzGX6c)U8sxn&A(1%S1-sWz@|6l1=ctFN?#O zQj1H8Yk@H%h&KZJh;4@IQ+P%@=`{nLPTC5E?1#+ldh=u zzNf>}uzBh@NFJW0Vd@<}QKZ!hdYU1Rd^w?~b4z=J$X%DE=_^-dIy5P`frhYhx5+GE z#8dQbY4tREZ4*bN&}|>_Y8)o3zJJ5)7NtW{tM}EY_q)4{uJ6HDW25TCl@xgw1?d2I8#l!Q(rvcvH!>4Eq!aP&AUyLAr( z^?f}(>}@LS7bcVD7gX12Do`brslARF4qH-BOgs`ejaHja)B)ZbM!7a1Kyh7iwJO>j zL+O&{I~HAy^zR18g0=O!ajnu8y7lRaX*`b0qG68BZjcMQZ&YD)mob}30>6qnKT{*J+vgE~O1?hVnymWzK7~x<>S-x|9C^zc3B% zd&uE%hLHwtcoD+SjU9Zvx+J^}!cWa?vr$konD~V4#|sN7hd2X1%AHIznT4pv%t_Ib zquo*E&n_2 z`{CN)rp~XW21cR?_gx4IqE6Et%DH$JY>!A@CGcYxhI_xCHnNWTI@JxrHKZQ@01*EG zRh^x1IKwsd0X?e_e|6IQJE5nq>K_MC?Tp%7_aEUB0Y52NIXkI@aTMyr zZ*17d60c8O;xRY$HuZG|J%)g!g0Oa*%^}TrgJx@|U{^<(;0Jl1c zB6^mg-t{IIkkQn*6AmDZs(z}VcrB`9ysW>AE~G!rU;PhYZ(8beBdKa#1D8xE{k_$r z{{XCY6Pv+aKHY6WijSVsvo~K}K^2}1@ZzQMXa4}$f0-WOdjmX2)xi<3t0NND{{Tg; zt?C-ypd0zkRa&4K0nq^a1?B@?sB3@g>R$7?Y|!dDs1aVLdEAxo=GG)1{D&NM6SsAD znqQR-aNcUPiT4ZZhX}0TO{Q1G`YM%mI_)>ad34oaz4G9`*Dxx)6L~AD;`~riJl~)5 z@;Ok;MD7xqX&@xqUg@;RK532+jDlCs6ixP&i;Bnng z9Q&e3HYy7onYRfB$GRH_Cg;%r18uPi8f#!8K{!->FozOEHhof(Ei>~_GZ?JVs5F6a zcHP*td#slhCQ@sLLZ*npB2)+jhTNb`BkmOu1|>q(yb^O^C%S*b2J(}Dn;axWgLw5( zJ&Xb}WT)?O2oN%UXdozKvPeDc2$&qI6Bf8rm@zTxl!>I>gr85U6?uQqLIR0_d;JQS zxd44t5fghtVm6NDLA(o*F(`2Wm{Ydr0IF$_LCDIA>jK;fm^QK`0TDc+M8Jfc00u~z zQEe10+7z{*o;#~(07wZs0J%|9(Gv~1A0Cssn#0k;+a0jNE!9 zxul80awC9!N{e6swUM+FlAA%=ap+Q+pRA!^aD+evgiB@x%0kYLe`L$7Aeruvv=#tM zWjAmIl57#gM<{F-nLkAT0AYT|0GOY22Len$`Jm1cYhlK2J0NUA!pE6TK`>%@Omo>G z10aFwq3A{?TlPS9*xNrafCR++k-7{O0%2pxQrIM3AyByyj?ks;@`yg@MC_*vALLZT zLCS4tX$IG|!fx|>CBBU(Y?oQG(|=V22nKJMR!A<3N3w^I48T(KEaUp?=~L*s_f~Li zKg-k(B=A^5`13=n6sysD*w<~^=QY?cac&pWzl!dyQ^TW4Mf(P&`zjHgpiF$%%Uwy0 zqpYgNmkq8UE}V0u(dXrYZ8mngdY+B>desL|6!kD(DSQ{fIRG3-y6U}KT6L~{T7j)~ zM{jUwfj)t0Inn$^Cy=>n9U`8mqn%?5+V%%c@<;Mmj8t9Nx8!l%5#JA8r!6rrZKH`^ zg!dBbNbbDbr`8m8Yi)T*YmC(rdoK_9E*?%ve%D=lOvgIAL$EZf?;G2_w{Vf=9;*cncAHYy{0g++ws#hhXzs9+ zS{LKA6b`FS1GL(zw9X@P%c|E>>J&{Lv!`p!Lu)j7fm+&bt)bS}bqzgcKBIqxXXLei zwt9V9!hv{m$t?tk1ooavs=>5%uMu=NXev!7d|D2>UKar&wZH^fdQPd0Z39NN=|A-P z9_!CKwwv<0hcILr1!^s~OsPDFhq`NP^}6()_coCq6KhUqy3_GPDACq|ra#?Xpc8u@ zKv&Z`;pu%|tyYGKcc-MyNWVJ@0?I?HK0FJE}LdULgFqqD2;y zO}k{6C2dgay*bT|R*ml_{{X0Gy4UH==yfTzn#cSy8zt;vadGyLxe7X83>QURV^}zJ zJ0C>ST19xnI`+BLb-LHo(^7kB@Cg=MbUH;Ex-(w8o*X}gjlNUr6}o1ZU0#-rtkh_q z5xPW?&(&^eH7uY>d!H3p9(K3^xc>m)yC&->;Uu0>*2oR0LvGo!?Ak7&&<2Wwby_;c zyJ>*uvLK#$U2{;;@!wz}=Y`ocT|1!F&{2I}rB01^Fc+|ig0noMP;|`y0G;r&gdH}h zJG=ry>%Cs4j;5{e(6#N;&4cWC;d9^E{SC$&)+iXi>Ro@U(bH+QH7RK-GPNd~JQ)Vu zEOEv*)f$_UM#qOWdVL7}T z3Fx(b2SuuV3XiE-f$oZhqbJdE{eQ#yZlBk>UWS-)r>qiA>D%Uy*)bMfXGZFDwPog@W?Bgo7G3(MgtU!62nw@_-wUSF=~SxKtOY~0k8=x7 zL>QCwM;co$Nj}aqRq*N46sXl}o0|(t!f3jyLun5-S0mE8OS*=s!xX8>FE{~qE{W0T z>h-lKXhm9v6ICOIhRP_3E z+(X<4XbXbnXII9KiTNti9}LfMR%(4eP+S9<^Ig;OG|Sqn;&;joV4Lc`YjYa2?FD{g{IqzM>Xk1J0nC&r|( zaD5bq^&JygcO69_J@>VJs6^GNAL2i{`17aqw`sb+Ni>La9ju@M9nD6sv0i_?eTafq z`J|mte0Vsvq&ATqlXlqzDPf;}$Swq3a7U?@2dXS*f|E!j%tr~6HzMBYGiYo#l4TYI zx@AFvU_kk*n_kMinyV^C1-?o}g!B8V1IuxAqyvL)6hx9qwBqnphd}H-?vsN-9nX8E z0!V}ON;1|)FMd0PL_hsiJT$C1L^L9JhmzzvA+uPe8cRbW0F&wNElG$M0uhCNY3jCRRoU2q}KV2l9XB& z=mJV2ARL~^k_<#9(hbPM2J8!C^d8jF`rb4AV?CF5-tetfS=s*pm3S|SXmpJhxz#naYPr=8?X=vz z^YJ+#9#;TJHo`Sp zcT=S5p^lI7MTGFS>H^9|&8p_b#ntskJ%W`T(#n*Y@+T!8SA=OrRh%s#8;F(LKMvkM z+5t_(kBdp{&!qnVCDp1p&B0vff@}OQ6LjL@Vb3f+#iT4XgGuI&*VHrzP1JhMgGlO! z<7im=dz@TcL&0#9>H932IzwC3nsf|xDrZ9aHT=7Lo(Rq;i5^6Z(jtM&(z8vksX~=ch}PSDWpfnt6l&<2z-g8puO-zskm&ur z7Z0n`KSfp4YYBT*lL?&(h!=(jDDqZnWssS7#^w z%vTTE`m7$|aNSErG#b~?E)RPn%sG&^Yv?+bq_*#<2mE;Yt|wZ`nrhCYTlEMhl|i-H zO}4U*{m{H!q0sbdI$DOft4*6h6!!stb;or-5a~62bEfMV@gChVm&-sV^I*F7hyMU3 zdX|~pO|@U5d;b8vZ6^To*&DXarZFp&>Y6^8Mx{#BDQYR{YE*Ed*Lj>9Hr*DO%I75* zyry-9xa{uqd{fc&Z*$wJ_$U7W=Cpweq3hj2`nunhCHn~<-N*N`?2kygeL9AX)kOjE zol(;T&4pU69+H@&Qm)`Pmb^~@8;_hU!co80!dCXppAG9M=~=Gn&U+0?l^W($#lV6W zXz-JF>h%Vr14(Q+A8@?qUqzI*)YG`So5hDC(5~P3YgVOQZDCRGb2fk^;{O2nC!3Dr zblx+0~p4+qk)vPejKf>(9zWHE%>bu+i1Gq40T?))P4(X@R+W0?Wb4CZk8PG|r&k2v!#_bo`b^ct)@?YCb)J?z_pzvZfd<4v-~s zPF~H@I!{T{(SO2iU`Y-wAomFI2AZ366)D=}?Q!73dOGyKF-m|SY8hIl@N*J=*j!hQ zx}AL$dSguD%7MpmAQ6ChUfw=8DI?E0D4^(61r2{p-FClOLYpLhr+h}?eLW=9c4keA z`5VJp%|^S$-4XhluBd;sH2MDk+BJP|aPzkJxn8bbKgU*Z;-BPhi!uzGJJ~*e#FL38 z4b=kxLrj&+tR1vB7#I`T4K@-!Nj6L5{S^cLBMB^7HV)zyZUwOddnw#^W0Xh|K)MF+ zLl(g>t0Kh8Ry-K=OpqIPpLHQhi9TsD2ePaVz@*m+O{Z`($8^!0djLtt9ncI^Jd}YG zU{xF2o={oX6Cj{~2r()|37G{kZ|b6M_7gbog+e9`ln4!)s5P)8VzVpJMW-O#ePr7+CFg?^e7T@@S6>dJLO|YneJOOX22iYJdwmcbz=69({9N#te#5k91#^LY@YiM-t~a!}c;RZIkNDM8x?E{Tumn@Hqm zgtzF=&|EeoDHa3~G3uxhxLA9s86;gvgQAakNCSI9qiMfoGH3ZH)l&_oCj)?`)3*6l zBn!zM(gw%+B_i=&#Uz_w-7*Lkx6KehJSDngb-}np3}|A+kpTBgbAzSd_@wLQ0Kp1b z6sTnX0EtPDk{l(+Kh-fYBtT9H`xq@9i1td{2mu>>(_&C;0sD4EAh04##l2L?GjI_m zeH65LZb!1qR3}mVOK3d~fhNOH-`KC5{{Rq}2T-6|W?F6YUq*f=vCr_>764aaKXvoh zjr5w0MSVHb(l2Ohfh`h6(;P6B)g2wq?Um>~I>8D*k+Sk=>j{{V>Q7LRBj&2_lw=Hn&V*55x0KtJS>)IOzF zZ!!ZzTRa7A1kfTotj*}%O1&XghJ#g7oKE&qbDAyO1*s^;nT|I$+BGUn`mO%}57ZR8 zzLn-$MMlB-!qGEu;kKhJ;R>{e;p!=wf*#-9a%#@jk5=p2y$I^!m1jP668&52207KKv8-_tX}O>c)7-+~oL0`N7HMkyOesHF$7$Q~bl@6tBYlC1;>C0H z9Nk4Kbx4rrgMKWyTD~1+MrvDK<>s+zF60m*eruZ1Q4}arrW{FQLvPFoQ?89u_Hr5$ z9kgvW;dIZ%om6Qk>1h7|);-ehz)J3I)Y8!D9bU0(S^+Y7UJv7by;rRDs`?&yqyJ?2qh@P5ob3 zUP!3sx_B#RPRiQWI=w*Zgih>Fhq^U>g-@vK_4Mfy^GUQDE_ebw#9}Yq668-Jmy4r-()C(&wR9@Jt74qtrd=S5 ztlGZ2r>M#T46>ndba4b(Yk0e-bo~aUqowNq05w6^+-a5(=s{Z^AL)8NqtaYnT-|$W zLu7!*GA}2-61-y{CR@RStaVyis#FCpiMfIuxBySBmhXf7Lf(t2tx~HEM!ld0$GXAR zIz=rWn)ILX9T4gA!sGOsI&Y>;TJ)&Fh;~Zflw}j2g)`Use~&a3s5-MW)tCX=Kn?z= zspI_(8lMWSEmrG92LAw6<~V@xvgn*f>xHdTk(2}>clo}>LM34#|JE;-yOUov0dZfU%*XXPLU!>Yg}tZ3%x$6{);9Zgocg01vY0daAlwojCk5(NLc& zMB!eJwK38)LC#=!BWq4w?1^zjJ4m6+D$6@RMeCh2O z>e_ZyV_!wInp`)wT=|TykTu(>+L;6s26&J2UAw7kG%o2*qe1O8O)yCV(*bG_lv-&7 z=Z2Zb_#vVGG3ZQ>)zmcOIR5dg>+5}2$escu_}9bs5_*LX{xH6{1WlqiE5FN5(Zk`q zx-Zc-NWr(NhZ8n36||5p2cq($*N(?9EI=uUCSgki$tUEffGjeh*a<$RHv<-l#;6qp+u6Dp;|#gy77azLHZsw%R_bQuw!nlfPF6Cj8Y>VbJ2!~&$5 z_sSjr0D=UAe5f6a8+Qp>Lr6a~5@R1^_XBuJ1^`9GNI;S#oN#~%Z*O!qZmTtP5^hMv zkl_N;+u1NPa8o$MAgi)qHxmapQcRN{WleNS3AimHc~O?f@^TWFAv8HCHY4^*Jl6;^ zMWhtqmfVs%pvXY>DK=z~oKIv%*dkO^21(`+S_mhIL`=#JhjkiFy^>@Rlo7ay!T{RY z7f2rJGT6YhNQBLg34tb07g9{*VMSmC=ah#a@`;y!RIWjsN`m?-0X9+$J2*_6_xa%v zL9&QqK=-~AH^GtWkd39p$w(2AiBYu4^eOC!Ad4z-20%9MqiKn0fRw{-hDqc=PG&$6 zWi4o%L9(wX=Zl~$8z5YOG7^jXb_!x5c*39#>mQmZe#C5LGC+%6A_+H-btDP7k?5Nn zVgQf}N}&^cc1YNHXXJyBaFCz}-7XVJ^ zenl2)CtC6Loz?t1QLJHeg+>?xZ7b#v6Y4e8y1t^F8ub{^VWF-fN6CGN&^NRZGU09o z*T)@vIlXgVE+Ds4SN{NvuKxfZjm^3|{{SPEK_+okH9c4uI-N*#cQU(ghBXzcbXucW z=lnqD!=KAy)AL+Lo5WpSl^@FKwKCUOG_j=oi4wS~uhv(oQl(0@9%5bWw)kF|&2uNC zl@sZIa_^yr)=q!-L<|}%#$%}1)<>=ZrAE3`~n2?E1m208ag(p zSngrG%Hhk2B#x1Tas7^;py{VqrE~r(DcrP9XS(zTbqWmWdU_g452IPMMH-K7UoB5A z(sss3T~ET@E&WB5+SfoXB0Yqz@1gMKozB%$)oba~ewvu7_HPU!q{QDU=5cD14k);{ zdOYu_bSHjpty(W-Hj`$Pk9DPAr&ZGc=RJ>u62mLKQquHJvr?{)L9KY!wqcb-2a*00 z%IY0kLtm$AUrM&%L5wYND|IJKxg^)^9HZL03;sVx(o|(uxEf8z7Fl(a-Le2N87ss( zcZfQVO(B|U^;*IFIUhyJx5u4d$_Z&aOJOqjvb->l$Y1hDr_$2DCmqVt>RP=nf{}G< z^uvI_<$2Dt#+_GC&4#IGqguM|r(LPkyt=(gj&aPYA0G0}zmp&MoX?E>MXTzg^15K= zX~$y{x_9<|sJeb0d0XJQ&bf3RVP|HaQ&yvyKt871lHLN`l8zfWN0Lq2M-F;kuW==c zM@rPr=TbXKlDW#%s_EL-RMZJ@oq^X182Ys=9^=~SP)%KzoUca)?w6};j8Y}34wtMa z^tcaY&1&7L(>6<-M{rg>HnzK}5xvkjQc3B9laA5FJEv-k3r!~nM+!$y)t5W_qx4yB=>Q}E^Qad0*TDO;q;@-(fZt^WXLYKQPTf8{E_ zE2`QUD46fHc2;ZkZg$o@m>2X|e>tcQimo7VN*rC)hnuTN;Qs)#>jDUAJ?v$35BQ9G zXw#_n(-hhO1D;l9>)s8>AgUUv0FWkEN5J1_HS#=Ojw4%3RaLa#u#y4(A63b=rCl+l zN|+^^HxgSnTO(fZU~K_YdWA@X5yh4KR@XwlNyl{-R@CWO{ikzYK>&T$uAZG=jsr{C zOaqC^=YOHmkTb|?%lPgp-4F6D(nnp?n$2^mg7VWH{{Wi9Qk_>c z8^QM371JJd4EU#Muc8e#tcqK3vpn+18(xpZr)24Qvz)uUX zby~Z+d&k3TTpS3s!q4F?BP&)Wy+$;>q)UK5zbmAm;9@dTI64e{O`@LS;l<>w#_8+) zJ5zjeLrU3W9n<>TnN8dN6+$F)b>6xyajw@r#`xtSwaqhmC zWq}44Z;>x5Is91Dl1$1kdAb`V)>wpti2h32Lk2tQdm9FwiaE+3JvZuqase}RdF-yrWbn{ z5;C2}2#!(&&Q)iF=z`dZ^6_+DKdO>_ZQTaMnN1UPFxUd}gOEkNQgiJiAq^Ii@8Mp7h^VkH2Ma+)jX3`}y2 zjFhGwnM??k1%}aYTckmp6+ys)XM~CK2s|KB79!>oak-N}$qqM&0(o{A)7;37aSzX~rG!nhD% z3#SGIH*SNELxfCnWA{ncHj+IMNQ($Qt2KHyo&a+l(f~}GAZ>|mc_<*s93_g%0wm95 z8MMvQv9uoHNY9wU$zMQrf?`1^fJMYfKp>ctKB}rlLW;-|Dge_r7hfNEv4OrO>kJVM z*4Gx}`^PV@fdGSiuaUfQPiKpI>vJ0V{{a4@m%HLW*`7a{AJH>{(?8&ADOLSJjtAXU zU2O_=Ywf2~W7z?q;FW-lrcM^{(&^0_RR@{Gd8Iu3iZSwI@O~`5Pb%Vpq0+j5>D6o0 zsirkt=Iy4}U|%I@^$ww_)l``+X~-4}u^N>2!EQZnxSp>MrVA_pUo~?nJ3jX>_>N8( zyLdZ4;iW<;g4x{HR5{Etf0FARL#*m6*EMQXd!3Itm^}QKKlp2^qo>ztSodvfs<@I& zWGtR2@MfZ(va9rrE~ikJ2)Mtp>3%YPU7sSG#7>OOZFhAOt)}wI`?pJN2fFpI9q5^= z@V1|-IBl+3NyoX|FZW%0!(Io~RjE#DhYv@;v&iF={&${DSOHSr~v4~2eYZs941n~>a%f{r-M;z2+ zkkwRfqf_ykAQtWPve@Z*f6UZwpL7>gxy6>V>D2gi+W8dQBwN4w34Av?OAHwLt$18% z0+mY5{;@)in_H{XHO_k&VVj`AT75B;zc;4*#i^Hhj$kcj(S6)Z`YeP9f!REKZ7Lb^ z+f;TvOEvU*Y1Oi${{SW`?H+qiXj&a;>FVlf9X&t`*bB}8a?tqojTh5Drc9G+q4C=;~3>jskedkSp*h?5Id=~94_jS#ilUIVWq^wd4jzoqiN{$ z4++1iSELLtr`9n1wNs`gSnj#AE{Ktjp_Ix@{ z&ot`&TdnG~53gTQsH6qMSW6n;q%Ota{-6IkP>Nqlw{p9%-t0M$I~f zq{E=NK{6L)@LyN_hQ1uq_PopjCSi4ERu=`5MXt7K*7$yEZQBPR!3$E1C5B~6N#P#2 z&lhyPPUgQss%=JzaUs9Ru9L6pRcrMMnw>dy65tvGPXTZ=wbfkW?e7J`=1O$l59$|3 z7suk`c08Q(lJPsI;lVXLf6yM|E;`l|_g_)dF=GI5zFhn;0p2|502v2X-`KCH8a=*9 z`8DLI*ThX(l#v$md#2ZI7Z5*XHi+dowlciuvQ51f00!nk;V}*z*yTWJ2J!VlwlU}s zf&REMa5E_zNm9`SpR!|XvPZB8(0`cSCM;h zD<04=ebXQiJF14TGXOx65C-693R*>t?y1g^1P^q(L9zjYMoe&Wenrf>JDwCpP_(7{CLvqGl!~Df4y`piD)V1QBt{k&*kS5JC6(C&Vhv zyaS`>-378B@o=U@a+s0~oTiNLERxqZ+Yk@{oBb7oXmPqu0AGTIFJjR*;7BR9&l**&^#$BptLd zXagL_D0IjAl)a795?gHYgGqx;rbHxm#Kd+<;LHnzIlu_9QdB`QU4V-KE%Q_yII>eG z7Da=|i6GoW`lWy*NL2A)Mf-(0I5XKBDgeyZEjsasV_jas^`U0pyY z_(PsZ8SIWvn-7S*q;ur>{{Wd%XNdKBVcjQ1S(F0+1UQVFA~h2DRVr@3{_zn@>oycJL$}p=ot$yBb;=O!i%(qvhg#Zca`~aK27X&CZFX)M|8J z6HQFG2SF|1uHVwUL*hP|)>Cai%u{WHD>S-2BwcV_FHciX;eDc}*43ygZWc2bT1VnE z39VJfIV;|zoN1%-a*Oh99mA=3x5T=-PNzXtlLm=hdNE)g9(@ zi%Bx7vx%LRbqw0u%Z^r%t6%>BzNx$ZVwg!oMrRpG<(a>;^+4ZGQ@7|UeKULUWUrO2(scTDW)pN%8 zHNU3cb=4Wv_3{4zq&SK6Om!}xF{m->0apz2QhhB%78)gu_$+Qixmq0^ znmV*M>YKOVJ8}O2jlI6Bi#=1Q5kDON0NEeWQ+lsaIjj%6NAyk74;IMwYA{S#A$D0(69SvT1S}EaP_9&=%2|< zzZuwTnjZ(^(KKWSH~54&RYnrn^a`tGbg;nUsTB@anca&$jQD zS6}|v)Jz&0V%|Ty3cs~$SpHK+eSQ#EmAsOQMfjGEk-E0k>E*7|JOQ*`2(escHbb3x zXu{ubRC$fsEhn+3f8AtfP+>6cpQJv75&YJdB^i8ntmC8(IaD_DZ&k^EXlagmbqSs% ze>6W?rIWWoNyE4Thm z!ugZ%d{5#JM-9)!bbt76UrquemCuuBUM|dr5ega!i69kV$EwX8wX&N4-YVy#l0qn4nKpgg28tYvpbWl5mtU^*^*eL^OIT!a%^Kq2{ zQrWq%PDF%=0NC5vPq>wAbWk9eAPdM=V5ks4k@ZeM5}kv6chVZsmYs?s1CRnfPBG!3!$NJNm1MXZsK z;#zE^tj&+h>V-aEU#b#jnVguuLG(!A`k;$J0z0H25DLJt?fx&>1j!q4nU;$wBJ!Fn zrZW&E5}Cj7@szO=*g*1=$S5?eh>$pw=$7Hz3axzEAp-H9$wq1PAWNp!OpZ;`bD|?C zJEXvsQ0yX15@iO&Kq)kk4X%M<<^vs+kQe%JB&m{M&5&fDEZe$caRw)94G}V6P4bxp zK?mrln_wAI1&NRfg2VK*obZ!yNEb}B7Ws*u6z2COI2_#}Ne0qNcLr}B$m}2wlBl$d z`=FAc5G2}CaTAY{TWuv(`+U_@R3E9uCqRJ+gCDw?2j$%ihbIO;NsN@>Ng^lFB2N=N zm9k2K#EXm+w+}bALPg$DAc8$ob=~Hlu2j7U00AUH^vYH-ax zCvF)iCu7m|X6jz*akas8-xO7?r0e>Rfbz}kYl&^L7h~zVVW!fGuXBDQu5X3YJWH%* zqpH?@Cx`7|X>c+ZgW~a}%={0f@VR3d{{ZATT~?qm*S{rcYhE9vE+dHxBpS6zbilIL z>Qx0jGn+n(?Zzt7?Dq5Xc|o_Mme*Btd@s|3L4zmky7fwrktD8vQ|9o#g~ntOK1;5= zxU`YoeFVQ#@>2f*W+zvva7&y!sQ9%wCi%KR5(qK?NRZJa&vhj1QbiiB0Or9VUBDFt z9iY1$Nt$K7FQG07gN*zLw3gWYE&qh?m>suMDQWkXc(4DR$;3tVCWOU<^%>d4YqGmTS* zNla>Om50^wLmzCpib z&Go>4v#8rOU2f}yAFmTBo=$MB83U|yZ$*-uyr$Bj;k%)Z-Ut_0PFutU%_}$dWn-s; z3e8N*g_ah94Y%E9V1sLLwu~M7@MM4fLFs1V1x$bJ+`gA&?ZlqT=C8qt9zW^CK_RtI z{{WWd^qX8j5r1{hm1OP1jF}q%07P=EI82OpSmc`?Q;2|1$z!9lvVkNYRKx)SWT26V zGL-F6e`6A4K_I1l(H`=iMZS`mu&}wxXe+Rh9FLNrfrHJ|!?H&SG|os;K;4$ocLH#z z5L{zCN|w8HM+%#F+z1y{p}N3s@{xj2;};YApyZorcK~ul&Sz} zGcg1wd9>w8^0zmX5^ZT|^-jpRf-kef7YTohfx!7|t)43dZ< zEdgN|q6ERdRb1lT(k4Gh5_>8Fb9-eYNR!;-vaEdWrqMSZ>Y$P(WfJsAK^8<2Zpb%w zViX+`=HJwEn2=ykx@Pzx2M`F{t1D@eP5o4z#xwFN!*-j>L)3{JB@0XdwX$srL!Dqd zs{ojfc}fRp{%Tk(ir`Fyi6mml-~7?Z5k9IWFhiRzjH%#85JuykJ0=XYjH|I)04xXt z=A@VgV5*b45;M#dHWy@w+$|wr5DY03XcKf#tj-m&PKPE@85fS}5Mp>tlOBUBENn(1 z)=8!bz2hi2xw2r3Si(W8VQ@|Yl3+;};Z(u7zX>}&WE#BCt^`c1PIazTlYF0&L84?C zP7L_6UPx?#cwQUgu8iGhU#G2mM%UDRO5~iL7rKC3VtD0vuZ%T9fHa9Mdx_obzGwLk zZb(#N+34efYT3tEtEaBV(G6@2f!ng^^`E5Z-CtJE$_lh<(r5sm)0u_Mbe7b9rmc5K zrt1aQyjtdttx$j?;Wzgy=KMH*E^18od|1YEu1y_suG(aXi6!4v#`P*c*H?S_xc$z_SM35LLhjq-e@Q+&XaB}onrFlK(sq(d}L;w@wnU1es*4``JD zOu@Da)s+Z1nHHWQGq{WW(t<^jF3ca1QBVQFxCk<0-cV&BxEq`&B$6aTgZO|CFRNj#=Suk!R$f?^a* zuQxCXPMHSN7(|)RDZ$;Co20v-f=_hf#ZzgxPSXIS){;w!k=YHRNT!lnVn3PzaS-4M z^eB=HSx^up5R4Dd+D2kMB~|Q7fBoK_JQ|Vi!hMGkFar zB*Mtx=AEV`s}oEBWO~ZT2`4c;lCu+7TQam3#0U#J2dIO>z|GUxEbImeBwMoRn=;l< zK>QU1`1eXIB9&4<{5LP8sp1Gi!tlxf*h!U6 zo7-}bO}R`HH&{AbLj>|JnUlgoV*dbiHh;{gVv2FF5`LAn76~oBQgoA$q6s-ZsmZVfDGmE5 zA9KQBlN0k&Cz6;lB)W zlu0d$i)Bv--3HCDl_Ew?dxRXvJi?MHDglGaB<%BGC6Wm+5h0@3{ghNV-VfRm&9@jt z+(EYxh`sPrc%h_JfJ7Um0N-`f20~G|cA3AN$4yl_@IKj9*KD+kML{_1F!%LKtE z{{RI7{NFN}#kfTFH-rnz4YF)RqzuTIDVR6EqJY^qgpDLO8L_i@DG+1{kt(9b-&HL! zV<+87gDaa82@S!wGLgx?ZkPa%)KD?b2GcSON3xSQ_e-z`=9VNI&nhPAmLqku?xcty z+{#46U+kGbE-;X8!X_*QkmG&7G&o$Tf(85cP~f5ua|gIk?q)iRjlhTsYRi* zbv3>lAbysC*?Zw`;k0D_>&SdnS-M>>6b=aH#0PuAkm=&fuoBses*W3Ynzn=@+ z2T1(XwhaNZ1jq{+YqKB)O^wh=7Rp2b?2+?b5tW8dn{f40wr22Wva$wlN<>H}l(ufX zi4BI_!52tNi6ZHNfKWgN-!&PsVZ{1-Cjw-X?2{Ya*+2k;C;ZZCDWbqITr=#XwC$O? z1c762$uxs;^g*F*k&Ak&<2OY2JYh+P0l`t7Xl~sx!Zs6eHbH5LHckk?@g~D;m^Zmr zHxe)Glv+Y$Zi8tyzY4NyhCINX+}$?GKV=>53fj}RAjhh(NziSXAd6XC8v)%(5tC&_ zqra$ClFJ)f>*hTX1lsX1n@J$aPx!s#l?<>j)5OZ;x*#J|2tJUw>p;wQE0XBKH6V+S zvU6_+-(Lsvx_th(E*-Z7;P4kU)`JF{N?ZvWoiJf=<(^p~+3bmA#0Jn@WFfeKUAg@JuC1uPmU>)aipw%z<)Bng=i2U zub#gGTRt`#$??%5$KzsS}n80pg8`Y-9WJb+$SOr*+N~wxhXW6xC%jlZatM3T}cxe zfJHh75DKuv690?K)yPyyZ2V2k~uKJux zez65D3KfP$xG8Kg+mzfu9n7LY$Rm{^be;raIm&Hmlemb*l9@L!oZ3X2@T{RsEZlnC zK?Y#mFd+G&5ZH^89?CPYa0dh^8>cwsWQN&DfJd(g!ZqVSU=adS7rp+fDs5;b#sC-g zQ?xb!22_&l9?EFQ5=w4?iG%{(!Z;a(w+=;zWR21Sa|DEoM-rU07|L#T*4oFF5~r3H z0_sC=Ke}PSjLAc;+=YR(h>{QU?z}(oQmb^`LG&pXJ&kY0CLjXhY`w;2%gKCcr=h5) zto+{NO|RTU!7ywW&i?=*$;S^DGAjL5rJVVbB%f8It_`WDc_3I>edtRktCB5oxZX}% zOwfE3*U$d6Tk5*o!$C8i(z_0X$AfRpTQ0p&fZ`3;vyA-Qwgib2du3yA;WmIdDUbn^ zftA%@RFlCX1C8T^dRX=riDg;04`19=>j6e{M9&zH`PhV^iiQ;nN}Lp&)p(H z25bjpnnc?jk{T#92?pU7BQRr>oa?~-)3ir)3(5f2?H4J`aFH!^2?jf+c7Y<~T&RFI z%ZMu@VB-CP$7uZ|Ph{HQ+>j4+yqw+oD8+@74cszJAUqi)3-(R08X*)zfJBq@K>{Zc z+k~a0fddMpZFPZK=te-C*se>XkN1wmE;v9Ih+L0IgI>|P7dyQelB92S;y){<9By~b zarL!te(?DL&;xG`_FLW{b4H&?NSH2gTq|9=;7RUNB~HvkMn;R|tc<=48>}G7Adjlg zPR)^(43?R&(02RnMP0Skaf(05)Qn80t2MMkD|P~pX#*HeZD5dWsVA}<<`amzne3a4j^3#ei0AH^09%C``JovpmX>@fo%VQtE&hgo-k^{wwg7;13d|&yivj2ZMfaqB55ZSsR2{U)^K2 z6z&J!VOtdF&+!LjEL(^8kf1pOX2=pHREv=?6to-9-3^l8D8j(f0zjF+3U_WXHY%t5 zNZK~}WFKV^Lt=6gk+_jygX$#vBF%RSp=98W_)NeeK|nSqvSg6Ul`sH75hhAN%d>By z2mpGcwq+=W;{srNN`Z2jGlK;x znn^NyCg%~)&0_}U1QiVfu1E>f1PO>9$r)xdl!JqTCPd{nSPk$=7@T%Y9NGZ7hhH3~ z4krpTW3N1v{;l}ZfO4=-C2h=9g z;|J4)q15RLtS^%-629bKRUWQLzr^L)yP&L3gHuU$X{~CiBWpH!vdUHxmS=Tuwj&A#B`Q0_pyWNw|W#(V2^ox9$+9Cpu?b8!x(kUi1Sp+$T+B!bf+lAJo>aBK0p&B4r?4i<$(JU}1+gs> zfJEW~LC5?i7Dnzk1_?P;fJmNW7fc3_fHPwrsFNL|veKc^Z6Yi^7a`INYCqMNA2x_2 z`mRT$+P&lzjTmnR*N8|OeIYinx6N?C_WLep#3ywvAPqP+?{&mfrus#{5sqx)Q6}`v zLqiA3C-zwy=o1{M1apOvj+{u8ESqL_yb+Hu)B%cM&loKQ)r9!LlvT=a6Q#I!fo^PSi!Zy zp6QFXHcdXv-=fJDj_3p%cI2p=TH!gc7gM8M7D$PcDQyxGj&6wsNF0ZL?fRiRe}xIM zIS@$`Gk#DcnZZc3OpBx>AJt;G?9(Z;z%$IEY%G3gnYZ02o6Y@{WG@%OLSpTf_Jqx{ zeyA=pWZ5U|KpnRsJCS=QGVBBwout9`2-&Mm3ipsFx<`2h2XUJb7EA+v5*yJXc>5|Y z0~YsEK?dOcQ*<}k8zpERGG_z2L`b=5OL2lMLImQ_Qi}p64kc5;I1>p;IN>|?Lm0AV zMBaVSN#Z_e7HPN0Bu%0|swT=27qXavYoA_I7bCJVZ5Hl?$v%YQPTOTjz>{F9#D@z5 zJgFlh(ddy&B#p*EB&yxJ69bOuAE=0gw;S#kO@kG!89k6eiHJVw0OHUxLL|t5ssg08 z*GQKc4#x^`Z+qniIGZf&-BwB`Yi!sP3P~l?bfm^adnIuLw{;{J*yJ?Gye}mDMAv>$ z>S>0p@q+7%bmEigOG3APK>nDCWHLlLN(doXm zXKKufa$3(&xX&3?2U|#&5BjU$)ptrdDmrZ&C{uYG8XA51T??%Ee^&mXTBRzcx083A z#0g&;>bO@I7fJd`=K3QZ-wSHZuIQ2OC-+<>KuMDj zzJzpsT3Zuj$-}anfgpp*f=fY=Aa+fq#h}=7Zna{fgh`1Fl9af%41lP(;8=H3U6GC! zlN6{g9?A9w!1qWv?t=vYnfC6Z3eypu%6!|dBqzM?AmUPKBM?<|bkM;&WQ)z=0mejs|6q-4lXB~wXkNSW=zhBThQ_Et%50g|mD0OKw_*8c!aaSwKG7aZtuAJKAM zEE4BH7+F}^&UUnI^~aV_(DwkJy5no=YSs<-uuDV>E=#Y2@bpjvw=k8()~8X9imfd% zH$>^Mg23A|JABJ4CKeVG7q`u2WKWkry2|!0`Z6|T65FkYk~q58ZN;KHt%hFng{#q; zJzww`k=DA2b7%e3Xa07tYZXY!@o&Ju4!^0BkwLBh0KC0`6C?r%^jV%>PP}fG4UtfV zO?jK;YnL6yE$*lT9DB;iwq}{J!ez!UG48O3MTt-16K>v#S=l@pfs`({gcU&ghU=ncM~|_V`(;$te!!iWn1XWMQ$ag zXVMeAsPjoOnDap>%E@fV37ZRgqz|Hy2{;(2rdkq!34U+^Mta1Sa7~&N=Gbm_Gm=_SFI^;I+kmC@WL>X<0 zHLxrZ=t2QA5FtxO37*NBv?BwW72>;Z$_ zAv4KA0!L+R%gE!gfgQO~xy=Ie8Mal#T%UBF;%-JbMH*qak(oRy5&@DumNIS#2zAY- zJF0^4QE_&1lcb3z-Qh?C5=r4uBiw~m5_Tqu7DHxmro`sz$UV~8JmetpWH_CO zE3rJly@~<3^|xd^fJ6^Kq+%fYrhu*2{{Te?2H!NSJpf8WZ$7AOQX5YpK`xscE0L7| zkSwIfx*UdxCl|V%#F#^e2MTaV=eiOL4aQ<2I5Xeoq)go+WSAsE(?hX`8~`j=nt1V= zdR1#`Ytw0|N$24nLysYPMdl{`*Nc8H^;%TAm0EP_?rj!LM$i7R@$$0y-#>zUu}UoM z$3Gjhu4wgIn)=hSulG$JX}q3RuZuNRwEC)NPf&p75a{Ge;W}?$ZjYz!sLl19;$l9F zkFV7=Jw?V?(puXF%JcsK@w}XKT%9A=@V;LRc~X{6BVAW*Qr2Jwrr(3WOWwFr?7a35w4h{HXFZW`vLdi~h-oo2~E5Y24HO@dA4#`F%Mc zeqeXPeC*1>r{>A|B{ma(RlE6WU{B^-w-`yfS}q_BQ(`S;c_Qr9GZ&AlO_FZ6&W@ND z)YG@3)~QGbLtY%yX~L9}2{RNB03S5NY%D&C_KBVq2F-`gR{W8hC8W)WfO{(9c`I*4 zp{c5KK(qS@B$f})*l1E~N8xM3UhsflxB@klX z=r9Bcw=#f6+!^k z4_FQfGL+^e1mGwx1muKOm6tLLcv9zpB4m0gHo2cgCD#7{C00v2EA<1oALxMu=jM=s zB%7oi?0pdSD1zt&wn<1hksO}kFNg_(1bx(#lY6LAwmtr0XZ+WN{y6EV(ds&8>}ap6 zF4Vc=WV9WyZ{2%)t;t?3`1siB{T=z8HBtWnd@j5&Q<_ZS@@C`4+)p8_(o2Aks^)wp zu4hfHTD@umP}+M9N2lrPYAYY%rX_N^j|}ymi$TuneLB4cI7oSi zH~!N}3x6_6LB(v>_;^Q?oeE9u%%wvWW$?&#?{iVk5i+xJ-h1z|_xmb>9j-TY5w z*?Pu}yBg-S?i0AojII``s@f)w_X=CL>?YIR7c~5q4YRO*ABj5K`JtanuEF94pX^;| zZT|qfGPoZCh<>h2d0X?!?3$Ohqg=yqY<*Jp3F9Z0JRT4EDS}D5+SgKzO=j&NhZ`+m z(oM~yl?!#yh_=$`_esD3VnIig7$<~?lV?mO(5(f_N0|F22?r)2C${ou5di~`2n4HO zR7*hPx=jKB2j+&{y8i&=p4QGu3f2u$0Mc8KKuOz(lNN*q%=&t!=XqoyFk^uP%%f{e zNU_S63;HUt1Ok0k{h6fpF%w}Ng;6aQkf4vKSsEmdHg9CKkfggiAA;MnsMI?B9B>Yfc~_|=|fK!Gc@YA>a$f8iv37HE_Guz96g)vCYy+OKyKZz@0{ z#%C)wl+$LRj;_Z(oQ>@c+DPWRPqhrM_JM?f*nuvGFaExIS24jd^&xyhK zx*2vTG%g{#jomiI*GI>ajN6C?IxqVy?WA06P875QBIRcc4Is*#5uY(qq~c__4`s03 zIUru$)Wbo7Z#~w*K0koBksypEsnYcbAK*GtoGn_)e9|F!* zyOLE+6Ej*3#E(SVx&d>7#k$~;2a_tB%x5cB(Ikw4!~?Y8ZOu+DgZuNG1*hN&QvxGkbdfcwbrOCWEW$E z$=W2M0W*AM0^kp_Few(}VFLcCoJoLB-AEB+@BmgiE4_fdr|Os=!c3lErN|>Vx}t3g zY@?730XGxCg(k*TI?zh3^iKnm-|mFB4#f%aMtoVy5#=ExVQYjo#MsGH+ut6Aa%|{J zV$s0+rq)=I_d}qPE%HcW;~!;`DX`~cN=`E9l0HZQ~IyRDY(h72&>p}7XIOo-7F2_TFE^!^u8ze!6Oc(ag^he9Giv{?bIG29~a*{Y0MA_6i?1TdBFD9mXX%C}fgD--_!P6!#XR!OVbjJ)E*(RlCU(|_MO8|`{kgm?2bdj&u? z;(3+ie~#^7XG5q*nEYTpytDk5Z;t-}@*~aifAz8c3v0Bj(RB?83~sODih%O^amTTr zs_&h0gLVBoRi$%TRkfM0hZ`St^Jbe_Q%zZwboJdry9~ALFhb`mc>BaAhr|BT3>YtM zCI?5j61B<4T^Tsh&b-xrgmY1yRS*05q*A_rF1QsdO%l&~$XGb$+7uyvLQT1UUZy!ZKDld>#vqGJmqs z!Rq>Db4lUii=6QuTEW)ty56s&)oDN7kVi7Rev?z-4y&nVpG(xM>8ld~)wIHxar7a? zePL?&-=XR}J+i$w{%1o}!)rtqyR(PvxpBou4+mLYTFKQk+I?=9cjd#_U7e#VDX-Mp zVq85)TJGv@FE)t#sCQE$YN`~yk0<= z3sl2onE3^jlyz!@bi@-q(BU?bBwZ$LCx{6g%-t(?VT5hw(64o_K>>^OoHk$?x3W%bkq|^osRfJ{^DvuzMX-@*CpOB=z&nHULlms0u)ri* z6}Ft+p>0uhHZ%uD?iS@5lm=MIL@rK{N=YLxN7vKP(=By37aNl+k$+!dpyqrh`PxPJ zS`DeDBlIwzbw;k2ZsSd)3q-D;j$V#Sl0BS9!7scSSs71ZgPd7!(biF-=QwGpR0hGX zl0L<98eL;GHCsxLNYmD+aMlM;?z+c~tjOgqM*jd*aPa~*j`C1j<^uS_?+xg>^GAF9 zGo^4h0o90i({kfAI+_|9n{_@Pp-tn5J>&VVkmZcA6O{Pmm9vKNTLU$kMo1B-Wc!+jRtQ0ZEZolp%!2nPp(ufvaSv&0l?xDY4;WQE!M zVbpZBv`x~8eQ@p5AEYWAlChs0JWf;aweDt@T4q6eaD58&I!`1^it~TNNN$f-WPjGS zeua11Z@7ytT)*tjzaJ1e9pD~MvdYSiH;9#`P2(hq9hP>wT-%8K*E71wv%DB3WNY|8 zy4PTF;@#GE3A9ILSnS9g0OnP-#FGU;0A>l{P-z^(o`othHPT{ktUJC`5qKj0%ESYL zlppp`n3)Zax~9718;&6(Z_;e7v6(%vt)MEHgAgH6H_g2ihMe)-Dj4npKFcVk$@;4P zD?}-1u+Orf2)r#kGHV4`oIhgg zU`3T|!t_qt2q7^Ol*>%VD=TTn0QIt49T>-zvC3cpVKT2}hoYj;OuEyAMny(4&e1X0iwz60yc|wS^ofBj@gT6q83>8LcEyx zo2J*1=#%c>PgKRHI6?heVhZJ5ku4++Ao?mI`TBB{4iEq!8Cn$RWXyvbxG7pB0*6jT z{ZJ$TRFE-^rhjE7#1b*UOuM)^LAeqPDPh>L`yy=s$@NNLeeRfP862RKvK-cljQSzb z5(WOM+(2!G{>n(jr74ONp{8cu=-$v`WS&#nTnw9xq##F>T@9eNgK(VRxG9H3$RPGm zT6UWz;{q;aR91($VgU&kdO}6UV#=kY++21_8v*YDLB11llk8^cz#>hLqAaq>IQc0T zFb+^h1v5KvJuH^NA!L}ma*@ml9?4G8+@??Xj(aS%L*0n&x`0Ic*--R_CS=Y~J0ucs z>ZQ@z^W9vMBb5X@kd!oxPAvx63lTE2xJigustC3tkfKP!@C27kE}{5?;rc!y(9mP0 zwP+U_P8@EDA7Hz6M;*Ko#g?Y2rQ;S#ZDj`}Q&f09tMKnq32Su>pHP32(5>JNU?hsv zvw1MR&c2@30P}FPt7&8r5$?44r|k2uj|HQ_>-ZyD!TPEE&-pGsuY>x7WW_QM{7c;R znk~jleyfYGqKRl}7RsL_t7g9p7Fp#z3!&tP7($)1p80)Ar%-uL^ zSlNhd10sHFxpnr9CXMx+!?Z;5v3QfA(D515>-tuqw6&ptd`6iL1A?%elk<8n75=Hw zQm?AAC2$iQkh8Fiyme(f4#H|8MKwNmO#O=tbi^_ORH~|AE3U@i9 z`lEZc+GfkBL<5_9AL^dw%nnkxndMVj?b1XjTCkCMM)L&VA?Jwur+k1+sZp8`BP#b~ zT-_o`4F$IJO{OYX6%o01Y1-h)CdD>`v=T^_xur8e2wZFH)DCkmVnp(>#mM5?9$qEl znXuC1$y)$<0g@IPd%hD%nT4<&U_CfrMj1%?+@G$3l_OH4TEV&RaBD}YUYo7e={yPH z`eKzKQ(sg=iOb!SK7o1fN~|dLJsnztTTZhaXS9XiKN7Tmozv+xjbW)ykseOHY02`n;xl!nKcfvvqpDG>u*)3g&8=mWRJ;OA3sLxx3u02)3}RN z^^%y%D=it6CnZMD;ll~()!hW{s3tuE?>c&JE)vlTSHry~hfLCnR9noQ$0c*Lm_djJ z7YWHW==5;$xWWz-<-fwcd(-n0&gIdZEb*A=}~|(09jc`ORZ$K z4OyztVk6{MP~ENtqE>dWToHAUG9tM&nb{+1d=kSUfimJGRX~S1ou{ce>F`O znE9Z=o)g(6NKJq+euy?UKUG2lNR+^grEdjTC8QIK_EljP5U7K~seuP7Ot41KOaenj z6L*-xpnS4$hUN%OlK?H|5)ptQ4iX>|5$%<9L)`Ag3O;B_8+Ra15-?2Uq)eL+@>J32 zxrDu-0^HB48e(Civ?V@h!aTxAIafrO5(qHwl0ZYV0XQep={q=5HEnD++<+h^AeeXE zA)DN%1W7RpWQ0M$0Z4QM1{D_)ad=CZfGnWVLF51nY=baj>IH-tSG432^i~ZfNE7`A zQY|OH%{!6A`yxpI0rWv*u>d0f07T17NsM46<_@~BWPok%hS?Fdq7FUqk+cqbsw3^5 z65Bq3B!pWnxhV_aliVjQ0DyQ<++2lFJ(cSg^-|`W8IR_k=)wWan<@Q``y?4~vT#L% zMsq2FH*5~cIBhW`j(rf>eS?4(uq9)4j7^nKngGIJ2{ylUB1S%L41}&^3;zH$TM0eA z5;k|bXxnLQgCVzR7f|8LRk|R>iib_{5yGew^kAsaNr(k&Rj25-;Ki~zQBkB^3wx+? zarJ3Tm|C@HNP~j8Dy$#E7Bfs1*`j%cQI@iBwRC~srr>s1AEIl`qB|~W^o+*~5v3Ah z$Y`x?omZ=Pe^1o3kJHx#HQC#cy5m0%sMC|D>N=Wpa2pHK%|7Ej);Cj}S-B~N4lf>l}9+{Y3}}O*GmjoSf=V`2R`Xn9Wzo&ODBlG z@V2DeS)TOPGx%GmAE#Q$9qFIyy|B`b0!EY*&oH`q%COEqjXsYO{{YFZrLH^mbja)& za(dR86Iy>;=t(o#r_Z1KHNd6r4rnYv{Aegg{^j}4d zGh+~a6b&;+nk0kBRkJ_zeB$rJ?zr3h8fX6ic>e%JKkOe{X#W7w+iU0im(oIj-C;9) zAke3@jh6ORNeez`{{UjT*mRvMVBR+=so?&!2o2J>jM##{m420k6SPma3A0B|{zj9{ zlTt7GK4E`_ddp4pbp`v6>ZJI4seC45Z3`RJ09!VM@ylPhTQg9X>`g0-v*R%F%=+rtbSd!B#k)hGzH#GaM)ZH+&fDf|B-95#yv*m2kf!FBt z&+!31*j$Az2yOPRQM3dMf#?=i(jnfeZp~#Hlr*pewmU7sL|}mnI~jQ^U^52Lc~}%` ziZBnBRf24Q|sQVN+U6k_yNhZ&XBs-BwL2 zvOpca%9_~4fk-w#R6@wspQ3@WHL`Ii7y<%tNjJr^m<00(M6)sIBZNHf(AaQ6CO|x?wdpyHY!en`V&L} z#99iVH)kmQH?TdEZ_HmQAsd1Sh(r(uZ_25G5`%t5dn&z=4F~9+&|486s)V%6{E}!T z&}@RM8E>KbAyxkX&_Zd6k&Kh(DS+W_d!x(;q9)>29!m^PY@ak(AmR4@MkE>qbz2k4z_gwwR%3@L(p!OW+%!X)Ro;ScxwD+Jj)cODP2 zlHPCZvRVe?5!p-ubChVVg)jt@V_>9{JSqjQl26S>+nK=TRGo~SlKGIO@CBu0HYeyU zm4sc;46O+;K$9w8yJ@g1GnzmUV5}y>0hw7s-$n;9L`g6qY?j-xi>xkbg6NQzWWaed z_EK565KLKSEjG8wOWJ#Ra-!Z1eU{8mWjh0JnOhEN!(21MPHY*K5>(6|3`BNV%MImh zxuAkUSjaK}fPD&yD>dCrms&lP3T6pgfutGisJGT9gqg}TO%hGOk99jO2E(%4ErAEy zx|cFwi1$#@tFVd3ucDCB51?Cym?glQ_EGS}oBdQ*(lz!M7?J3d5O%q277>RJbgDG> zlB^cCYy2_?A~6UwNh7vaeWZ{Kkg2T_MdT)#HjPJ7JEmZER@!3`e(N}4aW_Gx;ge-h zTQ&X-vZ=I$UOiUJMW?b0E(rT6;IeBPIEaMJJ_!0O?GSS*IfnB;MH0@;BA77-1^7wq zAmMC5!=ehP0t3v-g3@%2e}}n%H~^Vp4iORDEasSX=V9tMWecQC}3LTBWV$Wm=kk^8L%1tND&Y>N^(c)!Lltd z=sw7UH_sBF2DA`+1T;EGBu5A^&_8s`iL*kOQBY;Kx?#5_V5z*^fJR|d?meR^vC%+V znD$P5pdJ*+xZMJKAXwef6HJq#u!3x3It_wXQa2tzy@IBgGd!axHX;WxC>_3v$R^=qc4ktUmWzY_f)vQ?P@5kG z?8;igEs?=Rro-qHXp5lE?jdutUNi)T*iX$n2#k8EcbnJIAkgC_G#5pPHjtO0HuOp$ zU+R<)c!f~i>}8`fe#r?SkmEj~HsqY9S}`*`N`jMkBmuVO@}>lmcvoSN5I`BQO_oAI z7C$8>I0OX907x_LkRTFFm0cEsAi)BCQh_&=qq&la0DADIP1#NW-ae_AAYLzofV82d zw!sIPRg%FZ8-ip?BmfTEPbrYmB0Z9AgrB0fvHC;uv|4VFvxMi6o31>u3Iw>EN+=pb zBlTi<0ZDWYN3vj(`mH>r!~#kvs34J_x{shD5YX0ofu1#ma61 z{95XE-tw}Y4d6wxBto5#ARG3DavP8o_BdJuC?J4beU!PoVr}ZEkSqd-waNLEGNN2; zM*&L&Ho!-w6EVeyWd;HCT48mtaB!R2eU>-+h*aj-!6bK3;t2jJLPFi-mo@Ga|sX$ z7L}Z^n+P7O5oeeKKI>OvooJUgX(g&}mxv22NS~A>S|AdBXjBcdPR+A$LLeRttB8H(yBfta8$&=lfVOOr4ZsK4Y;E*NfLkpW3QZZT81{{(BL0YL z0TGpxWDlCK(a5nVBNoyHq5{396^dXw@%2Ff7#B{<8)gvGmhs-|nkS#C$XX)D-5Uf0 zGIFhtk&L%%VZT6?B1wk=C1(QglE*YS~e`Bw0c7-sGxlNHVG;<-!YiM2I2<`=_y?wrTpQ40iec6_gmb zl%&|a37}fU4#fmXfDddc02g2060szMbWtESwcsTZJ(X_r07tq|dx05RpaCP21R^jF z6+t+-j!*y>5$se$5x~!M7S9IVl(&&F!gFE_{E%jVk2T~J@%lx(DK=n|0&R?TOKjNY zNxGZ?XzvN0;C$1|U|0lLR&1TG@Ag6@f1-t|8#V#VsB`0s_9)<}1e~r>B-=9F;^I;U zMBNSj2ij9%Bmz!S5+VS}ReQr=D*iWnENmn?Cf$@NUIsr=&Am|&B$(pLzz}W1L-dbS zvhkrz0v!j?BX$kr3nP1Qu?#odsDP0$ILa7Il$rX}gXSm>i-v(RpzSWQ%To&++vb?z z1z5lU$9o{K0~tptkJ5dV&G@a7;{m4EQe^rh=Q?s#gXEr0P)jV4iJRuZ5FC9_4tO5H8j{%#hPxL?jM*m=96&Z!MS>VME@!>gH)Ak|ezy() zOk^sAncNISV(Lkmv@GqxSlT3+x~_$l(%@HfiNFM7$0;;b41boN)_j z{{W<{V(v40EZDV1A6>A6VG(W?Kp=?hl-b^xT1Hx<3yd)K$$+N7T!=lw$^eM7m z(j>vbhQ=84R!}f-G*)-Dhh>#0(*;ufg!eGb|76=ez!ln-^RX6mv z*=o^MU;Lq-|3le=4Hz(aY2Cz&9{*^#p%$(N*L`056Z-D4U2)-^J{#fEpljk_Z4CACen00uRkt!BEfF^I0^< z7vTG=1)vdcWWR_jAwaS~Ae&6~Swut;=%t_nAzNAW$LtZ%noP+~EsFKmgbi+Up z0tq{i7-d>VZUR3{N@M=4CeS39x|1hh={%r=%>MvHnr3IZWAu-7JPdL&L5|@DGXvjmVYW?|h(wS%ObB#G%@`nXsmn%M>_AA9!hvHsSr`Fr(;x>x!X5=77!mxG zg4j5edLf6-oIHZqHUS=~oQo=O1>#33xDDWNfoh07z%lBB0Kp$*-(q5NfFL$G%4h=d aBlbjgw5AMM081{5p+Rot3BW>4AOG1u9^F0w literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Pictures/ESP32_Shield.jpg b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/Pictures/ESP32_Shield.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7f1c2bf8af8db7cec154b68f2621eb31777f48e7 GIT binary patch literal 156483 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%0DwsZ;+4kPAxVzHoya360;Rag0%TMvSs5lI(ue?t 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 A*Z=?k literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/ReadMe.md b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/ReadMe.md new file mode 100644 index 0000000..f33e6f5 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/ESP32/ReadMe.md @@ -0,0 +1,111 @@ +## ESP32 - SX12XX Library Example Programs + +Having originally tested the SX127X part of the library on a ATMega328P based micro controller I decided to check that the library was code compatible with another popular processor that can be programmed in the Arduino IDE, the ESP32. + +Unfortunately there is frequently a misunderstanding about what an 'ESP32' is. The ESP32 is a single surface mountable module to which you need to add various components to make a usable board. However the many different types of 'ESP32' boards may use different types of components and connections which could conflict with the pins used in the library examples here. It is not practical to test the examples on all possible permutations of 'ESP32' assembled boards, there are over 60 different boards supported by the Arduino IDE and I would go poor buying them all. + +Thus the ESP32 examples presented here have been tested against a known standard which is an actual ESP32 Wroom module with no additional hardware, apart from the components shown in the schematic below. It is possible that the examples will not work against your particular 'ESP32' assembled board. If the example programs do not work for you, it is not an issue with the library but a difference between the reference hardware and your particular setup. I am not in a position to assist in resolving issues with particular ESP32 board versions. + +![Picture 1](/pictures/ESP32_Bare_Bones_Schematic.jpg) + + +As well as testing against the bare bones ESP32 schematic shown above, some of the examples will have been tested on a small portable unit I built which follows the same principles of the bare bones schematic. This PCB was developed to create a PCB that could be used for a small ESP32 based GPS tracker node, initially for use on The Things Network. The board has a GPS, I2C SSD1306 OLED and RFM98 lora device. There are options for a micro SD card, DS18B20 temperature sensor and a I2C FRAM. With it all assembled, the node consumes around 31uA in deep sleep with all devices connected. The 'Micro\_Node' contains additional circuitry to power off devices such as the lora module and GPS. Its highly unlikely that a standard ESP32 board will achieve a sleep current anywhere near the 'Micro_Node', this unit is pictured below; + +![Picture 1](/pictures/ESP32_Micro_Node.jpg) + + +### ESP32 Deep Sleep + +The ESP32 does some things with I\O pins when going into deep sleep that you might not expect. There are functions in this SX12XX library for putting the LoRa device into deep sleep, whilst still preserving register settings. The current then taken by the lora device is circa 0.5uA. However these functions will likely not work directly with most ESP32 boards due to the way the ESP32 handles the I\O pins in deep sleep mode. + +When designing a board where a very low deep sleep current it is important, you need to build the design in stages, checking the deep sleep current after adding each component. Debugging a high deep sleep current on a fully assembled board can be from extremely difficult to impossible. Achieving a low deep sleep current for particular ESP32 boards is well outside the scope of this SX12xx library. + +
+ +**3\_LoRa\_Transmitter and 4\_LoRa\_Receiver example programs** + +These example programs work when used with the ESP32 'bare bones' schematic shown earlier and the 'ESP32 Dev Module' board type selected in the Arduino IDE. + +The pins used for SPI were; + + SCK 18 + MISO 19 + MOSI 23 + NSS 5 + NRESET 27 + RFBUSY 25 (SX126X and SX128X devices only) + DIOX 35 (DIOX is DIO0 on the SX127X devices and DIO1 on SX126X and SX128X devices) + +In the example programs for ESP32, you can start the SPI with; + +SPI.begin(); + +And the default SPI pin outs for the ESP32 module shown above will be used. + +Alternatively you can uses this format to start SPI; + +SPI.begin(SCK, MISO, MOSI, NSS); + +And the pin definitions will be taken from those specified in the 'Settings.h' file. If you change these pin allocations from the defaults given you will need to be sure they are valid for your particular ESP32 board. + + +The SX12XX example programs are specifically written for and and tested on ATmega processors such as 328,1284 and 2560. However most will work, with minor modification, on the ESP32. + +This is a run through of the changes that were needed to have the tracker receiver examples; **25\_GPS\_Tracker\_Receiver\_With\_Display\_and\_GPS**, written for the ATmega328P run on the ESP32 bare bones type boards described above. + +The original SX12XX tracker program uses the SPI interface to talk to the lora device, I2C to talk to the OLED display and software serial to talk to the GPS. + +The pins used for SPI were; + + SCK 18 + MISO 19 + MOSI 23 + NSS 5 + NRESET 27 + RFBUSY 25 (SX126X and SX128X devices only) + DIOX 35 (DIOX is DIO0 on the SX127X devices and DIO1 on SX126X and SX128X devices) + +The ESP32 I2C pin connections were; + + SDA 21 + SCL 22 + +There is no need for software serial on the ESP32 as it has an available hardware serial port. These are the pin connections used; + + GPSTX 16 //this is data out from the GPS into the ESP32 + GPSRX 17 //this is data out from the ESP32 into the GPS + +The other pins used were; + + LED1 2 //On board indicator LED, logic high for on + GPSPOWER 26 //Pin that controls power to GPS, set to -1 if not used + +The only software changes required were to change the lines in the Settings.h file from; + + #define USE_SOFTSERIAL_GPS + #define HardwareSerialPort Serial2 + +to; + + //#define USE_SOFTSERIAL_GPS + #define HardwareSerialPort Serial2 + +This removes the definition USE\_SOFTSERIAL\_GPS from the sketch and the effect of this change is then to remove these two lines from the Sketch; + + #include + SoftwareSerial GPSserial(RXpin, Txpin); + +And include this one; + + #define GPSserial HardwareSerialPort + +Which sets the commands to read data from the GPS such as GPSserial.read() to be in effect Serial2.read(), which is the ESP32 hardware serial port used. + + +**Note:** The provided lora settings (in the Settings.h file) may not be optimised for long distance. See the 'What is LoRa' document for information on how LoRa settings affect range. + + +### Stuart Robinson + +### April 2020 + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino new file mode 100644 index 0000000..273ae30 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino @@ -0,0 +1,154 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program transmits a packet without using a processor buffer, the LoRa device + internal buffer is filled direct with variables. The program is a simulation of the type of packet + that might be sent from a GPS tracker. Note that in this example a buffer of text is part of the + transmitted packet, this does need a processor buffer which is used to fill the LoRa device internal + buffer, if you don't need to transmit text then the uint8_t trackerID[] = "Tracker1"; definition + can be ommited. + + The matching receiving program '9_LoRa_LowMemory_RX' can be used to receive and display the packet, + though the program '15_LoRa_RX_Structure' should receive it as well, since the packet contents are + the same. + + The contents of the packet received, and printed to serial monitor, should be; + + "tracker1" (buffer) - trackerID + 1+ (uint32_t) - packet count + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX126XLT LT; + +uint32_t TXpacketCount = 0; +uint8_t TXPacketL; +uint32_t startmS, endmS; + +void loop() +{ + TXpacketCount++; + + if (Send_Test_Packet()) + { + Serial.print(TXpacketCount); + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes Sent")); + Serial.print(F(" ")); + Serial.print(endmS - startmS); + Serial.print(F("mS")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } + + Serial.println(); + delay(packet_delay); +} + + +uint8_t Send_Test_Packet() +{ + //The SX12XX buffer is filled with variables of a known type and order. Make sure the receiver + //uses the same variable type and order to read variables out of the receive buffer. + + float latitude, longitude; + uint16_t altitude, voltage; + uint8_t satellites; + int16_t temperature; + uint8_t len; + + //test data + uint8_t trackerID[] = "tracker1"; + latitude = 51.23456; + longitude = -3.12345; + altitude = 199; + satellites = 9; + voltage = 3999; + temperature = -9; + + LT.startWriteSXBuffer(0); //start the write at location 0 + LT.writeBuffer(trackerID, sizeof(trackerID)); //= 13 bytes (12 characters plus null (0) at end) + LT.writeUint32(TXpacketCount); //+4 = 17 bytes + LT.writeFloat(latitude); //+4 = 21 bytes + LT.writeFloat(longitude); //+4 = 25 bytes + LT.writeUint16(altitude); //+2 = 27 bytes + LT.writeUint8(satellites); //+1 = 28 bytes + LT.writeUint16(voltage); //+2 = 30 bytes + LT.writeInt8(temperature); //+1 = 31 bytes total to send + len = LT.endWriteSXBuffer(); + + digitalWrite(LED1, HIGH); + startmS = millis(); + + TXPacketL = LT.transmitSXBuffer(0, len, 5000, TXpower, WAIT_TX); //set a TX timeout of 5000mS + + endmS = millis(); + + digitalWrite(LED1, LOW); + + return TXPacketL; +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h new file mode 100644 index 0000000..7bc656f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h @@ -0,0 +1,41 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino new file mode 100644 index 0000000..5f8c767 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino @@ -0,0 +1,195 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program receives a packet without using a processor buffer, the LoRa device + internal buffer is read direct and copied to variables. The program is a simulation of the type of packet + that might be received from a GPS tracker. Note that in this example a buffer of text is part of the + received packet, this does need a processor buffer which is filled with data from the LoRa device internal + buffer, if you don't need to send and receive text then the uint8_t receivebuffer[32]; definition can be + ommited. + + The contents of the packet received, and printed to serial monitor, should be; + + "Tracker1" (buffer) - trackerID + 1+ (uint32_t) - packet count + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX126XLT LT; + +uint32_t RXpacketCount; +uint16_t errors; + +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet + + +void loop() +{ + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +uint8_t packet_is_OK() +{ + float latitude, longitude; + uint16_t altitude, voltage; + uint8_t satellites; + int8_t temperature; + uint32_t txcount; + + uint8_t receivebuffer[16]; //create receive buffer, make sure this is big enough for buffer sent !!! + + //packet has been received, now read from the SX12xx Buffer using the same variable type and + //order as the transmit side used. + + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(" ")); + + LT.startReadSXBuffer(0); //start buffer read at location 0 + LT.readBuffer(receivebuffer); //read in the character buffer + txcount = LT.readUint32(); //read in the TXCount + latitude = LT.readFloat(); //read in the latitude + longitude = LT.readFloat(); //read in the longitude + altitude = LT.readUint16(); //read in the altitude + satellites = LT.readUint8(); //read in the number of satellites + voltage = LT.readUint16(); //read in the voltage + temperature = LT.readInt8(); //read in the temperature + RXPacketL = LT.endReadSXBuffer(); + + Serial.print((char*)receivebuffer); //print the received buffer, cast to char needed + Serial.print(F(",")); + Serial.print(txcount); + Serial.print(F(",")); + Serial.print(latitude, 5); + Serial.print(F(",")); + Serial.print(longitude, 5); + Serial.print(F(",")); + Serial.print(altitude); + Serial.print(F("m,")); + Serial.print(satellites); + Serial.print(F("sats,")); + Serial.print(voltage); + Serial.print(F("mV,")); + Serial.print(temperature); + Serial.print(F("c ")); + Serial.print(F(" RSSI")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB")); + return RXPacketL; +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + printpacketDetails(); + Serial.print(F("IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void printpacketDetails() +{ + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h new file mode 100644 index 0000000..e5ad937 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define BUZZER 4 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/ReadMe.md b/lib/SX12XX-LoRa/examples/SX126x_examples/ReadMe.md new file mode 100644 index 0000000..e031719 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/ReadMe.md @@ -0,0 +1,44 @@ +# SX126X Library + + + +This part of the SX12XX library supports the SX1261, SX1262 and SX1268 (all UHF) LoRa devices.. + +The objective of the library is to allow the same program sketches to be used across the range of UHF lora modules SX126x and SX127x (UHF) as well as the 2.4Ghz SX128x modules. + +The library was tested on both NiceRF and Dorji modules. + +The Library has not been tested on the Semtech SX1261MB2BAS or similar development boards, so consider them unsupported. These development modules are not low cost and are not in a form (and too big) to be of practical use to the author of the library. + + +###Considerations for pin usage + +There is a range of SX126X modules available and they have slightly different pins usage. + +The library only supports the SPI based LoRa modules and these all require that the SPI bus pins, SCK, MOSI and MISO are connected. All modules also need a NSS (chip select pin) and NRESET (reset) pin. All devices need the RFBUSY pin to be used also. + +Of the LoRa devices DIO pins the SX126X library in standard form only uses DIO1. The Dorji DRF1262 and DRF1268 modules have an additional SW pin which must be configured and used since it provides power to the antenna switch on these modules. Some SX126x modules have RX and TX enable pins that need to be appropiatly activated when receiving or transmitting. + +Thus a begin function that initialised all possible permutations of pins would look like this; + +begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX\_EN, TX\_EN, SW, LORA\_DEVICE); + +Clearly the above begin statement is somewhat cumbersome and could potentially be shortened for the NiceRF S1262 devices to; + +begin(NSS, NRESET, RFBUSY, DIO1, LORA\_DEVICE); + +And shortened for the Dorji devices to; + +begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA\_DEVICE); + +Which is a lot more manageable. + +The first edition of the SX126X part of the library did use use a begin function in the examples of; + +begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, SW, LORA\_DEVICE) + +This format is still valid so if you have written your own programs using the earlier library these programs do not need changing. You could not have used the newer constructs of the begin command (to support the newer devices) in your programs since the newer constructs did not exist in the older version library. + +Accepted its all a bit confusing, but regrettably module manufacturers have different ideas about design. + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino new file mode 100644 index 0000000..9b6efc2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino @@ -0,0 +1,307 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a remote control transmitter. When one of four switches are made + (shorted to ground) a packet is transmitted with single byte indicating the state of Switch0 as bit 0, + Switch1 as bit 1 and Switch2 as bit 2. To prevent false triggering at the receiver the packet contains a + 32 bit number called the TXIdentity which in this example is set to 1234554321. The receiver will only + act on, change the state of the outputs, if the identity set in the receiver matches that of the + transmitter. The chance of a false trigger is fairly remote. + + Between switch presses the LoRa device and Atmel microcontroller are put to sleep. A switch press wakes + up the processor from sleep, the switches are read and a packet sent. On a 'bare bones' Arduino setup + the transmitter has a sleep current of approx 2.2uA, so it's ideal for a battery powered remote control + with a potential range of many kilometres. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings + are not necessarily optimised for long range. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +#include //watchdog timer library, integral to Arduino IDE +#include //watchdog timer library, integral to Arduino IDE +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +SX126XLT LT; + +uint32_t TXpacketCount; +uint8_t TXPacketL; + +volatile bool switch0flag = false; +volatile bool switch1flag = false; +volatile bool switch2flag = false; +volatile bool switch3flag = false; + +void loop() +{ + uint8_t switches; + + digitalWrite(LED1, LOW); //turn off indicator LED + Serial.print(F("Sleeping zzzz")); + Serial.flush(); //make sure all serial output has gone + + LT.setSleep(CONFIGURATION_RETENTION); //sleep LoRa device, keeping register settings in sleep. + sleep_permanent(); //sleep Atmel processor permanently for switch wakeup only + LT.wake(); //wake up the lora device - nicely + + digitalWrite(LED1, HIGH); + + Serial.println(F(" - Awake !!")); //the processor has woken up + switches = readSwitches(); //read the state of the switches + + TXpacketCount++; + Serial.print(TXpacketCount); //print the numbers of sends + Serial.print(F(" Sending > ")); + + Serial.print(switches, BIN); + + if (sendSwitchPacket(switches)) + { + Serial.println(F(" SentOK")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } + + Serial.println(); + delay(500); +} + + +uint8_t sendSwitchPacket(uint8_t switches) +{ + //The SX12XX buffer is filled with variables of a known type and in a known sequence. Make sure the + //receiver uses the same variable types and sequence to read variables out of the receive buffer. + uint8_t len; + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + LT.writeUint8(RControl1); //this byte identifies the type of packet + LT.writeUint32(TXIdentity); //this 32bit integer defines the Identity of the transmiter + LT.writeUint8(switches); //this byte contains the 8 switch values to be sent + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + //now transmit the packet, 10 second timeout, and wait for it to complete sending + TXPacketL = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void sleep_permanent() +{ + attachInterrupts(); + + ADCSRA = 0; //disable ADC + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + + // turn off brown-out enable in software + MCUCR = bit (BODS) | bit (BODSE); //turn on brown-out enable select + MCUCR = bit (BODS); //this must be done within 4 clock cycles of above + interrupts (); //guarantees next instruction executed + + sleep_cpu (); //sleep within 3 clock cycles of above + + /* wake up here */ + + sleep_disable(); + + detachInterrupts(); +} + + +void attachInterrupts() +{ + if (SWITCH0 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH0), wake0, FALLING); + switch0flag = false; + } + + if (SWITCH1 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH1), wake1, FALLING); + switch1flag = false; + } + + if (SWITCH2 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH2), wake2, FALLING); + switch2flag = false; + } + + if (SWITCH3 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH3), wake3, FALLING); + switch3flag = false; + } + +} + + +void detachInterrupts() +{ + if (SWITCH0 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH0)); + } + + if (SWITCH1 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH1)); + } + + if (SWITCH2 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH2)); + } + + if (SWITCH3 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH3)); + } + +} + + + + +void wake0() +{ + switch0flag = true; +} + + +void wake1() +{ + switch1flag = true; +} + + +void wake2() +{ + switch2flag = true; +} + + +void wake3() +{ + switch3flag = true; +} + + +uint8_t readSwitches() +{ + uint8_t switchByte = 0xFF; //start assuming all switches off + + if (switch0flag) + { + bitClear(switchByte, 0); //if the flag is set clear the bit + Serial.println(F("SWITCH0 pressed")); + } + + if (switch1flag) + { + bitClear(switchByte, 1); //if the flag is set clear the bit + Serial.println(F("SWITCH1 pressed")); + } + + if (switch2flag) + { + bitClear(switchByte, 2); //if the flag is set clear the bit + Serial.println(F("SWITCH2 pressed")); + } + + + if (switch3flag) + { + bitClear(switchByte, 3); //if the flag is set clear the bit + Serial.println(F("SWITCH3 pressed")); + } + + return switchByte; +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setupSwitches() +{ + if (SWITCH0 >= 0) + { + pinMode(SWITCH0, INPUT_PULLUP); + } + + if (SWITCH1 >= 0) + { + pinMode(SWITCH1, INPUT_PULLUP); + } + + if (SWITCH2 >= 0) + { + pinMode(SWITCH2, INPUT_PULLUP); + } + + if (SWITCH3 >= 0) + { + pinMode(SWITCH3, INPUT_PULLUP); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + setupSwitches(); + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates LoRa device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Transmitter ready")); + Serial.println(); + +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h new file mode 100644 index 0000000..407659c --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h @@ -0,0 +1,52 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t RFBUSY = 7; //RF busy on LoRa device +const int8_t DIO1 = 3; //DIO1 on LoRa device, used for RX and TX done +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t DIO3 = -1; //DIO3 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on +const int8_t RX_EN = -1; //pin for RX enable, used on some SX126X devices, set to -1 if not used +const int8_t TX_EN = -1; //pin for TX enable, used on some SX126X devices, set to -1 if not used +const int8_t SW = -1; //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + +const int8_t SWITCH0 = 2; +const int8_t SWITCH1 = 4; +const int8_t SWITCH2 = A3; +const int8_t SWITCH3 = A2; + +const uint32_t TXIdentity = 1234554321; //define an identity number, the receiver must use the same number +//range is 0 to 4294967296 + + + +//******* Setup LoRa Test Parameters Here ! *************** + + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +#define TXpower 10 //power for transmissions in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino new file mode 100644 index 0000000..27d83dc --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino @@ -0,0 +1,297 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a remote control receiver. When a packet is received an 8 bit byte + (SwitchByte) is read and the four outputs (defined in Settings.h) are toggled according to the bits + set in this byte. If the Switch1 byte has bit 0 cleared, then OUTPUT0 is toggled. If the Switch1 byte + has bit 1 cleared, then OUTPUT1 is toggled. If the Switch1 byte has bit 2 cleared, then OUTPUT2 is toggled. + + To prevent false triggering at the receiver the packet contains also contains a 32 bit number called the + TXIdentity which in this example is set to 1234554321. The receiver will only act on, change the state + of the outputs, if the identity set in the receiver matches that of the transmitter. The chance of a + false trigger is fairly remote. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define programversion "V1.0" + +#include +#include +#include "Settings.h" +#include + + +SX126XLT LT; + +uint32_t RXpacketCount; +uint16_t errors; + +uint8_t RXPacketL; //length of received packet +uint8_t RXPacketType; //type of received packet +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet + +uint8_t SwitchByte = 0xFF; //this is the transmitted switch values, bit 0 = Switch0 etc + +void loop() +{ + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); //read the signal strength of the received packet + PacketSNR = LT.readPacketSNR(); //read the signal to noise ratio of the received packet + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +uint8_t packet_is_OK() +{ + //packet has been received, now read from the SX12xx Buffer using the same variable type and + //order as the transmit side used. + uint32_t TXIdentity; + + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(" Packet Received")); + + LT.startReadSXBuffer(0); //start buffer read at location 0 + RXPacketType = LT.readUint8(); //read in the packet type + TXIdentity = LT.readUint32(); //read in the identity of transmitter + SwitchByte = LT.readUint8(); //read in the Switch values + RXPacketL = LT.endReadSXBuffer(); //finish buffer read + + printpacketDetails(); + + if (RXPacketType != RControl1) + { + Serial.print(F(" Wrong packet type")); + led_Flash(5, 25); //short fast speed flash indicates wrong packet type + return 0; + } + + if (TXIdentity != RXIdentity) + { + Serial.print(F(" Transmitter ")); + Serial.print(TXIdentity); + Serial.print(F(" not recognised")); + led_Flash(5, 25); //short fast speed flash indicates transmitter not recognised + return 0; + } + + if (LT.readRXPacketL() != 6) + { + Serial.print(F(" Wrong Packet Length")); + led_Flash(5, 25); //short fast speed flash indicates transmitter not recognised + return 0; + } + + //if we get to here, then the packet is valid so switch outputs accordingly + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, HIGH); + } + + Serial.print(F(",SwitchByte Received ")); + Serial.print(SwitchByte, BIN); //print switch values in binary, if a bit is 0, that switch is active + actionOutputs(SwitchByte); + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + } + + return RXPacketL; +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + printpacketDetails(); + Serial.print(F("IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void printpacketDetails() +{ + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void actionOutputs(uint8_t switches) +{ + //read the recreived switch byte and toggle outputs as required + + if (!bitRead(switches, 0)) + { + //toggle Output state + digitalWrite(OUTPUT0, !digitalRead(OUTPUT0)); //toggle Output state + } + + if (!bitRead(switches, 1)) + { + digitalWrite(OUTPUT1, !digitalRead(OUTPUT1)); //toggle Output state + } + + if (!bitRead(switches, 2)) + { + digitalWrite(OUTPUT2, !digitalRead(OUTPUT2)); //toggle Output state + } + + if (!bitRead(switches, 3)) + { + digitalWrite(OUTPUT3, !digitalRead(OUTPUT3)); //toggle Output state + } +} + + +void setupOutputs() +{ + //configure the output pins, if a pin is defiend in 'Settings.h' as -1, its not configured, so stays as input + + if (OUTPUT0 >= 0) + { + pinMode(OUTPUT0, OUTPUT); + } + + if (OUTPUT1 >= 0) + { + pinMode(OUTPUT1, OUTPUT); + } + + if (OUTPUT2 >= 0) + { + pinMode(OUTPUT2, OUTPUT); + } + + if (OUTPUT3 >= 0) + { + pinMode(OUTPUT3, OUTPUT); + } + + if (BUZZER >= 0) + { + pinMode(BUZZER, OUTPUT); + } + +} + + +void outputCheck(uint8_t number, uint32_t ondelaymS, uint32_t offdelaymS) +{ + uint8_t index; + + Serial.println(F("Toggling outputs")); + + for (index = 1; index <= number; index++) + { + digitalWrite(OUTPUT0, HIGH); + delay(ondelaymS); + digitalWrite(OUTPUT0, LOW); + delay(offdelaymS); + digitalWrite(OUTPUT1, HIGH); + delay(ondelaymS); + digitalWrite(OUTPUT1, LOW); + delay(offdelaymS); + digitalWrite(OUTPUT2, HIGH); + delay(ondelaymS); + digitalWrite(OUTPUT2, LOW); + delay(offdelaymS); + digitalWrite(OUTPUT3, HIGH); + delay(offdelaymS); + digitalWrite(OUTPUT3, LOW); + delay(offdelaymS); + digitalWrite(BUZZER, HIGH); + delay(offdelaymS); + digitalWrite(BUZZER, LOW); + delay(offdelaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + setupOutputs(); + + outputCheck(3, 500, 100); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Receiver ready")); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/22_On_Off_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/22_On_Off_Receiver/Settings.h new file mode 100644 index 0000000..2ccfda1 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/22_On_Off_Receiver/Settings.h @@ -0,0 +1,48 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t RFBUSY = 7; //RF busy on LoRa device +const int8_t DIO1 = 3; //DIO1 on LoRa device, used for RX and TX done +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t DIO3 = -1; //DIO3 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on +const int8_t RX_EN = -1; //pin for RX enable, used on some SX126X devices, set to -1 if not used +const int8_t TX_EN = -1; //pin for TX enable, used on some SX126X devices, set to -1 if not used +const int8_t SW = -1; //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +const int8_t BUZZER = -1; //pin for buzzer, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + +const int8_t OUTPUT0 = 2; +const int8_t OUTPUT1 = 4; +const int8_t OUTPUT2 = A3; +const int8_t OUTPUT3 = A2; + +const uint32_t RXIdentity = 1234554321; //define an identity number, the receiver must use the same number + //range is 0 to 4294967296 + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +#define TXpower 10 //power for transmissions in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino new file mode 100644 index 0000000..848ab16 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino @@ -0,0 +1,205 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a remote control transmitter that uses a LoRa link to transmit the positions + from a simple joystick to a remote receiver. The receiver uses the sent joystick positions to adjust the + positions of servos. The postions of the joysticks potentiometers on the transmitter are read with the + analogueRead() function. + + If the joystick has a switch, often made by pressing on the joystick, then this can be used to remote + control an output on the receiver. The switch is read by an interrupt, the interrupt routine sets a flag + byte which is read in loop(). + + The program is intended as a proof of concept demonstration of how to remote control servos, the program + is not designed as a practical remote control device for RC model cars for instance. + + It would be straight forward to make the transmitter program send packets continuously, but in most places + in the world that would break a normal limitation of 10% duty cycle for unlicensed use. Therefore the + program was designed to only transmit at a 10% duty cycle. Thus the fastest (lowest air time) packets are + used, spreading factor 6 at a bandwidth of 500khz. This results in an air time for the 5 byte control + packet of around 4mS, so there are around 25 sent per second. + + To have the transmitter program print out the values read from the joystick, comment in the line; + + //#define DEBUG + + Which is just above the loop() function. With the DEBUG enabled the transmission rate, the rate at which + the control packets are transmitted will be slowed down. + + To reduce the risk of the receiver picking up LoRa packets from other sources, the packet sent contains a + 'TXidentity' number, valid values are 0 - 65535. The receiver must be setup with the matching identity + number or the received packets will be ignored. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings + are not necessarily optimised for long range. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +SX126XLT LT; + +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +uint32_t TXpacketCount; +uint8_t TXPacketL; + +uint8_t joystickX1value; //variable to read the value from the analog pin +uint8_t joystickY1value; //variable to read the value from the analog pin + +volatile bool switch1flag = false; + +//#define DEBUG //comment in thie line (remove the two // at the beggining) for debug output + + +void loop() +{ + uint8_t switchByte = 0xFF; + + joystickX1value = (uint8_t) (analogRead(joystickX1) / 4) ; //read the joystick X1 pot, turn 0-1023 into 0 to 255 + joystickY1value = (uint8_t) (analogRead(joystickY1) / 4); //read the joystick Y1 pot + + if (switch1flag) + { + bitClear(switchByte, 1); //if the switch is down clear the bit + digitalWrite(LED1, HIGH); //turn on LED as switch indicator + switch1flag = false; + } + + if (!sendJoystickPacket(joystickX1value, joystickY1value, switchByte)) + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } +} + + +uint8_t sendJoystickPacket(uint16_t X1value, uint16_t Y1value, uint8_t switches) +{ + //The SX12XX buffer is filled with variables of a known type and in a known sequence. Make sure the + //receiver uses the same variable types and sequence to read variables out of the receive buffer. + //uint8_t len; + uint32_t packetStartmS, packettimemS; + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + LT.writeUint8(RControl1); //this is the packet type + LT.writeUint8(TXIdentity); //this value represents the transmitter number + LT.writeUint8(X1value); //this byte contains joystick pot AD X1 value to be sent + LT.writeUint8(Y1value); //this byte contains joystick pot AD Y1 value to be sent + LT.writeUint8(switches); //switches value + LT.endWriteSXBuffer(); //close the packet, thee are 5 bytes to send + + //now transmit the packet, 10 second timeout, and wait for it to complete sending + packetStartmS = millis(); + TXPacketL = LT.transmitSXBuffer(0, PacketLength, 10000, TXpower, WAIT_TX); + packettimemS = millis() - packetStartmS; + +#ifdef DEBUG + Serial.print(TXIdentity); + Serial.print(F(",X1,")); + Serial.print(joystickX1value); + Serial.print(F(",Y1,")); + Serial.print(joystickY1value); + Serial.print(F(",")); + Serial.print(switches, BIN); + Serial.print(F(",")); + Serial.print(packettimemS); + Serial.print(F("mS")); + Serial.println(); +#endif + + digitalWrite(LED1, LOW); //LED off, may have been on due to switch press + + delay(packettimemS * 9); //delay for 9 times packet transmit time to ensure 10% duty cycle + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void attachInterrupts() +{ + if (SWITCH1 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH1), wake1, FALLING); + switch1flag = false; + } +} + + +void detachInterrupts() +{ + if (SWITCH1 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH1)); + } +} + + +void wake1() +{ + switch1flag = true; +} + + +void setupSwitches() +{ + if (SWITCH1 >= 0) + { + pinMode(SWITCH1, INPUT_PULLUP); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + setupSwitches(); + + Serial.begin(115200); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + attachInterrupts(); + + Serial.println(F("35_Remote_Control_Servo_Transmitter ready")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h new file mode 100644 index 0000000..84fe7de --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h @@ -0,0 +1,53 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t RFBUSY = 7; //RF busy on LoRa device +const int8_t DIO1 = 3; //DIO1 on LoRa device, used for RX and TX done +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t DIO3 = -1; //DIO3 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on +const int8_t RX_EN = -1; //pin for RX enable, used on some SX126X devices, set to -1 if not used +const int8_t TX_EN = -1; //pin for TX enable, used on some SX126X devices, set to -1 if not used +const int8_t SW = -1; //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + +const int8_t joystickX1 = A2; //analog pin for the joystick 1 X pot +const int8_t joystickY1 = A3; //analog pin for the joystick 1 Y pot +const int8_t SWITCH1 = 2; //switch on joystick, set to -1 if not used + +const uint32_t TXIdentity = 123 ; //define a transmitter number, the receiver must use the same number + //range is 0 to 255 + + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const uint8_t PacketLength = 5; //packet length is fixed +const int8_t TXpower = 10; //LoRa transmit power in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino new file mode 100644 index 0000000..18c43ff --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino @@ -0,0 +1,259 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a remote control receiver that uses a LoRa link to control the positions of + servos sent from a remote transmitter. + + If the ttransmitter joystick has a switch, often made by pressing on the joystick, then this can be used + to remote control an output on the receiver. + + The program is intended as a proof of concept demonstration of how to remote control servos, the program + is not designed as a practical remote control device for RC model cars for instance. + + It would be straight forward to make the transmitter program send packets continuously, but in most places + in the world that would break a normal limitation of 10% duty cycle for unlicensed use. Therefore the + program was designed to only transmit at a 10% duty cycle. Thus the fastest (lowest air time) packets are + used, spreading factor 6 at a bandwidth of 500khz. This results in an air time for the 5 byte control + packet of around 4mS, so there are around 25 sent per second. + + To have the receiver program print out the joystick values (0-255) read from the received packet, comment + in the line; + + //#define DEBUG + + Which is just above the loop() function. With the DEBUG enabled then there is a possibility that some + transmitted packets will be missed. With the DEBUG line enabled to servos should also sweep to and fro 3 + times at program start-up. + + To reduce the risk of the receiver picking up LoRa packets from other sources, the packet sent contains a + 'TXidentity' number, valid values are 0 - 255. The receiver must be setup with the matching RXIdentity + number in Settings.h or the received packets will be ignored. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings + are not necessarily optimised for long range. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#define programversion "V1.0" + +#include +#include +#include "Settings.h" +#include + +SX126XLT LT; + +#include +Servo ServoX1; //create the servo object +Servo ServoY1; //create the servo object + +uint8_t joystickX1value; //variable to read the value from the analog pin +uint8_t joystickY1value; //variable to read the value from the analog pin +uint8_t RXPacketL; //length of received packet +uint8_t RXPacketType; //type of received packet + +//#define DEBUG + + +void loop() +{ + uint16_t IRQStatus; + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort + + while (!digitalRead(DIO1)); //wait for DIO1 to go high + + IRQStatus = LT.readIrqStatus(); + + if (LT.readIrqStatus() == (IRQ_RX_DONE + IRQ_HEADER_VALID + IRQ_PREAMBLE_DETECTED)) + { + packet_is_OK(); + } + else + { + Serial.print(F("IRQ,")); + Serial.print(LT.readIrqStatus(),HEX); + Serial.print(F(",")); + packet_is_Error(); + } + +} + + +uint8_t packet_is_OK() +{ + //packet has been received, now read from the SX12xx Buffer using the same variable type and + //order as the transmit side used. + uint8_t TXIdentity; + uint16_t pulseX1, pulseY1; + uint8_t switchByte = 0xFF; //this is the transmitted switch values, bit 0 = Switch0 etc + + LT.startReadSXBuffer(0); //start buffer read at location 0 + RXPacketType = LT.readUint8(); //read in the packet type + TXIdentity = LT.readUint8(); //read in the transmitter number + joystickX1value = LT.readUint8(); //this byte contains joystick pot AD X1 value sent + joystickY1value = LT.readUint8(); //this byte contains joystick pot AD Y1 value sent + switchByte = LT.readUint8(); //read in the Switch values + RXPacketL = LT.endReadSXBuffer(); //end buffer read + +#ifdef DEBUG + Serial.print(TXIdentity); + Serial.print(F(",X1,")); + Serial.print(joystickX1value); + Serial.print(F(",Y1,")); + Serial.print(joystickY1value); + Serial.print(F(",")); + Serial.print(switchByte, BIN); + Serial.println(); +#endif + + + if (RXPacketType != RControl1) + { + Serial.print(F("Packet type ")); + Serial.println(RXPacketType); + led_Flash(5, 25); //short fast speed flash indicates wrong packet type + return 0; + } + + + if (TXIdentity != RXIdentity) + { + Serial.print(F("TX")); + Serial.print(TXIdentity); + Serial.println(F("?")); + return 0; + } + + //actionServos + pulseX1 = map(joystickX1value, 0, 255, 1000, 2000); //scale the numbers from the joystick + ServoX1.writeMicroseconds(pulseX1); + pulseY1 = map(joystickY1value, 0, 255, 1000, 2000); //scale the numbers from the joystick + ServoY1.writeMicroseconds(pulseY1); //move the servo to position + + //actionOutputs + if (!bitRead(switchByte, 1)) + { + digitalWrite(OUTPUT1, !digitalRead(OUTPUT1)); //Toggle Output state + } + + return RXPacketL; +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + int8_t PacketRSSI; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + PacketRSSI = LT.readPacketRSSI(); //read the signal strength of the received packet + Serial.print(F("Err,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm")); + } + Serial.println(); +} + + +void setupOutputs() +{ + //configure the output pins, if a pin is defiend in 'Settings.h' as -1, its not configured, so stays as input + if (OUTPUT1 >= 0) + { + pinMode(OUTPUT1, OUTPUT); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void sweepTest(uint8_t num) +{ + uint16_t index1, index2; + for (index1 = 1; index1 <= num; index1++) + { + for (index2 = 900; index2 <= 2100; index2++) + { + ServoX1.writeMicroseconds(index2); + ServoY1.writeMicroseconds(index2); + } + + delay(1000); + + for (index2 = 2100; index2 >= 900; index2--) + { + ServoX1.writeMicroseconds(index2); + ServoY1.writeMicroseconds(index2); + } + + delay(1000); + } +} + + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + setupOutputs(); + + Serial.begin(115200); + + ServoX1.attach(pinservoX1); //connect pin pinservoX1 to ServoX1 object + ServoY1.attach(pinservoY1); //connect pin pinservoY1 to ServoY1 object + +#ifdef DEBUG + Serial.println(F("Servo sweep test")); + sweepTest(3); +#endif + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("36_Remote_Control_Servo_Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h new file mode 100644 index 0000000..3998d15 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h @@ -0,0 +1,48 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t RFBUSY = 7; //RF busy on LoRa device +const int8_t DIO1 = 3; //DIO1 on LoRa device, used for RX and TX done +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t DIO3 = -1; //DIO3 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on +const int8_t RX_EN = -1; //pin for RX enable, used on some SX126X devices, set to -1 if not used +const int8_t TX_EN = -1; //pin for TX enable, used on some SX126X devices, set to -1 if not used +const int8_t SW = -1; //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + +const int8_t pinservoX1 = 2; //pin for controlling servo X1 +const int8_t pinservoY1 = 4; //pin for controlling servo Y1 +const int8_t OUTPUT1 = 8; //this output toggles when joystick switch is pressed on receiver + +const uint16_t RXIdentity = 123; //define a receiver number, the transmitter must use the same number + //range is 0 to 255 + + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const uint8_t PacketLength = 5; //packet length is fixed + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/1_LED_Blink/1_LED_Blink.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/1_LED_Blink/1_LED_Blink.ino new file mode 100644 index 0000000..2865c0a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/1_LED_Blink/1_LED_Blink.ino @@ -0,0 +1,69 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This programs is supplied as is, it is up to the user of the program to decide if the programs are + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program blinks an LED connected the pin number defined below. The pin 13 LED, + fitted to some Arduinos is blinked as well. The blinks should be close to one per second. messages are + sent to the Serial Monitor also. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define LED1 8 //pin number for LED, set logic level high for on + +#define Program_Version "V1.0" + +uint16_t seconds; //used to display time elapsed on Serial Monitor + +void loop() +{ + Serial.print(seconds); + Serial.println(F(" Seconds")); //this message should print on console at close to once per second + seconds++; + digitalWrite(LED1, HIGH); + digitalWrite(13, HIGH); + delay(100); + digitalWrite(LED1, LOW); + digitalWrite(13, LOW); + delay(890); //should give approx 1 second flash +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //general purpose routine for flashing LED as indicator + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); //LED on + digitalWrite(13, HIGH); //Arduino board LED on + delay(delaymS); + digitalWrite(LED1, LOW); //LED off + digitalWrite(13, LOW); //Arduino board LED off + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + pinMode(13, OUTPUT); //setup pin as output for some Arduino boards that include an LED on pin 13 + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("1_LED_Blink Starting")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/2_Register_Test/2_Register_Test.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/2_Register_Test/2_Register_Test.ino new file mode 100644 index 0000000..cc09bd5 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/2_Register_Test/2_Register_Test.ino @@ -0,0 +1,401 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is stand alone, it is not necessary to install the SX12XX-LoRa library + to use it. This test program is for the SX126X LoRa devices. + + The program checks that a SX126X LoRa device can be accessed by doing a test register write and read. + If there is no device found a message is printed on the serial monitor. The contents of the registers + from 0x00 to 0x7F are printed, there is a copy of a typical printout below. Note that the read back + changed frequency may be slightly different to the programmed frequency, there is a rounding error due + to the use of floats to calculate the frequency. + + The Arduino pin numbers that the NSS and NRESET pins on the LoRa device are connected to must be + specified in the hardware definitions section below. The LoRa device type in use, SX1261, SX1262, + or SX1268 must be specified also. + + Typical printout; + + 2_Register_Test Starting + Reset device + LoRa Device found + Reset device + Registers at reset + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x800 00 00 00 00 01 07 20 1E 00 10 19 04 0F FF 0F FF + 0x810 10 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 + 0x820 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x830 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x840 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x850 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x860 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x870 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x880 03 00 00 5F 10 08 00 00 08 05 00 39 30 00 00 0C + 0x890 00 00 00 00 00 0F 0A 07 10 00 26 01 01 53 06 07 + 0x8A0 10 00 AA 20 5A 04 F0 00 56 56 54 43 94 20 40 00 + 0x8B0 00 83 11 00 01 04 0A 4C 14 0A 2F 01 6B FF FF 00 + 0x8C0 00 A0 20 00 00 00 AC 00 1C 00 00 AB 05 30 00 00 + 0x8D0 0C 14 14 40 06 00 00 10 C8 00 00 00 00 00 31 39 + 0x8E0 90 39 0C 04 40 20 1C 18 03 00 05 04 03 02 01 01 + 0x8F0 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 + 0x900 30 00 00 00 00 00 00 00 00 00 00 00 24 04 47 04 + 0x910 14 12 12 04 00 03 0A 00 15 35 09 00 02 1F 5F 08 + 0x920 01 04 05 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x930 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x940 00 07 00 03 02 00 10 0E 0D 0C 03 04 03 70 0C 00 + 0x950 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 + 0x960 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x970 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x980 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x990 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + + Frequency at reset 915000000 + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x880 03 00 00 5F 10 08 00 00 08 05 00 39 30 00 00 0C + Change Frequency 434100000 + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x880 03 00 00 5F 10 08 00 00 08 05 00 1B 21 99 A0 0C + Changed Frequency 434100000 + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +const uint16_t REG_RFFrequency31_24 = 0x088B; +const uint16_t REG_RFFrequency23_16 = 0x088C; +const uint16_t REG_RFFrequency15_8 = 0x088D; +const uint16_t REG_RFFrequency7_0 = 0x088E; +const uint8_t RADIO_WRITE_REGISTER = 0x0D; +const uint8_t RADIO_READ_REGISTER = 0x1D; +const uint8_t RADIO_SET_RFFREQUENCY = 0x86; + +const uint8_t DEVICE_SX1261 = 0x01; +const uint8_t DEVICE_SX1262 = 0x00; +const uint8_t DEVICE_SX1268 = 0x02; + +//********* Setup hardware definitions here ! ***************** + +//These are the pin definitions for one of the Tracker boards, be sure to change them to match your +//own setup. You will also need to connect up the pins for the SPI bus + +#define NSS 10 //SX126X device select +#define NRESET 9 //SX126X reset pin +#define RFBUSY -1 //SX126X busy pin +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1261 //define the device, DEVICE_SX1261, DEVICE_SX1262 or DEVICE_SX1268 + +//**************************************************************/ + + +#include + + +void setup() +{ + Serial.begin(9600); + Serial.println(F("2_Register_Test Starting")); + + SPI.begin(); + SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //The begin function setups the hardware pins used by device and then checks if device is found + //the DIO1, DIO2 and DIO3 are not used in this example so are set to -1 + + if (begin(NSS, NRESET, RFBUSY, -1, -1, -1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + } + else + { + Serial.println(F("No device responding")); + } +} + + +void loop() +{ + uint32_t frequency; + resetDevice(LORA_DEVICE); //reset the device + Serial.println(F("Registers at reset")); //show the all registers following a reset + printRegisters(0x800, 0x9FF); + Serial.println(); + Serial.println(); + + frequency = getFreqInt(); //read the set frequency following a reset + Serial.print(F("Frequency at reset ")); + Serial.println(frequency); + printRegisters(0x0880, 0x088F); //show the registers before the frequency change + setRfFrequency(434100000, 0); //change the frequency at reset, in hertz + frequency = getFreqInt(); //read back the changed frequency + Serial.print(F("Change Frequency ")); + Serial.println(frequency); //print the changed frequency, did the write work (allow for rounding errors) ? + printRegisters(0x0880, 0x088F); //show the registers after frequency change + frequency = getFreqInt(); //read the set frequency following a reset + Serial.print(F("Changed Frequency ")); + Serial.println(frequency); + + Serial.println(); + Serial.println(); + delay(5000); +} + + +void readRegisters(uint16_t address, uint8_t *buffer, uint16_t size) +{ + + uint16_t index; + uint8_t addr_l, addr_h; + + addr_h = address >> 8; + addr_l = address & 0x00FF; + checkBusy(); + + digitalWrite(NSS, LOW); + SPI.transfer(RADIO_READ_REGISTER); + SPI.transfer(addr_h); //MSB + SPI.transfer(addr_l); //LSB + SPI.transfer(0xFF); + for (index = 0; index < size; index++) + { + *(buffer + index) = SPI.transfer(0xFF); + } + + digitalWrite(NSS, HIGH); + + checkBusy(); +} + + +uint8_t readRegister(uint16_t address) +{ + uint8_t data; + + readRegisters(address, &data, 1); + return data; +} + + +void writeRegisters(uint16_t address, uint8_t *buffer, uint16_t size) +{ + uint8_t addr_l, addr_h; + uint8_t i; + + addr_l = address & 0xff; + addr_h = address >> 8; + checkBusy(); + + digitalWrite(NSS, LOW); + SPI.transfer(RADIO_WRITE_REGISTER); + SPI.transfer(addr_h); //MSB + SPI.transfer(addr_l); //LSB + + for (i = 0; i < size; i++) + { + SPI.transfer(buffer[i]); + } + + digitalWrite(NSS, HIGH); + + checkBusy(); +} + + +void writeRegister(uint16_t address, uint8_t value) +{ + writeRegisters( address, &value, 1 ); +} + + +uint32_t getFreqInt() +{ + //get the current set device frequency from registers, return as long integer + uint8_t MsbH, MsbL, Mid, Lsb; + uint32_t uinttemp; + float floattemp; + MsbH = readRegister(REG_RFFrequency31_24); + MsbL = readRegister(REG_RFFrequency23_16); + Mid = readRegister(REG_RFFrequency15_8); + Lsb = readRegister(REG_RFFrequency7_0); + floattemp = ( (MsbH * 0x1000000ul) + (MsbL * 0x10000ul) + (Mid * 0x100ul) + Lsb); + floattemp = ((floattemp * 0.95367431640625) / 1000000ul); + uinttemp = (uint32_t)(floattemp * 1000000); + return uinttemp; +} + + +void printRegisters(uint16_t Start, uint16_t End) +{ + //prints the contents of SX126x registers to serial monitor + + uint16_t Loopv1, Loopv2, RegData; + + Serial.print(F("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F")); + Serial.println(); + + for (Loopv1 = Start; Loopv1 <= End;) //32 lines + { + Serial.print(F("0x")); + Serial.print((Loopv1), HEX); //print the register number + Serial.print(F(" ")); + for (Loopv2 = 0; Loopv2 <= 15; Loopv2++) + { + RegData = readRegister(Loopv1); + if (RegData < 0x10) + { + Serial.print(F("0")); + } + Serial.print(RegData, HEX); //print the register number + Serial.print(F(" ")); + Loopv1++; + } + Serial.println(); + } +} + + +void setRfFrequency( uint32_t frequency, int32_t offset ) +{ + //Note RF_Freq = freq_reg*32M/(2^25)-----> freq_reg = (RF_Freq * (2^25))/32 + + uint8_t Rf_Freq[4]; + + frequency = frequency + offset; + + frequency = ( uint32_t )( ( double )frequency / ( double )0.95367431640625 ); + + checkBusy(); + + Rf_Freq[0] = (frequency >> 24) & 0xFF; //MSB + Rf_Freq[1] = (frequency >> 16) & 0xFF; + Rf_Freq[2] = (frequency >> 8) & 0xFF; + Rf_Freq[3] = frequency & 0xFF;//LSB + + writeCommand(RADIO_SET_RFFREQUENCY, Rf_Freq, 4); +} + + +void checkBusy() +{ + uint8_t busy_timeout_cnt; + busy_timeout_cnt = 0; + + while (digitalRead(RFBUSY)) + { + delay(1); + busy_timeout_cnt++; + + if (busy_timeout_cnt > 10) //wait 10mS for busy to complete + { + busy_timeout_cnt = 0; + Serial.println(F("ERROR - Busy Timeout!")); + break; + } + } +} + + +void resetDevice(uint8_t device) +{ + if ( (device == DEVICE_SX1261) | (device == DEVICE_SX1262) | (device == DEVICE_SX1268) ) + { + Serial.println(F("Reset device")); + delay(10); + digitalWrite(NRESET, LOW); + delay(2); + digitalWrite(NRESET, HIGH); + delay(25); + checkBusy(); + } +} + + + +bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinDIO2, int8_t pinDIO3, int8_t pinSW, uint8_t device) +{ + pinMode(pinNSS, OUTPUT); + digitalWrite(pinNSS, HIGH); + pinMode(pinNRESET, OUTPUT); + digitalWrite(pinNRESET, LOW); + pinMode(pinRFBUSY, INPUT); + + if (pinDIO1 >= 0) + { + pinMode( pinDIO1, INPUT); + } + + if (pinDIO2 >= 0) + { + pinMode(pinDIO2, INPUT); + } + + if (pinDIO3 >= 0) + { + pinMode(pinDIO3, INPUT); + } + + if (pinSW >= 0) + { + pinMode(pinSW, OUTPUT); //Dorji devices have an SW pin that needs to be set high to power antenna switch + digitalWrite(pinSW, HIGH); + } + + resetDevice(device); + if (checkDevice()) + { + return true; + } + + return false; +} + + +bool checkDevice() +{ + //check there is a device out there, writes a register and reads back + uint8_t Regdata1, Regdata2; + Regdata1 = readRegister(0x88e); //low byte of frequency setting + writeRegister(0x88e, (Regdata1 + 1)); + Regdata2 = readRegister(0x88e); //read changed value back + writeRegister(0x88e, Regdata1); //restore register to original value + + if (Regdata2 == (Regdata1 + 1)) + { + return true; + } + else + { + return false; + } +} + + +void writeCommand(uint8_t Opcode, uint8_t *buffer, uint16_t size) +{ + uint8_t index; + checkBusy(); + + digitalWrite(NSS, LOW); + SPI.transfer(Opcode); + + for (index = 0; index < size; index++) + { + SPI.transfer(buffer[index]); + } + digitalWrite(NSS, HIGH); + + checkBusy(); +} + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino new file mode 100644 index 0000000..72ead20 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino @@ -0,0 +1,123 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a minimum setup LoRa test transmitter. A packet containing the ASCII text + "Hello World 1234567890" is sent using the frequency and LoRa settings specified in the LT.setupLoRa() + command. The pins to access the lora device need to be defined at the top of the program also. + + The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with + the transmit power used and the packet length. The matching receiver program, '4_LoRa_Receiver' can be used + to check the packets are being sent correctly, the frequency and LoRa settings (in the LT.setupLoRa() + commands) must be the same for the transmitter and receiver programs. Sample Serial Monitor output; + + 10dBm Packet> Hello World 1234567890* BytesSent,23 PacketsSent,6 + + For an example of a more detailed configuration for a transmitter, see program 103_LoRa_Transmitter. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library + +SX126XLT LT; //create a library class instance called LT + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //SX126X busy pin +#define DIO1 4 //DIO1 pin on LoRa device, used for sensing RX and TX done +#define SW 5 //SW pin on LoRa device, used to power antenna switch +#define LORA_DEVICE DEVICE_SX1261 //we need to define the device we are using +#define TXpower 10 //LoRa transmit power in dBm + +uint8_t TXPacketL; +uint32_t TXPacketCount; + +uint8_t buff[] = "Hello World 1234567890"; //the message to send + + +void setup() +{ + Serial.begin(9600); + Serial.println(); + Serial.println(F("3_LoRa_Transmitter Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1); + } + + LT.setupLoRa(434000000, 0, LORA_SF7, LORA_BW_125, LORA_CR_4_5, LDRO_AUTO); //configure frequency and LoRa settings + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on receiver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + Serial.println(); + delay(1000); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino new file mode 100644 index 0000000..3d2d920 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/STM32/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino @@ -0,0 +1,170 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a minimum setup LoRa test receiver. The program listens for incoming packets + using the frequency and LoRa settings in the LT.setupLoRa() command. The pins to access the lora device + need to be defined at the top of the program also. + + There is a printout on the Arduino IDE serial monitor of the valid packets received, the packet is assumed + to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect weird things to + happen on the Serial Monitor. Sample serial monitor output; + + 8s Hello World 1234567890*,RSSI,-44dBm,SNR,9dB,Length,23,Packets,7,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error; + + 137s PacketError,RSSI,-89dBm,SNR,-8dB,Length,23,Packets,37,Errors,2,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + If there are no packets received in a 10 second period then you should see a message like this; + + 112s RXTimeout + + For an example of a more detailed configuration for a receiver, see program 104_LoRa_Receiver. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library + +SX126XLT LT; //create a library class instance called LT + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define SW 5 //SW pin on LoRa device, used to power antenna switch +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using +#define RXBUFFER_SIZE 32 //RX buffer size + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet + + +void setup() +{ + Serial.begin(9600); + Serial.println(); + Serial.println(F("4_LoRa_Receiver Starting")); + Serial.println(); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1); + } + + LT.setupLoRa(434000000, 0, LORA_SF7, LORA_BW_125, LORA_CR_4_5, LDRO_AUTO); //configure frequency and LoRa settings + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + PacketRSSI = LT.readPacketRSSI(); //read the received packets RSSI value + PacketSNR = LT.readPacketSNR(); //read the received packets SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + + RXpacketCount++; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + printElapsedTime(); //print elapsed time to Serial Monitor + + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/17_Sensor_Transmitter/17_Sensor_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/17_Sensor_Transmitter/17_Sensor_Transmitter.ino new file mode 100644 index 0000000..2e0cd4f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/17_Sensor_Transmitter/17_Sensor_Transmitter.ino @@ -0,0 +1,315 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program transmits a LoRa packet without using a processor buffer, the LoRa + devices internal buffer is filled directly with variables. + + The sensor used is a BME280. The pressure, humidity, and temperature are read and transmitted. There + is also a 16bit value of battery mV (simulated) and and a 8 bit status value at the packet end. + + Although the LoRa packet transmitted and received has its own internal CRC error checking, you could + still receive packets of the same length from another source. If this valid packet were to be used + to recover the sensor values, you could be reading rubbish. To reduce the risk of this, when the packet + is transmitted the CRC value of the actual sensor data is calculated and sent out with the packet. + This CRC value is read by the receiver and used to check that the received CRC matches the supposed + sensor data in the packet. As an additional check there is some addressing information at the beginning + of the packet which is also checked for validity. Thus we can be relatively confident when reading the + received packet that its genuine and from this transmitter. The packet is built and sent in the + sendSensorPacket() function, there is a 'highlighted section' where the actual sensor data is added to + the packet. + + Between readings the LoRa device, BME280 sensor, and Atmel microcontroller are put to sleep in units of + 8 seconds using the Atmel processor internal watchdog. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + There is also an option of using a logic pin to turn the resistor divider used to read battery voltage on + and off. This reduces current used in sleep mode. To use the feature set the define for pin BATVREADON + in 'Settings.h' to the pin used. If not using the feature set the pin number to -1. + + The Atmel watchdog timer is a viable option for a very low current sensor node. A 'bare bones' ATmega328P + with regulator and LoRa device has a sleep current of 6.6uA, add the LoRa devices and BME280 sensor + module and the average sleep current only rises to 6.8uA. + + One of these transmitter programs is running on a long term test with a 150mAh battery, to see how long + the battery actually lasts. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +#include //watchdog timer library, integral to Arduino IDE +#include //get the library here; https://github.com/rocketscream/Low-Power + +SX126XLT LT; + +#include //get library here; https://github.com/Seeed-Studio/Grove_BME280 +BME280 bme280; //create an instance of the BME280 senosor +#include + +uint32_t TXpacketCount; +uint8_t TXPacketL; + +float temperature; //the BME280 temperature value +float pressure; //the BME280 pressure value +uint16_t humidity; //the BME280 humididty value +uint16_t voltage; //the battery voltage value +uint8_t statusbyte; //a status byte, not currently used +uint16_t CRCvalue; //the CRC value of the packet data up to this point +uint8_t packetlength; //the packet length that was sent, checked against length received + + +void loop() +{ + TXpacketCount++; + Serial.print(TXpacketCount); //print the numbers of sends + Serial.print(F(" Sending > ")); + + readSensors(); //read the sensor values + printSensorValues(); //print the sensor values + + if (sendSensorPacket()) + { + Serial.println(F("SentOK")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.println(LT.readIrqStatus(), HEX); + } + + Serial.print(F("Sleeping zzzz")); + Serial.flush(); //make sure all serial output has gone + + //now put the sensor, LoRa device and processor to sleep + sleepBME280(); //sleep the BME280 + LT.setSleep(CONFIGURATION_RETENTION); //sleep LoRa device, keeping register settings in sleep. + sleep8seconds(sleeps); //sleep Atmel processor in units of approx 8 seconds + + //wait a bit ................ + Serial.println(F(" - Awake !!")); //the processor has woken up + Serial.println(); + + LT.wake(); + normalBME280(); //BME280 sensor to normal mode +} + + +uint8_t sendSensorPacket() +{ + //The SX12XX buffer is filled with variables of a known type and in a known sequence. Make sure the + //receiver uses the same variable types and sequence to read variables out of the receive buffer. + uint8_t len; + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + + LT.writeUint8(Sensor1); //this byte defines the packet type + LT.writeUint8('B'); //this byte identifies the destination node of the packet + LT.writeUint8(1); //this byte identifies the source node of the packet + + /************************************************************************ + Highlighted section - this is where the actual sensor data is added to the packet + ************************************************************************/ + LT.writeFloat(temperature); //add the BME280 temperature + LT.writeFloat(pressure); //add the BME280 pressure + LT.writeUint16(humidity); //add the BME280 humididty + LT.writeUint16(voltage); //add the battery voltage + LT.writeUint8(statusbyte); //add the status byte + /************************************************************************/ + + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + addPacketErrorCheck(len); //add the additional CRC error checking to the packet end + + //now transmit the packet, set a timeout of 5000mS, wait for it to complete sending + digitalWrite(LED1, HIGH); //turn on LED as an indicator + TXPacketL = LT.transmitSXBuffer(0, (len + 2), 5000, TXpower, WAIT_TX); + digitalWrite(LED1, LOW); //turn off indicator LED + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void addPacketErrorCheck(uint8_t len) +{ + //calculate the CRC of packet sensor data + CRCvalue = LT.CRCCCITTSX(3, (len - 1), 0xFFFF); + + Serial.print(F("Calculated CRC value ")); + Serial.println(CRCvalue, HEX); + + LT.startWriteSXBuffer(len); //start the write packet again at location of CRC, past end of sensor data + LT.writeUint16(CRCvalue); //add the actual CRC value + LT.endWriteSXBuffer(); //close the packet +} + + +void readSensors() +{ + //read the sensor values into the global variables + temperature = bme280.getTemperature(); + pressure = bme280.getPressure(); + humidity = bme280.getHumidity(); + + if (BATVREADON >= 0) + { + voltage = readBatteryVoltage(); //read resistor divider across battery + } + else + { + voltage = 9999; //set a default value + } + statusbyte = 0x55; //manually set this for now, its a test +} + + +void printSensorValues() +{ + Serial.print(F("Temperature,")); + Serial.print(temperature, 1); + Serial.print(F("c,Pressure,")); + Serial.print(pressure, 0); + Serial.print(F("Pa,Humidity,")); + Serial.print(humidity); + Serial.print(F("%,Voltage,")); + Serial.print(voltage); + Serial.print(F("mV,Status,")); + Serial.print(statusbyte, HEX); + Serial.print(F(" ")); + Serial.flush(); +} + + +void sleep8seconds(uint32_t sleeps) +{ + //uses the lowpower library + uint32_t index; + + for (index = 1; index <= sleeps; index++) + { + //sleep 8 seconds + LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); + } +} + + +void sleepBME280() +{ + //write this register value to BME280 to put it to sleep + writeBME280reg(BME280_REGISTER_CONTROL, B01111100); +} + + +void normalBME280() +{ + //write this register value to BME280 to put it to read mode + writeBME280reg(BME280_REGISTER_CONTROL, B01111111); +} + + +void writeBME280reg(byte reg, byte regvalue) +{ + //write a register value to the BME280 + Wire.beginTransmission((uint8_t) BME280_ADDRESS); + Wire.write((uint8_t)reg); + Wire.write((uint8_t)regvalue); + Wire.endTransmission(); +} + + +uint16_t readBatteryVoltage() +{ + //relies on 1V1 internal reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t volts = 0; + byte index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn on MOSFET connecting resitor divider in circuit + } + + analogReference(INTERNAL1V1); + temp = analogRead(BATTERYAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(BATTERYAD); + volts = volts + temp; + } + volts = ((volts / 5) * ADMultiplier) + DIODEMV; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn off MOSFET connecting resitor divider in circuit + } + + return volts; +} + + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); + } + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates LoRa device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + if (!bme280.init()) + { + Serial.println("BME280 Device error!"); + led_Flash(100, 15); //long very fast speed flash indicates BME280 device error + } + + Serial.println(F("Transmitter ready")); + Serial.println(); + + readSensors(); //do an initial sensor read +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/17_Sensor_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/17_Sensor_Transmitter/Settings.h new file mode 100644 index 0000000..39853af --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/17_Sensor_Transmitter/Settings.h @@ -0,0 +1,47 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define BATVREADON 8 //when high turns on the resistor divider to measure voltage, -1 if not used +#define BATTERYAD A7 //Resitor divider for battery connected here, -1 if not used +#define ADMultiplier 10.00 //adjustment to convert AD value read into mV of battery voltage +#define DIODEMV 98 //mV voltage drop accross diode @ low idle current + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 14; //LoRa transmit power in dBm + +#define BME280_ADDRESS 0x76 //I2C bus address of BME280 +#define BME280_REGISTER_CONTROL 0xF4 //BME280 register number for power control + +const uint8_t sleeps = 2; //number of 8 second sleeps, gap between transmissions diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino new file mode 100644 index 0000000..1a386d9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino @@ -0,0 +1,358 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 18/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program receives a LoRa packet without using a processor buffer, the LoRa devices + internal buffer is read direct for the received sensor data. + + The sensor used in the matching '17_Sensor_Transmiter' program is a BME280 and the pressure, humidity, + and temperature are being and received. There is also a 16bit value of battery mV and and a 8 bit status + value at the end of the packet. + + When the program starts, the LoRa device is setup to set the DIO1 pin high when a packet is received. When + a packet is received, its printed and assuming the packet is validated, the sensor results are printed to + the serial monitor and screen. + + For the sensor data to be accepted as valid the folowing need to match; + + The 16bit CRC on the received sensor data must match the CRC value transmitted with the packet. + The packet must start with a byte that matches the packet type sent, 'Sensor1' + The RXdestination byte in the packet must match this node ID of this receiver node, defined by 'This_Node' + + In total thats 16 + 8 + 8 = 32bits of checking, so a 1:4294967296 chance (approx) that an invalid + packet is acted on and erroneous values displayed. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + With a standard Arduino Pro Mini and SSD1306 display the current consumption was 20.25mA with the + display and 16.6mA without the display. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +SX126XLT LT; + +#include //get library here > https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 + +uint32_t RXpacketCount; //count of all packets received +uint32_t ValidPackets; //count of packets received with valid data +uint32_t RXpacketErrors; //count of all packets with errors received +bool packetisgood; + +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet + +uint8_t RXPacketType; +uint8_t RXDestination; +uint8_t RXSource; +float temperature; //the BME280 temperature value +float pressure; //the BME280 pressure value +uint16_t humidity; //the BME280 humididty value +uint16_t voltage; //the battery voltage value +uint8_t statusbyte; //a status byte, not currently used +uint16_t TXCRCvalue; //the CRC value of the packet data as transmitted + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout set + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_Received_OK(); //its a valid packet LoRa wise, but it might not be a packet we want + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +void packet_Received_OK() +{ + //a LoRa packet has been received, which has passed the internal LoRa checks, including CRC, but it could be from + //an unknown source, so we need to check that its actually a sensor packet we are expecting, and has valid sensor data + + uint8_t len; + uint8_t contenterrors; //keep a count of errors found in packet + + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(",PacketsReceived,")); + + LT.startReadSXBuffer(0); + RXPacketType = LT.readUint8(); + RXDestination = LT.readUint8(); + RXSource = LT.readUint8(); + + /************************************************************************ + Highlighted section - this is where the actual sensor data is read from + the packet + ************************************************************************/ + temperature = LT.readFloat(); //the BME280 temperature value + pressure = LT.readFloat(); //the BME280 pressure value + humidity = LT.readUint16(); //the BME280 humididty value + voltage = LT.readUint16(); //the battery voltage value + statusbyte = LT.readUint8(); //a status byte, not currently used + /************************************************************************/ + + len = LT.endReadSXBuffer(); + + printreceptionDetails(); //print details of reception, RSSI etc + Serial.println(); + + contenterrors = checkPacketValid(len); //pass length of packet to check routine + + if (contenterrors == 0) + { + Serial.println(F(" Packet is good")); + ValidPackets++; + printSensorValues(); //print the sensor values + Serial.println(); + printPacketCounts(); //print count of valid packets and errors + displayscreen1(); + Serial.println(); + } + else + { + Serial.println(F(" Packet is not valid")); + RXpacketErrors++; + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + } +} + + +uint8_t checkPacketValid(uint8_t len) +{ + //this function checks if the packet is valid and will be displayed + + uint8_t errors = 0; + + if (RXPacketType != Sensor1) //is it a Sensor1 type packet + { + errors++; + } + + if (RXDestination != This_Node) //was the packet sent to this receiver node ? + { + errors++; + } + + if (!checkCRCvalue(len)) //is the sent CRC value of sensor data valid ? + { + errors++; + } + + Serial.println(); + Serial.print(F("Error Check Count = ")); + Serial.print(errors); + return errors; +} + + +bool checkCRCvalue(uint8_t len) +{ + uint16_t CRCSensorData; + + Serial.print(F("len = ")); + Serial.println(len); + + CRCSensorData = LT.CRCCCITTSX(3, (len-1), 0xFFFF); //calculate the CRC of packet sensor data + + Serial.print(F("(CRC of Received sensor data ")); + Serial.print(CRCSensorData, HEX); + Serial.print(F(")" )); + + TXCRCvalue = ((LT.getByteSXBuffer(len + 1) << 8) + (LT.getByteSXBuffer(len))); + + Serial.print(F("(CRC transmitted ")); + Serial.print(TXCRCvalue, HEX); + Serial.print(F(")" )); + + if (TXCRCvalue != CRCSensorData) + { + Serial.print(F(" Sensor Data Not Valid")); + return false; + } + else + { + Serial.print(F(" Sensor Data is Valid")); + return true; + } + +} + + +void printSensorValues() +{ + Serial.print(F("Temp,")); + Serial.print(temperature, 1); + Serial.print(F("c,Press,")); + Serial.print(pressure, 0); + Serial.print(F("Pa,Humidity,")); + Serial.print(humidity); + Serial.print(F("%,Voltage,")); + Serial.print(voltage); + Serial.print(F("mV,Status,")); + Serial.print(statusbyte, HEX); + Serial.print(F(",CRC,")); + Serial.print(TXCRCvalue, HEX); + Serial.flush(); +} + + +void printreceptionDetails() +{ + Serial.print(F("RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); +} + + +void printPacketCounts() +{ + Serial.print(F("ValidPackets,")); + Serial.print(ValidPackets); + Serial.print(F(",Errors,")); + Serial.print(RXpacketErrors); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + RXpacketErrors++; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout ")); + } + else + { + Serial.print(F("PacketError ")); + printreceptionDetails(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + Serial.println(); + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + } +} + + +void displayscreen1() +{ + //show sensor data on display + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("Sensor ")); + disp.print(RXSource); + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(temperature, 1); + disp.print(F("c")); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(pressure, 0); + disp.print(F("Pa")); + disp.clearLine(3); + disp.setCursor(0, 3); + disp.print(humidity); + disp.print(F("%")); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(voltage); + disp.print(F("mV")); + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("ValidPkts ")); + disp.print(ValidPackets); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Check LoRa")); + disp.setCursor(0, 1); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + disp.print(F("LoRa OK")); + led_Flash(2, 125); + } + else + { + disp.print(F("Device error")); + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/18_Sensor_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/18_Sensor_Receiver/Settings.h new file mode 100644 index 0000000..45f9a9f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Sensor/18_Sensor_Receiver/Settings.h @@ -0,0 +1,49 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 17/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define BATVREADON 8 //when high turns on the resistor divider to measure voltage, -1 if not used +#define BATTERYAD A7 //Resitor divider for battery connected here, -1 if not used +#define ADMultiplier 10.00 //adjustment to convert AD value read into mV of battery voltage +#define DIODEMV 98 //mV voltage drop accross diode @ low idle current + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//*************** Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 2; //LoRa TX power + +#define packet_delay 1000 //mS delay between packets +#define This_Node 'B' //this is the node that the remote sensors send data to + +//**************** Setup Display Parameters Here **************** + +//const uint8_t dispfont = u8x8_font_chroma48medium8_r; //display font from u8g2 library diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/59_Play_Star_Wars_Tune.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/59_Play_Star_Wars_Tune.ino new file mode 100644 index 0000000..6e3a6f9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/59_Play_Star_Wars_Tune.ino @@ -0,0 +1,176 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - A silly program really, but does demonstrate that you can shift a carrier generated + by the LoRa device in FSK mode fast enought to play audio tones that can be picked up on an FM UHF + handheld receiver. The tones are not true FM but the receiver does not know that. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the settings file, frequencies etc +#include "pitches.h" //lookup file for notes + +SX126XLT LT; //create a library class instance called LT + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.println(F("PlayTune> ")); + Serial.println(); + Serial.flush(); + + playpart1(); + + playpart2(); + + LT.toneFM(NOTE_F4, 250, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_GS4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 350, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 375, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_E5, 650, deviation, adjustfreq, TXpower); + + delay(250); + + playpart2(); + + LT.toneFM(NOTE_F4, 250, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_GS4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 375, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 375, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 650, deviation, adjustfreq, TXpower); + + LT.setMode(MODE_STDBY_RC); //turns off carrier + + Serial.println(); + + delay(2000); //have a delay between packets +} + + +void playpart1() +{ + LT.toneFM(NOTE_A4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 350, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 150, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 350, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 150, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 650, deviation, adjustfreq, TXpower); + + delay(250); + + LT.toneFM(NOTE_E5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_E5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_E5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F5, 350, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 150, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_GS4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 350, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 150, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 650, deviation, adjustfreq, TXpower); + + delay(250); +} + + +void playpart2() +{ + LT.toneFM(NOTE_A5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 300, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 150, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_GS5, 325, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_G5, 175, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_FS5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_FS5, 250, deviation, adjustfreq, TXpower); + + delay(250); + + LT.toneFM(NOTE_AS4, 250, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_DS5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F5, 325, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_CS5, 175, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_AS4, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 250, deviation, adjustfreq, TXpower); + + delay(500); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("59_Play_Star_Wars_Tune Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupDirect(Frequency, Offset); + Serial.print(F("Tone Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/Settings.h new file mode 100644 index 0000000..fe8a725 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/Settings.h @@ -0,0 +1,35 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup Direct Modem Parameters Here ! *************** + +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes +const uint16_t deviation = 10000; //deviation in hz, total frequency shift low to high +const float adjustfreq = 0.8; //adjustment to tone frequency + +const int8_t TXpower = 10; //LoRa transmit power in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/pitches.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/pitches.h new file mode 100644 index 0000000..2f733c7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Silly/59_Play_Star_Wars_Tune/pitches.h @@ -0,0 +1,94 @@ +//File from https://www.arduino.cc/en/Tutorial/toneMelody +//This note table was originally written by Brett Hagman, on whose work the Arduino tone() command was based. + +#define NOTE_B0 31 +#define NOTE_C1 33 +#define NOTE_CS1 35 +#define NOTE_D1 37 +#define NOTE_DS1 39 +#define NOTE_E1 41 +#define NOTE_F1 44 +#define NOTE_FS1 46 +#define NOTE_G1 49 +#define NOTE_GS1 52 +#define NOTE_A1 55 +#define NOTE_AS1 58 +#define NOTE_B1 62 +#define NOTE_C2 65 +#define NOTE_CS2 69 +#define NOTE_D2 73 +#define NOTE_DS2 78 +#define NOTE_E2 82 +#define NOTE_F2 87 +#define NOTE_FS2 93 +#define NOTE_G2 98 +#define NOTE_GS2 104 +#define NOTE_A2 110 +#define NOTE_AS2 117 +#define NOTE_B2 123 +#define NOTE_C3 131 +#define NOTE_CS3 139 +#define NOTE_D3 147 +#define NOTE_DS3 156 +#define NOTE_E3 165 +#define NOTE_F3 175 +#define NOTE_FS3 185 +#define NOTE_G3 196 +#define NOTE_GS3 208 +#define NOTE_A3 220 +#define NOTE_AS3 233 +#define NOTE_B3 247 +#define NOTE_C4 262 +#define NOTE_CS4 277 +#define NOTE_D4 294 +#define NOTE_DS4 311 +#define NOTE_E4 330 +#define NOTE_F4 349 +#define NOTE_FS4 370 +#define NOTE_G4 392 +#define NOTE_GS4 415 +#define NOTE_A4 440 +#define NOTE_AS4 466 +#define NOTE_B4 494 +#define NOTE_C5 523 +#define NOTE_CS5 554 +#define NOTE_D5 587 +#define NOTE_DS5 622 +#define NOTE_E5 659 +#define NOTE_F5 698 +#define NOTE_FS5 740 +#define NOTE_G5 784 +#define NOTE_GS5 831 +#define NOTE_A5 880 +#define NOTE_AS5 932 +#define NOTE_B5 988 +#define NOTE_C6 1047 +#define NOTE_CS6 1109 +#define NOTE_D6 1175 +#define NOTE_DS6 1245 +#define NOTE_E6 1319 +#define NOTE_F6 1397 +#define NOTE_FS6 1480 +#define NOTE_G6 1568 +#define NOTE_GS6 1661 +#define NOTE_A6 1760 +#define NOTE_AS6 1865 +#define NOTE_B6 1976 +#define NOTE_C7 2093 +#define NOTE_CS7 2217 +#define NOTE_D7 2349 +#define NOTE_DS7 2489 +#define NOTE_E7 2637 +#define NOTE_F7 2794 +#define NOTE_FS7 2960 +#define NOTE_G7 3136 +#define NOTE_GS7 3322 +#define NOTE_A7 3520 +#define NOTE_AS7 3729 +#define NOTE_B7 3951 +#define NOTE_C8 4186 +#define NOTE_CS8 4435 +#define NOTE_D8 4699 +#define NOTE_DS8 4978 + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino new file mode 100644 index 0000000..5c07947 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino @@ -0,0 +1,242 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program tests the sleep mode and register retention of the lora device in sleep + mode, it assumes an Atmel ATMega328P processor is in use. The LoRa settings to use are specified in the + 'Settings.h' file. + + A packet is sent, containing the text 'Before Device Sleep' and the LoRa device and Atmel processor are put + to sleep. The processor watchdog timer should wakeup the processor in 15 seconds (approx) and register + values should be retained. The device then attempts to transmit another packet 'After Device Sleep' + without re-loading all the LoRa settings. The receiver should see 'After Device Sleep' for the first + packet and 'After Device Sleep' for the second. + + Tested on a 'bare bones' ATmega328P board, the current in sleep mode was 6.5uA. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //watchdog timer library, integral to Arduino IDE +#include +#include //get the library here; https://github.com/rocketscream/Low-Power + +#include +#include "Settings.h" + +SX126XLT LT; + +boolean SendOK; +int8_t TestPower; +uint8_t TXPacketL; + + +void loop() +{ + digitalWrite(LED1, HIGH); + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("TestPacket1> ")); + Serial.flush(); + + if (Send_Test_Packet1()) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + Serial.println(); + delay(packet_delay); + + LT.setSleep(CONFIGURATION_RETENTION); //preserve register settings in sleep. + Serial.println(F("Sleeping zzzzz....")); + Serial.println(); + Serial.flush(); + digitalWrite(LED1, LOW); + + sleep1second(15); //goto sleep for 15 seconds + + Serial.println(F("Awake !")); + Serial.flush(); + digitalWrite(LED1, HIGH); + LT.wake(); + + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("TestPacket2> ")); + Serial.flush(); + + if (Send_Test_Packet2()) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + Serial.println(); + delay(packet_delay); +} + + +void sleep1second(uint32_t sleeps) +{ + //uses the lowpower library + uint32_t index; + + for (index = 1; index <= sleeps; index++) + { + LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF); //sleep in 1 second steps + } +} + + +void packet_is_OK() +{ + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes SentOK")); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F("SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); //this leaves the LED on slightly longer for a packet error +} + + +bool Send_Test_Packet1() +{ + uint8_t bufffersize; + + uint8_t buff[] = "Before Device Sleep"; + TXPacketL = sizeof(buff); + buff[TXPacketL - 1] = '*'; + + if (sizeof(buff) > TXBUFFER_SIZE) //check that defined buffer is not larger than TX_BUFFER + { + bufffersize = TXBUFFER_SIZE; + } + else + { + bufffersize = sizeof(buff); + } + + TXPacketL = bufffersize; + + LT.printASCIIPacket( (uint8_t*) buff, bufffersize); + digitalWrite(LED1, HIGH); + + if (LT.transmit( (uint8_t*) buff, TXPacketL, 10000, TXpower, WAIT_TX)) + { + digitalWrite(LED1, LOW); + return true; + } + else + { + return false; + } +} + + +bool Send_Test_Packet2() +{ + uint8_t bufffersize; + + uint8_t buff[] = "After Device Sleep"; + TXPacketL = sizeof(buff); + buff[TXPacketL - 1] = '*'; + + if (sizeof(buff) > TXBUFFER_SIZE) //check that defined buffer is not larger than TX_BUFFER + { + bufffersize = TXBUFFER_SIZE; + } + else + { + bufffersize = sizeof(buff); + } + + TXPacketL = bufffersize; + + LT.printASCIIPacket( (uint8_t*) buff, bufffersize); + digitalWrite(LED1, HIGH); + + if (LT.transmit( (uint8_t*) buff, TXPacketL, 10000, TXpower, WAIT_TX)) + { + digitalWrite(LED1, LOW); + return true; + } + else + { + return false; + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("5_LoRa_TX_Sleep_Timed_Wakeup_Atmel Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.print(F("Transmitter ready - TXBUFFER_SIZE ")); + Serial.println(TXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h new file mode 100644 index 0000000..e8da812 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h @@ -0,0 +1,45 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define BUZZER 4 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define TXBUFFER_SIZE 32 //RX buffer size + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/62_LoRa_Wake_on_RX_Atmel.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/62_LoRa_Wake_on_RX_Atmel.ino new file mode 100644 index 0000000..eb73c6b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/62_LoRa_Wake_on_RX_Atmel.ino @@ -0,0 +1,224 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + When the program starts the LoRa device is setup to recieve packets with pin DIO1 set to go high when a + packet arrives. The receiver remains powered (it cannot receive otherwise) and the processor + (Atmel ATMega328P or 1284P) is put to sleep. When pin DIO1 does go high, indicating a packet is received, + the processor wakes up and prints the packet. It then goes back to sleep. + + There is a printout of the valid packets received, these are assumed to be in ASCII printable text. + The LED will flash for each packet received and the buzzer will sound,if fitted. + + Tested on a 'bare bones' ATmega328P board, the current in sleep mode was 6.53mA. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include +#include +#include "Settings.h" + +#include +#include +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +#include + +SX126XLT LT; + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create a buffer for the received packet + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 0, NO_WAIT); //setup LoRa device for receive with no timeout + + Serial.println(F("Waiting for RX - Sleeping")); + Serial.flush(); + + attachInterrupt(digitalPinToInterrupt(DIO1), wakeUp, HIGH); + + atmelSleepPermanent(); //sleep the processor + + detachInterrupt(digitalPinToInterrupt(DIO1)); + + //something has happened ? + Serial.println(F("Awake")); + digitalWrite(LED1, HIGH); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + RXPacketL = LT.readPacket(RXBUFFER, RXBUFFER_SIZE); //now read in the received packet to the RX buffer + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + + digitalWrite(BUZZER, LOW); + } + Serial.println(); +} + + +void wakeUp() +{ + //handler for the interrupt +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); + RXpacketCount++; + + RXPacketL = LT.readPacket(RXBUFFER, RXBUFFER_SIZE); //now read in the received packet to the RX buffer + + Serial.print(F("Packet> ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + Serial.println(); + led_Flash(2, 125); //LED flash for approx 10 seconds +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.println(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + Serial.println(); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("62_LoRa_Wake_on_RX_Atmel Starting")); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + Serial.println(F("Radio Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/Settings.h new file mode 100644 index 0000000..708e00e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. BUZZER may not be used +//by this sketch so they do not need to be connected and should be set to -1. + +#define NSS 10 //select pin on lora device +#define NRESET 9 //reset pin on lora device +#define RFBUSY 7 //busy pin on lora device +#define DIO1 3 //DIO1 pin on lora device, used for RX and TX done + +#define LED1 8 //on board LED, high for on +#define BUZZER -1 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size +const uint16_t packetCRCcheck = 0x3F83; //CRC to check RX packet for +const uint8_t packetCRClengthcheck = 23; //packet length to check for + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/6_LoRa_RX_and_Sleep_Atmel.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/6_LoRa_RX_and_Sleep_Atmel.ino new file mode 100644 index 0000000..7b48cef --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/6_LoRa_RX_and_Sleep_Atmel.ino @@ -0,0 +1,248 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + When the program starts the LoRa device is setup to recieve packets with pin DIO1 set to go high when a + packet arrives. The receiver remains powered (it cannot receive otherwise) and the processor + (Atmel ATMega328P or 1284P) is put to sleep. When pin DIO1 does go high, indicating a packet is received, + the processor wakes up and prints the packet. It then goes back to sleep. + + There is a printout of the valid packets received, these are assumed to be in ASCII printable text. + The LED will flash for each packet received and the buzzer will sound,if fitted. + + Tested on a 'bare bones' ATmega328P board, the current in sleep mode was 6.51mA, or about half that on + a SX127X setup. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" + +#include +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +SX126XLT LT; + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create a buffer for the received packet + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + LT.fillSXBuffer(0, 1, '#'); //make sure the first part of FIFO is cleared, so we can tell its a fresh packet + + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 0, NO_WAIT); //setup LoRa device for receive and continue + + //receive is setup + + Serial.println(F("Going to sleep zzzz")); + Serial.println(); + Serial.flush(); //make sure all serial has gone, it can wake up processor + + sleep_permanent(); //put processor to sleep, with LoRa device listening, should + //wakeup when DIO1 goes high + + Serial.println(F("Awake !!!!")); + digitalWrite(LED1, HIGH); //something has happened ? + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + RXPacketL = LT.readPacket(RXBUFFER, RXBUFFER_SIZE); //now read in the received packet to the RX buffer + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + + digitalWrite(BUZZER, LOW); + } + + Serial.println(); +} + + +void sleep_permanent() +{ + LT.clearIrqStatus(IRQ_RADIO_ALL); //ensure the DIO1 low is cleared, otherwise there could be an immediate wakeup + attachPCINT(digitalPinToPCINT(DIO1), wakeUp, HIGH); //This is a hardware interrupt, the LoRa device is set for DIOo goes high on RXdone + ADCSRA = 0; //disable ADC + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + + MCUCR = bit (BODS) | bit (BODSE); //turn on brown-out enable select + MCUCR = bit (BODS); //this must be done within 4 clock cycles of above + interrupts (); //guarantees next instruction executed + + sleep_cpu (); //sleep within 3 clock cycles of above + + /* wake up here */ + + sleep_disable(); + + detachPCINT(digitalPinToPCINT(DIO1)); +} + + +void wakeUp() +{ + //handler for the interrupt +} + + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); + RXpacketCount++; + + RXPacketL = LT.readPacket(RXBUFFER, RXBUFFER_SIZE); //now read in the received packet to the RX buffer + + Serial.print(F("Packet> ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("6_LoRa_RX_and_Sleep_Atmel Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("Radio Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); + +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/Settings.h new file mode 100644 index 0000000..14661c4 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //SX126X busy pin +#define LED1 8 //on board LED, high for on +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define BUZZER -1 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/23_GPS_Tracker_Transmitter/23_GPS_Tracker_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/23_GPS_Tracker_Transmitter/23_GPS_Tracker_Transmitter.ino new file mode 100644 index 0000000..5cec8e1 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/23_GPS_Tracker_Transmitter/23_GPS_Tracker_Transmitter.ino @@ -0,0 +1,407 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 21/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is an example of a basic GPS tracker. The program reads the GPS, + waits for an updated fix and transmits location and altitude, number of satellites in view, the HDOP + value, the fix time of the GPS and the battery voltage. This transmitter can be also be used to + investigate GPS performance. At startup there should be a couple of seconds of recognisable text from + the GPS printed to the serial monitor. If you see garbage or funny characters its likley the GPS baud + rate is wrong. If the transmitter is turned on from cold, the receiver will pick up the cold fix time, + which is an indication of GPS performance. The GPS will be powered on for around 4 seconds before the + timing of the fix starts. Outside with a good view of the sky most GPSs should produce a fix in around + 45 seconds. The number of satellites and HDOP are good indications to how well a GPS is working. + + The program writes direct to the LoRa devices internal buffer, no memory buffer is used. + + The LoRa settings are configured in the Settings.h file. + + The program has the option of using a pin to control the power to the GPS (GPSPOWER), if the GPS module + or board being used has this feature. To not use this feature set the define for GPSPOWER in the + Settings.h file to '#define GPSPOWER -1'. Also set the GPSONSTATE and GPSOFFSTATE to the appropriate logic + levels. + + There is also an option of using a logic pin to turn the resistor divider used to read battery voltage on + and off. This reduces current used in sleep mode. To use the feature set the define for pin BATVREADON + in 'Settings.h' to the pin used. If not using the feature set the pin number to -1. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" +#define authorname "Stuart Robinson" + +#include +#include + +#include "Settings.h" +#include + +SX126XLT LT; + +#include //get library here > http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + + +#ifdef USE_SOFTSERIAL_GPS +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#else +#define GPSserial HardwareSerialPort //hardware serial port (eg Serial1) is configured in the Settings.h file +#endif + + +uint8_t TXStatus = 0; //used to store current status flag bits of Tracker transmitter (TX) +uint8_t TXPacketL; //length of LoRa packet (TX) +float TXLat; //Latitude from GPS on Tracker transmitter (TX) +float TXLon; //Longitude from GPS on Tracker transmitter (TX) +float TXAlt; //Altitude from GPS on Tracker transmitter (TX) +uint8_t TXSats; //number of GPS satellites seen (TX) +uint32_t TXHdop; //HDOP from GPS on Tracker transmitter (TX) +uint16_t TXVolts; //Volts (battery) level on Tracker transmitter (TX) +uint32_t TXGPSFixTime; //GPS fix time in hot fix mode of GPS on Tracker transmitter (TX) +uint32_t TXPacketCount, TXErrorsCount; //keep count of OK packets and send errors + + +void loop() +{ + + if (gpsWaitFix(WaitGPSFixSeconds)) + { + sendLocation(TXLat, TXLon, TXAlt, TXHdop, TXGPSFixTime); + Serial.println(); + Serial.print(F("Waiting ")); + Serial.print(Sleepsecs); + Serial.println(F("s")); + delay(Sleepsecs * 1000); //this sleep is used to set overall transmission cycle time + } + else + { + send_Command(NoFix); //send notification of no GPS fix. + } +} + + +bool gpsWaitFix(uint32_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for good fix + uint32_t endwaitmS, GPSonTime; + bool GPSfix = false; + float tempfloat; + uint8_t GPSchar; + + GPSonTime = millis(); + GPSserial.begin(9600); //start GPSserial + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.println(F("s")); + + endwaitmS = millis() + (waitSecs * 1000); + + while (millis() < endwaitmS) + { + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated()) + { + GPSfix = true; + Serial.print(F("Have GPS Fix ")); + TXGPSFixTime = millis() - GPSonTime; + Serial.print(TXGPSFixTime); + Serial.println(F("mS")); + + TXLat = gps.location.lat(); + TXLon = gps.location.lng(); + TXAlt = gps.altitude.meters(); + TXSats = gps.satellites.value(); + TXHdop = gps.hdop.value(); + tempfloat = ( (float) TXHdop / 100); + + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F(",")); + Serial.print(TXSats); + Serial.print(F(",")); + Serial.print(tempfloat, 2); + Serial.println(); + + break; //exit while loop reading GPS + } + } + + //if here then there has either been a fix or no fix and a timeout + + if (GPSfix) + { + setStatusByte(GPSFix, 1); //set status bit to flag a GPS fix + } + else + { + setStatusByte(GPSFix, 0); //set status bit to flag no fix + Serial.println(); + Serial.println(F("Timeout - No GPSFix")); + Serial.println(); + GPSfix = false; + } + + GPSserial.end(); //serial RX interrupts interfere with SPI, so stop GPSserial + return GPSfix; +} + + +void sendLocation(float Lat, float Lon, float Alt, uint32_t Hdop, uint32_t fixtime) +{ + uint8_t len; + uint16_t IRQStatus; + + Serial.print(F("Send Location")); + + TXVolts = readSupplyVoltage(); //get the latest supply\battery volts + + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeUint8(LocationPacket); //indentify type of packet + LT.writeUint8(Broadcast); //who is the packet sent too + LT.writeUint8(ThisNode); //tells receiver where is packet from + LT.writeFloat(Lat); //add latitude + LT.writeFloat(Lon); //add longitude + LT.writeFloat(Alt); //add altitude + LT.writeUint8(TXSats); //add number of satellites + LT.writeUint32(Hdop); //add hdop + LT.writeUint8(TXStatus); //add tracker status + LT.writeUint32(fixtime); //add GPS fix time in mS + LT.writeUint16(TXVolts); //add tracker supply volts + LT.writeUint32(millis()); //add uptime in mS + len = LT.endWriteSXBuffer(); //close buffer write + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); + digitalWrite(LED1, LOW); + + if (TXPacketL) + { + TXPacketCount++; + Serial.println(F(" - Done ")); + Serial.print(F("SentOK,")); + Serial.print(TXPacketCount); + Serial.print(F(",Errors,")); + Serial.println(TXErrorsCount); + } + else + { + //if here there was an error transmitting packet + TXErrorsCount++; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set + Serial.println(); + } +} + + +void setStatusByte(uint8_t bitnum, uint8_t bitval) +{ + //program the status byte + + if (bitval == 0) + { + bitClear(TXStatus, bitnum); + } + else + { + bitSet(TXStatus, bitnum); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //flash LED to show tracker is alive + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void send_Command(char cmd) +{ + bool SendOK; + uint8_t len; + + Serial.print(F("Send Cmd ")); + Serial.write(cmd); + + LT.startWriteSXBuffer(0); + LT.writeUint8(cmd); //packet addressing used indentify type of packet + LT.writeUint8(Broadcast); //who is the packet sent to + LT.writeUint8(ThisNode); //where is packet from + LT.writeUint16(TXVolts); + len = LT.endWriteSXBuffer(); + + digitalWrite(LED1, HIGH); + SendOK = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); //timeout set at 10 seconds + digitalWrite(LED1, LOW); + + if (SendOK) + { + Serial.println(F(" - Done")); + } + else + { + Serial.println(F(" - Error")); + } +} + + +uint16_t readSupplyVoltage() +{ + //relies on 1V internal reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t voltage = 0; + uint8_t index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn on MOSFET connecting resitor divider in circuit + } + + analogReference(INTERNAL); + temp = analogRead(SupplyAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(SupplyAD); + voltage = voltage + temp; + } + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn off MOSFET connecting resitor divider in circuit + } + + + voltage = ((voltage / 5) * ADMultiplier) + DIODEMV; + return voltage; +} + + +void GPSON() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void setup() +{ + uint32_t endmS; + + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + GPSON(); + } + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); + } + + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("23_GPS_Tracker_Transmitter Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + + TXVolts = readSupplyVoltage(); + + Serial.print(F("Supply ")); + Serial.print(TXVolts); + Serial.println(F("mV")); + + send_Command(PowerUp); //send power up command, includes supply mV + + Serial.println(F("Startup GPS check")); + + GPSserial.begin(9600); + + endmS = millis() + echomS; + + while (millis() < endmS) + { + while (GPSserial.available() > 0) + Serial.write(GPSserial.read()); + } + Serial.println(); + Serial.println(); + + Serial.println(F("Wait for first GPS fix")); + gpsWaitFix(WaitFirstGPSFixSeconds); + + sendLocation(TXLat, TXLon, TXAlt, TXHdop, TXGPSFixTime); +} diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h new file mode 100644 index 0000000..5d884d7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h @@ -0,0 +1,68 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used + +#define GPSPOWER -1 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define LED1 8 //On board LED, high for on +#define SupplyAD A7 //pin for reading supply\battery voltage +#define BATVREADON 8 //turns on battery resistor divider, high for on, -1 if not used + +const float ADMultiplier = 10.0; //multiplier for supply volts calculation +#define DIODEMV 98 //mV voltage drop accross diode at approx 8mA + + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa TX power + +#define ThisNode '2' //a character that identifies this tracker + +//************************************************************************************************** +// GPS Settings +//************************************************************************************************** + +#define USE_SOFTSERIAL_GPS //need to include this if we are using softserial for GPS +//#define HardwareSerialPort Serial1 //if using hardware serial enable this define for hardware serial port + +#define GPSBaud 9600 //GPS Baud rate + +#define WaitGPSFixSeconds 30 //time in seconds to wait for a new GPS fix +#define WaitFirstGPSFixSeconds 1800 //time to seconds to wait for the first GPS fix at startup +#define Sleepsecs 5 //seconds between transmissions, this delay is used to set overall transmission cycle time + +#define echomS 2000 //number of mS to run GPS echo at startup + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino new file mode 100644 index 0000000..df35e85 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino @@ -0,0 +1,322 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 22/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is an basic receiver for the '23_Simple_GPS_Tracker_Transmitter' program. + The program reads the received packet from the tracker transmitter and displays the results on + the serial monitor. The LoRa and frequency settings provided in the Settings.h file must + match those used by the transmitter. + + The program receives direct from the LoRa devices internal buffer. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include +#include + +SX126XLT LT; + +#include "Settings.h" +#include + + +uint32_t RXpacketCount; //count of received packets + +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet +uint8_t PacketType; //for packet addressing, identifies packet type +uint8_t Destination; //for packet addressing, identifies the destination (receiving) node +uint8_t Source; //for packet addressing, identifies the source (transmiting) node +uint8_t TXStatus; //A status byte +float TXLat; //latitude +float TXLon; //longitude +float TXAlt; //altitude +uint32_t TXHdop; //HDOP, indication of fix quality, horizontal dilution of precision, low is good +uint32_t TXGPSFixTime; //time in mS for fix +uint16_t TXVolts; //supply\battery voltage +uint8_t TXSats; //number of sattelites in use +uint32_t TXupTimemS; //up time of TX in mS + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort + + digitalWrite(LED1, HIGH); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + } + + Serial.println(); +} + + +void readPacketAddressing() +{ + //the transmitter is using packet addressing, so read in the details + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + LT.endReadSXBuffer(); +} + + +void packet_is_OK() +{ + float tempHdop; + + RXpacketCount++; + Serial.print(F("Packet OK > ")); + + readPacketAddressing(); + + if (PacketType == PowerUp) + { + LT.startReadSXBuffer(0); + LT.readUint8(); //read byte from FIFO, not used + LT.readUint8(); //read byte from FIFO, not used + LT.readUint8(); //read byte from FIFO, not used + TXVolts = LT.readUint16(); + LT.endReadSXBuffer(); + Serial.print(F("Tracker transmitter powerup - battery ")); + Serial.print(TXVolts); + Serial.print(F("mV")); + } + + + if (PacketType == LocationPacket) + { + //packet has been received, now read from the SX12XX FIFO in the correct order. + Serial.print(F("LocationPacket ")); + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readFloat(); + TXSats = LT.readUint8(); + TXHdop = LT.readUint32(); + TXStatus = LT.readUint8(); + TXGPSFixTime = LT.readUint32(); + TXVolts = LT.readUint16(); + TXupTimemS = LT.readUint32(); + RXPacketL = LT.endReadSXBuffer(); + + tempHdop = ( (float) TXHdop / 100); //need to convert Hdop read from GPS as uint32_t to a float for display + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F("m,")); + Serial.print(TXSats); + Serial.print(F(",")); + Serial.print(tempHdop, 2); + Serial.print(F(",")); + Serial.print(TXStatus); + Serial.print(F(",")); + Serial.print(TXGPSFixTime); + Serial.print(F("mS,")); + Serial.print(TXVolts); + Serial.print(F("mV,")); + Serial.print((TXupTimemS / 1000)); + Serial.print(F("s,")); + printpacketDetails(); + return; + } + + if (PacketType == LocationBinaryPacket) + { + //packet from locator has been received, now read from the SX12XX FIFO in the correct order. + Serial.print(F("LocationBinaryPacket ")); + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readInt16(); + TXStatus = LT.readUint8(); + RXPacketL = LT.endReadSXBuffer(); + + tempHdop = ( (float) TXHdop / 100); //need to convert Hdop read from GPS as uint32_t to a float for display + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 0); + Serial.print(F("m,")); + Serial.print(TXStatus); + printpacketDetails(); + return; + } + + if (PacketType == NoFix) + { + Serial.print(F("No Tracker GPS fix ")); + printpacketDetails(); + return; + } + +} + + +void printpacketDetails() +{ + uint16_t IRQStatus; + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Packets,")); + Serial.print(RXpacketCount); + + Serial.print(F(",Length,")); + Serial.print(RXPacketL); + IRQStatus = LT.readIrqStatus(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F("PacketError,RSSI")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("24_GPS_Tracker_Receiver Starting")); + + if (BUZZER >= 0) + { + pinMode(BUZZER, OUTPUT); + Serial.println(F("BUZZER Enabled")); + } + else + { + Serial.println(F("BUZZER Not Enabled")); + } + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("LoRa device found")); + led_Flash(2, 125); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h new file mode 100644 index 0000000..107b2b9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h @@ -0,0 +1,37 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 22/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define LED1 8 //On board LED, high for on + +#define BUZZER -1 //Buzzer if fitted, high for on. Set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino new file mode 100644 index 0000000..d4bd445 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino @@ -0,0 +1,624 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 21/03/20 + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is an example of a functional GPS tracker receiver using lora. + It is capable of picking up the trackers location packets from many kilometres away with only basic antennas. + + The program receives the location packets from the remote tracker transmitter and writes them on an OLED + display and also prints the information to the Arduino IDE serial monitor. The program can read a locally + attached GPS and when that has a fix, will display the distance and direction to the remote tracker. + + The program writes direct to the lora devices internal buffer, no memory buffer is used. The lora settings + are configured in the Settings.h file. + + The receiver recognises two types of tracker packet, the one from the matching program '23_GPS_Tracker_Transmitter' + (LocationPacket, 27 bytes) which causes these fields to be printed to the serial monitor; + + Latitude, Longitude, Altitude, Satellites, HDOP, TrackerStatusByte, GPS Fixtime, Battery mV, Distance, Direction, + Distance, Direction, PacketRSSI, PacketSNR, NumberPackets, PacketLength, IRQRegister. + + This is a long packet which at the long range LoRa settings takes just over 3 seconds to transmit. + + The receiver also recognises a much shorter location only packet (LocationBinaryPacket, 11 bytes) and when + received this is printed to the serial monitor; + + Latitude, Longitude, Altitude, TrackerStatusByte, Distance, Direction, PacketRSSI, PacketSNR, NumberPackets, + PacketLength, IRQRegister. + + Most of the tracker information (for both types of packet) is shown on the OLED display. If there has been a + tracker transmitter GPS fix the number\identifier of that tracker is shown on row 0 right of screen and if there + is a recent local (receiver) GPS fix an 'R' is displayed row 1 right of screen. + + When the tracker transmitter starts up or is reset its sends a power up message containing the battery voltage + which is shown on the OLED and printer to the serial monitor. + + The program has the option of using a pin to control the power to the GPS, if the GPS module being used has this + feature. To use the option change the define in Settings.h; + + '#define GPSPOWER -1' from -1 to the pin number being used. Also set the GPSONSTATE and GPSOFFSTATE defines to + the appropriate logic levels. + + The program by default uses software serial to read the GPS, you can use hardware serial by commenting out this + line in the Settings.h file; + + #define USE_SOFTSERIAL_GPS + + And then defining the hardware serial port you are using, which defaults to Serial1. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + + +#define Program_Version "V1.1" + +#include +#include +SX126XLT LT; + +#include "Settings.h" +#include + +#include //https://github.com/olikraus/u8g2 +//U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //standard 0.96" SSD1306 +U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //1.3" OLED often sold as 1.3" SSD1306 + + +#include //http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#ifdef USE_SOFTSERIAL_GPS +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#else +#define GPSserial HardwareSerialPort //hardware serial port (eg Serial1) is configured in the Settings.h file +#endif + +uint32_t RXpacketCount; //count of received packets +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //signal strength (RSSI) dBm of received packet +int8_t PacketSNR; //signal to noise ratio (SNR) dB of received packet +uint8_t PacketType; //for packet addressing, identifies packet type +uint8_t Destination; //for packet addressing, identifies the destination (receiving) node +uint8_t Source; //for packet addressing, identifies the source (transmiting) node +uint8_t TXStatus; //status byte from tracker transmitter +uint8_t TXSats; //number of sattelites in use +float TXLat; //latitude +float TXLon; //longitude +float TXAlt; //altitude +float RXLat; //latitude +float RXLon; //longitude +float RXAlt; //altitude +uint32_t TXHdop; //HDOP, indication of fix quality, horizontal dilution of precision, low is good +uint32_t TXGPSFixTime; //time in mS for fix +uint16_t TXVolts; //supply\battery voltage +uint16_t RXVolts; //supply\battery voltage +float TXdistance; //calculated distance to tracker +uint16_t TXdirection; //calculated direction to tracker +uint16_t RXerrors; +uint32_t TXupTimemS; //up time of TX in mS + +uint32_t LastRXGPSfixCheck; //used to record the time of the last GPS fix + +bool TXLocation = false; //set to true when at least one tracker location packet has been received +bool RXGPSfix = false; //set to true if the local GPS has a recent fix + +uint8_t FixCount = DisplayRate; //used to keep track of number of GPS fixes before display updated + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, NO_WAIT); //returns 0 if packet error of some sort + + while (!digitalRead(DIO1)) + { + readGPS(); //If the DIO pin is low, no packet arrived, so read the GPS + } + + //something has happened in receiver + digitalWrite(LED1, HIGH); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + RXPacketL = LT.readRXPacketL(); + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + } + Serial.println(); +} + + +void readGPS() +{ + if (GPSserial.available() > 0) + { + gps.encode(GPSserial.read()); + } + + + if ( millis() > (LastRXGPSfixCheck + NoRXGPSfixms)) + { + RXGPSfix = false; + LastRXGPSfixCheck = millis(); + dispscreen1(); + } + + + if (gps.location.isUpdated() && gps.altitude.isUpdated()) + { + RXGPSfix = true; + RXLat = gps.location.lat(); + RXLon = gps.location.lng(); + RXAlt = gps.altitude.meters(); + printRXLocation(); + LastRXGPSfixCheck = millis(); + + if ( FixCount == 1) //update screen when FIXcoount counts down from DisplayRate to 1 + { + FixCount = DisplayRate; + dispscreen1(); + } + FixCount--; + } +} + + +bool readTXStatus(byte bitnum) +{ + return bitRead(TXStatus, bitnum); +} + + +void printRXLocation() +{ + Serial.print(F("LocalGPS ")); + Serial.print(RXLat, 5); + Serial.print(F(",")); + Serial.print(RXLon, 5); + Serial.print(F(",")); + Serial.print(RXAlt, 1); + Serial.println(); +} + + +void readPacketAddressing() +{ + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + LT.endReadSXBuffer(); +} + + +void packet_is_OK() +{ + //uint16_t IRQStatus; + float tempfloat; + + RXpacketCount++; + + readPacketAddressing(); + + if (PacketType == PowerUp) + { + LT.startReadSXBuffer(0); + LT.readUint8(); //read byte from SXBuffer, not used + LT.readUint8(); //read byte from SXBuffer, not used + LT.readUint8(); //read byte from SXBuffer, not used + TXVolts = LT.readUint16(); //read tracker transmitter voltage + LT.endReadSXBuffer(); + Serial.print(F("Tracker Powerup - Battery ")); + Serial.print(TXVolts); + Serial.println(F("mV")); + dispscreen2(); + } + + if (PacketType == LocationPacket) + { + //packet has been received, now read from the SX12XX FIFO in the correct order. + Serial.print(F("LocationPacket ")); + TXLocation = true; + LT.startReadSXBuffer(0); //start the read of received packet + PacketType = LT.readUint8(); //read in the PacketType + Destination = LT.readUint8(); //read in the Packet destination address + Source = LT.readUint8(); //read in the Packet source address + TXLat = LT.readFloat(); //read in the tracker latitude + TXLon = LT.readFloat(); //read in the tracker longitude + TXAlt = LT.readFloat(); //read in the tracker altitude + TXSats = LT.readUint8(); //read in the satellites in use by tracker GPS + TXHdop = LT.readUint32(); //read in the HDOP of tracker GPS + TXStatus = LT.readUint8(); //read in the tracker status byte + TXGPSFixTime = LT.readUint32(); //read in the last fix time of tracker GPS + TXVolts = LT.readUint16(); //read in the tracker supply\battery volts + TXupTimemS = LT.readUint32(); //read in the TX uptime in mS + RXPacketL = LT.endReadSXBuffer(); //end the read of received packet + + + if (RXGPSfix) //if there has been a local GPS fix do the distance and direction calculation + { + TXdirection = (int16_t) TinyGPSPlus::courseTo(RXLat, RXLon, TXLat, TXLon); + TXdistance = TinyGPSPlus::distanceBetween(RXLat, RXLon, TXLat, TXLon); + } + else + { + TXdistance = 0; + TXdirection = 0; + } + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F(",")); + Serial.print(TXSats); + Serial.print(F(",")); + + tempfloat = ( (float) TXHdop / 100); //need to convert Hdop read from GPS as uint32_t to a float for display + Serial.print(tempfloat, 2); + + Serial.print(F(",")); + Serial.print(TXStatus); + Serial.print(F(",")); + + Serial.print(TXGPSFixTime); + Serial.print(F("mS,")); + Serial.print(TXVolts); + Serial.print(F("mV,")); + Serial.print((TXupTimemS / 1000)); + Serial.print(F("s,")); + + Serial.print(TXdistance, 0); + Serial.print(F("m,")); + Serial.print(TXdirection); + Serial.print(F("d")); + printpacketDetails(); + dispscreen1(); //and show the packet detail it on screen + return; + } + + + if (PacketType == LocationBinaryPacket) + { + //packet from locator has been received, now read from the SX12XX FIFO in the correct order. + TXLocation = true; + Serial.print(F("LocationBinaryPacket ")); + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readInt16(); + TXStatus = LT.readUint8(); + RXPacketL = LT.endReadSXBuffer(); + + if (RXGPSfix) //if there has been a local GPS fix do the distance and direction calculation + { + TXdirection = (int16_t) TinyGPSPlus::courseTo(RXLat, RXLon, TXLat, TXLon); + TXdistance = TinyGPSPlus::distanceBetween(RXLat, RXLon, TXLat, TXLon); + } + else + { + TXdistance = 0; + TXdirection = 0; + } + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 0); + Serial.print(F("m,")); + Serial.print(TXStatus); + Serial.print(F(",")); + Serial.print(TXdistance, 0); + Serial.print(F("m,")); + Serial.print(TXdirection); + Serial.print(F("d")); + printpacketDetails(); + dispscreen1(); + return; + } +} + + +void printpacketDetails() +{ + uint16_t IRQStatus; + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Packets,")); + Serial.print(RXpacketCount); + + Serial.print(F(",Length,")); + Serial.print(RXPacketL); + IRQStatus = LT.readIrqStatus(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + RXerrors++; + Serial.print(F("PacketError,RSSI")); + + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void dispscreen1() +{ + //show received packet data on display + float tempfloat; + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(TXLat, 5); + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(TXLon, 5); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(TXAlt, 0); + disp.print(F("m")); + disp.clearLine(3); + disp.setCursor(0, 3); + + disp.print(F("RSSI ")); + disp.print(PacketRSSI); + disp.print(F("dBm")); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(F("SNR ")); + + if (PacketSNR > 0) + { + disp.print(F("+")); + } + + if (PacketSNR == 0) + { + disp.print(F(" ")); + } + + if (PacketSNR < 0) + { + disp.print(F("-")); + } + + disp.print(PacketSNR); + disp.print(F("dB")); + + if (PacketType == LocationPacket) + { + disp.clearLine(5); + disp.setCursor(0, 5); + tempfloat = ((float) TXVolts / 1000); + disp.print(F("Batt ")); + disp.print(tempfloat, 2); + disp.print(F("v")); + } + + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("Packets ")); + disp.print(RXpacketCount); + + disp.clearLine(7); + + if (RXGPSfix) + { + disp.setCursor(15, 1); + disp.print(F("R")); + } + else + { + disp.setCursor(15, 1); + disp.print(F(" ")); + disp.setCursor(0, 7); + disp.print(F("No Local Fix")); + } + + if (RXGPSfix && TXLocation) //only display distance and direction if have received tracker packet and have local GPS fix + { + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(TXdistance, 0); + disp.print(F("m ")); + disp.print(TXdirection); + disp.print(F("d")); + } + + if (readTXStatus(GPSFix)) + { + disp.setCursor(15, 0); + disp.write(Source); + } + +} + + +void dispscreen2() +{ + //show tracker powerup data on display + float tempfloat; + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Tracker Powerup")); + disp.setCursor(0, 1); + disp.print(F("Battery ")); + tempfloat = ((float) TXVolts / 1000); + disp.print(tempfloat, 2); + disp.print(F("v")); +} + + +void GPSON() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void setup() +{ + uint32_t endmS; + + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("25_GPS_Tracker_Receiver_With_Display_and_GPS Starting")); + + if (BUZZER >= 0) + { + pinMode(BUZZER, OUTPUT); + } + + SPI.begin(); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + + Serial.print(F("Checking LoRa device - ")); //Initialize LoRa + disp.setCursor(0, 0); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + Serial.println(F("Receiver ready")); + disp.print(F("Receiver ready")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No LoRa device responding")); + disp.print(F("No LoRa device")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + Serial.println(F("Startup GPS check")); + + endmS = millis() + echomS; + + //now startup GPS + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + } + + GPSON(); + GPSserial.begin(GPSBaud); + + while (millis() < endmS) + { + while (GPSserial.available() > 0) + Serial.write(GPSserial.read()); + } + Serial.println(); + Serial.println(); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h new file mode 100644 index 0000000..512c691 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h @@ -0,0 +1,67 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER SWITCH1 may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define LED1 8 //On board LED, high for on + +#define BUZZER -1 //Buzzer if fitted, high for on. Set to -1 if not used + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER 4 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + + + + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +//************************************************************************************************** +// GPS Settings +//************************************************************************************************** + +#define USE_SOFTSERIAL_GPS //need to include this if we are using softserial for GPS +//#define HardwareSerialPort Serial1 //if using hardware serial enable this define for hardware serial port + +#define GPSBaud 9600 //GPS Baud rate +#define WaitGPSFixSeconds 30 //time to wait for a new GPS fix +#define echomS 2000 //number of mS to run GPS echo for at startup + +#define NoRXGPSfixms 15000 //max number of mS to allow before no local fix flagged +#define DisplayRate 7 //when working OK the GPS will get a new fix every second or so + //this rate defines how often the display should be updated + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino new file mode 100644 index 0000000..0921913 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino @@ -0,0 +1,173 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program will receive a lora packet and relay (re-transmit) it. The receiving + and transmitting can use different frequencies and lora settings, although in this example they are + the same. The receiving and transmitting settings are in the 'Settings.h' file. If the relay is located + in an advantageous position, for instance on top of a tall tree, building or in an radio controlled model + then the range at which trackers or nodes on the ground can be received is considerably increased. + In these circumstances the relay may listen at a long range setting using SF12 for example and then + re-transmit back to the ground at SF7. + + For an example of the use of such a program see this report; + + How to Search 500 Square Kilometres in 10 minutes.pdf in the libraries 'Test_Reports' folder. + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + + +#include +#include +#include "Settings.h" + +SX126XLT LT; + +uint8_t RXPacketL, TXPacketL; +int8_t PacketRSSI, PacketSNR; +uint16_t RXPacketErrors; + + +void loop() +{ + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout set + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + Serial.println(); +} + + +void packet_is_OK() +{ + //a packet has been received, so change to relay settings and transmit buffer + + Serial.print(F("PacketOK ")); + printreceptionDetails(); + delay(packet_delay / 2); + digitalWrite(LED1, LOW); + delay(packet_delay / 2); + + Serial.print(F(" Retransmit")); + LT.setupLoRa(RelayFrequency, RelayOffset, RelaySpreadingFactor, RelayBandwidth, RelayCodeRate, RelayOptimisation); + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitSXBuffer(0, RXPacketL, 10000, TXpower, WAIT_TX); + Serial.print(F(" - Done")); + digitalWrite(LED1, LOW); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + RXPacketErrors++; + IRQStatus = LT.readIrqStatus(); + + led_Flash(5, 50); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout ")); + } + else + { + Serial.print(F("PacketError ")); + printreceptionDetails(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + } +} + + +void printreceptionDetails() +{ + Serial.print(F("RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); +} + + +void led_Flash(uint16_t flashdelay, uint16_t flashes) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + + delay(flashdelay); + digitalWrite(LED1, HIGH); + delay(flashdelay); + digitalWrite(LED1, LOW); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, SW, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + Serial.print("ListenSettings,"); + LT.printModemSettings(); + Serial.println(); + LT.setupLoRa(RelayFrequency, RelayOffset, RelaySpreadingFactor, RelayBandwidth, RelayCodeRate, RelayOptimisation); + Serial.print("RelaySettings,"); + LT.printModemSettings(); + Serial.println(); + Serial.println("Relay Ready"); +} + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/38_lora_Relay/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/38_lora_Relay/Settings.h new file mode 100644 index 0000000..9900918 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/38_lora_Relay/Settings.h @@ -0,0 +1,53 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 on LoRa device, normally not used so set to -1 +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define LED1 8 //On board LED, high for on +#define BUZZER -1 //normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa receiving parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +//LoRa relay (re-transmitting) parameters +const uint32_t RelayFrequency = 434000000; //frequency of transmissions +const uint32_t RelayOffset = 0; //offset frequency for calibration purposes + +const uint8_t RelayBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t RelaySpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t RelayCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t RelayOptimisation = LDRO_AUTO; //low data rate optimisation setting + + +const int8_t TXpower = 10; //LoRa TX power in dBm + +#define packet_delay 1000 //mS delay before received packet transmitted + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/67_Balloon_Tracker_Transmitter/67_Balloon_Tracker_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/67_Balloon_Tracker_Transmitter/67_Balloon_Tracker_Transmitter.ino new file mode 100644 index 0000000..6c1abd6 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/67_Balloon_Tracker_Transmitter/67_Balloon_Tracker_Transmitter.ino @@ -0,0 +1,744 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/09/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a tracker intended for use as a high altitude balloon (HAB) tracker. The + program sends out a standard format payload with LoRa that is compatible with the HABHUB online tracking + system. + + The HAB payload is constructed thus; + + PayloadID,Sequence,Time,Lat,Lon,Alt,Satellites,Volts,Temperature,Resets,Status,Errors,TXGPSfixms,Checksum + Field 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + + The LoRa and frequency settings can be changed in the Settings.h file. There is the option of sending + out a much shorter Search mode binary location only payload. This is intended for ground based searching + and locating. The frequency and LoRa settings of the Search mode packet can be different to the Tracker + mode used by the HAB payload. There is also the option of sending the HAB payload in FSK RTTY format, + see the Settings.h file for all the options. FSK RTTY gets sent at the same frequency as the Tracker mode + HAB packet. The LT.transmitFSKRTTY() function sends at 1 start bit, 7 data bits, no parity and 2 stop bits. + For full control of the FSK RTTY setting you can use the following alternative function; + + LT.transmitFSKRTTY(chartosend, databits, stopbits, parity, baudPerioduS, pin) + + There is a matching Balloon Tracker Receiver program which writes received data to the Serial monitor as well + as a small OLED display. + + In the Settings.h file you can set the configuration for either a Ublox GPS or a Quectel L70\L80. The GPSs + are configured for high altitude balloon mode. + + It is strongly recommended that a FRAM option is fitted for this transmitter. The sequence, resets and error + nembers are stred in non-volatile memory. This defaults to EEPROM which has a limited endurance of only + 100,000 writes, so in theory the limt is reached after the transmission of 100,000 hab packets. The use of + a FRAM will extend the life of the tracker to circa 100,000,000,000,000 transmissions. + + Changes: + 240420 - Change to work with Easy Pro Mini style modules + 300420 - Improve error detection for UBLOX GPS library + + ToDo: + + Serial monitor baud rate is set at 115200 +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include +#include //include the appropriate library + +SX126XLT LT; //create a library class instance called LT + +#include "Settings.h" +#include "ProgramLT_Definitions.h" + +//************************************************************************************************** +// HAB tracker data - these are the variables transmitted in payload +//************************************************************************************************** +uint32_t TXSequence; //sequence number of payload +uint8_t TXHours; //Hours +uint8_t TXMinutes; //Minutes +uint8_t TXSeconds; //Seconds +float TXLat; //latitude from GPS +float TXLon; //longitude from GPS +uint16_t TXAlt; //altitude from GPS +uint8_t TXSatellites; //satellites used by GPS +uint16_t TXVolts; //measured tracker supply volts +int8_t TXTemperature; //measured temperature +uint16_t TXResets; //number of tracker resets +uint8_t TXStatus = 0; //used to store current status flag bits +uint16_t TXErrors; //number of tracker Errors +uint32_t TXGPSfixms; //fix time of GPS +//************************************************************************************************** + +uint8_t TXPacketL; //length of LoRa packet sent +uint8_t TXBUFFER[TXBUFFER_SIZE]; //buffer for packet to send + +#include Memory_Library + +#include + +#include //http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#ifdef USESOFTSERIALGPS +//#include //https://github.com/SlashDevin/NeoSWSerial +//NeoSWSerial GPSserial(RXpin, TXpin); //this library is more relaible at GPS init than software serial +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#endif + +#ifndef USESOFTSERIALGPS +#define GPSserial HARDWARESERIALPORT +#endif + +#include GPS_Library //include previously defined GPS Library + +#include //get library here > https://github.com/PaulStoffregen/OneWire +OneWire oneWire(ONE_WIRE_BUS); //create instance of OneWire library +#include //get library here > https://github.com/milesburton/Arduino-TXTemperature-Control-Library +DallasTemperature sensor(&oneWire); //create instance of dallas library + +uint32_t GPSstartms; //start time waiting for GPS to get a fix + + +void loop() +{ + Serial.println(F("Start Loop")); + + GPSstartms = millis(); + + if (!gpsWaitFix(WaitGPSFixSeconds)) + { + GPS_OutputOff(); + sendCommand(NoFix); //report a GPS fix error + delay(1000); //give receiver enough time to report NoFix + } + Serial.println(); + + do_Transmissions(); //do the transmissions + + Serial.println(F("Sleep")); + LT.setSleep(CONFIGURATION_RETENTION); //put LoRa device to sleep, preserve lora register settings + Serial.flush(); //make sure no serial output pending before goint to sleep + + delay(SleepTimesecs * 1000); + + Serial.println(F("Wake")); + LT.wake(); //wake the LoRa device from sleep +} + + +void do_Transmissions() +{ + //this is where all the transmisions get sent + uint32_t startTimemS; + uint8_t index; + + incMemoryUint32(addr_SequenceNum); //increment Sequence number + + if (readConfigByte(SearchEnable)) + { + setSearchMode(); + TXPacketL = buildLocationOnly(TXLat, TXLon, TXAlt, TXStatus); //put location data in SX12xx buffer + Serial.print(F("Search packet > ")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt); + Serial.print(F(",")); + Serial.print(TXStatus); + digitalWrite(LED1, HIGH); + startTimemS = millis(); + TXPacketL = LT.transmitSXBuffer(0, TXPacketL, 10000, SearchTXpower, WAIT_TX); + printTXtime(startTimemS, millis()); + reportCompletion(); + Serial.println(); + } + + delay(1000); //gap between transmissions + + setTrackerMode(); + + TXPacketL = buildHABPacket(); + Serial.print(F("HAB Packet > ")); + printBuffer(TXBUFFER, (TXPacketL + 1)); //print the buffer (the packet to send) as ASCII + digitalWrite(LED1, HIGH); + startTimemS = millis(); + TXPacketL = LT.transmit(TXBUFFER, (TXPacketL + 1), 10000, TrackerTXpower, WAIT_TX); //will return packet length sent if OK, otherwise 0 if transmit error + digitalWrite(LED1, LOW); + printTXtime(startTimemS, millis()); + reportCompletion(); + Serial.println(); + + delay(1000); //gap between transmissions + + if (readConfigByte(FSKRTTYEnable)) + { + LT.setupDirect(TrackerFrequency, Offset); + LT.startFSKRTTY(FrequencyShift, NumberofPips, PipPeriodmS, PipDelaymS, LeadinmS); + + startTimemS = millis() - LeadinmS; + + Serial.print(F("FSK RTTY > $$$")); + Serial.flush(); + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + + for (index = 0; index <= (TXPacketL - 1); index++) //its TXPacketL-1 since we dont want to send the null at the end + { + LT.transmitFSKRTTY(TXBUFFER[index], BaudPerioduS, LED1); + Serial.write(TXBUFFER[index]); + } + + LT.transmitFSKRTTY(13, BaudPerioduS, LED1); //send carriage return + LT.transmitFSKRTTY(10, BaudPerioduS, LED1); //send line feed + LT.endFSKRTTY(); //stop transmitting carrier + digitalWrite(LED1, LOW); //LED off + printTXtime(startTimemS, millis()); + TXPacketL += 4; //add the two $ at beginning and CR/LF at end + reportCompletion(); + Serial.println(); + } +} + + +void printTXtime(uint32_t startmS, uint32_t endmS) +{ +Serial.print(F(" ")); +Serial.print(endmS-startmS); +Serial.print(F("mS")); +} + + +void reportCompletion() +{ + Serial.print(F(" ")); + if (TXPacketL == 0) + { + Serial.println(); + reporttransmitError(); + } + else + { + Serial.print(TXPacketL); + Serial.print(F("bytes")); + setStatusByte(LORAError, 0); + } +} + + +void printBuffer(uint8_t *buffer, uint8_t size) +{ + uint8_t index; + + for (index = 0; index < size; index++) + { + Serial.write(buffer[index]); + } +} + + +uint8_t buildHABPacket() +{ + //build the HAB tracker payload + uint16_t index, j, CRC; + uint8_t Count, len; + char LatArray[12], LonArray[12]; + + TXSequence = readMemoryUint32(addr_SequenceNum); //Sequence number is kept in non-volatile memory so it survives TXResets + TXResets = readMemoryUint16(addr_ResetCount); //reset count is kept in non-volatile memory so it survives TXResets + TXVolts = readSupplyVoltage(); + TXTemperature = (int8_t) readTempDS18B20(); + TXErrors = readMemoryUint16(addr_TXErrors); + + dtostrf(TXLat, 7, 5, LatArray); //format is dtostrf(FLOAT,WIDTH,PRECISION,BUFFER); + dtostrf(TXLon, 7, 5, LonArray); //converts float to character array + + len = sizeof(TXBUFFER); + memset(TXBUFFER, 0, len); //clear array to 0s + Count = snprintf((char*) TXBUFFER, + TXBUFFER_SIZE, + "$%s,%lu,%02d:%02d:%02d,%s,%s,%d,%d,%d,%d,%d,%d,%d,%lu", + FlightID, + TXSequence, + TXHours, + TXMinutes, + TXSeconds, + LatArray, + LonArray, + TXAlt, + TXSatellites, + TXVolts, + TXTemperature, + TXResets, + TXStatus, + TXErrors, + TXGPSfixms + ); + + CRC = 0xffff; //start value for CRC16 + + for (index = 1; index < Count; index++) //element 1 is first character after $ at start (for LoRa) + { + CRC ^= (((uint16_t)TXBUFFER[index]) << 8); + for (j = 0; j < 8; j++) + { + if (CRC & 0x8000) + CRC = (CRC << 1) ^ 0x1021; + else + CRC <<= 1; + } + } + + TXBUFFER[Count++] = '*'; + TXBUFFER[Count++] = Hex((CRC >> 12) & 15); //add the checksum bytes to the end + TXBUFFER[Count++] = Hex((CRC >> 8) & 15); + TXBUFFER[Count++] = Hex((CRC >> 4) & 15); + TXBUFFER[Count] = Hex(CRC & 15); + return Count; +} + + +char Hex(uint8_t lchar) +{ + //used in CRC calculation in buildHABPacket + char Table[] = "0123456789ABCDEF"; + return Table[lchar]; +} + + +uint8_t buildLocationOnly(float Lat, float Lon, uint16_t Alt, uint8_t stat) +{ + uint8_t len; + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeUint8(LocationBinaryPacket); //identify type of packet + LT.writeUint8(Broadcast); //who is the packet sent too + LT.writeUint8(ThisNode); //tells receiver where is packet from + LT.writeFloat(Lat); //add latitude + LT.writeFloat(Lon); //add longitude + LT.writeInt16(Alt); //add altitude + LT.writeUint8(stat); //add tracker status + len = LT.endWriteSXBuffer(); //close buffer write + return len; +} + + +void reporttransmitError() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F("TXError,")); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set + incMemoryUint16(addr_TXErrors); //increase the error count + setStatusByte(LORAError, 1); +} + + +void incMemoryUint32(uint32_t addr) +{ + uint32_t val = readMemoryUint32(addr); + val++; + writeMemoryUint32(addr, val); +} + + +void incMemoryUint16(uint32_t addr) +{ + uint16_t val = readMemoryUint16(addr); + val++; + writeMemoryUint16(addr, val); +} + + +void setStatusByte(uint8_t bitnum, uint8_t bitval) +{ + //program the status byte + + if (bitval == 0) + { + bitClear(TXStatus, bitnum); + } + else + { + bitSet(TXStatus, bitnum); + } +} + + +uint8_t readConfigByte(uint8_t bitnum) +{ + return bitRead(Default_config1, bitnum); +} + + +void setTrackerMode() +{ + Serial.println(F("setTrackerMode")); + LT.setupLoRa(TrackerFrequency, Offset, TrackerSpreadingFactor, TrackerBandwidth, TrackerCodeRate, TrackerOptimisation); +} + + +void setSearchMode() +{ + Serial.println(F("setSearchMode")); + LT.setupLoRa(SearchFrequency, Offset, SearchSpreadingFactor, SearchBandwidth, SearchCodeRate, SearchOptimisation); +} + + +uint8_t sendCommand(char cmd) +{ + uint8_t len; + TXVolts = readSupplyVoltage(); + Serial.print(F("Send Cmd ")); + Serial.write(cmd); + Serial.println(); + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + LT.writeUint8(cmd); //this byte defines the packet type + LT.writeUint8(Broadcast); //destination address of the packet, the receivers address + LT.writeUint8(ThisNode); //source address of this node + LT.writeUint16(TXVolts); //add the battery voltage + LT.writeUint8(TXStatus); //add the status byte + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + //now transmit the packet, set a timeout of 5000mS, wait for it to complete sending + + digitalWrite(LED1, HIGH); //turn on LED as an indicator + TXPacketL = LT.transmitSXBuffer(0, len, 5000, TrackerTXpower, WAIT_TX); + digitalWrite(LED1, LOW); //turn off indicator LED + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //flash LED to show tracker is alive + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void clearAllMemory() +{ + //clears the whole of non-volatile + Serial.println(F("Clear Memory")); + fillMemory(addr_StartMemory, addr_EndMemory, 0); +} + + +float readTempDS18B20() +{ + float DS18B20TXTemperature; + sensor.requestTemperatures(); + DS18B20TXTemperature = sensor.getTempCByIndex(0); + return DS18B20TXTemperature; +} + + +void printTempDS18B20() +{ + float DS18B20TXTemperature; + DS18B20TXTemperature = readTempDS18B20(); + Serial.print(F("Temperature ")); + Serial.print(DS18B20TXTemperature, 1); + Serial.println(F("c")); +} + + +void printSupplyVoltage() +{ + //get and display supply volts on terminal or monitor + Serial.print(F("Volts ")); + Serial.print(readSupplyVoltage()); + Serial.println(F("mV")); +} + + +uint16_t readSupplyVoltage() +{ + //relies on internal 1v1 reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t volts = 0; + uint8_t index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn MOSFET connection resitor divider in circuit + } + + analogReference(INTERNAL); + temp = analogRead(SupplyAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(SupplyAD); + volts = volts + temp; + } + volts = ((volts / 5) * ADMultiplier); + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn MOSFET connection resitor divider in circuit + } + + return volts; +} + + +//*********************************************************** +// Start GPS Functions +//*********************************************************** + +void GPSTest() +{ + uint32_t endmS; + + endmS = millis() + 2000; //run GPS echo for 2000mS + + while (millis() < endmS) + { + while (GPSserial.available() > 0) + Serial.write(GPSserial.read()); + } + Serial.println(); + Serial.println(); + Serial.flush(); +} + + +bool gpsWaitFix(uint16_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for good fix + + uint32_t endwaitmS, millistowait, currentmillis; + uint8_t GPSchar; + + Serial.flush(); + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.print(F("s ")); + Serial.flush(); + + GPS_OutputOn(); + Serial.flush(); + + currentmillis = millis(); + millistowait = waitSecs * 1000; + endwaitmS = currentmillis + millistowait; + + while (GPSserial.read() >= 0); //clear the GPS serial input buffer + + while (millis() < endwaitmS) + { + + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated()) + { + TXLat = gps.location.lat(); + TXLon = gps.location.lng(); + TXAlt = (uint16_t) gps.altitude.meters(); + + //Altitude is used as an unsigned integer, so that the binary payload is as short as possible. + //However gps.altitude.meters(); can return a negative value which converts to + //65535 - Altitude, which we dont want. So we will assume any value over 60,000M is zero + + if (TXAlt > 60000) + { + TXAlt = 0; + } + + TXHours = gps.time.hour(), + TXMinutes = gps.time.minute(), + TXSeconds = gps.time.second(), + TXSatellites = gps.satellites.value(); + + setStatusByte(GPSFix, 1); + + TXGPSfixms = millis() - GPSstartms; + + Serial.print(F("Have GPS Fix ")); + Serial.print(TXGPSfixms); + Serial.print(F("mS")); + Serial.println(); + + return true; + } + + } + + //if here then there has been no fix and a timeout + GPS_OutputOff(); + setStatusByte(GPSFix, 0); //set status bit to flag no fix + incMemoryUint16(addr_TXErrors); + Serial.println(F("Error No GPS Fix")); + return false; +} + +//*********************************************************** +// End GPS Functions +//*********************************************************** + + +void setup() +{ + uint32_t i; + uint16_t j; + + Serial.begin(115200); //Setup Serial console ouput + Serial.println(); + Serial.println(); + Serial.println(F("67_HAB_Balloon_Tracker_Transmitter Starting")); + + memoryStart(Memory_Address); //setup the memory + j = readMemoryUint16(addr_ResetCount); + j++; + writeMemoryUint16(addr_ResetCount, j); + j = readMemoryUint16(addr_ResetCount); + + Serial.print(F("TXResets ")); + Serial.println(j); + + if (GPSPOWER >= 0) //if GPS needs power switching, turn it on + { + pinMode(GPSPOWER, OUTPUT); + digitalWrite(GPSPOWER, GPSONSTATE); + } + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); //for MOSFET controlling battery volts resistor divider + } + + #ifdef QUECTELINUSE + Serial.println(F("Quectel GPS library")); + #endif + + #ifdef UBLOXINUSE + Serial.println(F("UBLOX GPS library")); + #endif + +#ifdef ClearAllMemory + clearAllMemory(); +#endif + + SPI.begin(); //initialize SPI + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("LoRa Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + setTrackerMode(); + + Serial.print(F("Config ")); + Serial.println(Default_config1, BIN); + + j = readMemoryUint16(addr_TXErrors); + Serial.print(F("TXErrors ")); + Serial.println(j); + + Serial.print(F("TXSequence ")); + i = readMemoryUint32(addr_SequenceNum); + Serial.println(i); + + Serial.print(F("ThisNode ")); + Serial.println(ThisNode); + + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + + Serial.println(); + printSupplyVoltage(); + printTempDS18B20(); + Serial.println(); + + //j = readSupplyVoltage(); //get supply mV + TXStatus = 0; //clear all TX status bits + + sendCommand(PowerUp); //send power up command, includes supply mV and config, on tracker settings + + GPS_OutputOn(); + GPSTest(); + GPS_Setup(); //GPS should have had plenty of time to initialise by now + + delay(2000); + + if (GPS_CheckConfiguration()) //Check that GPS is configured for high altitude mode + { + Serial.println(); + GPS_OutputOff(); //GPS interrupts cause problems with lora device, so turn off for now + setStatusByte(GPSError, 0); + setStatusByte(GPSConfigError, 0); + + //Alert user to GPS OK, turn LED on and send a FM tone. + digitalWrite(LED1, HIGH); + Serial.println(F("GPS Config OK")); //check tone indicates navigation model 6 set + Serial.println(); + Serial.flush(); + LT.setupDirect(TrackerFrequency, Offset); //need direct mode for tones + LT.toneFM(1500, 500, deviation, adjustfreq, TrackerTXpower); //Transmit an FM tone, 1000hz, 3000ms + delay(1000); + digitalWrite(LED1, LOW); + } + else + { + setStatusByte(GPSConfigError, 1); + incMemoryUint16(addr_TXErrors); + Serial.println(F("GPS Error")); + Serial.println(); + setTrackerMode(); + sendCommand(NoGPS); //make sure receiver knows about GPS error + led_Flash(100, 25); //long very rapid flash for GPS error + } + + GPSstartms = millis(); + + setTrackerMode(); //so that commands indicating wait for a GPS go out + + while (!gpsWaitFix(5)) //wait for the initial GPS fix, this could take a while + { + sendCommand(NoFix); + led_Flash(2, 50); //two short LED flashes to indicate GPS waiting for fix + } + + LT.setupDirect(TrackerFrequency, Offset); //need direct mode for tones + digitalWrite(LED1, HIGH); + LT.toneFM(500, 2000, deviation, adjustfreq, TrackerTXpower); + digitalWrite(LED1, LOW); + GPS_OutputOn(); + delay(2000); //GPS may be in software backup allow time for it to wakeup + GPS_SetCyclicMode(); //set this regardless of whether hot fix mode is enabled + GPS_OutputOff(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/67_Balloon_Tracker_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/67_Balloon_Tracker_Transmitter/Settings.h new file mode 100644 index 0000000..977427b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/67_Balloon_Tracker_Transmitter/Settings.h @@ -0,0 +1,141 @@ + /******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 28/05/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//************************************************************************************************** +// 1) Hardware related definitions and options - specify lora board type and pins here +//************************************************************************************************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define RFBUSY 7 //busy pin on LoRa device +#define LED1 8 //On board LED, high for on +#define BATVREADON 8 //Pin that turns on the resistor divider to read battery volts +#define ONE_WIRE_BUS 4 //for DS18B20 temperature sensor +#define ADMultiplier 11.75 //adjustment to convert into mV of battery voltage. for 100K\10K divider +#define SupplyAD A0 //Resistor divider for battery connected here + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER -1 //Pin that powers GPS on\off, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define LORA_DEVICE DEVICE_SX1262 //this is the device we are using + +//************************************************************************************************** +// 2) Program Options +//************************************************************************************************** + +//#define ClearAllMemory //Clears memory of stored tracker information, counts, errors etc + +//************************************************************************************************** +// 3) LoRa modem settings +//************************************************************************************************** + +//LoRa Modem Parameters +const uint32_t Offset = 0; //offset frequency for calibration purposes + +//Tracker mode +const uint32_t TrackerFrequency = 434000000; //frequency of transmissions +const uint8_t TrackerBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t TrackerSpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t TrackerCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t TrackerOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t TrackerTXpower = 10; //LoRa TX power in dBm + +//Search mode +const uint32_t SearchFrequency = 434000000; //frequency of transmissionsconst +uint8_t SearchBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SearchSpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t SearchCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t SearchOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t SearchTXpower = 10; //LoRa TX power in dBm + +const uint16_t deviation = 10000; //deviation in hz for FM tones +const float adjustfreq = 0.9; //adjustment to tone frequency + +const byte TXBUFFER_SIZE = 128; //defines the maximum size of the trasnmit buffer; + + +//************************************************************************************************** +// 4) GPS Options +//************************************************************************************************** + +#define GPSBaud 9600 //GPS Baud rate + +#define USESOFTSERIALGPS //need to include this if using softserial for GPS, otherwise hardware serial assumed + +//#define HARDWARESERIALPORT Serial1 //if your using hardware serial for the GPS, define it here + +const uint16_t WaitGPSFixSeconds = 60; //when in flight the time to wait for a new GPS fix + +//#define GPS_Library //use library file for UBLOX GPS +#define GPS_Library //use library file for Quectel GPS + + +//************************************************************************************************** +// 5) FSK RTTY Settings +//************************************************************************************************** + +uint32_t FrequencyShift = 500; //hertz frequency shift, approx, sent at nearest 61.03515625hz step +uint8_t NumberofPips = 4; //number of marker pips to send +uint16_t PipDelaymS = 1000; //mS between pips when carrier is off +uint16_t PipPeriodmS = 100; //mS length of pip +uint16_t BaudPerioduS = 10000; //uS period for baud, 10000uS for 100baud +uint16_t LeadinmS = 1000; //ms of leadin constant shifted carrier + + +//**************************************************************************************************** +// 6) Program Default Option settings - This section determines which options are on or off by default, +// these are saved in the Default_config1 byte. These options are set in this way so that it is +// possible (in future program changes) to alter the options remotly. +//************************************************************************************************** + +uint8_t OptionOff = 0; +uint8_t OptionOn = 1; + +const char option_SearchEnable = OptionOn; //set to OptionOn to enable transmit of Search mode packet +const char option_FSKRTTYEnable = OptionOn; //set to OptionOn to enable transmit of FSKRTTY + +#define option_SearchEnable_SUM (option_SearchEnable*1) +#define option_FSKRTTYEnable_SUM (option_FSKRTTYEnable*4) + +const unsigned int Default_config1 = (option_SearchEnable_SUM + option_FSKRTTYEnable_SUM); +//const unsigned int Default_config1 = 0x05; //Phew, the default config can always be set manually........ + //0x05 would turn on transmit of search mode and FSKRTTY + + +//************************************************************************************************** +// 7) Memory settings - define the type of memory to use for non-Volatile storage. +// Default is internal ATmega device EEPROM but EEPROM has a limited write endurance of 'only' +// 100,000 writes. Since the non-Volatile memory selected is written to at each transmission loop +// and error, its highly recommended to use one of the FRAM options, these have an endurance of +// 100,000,000,000,000 writes. +//************************************************************************************************** + +#define Memory_Library +//#define Memory_Library +//#define Memory_Library + +int16_t Memory_Address = 0x50; //default I2C address of MB85RC16PNF and FM24CL64 FRAM + +//************************************************************************************************** +// 8) HAB Flight Settings +//************************************************************************************************** + +char FlightID[] = "Flight1"; //flight ID for HAB packet + +const unsigned int SleepTimesecs = 13; //sleep time in seconds after each TX loop + +const char ThisNode = '1'; //tracker number for search packet + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/68_Balloon_Tracker_Receiver/68_Balloon_Tracker_Receiver.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/68_Balloon_Tracker_Receiver/68_Balloon_Tracker_Receiver.ino new file mode 100644 index 0000000..510f0b2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/68_Balloon_Tracker_Receiver/68_Balloon_Tracker_Receiver.ino @@ -0,0 +1,1038 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/09/20 + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a LoRa tracker receiver intended to be used with the matching high altitude + balloon (HAB) tracker program '67_Balloon_Tracker_Transmitter'. The program receives a standard format + payload with LoRa that is compatible with the HABHUB online tracking system. + + The HAB payload sent by the tracker transmitter is assumed to be formatted like this; + + PayloadID,Sequence,Time,Lat,Lon,Alt,Satellites,Volts,Temperature,Resets,status,errors,Checksum + Field 0 1 2 3 4 5 6 7 8 9 10 11 12 + + The LoRa and frequency settings can be changed in the Settings.h file. There is the option of the transmitter + sending out a much shorter Search mode binary location only payload. This is intended for ground based searching + and locating. The frequency and LoRa settings of the Search mode packet can be different to the Tracker + mode used by the HAB payload. To switch between standard tracker mode press the switch that is defined in the + Settings.h file. This receiver cannot receive the transmitted FSK RTTY payload. + + There is the option to enable an audio FSK RTTY uplaod into FLDIGI from where it can be sent to the HABHUB + online tracking system. + + The program will drive a SSD1306 or SH1106 OLED display for portable use. + + Not that the distance and direction to the tracker is only displayed when there has been at least one location + fix from the remote tracker and the locally attached GPS has a fix. + + Serial monitor baud rate is set at 9600. + + ToDo: + +*******************************************************************************************************/ + + +#define Program_Version "V1.2" + +#include +#include +SX126XLT LT; + +#include "Settings.h" +#include + +#include //https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //1.3" OLED often sold as 1.3" SSD1306 + +#include //http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + + +#ifdef USESOFTSERIALGPS +//#include //https://github.com/SlashDevin/NeoSWSerial +//NeoSWSerial GPSserial(RXpin, TXpin); //this library is more relaible with a GPS over SoftwareSerial +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#else +#define GPSserial HARDWARESERIALPORT //hardware serial port (eg Serial1) is configured in the Settings.h file +#endif + +#include + +//************************************************************************************************** +// HAB tracker data - these are the variables transmitted in payload +//************************************************************************************************** +uint32_t TXSequence; //sequence number of payload +uint8_t TXHours; //Hours +uint8_t TXMinutes; //Minutes +uint8_t TXSeconds; //Seconds +float TXLat; //latitude from GPS +float TXLon; //longitude from GPS +uint16_t TXAlt; //altitude from GPS +uint8_t TXSatellites; //satellites used by GPS +uint16_t TXVolts; //measured tracker supply volts +int8_t TXTemperature; //measured temperature +uint16_t TXResets; //number of tracker resets +uint8_t TXStatus; //used to store current status flag bits +uint16_t TXErrors; //number of tracker Errors +//************************************************************************************************** + +float RXLat; //latitude of RX +float RXLon; //longitude of RX +float RXAlt; //altitude of RX + +uint32_t RXpacketCount; //count of received packets +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //signal strength (RSSI) dBm of received packet +int8_t PacketSNR; //signal to noise ratio (SNR) dB of received packet +uint16_t RXerrors; //count of packets received with errors +uint8_t PacketType; //for packet addressing, identifies packet type +uint8_t Destination; //for packet addressing, identifies the destination (receiving) node +uint8_t Source; //for packet addressing, identifies the source (transmiting) node +float TXdistance; //calculated distance to tracker +uint16_t TXdirection; //calculated direction to tracker +uint16_t RXVolts; //supply\battery voltage of this receiver +uint32_t LastRXGPSfixCheck; //used to record the time of the last GPS fix +bool TXLocation = false; //set to true when at least one tracker location packet has been received +bool RXGPSfix = false; //set to true if the local GPS has a recent fix + +uint8_t FixCount = DisplayRate; //used to keep track of number of GPS fixes before display updated + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into +char FlightID[16]; //buffer for flight ID +uint8_t FlightIDlen; //length of received flight ID + +uint8_t modeNumber = 1; //mode receiver is in default to 1. (1 = Tracker, 2 = Search) + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, NO_WAIT); + + GPSserial.begin(GPSBaud); //startup GPS input + + while (!digitalRead(DIO1)) + { + readGPS(); //If the DIO pin is low, no packet has arrived, so read the GPS + + if (!digitalRead(SWITCH1)) + { + checkModeSwitch(); + break; + } + } + + if (digitalRead(DIO1)) + { + //something has happened in receiver + GPSserial.end(); //stop GPS input to use SPI reliably + digitalWrite(LED1, HIGH); + + RXPacketL = LT.readRXPacketL(); + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + Serial.println(); + printElapsedTime(); //print elapsed time to Serial Monitor + + if (LT.readIrqStatus() == (IRQ_RX_DONE + IRQ_HEADER_VALID + IRQ_PREAMBLE_DETECTED)) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + + digitalWrite(LED1, LOW); + + Serial.println(); + } +} + + +void readGPS() +{ + + if (GPSserial.available() > 0) + { + gps.encode(GPSserial.read()); + } + + if ( millis() > (LastRXGPSfixCheck + NoRXGPSfixms)) + { + RXGPSfix = false; + LastRXGPSfixCheck = millis(); + if (TXLocation) //only display location screen if we have had an update + { + displayscreen1(); //shows the received location data and packet reception on display + displayscreen3(); //show receive mode on display + displayscreen4(); //put RX and TX GPS fix status on display + } + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated()) + { + RXGPSfix = true; + RXLat = gps.location.lat(); + RXLon = gps.location.lng(); + RXAlt = gps.altitude.meters(); + LastRXGPSfixCheck = millis(); + displayscreen4(); //put RX and TX GPS fix status on display + + if (FixCount == 1) //update screen when FIXcoount counts down from DisplayRate to 1 + { + FixCount = DisplayRate; + if (TXLocation) //only display location screen if we have had an update + { + doDistanceDirectionCalc(); + displayscreen1(); //shows the received location data and packet reception on display + displayscreen3(); //show receive mode on display + displayscreen4(); //put RX and TX GPS fix status on display + displayscreen5(); //put distance and direction on display + printDistanceDirection(); + } + } + FixCount--; + } +} + + +void checkModeSwitch() +{ + digitalWrite(LED1, LOW); + Serial.println(); + Serial.print(F("Listening in ")); + + modeNumber++; + + if (modeNumber == 3) + { + modeNumber = 1; + } + + if (modeNumber == 1) + { + setTrackerMode(); + Serial.println(F("Tracker Mode")); + } + + + if (modeNumber == 2) + { + setSearchMode(); + Serial.println(F("Search Mode")); + } + + displayscreen3(); //show receive mode on display + + LT.printModemSettings(); + Serial.println(); + Serial.println(); + delay(1500); //do a bit of switch de-bounce +} + + +bool readTXStatus(uint8_t bitnum) +{ + return bitRead(TXStatus, bitnum); +} + + +void readPacketAddressing() +{ + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + LT.endReadSXBuffer(); +} + + +void packet_is_OK() +{ + uint16_t includedCRC, actualCRC; + + RXpacketCount++; + + readPacketAddressing(); + + if (PacketType == PowerUp) + { + read_Command(); + Serial.print(F("TrackerPowerup,Battery,")); + Serial.print(TXVolts); + Serial.print(F("mV")); + displayscreen2(); + displayscreen3(); //show receive mode on display + displayscreen4(); //put RX and TX GPS fix status on display + displayscreen7(); //display received packet count + return; + } + + if (PacketType == HABPacket) + { + includedCRC = calcIncludedCRC(); + actualCRC = LT.CRCCCITTSX(1, RXPacketL - 6, 0xFFFF); + + if (actualCRC != includedCRC) + { + Serial.print(F("PayloadCRCError")); + Serial.print(F(",includedCRC,")); + Serial.print(includedCRC, HEX); + Serial.print(F(",actualCRC,")); + Serial.print(actualCRC, HEX); + Serial.print(F(",")); + LT.printSXBufferASCII(0, (RXPacketL - 1)); + printpacketDetails(); + displayscreen7(); //display received packet count + return; + } + + extractHABdata(0); + + TXLocation = true; + LT.printSXBufferASCII(0, (RXPacketL - 1)); + + TXdistance = 0; + TXdirection = 0; + + if (RXGPSfix) + { + doDistanceDirectionCalc(); + } + + printpacketDetails(); + Serial.println(); + + displayscreen1(); //shows the received location data and packet reception on display + displayscreen3(); //show receive mode on display + displayscreen4(); //put RX and TX GPS fix status on display + displayscreen5(); //put distance and direction on display + printDistanceDirection(); + + //Serial.println(); + //printHABdata(); + //Serial.println(); + +#ifdef UPLOADHABPACKET + if (actualCRC == includedCRC) + { + Serial.println(); + uploadHABpacket(); + } +#endif + + return; + } + + if ((PacketType == LocationBinaryPacket) && (Destination == '*') && (RXPacketL == 14)) + { + //packet from tracker transmitter has been received, now read from the SX12XX FIFO in the correct order. + TXLocation = true; + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readInt16(); + TXStatus = LT.readUint8(); + RXPacketL = LT.endReadSXBuffer(); + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt); + Serial.print(F("m,")); + Serial.println(TXStatus); + + TXdistance = 0; + TXdirection = 0; + + if (RXGPSfix) + { + doDistanceDirectionCalc(); + } + + displayscreen1(); //shows the received location data and packet reception on display + displayscreen5(); //put distance and direction on display + displayscreen7(); //display received packet count + printDistanceDirection(); + return; + } + + if (PacketType == NoFix) + { + read_Command(); + Serial.write(Source); + Serial.print(F(",NoTrackerGPSFix")); + Serial.write(7); //send a BELL to serial terminal + delay(250); + Serial.write(7); + displayscreen6(); //send a note about no tracker GPS fix to screen + displayscreen7(); //display received packet count + return; + } + + if (PacketType == NoGPS) + { + Serial.write(Source); + read_Command(); + Serial.print(F(",GPSError")); + return; + } + + Serial.print(F("PacketNotRecognised")); + printpacketDetails(); + printmorepacketDetails(); +} + + +void uploadHABpacket() +{ + uint8_t index; + uint8_t chartosend; + + Serial.print(F("Dl-Fldigi Upload $")); + Serial.flush(); + + startAFSKRTTY(AUDIOOUT, tonehighHz, leadinmS); + sendAFSKRTTY(13, AUDIOOUT, CHECK, tonelowHz, tonehighHz, AFSKRTTYperiod); + sendAFSKRTTY('$', AUDIOOUT, CHECK, tonelowHz, tonehighHz, AFSKRTTYperiod); + + for (index = 0; index <= (RXPacketL - 1); index++) + { + chartosend = LT.getByteSXBuffer(index); + sendAFSKRTTY(chartosend, AUDIOOUT, CHECK, tonelowHz, tonehighHz, AFSKRTTYperiod); + Serial.write(chartosend); + Serial.flush(); + } + + sendAFSKRTTY(13, AUDIOOUT, CHECK, tonelowHz, tonehighHz, AFSKRTTYperiod); + sendAFSKRTTY(10, AUDIOOUT, CHECK, tonelowHz, tonehighHz, AFSKRTTYperiod); + Serial.println(); + endAFSKRTTY(AUDIOOUT); +} + + +uint16_t calcIncludedCRC() +{ + uint8_t high, midhigh, midlow, low; + uint16_t crc; + high = LT.getByteSXBuffer(RXPacketL - 4); + midhigh = LT.getByteSXBuffer(RXPacketL - 3); + midlow = LT.getByteSXBuffer(RXPacketL - 2); + low = LT.getByteSXBuffer(RXPacketL - 1); + + high = convertASCIIbyte(high); + midhigh = convertASCIIbyte(midhigh); + midlow = convertASCIIbyte(midlow); + low = convertASCIIbyte(low); + + crc = (high * 4096) + (midhigh * 256) + (midlow * 16) + low; + return (uint16_t) crc; +} + + +uint8_t convertASCIIbyte(uint8_t val) +{ + if (val > 0x40) + { + val = (val - 0x41) + 10; + return val; + } + else + { + val = (val - 0x30); + return val; + } +} + + +void read_Command() +{ + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXVolts = LT.readUint16(); //read tracker transmitter voltage + LT.endReadSXBuffer(); +} + + +void printDistanceDirection() +{ + if (RXGPSfix && TXLocation) //only display distance and direction if have received tracker packet and have local GPS fix + { + Serial.print(F("Distance,")); + Serial.print(TXdistance, 0); + Serial.print(F("m,Direction,")); + Serial.print(TXdirection); + Serial.println(F("d")); + } +} + + +void doDistanceDirectionCalc() +{ + TXdirection = (int16_t) TinyGPSPlus::courseTo(RXLat, RXLon, TXLat, TXLon); + TXdistance = TinyGPSPlus::distanceBetween(RXLat, RXLon, TXLat, TXLon); +} + + +void printpacketDetails() +{ + int32_t hertzerror; + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,FreqErr,")); + hertzerror = LT.getFrequencyErrorHz(); + Serial.print(hertzerror); + Serial.print(F("hz,PacketErrs,")); + Serial.print(RXerrors); + Serial.print(F(",PacketsOK,")); + Serial.print(RXpacketCount); +} + + +void printmorepacketDetails() +{ + uint16_t IRQStatus; + Serial.print(F(",Length,")); + Serial.print(RXPacketL); + IRQStatus = LT.readIrqStatus(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + RXerrors++; + Serial.print(F(",PacketError,RSSI")); + + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void extractHABdata(uint8_t startaddr) +{ + //extracts data from received HAB packets where first fields are lat,lon,alt + //all varialbles are extracted, not all are used. + + uint8_t ptr = startaddr; //pointer to current location in SXbuffer + uint8_t buffData; + + //Skip leading $ + do + { + buffData = LT.getByteSXBuffer(ptr++); + } + while ( buffData == '$'); + + ptr--; //so ptr is at location of first non $ + + FlightIDlen = extractBuffer(FlightID, sizeof(FlightID), ptr); //extract flight ID + + ptr = nextComma(ptr); //step to next comma in SX buffer + TXSequence = extractUint(ptr); //extract sequence + + ptr = nextComma(ptr); + TXHours = (uint32_t) extractUint(ptr); + + ptr = nextComma(ptr); + TXMinutes = (uint32_t) extractUint(ptr); + + ptr = nextComma(ptr); + TXSeconds = (uint32_t) extractUint(ptr); + + //ptr = nextComma(ptr); + ptr = nextComma(ptr); + TXLat = extractFloat(ptr); + + ptr = nextComma(ptr); + TXLon = extractFloat(ptr); + + ptr = nextComma(ptr); + TXAlt = extractUint(ptr); + + ptr = nextComma(ptr); + TXSatellites = extractUint(ptr); + + ptr = nextComma(ptr); + TXVolts = extractUint(ptr); + + ptr = nextComma(ptr); + TXTemperature = extractUint(ptr); + + ptr = nextComma(ptr); + TXResets = extractUint(ptr); + + ptr = nextComma(ptr); + TXStatus = extractUint(ptr); + + ptr = nextComma(ptr); + TXErrors = extractUint(ptr); +} + + +void printHABdata() +{ + printBuffer(FlightID, FlightIDlen); + Serial.print(F(",")); + Serial.print(TXSequence); + Serial.print(F(",")); + + Serial.print(TXHours); + Serial.print(F(",")); + Serial.print(TXMinutes); + Serial.print(F(",")); + Serial.print(TXSeconds); + Serial.print(F(",")); + + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt); + Serial.print(F(",")); + Serial.print(TXSatellites); + Serial.print(F(",")); + Serial.print(TXVolts); + Serial.print(F(",")); + Serial.print(TXTemperature); + Serial.print(F(",")); + Serial.print(TXResets); + Serial.print(F(",")); + Serial.print(TXStatus); + Serial.print(F(",")); + Serial.print(TXErrors); +} + + +uint8_t extractBuffer(char *mybuffer, size_t bufferSize, uint8_t startptr) +{ + //extracts a character buffer in ASCII format from lora device RX buffer, returns the length of the buffer to , char + uint16_t index; + + memset(mybuffer, 0, bufferSize); //clear array to 0s + + bufferSize--; //last index location is one less than buffer size + + for (index = 0; index <= bufferSize; index++) + { + mybuffer[index] = LT.getByteSXBuffer(startptr++); + if ((mybuffer[index] == ',') || (mybuffer[index] == '*')) + { + break; + } + } + mybuffer[index] = 0; //it was a , so clear it + return index++; //buffer length is one more than index location +} + + +void printBuffer(char *buff, uint8_t len) +{ + //send buffer to serial terminal + uint8_t index; + + for (index = 0; index < len; index++) + { + Serial.write(buff[index]); + } + +} + + +uint8_t nextComma(uint8_t localpointer) +{ + //skips through HAB packet (in SX device buffer) to next comma + uint8_t bufferdata; + do + { + bufferdata = LT.getByteSXBuffer(localpointer++); + } + while ((bufferdata != ',') && (bufferdata != ':') && (localpointer < RXPacketL)); + return localpointer; //note returns start of next field +} + + +uint8_t nextColon(uint8_t localpointer) +{ + //skips through HAB packet (in SX device buffer) to next colon + uint8_t bufferdata; + do + { + bufferdata = LT.getByteSXBuffer(localpointer++); + } + while ((bufferdata != ':') && (localpointer < RXPacketL)); + return localpointer; +} + + +int32_t extractUint(uint16_t localpointer) +{ + //extracts an unsigned int in ASCII format from buffer + + char temp[16]; + uint8_t tempptr = 0; + uint8_t buffdata; + uint32_t tempint; + do + { + buffdata = LT.getByteSXBuffer(localpointer++);; + temp[tempptr++] = buffdata; + } + while ((buffdata != ',') && (buffdata != '*') && (localpointer < 256) ); + temp[tempptr] = 0; //terminator for string + tempint = (int32_t) atof(temp); + return tempint; +} + + +float extractFloat(uint16_t localpointer) +{ + //extracts a float in ASCII format from buffer + char temp[16]; + uint8_t tempptr = 0; + uint8_t buffdata; + float tempfloat; + do + { + buffdata = LT.getByteSXBuffer(localpointer++);; + temp[tempptr++] = buffdata; + } + while ((buffdata != ',') && (buffdata != '*') && (localpointer < 256) ); + temp[tempptr] = 0; //terminator for string + tempfloat = (float)atof(temp); + return tempfloat; +} + + +void setTrackerMode() +{ + LT.setupLoRa(TrackerFrequency, Offset, TrackerSpreadingFactor, TrackerBandwidth, TrackerCodeRate, TrackerOptimisation); +} + + +void setSearchMode() +{ + LT.setupLoRa(SearchFrequency, Offset, SearchSpreadingFactor, SearchBandwidth, SearchCodeRate, SearchOptimisation); +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F(",")); +} + + +void GPSPowerOn(int8_t pin, uint8_t state) +{ + if (pin >= 0) + { + digitalWrite(pin, state); + } +} + +//************************************************************************ +// Display screen functions +//************************************************************************ + +void displayscreen1() +{ + //shows the received location data and packet reception on display + uint8_t index; + + disp.clearLine(0); + disp.setCursor(0, 0); + + if (PacketType == HABPacket) + { + for (index = 0; index < FlightIDlen; index++) + { + disp.write(FlightID[index]); + } + } + + if (PacketType == LocationBinaryPacket) + { + disp.print(Source); + } + + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(F("Lat ")); + disp.print(TXLat, 5); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(F("Lon ")); + disp.print(TXLon, 5); + disp.clearLine(3); + disp.setCursor(0, 3); + disp.print(F("Alt ")); + disp.print(TXAlt); + disp.print(F("m")); + + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(F("RSSI ")); + disp.print(PacketRSSI); + disp.print(F("dBm")); + disp.clearLine(5); + disp.setCursor(0, 5); + disp.print(F("SNR ")); + + if (PacketSNR > 0) + { + disp.print(F("+")); + } + + if (PacketSNR == 0) + { + disp.print(F(" ")); + } + + if (PacketSNR < 0) + { + disp.print(F("-")); + } + + disp.print(PacketSNR); + disp.print(F("dB")); + + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("Packets ")); + disp.print(RXpacketCount); +} + + +void displayscreen2() +{ + //show tracker transmitter powerup data on display + float tempfloat; + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("TXPowerup")); + disp.setCursor(0, 1); + disp.print(F("Battery,")); + tempfloat = ((float) TXVolts / 1000); + disp.print(tempfloat, 2); + disp.print(F("v")); +} + + +void displayscreen3() +{ + //show receive mode on display + disp.setCursor(14, 0); + + if (modeNumber == TrackerMode) + { + disp.print(F("TR")); + return; + } + + if (modeNumber == SearchMode) + { + disp.print(F("SE")); + return; + } + + disp.print(modeNumber); +} + + +void displayscreen4() +{ + //put RX and TX GPS fix status on display + + disp.setCursor(14, 1); + + if (RXGPSfix) + { + disp.print(F("RG")); + } + else + { + disp.setCursor(14, 1); + disp.print(F("R?")); + } + + disp.setCursor(14, 2); + + if (readTXStatus(GPSFix)) + { + disp.print(F("TG")); + } + else + { + disp.print(F("T?")); + } +} + + +void displayscreen5() +{ + //put distance and direction on display + if (RXGPSfix && TXLocation) //only display distance and direction if have received tracker packet and have local GPS fix + { + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("D&D ")); + disp.print(TXdistance, 0); + disp.print(F("m ")); + disp.print(TXdirection); + disp.print(F("d")); + } +} + + +void displayscreen6() +{ + //no GPS fix + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("No GPS Fix")); +} + + +void displayscreen7() +{ + //display received packet count + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("Packets ")); + disp.print(RXpacketCount); +} + + +void setup() +{ + uint32_t endmS; + + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + pinMode(SWITCH1, INPUT_PULLUP); //setup pin as switch input + + if (CHECK <= 0) + { + pinMode(CHECK, OUTPUT); + } + + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + } + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("68_Balloon_Tracker_Receiver Starting")); + + SPI.begin(); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + + Serial.print(F("Checking LoRa device - ")); + disp.setCursor(0, 0); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) //Initialize LoRa device + { + Serial.println(F("Receiver ready")); + disp.print(F("Ready")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No LoRa device responding")); + disp.print(F("No LoRa device")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + Serial.println(); + Serial.println(F("Startup GPS check")); + + endmS = millis() + 2000; + + //now startup GPS + GPSPowerOn(GPSPOWER, GPSONSTATE); + + GPSserial.begin(GPSBaud); + + while (millis() < endmS) + { + while (GPSserial.available() > 0) + Serial.write(GPSserial.read()); + } + Serial.println(); + Serial.println(F("Done")); + Serial.println(); + Serial.flush(); + + GPSserial.end(); //software serial interferes with SPI for LoRa device + + setTrackerMode(); + displayscreen3(); //show receive mode on display + displayscreen4(); //put RX and TX GPS fix status on display + + LT.printModemSettings(); + Serial.println(); + Serial.println(F("Listening in Tracker mode")); + Serial.println(); + Serial.write(7); //send a BELL to serial terminal + TXStatus = 4; //set default flag of no TX GPS fix +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/68_Balloon_Tracker_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/68_Balloon_Tracker_Receiver/Settings.h new file mode 100644 index 0000000..7ab3fda --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/68_Balloon_Tracker_Receiver/Settings.h @@ -0,0 +1,97 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 12/05/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//************************************************************************************************** +// 1) Hardware related definitions and options - specify lora board type and pins here +//************************************************************************************************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define RFBUSY 7 //busy pin on LoRa device +#define LED1 8 //On board LED, high for on +#define SWITCH1 2 //if pin shorted to ground, switch is active + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER -1 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define AUDIOOUT 4 //pin used to output Audio tones for HAB packet upload +#define CHECK -1 //this pin is toggled inside the AFSKRTTY library, high for logic 1, low for logic 0, so it can be used to check the timing. + +#define LORA_DEVICE DEVICE_SX1262 //this is the LoRa device we are using + +//************************************************************************************************** +// 2) Program Options +//************************************************************************************************** + + + +//************************************************************************************************** +// 3) LoRa modem settings +//************************************************************************************************** + +const uint32_t Offset = 0; //offset frequency for calibration purposes + +//Tracker mode +const uint32_t TrackerFrequency = 434000000; //frequency of transmissions +const uint8_t TrackerBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t TrackerSpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t TrackerCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t TrackerOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t TrackerTXpower = 10; //LoRa TX power in dBm +const uint8_t TrackerMode = 1; //used for receiver to tell whatmode it is in + +//Search mode +const uint32_t SearchFrequency = 434000000; //frequency of transmissions +const uint8_t SearchBandwidth = LORA_BW_062;; //LoRa bandwidth +const uint8_t SearchSpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t SearchCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t SearchOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t SearchTXpower = 10; //LoRa TX power in dBm +const uint8_t SearchMode = 2; //used for receiver to tell whatmode it is in + +const uint8_t RXBUFFER_SIZE = 128; //RX buffer size + +//************************************************************************************************** +// 4) GPS Options +//************************************************************************************************** + +const uint16_t GPSBaud = 9600; //GPS Baud rate + +#define USESOFTSERIALGPS //need to include this if we are using softserial for GPS +//#define HARDWARESERIALPORT Serial1 //if using hardware serial enable this define for hardware serial port + +const uint16_t WaitGPSFixSeconds = 30; //time to wait for a new GPS fix + +const uint16_t NoRXGPSfixms = 15000; //max number of mS to allow before no local fix flagged +const uint8_t DisplayRate = 7; //when working OK the GPS will get a new fix every second or so + //this rate defines how often the display should be updated + + +//************************************************************************************************** +// 6) AFSK RTTY Settings - For PC upload into Dl-Fldigi in HAB mode. +// Sent at 300baud, 7 bit, no parity, 2 stop bits. +// Shift 500hz, low tone 800hz, high tone 1300hz. +//************************************************************************************************** + +//#define UPLOADHABPACKET //comment in define to output HAB packet as AFSKRTTY for PC upload + +const uint16_t AFSKRTTYperiod = 3333; //period in uS for 1 bit at chosen baud rate, e.g. 10000 for 100baud, 3333 for 300baud +const uint16_t leadinmS = 500; //number of ms for AFSK constant lead in tone +const uint16_t tonehighHz = 1300; //high tone in Hertz +const uint16_t tonelowHz = 800; //low tone in Hertz + + + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/71_FSKRTTY_Transmitter_Test/71_FSKRTTY_Transmitter_Test.ino b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/71_FSKRTTY_Transmitter_Test/71_FSKRTTY_Transmitter_Test.ino new file mode 100644 index 0000000..0c6d337 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/71_FSKRTTY_Transmitter_Test/71_FSKRTTY_Transmitter_Test.ino @@ -0,0 +1,194 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/09/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a test program for using the LoRa device to transmit upper side band FSK + RTTY. With the LoRa device in FSK direct mode, the frequency of the generated carrier is shifted up + or down at the correct baud rate depending on whether a logic 0 or 1 is being sent. + + The desired shift in frequency is defined in the Settings.h file as 'FrequencyShift'. When the program + starts the actual frequency shift will be calculated according to the discrete frequency steps the + LoRa device can be set to. There are settings for number of data bits, number of start bits and the + value of parity which can be ParityNone, ParityOdd, ParityEven, ParityZero or ParityOne. + + Before the actual data transmission starts you can send a series of marker pips which are short bursts + of up shifted carrier which will be heard as beeps in a correctly tuned receiver. These pips can aid + in setting the receiver decode frequemcy to match the transmission. on some LoRa devices, such as the SX127x + series there can be considerable temperature induced frequency drift. This drift can be caused by outside + temperature changes or the RF device self heating when transmit is turned on. The duration of the pips, + the gaps between them and the period of leadin carrier before the data starts can all be set. To send no + pips just set the number to 0. + + The FSK RTTY routines use the micros() function for timing, and a check is made at the begging of a + character to send to see if micros() migh overflow during the transmission of the character. This check + assumes the lowest baud rate of 45baud, and if an overflow is likley, there will be a short in transmission + pause to allow the overflow to occur. + + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based +#include //include the appropriate SX12XX library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX126XLT LT; //create a library class instance called LT + +//Choose whichever test pattern takes your fancy +//uint8_t testBuffer[] = "0123456789* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"; //This string is sent as AFSK RTTY, 7 bit, 2 Stop bit, no parity, 300 baud. + +//uint8_t testBuffer[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789"; +//uint8_t testBuffer[] = "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"; +uint8_t testBuffer[] = "$$$$MyFlight1,2213,14:54:37,51.48230,-3.18136,15,6,3680,23,66,3,0*2935"; + +uint8_t freqShiftRegs[4]; //to hold returned registers that set frequency + + +void loop() +{ + uint8_t index; + + printRegisterSetup(FrequencyShift); + Serial.println(); + + LT.startFSKRTTY(FrequencyShift, NumberofPips, PipPeriodmS, PipDelaymS, LeadinmS); + + if (micros() > 0xF8000000) + { + Serial.print(F("Waiting micros()")); + while(micros() < 0xFFFB6000); + } + + Serial.print(F("Start RTTY micros() = ")); + Serial.println(micros(),HEX); + LT.transmitFSKRTTY(13, DataBits, StopBits, Parity, BaudPerioduS, LED1); //send carriage return + LT.transmitFSKRTTY(10, DataBits, StopBits, Parity, BaudPerioduS, LED1); //send line feed + for (index = 0; index < (sizeof(testBuffer)-1); index++) + { + LT.transmitFSKRTTY(testBuffer[index], DataBits, StopBits, Parity, BaudPerioduS, LED1); + Serial.write(testBuffer[index]); + } + + LT.transmitFSKRTTY(13, DataBits, StopBits, Parity, BaudPerioduS, LED1); //send carriage return + LT.transmitFSKRTTY(10, DataBits, StopBits, Parity, BaudPerioduS, LED1); //send line feed + + Serial.println(); + Serial.print(F("END RTTY micros() = ")); + Serial.println(micros(),HEX); + digitalWrite(LED1, LOW); + Serial.println(); + Serial.println(); + + Serial.println(micros(),HEX); + + LT.setMode(MODE_STDBY_RC); + + delay(2000); +} + + +void printRegisterSetup(uint32_t shift) +{ + + uint32_t nonShiftedFreq, ShiftedFreq; + uint32_t freqShift; + float exactfreqShift; + + LT.setRfFrequency(Frequency, Offset); //ensure base frequecy is set + LT.getRfFrequencyRegisters(freqShiftRegs); //fill buffer with frequency setting registers values + nonShiftedFreq = ( (uint32_t) freqShiftRegs[0] << 24 ) + ( (uint32_t) freqShiftRegs[1] << 16 ) + ( (uint32_t) freqShiftRegs[2] << 8 ) + freqShiftRegs[3]; + Serial.print(F("NoShift Registers 0x")); + Serial.println(nonShiftedFreq, HEX); + + LT.setRfFrequency((Frequency + shift), Offset); //set shifted frequecy + LT.getRfFrequencyRegisters(freqShiftRegs); //fill buffer with frequency setting registers values + ShiftedFreq = ( (uint32_t) freqShiftRegs[0] << 24 ) + ( (uint32_t) freqShiftRegs[1] << 16 ) + ( (uint32_t) freqShiftRegs[2] << 8 ) + freqShiftRegs[3]; + Serial.print(F("Shifted Registers 0x")); + Serial.println(ShiftedFreq, HEX); + + freqShift = ShiftedFreq - nonShiftedFreq; + exactfreqShift = freqShift * FREQ_STEP; + Serial.print(F("FSKRTTY register shift ")); + Serial.println(freqShift,HEX); + Serial.print(F("FSKRTTY frequency shift ")); + Serial.print(exactfreqShift, 8); + Serial.println(F("hZ")); + + LT.setRfFrequency(Frequency, Offset); //ensure base frequecy is set +} + + +void printRegisterBuffer() +{ +Serial.print(freqShiftRegs[0],HEX); +Serial.print(F(" ")); +Serial.print(freqShiftRegs[1],HEX); +Serial.print(F(" ")); +Serial.print(freqShiftRegs[2],HEX); +Serial.println(); +Serial.print(freqShiftRegs[2],HEX); +Serial.println(); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("71_FSKRTTY_Transmitter_Test")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupDirect(Frequency, Offset); + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/71_FSKRTTY_Transmitter_Test/Settings.h b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/71_FSKRTTY_Transmitter_Test/Settings.h new file mode 100644 index 0000000..5f537ce --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX126x_examples/Tracker/71_FSKRTTY_Transmitter_Test/Settings.h @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2 are not used by this particular sketch so they are set to -1 and not connected. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 4 //on board LED, high for on +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define RFBUSY 7 //busy pin on LoRa device + +#define LORA_DEVICE DEVICE_SX1262 //we need to define the device we are using + + +//******* Setup Direct Modem Parameters Here ! *************** + +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes +const uint16_t deviation = 10000; //deviation, total frequency shift low to high +const float adjustfreq = 0.9; //adjustment to tone frequency + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +//******* Setup FSKRTTY Settings here ! *************** + +uint32_t FrequencyShift = 500; //hertz frequency shift, approx, sent at nearest 0.95367431640625hz step +uint8_t NumberofPips = 2; //number of marker pips to send +uint16_t PipDelaymS = 500; //mS between pips, carrier off +uint16_t PipPeriodmS = 100; //mS length of pip +uint16_t BaudPerioduS = 10000; //uS period for baud, 10000uS for 100baud +uint16_t LeadinmS = 5000; //ms of leadin, shifted carrier +uint8_t DataBits = 7; //number of databits, normally 7 or 8 +uint8_t StopBits = 2; //number of stopbits, normally 1 or 2 +uint8_t Parity = ParityNone; //parity on data bits, ParityNone, ParityOdd, ParityEven, ParityZero, ParityOne + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/103_Lora_Transmitter_Detailed_Setup/103_Lora_Transmitter_Detailed_Setup.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/103_Lora_Transmitter_Detailed_Setup/103_Lora_Transmitter_Detailed_Setup.ino new file mode 100644 index 0000000..5332fbb --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/103_Lora_Transmitter_Detailed_Setup/103_Lora_Transmitter_Detailed_Setup.ino @@ -0,0 +1,183 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a program that demonstrates the detailed setup of a LoRa test transmitter. + A packet containing ASCII text is sent according to the frequency and LoRa settings specified in the + 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with + the transmit power used, the packet length and the CRC of the packet. The matching receive program, + '104_LoRa_Receiver' can be used to check the packets are being sent correctly, the frequency and LoRa + settings (in Settings.h) must be the same for the transmitter and receiver programs. Sample Serial + Monitor output; + + 10dBm Packet> Hello World 1234567890* BytesSent,23 CRC,DAAB TransmitTime,64mS PacketsSent,2 + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint8_t TXPacketL; +uint32_t TXPacketCount, startmS, endmS; + +uint8_t buff[] = "Hello World 1234567890"; + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on reciver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + startmS = millis(); //start transmit timer + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + endmS = millis(); //packet sent, note end time + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + uint16_t localCRC; + + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + localCRC = LT.CRCCCITT(buff, TXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); //print CRC of transmitted packet + Serial.print(F(" TransmitTime,")); + Serial.print(endmS - startmS); //print transmit time of packet + Serial.print(F("mS")); + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("103_LoRa_Transmitter_Detailed_Setup Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); //got to standby mode to configure device + LT.setPacketType(PACKET_TYPE_LORA); //set for LoRa transmissions + LT.setRfFrequency(Frequency, Offset); //set the operating frequency + LT.calibrateImage(0); //run calibration after setting frequency + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); //set LoRa modem parameters + LT.setBufferBaseAddress(0x00, 0x00); //where in the SX buffer packets start, TX and RX + LT.setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); //set packet parameters + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword, LORA_MAC_PRIVATE_SYNCWORD = 0x12, or LORA_MAC_PUBLIC_SYNCWORD = 0x34 + LT.setHighSensitivity(); //set for highest sensitivity at expense of slightly higher LNA current + LT.setDioIrqParams(IRQ_RADIO_ALL, IRQ_TX_DONE, 0, 0); //set for IRQ on RX done + //*************************************************************************************************** + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/103_Lora_Transmitter_Detailed_Setup/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/103_Lora_Transmitter_Detailed_Setup/Settings.h new file mode 100644 index 0000000..5b38e9b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/103_Lora_Transmitter_Detailed_Setup/Settings.h @@ -0,0 +1,39 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER are not used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/104_Lora_Receiver_Detailed_Setup/104_Lora_Receiver_Detailed_Setup.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/104_Lora_Receiver_Detailed_Setup/104_Lora_Receiver_Detailed_Setup.ino new file mode 100644 index 0000000..af97e74 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/104_Lora_Receiver_Detailed_Setup/104_Lora_Receiver_Detailed_Setup.ino @@ -0,0 +1,250 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a program that demonstrates the detailed setup of a LoRa test receiver. + The program listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins + to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout on the Arduino IDE Serial Monitor of the valid packets received, the packet is + assumed to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect + weird things to happen on the Serial Monitor. The LED will flash for each packet received and the + buzzer will sound, if fitted. + + Sample serial monitor output; + + 7s Hello World 1234567890*,CRC,DAAB,RSSI,-52dBm,SNR,9dB,Length,23,Packets,5,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 968s PacketError,RSSI,-87dBm,SNR,-11dB,Length,23,Packets,613,Errors,2,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + delay(10); //so we can see LED flash more easily on fast processors + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the device packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } + + delay(250); //gives a longer buzzer and LED flash for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("104_Lora_Receiver_Detailed_Setup Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in the library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("Lora Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup Lora device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + //*************************************************************************************************** + //Setup Lora device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); //got to standby mode to configure device + LT.setPacketType(PACKET_TYPE_LORA); //set for LoRa transmissions + LT.setRfFrequency(Frequency, Offset); //set the operating frequency + LT.calibrateImage(0); //run calibration after setting frequency + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); //set LoRa modem parameters + LT.setBufferBaseAddress(0x00, 0x00); //where in the SX buffer packets start, TX and RX + LT.setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); //set packet parameters + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword, LORA_MAC_PRIVATE_SYNCWORD = 0x12, or LORA_MAC_PUBLIC_SYNCWORD = 0x34 + LT.setHighSensitivity(); //set for highest sensitivity at expense of slightly higher LNA current + LT.setDioIrqParams(IRQ_RADIO_ALL, IRQ_RX_DONE, 0, 0); //set for IRQ on RX done + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/104_Lora_Receiver_Detailed_Setup/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/104_Lora_Receiver_Detailed_Setup/Settings.h new file mode 100644 index 0000000..60f933f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/104_Lora_Receiver_Detailed_Setup/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define BUZZER 4 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 2; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino new file mode 100644 index 0000000..845f658 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino @@ -0,0 +1,145 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program demonstrates the transmitting of a structure as a LoRa packet. The + contents of the structure are the same as in the '8_LoRa_LowMemory_TX' program. The packet sent is + typical of what might be sent from a GPS tracker. + + The structure type is defined as trackerPacket and an instance called location1 is created. The struture + which includes a character array (text) is filled with values and transmitted. + + The matching receiving program '15_LoRa_RX_Structure' can be used to receive and display the packet, + though the program '9_LoRa_LowMemory_RX' should receive it as well, since the contents are the same. + + Note that the structure definition and variable order (including the buffer size) used in the transmitter + need to match those used in the receiver. + + The contents of the packet transmitted should be; + + "tracker1" (buffer) - trackerID + 1+ (uint32_t) - packet count + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Good luck. + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX127XLT LT; + +uint32_t TXpacketCount = 1; +uint32_t startmS, endmS; + +struct trackerPacket +{ + uint8_t trackerID[13]; + uint32_t txcount; + float latitude; + float longitude; + uint16_t altitude; + uint8_t satellites; + uint16_t voltage; + int8_t temperature; +}; + +struct trackerPacket location1; //define an instance called location1 of the structure trackerPacket + + +void loop() +{ + + //fill the defined structure with values + uint8_t buff[] = "tracker1"; //create the contents to be of location1.trackerID + memcpy (&location1.trackerID, &buff, sizeof(buff)); //copy the contents of buff[] into the structure + location1.txcount = TXpacketCount; + location1.latitude = 51.23456; + location1.longitude = -3.12345; + location1.altitude = 199; + location1.satellites = 8; + location1.voltage = 3999; + location1.temperature = -9; + + digitalWrite(LED1, HIGH); + startmS = millis(); + + if (LT.transmit((uint8_t *) &location1, sizeof(location1), 0, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 + { + endmS = millis(); + digitalWrite(LED1, LOW); + TXpacketCount++; + Serial.print(TXpacketCount); + Serial.print(F(" ")); + Serial.print(sizeof(location1)); + Serial.print(F(" Bytes Sent")); + Serial.print(F(" ")); + Serial.print(endmS - startmS); + Serial.print(F("mS")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/14_LoRa_Structure_TX/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/14_LoRa_Structure_TX/Settings.h new file mode 100644 index 0000000..5387024 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/14_LoRa_Structure_TX/Settings.h @@ -0,0 +1,39 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 8 //On board LED, high for on + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa TX power + +#define packet_delay 1000 //mS delay between packets diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino new file mode 100644 index 0000000..393b041 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino @@ -0,0 +1,194 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 17/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program demonstrates the receiving of a structure as a LoRa packet. The packet + sent is typical of what might be sent from a GPS tracker. + + The structure type is defined as trackerPacket and an instance called location1 is created. The structure + includes a character array (text). + + The matching receiving program is '15_LoRa_RX_Structure' can be used to receive and display the packet, + though the program '9_LoRa_LowMemory_RX' should receive it as well, since the packet contents are the same. + + Not that the structure definition and variable order (including the buffer size) used in the transmitter + need to match those used in the receiver. Good luck. + + The contents of the packet received, and printed to serial monitor, should be; + + "tracker1" (buffer) - trackerID + 1+ (uint32_t) - packet count + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX127XLT LT; + +uint8_t RXPacketL; //stores length of packet received +uint32_t RXpacketCount; //count of received packets +int16_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet +uint32_t errors; //count of packet errors + + +struct trackerPacket +{ + uint8_t trackerID[13]; + uint32_t txcount; + float latitude; + float longitude; + uint16_t altitude; + uint8_t satellites; + uint16_t voltage; + int8_t temperature; +}; + +struct trackerPacket location1; //define an instance called location1 of the structure trackerPacket + + +void loop() +{ + RXPacketL = LT.receive( (uint8_t *) &location1, sizeof(location1), 0, WAIT_RX); //wait for a packet to arrive with no timeout + + digitalWrite(LED1, HIGH); //something has happened, what I wonder ? + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +void printlocation1() +{ + uint8_t buff[13]; //define a buffer to receive a copy from the structure + memcpy (&buff, &location1.trackerID, sizeof(buff)); //copy the contents of buffer in struture to buff[] + + //now print the contents of the structure + Serial.print((char*) buff); //cast to a char type for printing + Serial.print(F(",")); + Serial.print(location1.txcount); + Serial.print(F(",")); + Serial.print(location1.latitude, 5); + Serial.print(F(",")); + Serial.print(location1.longitude, 5); + Serial.print(F(",")); + Serial.print(location1.altitude); + Serial.print(F("m,")); + Serial.print(location1.satellites); + Serial.print(F("sats,")); + Serial.print(location1.voltage); + Serial.print(F("mV,")); + Serial.print(location1.temperature); + Serial.print(F("c ")); +} + + +void packet_is_OK() +{ + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(" ")); + printlocation1(); + printpacketDetails(); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + printpacketDetails(); + Serial.print(F("IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + +void printpacketDetails() +{ + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup(void) +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.print(F("Receiver ready")); + Serial.println(); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/15_LoRa_Structure_RX/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/15_LoRa_Structure_RX/Settings.h new file mode 100644 index 0000000..d3dc3d9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/15_LoRa_Structure_RX/Settings.h @@ -0,0 +1,39 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 17/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 8 //On board LED, high for on + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa TX power in dBm + +#define packet_delay 1000 //mS delay between packets diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/1_LED_Blink/1_LED_Blink.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/1_LED_Blink/1_LED_Blink.ino new file mode 100644 index 0000000..d80c1e5 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/1_LED_Blink/1_LED_Blink.ino @@ -0,0 +1,72 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This programs is supplied as is, it is up to the user of the program to decide if the programs are + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program blinks an LED connected the pin number defined below. The pin 13 LED, + fitted to some Arduinos is blinked as well. The blinks should be close to one per second. messages are + sent to the Serial Monitor also. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define LED1 8 //pin number for LED, set logic level high for on + +#define Program_Version "V1.0" + +uint16_t seconds; //used to display time elapsed on Serial Monitor + +void loop() +{ + Serial.print(seconds); + Serial.println(F(" Seconds")); //this message should print on console at close to once per second + seconds++; + digitalWrite(LED1, HIGH); + digitalWrite(13, HIGH); + delay(100); + digitalWrite(LED1, LOW); + digitalWrite(13, LOW); + delay(890); //should give approx 1 second flash +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //general purpose routine for flashing LED as indicator + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); //LED on + digitalWrite(13, HIGH); //Arduino board LED on + delay(delaymS); + digitalWrite(LED1, LOW); //LED off + digitalWrite(13, LOW); //Arduino board LED off + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + pinMode(13, OUTPUT); //setup pin as output for some Arduino boards that include an LED on pin 13 + digitalWrite(LED1, HIGH); + digitalWrite(13, HIGH); + + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("1_LED_Blink Starting")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/2_Register_Test/2_Register_Test.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/2_Register_Test/2_Register_Test.ino new file mode 100644 index 0000000..6f93f8d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/2_Register_Test/2_Register_Test.ino @@ -0,0 +1,266 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 18/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is stand alone, it is not necessary to install the SX12XX-LoRa library + to use it. This test program is for the SX127X LoRa devices. + + The program checks that a lora device can be accessed by doing a test register write and read. + If there is no device found a message is printed on the serial monitor. The contents of the registers + from 0x00 to 0x7F are printed and the program then changes the frequency between two values and prints + out the registers. This is to prove that the device registers are being read and written correctly. + There is a copy of the typical printout below. Note that the read back changed frequency may be slightly + different to the programmed frequency, there is a rounding error due to the use of floats to calculate + the frequency. Although the program sets the frequency in the 434Mhz band, it will work on 868Mhz and + 915Mhz devices and there is no attempt to confugure the device for transmission or reception. + + The Arduino pin number that NSS on the LoRa device is connected to must be specified in #define NSS + line below. Leave the NRESET and DIOx pins not connected. + + Typical printout; + + 2_Register_Test Starting + LoRa Device found + Device version 0x12 + Frequency at Start 434000000 + Registers at Start + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00 00 09 1A 0B 00 52 6C 80 00 4F 09 2B 20 08 02 0A + 0x10 FF 70 15 0B 28 0C 12 47 32 3E 00 00 00 00 00 40 + 0x20 00 00 00 00 05 00 03 93 55 55 55 55 55 55 55 55 + 0x30 90 40 40 00 00 0F 00 00 00 F5 20 82 FD 02 80 40 + 0x40 00 00 12 24 2D 00 03 00 04 23 00 09 05 84 32 2B + 0x50 14 00 00 10 00 00 00 0F E0 00 0C FD 06 00 5C 78 + 0x60 00 19 0C 4B CC 0D FD 20 04 47 AF 3F F6 3F DB 0B + 0x70 D0 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 + + + Change Frequency to 434100000 + Changed Frequency 434099968 + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00 00 09 1A 0B 00 52 6C 86 66 4F 09 2B 20 08 02 0A + 0x10 FF 70 15 0B 28 0C 12 47 32 3E 00 00 00 00 00 40 + 0x20 00 00 00 00 05 00 03 93 55 55 55 55 55 55 55 55 + 0x30 90 40 40 00 00 0F 00 00 00 F5 20 82 FD 02 80 40 + 0x40 00 00 12 24 2D 00 03 00 04 23 00 09 05 84 32 2B + 0x50 14 00 00 10 00 00 00 0F E0 00 0C FD 06 00 5C 78 + 0x60 00 19 0C 4B CC 0D FD 20 04 47 AF 3F F6 3F DB 0B + 0x70 D0 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Change Frequency to 434200000 + Changed Frequency 434199936 + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00 00 09 1A 0B 00 52 6C 8C CC 4F 09 2B 20 08 02 0A + 0x10 FF 70 15 0B 28 0C 12 47 32 3E 00 00 00 00 00 40 + 0x20 00 00 00 00 05 00 03 93 55 55 55 55 55 55 55 55 + 0x30 90 40 40 00 00 0F 00 00 00 F5 20 82 FD 02 80 40 + 0x40 00 00 12 24 2D 00 03 00 04 23 00 09 05 84 32 2B + 0x50 14 00 00 10 00 00 00 0F E0 00 0C FD 06 00 5C 78 + 0x60 00 19 0C 4B CC 0D FD 20 04 47 AF 3F F6 3F DB 0B + 0x70 D0 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Note: An SX1272 will report as version 0x22 and the frequency at power up is 915000000hz. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +const uint8_t REG_FRMSB = 0x06; //register number for setting and reading frequency, high byte +const uint8_t REG_FRMID = 0x07; //register number for setting and reading frequency, mid byte +const uint8_t REG_FRLSB = 0x08; //register number for setting and reading frequency, low byte +const uint8_t REG_VERSION = 0x42; //register containg version number of device + + +//********* Setup hardware pin definition here ! ************** + +#define NSS 10 //lora device select + +//**************************************************************/ + + +#include + +uint32_t frequency; + + +void setup() +{ + Serial.begin(9600); + Serial.println(F("2_Register_Test Starting")); + + SPI.begin(); + SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //The begin function setups the hardware pins used by device and then checks if device is found + + if (begin(NSS)) + { + Serial.println(F("LoRa Device found")); + } + else + { + Serial.println(F("No device responding")); + } + + Serial.print(F("Device version 0x")); + uint8_t deviceversion = readRegister(REG_VERSION); + if (deviceversion < 0x10) + { + Serial.print(F("0")); + } + Serial.println(deviceversion, HEX); + + frequency = getFreqInt(); //read the set frequency following a reset + Serial.print(F("Frequency at Start ")); + Serial.println(frequency); + + Serial.println(F("Registers at Start ")); //show the all registers following a power up + printRegisters(0x00, 0x7F); +} + + +void loop() +{ + Serial.println(); + Serial.println(); + + Serial.println(F("Change Frequency to 434100000")); + setRfFrequency(434100000, 0); //change the frequency at reset, in hertz + frequency = getFreqInt(); //read back the changed frequency + Serial.print(F("Changed Frequency ")); + Serial.println(frequency); //print the changed frequency, did the write work (allow for rounding errors) ? + printRegisters(0x00, 0x7F); //show the registers after frequency change + Serial.println(); + + Serial.println(F("Change Frequency to 434200000")); + setRfFrequency(434200000, 0); //change the frequency at reset, in hertz + frequency = getFreqInt(); //read back the changed frequency + Serial.print(F("Changed Frequency ")); + Serial.println(frequency); //print the changed frequency, did the write work (allow for rounding errors) ? + printRegisters(0x00, 0x7F); //show the registers after frequency change + Serial.println(); + + delay(5000); +} + + +uint8_t readRegister(uint8_t address) +{ + uint8_t regdata; + digitalWrite(NSS, LOW); //set NSS low + SPI.transfer(address & 0x7F); //mask address for read + regdata = SPI.transfer(0); //read the byte + digitalWrite(NSS, HIGH); //set NSS high + return regdata; +} + + +void writeRegister(uint8_t address, uint8_t value) +{ + digitalWrite(NSS, LOW); //set NSS low + SPI.transfer(address | 0x80); //mask address for write + SPI.transfer(value); //write the byte + digitalWrite(NSS, HIGH); //set NSS high +} + + +uint32_t getFreqInt() +{ + //get the current set LoRa device frequency, return as long integer + + uint8_t Msb, Mid, Lsb; + uint32_t uinttemp; + float floattemp; + Msb = readRegister(REG_FRMSB); + Mid = readRegister(REG_FRMID); + Lsb = readRegister(REG_FRLSB); + floattemp = ((Msb * 0x10000ul) + (Mid * 0x100ul) + Lsb); + floattemp = ((floattemp * 61.03515625) / 1000000ul); + uinttemp = (uint32_t)(floattemp * 1000000); + return uinttemp; +} + + +void printRegisters(uint16_t Start, uint16_t End) +{ + //prints the contents of lora device registers to serial monitor + + uint16_t Loopv1, Loopv2, RegData; + + Serial.print(F("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F")); + Serial.println(); + + for (Loopv1 = Start; Loopv1 <= End;) + { + Serial.print(F("0x")); + if (Loopv1 < 0x10) + { + Serial.print(F("0")); + } + Serial.print((Loopv1), HEX); + Serial.print(F(" ")); + for (Loopv2 = 0; Loopv2 <= 15; Loopv2++) + { + RegData = readRegister(Loopv1); + if (RegData < 0x10) + { + Serial.print(F("0")); + } + Serial.print(RegData, HEX); + Serial.print(F(" ")); + Loopv1++; + } + Serial.println(); + } +} + + +void setRfFrequency(uint64_t freq64, int32_t offset) +{ + freq64 = freq64 + offset; + freq64 = ((uint64_t)freq64 << 19) / 32000000; + writeRegister(REG_FRMSB, (uint8_t)(freq64 >> 16)); + writeRegister(REG_FRMID, (uint8_t)(freq64 >> 8)); + writeRegister(REG_FRLSB, (uint8_t)(freq64 >> 0)); +} + + +bool begin(int8_t pinNSS) +{ + pinMode(pinNSS, OUTPUT); + digitalWrite(pinNSS, HIGH); + + if (checkDevice()) + { + return true; + } + + return false; +} + + +bool checkDevice() +{ + //check there is a device out there, writes a register and reads back + + uint8_t Regdata1, Regdata2; + Regdata1 = readRegister(REG_FRMID); //low byte of frequency setting + writeRegister(REG_FRMID, (Regdata1 + 1)); + Regdata2 = readRegister(REG_FRMID); //read changed value back + writeRegister(REG_FRMID, Regdata1); //restore register to original value + + if (Regdata2 == (Regdata1 + 1)) + { + return true; + } + else + { + return false; + } +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino new file mode 100644 index 0000000..3884ab4 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino @@ -0,0 +1,121 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a minimum setup LoRa test transmitter. A packet containing the ASCII text + "Hello World 1234567890" is sent using the frequency and LoRa settings specified in the LT.setupLoRa() + command. The pins to access the lora device need to be defined at the top of the program also. + + The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with + the transmit power used and the packet length. The matching receiver program, '4_LoRa_Receiver' can be used + to check the packets are being sent correctly, the frequency and LoRa settings (in the LT.setupLoRa() + commands) must be the same for the transmitter and receiver programs. Sample Serial Monitor output; + + 10dBm Packet> Hello World 1234567890* BytesSent,23 PacketsSent,6 + + For an example of a more detailed configuration for a transmitter, see program 103_LoRa_Transmitter. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library + +SX127XLT LT; //create a library class instance called LT + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define DIO0 3 //DIO0 pin on LoRa device, used for sensing RX and TX done +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using +#define TXpower 10 //LoRa transmit power in dBm + +uint8_t TXPacketL; +uint32_t TXPacketCount; + +uint8_t buff[] = "Hello World 1234567890"; //the message to send + + +void setup() +{ + Serial.begin(9600); + Serial.println(); + Serial.println(F("3_LoRa_Transmitter Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1); + } + + LT.setupLoRa(434000000, 0, LORA_SF7, LORA_BW_125, LORA_CR_4_5, LDRO_AUTO); //configure frequency and LoRa settings + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on receiver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + Serial.println(); + delay(1000); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino new file mode 100644 index 0000000..93bda37 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino @@ -0,0 +1,168 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a minimum setup LoRa test receiver. The program listens for incoming packets + using the frequency and LoRa settings in the LT.setupLoRa() command. The pins to access the lora device + need to be defined at the top of the program also. + + There is a printout on the Arduino IDE serial monitor of the valid packets received, the packet is assumed + to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect weird things to + happen on the Serial Monitor. Sample serial monitor output; + + 8s Hello World 1234567890*,RSSI,-44dBm,SNR,9dB,Length,23,Packets,7,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error; + + 137s PacketError,RSSI,-89dBm,SNR,-8dB,Length,23,Packets,37,Errors,2,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + If there are no packets received in a 10 second period then you should see a message like this; + + 112s RXTimeout + + For an example of a more detailed configuration for a receiver, see program 104_LoRa_Receiver. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library + +SX127XLT LT; //create a library class instance called LT + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using +#define RXBUFFER_SIZE 255 //RX buffer size + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet + + +void setup() +{ + Serial.begin(9600); + Serial.println(); + Serial.println(F("4_LoRa_Receiver Starting")); + Serial.println(); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1); + } + + LT.setupLoRa(434000000, 0, LORA_SF7, LORA_BW_125, LORA_CR_4_5, LDRO_AUTO); //configure frequency and LoRa settings + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + PacketRSSI = LT.readPacketRSSI(); //read the received packets RSSI value + PacketSNR = LT.readPacketSNR(); //read the received packets SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + + RXpacketCount++; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + printElapsedTime(); //print elapsed time to Serial Monitor + + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/58_FM_Tone/58_FM_Tone.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/58_FM_Tone/58_FM_Tone.ino new file mode 100644 index 0000000..e19742f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/58_FM_Tone/58_FM_Tone.ino @@ -0,0 +1,93 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - Transmits a FM tone using the LoRa device that can be picked up on an FM UHF + handheld receiver. The tones are not true FM but the UHF receiver does not know that. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.println(F("PlayTone> ")); + Serial.println(); + + digitalWrite(LED1, HIGH); + LT.toneFM(1000, 1000, deviation, adjustfreq, TXpower); + digitalWrite(LED1, LOW); + + delay(1000); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("58_FM_Tone Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupDirect(Frequency, Offset); + Serial.print(F("Tone Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/58_FM_Tone/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/58_FM_Tone/Settings.h new file mode 100644 index 0000000..75650d2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/58_FM_Tone/Settings.h @@ -0,0 +1,32 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2 are not used by this particular sketch so they are set to -1 and not connected. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup Direct Modem Parameters Here ! *************** + +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes +const uint16_t deviation = 10000; //deviation, total frequency shift low to high +const float adjustfreq = 0.9; //adjustment to tone frequency + +const int8_t TXpower = 10; //LoRa transmit power in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/73_LoRa_Receiver_AFC/73_LoRa_Receiver_AFC.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/73_LoRa_Receiver_AFC/73_LoRa_Receiver_AFC.ino new file mode 100644 index 0000000..d0453d7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/73_LoRa_Receiver_AFC/73_LoRa_Receiver_AFC.ino @@ -0,0 +1,200 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 10/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is demonstration of using automatic frequency adjustments (AFC) on a receiver + to keep the frequency difference between transmitter and receiver as low as possible. The LoRa settings + used are defined in the Settings.h file. + + If a packet is received OK, then all that is needed is a call to the LT.doAFC() function, that reads the + last frequency error, calculates the new offset and changes the set frequency accordingly. + + When the receiver starts the frequency error may be as large as 4000hz, when the AFC operates the error + should reduce to 100hz or so. The first AFC correction to run is doAFCPPM(); which based on the frequency + error also adjusts the PPM setting. If doAFC(); were only used then as the frequency error is reduced then + the PPM adjustment would reduce. + + Note that the maximum permitted frequency error between transmitter and receiver is 25% of the bandwidth + in use. So at 125000hz bandwidth the maximum frequency error is 31500hz, if the bandwidth is 7800hz the + maximum frequency error is 1950hz. Whilst the AFC functionality can keep transmitter and receiver close + together when reception is working if the transmitter and receiver are to far apart in frequency for + reception to work in the first place then AFC cannot operate to correct for the frequency differences. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" + +SX127XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet +int32_t frequencyerror; //frequency error of receved packet + +bool FirstAFC = true; //used to note that AFC has been called more than once + +void loop() +{ + + Serial.print(F("SetFrequency,")); + Serial.print(Frequency); + Serial.print(F("hz,Offset,")); + Serial.print(LT.getOffset()); + Serial.print(F("hz ")); + + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + PacketRSSI = LT.readPacketRSSI(); //read the received packets RSSI value + PacketSNR = LT.readPacketSNR(); //read the received packets SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + if (FirstAFC) + { + LT.doAFCPPM(); //the first time AFC is called do the PPM adjust also + FirstAFC = false; + } + else + { + LT.doAFC(); //PPM adjust has been done so now just adjust frequency + } + } + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + + + RXpacketCount++; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + frequencyerror = LT.getFrequencyErrorHz(); + printElapsedTime(); //print elapsed time to Serial Monitor + + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + Serial.print(F(",")); + Serial.print(LT.readRegister(REG_FEIMSB),HEX); + Serial.print(F(",")); + Serial.print(LT.readRegister(REG_FEIMID),HEX); + Serial.print(F(",")); + Serial.print(LT.readRegister(REG_FEILSB),HEX); + Serial.print(F(",Regval,")); + Serial.print(LT.getFrequencyErrorRegValue(),HEX); + Serial.print(F(",FreqErrror,")); + Serial.print(frequencyerror); + Serial.print(F("hz,PpmCorrection,")); + Serial.print(LT.readRegister(REG_PPMCORRECTION)); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void setup() +{ + Serial.begin(9600); + Serial.println(); + Serial.println(F("73_LoRa_Receiver_AFC starting")); + Serial.println(); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1); + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/73_LoRa_Receiver_AFC/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/73_LoRa_Receiver_AFC/Settings.h new file mode 100644 index 0000000..99b3506 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/73_LoRa_Receiver_AFC/Settings.h @@ -0,0 +1,41 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 10/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 2; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 128 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/80_Direct_DIO2_Tone/80_Direct_DIO2_Tone.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/80_Direct_DIO2_Tone/80_Direct_DIO2_Tone.ino new file mode 100644 index 0000000..9a3ceeb --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/80_Direct_DIO2_Tone/80_Direct_DIO2_Tone.ino @@ -0,0 +1,106 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - Transmits a FM tone using the LoRa device that can be picked up on an FM UHF + handheld receiver. The tones are generated in direct mode by applying a pulse using the Arduino tone() + function (not supported by all Arduinos) to the DIO2 pin. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LoRa; //create a library class instance called LoRa + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.println(F("PlayTones> ")); + Serial.println(); + + LoRa.setupDirect(Frequency, Offset); + LoRa.setTXDirect(); //turn on transmit + LoRa.setTxParams(TXpower, RADIO_RAMP_DEFAULT); //set TX power + LoRa.writeRegister(REG_FDEVLSB, deviation); //set the deviation + LoRa.writeRegister(REG_PLLHOP, 0xAD); //set fast hop mode, needed for fast changes of frequency + + + while(1) //repeat forever + { + digitalWrite(LED1, HIGH); + tone(DIO2, 1000); //pin, frequency, durationmS + delay(1000); + digitalWrite(LED1, LOW); + noTone(DIO2); + delay(1000); + }; +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + pinMode(6, OUTPUT); + + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("80_Direct_DIO2_Tone Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LoRa.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + Serial.print(F("Tone Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/80_Direct_DIO2_Tone/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/80_Direct_DIO2_Tone/Settings.h new file mode 100644 index 0000000..4f78e28 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Basics/80_Direct_DIO2_Tone/Settings.h @@ -0,0 +1,31 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2 are not used by this particular sketch so they are set to -1 and not connected. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 6 //DIO2 pin on LoRa device can be used for tone + + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup Direct Modem Parameters Here ! *************** + +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t deviation = 0x52; //set approx 5khz deviation +const int8_t TXpower = 10; //LoRa transmit power in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino new file mode 100644 index 0000000..8c9d438 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino @@ -0,0 +1,266 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a program that can be used to test the effectiveness of a LoRa link or its + attached antennas. Simulations of antenna performance are no substitute for real world tests and this + simple program allows both long distance link performance to be evaluated and antenna performance to be + compared. + + The program sends short test packets that reduce in power by 1dBm at a time. The start power is defined + by start_power and the end power is defined by end_power (see Settings.h file). Once the end_power point + is reached, the program pauses a short while and starts the transmit sequence again at start_power. + The packet sent contains the power used to send the packet. By listening for the packets with the basic + LoRa receive program (4_LoRa_Receiver) you can see the reception results, which should look something + like this; + + 11s 1*T+05,CRC,80B8,RSSI,-73dBm,SNR,9dB,Length,6,Packets,9,Errors,0,IRQreg,50 + 12s 1*T+04,CRC,9099,RSSI,-74dBm,SNR,9dB,Length,6,Packets,10,Errors,0,IRQreg,50 + 14s 1*T+03,CRC,E07E,RSSI,-75dBm,SNR,9dB,Length,6,Packets,11,Errors,0,IRQreg,50 + + Above shows 3 packets received, the first at +05dBm (+05 in printout), the second at 4dBm (+04 in + printout) and the third at 3dBm (+03) in printout. + + If it is arranged so that reception of packets fails halfway through the sequence by attenuating either the + transmitter (with an SMA attenuator for instance) or the receiver (by placing it in a tin perhaps) then + if you swap transmitter antennas you can see the dBm difference in reception, which will be the dBm difference + (gain) of the antenna. + + To start the sequence a packet is sent with the number 999, when received it looks like this; + + T*1999 + + This received packet could be used for the RX program to be able to print totals etc. + + LoRa settings to use for the link test are specified in the 'Settings.h' file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include +#include "Settings.h" + +SX127XLT LT; + +int8_t TestPower; +uint8_t TXPacketL; + +void loop() +{ + Serial.println(F("Start Test Sequence")); + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("Start Packet> ")); + + SendTest1ModePacket(); + + Serial.println(); + + for (TestPower = start_power; TestPower >= end_power; TestPower--) + { + Serial.print(TestPower); + Serial.print(F("dBm ")); + Serial.print(F("Test Packet> ")); + Serial.flush(); + SendTestPacket(TestPower); + Serial.println(); + delay(packet_delay); + } + + Serial.println(F("Finished Test Sequence")); + Serial.println(); +} + + +void SendTestPacket(int8_t lpower) +{ + //build and send the test packet in addressed form, 3 bytes will be added to begining of packet + int8_t temppower; + uint8_t buff[3]; //the packet is built in this buffer + TXPacketL = sizeof(buff); + + if (lpower < 0) + { + buff[0] = '-'; + } + else + { + buff[0] = '+'; + } + + if (TestPower == 0) + { + buff[0] = ' '; + } + + temppower = TestPower; + + if (temppower < 0) + { + temppower = -temppower; + } + + if (temppower > 19) + { + buff[1] = '2'; + buff[2] = ((temppower - 20) + 0x30); + } + else if (temppower > 9) + { + buff[1] = '1'; + buff[2] = ((temppower - 10) + 0x30); + } + else + { + buff[1] = '0'; + buff[2] = (temppower + 0x30); + } + + LT.printASCIIPacket(buff, sizeof(buff)); + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitAddressed(buff, sizeof(buff), TestPacket, Broadcast, ThisNode, 5000, lpower, WAIT_TX); + digitalWrite(LED1, LOW); + + if (TXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } +} + + +void SendTest1ModePacket() +{ + //used to allow an RX to recognise the start off the sequence and possibly print totals + + uint8_t buff[3]; //the packet is built in this buffer + + buff[0] = '9'; + buff[1] = '9'; + buff[2] = '9'; + TXPacketL = sizeof(buff); + + LT.printASCIIPacket(buff, sizeof(buff)); + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitAddressed(buff, sizeof(buff), TestMode1, Broadcast, ThisNode, 5000, start_power, WAIT_TX); + delay(mode_delaymS); //longer delay, so that the start test sequence is obvious + digitalWrite(LED1, LOW); + + if (TXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes SentOK")); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + delay(packet_delay); //change LED flash so packet error visible + delay(packet_delay); + digitalWrite(LED1, HIGH); + delay(packet_delay); + delay(packet_delay); + digitalWrite(LED1, LOW); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("10_LoRa_Link_Test_Transmitter Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); + +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h new file mode 100644 index 0000000..2af3e21 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2 may not be in used by this sketch so they do not need to be connected and should +//be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 8 //On board LED, high for on + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 17; //Transmit power used when sending packet starting test sequence +const int8_t start_power = 17; //link test starts at this transmit power +const int8_t end_power = 2; //and ends at this power +const uint8_t ThisNode = 'T'; //this identifies the node in transmissions + + +#define packet_delay 1000 //mS delay between packets +#define mode_delaymS 2000 //mS delay after sending start test sequence + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino new file mode 100644 index 0000000..46e1a2b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino @@ -0,0 +1,223 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout of the valid packets received in HEX format. Thus the program can be used to receive + and record non-ASCII packets. The LED will flash for each packet received and the buzzer will sound, + if fitted. The measured frequency difference between the frequency used by the transmitter and the + frequency used by the receiver is shown. If this frequency difference gets to 25% of the set LoRa + bandwidth, packet reception will fail. The displayed error can be reduced by using the 'offset' + setting in the 'Settings.h' file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" +#include //get the library here; https://github.com/PaulStoffregen/Time +time_t recordtime; //used to record the current time, preventing displayed rollover on printing + +SX127XLT LT; + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 60000, WAIT_RX); //returns 0 if packet error of some sort, timeout set at 60secs\60000mS + + digitalWrite(LED1, HIGH); //something has happened + recordtime = now(); //stop the time to be displayed rolling over + printtime(); + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + delay(50); //lets have a slightly longer beep + digitalWrite(BUZZER, LOW); + } + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + RXpacketCount++; + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + Serial.print(F(" FreqErrror,")); + Serial.print(LT.getFrequencyErrorHz()); + Serial.print(F("hz ")); + + LT.printSXBufferHEX(0, (RXPacketL - 1)); + + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void printDigits(int8_t digits) +{ + //utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(F(":")); + if (digits < 10) + Serial.print('0'); + Serial.print(digits); +} + + +void printtime() +{ + Serial.print(hour(recordtime)); + printDigits(minute(recordtime)); + printDigits(second(recordtime)); +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("11_Packet_Logger_Receiver Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("Radio Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); + Serial.println(); + LT.printOperatingSettings(); + Serial.println(); + Serial.println(); + printtime(); + Serial.print(F(" Receiver ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h new file mode 100644 index 0000000..8e94b8d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h @@ -0,0 +1,39 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be connected +//and should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 8 //On board LED, high for on +#define BUZZER 4 //Buzzer if fitted, high for on. Set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 2; //LoRa TX power + +#define packet_delay 1000 //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/13_Frequency_and_Power_Check_TX/13_Frequency_and_Power_Check_TX.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/13_Frequency_and_Power_Check_TX/13_Frequency_and_Power_Check_TX.ino new file mode 100644 index 0000000..7878ba9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/13_Frequency_and_Power_Check_TX/13_Frequency_and_Power_Check_TX.ino @@ -0,0 +1,138 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a program that transmits a long LoRa packets lasting about 5 seconds that + can be used to measure the frequency and power of the transmission using external equipment. The bandwidth + of the transmission is only 10khz, so a frequency counter should give reasonable average result. + + The LoRa settings to use, including transmit powwer, are specified in the 'Settings.h' file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" + +SX127XLT LT; + +uint8_t TXPacketL; +uint32_t TXPacketCount; + + +void loop() +{ + Serial.print(F("Sending ")); + Serial.print(TXpower); + Serial.print(F("dBm Packet > ")); + Serial.flush(); + + uint8_t buff[] = "Hello World!1234Hello World!1234"; //use a longish packet to measure + TXPacketL = sizeof(buff); + + LT.printASCIIPacket(buff, TXPacketL); //print the packet + + digitalWrite(LED1, HIGH); + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 + { + TXPacketCount++; + packet_is_OK(); + digitalWrite(LED1, LOW); + } + else + { + packet_is_Error(); + digitalWrite(LED1, LOW); + } + + Serial.println(); + delay(packet_delay); +} + + +void packet_is_OK() +{ + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes SentOK")); + Serial.print(F(" PacketsSent ")); + Serial.print(TXPacketCount); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F("SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); //this leaves the LED on slightly longer for a packet send error +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("13_Frequency_and_Power_Check_TX Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.print(F("Transmitter ready - TXBUFFER_SIZE ")); + Serial.println(TXBUFFER_SIZE); + + LT.printModemSettings(); + Serial.println(); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/13_Frequency_and_Power_Check_TX/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/13_Frequency_and_Power_Check_TX/Settings.h new file mode 100644 index 0000000..6fa1eac --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/13_Frequency_and_Power_Check_TX/Settings.h @@ -0,0 +1,41 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER SWITCH1 may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 8 //On board LED, high for on + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_010; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_8; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa TX power in dBm, 10dBm = 10mW + +#define packet_delay 2500 //mS delay between transmissions + +#define TXBUFFER_SIZE 64 //TX buffer size + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino new file mode 100644 index 0000000..1fa01e5 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino @@ -0,0 +1,183 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program can be used to check the frequency error between a pair of LoRa + devices, a transmitter and receiver. This receiver measures the frequecy error between the receivers + centre frequency and the centre frequency of the transmitted packet. The frequency difference is shown + for each packet and an average over 10 received packets reported. Any transmitter program can be used + to give this program something to listen to, including example program '3_LoRa_Transmit'. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" + +SX127XLT LT; + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //a buffer is needed to receive packets + +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet +int32_t totalHzError = 0; //used to keep a running total of hZ error for averaging + + +void loop() +{ + + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 0, WAIT_RX); //wait for a packet to arrive + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + RXpacketCount++; + Serial.print(F("PacketOK > ")); + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + Serial.println(); + printFrequencyError(); +} + + +void printFrequencyError() +{ + int32_t hertzerror, regdata; + regdata = LT.getFrequencyErrorRegValue(); + hertzerror = LT.getFrequencyErrorHz(); + Serial.print(F("ErrorRegValue,")); + Serial.print(regdata, HEX); + Serial.print(F(" PacketHertzError,")); + Serial.print(hertzerror); + Serial.println(F("hz")); + + totalHzError = totalHzError + hertzerror; + + if (RXpacketCount == 10) + { + Serial.print(F("******** AverageHertzerror ")); + Serial.print((totalHzError / 10)); + Serial.println(F("hz")); + RXpacketCount = 0; + totalHzError = 0; + delay(5000); + } +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + errors++; + Serial.print(F("PacketError,RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("16_LoRa_RX_Frequency_Error_Check Starting")); + Serial.println(); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h new file mode 100644 index 0000000..0d2e943 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h @@ -0,0 +1,43 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER SWITCH1 may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 8 //On board LED, high for on + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa TX power + +#define packet_delay 1000 //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino new file mode 100644 index 0000000..a04453d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino @@ -0,0 +1,298 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + The program is a matching receiver program for the '10_LoRa_Link_Test_Transmitter'. The packets received + are displayed on the serial monitor and analysed to extract the packet data which indicates the power + used to send the packet. A count is kept of the numbers of each power setting received. When the transmitter + sends the test mode packet at the beginning of the sequence (displayed as 999) the running totals of the + powers received are printed. Thus you can quickly see at what transmit power levels the reception fails. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc +#include + +SX127XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + +uint32_t Test1Count[21]; //buffer where counts of received packets are stored, +2dbm to +20dBm +uint32_t Mode1_Cycles = 0; //count the number of cyles received +bool updateCounts = false; //update counts set to tru when first TestMode1 received, at sequence start + + +void loop() +{ + RXPacketL = LT.receiveAddressed(RXBUFFER, RXBUFFER_SIZE, 15000, WAIT_RX); //wait for a packet to arrive with 15seconds (15000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + delay(25); //gives a slightly longer beep + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + + if (BUZZER > 0) //turn buzzer on for a valid packet + { + digitalWrite(BUZZER, HIGH); + } + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL - 3); //print the packet as ASCII characters + + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + + processPacket(); +} + + +void processPacket() +{ + int8_t lTXpower; + uint8_t packettype; + uint32_t temp; + + packettype = LT.readRXPacketType(); //need to know the packet type so we can decide what to do + + if (packettype == TestPacket) + { + lTXpower = ((RXBUFFER[1] - 48) * 10) + (RXBUFFER[2] - 48); //convert packet text to power + + Serial.print(F(" (")); + Serial.print(lTXpower); + Serial.print(F("dBm)")); + + if (updateCounts) + { + temp = (Test1Count[lTXpower]); + Test1Count[lTXpower] = temp + 1; + } + } + + if (packettype == TestMode1) + { + //this is a command to switch to TestMode1 also updates totals and logs + updateCounts = true; + Serial.println(); + Serial.println(F("End test sequence")); + + if (Mode1_Cycles > 0) + { + print_Test1Count(); + } + + Serial.println(); + Mode1_Cycles++; + } + +} + + +void print_Test1Count() +{ + //prints running totals of the powers of received packets + int8_t index; + uint32_t j; + + Serial.print(F("Test Packets ")); + Serial.println(RXpacketCount); + Serial.print(F("Test Cycles ")); + Serial.println(Mode1_Cycles); + + Serial.println(); + for (index = 20; index >= 2; index--) + { + Serial.print(index); + Serial.print(F("dBm,")); + j = Test1Count[index]; + Serial.print(j); + Serial.print(F(" ")); + } + Serial.println(); + + Serial.print(F("CSV")); + for (index = 20; index >= 2; index--) + { + Serial.print(F(",")); + j = Test1Count[index]; + Serial.print(j); + } + Serial.println(); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("20_LoRa_Link_Test_Receiver Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + //setup SPI, its external to library on purpose, so settings can be mixed and matched with other SPI devices + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //this function call sets up the device for LoRa using the settings from settings.h + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h new file mode 100644 index 0000000..70fce03 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define BUZZER 4 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino new file mode 100644 index 0000000..c8088b9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino @@ -0,0 +1,283 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 21/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, + if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. + The LED will flash for each packet received and the buzzer will sound, if fitted. + + Sample serial monitor output; + + 1109s {packet contents} CRC,3882,RSSI,-69dBm,SNR,10dB,Length,19,Packets,1026,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 1189s PacketError,RSSI,-111dBm,SNR,-12dB,Length,0,Packets,1126,Errors,1,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + A summary of the packet reception is sent to the OLED display as well, useful for portable applications. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +#include //get library here > https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 +#define DEFAULTFONT u8x8_font_chroma48medium8_r //font for U8X8 Library + +uint32_t RXpacketCount; +uint32_t RXpacketErrors; +uint16_t IRQStatus; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 0, WAIT_RX); //wait for a packet to arrive with no timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); //buzzer on + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t localCRC; + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(RXpacketErrors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("OK")); + dispscreen1(); +} + + +void packet_is_Error() +{ + printElapsedTime(); //print elapsed time to Serial Monitor + + RXpacketErrors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(RXpacketErrors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("Packet Error")); + dispscreen1(); + + delay(500); //gives longer buzzer and LED falsh for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void dispscreen1() +{ + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(F("RSSI ")); + disp.print(PacketRSSI); + disp.print(F("dBm")); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(F("SNR ")); + + if (PacketSNR > 0) + { + disp.print(F("+")); + } + + disp.print(PacketSNR); + disp.print(F("dB")); + disp.clearLine(3); + disp.setCursor(0, 3); + disp.print(F("Length ")); + disp.print(LT.readRXPacketL()); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(F("Packets ")); + disp.print(RXpacketCount); + disp.clearLine(5); + disp.setCursor(0, 5); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("IRQreg ")); + disp.print(IRQStatus, HEX); +} + + + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("33_LoRa_RSSI_Checker_With_Display Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + disp.begin(); + disp.setFont(DEFAULTFONT); + + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Check LoRa")); + disp.setCursor(0, 1); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + disp.print(F("LoRa OK")); + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + disp.print(F("Device error")); + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //this function call sets up the device for LoRa using the settings from settings.h + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h new file mode 100644 index 0000000..314d367 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define BUZZER 4 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 2; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 255 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino new file mode 100644 index 0000000..92399f9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino @@ -0,0 +1,282 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 26/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a program that can be used to test the throughput of a LoRa transmitter. + Whilst the various LoRa calculators tell you the on air data rate, in practice the achievable data + rate will be less than that due to the overhead of the software routines to load and send a packet + and internal delays in the LoRa device itself. + + A buffer is filled with characters and that buffer is then transmitted. The total time for a number of + transmissions is recorded and the bit rate calculated. The packet size (1 - 255 bytes) and the number of + packets to send in the test are specified in the 'Settings.h' file, see the 'Setup packet parameters Here !' + section. The setting file also has the lora settings to use. A lower spreading factors and higher + bandwidths will result in higher bitrates. + + There is the option of turning on an a requirement for an acknowledgement from a remote receiver, before + the transmitter sends the next packet, set this; 'const bool waitforACK = true;' definition in the + settings file. The matching receiver program '43_LoRa_Data_Throughput_Acknowledge_Receiver' does then need + to be configured with same lora settings as this transmitter. When this option is set, the program will + keep running until the number of transmissions and acknowledgements has completed without any timeouts + in order to produce a valid average. + + The results of the test are printed out thus; + + SX1278,434000000hz,SF7,BW500000,CR4:5,LDRO_Off,SyncWord_0x12,IQNormal,Preamble_8 + Total transmit time 100 packets = 1382mS + Average 16 byte packet transmit time = 13.82mS + Packets per second 72.36 + Bits per packet sent = 128 + Data rate = 9262bps + + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint32_t startmS, endmS, sendtimemS, bitspersecond, bitsPerpacket; +uint32_t TXPacketCount; +float averagePacketTime; +uint8_t packetNumber; +uint8_t RXPacketL; //length of received packet +uint8_t PacketType; //for packet addressing, identifies packet type received +uint32_t packetCheck; +bool loopFail = false; + + +void loop() +{ + uint16_t index, index2; + uint8_t TXBUFFER[TXPacketL + 1]; //create buffer for transmitted packet + loopFail = false; + + Serial.println(F("Start transmit test")); + + startmS = millis(); //start transmit timer + + for (index = 0; index < numberPackets; index++) + { + //fill the buffer + for (index2 = 0; index2 < TXPacketL; index2++) + { + TXBUFFER[index2] = index2; + } + + TXBUFFER[0] = TestPacket; //set first byte to identify this test packet + TXBUFFER[1] = index; //put the index as packet number in second byte of packet + Serial.print(index); //print number of packet sent + + if (waitforACK) + { + packetCheck = ( (uint32_t) TXBUFFER[4] << 24) + ( (uint32_t) TXBUFFER[3] << 16) + ( (uint32_t) TXBUFFER[2] << 8) + (uint32_t) TXBUFFER[1]; + Serial.print(F(",Checkvalue,")); + Serial.print(packetCheck, HEX); + Serial.print(F(",")); + } + + digitalWrite(LED1, HIGH); + + if (LT.transmit(TXBUFFER, TXPacketL, 10000, TXpower, WAIT_TX)) //will return 0 if transmit error + { + digitalWrite(LED1, LOW); + + if (waitforACK) + { + if (!waitAck(packetCheck)) + { + Serial.print(F("NoACKreceived,")); + loopFail = true; + break; + } + } + + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + loopFail = true; + } + + Serial.println(); + + } + + if (!loopFail) + { + endmS = millis(); //all packets sent, note end time + digitalWrite(LED1, LOW); + Serial.println(); + LT.printModemSettings(); + sendtimemS = endmS - startmS; + Serial.println(); + Serial.print(F("Total transmit time ")); + Serial.print(numberPackets); + Serial.print(F(" packets = ")); + Serial.print(sendtimemS); + Serial.println(F("mS")); + + averagePacketTime = (float) ((endmS - startmS) / numberPackets); + + if (waitforACK) + { + Serial.print(F("Average ")); + Serial.print(TXPacketL); + Serial.print(F(" byte packet transmit and acknowledge time = ")); + } + else + { + Serial.print(F("Average ")); + Serial.print(TXPacketL); + Serial.print(F(" byte packet transmit time = ")); + } + + Serial.print(averagePacketTime, 2); + Serial.println(F("mS")); + Serial.print(F("Packets per second ")); + Serial.println((float) (1000/averagePacketTime)); + bitsPerpacket = (uint32_t) (TXPacketL * 8); + Serial.print(F("Bits per packet sent = ")); + Serial.println(bitsPerpacket); + + Serial.print(F("Data rate = ")); + Serial.print((bitsPerpacket / (averagePacketTime / 1000)), 0); + Serial.print(F("bps")); + Serial.println(); + Serial.println(); + delay(10000); //have a delay between loops so we can see result + } + else + { + Serial.println(F("Transmit test failed, trying again")); + Serial.println(); + delay(1000); + } + +} + + +bool waitAck(uint32_t TXnum) +{ + uint32_t RXnum; + uint16_t IRQStatus; + + RXPacketL = LT.receiveSXBuffer(0, 1000, WAIT_RX); //returns 0 if packet error of some sort + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F("RXTimeout,")); + return false; + } + else + { + Serial.print(F("ACKRX,")); + } + + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + RXnum = LT.readUint32(); + RXPacketL = LT.endReadSXBuffer(); + + if ( (PacketType != ACK) || (RXnum != TXnum)) + { + Serial.print(F("NotValidACK,")); + return false; + } + else + { + Serial.print(RXnum, HEX); + Serial.print(F(",OK")); + return true; + } + +} + + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("42_LoRa_Data_Throughput_Test_Transmitter Starting")); + + SPI.begin(); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h new file mode 100644 index 0000000..6d46521 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h @@ -0,0 +1,42 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 26/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER are not used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_500; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +//******* Setup packet parameters Here ! *************** +const uint8_t numberPackets = 50; //number of packets to send in transmit loop +const uint8_t TXPacketL = 16; //length of packet to send +const bool waitforACK = true; //set to true to have transmit wait for ack before continuing + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/43_LoRa_Data_Throughput_Acknowledge_Receiver/43_LoRa_Data_Throughput_Acknowledge_Receiver.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/43_LoRa_Data_Throughput_Acknowledge_Receiver/43_LoRa_Data_Throughput_Acknowledge_Receiver.ino new file mode 100644 index 0000000..80baabd --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/43_LoRa_Data_Throughput_Acknowledge_Receiver/43_LoRa_Data_Throughput_Acknowledge_Receiver.ino @@ -0,0 +1,174 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 25/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a receiver program that can be used to test the throughput of a LoRa + transmitter. The matching program '42_LoRa_Data_Throughput_Test_Transmitter' is setup to send packets + that require an acknowledgement before sending the next packet. This will slow down the effective + throughput. Make sure the lora settings in the 'Settings.h' file match those used in the transmitter. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[255]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +uint8_t TXPacketL; //stores length of packet sent +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet + +uint8_t PacketType; //for packet addressing, identifies packet type +uint32_t packetCheck; + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, 255, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + RXpacketCount++; + Serial.print(RXBUFFER[1]); + Serial.print(F(" RX")); + packetCheck = ( (uint32_t) RXBUFFER[4] << 24) + ( (uint32_t) RXBUFFER[3] << 16) + ( (uint32_t) RXBUFFER[2] << 8 ) + (uint32_t) RXBUFFER[1]; + Serial.print(F(",SendACK")); + sendAck(packetCheck); +} + + +void sendAck(uint32_t num) +{ + //acknowledge the packet received + uint8_t len; + + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeUint8(ACK); //identify type of packet + LT.writeUint32(num); //send the packet check, bytes 1 to 5 of packet + len = LT.endWriteSXBuffer(); //close buffer write + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); + digitalWrite(LED1, LOW); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(",RXTimeout")); + } + else + { + errors++; + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + Serial.print(F("Error")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,Len,")); + Serial.print(LT.readRXPacketL()); //get the device packet length + } +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("43_LoRa_Data_Throughput_Acknowledge_Receiver Starting")); + Serial.println(); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/43_LoRa_Data_Throughput_Acknowledge_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/43_LoRa_Data_Throughput_Acknowledge_Receiver/Settings.h new file mode 100644 index 0000000..0f3216a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/43_LoRa_Data_Throughput_Acknowledge_Receiver/Settings.h @@ -0,0 +1,39 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 25/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done + + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_500; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/60_LoRa_Packet_Logger_Receiver_SD.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/60_LoRa_Packet_Logger_Receiver_SD.ino new file mode 100644 index 0000000..fd1a5df --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/60_LoRa_Packet_Logger_Receiver_SD.ino @@ -0,0 +1,252 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout and save to SD card of the valid packets received in HEX format. Thus the program + can be used to receive and record non-ASCII packets. The LED will flash for each packet received and + the buzzer will sound, if fitted. The measured frequency difference between the frequency used by the + transmitter and the frequency used by the receiver is shown. If this frequency difference gets to 25% + of the set LoRa bandwidth, packet reception will fail. The displayed error can be reduced by using the + 'offset' setting in the 'Settings.h' file. + + There will be a limit to how fast the logger can receive packets, mainly caused by the delay in writing + to SD card, so at high packet rates, packets will be lost. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include + +#include //https://github.com/greiman/SdFat +SdFat SD; +File logFile; +char filename[] = "Log000.txt"; + +bool SD_Found = false; //set if SD card found at program startup +uint8_t lognumber; + + +#include +SX127XLT LT; + +#include "Settings.h" +#include //get the library here; https://github.com/PaulStoffregen/Time + +uint32_t RXpacketCount; //count of good packets +uint32_t errors; //count of packet errors +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet +uint16_t IRQStatus; //used to read the IRQ status +int32_t FreqErrror; //frequency error of received packet, in hz + + +time_t recordtime; //used to record the current time, preventing displayed rollover on printing + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +#include "SD_Logger_Library.h" + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + recordtime = now(); //stop the time to be displayed rolling over + printtime(); + printtimeSD(); + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + FreqErrror = LT.getFrequencyErrorHz(); + IRQStatus = LT.readIrqStatus(); + + if (RXPacketL == 0) + { + packet_is_Error(); + packet_is_ErrorSD(); + } + else + { + packet_is_OK(); + packet_is_OKSD(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + delay(50); //lets have a slightly longer beep + digitalWrite(BUZZER, LOW); + } + + Serial.println(); +} + + +void packet_is_OK() +{ + + RXpacketCount++; + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + Serial.print(F(" FreqErrror,")); + Serial.print(FreqErrror); + Serial.print(F("hz ")); + + LT.printHEXPacket(RXBUFFER, RXPacketL); + + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + RXPacketL = LT.readRXPacketL(); //get the real packet length + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void printDigits(int8_t digits) +{ + //utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(F(":")); + if (digits < 10) + Serial.print('0'); + Serial.print(digits); +} + + +void printtime() +{ + Serial.print(hour(recordtime)); + printDigits(minute(recordtime)); + printDigits(second(recordtime)); +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("60_LoRa_Packet_Logger_Receiver_SD Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("lora device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); + } + } + + lognumber = setup_SDLOG() ; //setup SD card + Serial.print(F("Lognumber ")); + Serial.println(lognumber); + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); + Serial.println(); + printModemSettingsSD(); + logFile.println(); + LT.printOperatingSettings(); + Serial.println(); + printOperatingSettingsSD(); + logFile.println(); + printtime(); + Serial.print(F("Receiver ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/SD_Logger_Library.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/SD_Logger_Library.h new file mode 100644 index 0000000..cfa0313 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/SD_Logger_Library.h @@ -0,0 +1,410 @@ +//SD_Logger_Library.h +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +void printModemSettingsSD(); +void printOperatingSettingsSD(); +void printOperatingModeSD(uint8_t opmode); +uint8_t setup_SDLOG(); +void printDeviceSD(); +void printSXBufferHEXSD(uint8_t start, uint8_t end); +void printHEXByteSD(uint8_t temp); +void packet_is_OKSD(); +void packet_is_ErrorSD(); +void printDigitsSD(int8_t digits); +void printtimeSD(); + + +void printModemSettingsSD() +{ + uint8_t regdata; + uint8_t sf; + uint32_t bandwidth; + uint8_t cr; + uint8_t opt; + uint16_t syncword; + uint8_t invertIQ; + uint16_t preamble; + uint32_t freqint; + + if (LORA_DEVICE == DEVICE_SX1272) + { + regdata = (LT.readRegister(REG_MODEMCONFIG1) & READ_BW_AND_2); + } + else + { + regdata = (LT.readRegister(REG_MODEMCONFIG1) & READ_BW_AND_X); + } + + //get al the data frome the lora device in one go to avoid swapping + //devices on the SPI bus all the time + + if (LORA_DEVICE == DEVICE_SX1272) + { + regdata = (LT.readRegister(REG_MODEMCONFIG1) & READ_BW_AND_2); + } + else + { + regdata = (LT.readRegister(REG_MODEMCONFIG1) & READ_BW_AND_X); + } + + bandwidth = LT.returnBandwidth(regdata); + freqint = LT.getFreqInt(); + sf = LT.getLoRaSF(); + cr = LT.getLoRaCodingRate(); + opt = LT.getOptimisation(); + syncword = LT.getSyncWord(); + invertIQ = LT.getInvertIQ(); + preamble = LT.getPreamble(); + + printDeviceSD(); + logFile.print(F(",")); + logFile.print(freqint); + logFile.print(F("hz,SF")); + logFile.print(sf); + + logFile.print(F(",BW")); + logFile.print(bandwidth); + + logFile.print(F(",CR4:")); + logFile.print(cr); + logFile.print(F(",LDRO_")); + + if (opt) + { + logFile.print(F("On")); + } + else + { + logFile.print(F("Off")); + } + + logFile.print(F(",SyncWord_0x")); + logFile.print(syncword, HEX); + + if (invertIQ == LORA_IQ_INVERTED) + { + logFile.print(F(",IQInverted")); + } + else + { + logFile.print(F(",IQNormal")); + } + logFile.print(F(",Preamble_")); + logFile.print(preamble); + logFile.flush(); +} + + + +void printOperatingSettingsSD() +{ + //get al the data frome the lora device in one go to avoid swapping + //devices on the SPI bus all the time + + uint8_t ver = LT.getVersion(); + uint8_t pm = LT.getPacketMode(); + uint8_t hm = LT.getHeaderMode(); + uint8_t crcm = LT.getCRCMode(); + uint8_t agc = LT.getAGC(); + uint8_t lnag = LT.getLNAgain(); + uint8_t boosthf = LT.getLNAboostHF(); + uint8_t boostlf = LT.getLNAboostLF(); + uint8_t opmode = LT.getOpmode(); + + printDeviceSD(); + logFile.print(F(",")); + + printOperatingModeSD(opmode); + + logFile.print(F(",Version_")); + logFile.print(ver, HEX); + + logFile.print(F(",PacketMode_")); + + if (pm) + { + logFile.print(F("LoRa")); + } + else + { + logFile.print(F("FSK")); + } + + if (hm) + { + logFile.print(F(",Implicit")); + } + else + { + logFile.print(F(",Explicit")); + } + + logFile.print(F(",CRC_")); + if (crcm) + { + logFile.print(F("On")); + } + else + { + logFile.print(F("Off")); + } + + + logFile.print(F(",AGCauto_")); + if (agc) + { + logFile.print(F("On")); + } + else + { + logFile.print(F("Off")); + } + + logFile.print(F(",LNAgain_")); + logFile.print(lnag); + + logFile.print(F(",LNAboostHF_")); + if (boosthf) + { + logFile.print(F("On")); + } + else + { + logFile.print(F("Off")); + } + + logFile.print(F(",LNAboostLF_")); + if (boostlf) + { + logFile.print(F("On")); + } + else + { + logFile.print(F("Off")); + } + logFile.flush(); +} + + + +void printOperatingModeSD(uint8_t opmode) +{ + switch (opmode) + { + case 0: + logFile.print(F("SLEEP")); + break; + + case 1: + logFile.print(F("STDBY")); + break; + + case 2: + logFile.print(F("FSTX")); + break; + + case 3: + logFile.print(F("TX")); + break; + + case 4: + logFile.print(F("FSRX")); + break; + + case 5: + logFile.print(F("RXCONTINUOUS")); + break; + + case 6: + logFile.print(F("RXSINGLE")); + break; + + case 7: + logFile.print(F("CAD")); + break; + + default: + logFile.print(F("NOIDEA")); + break; + } +} + + +uint8_t setup_SDLOG() +{ + //checks if the SD card is present and can be initialised + //returns log number, 1-99, if OK, 0 if not + + uint8_t i; + + Serial.print(F("SD card...")); + + if (!SD.begin(SDCS)) + { + Serial.println(F("ERROR SD card fail")); + Serial.println(); + SD_Found = false; + return 0; + } + + Serial.print(F("Initialized OK - ")); + SD_Found = true; + + for (i = 1; i < 100; i++) { + filename[4] = i / 10 + '0'; + filename[5] = i % 10 + '0'; + if (! SD.exists(filename)) { + // only open a new file if it doesn't exist + logFile = SD.open(filename, FILE_WRITE); + break; + } + } + + Serial.print(F("Writing to ")); + Serial.println(filename); + + return i; +} + + +void printDeviceSD() +{ + + switch (LORA_DEVICE) + { + case DEVICE_SX1272: + logFile.print(F("SX1272")); + break; + + case DEVICE_SX1276: + logFile.print(F("SX1276")); + break; + + case DEVICE_SX1277: + logFile.print(F("SX1277")); + break; + + case DEVICE_SX1278: + logFile.print(F("SX1278")); + break; + + case DEVICE_SX1279: + logFile.print(F("SX1279")); + break; + + default: + logFile.print(F("Unknown Device")); + + } +} + + +void printHEXPacketSD(uint8_t *buffer, uint8_t size) +{ + uint8_t index; + + for (index = 0; index < size; index++) + { + printHEXByteSD(buffer[index]); + logFile.print(F(" ")); + } +} + + +void printHEXByteSD(uint8_t temp) +{ + + if (temp < 0x10) + { + logFile.print(F("0")); + } + logFile.print(temp, HEX); +} + + +void packet_is_OKSD() +{ + //uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + logFile.print(F(" FreqErrror,")); + logFile.print(LT.getFrequencyErrorHz()); + logFile.print(F("hz ")); + + printHEXPacketSD(RXBUFFER, RXPacketL); + + logFile.print(F(" RSSI,")); + logFile.print(PacketRSSI); + logFile.print(F("dBm,SNR,")); + logFile.print(PacketSNR); + logFile.print(F("dB,Length,")); + logFile.print(RXPacketL); + logFile.print(F(",Packets,")); + logFile.print(RXpacketCount); + logFile.print(F(",Errors,")); + logFile.print(errors); + logFile.print(F(",IRQreg,")); + logFile.println(IRQStatus, HEX); + logFile.flush(); +} + + +void packet_is_ErrorSD() +{ + //uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + RXPacketL = LT.readRXPacketL(); //get the real packet length + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + logFile.print(F(" RXTimeout")); + } + else + { + logFile.print(F(" PacketError")); + logFile.print(F(",RSSI,")); + logFile.print(PacketRSSI); + logFile.print(F("dBm,SNR,")); + logFile.print(PacketSNR); + logFile.print(F("dB,Length,")); + logFile.print(RXPacketL); + logFile.print(F(",Packets,")); + logFile.print(RXpacketCount); + logFile.print(F(",Errors,")); + logFile.print(errors); + logFile.print(F(",IRQreg,")); + logFile.println(IRQStatus, HEX); + } + logFile.flush(); +} + + +void printDigitsSD(int8_t digits) +{ + //utility function for digital clock display: prints preceding colon and leading 0 + logFile.print(F(":")); + if (digits < 10) + logFile.print('0'); + logFile.print(digits); +} + + +void printtimeSD() +{ + logFile.print(hour(recordtime)); + printDigitsSD(minute(recordtime)); + printDigitsSD(second(recordtime)); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/Settings.h new file mode 100644 index 0000000..9b98cff --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/60_LoRa_Packet_Logger_Receiver_SD/Settings.h @@ -0,0 +1,41 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, An ATmeag1284P base for my BBF modules. +//Be sure to change the definitions to match your own setup. Some pins such as DIO1, DIO210 BUZZER may not +//be in used by this sketch so they do not need to be connected and should be included and be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 8 //On board LED, high for on +#define BUZZER 4 //Buzzer if fitted, high for on. Set to -1 if not used +#define SDCS 30 //CS pin for SD card + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa TX power + +#define packet_delay 1000 //mS delay between packets + + +#define RXBUFFER_SIZE 255 //RX buffer size diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/66_FSK_Carrier_Generator/66_FSK_Carrier_Generator.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/66_FSK_Carrier_Generator/66_FSK_Carrier_Generator.ino new file mode 100644 index 0000000..c69df8e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/66_FSK_Carrier_Generator/66_FSK_Carrier_Generator.ino @@ -0,0 +1,98 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - Transmits an FSK carrier on a single frequency. Useful for checking the output + of a LoRa device. View the output level on a low cost SDR or spectrum analyser. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + + digitalWrite(LED1, HIGH); + Serial.print(F("Transmit Carrier > On ")); + + LT.setupDirect(Frequency, Offset); + LT.fskCarrierOn(TXpower); + delay(2000); + LT.fskCarrierOff(); + + digitalWrite(LED1, LOW); + Serial.println(F("off")); + + delay(1000); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("66_FSK_Carrier_Generator Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //LT.setupDirect(Frequency, Offset); + //Serial.print(F("Transmitter ready")); + //Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/66_FSK_Carrier_Generator/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/66_FSK_Carrier_Generator/Settings.h new file mode 100644 index 0000000..c1f51f0 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Diagnostic_and_Test/66_FSK_Carrier_Generator/Settings.h @@ -0,0 +1,28 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2 are not used by this particular sketch so they are set to -1 and not connected. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup Direct Modem Parameters Here ! *************** + +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const int8_t TXpower = 2; //LoRa transmit power in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/1_LED_Blink_ESP32/1_LED_Blink_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/1_LED_Blink_ESP32/1_LED_Blink_ESP32.ino new file mode 100644 index 0000000..e9a69bf --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/1_LED_Blink_ESP32/1_LED_Blink_ESP32.ino @@ -0,0 +1,63 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/19 + + This programs is supplied as is, it is up to the user of the program to decide if the programs are + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program blinks an LED connected the pin number defined below. The blinks + should be close to one per second. messages are sent to the Serial Monitor also. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define LED1 2 //pin number for LED, set logic level high for on + +#define Program_Version "V1.0" + +uint16_t seconds; //used to display time elapsed on Serial Monitor + +void loop() +{ + Serial.print(seconds); + Serial.println(F(" Seconds")); //this message should print on console at close to once per second + seconds++; + digitalWrite(LED1, HIGH); + delay(100); + digitalWrite(LED1, LOW); + delay(890); //should give approx 1 second flash +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //general purpose routine for flashing LED as indicator + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); //LED on + delay(delaymS); + digitalWrite(LED1, LOW); //LED off + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("1_LED_Blink_ESP32 Starting")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/2_Register_Test_ESP32/2_Register_Test_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/2_Register_Test_ESP32/2_Register_Test_ESP32.ino new file mode 100644 index 0000000..e7cee2f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/2_Register_Test_ESP32/2_Register_Test_ESP32.ino @@ -0,0 +1,300 @@ +/******************************************************************************************************* + + Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is stand alone, it is not necessary to install the SX12XX-LoRa library + to use it. + + The program checks that a SX127X LoRa device can be accessed by doing a test register write and read. + If there is no device found a message is printed on the serial monitor. The contents of the registers + from 0x00 to 0x7F are printed, there is a copy of a typical printout below. Note that the read back + changed frequency may be slightly different to the programmed frequency, there is a rounding error due + to the use of floats to calculate the frequency. + + The Arduino pin numbers that the NSS and NRESET pins on the LoRa device are connected to must be + specified in the hardware definitions section below. The LoRa device type in use, SX1272, SX1276, + SX1277, SX1278 or SX1279 must be specified also. + + Typical printout; + + 2_Register_Test Starting + SX1276-79 Selected + LoRa Device found + Device version 0x12 + + Frequency at reset 434000000 + Registers at reset + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00 00 09 1A 0B 00 52 6C 80 00 4F 09 2B 20 08 02 0A + 0x10 FF 6F 15 0B 28 0C 12 47 32 3E 00 00 00 00 00 40 + 0x20 00 00 00 00 05 00 03 93 55 55 55 55 55 55 55 55 + 0x30 90 40 40 00 00 0F 00 00 00 F5 20 82 00 02 80 40 + 0x40 00 00 12 24 2D 00 03 00 04 23 00 09 05 84 32 2B + 0x50 14 00 00 12 00 00 00 0F E0 00 0C 00 08 00 5C 78 + 0x60 00 19 0C 4B CC 0F 01 20 04 47 AF 3F CF 00 53 0B + 0x70 D0 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Changed Frequency 434099968 + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00 00 09 1A 0B 00 52 6C 86 66 4F 09 2B 20 08 02 0A + 0x10 FF 6F 15 0B 28 0C 12 47 32 3E 00 00 00 00 00 40 + 0x20 00 00 00 00 05 00 03 93 55 55 55 55 55 55 55 55 + 0x30 90 40 40 00 00 0F 00 00 00 F5 20 82 00 02 80 40 + 0x40 00 00 12 24 2D 00 03 00 04 23 00 09 05 84 32 2B + 0x50 14 00 00 12 00 00 00 0F E0 00 0C 00 08 00 5C 78 + 0x60 00 19 0C 4B CC 0F 01 20 04 47 AF 3F CF 00 53 0B + 0x70 D0 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +const uint8_t REG_FRMSB = 0x06; //register number for setting setting and reading frequency, high byte +const uint8_t REG_FRMID = 0x07; //register number for setting setting and reading frequency, mid byte +const uint8_t REG_FRLSB = 0x08; //register number for setting setting and reading frequency, low byte +const uint8_t REG_VERSION = 0x42; //register containg version number of device + +const uint8_t DEVICE_SX1272 = 0x10; //SX1272 +const uint8_t DEVICE_SX1276 = 0x11; //SX1276 +const uint8_t DEVICE_SX1277 = 0x12; //SX1277 +const uint8_t DEVICE_SX1278 = 0x13; //SX1278 +const uint8_t DEVICE_SX1279 = 0x14; //SX1279 + +//********* Setup hardware definitions here ! ***************** + +//These are the pin definitions for one of the Tracker boards, the ESP32_Micro_Node, be sure to change +//them to match your own setup. You will also need to connect up the pins for the SPI bus, which on the +//ESP32_Micro_Node are SCK on pin 18, MISO on pin 19 and MOSI on pin 23. + +#define NSS 5 //SX127X device select +#define NRESET 27 //SX127X reset pin +#define DIO0 -1 //DIO0 pin on LoRa device, not used here so set to -1 +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1278 //defines the type of LoRa device used, needed for correct program operation + +//**************************************************************/ + + +#include + + +void setup() +{ + Serial.begin(9600); + Serial.println(F("2_Register_Test_ESP32 Starting")); + + SPI.begin(); + SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //The begin function setups the hardware pins used by device and then checks if device is found + //the DIO0, DIO1 and DIO2 pins are not used in this example so are set to -1 + //the LT.begin fuction can define the pins and device type directly in this way (for SX1278); + //LT.begin(10, 9, -1, -1, -1, DEVICE_SX1278) + + if (begin(NSS, NRESET, -1, -1, -1, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + } + else + { + Serial.println(F("No device responding")); + } + + Serial.print(F("Device version 0x")); + uint8_t deviceversion = readRegister(REG_VERSION); + if (deviceversion < 0x10) + { + Serial.print(F("0")); + } + Serial.println(deviceversion, HEX); +} + + +void loop() +{ + uint32_t frequency; + + frequency = getFreqInt(); //read the set frequency following a reset + Serial.print(F("Frequency at reset ")); + Serial.println(frequency); + + Serial.println(F("Registers at reset")); //show the all registers following a reset + printRegisters(0x00, 0x7F); + + Serial.println(); + Serial.println(); + + setRfFrequency(434100000, 0); //change the frequency at reset, in hertz + frequency = getFreqInt(); //read back the changed frequency + Serial.print(F("Changed Frequency ")); + Serial.println(frequency); //print the changed frequency, did the write work (allow for rounding errors) ? + printRegisters(0x00, 0x7F); //show the registers after frequency change + Serial.println(); + delay(5000); + resetDevice(LORA_DEVICE); //reset the device and start again +} + + +uint8_t readRegister(uint8_t address) +{ + uint8_t regdata; + digitalWrite(NSS, LOW); //set NSS low + SPI.transfer(address & 0x7F); //mask address for read + regdata = SPI.transfer(0); //read the byte + digitalWrite(NSS, HIGH); //set NSS high + return regdata; +} + + +void writeRegister(uint8_t address, uint8_t value) +{ + digitalWrite(NSS, LOW); //set NSS low + SPI.transfer(address | 0x80); //mask address for write + SPI.transfer(value); //write the byte + digitalWrite(NSS, HIGH); //set NSS high +} + + +uint32_t getFreqInt() +{ + //get the current set LoRa device frequency, return as long integer + + uint8_t Msb, Mid, Lsb; + uint32_t uinttemp; + float floattemp; + Msb = readRegister(REG_FRMSB); + Mid = readRegister(REG_FRMID); + Lsb = readRegister(REG_FRLSB); + floattemp = ((Msb * 0x10000ul) + (Mid * 0x100ul) + Lsb); + floattemp = ((floattemp * 61.03515625) / 1000000ul); + uinttemp = (uint32_t)(floattemp * 1000000); + return uinttemp; +} + + +void printRegisters(uint16_t Start, uint16_t End) +{ + //prints the contents of SX127x registers to serial monitor + + uint16_t Loopv1, Loopv2, RegData; + + Serial.print(F("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F")); + Serial.println(); + + for (Loopv1 = Start; Loopv1 <= End;) + { + Serial.print(F("0x")); + if (Loopv1 < 0x10) + { + Serial.print(F("0")); + } + Serial.print((Loopv1), HEX); + Serial.print(F(" ")); + for (Loopv2 = 0; Loopv2 <= 15; Loopv2++) + { + RegData = readRegister(Loopv1); + if (RegData < 0x10) + { + Serial.print(F("0")); + } + Serial.print(RegData, HEX); + Serial.print(F(" ")); + Loopv1++; + } + Serial.println(); + } +} + + +void setRfFrequency(uint64_t freq64, int32_t offset) +{ + freq64 = freq64 + offset; + freq64 = ((uint64_t)freq64 << 19) / 32000000; + writeRegister(REG_FRMSB, (uint8_t)(freq64 >> 16)); + writeRegister(REG_FRMID, (uint8_t)(freq64 >> 8)); + writeRegister(REG_FRLSB, (uint8_t)(freq64 >> 0)); +} + + +void resetDevice(uint8_t device) +{ + if (device == DEVICE_SX1272) + { + digitalWrite(NRESET, HIGH); + delay(2); + digitalWrite(NRESET, LOW); + delay(20); + Serial.println(F("SX1272 Selected")); + } + else + { + digitalWrite(NRESET, LOW); + delay(2); + digitalWrite(NRESET, HIGH); + delay(20); + Serial.println(F("SX1276-79 Selected")); + } +} + + +bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinDIO0, int8_t pinDIO1, int8_t pinDIO2, uint8_t device) +{ + pinMode(pinNSS, OUTPUT); + digitalWrite(pinNSS, HIGH); + pinMode(pinNRESET, OUTPUT); + digitalWrite(pinNRESET, LOW); + + if (pinDIO0 >= 0) + { + pinMode( pinDIO0, INPUT); + } + + if (pinDIO1 >= 0) + { + pinMode( pinDIO1, INPUT); + } + + if (pinDIO2 >= 0) + { + pinMode( pinDIO2, INPUT); + } + + resetDevice(device); + + if (checkDevice()) + { + return true; + } + + return false; +} + + +bool checkDevice() +{ + //check there is a device out there, writes a register and reads back + + uint8_t Regdata1, Regdata2; + Regdata1 = readRegister(REG_FRMID); //low byte of frequency setting + writeRegister(REG_FRMID, (Regdata1 + 1)); + Regdata2 = readRegister(REG_FRMID); //read changed value back + writeRegister(REG_FRMID, Regdata1); //restore register to original value + + if (Regdata2 == (Regdata1 + 1)) + { + return true; + } + else + { + return false; + } +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/3_LoRa_Transmitter_ESP32/3_LoRa_Transmitter_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/3_LoRa_Transmitter_ESP32/3_LoRa_Transmitter_ESP32.ino new file mode 100644 index 0000000..ff68d53 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/3_LoRa_Transmitter_ESP32/3_LoRa_Transmitter_ESP32.ino @@ -0,0 +1,193 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a simple LoRa test transmitter for an ESP32 device. A packet containing + ASCII text is sent according to the frequency and LoRa settings specified in the 'Settings.h' file. + The pins to access the SX127X need to be defined in the 'Settings.h' file also. + + The program also has the option of using a logic pin to control the power to the lora and SD card + devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are + powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + + The details of the packet sent and any errors are shown on the Serial Monitor, together with the transmit + power used, the packet length and the CRC of the packet. The matching receive program, '4_LoRa_Receive' + can be used to check the packets are being sent correctly, the frequency and LoRa settings (in Settings.h) + must be the same for the Transmit and Receive program. Sample Serial Monitor output; + + 10dBm Packet> {packet contents*} BytesSent,23 CRC,DAAB TransmitTime,54mS PacketsSent,1 + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the SX127X device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint8_t TXPacketL; +uint32_t TXPacketCount, startmS, endmS; + +uint8_t buff[] = "Hello World 1234567890"; + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on reciver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + startmS = millis(); //start transmit timer + if (LT.transmit(buff, TXPacketL, 5000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + endmS = millis(); //packet sent, note end time + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + uint16_t localCRC; + + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + localCRC = LT.CRCCCITT(buff, TXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); //print CRC of sent packet + Serial.print(F(" TransmitTime,")); + Serial.print(endmS - startmS); //print transmit time of packet + Serial.print(F("mS")); + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + if (VCCPOWER >= 0) + { + pinMode(VCCPOWER, OUTPUT); //For controlling power to external devices + digitalWrite(VCCPOWER, LOW); //VCCOUT on. lora device on + } + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("3_LoRa_Transmitter_ESP32 Starting")); + + //SPI.begin(); //default setup can be used be used + SPI.begin(SCK, MISO, MOSI, NSS); //alternative format for SPI3, VSPI; SPI.begin(SCK,MISO,MOSI,SS) + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); //got to standby mode to configure device + LT.setPacketType(PACKET_TYPE_LORA); //set for LoRa transmissions + LT.setRfFrequency(Frequency, Offset); //set the operating frequency + LT.calibrateImage(0); //run calibration after setting frequency + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); //set LoRa modem parameters + LT.setBufferBaseAddress(0x00, 0x00); //where in the SX buffer packets start, TX and RX + LT.setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); //set packet parameters + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword, LORA_MAC_PRIVATE_SYNCWORD = 0x12, or LORA_MAC_PUBLIC_SYNCWORD = 0x34 + LT.setHighSensitivity(); //set for highest sensitivity at expense of slightly higher LNA current + LT.setDioIrqParams(IRQ_RADIO_ALL, IRQ_TX_DONE, 0, 0); //set for IRQ on RX done + //*************************************************************************************************** + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/3_LoRa_Transmitter_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/3_LoRa_Transmitter_ESP32/Settings.h new file mode 100644 index 0000000..6e1a5d8 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/3_LoRa_Transmitter_ESP32/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of the Tracker boards, the ESP32_Micro_Node, be sure to change +//them to match your own setup. You will also need to connect up the pins for the SPI bus, which on the +//ESP32_Micro_Node are SCK on pin 18, MISO on pin 19 and MOSI on pin 23. Some pins such as DIO1, DIO2 and +//BUZZER may not be in used by this sketch so they do not need to be connected and should be set to -1. + +#define NSS 5 //select pin on LoRa device +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NRESET 27 //reset pin on LoRa device +#define LED1 2 //on board LED, high for on +#define DIO0 35 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define VCCPOWER 14 //pin controls power to external devices + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/4_LoRa_Receiver_ESP32/4_LoRa_Receiver_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/4_LoRa_Receiver_ESP32/4_LoRa_Receiver_ESP32.ino new file mode 100644 index 0000000..0b07e41 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/4_LoRa_Receiver_ESP32/4_LoRa_Receiver_ESP32.ino @@ -0,0 +1,258 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the SX127X need to be defined in the 'Settings.h' file also. + + The program also has the option of using a logic pin to control the power to the lora and SD card + devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are + powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + + There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, + if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. + The LED will flash for each packet received and the buzzer will sound, if fitted. + + Sample serial monitor output; + + 1109s Hello World 1234567890*,CRC,DAAB,RSSI,-61dBm,SNR,9dB,Length,23,Packets,1026,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 1189s PacketError,RSSI,-111dBm,SNR,-12dB,Length,0,Packets,1126,Errors,1,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the SX127X device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } + + delay(250); //gives a longer buzzer and LED flash for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + if (VCCPOWER >= 0) + { + pinMode(VCCPOWER, OUTPUT); //For controlling power to external devices + digitalWrite(VCCPOWER, LOW); //VCCOUT on. lora device on + } + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("4_LoRa_Receiver_ESP32 Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + //SPI.begin(); //default setup can be used be used + SPI.begin(SCK, MISO, MOSI, NSS); //alternative format for SPI3, VSPI; SPI.begin(SCK,MISO,MOSI,SS) + + //SPI beginTranscation is normally part of library routines, but if it is disabled in the library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); //got to standby mode to configure device + LT.setPacketType(PACKET_TYPE_LORA); //set for LoRa transmissions + LT.setRfFrequency(Frequency, Offset); //set the operating frequency + LT.calibrateImage(0); //run calibration after setting frequency + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); //set LoRa modem parameters + LT.setBufferBaseAddress(0x00, 0x00); //where in the SX buffer packets start, TX and RX + LT.setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); //set packet parameters + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword, LORA_MAC_PRIVATE_SYNCWORD = 0x12, or LORA_MAC_PUBLIC_SYNCWORD = 0x34 + LT.setHighSensitivity(); //set for highest sensitivity at expense of slightly higher LNA current + LT.setDioIrqParams(IRQ_RADIO_ALL, IRQ_RX_DONE, 0, 0); //set for IRQ on RX done + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/4_LoRa_Receiver_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/4_LoRa_Receiver_ESP32/Settings.h new file mode 100644 index 0000000..fc231e7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Basics/4_LoRa_Receiver_ESP32/Settings.h @@ -0,0 +1,49 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 15/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of the Tracker boards, the ESP32_Micro_Node, be sure to change +//them to match your own setup. You will also need to connect up the pins for the SPI bus, which on the +//ESP32_Micro_Node are SCK on pin 18, MISO on pin 19 and MOSI on pin 23. Some pins such as DIO1, DIO2 and +//BUZZER may not be in used by this sketch so they do not need to be connected and should be set to -1. + +#define NSS 5 //select pin on LoRa device +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NRESET 27 //reset pin on LoRa device +#define RFBUSY 25 //busy line + +#define LED1 2 //on board LED, high for on +#define DIO0 35 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define BUZZER -1 //pin for buzzer, set to -1 if not used +#define VCCPOWER 14 //pin controls power to external devices + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Diagnostic_and_Test/42_LoRa_Data_Throughput_Transmitter_Test_ESP32/42_LoRa_Data_Throughput_Transmitter_Test_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Diagnostic_and_Test/42_LoRa_Data_Throughput_Transmitter_Test_ESP32/42_LoRa_Data_Throughput_Transmitter_Test_ESP32.ino new file mode 100644 index 0000000..c2ca1cc --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Diagnostic_and_Test/42_LoRa_Data_Throughput_Transmitter_Test_ESP32/42_LoRa_Data_Throughput_Transmitter_Test_ESP32.ino @@ -0,0 +1,277 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 26/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a program that can be used to test the throughput of a LoRa transmitter. + Whilst the various LoRa calculators tell you the on air data rate, in practice the achievable data + rate will be less than that due to the overhead of the software routines to load and send a packet + and internal delays in the LoRa device itself. + + A buffer is filled with characters and that buffer is then transmitted. The total time for a number of + transmissions is recorded and the bit rate calculated. The packet size (1 - 255 bytes) and the number of + packets to send in the test are specified in the 'Settings.h' file, see the 'Setup packet parameters Here !' + section. The setting file also has the lora settings to use. A lower spreading factors and higher + bandwidths will result in higher bitrates. + + There is the option of turning on an a requirement for an acknowledgement from a remote receiver, before + the transmitter sends the next packet, set this; 'const bool waitforACK = true;' definition in the + settings file. + + The results of the test are printed out thus; + + SX1278,434000000hz,SF7,BW500000,CR4:5,LDRO_Off,SyncWord_0x12,IQNormal,Preamble_8 + Total Transmit time 50 packets = 567mS + Average 10 byte packet transmit time = 11.34mS + Bits per packet sent = 80 + Data rate = 7055bps + + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint32_t startmS, endmS, sendtimemS, bitspersecond, bitsPerpacket; +uint32_t TXPacketCount; +float averagePacketTime; +uint8_t packetNumber; +uint8_t RXPacketL; //length of received packet +uint8_t PacketType; //for packet addressing, identifies packet type received +uint32_t packetCheck; +bool loopFail = false; + + +void loop() +{ + uint16_t index, index2; + uint8_t TXBUFFER[TXPacketL + 1]; //create buffer for transmitted packet + loopFail = false; + + Serial.println(F("Start transmit test")); + + startmS = millis(); //start transmit timer + + for (index = 0; index < numberPackets; index++) + { + //fill the buffer + for (index2 = 0; index2 < TXPacketL; index2++) + { + TXBUFFER[index2] = index2; + } + + TXBUFFER[0] = TestPacket; //set first byte to identify this test packet + TXBUFFER[1] = index; //put the index as packet number in second byte of packet + Serial.print(index); //print number of packet sent + + if (waitforACK) + { + packetCheck = ( (uint32_t) TXBUFFER[4] << 24) + ( (uint32_t) TXBUFFER[3] << 16) + ( (uint32_t) TXBUFFER[2] << 8) + (uint32_t) TXBUFFER[1]; + Serial.print(F(",Checkvalue,")); + Serial.print(packetCheck, HEX); + Serial.print(F(",")); + } + + digitalWrite(LED1, HIGH); + + if (LT.transmit(TXBUFFER, TXPacketL, 2000, TXpower, WAIT_TX)) //will return 0 if transmit error + { + digitalWrite(LED1, LOW); + + if (waitforACK) + { + if (!waitAck(packetCheck)) + { + Serial.print(F("NoACKreceived,")); + loopFail = true; + break; + } + } + + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + loopFail = true; + } + + Serial.println(); + + } + + if (!loopFail) + { + endmS = millis(); //all packets sent, note end time + digitalWrite(LED1, LOW); + Serial.println(); + LT.printModemSettings(); + sendtimemS = endmS - startmS; + Serial.println(); + Serial.print(F("Total transmit time ")); + Serial.print(numberPackets); + Serial.print(F(" packets = ")); + Serial.print(sendtimemS); + Serial.println(F("mS")); + + averagePacketTime = (float) (endmS - startmS) / numberPackets; + + if (waitforACK) + { + Serial.print(F("Average ")); + Serial.print(TXPacketL); + Serial.print(F(" byte packet transmit and acknowledge time = ")); + } + else + { + Serial.print(F("Average ")); + Serial.print(TXPacketL); + Serial.print(F(" byte packet transmit time = ")); + } + + Serial.print(averagePacketTime, 2); + Serial.println(F("mS")); + + bitsPerpacket = (uint32_t) (TXPacketL * 8); + Serial.print(F("Bits per packet sent = ")); + Serial.println(bitsPerpacket); + + Serial.print(F("Data rate = ")); + Serial.print((bitsPerpacket / (averagePacketTime / 1000)), 0); + Serial.print(F("bps")); + Serial.println(); + Serial.println(); + delay(10000); //have a delay between loops so we can see result + } + else + { + Serial.println(F("Transmit test failed, trying again")); + Serial.println(); + delay(1000); + } + +} + + +bool waitAck(uint32_t TXnum) +{ + uint32_t RXnum; + uint16_t IRQStatus; + + RXPacketL = LT.receiveSXBuffer(0, 1000, WAIT_RX); //returns 0 if packet error of some sort + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F("RXTimeout,")); + return false; + } + else + { + Serial.print(F("ACKRX,")); + } + + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + RXnum = LT.readUint32(); + RXPacketL = LT.endReadSXBuffer(); + + if ( (PacketType != ACK) || (RXnum != TXnum)) + { + Serial.print(F("NotValidACK,")); + return false; + } + else + { + Serial.print(RXnum, HEX); + Serial.print(F(",OK")); + return true; + } + +} + + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("42_LoRa_Data_Throughput_Transmitter_Test_ESP32 Starting")); + + SPI.begin(); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO1, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Diagnostic_and_Test/42_LoRa_Data_Throughput_Transmitter_Test_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Diagnostic_and_Test/42_LoRa_Data_Throughput_Transmitter_Test_ESP32/Settings.h new file mode 100644 index 0000000..e4b4ef9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Diagnostic_and_Test/42_LoRa_Data_Throughput_Transmitter_Test_ESP32/Settings.h @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 26/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the an ESP32 shield base. + +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NSS 5 //select pin on LoRa device +#define NRESET 27 //reset pin on LoRa device +#define RFBUSY 25 //busy line +#define DIO1 35 //DIO1 pin on LoRa device, used for RX and TX done + +#define LED1 2 //on board LED, high for on + + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_500; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 14; //LoRa transmit power in dBm + + +//******* Setup packet parameters Here ! *************** +const uint8_t numberPackets = 100; //number of packets to send in transmit loop +const uint8_t TXPacketL = 255; //length of packet to send +const bool waitforACK = false; //set to true to have transmit wait for ack before continuing + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Pictures/ESP32_Bare_Bones_Schematic.jpg b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Pictures/ESP32_Bare_Bones_Schematic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19b2873de7f359235804304b780c2826347e98c1 GIT binary patch literal 38883 zcmeEubwE|!w(q795Trp;T2e}y4Js`l-Jo=LBWyxZ8a5>%-6_%y(jZ;Z-QB(STYl%< zbM(9SJLkUp-uv$%Q}$+$HP;+-j{J?Wfd7Cmf*#09%1D9`5D-AGfqx+Q4Cpxs6&VE; z1sN3;1r-eq6&>RN2FAU67zEfjSPzH^NJxkYh=?9i&{I7mqa`OIqGqM0Wng^F{Fs!A z?Fk#x6MCk{Og|?VW{R7E&OLZAU5;vyj7BEZ{0 zR3H!n3b5Lr3;z2T0wNNyUNrQ37?=P-rEO$vVrph?Vd>=T;_Bw^;TiDmec*?WLBY`InAo`ZPYH<`nOWI6xq0~o zU&|{htEy{i>%O(McXW1j_w2GZPgR?*Jg$wY7h>VPcjP{c+1VlGrAmJjTJmN&f6IVieSV#!K;UVFIz@Y1#V5T(0|MBoY z-zQ^nO<4=>MAv=KDnkdHK2(NUjk{!PNzypU1H#dFBb;KoYkQOA=Dd*&vKZDH-BEwV zYAW@Kl%Pi3?vZ`kV%e)&rG2r?8#_gA)@O^*nI}7B1XoAZ8=doxoP(aA;f!D=(Epv0 zTWD}!bY9@4sr-o)HbN&~jV_Ed4J*uNd0NDFn&nP>cdx%99KH6*S{u6{ZnVGqdfrxI zh_1obP=6lRHkr~!xYjdJw!0FQt1VeBWu~8cSIBEYWqmR;b`D{YXSl z`%;Or8?*3b?$=si`sX`DK3!Qu&SQ99|Gs5w;(d6Zx-p*l8>F{N(t_4hLcn2?@vBU8_b0g=~U(S-X%I zao+UHX^PT~J6~qhjUI%)6R8*W=c=6vTtpR)t~jTiBzVFeX4^jSr2?Au&Xz`+PAoIo zmR?MEkAHwWxZ2F={#ub6(bs#AB5kE8&`ShtD+DU#gvvIivykZwc|LYhmBwM%T7Pbd z%tg!94W2F^Kdq+_9T2jZH>Xs>ctY;)F*_$}@X2YP3CZQHgQ2C7^NoJqIWpE?|@@XE+Q5wI+-foZ4BZ*)cnMF z#3C`qQFx*U`)Y6G9&V}e9A&2=M(?Y*ft6%G)#$dwNQZSff|9rXW_(=iq=>7Z{P%p4 zjmMLEOoA5-_}uuo(JAyFkq6S;6R$+jeZVDFfG6jXv#lN%&lfGXMn~r4*t%}!NL~=> z=GTSBPx&E{XmMaZc)dZuZGDF?>bd)c>%1(#X-xNX+t)LGNPV^euSu?~veN{AFl7XT z{!a&UNu~+K?LC6;+xg70Paj|_1>m&Ne7Th~!%klo!JgjvZcfTh8bxp1gTVPY%X4gR z={{jhq0WFf33$s1a^#ZAlVrixT+;dW*e0v$1P=0j;~s_@Tf7

5~d^d;Fzlj=uPt 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 literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Pictures/ESP32_Micro_Node.jpg b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Pictures/ESP32_Micro_Node.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ff1845912bdda979f9f7f2cd3a3f57dd28a1b6c4 GIT binary patch literal 89760 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 aBlbjgw5AMM081{5p+Rot3BW>4AOG1u9^F0w literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Pictures/ESP32_Shield.jpg b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Pictures/ESP32_Shield.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1ad823bae4e34c48c6bb8ef3c982507b2bd37deb GIT binary patch literal 214109 zcmeFXbyOWq*C*Pz6Fj&Cmk`{YAP08~?(UutJVI~{?i}0+PLSa4!4vG@5F7%@G*9w= z-@CqA#q|KaNoc|oD)m(h6+%;`oJgMab zfd^&(;rv%E2T@j|=3?jMVCMt4IJh{3I0S?^d8s+M!J`lt2S5d+h5XS`0aPaQFV+At z3JYViT~3t64)QwUrY>Q^uIi%AkV9K6yO{M0BS+}2E-3DQUs0* zkn>khP7V&Zoc|m@_?$oE7XTW8@E2Pm004I0AAKspwvYbePaww0`_mQ_4?O-i4CJHy zV++_6fGhaJ1401)-!Q_zVZ?$z@;gA8$p1!vX!Ec>EWyGAmIu}U%npwb2M;wkHQanc zT)h87+CP1R{Ig)8220T28mxea|4ja#?~nt)4oD#RhyJiqH^DOmFh9)X35baS&_3{Y z{SO!X0|Yq1LJpz_%mEe(5Yd43H8>rVAVvc*DJYBz#1E^T4#ZDDTmqU?4PsglKgbuL z<^bqGoCRV7aC+!LOabC+5Ho`KF^GvkVN4+A2QfQ{pMv;b{W5@hKJ;%5(osQ)52N)x z?GbI!J$V4@e@#{ z`NK5)PZ9_9|IPnD9sz)xi<%Q$0$hLde>W81mBYu+FTlkEE?Q1*fq$5JsKKk?|7-?$ zIQjn30IVLA;-u!|;h^Rb_;QwhLa!kHh8JQA14=h0rButgBu(NH#HZy(eZ)=5OVVK zQ1gM1i&}siYzhFT;N2VzyhrCeYQnGtcS*eeh5T^i4h8~$|K||^N`IGv=MPEepZu_k zJS15=cPAe+cPnZgD|Zh&7iVftb`B1(&mD3WxNiVNSx)HRy((%2H5s`-4Z)-7KYI}Z z@Zg_3>>Yo0@_$bLIXnV)69fQR0`wXK0l>9lhkOUY1X%eGh6f!UO3tI4NB<)J4t(H! zNWQ>x;Out}unGkIv%~+P{?{WM@C+mc0SkZsxc#pT|2jt#1!%#}I{TgiOCar`$A10M8DL3aRP!vKASzg{Z1tpmvah6(@T2R=CXKXSN0e)zw5g%tb) zOsN0l-~aAUKbQ{o_zny+} z0=TF^7w8Vafo=>g90D%f?=PTt1Hd7H-v7e`z~2PAwTMW_D5z-Y7$8Ff&cjF{z#}3c zAt8cm050Ic?M1{z!lU7oM8;P)LwVvxz!e;qi%Kh1(?zH;c|yl+?jC}MPV|_Vgp{6v zk%^gwhnJ6EKu}2fxs0rwJVZfLOIt@*Pv5}8(#qP#*3RC;)63h(*UvvREd0&eckd(O z6B3h>Q$D7q<>eO?7D0^z8iN^6L8L_Q9?PJO7pb81`T6!UgSuM?^$GM0v0a4&DbW2)KwyG@QtIlIkdC zZun2Qf>8;i;&N-c&}g|eP6*B2C(()Mc)rvBcrfiB%l_943;BO#*?$=JpLQ(*m=WT)#3U<3KIl9W*2(*hFr2LXw4exvq97l*JQQxY^+rs%G zOr_jR`4n6oOJIyGif1yoBl}NXY);S!znn79%(gcZ+5QMuh{Etul`yl^zGm0k#r0ba zIno6WT#2lQ;ZfYW&J25a+a#j3(SX*b&b8onx{kNGhN_mhh}t8VG72O44~Z_GH_r+g z_Z9500Crbj3TBT{#&$r1IOm6AbYiw9sP{ADdU$Sm71140B;eUp^qchQLiRvXfXhR{ z?RwgF6cET(u%pIihOEa25xv>%--2YI8vsH9w#TxrOrrqPy`3!F7<048nZ!nd1oi+= zXsFzVVwx!p`47b`W^pz(TR61aGi4FaeFas5u&jDi%cx$r)n_(*dWTKACN#t%y7cRD z=_r@mK|2<({2WGD;t8CmgFXH`m(ccHw6$|EHQN{5E0Sj>BZ1&4D%@XWHz++F#N2GWl!o)H(psD8EJa|t}x81V(LmEl}-s7~#W zjVvv1!fYJk#-?`ySVZsK?Y`S_eKjA!c<)u&X6Lqorgjj;odkbZ}D@Z0VS(`Wi`>6WZWNqqmNS_TZX5BhVi5b z;fj1C(tkTeVDvdzgwb^lnw>|l6y5P%iL{#p9)5oXqmp@gnsHwcL7K|KdYUppXS8%z zrNx(gudvX?76>-C;jgyz3VPyS_>d5R0pbr5ou|+z?Xrs!Cw)EFiKM4Xn&~zM=T3O4 z{POkApIg+kp@bF630%-t-eB5xq10W4N9~sO0&3z4Lcx%!;BbZ`X2>}CjM7(QJGhvN zJrnatvD3D19y7BP!p5<`eu>7{Zg~%=Oa%N;yDq7Nhs!voK>gLA*i&5|`O6v8561qu z-BZ(mcBBk>iAsPYr7Vcy>CO5?2S+tS*8O6K#=L7-2pS3Xk1h>wkjMna_A*h_hnC}b z>hE9N^^@;it^^TM<9yoV<^l!)%-v zicnCBH8@z&_jQ}!tom@_d^!ApBEXyNVC}@nHRvv>bt`h$8OzeRKF z8@#-E&Dcm{iWubXb|>1aG$p^+HRPVOWK-xo{v<@zK9Z9*!f+0ylrCt4_iY(w=SB@- zJ&d-((q5mFmx0GM*@z-wzwP8pdcEG;E5&E~hV+fY9$c{NXx7@3^zA;|vnwm{3WsOG z#Ql&uDES5SC3=*ySS=q>*x?ZobC;!w%q^oDkolf*OKp99H!JEu?`epFh3QbZ| zuY;$16R(|KXSNK{9$K0#{m{mbGTkTw4Ei$ppTx`<_3*wIy+$eC(=B|6@p2*=X%%#0 z0he2i+ACMu=>k*+Z>nnY(jW@*dTeGgjbZW!wUt*lvXbZH{;xi{*5l#RJR{Bcc6AQz zCvViPkzv+aZI*Fq5kAxRUVM5!Uxm@;7LEzA-iXk2SJqn$$rTvV_SwC{*+onrE&_t| zD1!opc4s>bby@SPZnfu!&@j0q7^%J+ab-M%uPK|DdutL$F?qw930XDL@nT=X<_nQU znQAdo{oI|`F_HkHaA~a`?#9y?yiQ7r~t3A&`0lGLs%)c!!tD!X#c_+hltVnNV`a#4B{Cs+8P5xZ&aGQCx58?A9km`>ThgYn};- z3k+JhX4|3!vNSuMy#BnQ67fAV+K|-}=#~HWNL}0c$YgL)jiU2(B@n&g`}JIa3hJh| z<-kx}tb;bgk}RPaaUXe%ZZl^50H!piFA2%M&abHg*(x$jpztdmGLk>)rv{_^gCAzT z#FG6}nQUqb==Dr@fjG#eoS*tU+Sc<{`_tOLH@3c6pUFu=K32jL-e@-tZy;z)&{7P4 zBs5@T;(i4~q*R?N_(4%o_$i+*PMS2Sk5)C2QnXA^OVO#Vl*Tcr^@kE{%PlDqi(~y| zRDb41NoAD3SEYx7%bF2OJl9I}@_m3qj&ahcZtjlRRUl0Y$`+R=}Md)9)F zcW((t4LU{*%teJZZv1Dam&@h4?J$!sL{BepZgw2xrr5%>0tf8Q=ZT!Goc)y$*kZ*pFn3yd%C}v49Z=}R{Hj)q zK~~G+vr4_eErsXW(SX_XcBsM|TpS4DuwNKCtcmAj9}P`pfQqC7BbCvX1b#E$T`1#r zz4=3WN%@5;@8o-8ajsjfL8hOBkClg7FEx|$oLcjl^*-{gslv1lJd@HjqqiYGPvTB* zGcY|5b$C{!4+lMs`20ThA-mhJL_eQoHZ=9@6DLhS>pLi&`TnLfU%_y2$t1NctAS6n zxj5E;rsur;(Q}7i4cUHIShnH~>MbOi3es{2B~dNSVmUflA>Vnw1}R6sVe~hQQzpotm_W3gwL|MS zs9yFe1)We7vUUE^P18mhN6Yiy0D4WH^arx{1|%6cfNh^t_GL5+^qq=}Ecca)tkBu+ zYSm84rwO}L$tO(7Qmqt8zPoG1l}#^7V6CS+@=nf(Pi&$$ES%AMRi-%nN#{>yoYTJ0 z1Z!q#MFwUi5njIu;CvF#@J1{4I3R*6fP21BCU+P)sg&)kl}&%>n2KN8z&0{zCEcc| z&diSmIF{!qg|O<;_8G9++&=?6hSDDQr4jG@{RX1bd}J0i#{j{hjOa`x_uBF@ zoS$7UME7&%c!?Vn8{?<+vWRHTqJ066)!M2zC3Iq*m#fO<%e038|7&sV3;vpnlC#0q z#eu#lcbjHnMyz?VnqP#IOzX{~OayJsx`sGkVbOsVohv>_>Q;r``Y zb$@DqS;hc+FAbj#M_fLIp`*4QWu{!HN z!=g<99$p=kBlViB^9XS)0(=wR@=ZWaTpF3#FyVMwRIOzIgrQtOnIVb;;|hbN)OS z#h!Cs-iM=^{AiDe&hs}N_L&3U;&k<{N)t}8?YQD#znH_?PLe3z6_*#qF@AbRLPn<< zx0W{bgXO!0*}^_Qoou8~2$r3yUL9#R?jEW~WJEeI;?oXZ7d0#;XtV>j`bMbYU;*NZ zvVeyY>QbRjEL?)lN6WM-uDb%#Sb=A_Hizlef*JxM7xygVZyJomAXJp6DR74EopHrG#dPEFar$tPtkJrUq^{RXK}@pOeb%RD?|Yhi zQd>j{a40II<5_H!O|lyaXOMz`?!b9@?DS=W{E?^ zchT1kau&Y%1(iIAG9@yPCnEKjR34_{=Z`;j{`PVB1Dzri8YF~-dB*-+FF9^%uy(cVv#EbmF z>{@VNr6@Frdn(a}2UB`*UtLK|!@i4h*5KRggN&K?snOo5j_74akwN5^><}2?!hJ%Z zUSfzOUGR7y%rGeo*~ogDixnU|In3g>AP2pTbMmNA;!%IKucyrX7BuDj%nXQhM}jOu zY3moQ3`XktZ`kC}-}P~zRH^p06WMTn8ELafSc|@ZQPQsRzO2e5Nt4BD{Ir&GtF9@N z13THT9-!x73WDIvL(uc%nG+ySzaWLV#g@C%8peuauk2@Qsk_UfnXP^z@>{R}T8o~35W{? zbl<&c5TOXbirU&xegYkA-9D#K79w(==_fpin7=l_1bc|-;I+=lWW^(OIa-?BNUTQIe8*~JC{!r zAoclL)(X_zVmDM^hw~&my(DmNXu-VsNPJh9))zwGoXoYxE@q0>>qE*8b#VFCOr884 zK}0E7#Uopxcp-f{pF~bm=ur!Fv3w(<%+Uic07-wa*R5{(O^uY8n>NFyd3jASUwQP9 zCA!qnc5a_tb`Lymyq3T-=pk|6JqNgte=lDiuYI?!kp(6r*aW z%1_?-aE}VQO~aSReaW~ewl>V&XiyGL zQxTqt?DBAzy!mVFYt|9Lg$*L}p~S7K-PsvWVVRd}E_g_Y)^OGB=EA-pL!>+t*roJQPUS^=s;Fyn?LU2|w`@vgzxd zf6^GneV=TUmAY%>lH9bzT*P}$c_B9SyqrRK?Lv^m$MU zxoJA`g+qzTy4WLw-jY${5A^&2QP zSpG>YvYvj)xvQFHH1fJc`bJ6l^wEuZ)156I)TnTK;{qI z@@_v13s-1<1IZ3Ao(%YF+`sczA7%f3p+Gfc0l>=5D_VYG-taut&US205Q0XzKGJDw!I&i@!yf;|iO*e}_$J??AF9GRSKQA*+K_IU zWe$XV{rUCwwbv>wk%&(P$5*dA#deifDl~UQU$@g%%5#hchKV=mC|RFqW4Q5g#((=| z-qP;HZ|9ClZsi}~5dZ4f+#|l!HISuGPj8|)?PXd&&c4*BBh#+R$~CDetX;U|+gzt& z=eKFO3W1Ha2-QM)w5|=2sf{N=%tF{qaQ+^0&c1nxr`&0PlIdkWpe86d%I=qA=_tsH zA+ttkgHXXR@p1xDKBN#~F2#u{2XT>8nKN`Tdny5;9pI4?R-+-fTaT7AnG43fF^^$( z)4-&xe`Jp=x5W;?C-!k>SQlLJe2#*r!L7HUFbL_z*u2LJbsz^X|?c2c6Z(jf~erFK^}J&tK7u}=J+PJH4)t$ zT0qv}y%XVaMY8;8ivseTouIh-ale7?EN^-2JjY#|8}V;|bT`+4GvssXpg|&(b$Opx zEx~TO%(TQ32J|{Sj@c!%l{V$7V4W`{F8J>^v08Lz&o#?bek~ex{5- zTglv+-4}!jYv*j*Ro%SoEXo4s3a?W3<($(D-67#AIUdjNs!(}HFaZ=;yta8k*iA?BmU#6$$dcm#w5|3tD7z&40T zXh=wC1XvhY1pnLVcQ+WxLa;;(K!5{-IRA=dfgb_E{}b~0R~!qB=Ky~qSztVf>|e2< z{|IwEgtZ>xT)+PYRd5h+!06Vwzcz!FRoWAk>ZLHXATwE3b;Uh&oN9bRmM^h$TXZ?K{;s~_^VS*+$DXs^q zj{`x(t5_u*$5<-~yX~S;i%A3~Wu|NOGd*Id(cZL_C~BP-g%q*E4GJsNi+BOlP4qI~ zCQy-SSSf8e2+cLjyL$17Lgm?lW=wHH!X$@f0AvEQAcl7yH~|wR-N?nBUuGBx5YXq? zz7{FhLz-l|iFW0wdoXC^Xmn874fS*4B+wRQhafMS*5_~uG@?Rd--iw1ITa$nas1@= zqVq8*YFl{2hR=(T$8IsI+?OZ+_PI4vbJ2$n2)Q&xEM3H*wy4ausQqaMotUKTpxk%t z#EIs$UC)Vw3Fz~}q)-Lq*@HZ&ee@vRqV`cCx^J*!sVTzByybV_H=7v~q?w{}n(3WC zc^||u8UjgdV1YCwHv94In3g_?PPC)TFi3WcA|QUklWfnRGQ?n4LIOp{8`Pn7#D3=n zkb-Y@QJbr+IShhXQBkPj8Jy}X-Gcfbd*XRcY{NU_SqFRh$zoxn4$3`;P~@q;bz2!{j4jdu>iw6R~a>hicYRQ)yz*(z=q%Mh#c zlPgUbQU|k5;bT%`kigYzzR;@UQtPYHNsD)ls*Q4*Nm!-PGz9QjzEl>(ClQ{FsVd!| zn20ORcO6spP9GQtYR}GcQQ<$5EVZu3vCjD!BCVCbcTzYK6*TK@rX14KnMpz&$h1Rh@9ZaH_u&s zz8oIJz>0=El|mRXM@y~HM@~}6?Ip{KDuJ`}C_?&e&PcE=K1Zpu9u_*ACcOd5puf&6 z+m|QmDiS(q1y(%~n7k~g99^jCL8;ZIp|Xnd$`G-wClhgOU7vbi@Jf1kup%Ch3%Q@J z2wdD72ejKH^;Av03b^-Zh}5T;5_80|L{>{U3YzJ%lv2ie##um7&Oq&Yu7tx?yD~?l zDBH!-6IIM42VT&uaL5RzwFHfTP(hv7w6Q~6P?h(T89enjhWDsSG`4U#3{P>xs>|K0 zBU$AaL)M2_iFA%wq5)p?6RjmSfDn^oa~Zsjd20F8wAL&n+l zJSok*Tu(O-SI&;bjJ;f)A?QGAh^>f*CwTPvr^8TAgjTe7OofH!J4EP898wh2P3D|K z+Gui!BN&EG)SOHs@#&xFxq^8V&P!jVBeN2yzoS@SuEp?ro;JV{>Ii#g&+EamT;!0= zJCYJoa%OaQ|45bMibQY?dg^^E>piJ8@oKh&q|tPy%yK^hozFg^KdugrFG*R36Ce<7vu%>WD+8@X&ZQEIk>B< zi{x~0OXCvH-)ociv-Z}jg-AH{s++2t&yhn?>(M@HErm&8getnpqZ%x3u|CBKnG=~R zLY=}z0dF>NIecaalEhm4Xz)OIB=Y;Gz?1J`%@S%DSsqBS9jK{&~V~@z>y4vo2H_J6fP2I#&SMN+Hr!hf}YoC`k(5 zU87`iHuK|%V28+$_O{lS?z^|XI;Cw!9fPp*y_LHaH5v+{7-iF}*>PEY5akkkKr*MG!MO^{g4GrmA%KV??1~;6U8~&R~$ck`-53PzVDEdJ>@( zsxm1jV?QkXu9s|c9DnD`_7Ha9@5j>oOJ&XW?ACME){pFEv+q z8k9(-%SWHSlwy!!CCE<65x0=0oDWT&>AKoHR}smah5l{c03XV8!>Obdn$aK&w3cp; zxY)Q7DNZDEq}F%HT_<^%$^#`_kd}Pp4+4-Dp0D8{AJWm9I#CMu(ns1Mx3wgBMTth`_WwBEtw$APOC=E68W6t6xM+~NJWH0#z zO}vqcYL?_ARJcc#p1 zS@7I~3lrYS%5YOhRY#gdggfPJ4i$Ar^s{Exh9@NlA)rB(^>r8;^l0FFQM(qIm2kPO zuuZ9@bo6-fd4t1g6{IClz^{qAF>>Wb9C0Oc_)wFSPz}=3I;X>vi0tGSSk5~w>`oH7 zgRqSF;+{gnXq42rKZRxs!&DKLBD^;i&TlGrV16wD7`;0eKWE=+Msw(O*GEXE8f0@| zCFYvZ6Zle(XtR<(HLb1bBQYgBl6ecOMAR-wcv4 zVD=!_#;L8Hz}6xcB@Du{XSeuf->ITi^9swZzJDDA^I$|ak6gO^t4V&b;3d! zdjlUpq;w;2AlCniLu9Q()M<_zC6mTW%ll@Qt3grYtJfu}3 zG^WzD>~hc$LQ&3GuZg;zjYJndww54It&>Cq2mCR(@g0M?x2Ummy1WTKHnTIj5X&mE9M-V+jg6YilKAI>+Jj()hdxCKal{j z>7L0lXJrE+EAuTqWeFVa$Rd{6^jMOT55)P_IV062Rg%FpMHqTj9N^;(JW&pp3~Se9 zIhPLZzUe|SRwtU|Nnu)~QW3s4y&am+EHB5nWNKd2$FLzY9$AK>A&-S3EId0Oa&=^g zeZv48-j@|Ftf0C_GzGmO*hJKaK;n)dJA-=lL}yUWa(zi`r2=|klHP#j&{kRRJUi%! z%6(?f_SHFi)!y+Lb~vN19o|ImUoBhB=!j$i*(WeW%lDPd(6BCX0HSI`;UPu1u}+=r zcEoBr5(njFvmr;RPt6D`F*~v5kkOEllNfnERd1nx6xr7bpQlZ*vK-Y|F6}8K2b4q1 zu?(8kgGdFm-XzCkt804ba#$p?DO(TA{9GTnC)*z5<2+m%}8Z`}$Sqn>{dPIfG^cgD3G6;f~DG>+U| z*2{48N+z;j4qE(}ZKAscGxg#X-$M!a1omtm(*8@k)gYuAC7=o2|s{RHW$}h)+_W zD&r<{1J~!9v-f0G&y$neI=zX+w9qbi`Mr{8Tv|6%*rt;7Y=2w^+uKG< z!I9dd{QE`RPV47B63iUc5AE_;cR6r8V{!MG7y4>_rZ6DK! z6ki@RJ^1L3!MRm4=N;kvq{-y&>234&&G`;Y%uA5$l-VyUb1akVkUW#a|HTW#ri|-1 z4wdb!7xUwl5nEaTn1Zjie5U!cvtD_P)1|T1j5~Zbf&ChR$>l}!Qpcn9@810eo=x7g z)CT>WUeKL6)8E{iFVlggq+WRK{#gFyQ&*M0ZOmTQv71fxiZx4Uvx^_P=UHoM?3x^W&O&brqV>>?pik4c@{bzDU1kMtBa*^WB}kqsoOgCIePMNm zlrOX`_#I}GEJfY>c?FPrc|`}ZcVzc2?Qrfi9XgbLdF~gnQ zIdD@)lD5D9{0-pt!j9H`e*==!7k8^Efk`@7-}@(rE+`x;W{ug~u6xp^S8hegi$|h` z8Yi82>fWjJ7uMk{A5lfm+Aqv127n`woJs#MY!YH06;GK{Il~d?mQP&8xzeTn}nsyE0?x!N|&rvv9Rs)808f~zh9h| zf^1?=Tl?YujGl9}{AvA2P@3mzS>jj3Xg_XB?>JMwtsVc+VVP)D@TmEhg&<*R^> zRLT!z-V7D_rkHSrdQ5T&%pRa4^xRNSQnHC5#VWKM#{cS-s?SE7%Ii3b8UC1p!yi?Z zxN8{ovlo2L7xq`{k$qn$*$Ah-Z5ITSt!zei&-XjC`7+K$bcLGFphegL+~?cV!pCn? zP@=w_{uFszH|uq6I4CtkI^YDYhG=EvFA`Rtz7 z&Bk=g4dnX)>+~kMsF-)9-_59hWuR*R)lHM&?a!Cv1H&=HJFbG4w@~OApGzvA{Q1&+ zc?M*}Eh4{3tJ#=){F1Z3ihJ^E%&00lN>4%$i%E(VHMO4LP|G}`iyi-!oXXR+x4M)4 zCXnH$l+RS>n!G!9-#Em>_#{W9yh z5XGJxgROnNyuNqN7U0!_G_UMWPaQ*3`-_sfV@yPiUpG08-H> zKq%FsN<~-P>0Ydg!T0>CcwoS}Y(rJ-{`_0N*y^Hmrl_b%_Ws7zYZvUBb0yy~aY@QH zm0vI@tjh1rAiTVV%wsQYzM*Efo$r3XDvi#ycCWB08srN1Fo+Q+30%3*()DF$k!>q2 zn+{J_1fts>7DlT_k-z5p@ue6=RYzEJq>dp$kTL}y5)qFlPZ0xU5VQU#LKft>Mw~K2 z^1Yi?pS7E{jI(>nqttlT)@hi}?cF{8RLdsvnAs<*xMvXPGiC>mc{0BX1DH+)i>_vN zUF!Bvrrp8AjdSg9MQuD#tufal_vW8>+2Un~FJEP~`QE-3pE-!0D`|w|urn-EQ31yR z29ASB!(3zj;OS4E`6?o#S&YQ+dqyhOU9T}4(b*})!&m#QTj!VefmxrS5m_!&;;Lf) z*EL7C;?OgHN=UV}$|knLgcbO)S3|3-@6}+oVrKt*&)9Du_q0Xime%7({R^q_WP~dIOx?}{8*Z@%k0@ayfop5ny1C2b(PbxA@042gh(OL@|3WuDBrw; zC;XIm-^LUJ!=| zPr|8nJ?2X--RCw>t#oWw_>n2_eIK^xvoUqZGk4aODb@%(zwpS1TtU1{iGwqPbFkq! z@tya|xOl825#GVU&nO#6$IZk1#%=m<#?uea_g!ahJ62Vt=il^gXY=W1S9rqGw&utD zUN@>%rPuA;lu+qL?tgONJDW`u^5b(!nAKG=d1ew-8Pz(GA}g?b!=tN&J3r+2dTcBs zKpeW|wz0A$5b!K;=O!TY%=5L+j%Uk43OAv`N8RR3!P`3)(QNprbF*w{ej|nEZg(q? zsB-0ukZoL+AoQbkKPxSr(a&pxMLtHne>Uo{zvHvX{D!AbXI*c`SPl|HUfjI!e9vhB zX6st2SjJek9n3i7rP6=`2`Qdg*bJ*W(@1`%l`~1Dj7{wBc*^`+xkk$I*M4N{O$L?A@Q=8Qiad+DwZVfH zx6z@jYdx5enN8OBS0>Nqmge952INM!VAJE{Wf(<$M_I~#n}_qt>iZ{zQ=_YGEo87S zH<@T#!!a(YX90)&Jr%n*TB>Ei{Y$%|RKj&x&W&UD>`5=u77L}U(9djD0#&=K7EZY2 zS|2?tESq1yS1S%l*C1%=i9{s}}IfWbwjn4OYvua>4M52J^b*vzwO(O~s?bP>hs(aaE~fRn_@T z&sCa?A$>skh5@%E}OPlfaCdz`M9P zSW(lktWaaiUra9H!uPIGysR`Y$@8V3@UggNE~P*2Qb*&LAB^LPGX2IV&rF0<@->s} ztv!X@_Rlwr+zc9;?i?IH}{Tb-fdL5vzdBs zGTkJck|u%gw_eUiZkz|e0#nMM=`Jbf{Yy7qe(j7ZOZZ(j&r%uSEmRe%gxo}a1Dbt5 zu5Q^o*rPl)-S<=15YI2Os$%AM&x#=O=6sn45WyDr_EJ_T?eK-G@u~0a_07^gch*vN z;hv;}6S}0*QsF)hq@IP9+uAHEqiu~_6ZZ1v(&zise!mQd`)BE`<8%3Qy%*^i{5n=w zl2+z1)I=>Z4#?g$;VfU+aC9Wylv%sU1LbvlF=1)69kFM9mv#GQX)+qPv-H+4r z`1IC@|*Y8R5`Qd){h(x;*f z9a~oA|NK~`ZPjQAs_Hj%|C#Ffi1}g;m!ZxZu`Ma<Uov-C7cywr=&H)`rvwW3tn3I~H(ZNlXYFo} zZ{IezQwdM{i`keoW{+)paZj(@e4cDd-=3X@?QPs<~H z3la6!O4wm;Yd&(x+`1JI+_^HY%Fg@s`l^*|dnZeftL2#}r0<+z0*%nx=Pct_Ox(uN z<*yh<-KrEi?~QSl=U3-OA{4?mQ+~Dft3P;4VWNs7nP(y`hXZE;l&}5$CRZC(Uyr}+ zpSXz@%9-VGMeu_ANLpH?x8Xi&I(n9AGw5|{@-RZSy96FPuQJ7Y`X{_AN9%bs6gsDX z;7n=<2A*hLx!4yS)+hF*cFS%HrJYSJl|>FcBJO zl=4^bYUSQQ{9Xn-z2FZ$9#{Rcws}iQJ9%oCHFr@3aUOkjMCa#F_4;$lJ#EbRV)}4d zX0wUzO(U00>hSFDJ(aNakAu7Mi6zme6l!Q1MV2nC(9qFa#^B6$e$~f5jl;!D86GO< zS^f?j!cWo_lzKe_E>3Lb`!^0Q0@_+z`~=UIJ`7K9H~F^*=EH6&vpI{Tc& zpcv*S?ze_2lv$cn{Ls3TiR;^^PGJjpygG@NJII?(9$raf^jy7`Loh4XOOdzv<2EXy z>~|MlFs1WR*zV;seyFnnzm&?}`eKq3y!?v*D_}Uym@?9;Z~Tyc9E0@ZG!loI5MZMj?+lr?D(2 z4hR;$q; zTGA>`=JjxI`bo(;345L4A8@n3Ki0e(Gd{gf>3pBjU{n&Yz|b68A|<7Q#DRAmyaHqz zB-)~)bD%FM3UrK*U+|uA)Aht=qL}SlBrw`=+(;i5)R)Ggevi zxOLCZuXR3C%xk3MR&1LsKVh;k5v)@o@@t;p%45Vtnz(>$h57 zv`%bAqgEvP(Jq16+x{MBukJy|Zsg$VW-K~19DZxN)GsuA*`P2h1?_b-Cg=#Ktn3F+ z8dsT!^JhDYi`_W~v_A`cRJeQQkyDuevE(EDd#5r5qnqVlG5Bw*-ins3u4ZxX-Q5G# zwPB`jyy6>jthHGd6pMV#(|XeTMdb2Fg-*ag$zgM48DlzhS|l@oti2-G0xwdbKa320 zN=lvHvCxhc&&nXYzPt)mZD?Wa@>gJ=Nq&l0c;Qb{p__K7ir9N&bAk1R^X2>Hz(+g& z9mgN4pu=Zpn-e>Gu<=Y*UiVwA<7^Za4_Anfzt=R8^5}TRJ-z?w$*tJ446W4_f6*8h zlu?0sR?!Y<+44!Eu;z{ z*_KX}vqyDAv)$fg+#6+O_#CH*voFi<%laJOimbvW*Edh%UUb#~cZ4YW|0>cU`>9f=TrN3v-3!hyMuxm>@kuIuuQ(U>AeRn&+ft^Bof zw#=#5g~}!8TL-+bq$qeLD88R&>(s{H*_!wTNdOi1H24ldyf34D2|8X0`Edv{ROLThZPh*(M=A5&j@D0K z+TR~0FCNRN_~ApjrawOY-CpEqtj4xIOP+Ss4?WfS{V#Zho zk(jhCfrJ2PM3UjDV=J1katnkOl}KuoaXtS4o$^nAJ=v3Lc2|qF+dang;5b)v=p(<7 z@%=NtpI7Sm9BcC1ov!fxeDm_I=e+v;W_H$>*DC z(A?zFY*lCkYQ&r`hl-%llc+vjrg{o~(6_kIh| z_vNO!^COj2BYIa3A1)mVuCn~(&MN8YT{T4f;(eSyH_-YQv*h0Ar{;b8=dGo>u0cpq ziq2ne2c7L4cYj;c=j`qB>q}%b%wQWEY@1@p5^3IKF>$ zSJXb2$ZlUx#eBcNzn8Y}Pr&PaKghGrRdEVk7I%rh&L@}M&E@R5KVRqU=&71VE!4Sr z#88#3S|)8+Tq#WMIjotybH#S!kFTG1c-`BR?quWFyX5Wk`Rp>T9)PoPfrT4%l0bYY z+o3Mh4rCX0xh{~cid4+$;QRjoJLLKMd5>O>jhoB6&%d_o*Pqwra(Z&UZ)YEVjN|1y z_%`ni3cGvJ$GtVjOkX3<`ZaXvd%q>v-gxWJk~6hk(>c{S$>-zCU8y>?+uHN?eBr{h zai{2WC)w*ahi+C|A0C|_ zCr_-;XU>qkB;2q-7gkm@(vOEK-LWyXhDM8pE38>k)D)&;mGnP1;&Hh#Vt9-AUeRS{a@hh2)#WhiVb2$#@lB=TiGtTnI zJ~o-n)3(Q_)aRqH>EiP|OmyS&_j?aV%g~O8&ineKuoz-)=ap>A+!y2X-i~~}o8GTC zORvj$;t~sGYH{t9=~hLV+SY5g61|+R9^Tb?{TE-I9lZYl9P0Gr-0d9cmza5zj9cid zm#yoCuU#|2tDAQ5v=s7hxN9{cEjn3EADD6no;W@ld4sfSxmA`bMtW? zN2e|?Z$7@!(e2LaoSg2@FQe>H(#*Hh4JkVP52#7U00II8Owf^<^s5X}IB8o{psKie z&sXPsp8ZVD+2*$F^f>*#uGRI+N3Zi&M{YNp^xa;5oZhd*S=Ym-IZEhrr%HVrw)yo- zPRy^^8MraaM=j>Z*-X?_ zUF_E!?scg>3E}6TI@#P4>+%!!ZN2RIKTp)3q337i`&{?o_kTU*bn(~2)!FOv-y^%> z{f3zIGj5_u>2Ap+(H6A{ISMkNmRY8-=JSs;(md~PKEB+#J-OXemz~w;>GqBujVrQ{ zGJ3`aptG(Jj3@>UZAoT<0#kq?Syp8g$CvbfIpDbcYOAvndVL$u`+ZYhd(-)i{=d&@ z;q>nEe!jfFH|+j(zqiXY)6eLA8?RqZhSG;|iHJc-oAP| zshzpC?BnwF_BwI)evc|_;>gu?)SS!5&CciM$6a`l^!OZpkD%x3^xhxnTDs?#e1Bih z?&tb{%($M9&h&WqtEbJ;4)=@o_w)3Ixn=FCiDedoX+)h|E-NdY$u)~Jp1(EMpPK3B z(b76TjyHA6IaY5sPqA?H^j5{%P`0wxDQHFkHv)v2Em*Ta0su?`lCrJJtB(`k`Oh22 z>sNg_G1E!h^Zt*e&+0d)^8HU=!r304XM^{2=3w`KJLz}%=IdXd>U}rU&(XOSsO+~; z0XfF(-;_;dPaWYsH&%J>&bCKRRL_==XP4vjKAG+LdS{XOzkh6XH9{y8K%!=fbFa_l zI&t9VUtinB*zNN6c7x9-O2j6N;T5wfD{?Bm?UucsQ_1vt{XCxM)AA8j=4e33GtZj5 zwdv-^znRC|SD)&7FAwc)&e&d0P2ZPe>whWo*UMiSe!hJ%=kIvGUw1!q(Tg`OQp_uC zt$@)eoj6RY1)R+_moozV-OrBc=M3q3ICVXIspVNXbo#d+PjW4bG)UMsn1h{{Tnx-!kL%nbG$bF}(hbQSI(}zIRL0^X)Kv-_X~}vwaWD zYV`TE?sUBW08{C{nC`jCB&O^hvn*7_l(RGD9D8G^^VRYwxz{zBprUYwo}tlr^_3?A# zGe@&mUzx6W_k5qLyNA$y+Xo1D1k-L<3e+o*7Znvv(JXnq)Ym8)n|qC`na5Ze$&6r>h$4v%lrB#xs$3D3@M!D-PIPxa@b>{7QK4)@1 zub%jysNMFz2z&hXg?IqmT%^jTx)|u)y?#f_^mW%CqI!OQr(9FhpzwKzm(g_kJj~*) z=JLNc=k;RY-FAX{{*OPP>o)v<*K|Iw;BsxAo;_2;x90s;z8_KAabOF#1yfEY14w`q z6%|e9mVDk}#k~2aPAi$%@5jIE;0$~^`_C_5V?Kv1(V}KsgOb%vni9$hjbKPp5Lwci z^i^9FTvbZvG4=9g`X3MF{(rG<-E}$>vibVU%(c%v^16Cq`QM`H`aB%pSHas{?yT&` z*7{#geTO%~;Wt53Tz$nX+4*Oe*PE{n4SZxyb@Xbo&rGKem(b($_4|JR06SBTkDd4R z$7d!{6Ev0XW;xa8@x6TbGQD3<9?wUHuP@uD<$jB`_WuA3{l0SK*E*!yOeXD9*AGse z4fgo2FR!c9&+B{m`Wn|0|T^`)@arfow)A2OdJq_6Ue_7n@Igv^;R95I0aECMHzF$_}{!bBKDwdQh zE1s24Zccp;XFp%0@AJ1jy)T{mJ+a&{*!SRYoXu1i}fb%B7FL zgNN(*z9Wy?e#{*6t<#rFw-0^WpAtFq;w>$%mCHNNrl4<5r$43jK9kT(X3{=wLt^Sp zn52yPw;xV^{!cXdm8Kj!&bTB_ZSk8XOc+Vb@Jl=>u{k5z7BhY)MmOwAG$l9~hHQ6Ph9MZy(m6-?mo z;OBdOKh8M)t(_%Kmrfnd+;B%dbLKhC!9gl|bk#Fv?_Ko1t5eWS%}}(Q(uA^!pt89+9_HzG)W{xM!$%fJKsi>|Jds2?Q885(b|*g0E}joL z?fCvnpV~E?%&$k+$?Cgv_DS*&CH20aJ7LQ5c~)0KW1l}+>-!Go)XV{LvzP3_sa#d) z`4sm}=fkg&vl{EF(=R&W&TyyS=ltIP0HS>lm)7UN{O`Z3m!XEO0>&jNmUBOEo;`n- z#eS1NLAv<*_2;<9&ojp{DY!HjCYM#MD*X39qs!;*6lvJn+igjl^gX_RR=#r+A_aq4 zx1@BZhv+*V59tN;I6-&q2%rG`x^YerqI(T(i>hr`_=Yc%hNETX~e^p z(p29Y@?Jfy?Ze1EXQ{7Uo=>yoJih(DpC6d?d|XrCm&e!X`uy&U`MO>&(P-uAayAoj zB}q|}w}t(GlIQg6MEiO@IC`mPsq@LrJR}1p0VdZfrDl4($Diur=jfCTgSDo2_TlwE zch2ed=a-*9CFk?Z#oN<7;^^t=uh05Ur-S+)KEIf6Pvx%d@|p7b+&;fU%ld6AdY0K% zhLPmttVKyeyxPj(0t$jl0L!?Gx#WY{))q4Yvc+>+?MKujcs~`t7V4zK5ra+;-#ZZQ=R&@45H6?&Hk(4C&V2 zU!UX6dwD$vKWEvx#qMh8gQZm2j16c5J|^kQ&I><0ar)=xe#4sKPtg3>^!)C3KVKKm z?Dk&ImD#^8ub28AeEm+KkttfGc+O?%;BR;2GFPXY$Li{Mz2B3EE76=URIG86kN{9p z&evTr=WZvP=-0ifQs&!J3sKkY;_7$uwqc%9aD#4=@wc}@`b>W(`Zr6_`R_OG{3k`> zvEb|R`aK^n>ukm07$nkR=rdCds^pqQRU2L^#yP%fJdb{S4xF|0;lIB(rg^mTbo)OK zMw+JuB8ugBwid>=UTeXg%erl+BPUA4!v zb-m){?sPg;b6pMIHyg6J(w@&hsqODwr7SrcGGh&xX|D^R--1K4)9c?do}dTXxmp2&KB1Y9?&L>bX^gRdc9j#u>aw^Bwv5uADUV zjs1CD+bHLGyq!MB!?xP(a$16R496znAp=?@%wUY9#bsHQjH=Rt73}i6{9Yfc%j75O z-esNW^y9nF^eg4++3fkhK=yo(Jf5eY-@^I-0HDv;$J+WIoaRTDkzZD>9*i!P?CMjP zlQemSYzsY4PId2>(Dw0hv!~GG+w5ui?dN=)ynVh~ho{qeK5t3n>v%syua~42Wi=9X z6y{~cUA#1V{{SVEdvf`GBXR2{ba#(B<7Yo9$QXoZQf&#O>C&9-gFcQ4^({+HP1Z-X z?ehfqq|D5Wfyo7GrQet4`+c7m^&W?R4;R_|+p%68Rr9*M{XI_~?5-Y`C5Wjeq5^29 zX4D4PB+97H+)~9m=DPFpy}bH*a_IUw-E&4>PcLt+`TF*&tb^3JsI z0%pN;5UEt92n}+({9iNb^Z4#RS65DSO&Qm8_uYB*dU2kQ7FB(J&*=GsJf6=-r-GRB z@T=;s_N;uG)c}*b0tQ+$HeZL|*FQVUpI3bwYP@fsfWDmB`aUzs>G}_|=Jl(|XU+W% z{yvf&p&->WX45J<_*38b?04vs^ssh~)y9lYaiTnIgMbFbE|z;QMNZr=KhVQH(c0>n zocbL&_q%xPpFGSXu!jl-P`)oOdU^i$ru2O5{dn~==VPy()aT!?e4eN)TV(N|fN)hw4&fH{O+&^s7i zSSBne!50cmQYI8!a{Ye~&iCWur|Id;NvhM)^>h7evA;O}VC%=M@A^ZRUy%3ne3O?g z(d9*RI(z2ztgwM46DGh>B>RIM9r@nMwPrXRc;MedPdBc)pG(Vf^?EA#ojvh&{6C}D z$JoP^%~`owo_3;+z8d}Cj=_Gav)ej(eVt?0BX83YmY}JzM5w3-aeZ=`LE-2qpxqn`&ZkxPq@RC?OC$I z&Zw9I6-`ar3S)9h3wT{4#P;*u)0a!p&rQWyxIF#7ljZB{UCRh{XfmBIs>RI%u#aXE z1VPD)3iL|wr5T)0U%>MHXU|8|s&_or?wob@=jpoHnZ;ZYJdS>?eYyH8@!^gY7@oD> z>M%s3=^)r5(f1cQ^!Mj`THhxT^r-F9-v0n1pB?qnjnCVVO&5$H0b3>p?s9x6e<GKTvl3=-kfI%Buza~9=srr3>nYz5Y9vdG!ryYB~AKKZSY@=ogB%3m$xlBl)*1Fy zUEwJSh#kSxXLJrb|&5%2VSUz#Vy&dbTm+2*-=y)RFh+21bz0G;|B@$5mg z%1H#%yFre;b^E^=Rakuty{Ftg)YS6l!Nud{WDKB_ngP00ySLYWCCllqchgi%vc1RF z`u=2|8ToUJnIK_QHtBVyeViB5Sn0a_zQ=fT-xH-b+wuO+=T>xli%Ee=vdT%z6;o-LKjUqx=1dG)fnZ0pA~b*6fDyZ8%@n>pvD zZh%RmE+IE!OymyD&Re>9`#mi-zO%mW+WsG&ZYM1B-hZLP^ZWgmv*Pxz%e&|Pmwz8q z14yNUrmEcLdhplo{AE?zN7S8oThoB&bx#J8re?&Q($TdQGces_-q-WJ{*JD3_~p(T zugudgny5L12?|Z>cdqW9&kxV(>7DD>hoe4v-#2{EbI1F8r>*1exHTmAH7L$1gn?cV zt1Tr%F|Q_OYmc1u;+M}?emgn@?L4d9bH8cJ({RCKQLw@eFb2&vD>O;E=}cAt6*Q)~ z3T1B_)A_DfZzm7Q`br(H_57ZfPBNHtbJcgLeNIYd;;wnyT(8No#L$*av!(S^Ih@^U z<>#2W5+u$-Ro3ZV^ytT%m7b1AAAdiOx6t~&M^Aj6uNUgK^Y&UUYSCp#xXn!U;iK93 z?6;zQHdE1e^<|3l^YhwfNbW*2x#{$E9k|n)ja@4QIh_3czW$Zf)3wh$_|(PRd19V? zsN%aJO_x^m?05HgP9IGpb-cT$$=OEIllhx~Oa*bil_ zm8j{yIdbQs*KQw|?c?+PUeBxIcKiH3=Zp0k`TN{Et0gNc#T3t88aOortE-P6+bPek8W zosXw}^zJ@m*U!-M?TqgD{{U-r`fMy!lzb?00j3uf6E32uRAp{?WXw&|6siNMUQa;{9H(KIbjnKw%8Cxy=1MRr|jcn^*MhmD985`{FzB zeLt8ptccA`R##YaC1A@tmAiCj&(6NgmYg|*at1|7Ql+`o%&ZE-9X%W>yj^s(R7*Qe&(dVB9ieD{rg z4`atSyu_{KagSB#cuzkLoq25D9U14dudlD$t0bx?136qc^EvU8fm24ap}e`X&}U0( zP)g`I=}w7uykDm1bN4tmK$udA$_!mRiSGPXZ7Pj*^j?3y-2Gl(+4+W;2`rm>_iAEh zE@a(-Z$jMc)y+D6)sAhUn;4C^S~I3Cg2YyM?dN>|083X)?s`6VP0KGnTO7NdU)kTw z-+23uOva?jC1Q`8NH7f3XlKR=R%AYV?DHjYwxrU{Nv4O}yuOQTp&5XLEYg}7f+(ik zKE`=1bLq+RdOaU+o8G^X-0*&vqtD%7cWRKzq^D;omb{DZ{6%$lJ7(CG&`#By zNhJxn6s^?Umt7|%Nt+eLri{>`Y1B@9tZtiGiH^g$Ctx>1pvh{(F{2~BN~ z*ambLNdPel$jS`69N(k)Zik~hzdkx9{pWwN*y#gQ&hDo;=v4D#dK$$Q$>jN*crULr zPELI^dHs8QUirtTKk0b#dwyo;2$>;~PW8iG987s$KX$inm#2$XXXD$I+cF8XZ7(=>*AnYK` zQJ96egdjyLtgOwvxt~Y!-Co<|dGXk$H7W{sD=KT(g`q*qjkxH?SSvq+a=zcO^tLlBep4xoUJYIW;hA^ZPBs%l6->0V@ z<@4czfZ&rbeJ&+>Vky*KBZOMuZ* zbl*Xq?nQCeeLb7mYmbbN4&Mjc-^cD)H7*-43Z_ijlBtTMom^3vi(Jggp_G+V7dC&H z-|0MKr`LQpTI&5THI%ld(d7YJN8BYt4#G^;8Hit-Np9i`14Sz_*BXAG<&R18Q$1S0 zUDN9OJC;Gxf~F0+1((fy{P@o3aNJzTZF8GezKw!K~ppO?+~zP)bUA6Ex5^Xtm-^!7bo^QV)a;`el8(V#|w1vAZOPHw(Beg6QC z+tb_kKX23M^L&Rqo(IeGWW?NrkahANAJp{y9v=6v;r4oTY`*^hZrk?xQ#@Wg6vY#> zr(a2ZG~ILgI5X$tbDt&Q{V!7fe{0e3ibYmuef9RRSC8nvo%+7_-5ow3$L8eu-%o!Z zv`bjZeai)qqO?dUMaig0+LvBs4bPcDS4pcCe4gKG?dR)k{Gsc+-@`?lafcMKsy^f< zVuhquG}BfoejxjZEGeRu80&{4-}yY16;<@QU)p^iM$E@bZBz{a)+iUy{XH$J?dFHfp^ zx&1G{%vTE$oMs^k$|kcjJ^6F{zZ;{kx9)!5r|7)(@q6-o{{WB8GsHqU#X_9) zGG4hTcV4Jg$;IY`tR>sE)imW(&N7+yS-h-yiltv0pU}IH>F)M?A9Ly7s&AvzM*KE@ zZ!_n6x*mVH<&AW<>j^a|tXV}VT$dH?A}hrz&58x7Ond9{{Ytr5a$Pw-jqrSZ4;uy? zW;i!VR%;B!3ofZGQTYm#uSanOWhOe~ljwd^7fHbB)2sU*quqTiO;W8)#S-(26~*V* z!|~-z_&Jrd@~r%OSGsO&xajYmy!Uy@_w`&l)$&_-uFeYf5{&8PyuV(~oj3Y;HZ$|- z%kua1p05vXzBi%y-=pYr_OND-yfXQ#bXT0 z)wXEPX9AEruwoMm77}d<3Wx?p9LS?|3M1(0@&3NQf9CyPc=~6f(cS0ML{?tHp*ZPMu~=&mW@j;1p9j(WuKu^?dG+^I^u52Mj*VEFbc$vuFF5?`y~D4} zXRnu^7c#sOGMT<-FQO*8K98PUy6;s}JLYm%fNq`aYE{!Nbnv`>+t*Fro(B~3>CEza zJr94G-mi`5d|#*N^Y?+}1j<|@ajJ{*wP8m40)@ z`h9&oeUASCpM>2c#0>~Ch!RP=Br9PjB9`-oh%6UB^~Rd@V?!g`i4=Ji z-j}0o-dNN0yXoV8{{X}9r;Sm&;r(ALtJ{@5`wh#8LRKW0G_wd_&tND7Vt~$I-#0Jl z{!?FWoLH}Oe@or^AdU?w7y(0>#N*rK{5reCf#4YwbM+8GxXZF-qOjhpG=%9uU;>@iHsSjpy+C_HP}XzGNihC=fM}Gq8<`AQh)mFjj+`=KD{4_jdT` z@7TYi%D!*M)$O_cUpw2uh4Fg)J^eo?>Mb8B!?MEZrmD#becDBwtn;z;eQ%rO^8FF) zeizz2??;=pT_=O%oGhPQit7JM&nwfTQc;WIpk-1z+$k$v6GWTcga zMZ#_Zz?SpS3Mg4wSy7JoV){Rn%yQy-J^i0%^m+*f)F1((o^;>He6h}-6F7;Kbj!o) zqO|q+?2CF{m#djReh;7PX}R^jpPkjdzc})HaP#(P^+dGVi6qL^$Hm>xk>2tCn|}|b zqK0sZw34XIQ`60h-S~WW(-iyL8lEQJRwQmAGX+$MAYs~pCTt@PisE4i#U^UfBr+&W zg{^cfrhD4=3i9ipA7VbIOUe4H%VvNoSZyrP$*ELk!ZRu4Bi-)&z8-J8%k4bA+kT%e zr{{jgKObHN&3~=@=K;f`)y(qu>-?{$>HZhhvMvj=;Xgz1y4`$F-?LCqv4kuea0Iwc zc0!tiM=H#!So-V8k2&Q~65zMVZi!==FDQe#y;S4rr)M))5$o0-eTTm(`%joC>P z00k=fynfA{Id{EZI}~?XKOmbjuH@U%LQd>)6SGsuqaCNX~tfjeos%6izd3-w`*A4 zR*>ooB@xwGYn|>z0%ZcpLm~wQEajB1b!37lXKydh>EO+!E46X0o%-S4eE$GJ&+2TC zThhd>8x)mJ<|t-tyjhvd?Np{vO_~DCs*`ZiX4XbA6hvcy1Yr!AYSJpATLf(}cIK4p zs@R=-H`Uz8SXvE03aV#DE5#)lx!dLE(eJ$6`yY4Z-}x`StIzIukqJcU&&~d)^8Nn+ zUoJPv_xexI{qZ|Y-Mq`Y=f2;}(e%D%eeEw8umPh9nP3`;+0LpWh`Az*pk+D9$t4ni zIst43AQ%eT29d(%DQ1L@>>vZ6CT(OZCRTGcU1U*4oNcE4p4q2#1&{@0O`xF9ESb5A z*i?y(tweTKD;Nw_GgTo(G>J1vHsoZSdb5Bw#nre13>S%4Puw;-Z5_JT{ zWjLs%T-mNP_Vu1#jqmwB$K`z{AVpQH0G@8@qfqqX7snmWfH<$Cc( z?ecngKK}qFZ+FSFr=oY3CafzMOju3WoB8czP!3Jht4JbfRLMbX3h`hYSTF$6D=Z%b zT*d`cLcPW&fW>09O7Mj_S6GW*paxZEUaY|NztooF`Anu&cp^(rHv>cE!w=jH?qoFYEb~hAhWfLii&WCN$Jn3 z^Yhc!`FeIfm&)&WJ^T-64(HPOUVFcr?enNay=7WiCa*pvKF!~q`rAAn?nl)4ySLE2 z<%iG!M#7UdlXPkaMXOl|IRp&iQJYy9qPE(arhpiu5luO6pD{(O1LZCXfT@LGE6S*; znM{pRXqydYjN3*Si7TUG!kDV5TPiBmMObk#qv!w$3Sj~!ObVcA0|7?RAjHW4Q6ETL zl~uKtCj8i5X{D2_nYQFjN)1w(#haxy8Rof)OwnV!G7_qh1LBIh;X_M9= zV=E(Crn#aPxmsC9aOh^JYXLAYR0@-EKnb-$nq>;Fu{EkTqM6F( z(K*cXSQW;0?fJbOe@Djc_}e?)n@E~p)rgJnvU^4jlD!up+Gh4r|9 zSL1Z^(NC!2<~>U_iKG~;+sy%#XuB#w#FJ>~mB@yGl7m(NG!2C&(xl*)Whn(9pxKHT z1#PUMlmM&+!x{^;vbC&65qCi{K%10`P!&L=f`D0~3YZC?3YH9j22iYwjbP3JX<#t4 zm?JgV2wA2q#l@p8wi0gFCB!MrRp-p8%QcN$W^VI(eRqGE)AD z+c0g=1~Fvf(%x3U&;SL)=+uH`B#xLml1&lHhZt0qM9K-ICL9|uTrn#mmoy29N~K6P zVB3ais|?kFF3}aFjD*mT(#ooms_Pkt1UX0q!Ga1gB!l57+J#dWXg}Oqp6GUy84GAt( zETqK5B0&_yN^Ko1m|-vn(gPL|fm3pzVd+$C?)e0}{#6rni!jYw?00Za*taPaq!w5EQBkn>5Mzj>M z%COZnDYUq&7A9%(tQ!bE=?zQG48|satx&XSa$izps4`$y!SqDH8H%9B7=jKKXD{4Q zvWZz(<$_`Y=Eg?C02xM;3Pn~|I7Gz?u5y}Z%EK;N@bdJ11AayRj-5}3_Ibaj{MJ9Q z;rHHGX_wJ!HBdH6jm|b#6UFT2{&(+r&fGg2!mm|5hOCm7QZf@X)avC(Cae$zyEU=1 zNl?TiTW}1eCJ3SyCX)3MvcY#$Y``$GO4k_`11YL&nzEH*0DvtHEE{zQ)3GN=H3K^X zva~s;LPa)K*CD%LxKe2dfTwE^LTX?#z-F;tuPGE!!HWdEfMbv?Owmb(BNVP_M8!(2 zndO^nEV-@ba`pWakIBL4<*Zc=PoezJ z+09$0uacifNoU$=K+%$+%37U#Kuy*w-6LemOr9j(39#i^L;@BSmNmx3VPl4|2g?X5te6Arg|QK95LGY$E^AU|Af=*?JvRYp~n<|^8mgD&A-p; zDnaufYRpYBZ%$hK^7NiBZWlW}-zvED9@LpuC5Hl(2_@2UC>0i^XtPbDK_QDUq0lQ~ zRLpU9SY;t#1qlKH!Kx(0A#qz&eE=|)W~jT4E^8*e#!}$oG|@0Zz&4z$V!SytgVdze ztO`WB*@{6(mKQb_=}6c+6jm`_)s#ub#aMD_O7g54`PQcyB56sPQsXF+ZsieFocwv% z>GV&R=XQFYe^XD|`8m6J`CdPz=6!xk<1g(w{FL75K71aE?EZV|*`0KG`gXqO%0GYO zc(0Z98Y$=!#eh{oNi<0m;7TYZz@h>Ul~ET&5(DhQC>7?~7D`u=sqsve0gz@TkZ}^{P$?lBZ_B=dKqvRfLty1|)T$EHIS&FR_p)d*nO`<76M3oW) z1RxH`9aV!i7FJCzO64`7Wlt7|O56LJfhkmj2K-xTP#&faI%Fqs+Cv` zrIZ=6vMVbqRa1yW)~C@R!|b`EXF;y>My-{_m4%jQk2Lf^m8Hw#kT|TBGFE5{gljZ| zRtlFOgjJ$3S5{@D+(661}qmc2X>O}$V|Yn!Y~Hhf@KscoI($h zD%L=z{M z6_x8Uva+Ql;)+F0mmmvwEG^t)U1f%un@6LUX-1k9B9S1oodL7}2{kJ$2t_SQ>SYoN zI8$C|TO?>^p!!100B*VqVHgVAU^8F@FJh6f1!J&3&jhHBKphznr6$;9MHBm;gm0>kg zB#xl1gxn>&j9;Q#vtfC7xV$yDmF5794IytjA=O|WMub#?#VHsDXbA`urfgWLBTk^& zL9j!z6&MA|xSJ|W)Ci$uBS`=jSPax3aYCjB;{yyLNlKgm9SjV%pkfC!1&CP02vj#5 z6-XJ9OiC^QFy!2eY#N2!E`o6e%#=6;6jBgT2v*99G^JQVx}<^73TrINg}VnaWSjsh3djcNDgiYl`cM#7F*cwx0d8ti(FM#=hX$cD zSgx|cXIoW_OhAx2KxUG}0XB&+NLCHT9}T3z2FXy(Ky0|mQJ4S`^-P>$HyEmLs#R7x zTy4-ZAW*8WHo*iQs<}4vMOTI@6*MhUwUwz-ssRZA%UGInz!#SX76(LZqBhK8X2G{Y zUJMq~OC*FT9a^t5Fi_H2MMb*36n~!%>mhAR07B$ps!L!fLNp+u~5xYHonp@t1Siq+HhhOX0;nO z#8b4y#2JfIT-v7nDQ0a{e8mP(98fhz%oJv+Y8a;sqmwG;KwUeRj*z4fv5+~aF3ApK z(!8c66pRB-*P(%>*{H2bHbPL%%sogK1Z@%)P+*p^OE+8+YAzuZMbnd%noOakzzJeN zqbbEJB!Jtbm6b~6L>kDZzz~Ar#N2VCWfiI}YtZDvGPTJtcL3FnCJ=p>7buf;B-^DE zX+*;bnO@GeqL(HUcG@OHbeI4D=^@%#N|-SO%e4!oo0|b=GHPzR6+n!GW`V8)B((X`5RL78EjHmPW>2z}L*V5PW4s~=@@ zibhZ^+zCudp*p6Gl$*10ESPCC7LrV5)#-I8xlK@6TCBxE)rA?XKFb-Vv{48v9+qa; zhs-(|uv?PJO66?TX>BCc)s#tR8iPPhvof`&M{)_WX05zMMkPzmg*J?aOKGZWnzt!N z+?1M7=0S3p|HJ?@5C8!L0RsaB0R#dA0RaI30|5X60}%ugAu$CIK@$`rG6oeOBSIuI z79}(=Lp4MuK>ykR2mt{A0R;k9iZ+JCtJHYxZm(@yY*nJv zy`DTK@ODM&&_ry7>IZOX6{mG*jYKgJfu@>GSKa7t7MffwksB`^wIFO&G@XxUAQdfF z*~nO44<~?14qkXf>O$^hk(d^12gi7DG%Kkb~MKszf zzEp~8Bef&VtEX!0IPG+b2&!6>QF0rtHnnf3a_zuGu2G~_YFbeS_)ZH?X6}Z|?3^^J z*;d>R)Pr|*t?G>it;!va=y!27!XG}|p|aA72ntRBki4xa-G<$~fVf^MZ`dwSwaVRY z5pvuw0dnd?jh734!(~)YJQrFG_$$~Q{GK}%Xd;Km(OfI-xK?|8kX}B^Wy1ZDxh6UkwI{ZJ0r5SR8HNfUf~nDUK5ewa=e7&u=?zrYrQ77 zYuJQP*lfJEfVIl<2t=C{;(1}z^Rg3LT#hU(_|yJaJ-Zntyah@?RJBeZAfg6>y$nxb*R^h*%S&T1Ep-5yp9m&yKaxm zwFGuVg?*fs@INt3Qqei3rItC$nimN zZq*9xIiv-x(>b-_R@C0XIHAK)M4sL}gmy&`uXKA*N1H_k(b-sAg=@(d*dL3z2=28n z8dl%2#I5hJ6o&(o+w0objVr@ix9zf}$XB`>A!r?n3vS+fcx$N?Qyr(bAaJ*G(gR!- z8w&Rd%HD_}#d61OXU+PAH|Ql+ZHND14bO649Y8ldD?0?;VzT+*BnTT%`{TDn1SnnDf; zA+p(WxkU$2+@M0)1Xk@_kwi37;aDWn)VvN3$Kp{yf`f&cy+{(aRJcJnQo2@(*!BRe zD{FT&t6_8y_@A*RmZ5v2xw`{#!R1{lM1*%F@8g19w_0?~2= zfIAL+pcQuW;36$nrAu{aFC&tEPIA?@>DTQgZsO z*MO^SD767d?0bEC3qfAwt`X(c+k;A#8r=#B#>-Q!8q(XlKrb&Ahavf>5%J6=tWqWMAlSnxmO}Yr|tfDL@ZoCUkTocW!v)H>XFMc=mv~t3k*f=dj%l9OF$pxLT_1vI^FvdzHsxj|6UP5oz6?O#`s()OHNn z2J2Ko**4bYcnI)_Tae;xZn;#NRicev%f`TyMYUA7D7$V2Ut*6sw36abJ(?|5B2e#J z?O$HS+atQ!sj84(H7g-fbs#$wIBRSxMZ&LArKZs2*;Kq0r)^62RGhr3C$o;_)C>3O z`Tolp?=l!br?4z6*I{HwD3PDlqLJ(ZG>pv-+L1cDR@|V7(uMXbPkp;Ne5?)14eSQh zXt#Ua&ZIXaS8G?Z8mT+C_T^gLH{v!XmdLABtF@xFY$}B>uVO5Q@8^|Njo1rFvNzjH zKt_-oaE9n^O#|R31wlHiO5p`$E>UizT0zy=p-7F(c4_aj27zs$+U{3{tF<1@`4Xt3 zurx-iT&E&R`u1 z(1*RCcC&le$#6ouMaXWrK?_`xS1OZRxcGptwb~tmatOQSVzjR}Xzqy8Iqkyn=T?+%#@(-dUHBupLt;g%q!#MU6nR?qUli^~0csYA zO}7K}T%lwdx+q%mTB?_uD|+P}k=bdbx+`T{hpSeI*!J8mI})!-aTHvpa_bM*AW-ml zm+koTwPCcPgOR0Fo4Pw7Q45e3ly>jGW4iEowSOKE-?xUN*mG*_Sxwj~?c}RL3bF+^ zZpUN?W}Wx4I}SWFlD7zMEpoK#LBf{#miFQR zwm?b0wz{xFZp*-O1$opF`2d0&BWMli6z0-xl1k+c2FI1c-%(pH2eG^55Kw9*Y^@qV z3ZxT_cW(wj0DhpI#?Qg)(UN;kb+iRHn5Gbr?!>s+C-+U*fX$X0<(-5z|7 zTu=dBj#E(y&0Hh8I}|=$wQ5!o-8G_*#d+ho1#q|;Z4TV8H%8X9;%%s)wkya&ua(DU zkC8NsZ8Wd!O}KV6hUD^(Ye%)B@P^@8M3mF*GFj0O1P z{{YD!R^4(_9IUUm?E3!Ff%t>BgG7e~v0N?*yByZd2(3tbLArDFp|}*aE>(vDEcw)l z*ezRfw+hfIg4IBj1pBp}@(yC-%I z`0Q7dc#0265~`JyYz6?W4og;$J03zjkb8M^Ly_PeJ-1dYfRAe0k?uY!l`6C2mm_E* z-OvFN7b3R^tEIpyL8VSM>P!x2m~XVJ@%PqfLDkqJRsBVyeZmXs09&UGh$R+m92?m4 zU@EkCR);nU8z(BGM(eN?t7BB%!%-AF5wEzkRRrO;2goSe9C$<(OG@%-ZQF2(8Z4?q zu@s100=Mr=O@6}E07qr_O&iy64}B<=y5)aiW3e093d%d6QB+pMeN}u_QRlU*T_LwIE#~+H+b^0HIZ~2-4*b*{L1c1rH~I zba>OGBiIyOKriens=HMkK&HJ1b`IKuN)8AV=7s`-0Y2Z*dT`mwvNbly|%4Ec`A_>ow6!kI)Jv<$~}vC zmy9>)%4H$2;cSJk9xl*34#Xk*Hw%?}6xwQcaH~-kP&|}G zkQGykXr<@LavkXsLL!ZY^()1dwZCy?ZWBs8610V*epbO>ZP0L5w5^d^wzNAGZn*7K zhaM1BW#0r4Rbx)v4;-!+jVjkA%ay%?g_jSCv@H}7T!MqBB6eILdtRZY`#5g&@}=W9 z>h5FZoJg2UWsl^>bKN3+zaWm>AiTS`ju!p3BDO^A9GXR~ zJN5y19P2l^>`->#qM8$T>`+xj$SXk-7lx5uHY&Co5;b>lu(u1zDa(QO08-%xT5mcL zLe;HB6yg_`sYqFk+5E(r#G8-rfa3f?X|CB5NNz(=*=(+ct)80gg_k4WbOPYsuGg^R zgnmeHcIz(Ac>ZYlLkOtpUH3F1G9)XGo<*RSr<$ zyjItJow@~BUTp)i*%aaOB9^4ps*TwNUhXHbD5k1Cg1z^$f%uJsQ@Iup6q;4;tShB! z?48c5?WuU!uLE=)VDn4Lakb~_09EpJk^r>ozlv=&eUD-mqC0YfQP~9T)o)Y^b+t*j z${S2RQF6j)?j;m@1Md^C3c}QfZLjQ)#cD^faSKxNO8YG#QY!lrhhS}P)ClbA0In9T z%Teq`=j^Q#s8K_8yO9M1PTQ5^lpFH%wb1Qdx$F+j+*wz@D6O0Z$l5)KC~|uqEVT(N z+6bM;4nP2bLz?XCMOSveQNG-PvO6Y<4|?1$JOYEeX&2aRif6?eM$zG^!4MSyN~3g* z*2sNI)Vy%FDdK@Ua2}t=O342JWPHYZPQM*@#i^QaRUkH{x}|i2v=HW(fGG|ZrOF0_ zO0n48UgZwltu(0xr5kF}NC~?*@;N)Bz#A6X`zqKS#>ACI&uZA7!;*IeY_G6yW`uWK zs_b@JfFqLK(LkWK3x)Ph;iKZ>6@^OIkOiN&E6*LZ3U>p3($Asd+<@P7en30+S8D7y z6o9oux1T172px{-TJSh7TDu*?cyLv=%gJ632W0gi-y@8%{EX%<)33*${OxMA92JB& zP6@WRt(>Sd3avqT2=U-?$#%d1S^xu;TW*7qqE4GTpn~(Ycn(~O8jUXlWoRu&x!nep zxEAC}B@PRbACaZu2Q5O%qgC6U+#*FGOJ+aR@VpcQB$6Py|-Uw+!;ptT^J{Z3WeO%FPj^x@mbi`bQGcOZvh$me8M z{j0Z+8?Pm$Y528P{G!!c1nr4C6>FZ_w{Z?UcHACQO##^KtWe-_y4@ZxwV=EnPsg^U zwxT-~dphV3lZjTS5bEsiWa4`(J7^ym6{U|I-lhXb>_4U=m5?fpU>jolJ& ze*qEO!fMq(61PD1cEjvA@50?3&xbp0tNU6d6KS@IT0*5;6}?irH@={?e0M8k5w89! z*2lL9F9UPJBGEzCxLcxjK!om9pt&@-C0&hz)VXm@b)ePe(513=$P`=^(z-UUcW~X0 zJ;^0epb)z3*L>XFlXygS*ecmM#SN2{YCd*P;o{Vaa7yY8r+I~H6(Iyf?J`2^XFY-h zr*5qc+IB=Mqzcu#2$bFvkngeqG;VIIwQf-~wIXdhbU3Zg1fquOyHIL(9fvD)F6|B0 zqPOkl&{wt6yBEFf;_NG2pC{@8qkWnv7N95;T(<1f@w7n!3$E5I^LrkCq${P#x>kD| zE9woB8ZF0u#a`R0-@+D;7Kh0c5am|LD_kboBqunNr4${pT9s~gM5iZ`1jTh8e8f`| z(EDJ&RwG=#sjlrw%_tRnhg#iHs_m(G)xUZ{9^M1@2oyfrzDHymwtxofbS#b&X%LNB zD`nxq5lYbStG1qie=Z@W)!R;8^6 zZHmkR7gWvZoi{i2zL=tSNOw!>5k&?_Db78&vC2FPD{I*mYsIcn9knY8g77#fxdj`8 zT<%uOWvCQsY)vl&pzn_Xue1t0AjB7sk8kVYtyRB`m5_y$UAHQ?Aruvbber-#DplFr ztvkAt)PmPg`krD@@IX)Jpt|tDKqiDMySHeyXc|;X4PI~JR_HDeR;g**bls74pqx;u zYXL+ewI^j#_dy)6SGz|!48ltq_kK;)pCdY$ndyb@{8y=Z?CN7Rd!#ao_h{u6?#lP2 zd$%K)SG_CVmG25>OWye;k`*M+jYT|@KP8fTugUeZuL&HZick2SV+K?-olX!fbPyq{(ye<;C@LWp-ARAFD1uyb51d z2;-E@`SoJU33fxyOINyRUhTH-p$J1hCogi#8Eph)vi*3;6ErVfh~zT8W)c?Dl_ z%H0#NyCB`t2Gyh5)h#r%@3P+cTPJLbiVh_?7n*t8y?Tq(wW)L&VQy#q&kqYHEAw8F zogn<9p#uK^&3aSu;rx&F4BVH0lc{P@^taBi@jc_9!xQBCEccSbrU&9wa77Hb-tDX1 zg^eq6CT?_Cxu+&5F*;PZ;6?eHaM_*th9-QU@;;oI`Q|$@vA-idQ#xsc**ccA9)px_ z`D#OS1&;u)3EZjn4sz7BEn9Wd{vHeT=YBwc?f(E6aUEV;^*VdM=nJWJxg7qPg(i&O z$R2-I{8|41_7;nj(FXMV_v5;JGSeiu?_lbA1Q>H(az=KO>e&+?%pAfLtotm1$bFF9g~MBAajpN>fWO35)sgx8}2_N`1Oy(o&NxKcJUwc-%o<@lB80nzHh4D*z zj_UhrK^?&%6fTvbxkc1op6gPOvVN}nN3x8oQ!a$+%^dl%KtT#zA10QSq^DR7-BQzP zw5`z^fZ4pR7cIWP?%}buTR4>CvyCZDsm_*5)Y|jwG_o$B$eoQhs=}Egp>qSGT3zXD zmr>U=m^jao^5Pbj^x0mU)5~2Z4nx}(0ehd*q=~TM8I+h}B;K0mwoGP5{{Yjp!z?ky zF&>GPv9dFd5|gKio_b-??3$a3)f{1nE!Iq4k@u`vj@JZfma2d~w?%xBAr3M)DE5EX zt8dYxclke){{a23#gG30>=QZCP0}9Y(It5;>Mqy#{-=8X0FyWV-|;bDkUU({mqLIM zlM_8WX=4*+JQn1i@gZ?@p*I(OZuFVdG8o~XH@V5mlP%?6a{VJQ?sV^w(PP1#Bi|G5 zAa(ntt7+8h<++6bpryN9(syJRw29iug?$TWFet=EKYT`g9cZ9B79 z9jqdNwSB8nszhzbT5T0ZiBs{Ee}(vxxQN^!7}8!5(&CDxN|afRW;QIxDhcuP2Sb|^ zGG3e3gwoE~GXDTv>CnvHt&fYFje&)Sl>98rJf`%?@N*`^>e#bO)Z@p@Z(r&k9PyqR zl{PD#JQ*I0xKLJ0^BeBPh|2@VXFRxOjadHHOTccNc+`9Y-Ng0B?eEz+D2X`*)m)ppVm+%%_b zj_c}&$x@}@sj9Ni@SNE%)o_WqkG4Nc+wvwCw0_9iTukMw)scFySnQuFUs1?5M@6iu z81^$mSkT{9-x!yVWagZiMm)amJzXsOtm!dG$^QWSc9PRVVQW&(eMj!(yf zS_{G)k`s-hc9hsXj~zw&=6BW~wnof`u7A8=>v!1wVdp3Paee3i0DdP_nk;DYq>Y~N zFI!rBE;lf{9k{ZGWB&lB{{VqSIPEjF9R25bxFbIF*sYVe0>SKeP5~7l zQmnnDVWSzf=0wlDF4q|B-O!|VKrZNU(`|?t*@64k84H9 zm6JeqA4v4$hQwkHYjD~Ons20KK^A0r8Btx8FKsjnwvL`xY$o@H| zKB36cnJc|$u4G@KsPU22XP+8uty3JQhhXF~3B#SM{{ZpHTIlK%BIT3vXSa!T_s0}3 z%WLCeZx)GiWyqp!_xM%9qyM4rM;p@Ym|i-4*vi~ve-s+w}d@RYee?H+EGW3MbB<=`9^fc+^9>s zRl5VRRYk_P3ad(n5ag`}D`&5VnSbfum>*wC})0ZjI34 z6%>hLC2`Y z6C0B+r5m329664wSkl8B%{pQ;wcS1L3VeYx;rThd=1+q8rOJvK+4NYGJ`)4++luI$ zD|hzuN$JHTvP$e(oY#-!S)c2ox@I%-A@=W$k)GTOB^{6|!g6s}+3pL_bwcJwvT`EI zixfkJB$_i;JdtB#=ix)0G$>;8>kmIp$XD>V> z+3D(DbN>L?&STjL99V5T?a>{p0laXyLAHa4K-wX2j_9>tYO%d+ilP?bStGzzzbFew z;(0|7Q5LsZTMns|>7w@$Cq1oAnkPCzsYj6PIfSuCis*sKltA2Rbx)Cy{dn0ida?R+ zvEkC{U3CVR}zn%K$Q#z4bnX^*+-@j*1rcdFQ2T!Hy?{{WUS zrqL8vlm7sc7X&k8zJUFGI9jShb+X#FT6fs3yKqV_P8Ji!@&z%ao4cFK6lFa+gSrFn z96H^>-3NTNA!|zUu>)?-?CwC)wj@%ZsRTu%+ry~>jdnXCvd-Oq3}eUbWVs@9n+z{& z@%Ntrj(m|gWpsHOV$8{WnPwa}J?xpe;C)IcsU>JmoHehRu4&0`uUWeB9v?TyA-y*f zBi!c2nCzB4zMrVYi4-|bZYEo>Qyya`r4JpPvzlH#q>H6Q$3o^p*drSR+j4heh(>}b zyyfM4b^4=9kK4ZxyGnx)oIELC?7ItjZAQK53*LqAT35Sb_Y49|h{$x2M1ACMO`9Et zcEvkhTF9c)uh+*|E)IvsYehZk><79jB$iBx9Pt2u#gp8}Ab)|3DpQ9I^a^GZP4?k( zX$^_nvq(WX)VW2Pl_Sg4ie5T^O;M#Y0hzI_cC5Jx+XQsBeDx*c}COM0lf8ZkfI$7ahMAW+SvVUNg3(N(XL6l8)ILi zF@qByA2dG-W;@$Ty}XOikqDlj6YC}9$X?*=M(b`&zsDO~AddY}i#D#B3vvGdX0&&3 zG)qg#A{1g{6WbE^jKi8npVIN3eOF$;#oc&d?n$MqYEC~73efF#iKGg&?bEhWg0udr zG{jV^pt`br1KZ0`9hy<%)po7<2W5SV;8{gA!CE|0;1jV$5w=e~Vn*aU+sAK+NTPwP zcBYr%=0xH>7&99#q1*Of`zBAC;8QfOeyWy6&72L6NOb|x-o{;2KJocR62ddnn! z1eln3gs>eOh>xV3&fZK9E-uub6d3e>jlNL+Sz6}$FM zYLFG4-{VYCqeV!%Jf5C&cUo0hXj=u!1RyAyD6}EMD11@fG?BFc+3f(e3gm|p4h*TU{1&Wi=O2Coa}S@jzeO_#?Q(VbpEEuv`HEDCiKaP*>Pd| z6SR7m`7QLZv1D^G`m5VCFJl_#xa-Y+M0oz=(r>>mRg(Ag$+9q~%*dVZ1hnbK^nJ0# z10mS%j&^1|9WnJ7JsI+E(V`bmhu6j!uZ}4AT7uPRp3XZtJL@T?pFCY0K2INGa_eXNi&06?xR>Txv8=}&5wZVK75tr?wp^*+qWgfnC;Ax@z_*< z=#Cw3E^Al=vL4VF-9^SpH39nyY154-2R+)lJg#)2N6U#bot0eLnBJiUQJ?(TQyABJ;>i3uO6iNPh`K0%=si7ledaMmC6aB%@=CXE0pH$ zO$Tm?p!Fu}Fa2&zQLa1iV!?E1138WPYEmuoq;oD`py~;F-cf~mG;OviW zkv6HX$?DM`QWzX1dmQHDvCMNFze#H{GXDTg!c(>=x`7N4bo?wSv9mC+A;ywKC4uTJ zr({nyJFrA7c5GQM^n`e<&Wd`C&n;<%hdsH?kJ58V5Jt+50LDocwq;G}MGU@~WKq6u zs~Lu3`daN;)RFWoV~NxvXy%&m$aEU zjBq;$^{mH*8-I^S%3N6Ge?X0~Wc_P3_Ow{sed(h1xT zw@y0n#}DD4QKQ%*7R$$gdv1e`Ahjy}hmHwe9Eu=06_N6Ge1(QAhdJ(<%n-`wmh;jd zf+Yu_)<%}*9El_tpQZb3*L0(a>A8AtSg!W-v(bwaLuJLv{{SLp#T)VS9Q>)I$<#V- zR9Ftn&TNa^IeN}a&6+Um>E6H*3H>^Jd|siH9HenAiRVamrv*F9S?=UrIggaa(iD7f zw_A?p$|W8q`sM!s=CU7;2`AOvtaQ&CH~ofLeK04Pdx_V&$VV!5?&m`%A;tL)h{utv zJ>MB8k&<+8kcqzGv&1oAh-(=UyVdCtNg^0iEgOcdD?nNT&=q!MW(Q4mfX0mSE<_!~7~2;>1B?1Dsy!RH+`HKcy57o=LIakK3lp z4pVxj2TW|9)IMW@E-LBRI5N-GI(J8d4=N$3;1(|{(fSi7YiHF|gUq5$Tu!0N={Tn$dpAcEUs^-4*;+K6!BhQmf%H8mHQ4yhF|sXXthFqt ze%6BbIpP7@6a0_elLN1(pW+PEXLxF1-y;dq$bThX4~l3E$l*I44^69~Pc|gutY)>QR?nT7A#5Elt7gH4 z)_SH^PfyHn5_*s?5|qBlXP)-Il=YLM2D!q?9naMd~?pSRatitJ4Qg%YsB6ibke`-&&x9-kEv(N)Uq*;tv+0Zu7Cz(&ydW8?xN9&Eg9eOm}BV=A%*cAN0(tf0@uFPe%RP$e3FRUU89U?mP0GC8~$Gr&5a}E z>9IYcP9EAaTi+Znbe%blz{~uT(9L9WmY;Ad9saAE-YB&xr%=PGWJ_xyx>#HD;qk^^ z{48;c2OWT=@e6=Zswfl+aqVci60Xy>PpvYAmMUTPjWH0U3!$3M8!cP4+aN7cYx@pR zwR;5B=Y?w4r1aV-@z}duv@)aRIaCbyi0dxvVdi3@vhC zVYFoRsN&I&6m1d)GF)HheDOz(3$YBV6I@eIEYrXwds!r}ECfoXk@LnfW5y86?Prgt zLJV0f%6?SL#fVPHRFV39x?UIR9Yd{tD$EX_3?c5F_NP9ZTbNce9Oej~#UBVON4Ch6 zc%SQ-NQ6%PP_?wtU$!{R&z4rTp%olMOwyU0$hE9loA1lVp)Op{szJM(^1_e# z0`(rH!ephG02tU91uIcUsZBD54QhF9QkB{VQ4<_2%vQ(Laj;p4t6+s%(y1NRp-7GI zvF)-aXOwpm4dh)we=nHRQBPed+H(bI4GMw_gjFt0AzFARvsXR1TT(nZrFcD$W60&m zO302}`d-OH%kkVyOe`He!MM?6I$0;kGN5GHFa2DwVY+jUMFtRjkpH-gg5*9E?m5R)8=%qmEQbt&?F|udK>Ns6u zEFZ_R`fe{y>+Z*!spYi1dq>_Z&txYl(g=878g>muyAYkjl>Y!(+8rOah@5rROwKTq>!4=e8KgW{<%I=wuByt!t2+YNhp$p!iWRbTFYc7?| zO>r&O7NAkhj4V#AVt%4MIJ$_+!^-MlH(tKe4~|9_Q*ks#%%A@NRxcc;>|BME>P5Cj z%9iVH&I#Lga(2o5zmB9nWkj3WMTYGe3w6nI4?Tf2J8DjMZc#&x!nAi=g|a)MiX`@} z-MZAI`pwQPE-9ekMIrMhA4;^w_e<@ zZl#yw*^CTqvN~5q4y81@%`8ukPne6*V=^5*RAlhldpsgBX6_K`>NbZ@XXLygeo0h5nmOaCMlV0ZtZ3vB9=d3_D2xvGH7!fN=&B{)H2^J zEfSUbjDrWGP+6X{#Mt=;d_PpKqdTUNI zc}2#iKMdP^A~$Qt&k&|I8upR;bK=L3C#4GXp++c|r4sa_UW{nP8uWIx8>=TI5VF77 z(b;n8lT&cq_WO54_PS7Al_guA>8CRlof-5?i(_xvJf}nse2x)lf&D%CIg;gKJjXLE zI~zo8q{PGV=ESbighQc7f~O?dt#dwo`>6{$X$Zdu={RRcJoVu zHwf&3wdT8WxEwF2M}ye6u}6tMj)A8KDE|O1IYwCSS|OYQz+$5dbUo6RT#yGz?er*_ zrCy}Zh=tKIE{9*H_VEcT*BYBBD9pyauWQq^kSktqqx#+>FyGOK{&fyHe5N^^*_$qG z*-+?@DeY?aQLHH4B&~_SN5=hv2=*wc+wsek!p7#foLo0#ptBwAu^TCww5b-YDwdR5 zfh;bmh5rCHU;8ABYf%@-JE8 zM(G~zmyzw-*&fxL9T|fu!Xdh1i|AQh=~b=BqC0J9(OfFrx{2)ZVnUv}gKr(uQgg{z z2VuH$^U$AoYWrGp;Rh>40EkT}gaRwaD})HGyN3?vVoH(U2~@6+TXYtfoo*D^o9ZUx z2Ml&;E_+5aY-F-K`IIo+usdyZzN;EvPYvEJD7qRMm&8fe)Lc4)}y`E9m(55AoC#VO8ou|h>s9GhH?RIL>CIr!zp z>ab$Tt|mzvp@_V7yvY9mPKn(*Y%C;V7q`$XjkwcH zWpnal!gKKIcEJnGxNXQ&L!gkfpw)=alOhHTXK9K->7jt;rlu&qg8NuOd9*m?4o@p< zCvuM_soN(sI7!ZHf5&#j1%a^%)SqoXk30B4O+kG?`Ks7tul!4hRE_t zC0IpG^_wZ5r{pyHkXu>qoQc%jTQ10sJh`>GrddXh)Ls-PY-xX*Abn6Zw{jfsS0AJ^ zM@`TcltrxpJ+3Q6h_*`_f@>ZYlWu$2`6MpO#{7II^9~^%u_MezIcY&ywIV;&iR*cynd@$+G_d zmOqLuV~&QXWZ&FK++2A!lgj3h!j{ZO`2UlV@Z^akc@#BTcHUJ1N3FlV99!fhZwW&EBk>SYfCi>3}K_*EK zW6Z7gru3FK2F#7%Hi$TC{yomlT)1?P$P>=e~mHzv;|->A3ox7%AvyA(pnXQ;iAQOy4UMUuZvLfSZ6_SsT%Q}~+n zyb}CKeOUtzmNQE-oL>tdcA=E@?%uuQ*S&i8uU_@b-nkstBbHS8ZqwJytj=^!Xz3uw ziK(+;;(Adxs5jL(0gz)%*?q1~W3Z+=vK{cB40@7DGaT7;qSd8B-{gD`i>JcZv9U{_ zV&lV?l+?u!*3(>j2T)utSA)&hZv_B|M$?`2xv&!#5`0g(ZF~zLk^ur_68Tjv>X`3>C z^1XyHuar4J=EU~G`ePo%_UUd;DZ^(>ZS=tj4Rc9$jViug*~sHL2&t}wIXR#xf7sR; z`uks(KjTjS07r6)&-gQ0yl+V1$jnh3JPvv6ScuqciB3Dq!(z8bAEt2J*qa{W8!mHQ z>euh3OCWqYw*5+>j6&#blH$T6&Bl0lQh|zW$R~(pVL=ht)l3_@l{7WT`3q%(e)ru}SLzQK6oNZEf#Yoi@R$ceh z{@COBWRFzZOI^C%U3cTQr7+aqqm>(ucg9R3{Fx@E!{Nm0i+n@&>9V#qNcT+fmpfZ7 zOwzJ9x+guM!@QXOp%|f%;^w|Qd|4s)bLrDBD*nb;vsj z6Sq<|1J+c)r4|5}Ecqo*Pw5O)Zf{6gNkO zll91P&~aK**`gMm$^I^nns8+OdHO?HDv}stem`7uAnN^9*?4egh%j*aki~-sse4Al zC@mAi`Zgf{01rb}hNBx)kq5FIa>s@dArA}F+t4LP9)+qF^{6B1!}04fn4uEJ%y?mZ z(PKROa2)4`jL-D&dUixm&RS9!3<^kM#ir5JgxG=!!#EYVX23qsC9y zs=bb3JF(%giJ~NuS3#esLnprchzOkb30)(`HdkG4FIvdz^Wf_NYiicjjt8X^czE<+}srpNuY%eF}WV=&uUy-XDtIyOhGCv<5%DOp$`CUh=JqY1eg8IrsCTCvaw zBGdt1d2lp?w`1t?o%SiJ!f{+9Y3cmDh15s$o>{|DPgpO}klI#?t3G_v+LqN@6y1s4 zcpwyhAroZU((4GKH!0f-SzKnrBvF?_ak(ubYls}>3u3_v$|VhmL{0ns%CyY9?=9`K?IRy7dRmQ0Fz!*o?F;+4rG}9 zW+^|zdu4ErV%YTnvNPJo;XfxxS#2(1hDC<)5IOS894BGP9fLIXD77Dozmy+&+F1D} zd`-e9+yS>xC(-`^;8dze!6A(x#Yad{GkqvAV@0J3{mx96QqsdNYz&D{{EfoKh3sv$ zNsjz!TWc8a;338K8zMFXx#qSYqw-GxEhv=j)QQ1$R@AA9y`G^o_8n~*?(c!?73G&zl!X4)t8fo0Q^4^2HH4~3CwEnW)4LIu6Eh;mnHD;u!hEj9^y0@D@y^_*7^C-+EGUfY z(O|}aSO^aLltDt&oc{oKTS#!oWF0xO$svyyFCIwrbpU-4&+a{>X({m(_7U4VE8k6v zkjz|{en2TJ)I=e&SDV9g;%?gO~t%4eS-K6}sVg3xJ4QwH150 zI47ryU05nnrtqJ`KG;U__dskmUI0M_u@txNcP$j0f}|C0=C3H#TJqwuS5blZ_}=Y3 zrg+(m=DE6c$B@|Lc41vRDXhqw7_1f-x2Q{tie!*EH`JArV?x5mnBW-?jiVj;^EQHX zFKltj#x_agb44LY(%EOC_HpzF2V29haBf;g#wW;X*9OfTVRu{JuB(d4~Ugd+I{x4F0s;$@ZV>Kbf zZJ~+N{ls3-H2YA!pkCB3XuYELh~cA$wAlI`7&%e0;f>SB9C>g;(iS%&?XI;;PTVRe z_9Zp6j~));I4!86)bJD9y0d;NG~UnQyFaEoRuTvle!n2Lt6Lzp3zT@HK~e4zauz`d z+93g?TJx3hJxW<7i`&U_In88pxyIe<0_|NN@qDhNGd~i`=|JT=A=9vrRCYh%y)zyk z$UhwYDqmC#SM^6^gJTukw}Z>K3Y8$MwII<=U(+-A<*S2jQQ>rdO59`i@NS07#R{e5+H%4t7wx* zia?-(N4c2KUfMP+Yl!C_l zE(o-C9D;!reYev~N_WX}ydo@a*BehJx~KY7Wizwa_vq3CfGGi8gSO|j?oe8LUhO4O zJ*c46?}`o7h}@91T00``ue71Ao2)W8AomX{S2fBgqX&I>FZ6PH6QYx&^&K9=CSO}8 z@tpqv)B3Y=VSHbe^;ojtlN{7608K8m^xnsYs|#uh!r2wzaz4FThxH)@__*?8>M}D! zvB&a4(t4L&YhToT2v#&j;gLT?2Wvw61G;%sQ?^a}IZ9WjtWs`M@H!7yahs=0gFZL^ z00Y>f=$SCegh`lUc1|;4GrQOa#gWtX&{VW7D@xL|tqVe9j7K4@^w?8=ZlaX0pZ074{v)?Oe8h4J1q#7gXAChJCR`p-X^SZk_fAhxK zOb*J*d7B$2R2U8QW|7*QQ~tw*cyDj&5kr&D^&F3@BDP zLae*78muzkZ)DrYy;aGh3q-)h|wVo}zwEDg5eM z*0rm(txD~1y+E}D&PUTeN5#~f*mD=-qyCifiwtc&N^>-wb>W>y)rhX25z7Y+@iHD2 z0=#yA1sb9L*Cq{e`cmXL1rW`DLF(S%bljYbNg4kD#s2`n*$bH`sNi4fMBvxiFJXnx zac*0L855kDe17LA;7>eGbF~LK#%bT_X3di;8lXQQxEuzpm09ifP1vKbI}J(Ok3MDP z=WAJ; zeY`%}iCG_Ksp6;lWTr*CpxYnf>G8~uIFw64<#2Tqr{{RNYUO|dT%N)}an;e;Mk0U4AO3W~!`wftFpw!=s zxgyt<8I;1m!>3!qH`-%1vr)W=48mjOLS=nTd+z=0PZy9DP|)Gan_S z6|4l(3eZ-Pw3QSunn^Lhh8!PJLyTpXd~pnM^z!qye&f$6jrQ!5`5%Hz#M*DI|RkPd}oV26E&d(_BoB&x*F=K;esQnMw zycL4|8JD)sHm`2w4tZ5WWbXVIZjS2jwQRikDwapx;W|Aiw^FJRV#_>c(ZiTx?|B$g zV#dvKyE_VQpEed8-A^ibABi_k=~MLRBGNx%15CO88$n)dOXCjhXojm~|Y1Kk8Ha ziE^Zstk0FQexpCRe^Q;&dd~EFM-i0GC?c1d{hIIgM!Z3^&1yWOK?iQ+H&nSmE&G(+h1vxYwz2Y5 ziqfqVT}FSlcArG8(F>rzPeQR-5})`!u)ivoS5E2f9{ck-Z?1kQP@N@#sm z?cZ@n>9#c=6svu!!YJB((Am#LL6tTz@r^&F4tw=AZns}06uU_ojE~w>zxjuH{in21 z8BDT@MNh*H@ig2+OC`mZ90eaN-2^vV4%T~VT&uJfm9lpQ+a4Y%T6ncL`bW{}{J7a? zBZll+du?i!g}-3cYLjUdbiT_<3u`o+vz$=b5K#z(lj%7#o*sWTnYj*{Iy7DzNZ zAP@L;1^F|7!@!Jt@5_L9So~ptb$+k`>pzY+MjR~IJ;+M%emM-oW|-xl*T|A~#K7Rz z#U`xjCTx8(sekWR+-`$J4Sa?|6@JbW6ev(Y5{RWpu2aWv{{UB`*zwcyMSZkMw|mHx>;^Y_2o<&4ywI1#OpP9>^Lb9ngQCXJ`dlOzPh->X4D?Z03*PBxpe z9HUE=Q-v2ILV7&B+JDq=>DqyF$cWI4r+gD%ZHW8|2-j{GfQSWF%EjG}6eufOQAh2m zY1EF(N~Gbmziy+DWW^z3^oM0*^o&TN7~MIzcqgSnl+37N7_ojmgd^%WQ01|m4;8QK z9WNiKXJB+z;Pov2jM--C_!8iB_&5WzNn^1z<;3PU0ZEA5r)y?tZl0R=bm1gkC1EpW4{{TxH@SpAdUA|A$ zM-YTH(oNVjwoiaVy=`BYY# z8-d%DS1SA3a86BYpEpp8Oa($dJv4vVWxGRQ`p)yIzgM`rVm=WUK3R1JVgkDFP#8^T z1GO7&Gv0?yayV>inijD!h3@w8h3(;9-WBLVrN;nvC1mpnL$rzCZ^n8%y3CzVqh$1l zV{XyPquBD~@2i6U0Q7Q#2xP#U4mi4=P6J}Y%=Eo2iT(J@GPeh!OYE|+!=I1%)a+n~ zTi?@XV`6~LW-MP#ffaSXju7DyYSnfZuBeV3!CF&=BHcv22Uru^^FNG$h#+3u z7ONkUY<_?qGut8;lCYLDEg^E$zyaqBOo-(Zxf5uUixq<<)t-i{Y>3)2%{WLn~63+&ObWb<#{h7ImmekcMR% zZM08=ZEoO|c1J&mYCX+xmeFNn&U~5ewUG-J1`&aOh5Uw#niz`a-ro) zFT57H*n?ZwM~mg_RfI6ba>dD|h-)SV*hgv}ew%^U$nO=OYA_Hu@7nxPf1#dLL*pk} zuX=f*Y^%Qz>6UOK_W6A3mBHMWS3*Jr5{biNtS{7Ba`EtJY5$|qNzG3=drq%haDz~+nzE@7ma3cc1An)O6oX=ju zDP;h7uaBFH9@GJ$9b3Ts_VFb4Y`{BdWAWfqO0rR5nU-+9RgLj%bTPBbU#OH-m`S7$ z9p4|vE$UlK;_Sv@oqL~SKf71`G2J*XY@fqkqQ50O=md9zpKoV~_dbziHTr`Kxjs5m zoZ6=lBcGUEnl;)ceZ_bt1RiU#Su(Yj(ByI?X`|FY^Z6RdFE=lla*?E~QR&?CWo6$w ztiRs*k6hs@%^gnV&b zI_kVP{>)%o0MJXAEU_&UOm|MxxCgieR!7v`O8c$U?7oxt18Z zb8nFa!cw?kVGm+hQ|jRzTPiK$lb~{m{Tp?dH@_AZ?h$5NmvwohooW|XD0Dx4r={k; z0|flL^@J=hO2Qf`?tk}LEYGr4JBbdImFnfqQh9T_ESwt!d8F!9&QtqH^|V&rWONum zyv4)`$G|d*qQ%t?8PlBAZ<;(28)G4qoG5x7eRP>SxGZsh7<^mPgpVOcvVNq8RUMkU zM5o|!5kMAa4Qp#KlN$Ye6uIe1Uz3?n`|La`oPn^S#-`zAI=n&N^z z<-?n?aim89o_~fwQZ_w<$?AEob)8OAKO}~Bh^d#Fy`R~7#D3*Q(3DI?#Gy>?oqE5E z_h&J3@A>ZdY7KuF|67YNacI&fn!6j?c#qGNfO4L2?vD|Ek&Z>HA-$-51*`RXbtyOyzhMqd~_0x82wDdED|O z`5Rfv!a7ukGk9hX$Hy9JKlWbG16|P)8M-A)KeoBUa1L{`im~9nY&`y9e5>alvhOk% zt}1)WQm;mF?W{(zL-MxInHFeoBKTffJ)8`-j)z20&B10mj=t5{k1Cr!y9i~QPQ9oi zwanA;^4htr9ctL!H+RPCiwd_oXW=44e)yPms*4D%9V1J@(Mqpe6DQGKGCa=F)5x{~a{M(;asyw`h5(7{whskGf)AhdA^>%*NZB0sb4KUk6W23zq zJ3pAsO>1D#xQ`;AI;cdIU<(6PwrherNRBvldE{*7Y|A9!k=dQ7yF;?^S?`WoU)veZ zma{$*@i~-qlwo`%6*3GM&Wxp5M5L8t5ON~Hx%&(Yv(@~2FJ*bd+up*QeO%qyKlK|K zRPq0d3S+sg@|oeLBv&zFr%d-j1J8mr@G=CY<`M_68 z2=n2p%MV&LM)`;TUIJgYS|0uT6+-^^mV5|}8k1{Ra#TriS9;{0dU9a1^bIgWZ@GWp%&IZ+k8!9OviS~DFhP@52ml0yzW{wHFX^;+M z-w43@GoZ#7Qb()PI0I?O3jnNfGBq_mFmk{!r_=_UZXBQzt568eNRLl)EwC?&Ll+1M$%5wF3IaeQm;=R{aX6Hadmqt zK9X9ypDyKYbw))_%AKPT-Y|vFoy+Uhxepe1goGm5bM72)RKLzNi{~uH^h@gs_LHGT zXWhr9JpxA>M+9>zGFbILm|Z7*umc$FrQ7GMxz^$g0`oL zEC~mn3OL(-KbO7YyVyD1=08=V(A!1^aUpc-dFsF?Vrxki{>^+~N83H+E;J*%*YQ#2my&R=r>09(r=|BG7wIddn8UN*{; zYv-UMwSN2Y$PwG+2e#+74b%nS`~3&7O?MOTEev4#W!prUr(P$&2%xN>)xFVL9qD6G zAL)1+*3aC}CxzuXh%?Jc#W-ad>mK}_>=*Sg%Vzg?UzsrXfy8CgPAx-xvCoI)JIk!w zY30BG#KX#a7U;U+Xq`Qy&}bRUd7#TB>;&UumPsdMci# z@y9d!(`9cGSzv9oNy@UipU1H<3-fu32ZZZ{pWY-)?#UgVoO%$wvbVt{XXIMOLa}&(1%u=^0rby&6QqV^2w#nf-T4J zRZ?cB5O=q-d09iULrOu6}Au<1tapk>n~iCwuA2)?Y8Ya=s-41 z=6uSqZhNj?`QD)DTxW#|Q^~YqPwO?F2A19Qfjt)RT%=aF(%3hb`ds_c_xc)RJtIqb zyFaXx!e~0kR92mO5-^tlm1_^E@d01e@devYXIemdshikSp3mv{!XDQ=SB;0luFxBh z&|#9(ZARgQanFN4_enTS<7z%N#oJTOd$YG7Yr*afE9U;`H!{igl> z8za9OVHjzO3UpvLvOxb>XIjvj4a;YUM8Z@K-cBd_rwArKW@lLNbqWv1?>&G*RQ=N- z>d!hOjZsxUK0H{^)Nsk{`FYx7?m9X=5_7+*VWJeeujRJBpY8$ozqrS8H)xwnMyAp- z0K;%U4Wq0Vnz9Z#;_aH)=#jEM^u0M56k{xBXadcM}wy#<^3Uic!1>2gWO^?3&(n4--zoC1@ zo#-=7jBEQ{!$^*yI%pv9Ov_rgO=EP)>@j5G^)Mwps1$w=3)B!rJM;F@3z>}I&Dh^t za!AO`KV->|%!wnL(c)SL(l@?ooR1rfnNRVWjs_` zJAq*8S^J7>&YEtD-PT!^+g%0JCEGpY*}_34sMzTs2G@{xdT zx7x@^jfLdBRKY}_?6#dRvzF~)O;@3ybk>Hc6}IP1lCJmJf?k>WX;dmLJqWF*YFczs zh8fKSQusl7sT$@pxbHzER6^cZ(}=i@syTWET6vQ2_-pnRT z9lUZDT*sdC)NXvdtjl!y)Au^^KH*uapPAGNHdk@P3Ixo;(cZl2X%mlp15IK!83R7Q z+r>abmb{K~-P3B7$xr3V{M8-|2L%)?+!Rr*n{d3<=qN&X-U4s8)Db{Ni}Yyl^Hykk zcHXdOxT<2guhN(vNN{S-foH1|7`2YFf@_K9Py1m zQ_-RECfU(?XO*UoEsL*u?p0rdN8d+#VeLJC!d^<#C#r8!GXEg`wE;Aiz@1_TC0g&@tjnD{_>`Fa#R8vK-xfnSOmt=uQ2?oUBKPA<$bm! z*^N-UM4TVpa!W&#F=0UoSwX15h&*0Tj%9fx{*r(B=;}3g^gDJP+9XR7k!TdN<($OK z5F+bCR21ybb0bvEfg){PP2Zh^^N&x@gaVW^FHIwYrJIJ~8^`_1Tp_En8NTC!g|E@8 zI-&Jf>S-u5;(Km_t~wxrr%HbiR_ooDF18B zj~xrgLYb5`=((j>C)1a+p~A9Mqy)@%$`N-qvOQCdI;2bccYoLK$TAu6#SjSp(_G}2 z&IVCdv*GPJB-L~7%*OyW@tVD)tZjyfpCc-61s1qH8GsI5dAqh~=qRoG>!WDWldF8` z-oj^5f2N1&Aobr zjCuTe&Go^wg+WIF)Tw$;J1)CXFq}FWdbRe=Pkeh_I|HJ>qet6PB=#4oXxyk~wdr%^ zJySWG@aKt=dG-8sD{P)h6;P3n3ES~5WH3bIA3Q~nvIW>mB0xFiTQ42KN!p-wa`gq^ z>X8NchwPRCniA)ce#bbph+j1HPyjGKm=`gOMyAM#Qna|W@%N_;xD4v;-(dZX--V?T zBb0sv0IRV?RYi}%+28^5QSzsKpR?^rVe?-XH))z?OrH<5e-4m-xKv=f(*1jVP>&c+ zC*ZNi{aQ2`N2Kbg4{o$r&6EA=+6tXcm=AB08i!VgM+D-V3T1ERSv$oG`hEQXnEpm& zNmgqt4?cRl!TH{a3&F6ztB;@=a|_>rB(Wftum+j!bz54jW{U`IX$ZeIWmVdpqWplh z(L8lkHor-~Sl*oB?bu79*4?nq-K%7|{7fW>Y~CuV#m@%HR)1T`-|kQ&{A*_%`4w6eHP7cbn@KvU zYJvB1Rv$6odN_h6vrQkz7eatnqeC?>7 zM$BxHHUXXf`x)=kL|VP6hhhnw6zjK?B-d})d#{(~DXfXnFWQjD*F<-2yE5&uVf?R2 z-XT!aNe7}yx##sT8E9V3oJEoQt|(PH+V#v@ILX$j zS&$gU-_nxEsvV{20VP7IkJ3;A2{{6QdD^0`1+20~n);ar78uRFTA&~Ez~rw6lkfLg zTrW>rDZHuISq>|k{uwmLZfdbC^7hYn@&rohFL=@wFpb?}hpmxtc7<*F%Fsa3QRu9; z97%Qd7j1lNLh~vrS+>a!X8IyzVM!wN8_-J)G6aFS{5QGdD^Lxy_w zps6iQu_XEVcv)KsP9JCa~+JGzsJwuOL(mVy^#(O;{BQ7cV;D^A$@O~AZ! zl&4H>pxI_f_kl1!s}oc&BeB$93+kaIgrfQ=Eq7EN`K!w5PW4_5bsE931rl#kvqGho zq?f0ANl~)bEV<&0 zA@}|w$&y+<8gJJNb+HlbqkAhdkZ7}p4_flv0>h+@YILk7yQ~xKYTKr#+-h!9vh74M zy>hWjg&j9Kp_Ceqa~{dxjT7y%?7cp2Br8c{W*We)qq_LV_`&n!CUx6y8N4U6iTU9M z*dK_WexdX__6B)fltF|2zpZMbm|nlDf7;6g?xxr9JxaAL1srpKjhvKijw8iKzx*W)h^v6MpRc zIB%1^;sa?Y%03A9(GKM4XPuSuPr#^|?L-wlr`g}p-=IH3FK#cs*7Ky{2zN?GrMFo~ zeHESTQj!b>7R=C|Wz9P5ojsD_HJHaT=z2yUX4^&|_^go{+{;Hil0+1trx!L zg|YcuFBRIFjswitxS9uHy#9gDd~(LTh{A=5XZ}}M= zU(TnsE?eK`Pnn(lqvaS!Gcw9wVtwScBhk6k52F*lKQHi$ht>NzO1B?b_i+RoIwl*R zg>EbTrtn%lx7{F>tx~n)uDQKwuUiSc?MxQ8a-LYz*CxSgn+yO1k(U#ihgrY8+4f?7 zDJ&IhbhiuQ%%EKL22~z#Ma^uI-$e**?oyu!Iz~9W~FE z``o_rO2>+oQ9Vw5n!54C?%^n^8~i|nL5T96j!1#B4v$^8`hGe|ZO=V@#~FMR$5T%g z=ZBDfbaZtbK#XEYZ{rJ`KMHsrr}AzN!y(VgUeV$w7MLsO@6gdm*SmK0yX|{DBAfpe z+s1E;(qU$wq-y*Y|MFHLl^T{cjkg9Gga-Zix*#f^KLYR3HR=@$Aa~0;`jv9YWTgjJ zog+f0+9}sseODTiMLPY!fs8-ul6-J2&9Yb*bid$>(+c#d-Qk*2(n^}thbyNw^b? znv7?wS?q@R&OW2-*ml|av;lLMjv0Zmw+DtpR?ZpgJ|T(R^}fruZGTp((|sy5ju{-J z0SB_7v9^u(ef^qeQwFKf0pgRaedLlm8GbcJe=&YtI-zOp(DQ7X1-iHWAIGsZH7!Bn z2uc66uY~5g`NnO(Y`QbXHk1-do5Y=dg;kCAtw=2I&8S*gm)lpPNH$Lcs+o)tMn&UZ zy6rTwD_gx*G=HUo(;>YgOZVgE%k&;zq|pn-`z&pBl9g#1QBK~ml8gUh6Ar_c3Fer*&N5$xFeLc)UBM2enDCE;0Zlc0px>LooE2FO0gjs<5%^E!AQ| zIN_P=xaGiB1jYUMR!5~BM@UL8wcz7DjJz~{edx8IYV$p_o%vVK0ZjL!@ALu(<_naQ zeezc*^$Rb0d(+BzX;!9M}5=Hb7y=9-n2=xnwV`z!2w|7lUqz= z^y^ z7jp^&4WkNSsb>v(t~Ocjn+!*@T0LWPv={9RJJ>_rf5_g>k4@43OM=+{9jIN8?{UeC z#td;_KLTh>PwMtczhQr%SXOqvA|1@RDit+C67VyRaA8gbPH5%f&8|V-9G&W{nAk6Zyg5xQs!*8<6-Zd10O2s*~8}|;6Q=D zkQ`gour70KDwW9qi(I+*ZU4$z#;}hBEz$D{?;q~$Elej7xp)Jj$e2M^E*C#Y!@g3+ zbmL3rX_jZtaP9$jZhUcRhRXbc$OGM5irrqVQ68YM#RyL~=%qRJZfchO``YPSIBB<{ z@GLI1O`y&~Kvt9-$;Pf1m=@hX$vckHs(T`|4|;0C_a6M1JA#C$%Q-3Wu((-k3KL?m zY)~3~gwLj`WJXkHg$#d|k>lki$q%oNyg-X9$~=*7sWURDTM-R}|A;_p7}6@$<@h?h z2+W>HyUgSqn--19=jg3JX8r*$zWN%HpyYQ;@*oqBdjA9gP+HIUivfX%H1R9d99ApM zPtPNn@h+S*Ih7?|DPqTO9xJK2rSH367%=NQ$8dpXy{xLPr4LFuly{wupc4>xB+-fS zoFTPt4YCeQ7wihT80dw#{A242N}GwHR6h>NwZ3t>1QwNYf~PF*7S0~OzHI6*l#4?F*F?U;sR&(eLG9zA* z*ySxYwLf%gC1i=RaKq69M$&}daY~&jDz&`RP|i~!+TXu8Fe3t%oWWxFZ(ate1Q`w1 z0B$C);%NoZIy>Dk0;Jg4`kqj-qB$otTKWX^yBbvib(pBQ0-}m*s$|XkDpWzE(lbP%rTgvHIjQG((gzpBb+8LMWFYF!0ie zlcNq}B?@ge)JuRaS}M8a?75?rZ{Eyu?#r5hFzpx+G>3}C7Bcy8oHx4Y3f!f*riBD7 zb30p4<5_qsa?H|*K6?v%5a!v1^(thHkw(~E`*zB9pL3Lhi+*4TxZh-Ha9m+gL|LlU za@pfga~_8W6MSFGOb?hKV{MO>F5Y>8z?sBn$1)xg^Fy);<8c3?#|q*5iVAQse;30e z<$VeL8%dzj&>|11(tL&SS&!p0tvaS#bNLM{V1-5T4YN~!4P}4$v}6eDjj&QtOxh4K z#E=tJs)`ZK&_{QjZ;5}&6YLVqV5Rzaom+zRZ$fDB zkV@VRq=J1u)`3v}$6|`)V zM*u;s(+%mVEp@9WNrri=p$Z?eT3cx`n)YXm-%H(~BRLLSsoHt#y!ruEgPJ9?P#@5i z$Y8NvG3AX&|I%ZF?UUU<`e66X!jkV>3g^)kPET$b#19jg`AY=~rWM~UczSiMwC(u^ zj(g!2{Ig7qlEvXTDZJqur%b-GB+a^IHQFX7FbAc(B`4w(>LFk)|0r|g7gIc|rKn-K zIUUk}d4ydRYsmPZh7Drhr64;oo`cBkmUNPt_R5kliEOGh+41b;7kexqu3=hLv-tKw zd6|-BkyKe$`Z@{jRFVNgF&%PVYJPapo%w13X#j_zB|=ag%!TPRV!{Y0#v`5erlKKZ zS=zo7wF8~@XKU4RgF$8p0?saX!6I@n54?3(-2Z+x4SHxkk4hId$u+4s1tZ#-5`i7X zy%t{V_<8Wvs&s&Hr>0-fiGETdAaJOXp42J$AF^+XW1+X!mssv8VY#~DioON84>`sb zW*-YNz0G=d0JB_HvA?Mgs zSgIQkIAOCqIR9^7r4b%;F}r7iL72zN<%Yu4Ssgg5e6SZV6ZGak)X-PMbTrsp=qUQHu+TsAT540cB+m?=;W6d}ep1IuH1I zNVGrpMtX`9ZMQpKSNGK4W1~m!)$5(6$Smn;OFeM*Semjpq=PWnW(dj(5Pk>-?V5#8 zJn>}^!#P;#YczpEaoNf2t`J%!hxN3go*OxxzV)*jbw2U0-lZA*%a?Z&C=9u9O1A6a zgZ*z03M?UyP-)-{RuhD9CtI80b+m=zgY@yuWmF$RVG4?Yh@5EUzK|E*6zFe^LDL=- z*%K8*Rk@jOVdk+CEOOkUby*H;SlzsJJnH_;j-u~(c2QJMjdu!uxWI?x0hcW>vM>ql zaHZ0BNA$hJ8~=}htBPXGQHGHz9qECaUd5k=wN37UpqYy8r6fAc+0Cv*5-a_1&bJfm)$(-V@=CFQ&WqpB7}*GdkrtMcv8yp@7G~%zG!l z5SNDWGDdB3St81wMrB28_ula`CG%AjPDKz0 zB{wI)BQ}wkxV|vUdB$Ui@;AEZqBbneXH44%%2t+`vG|-aF};ES;6$A{-!Y~QlPq2J zoqUIiWKQuxjm=G5L|0si4c*y@vN0?6ke$*dgCu|7~BPWkdG6S zEGn6HFOToT4UehcdS;p5%|XZxA#JJW@n#{gWApCn2ryV`7oL}f6Dee_60g#n;rFRj zG7ZEUT1o`f(A!F8r*oEi+L7;oW6hlZeaCK1xmZKTmq+HIKBbvDGUrttUh$PIqZ>Oi z;RXliwa)DJIZ7bO2H|6Mtf2$?v;XR+nCE2}&!WE`c|<+LaF!EcaQFu$yaX?;%)AO6 z4G8nh#7XD#01d%NQZk(+YTe;PglU5Y4#%RtSq3AYf`61`$t3YI(9#Uc0ZB_<}K6HA7e5W+$iPOI+jt+E^%UTBMt69pR z--`^F{E$hkIEd!B@uZ&fP~9Ydk7lC+q5moQrs#x!!7+$w$9?iie&K5)IPa($C?Va+7xI)jGKfDx2#&Yz=;mu$WfPF|lgBD3C6~ z+M(trV{f$DqhiRkRzPVK-Dy@(!f?`2uhQwFwXE&IgA?~$@nZ31jH1(xKcT~Z9HMzy z&gqZ-D{P~*d{&Aiarfu&p4dNRgPz6zkXR?kaE!6cva3Dy z=u{n2LzmRUuNS<)S&^+}DuGYhk|63El?u?)zX$kwkgm2>2KVuy!nmJ?^F*znrgNqG{+G zb-fB7-A<+rejq<{H<`5J-*b(MSF{i@Srwx`1H-Wbrn#6Dp>Uyt`99LbF$X?3FcZxz z#=}efCgtX+mkSb-+08%PSKwNhr+C8?a$C2IM}e-?zUF@fq$(Mr^pgsluw|7Jq`~w& zL>brsDY@r90l2QMQBU#q8Zzk~AWFl6Ao=i8eTN%8s^(l(EQ7+zQ~6!*IgUs6PW_`r z6{yGl-sVdiu;dss?dMI-tJEQSin|q}ZH+Ne2$Jz4Tv1J84!SbK_|22!Aw!V26w`2} zI{)35)6%?KRLzDHZ7^B(Z2WR27{&sYMENWjP-66zBa^f&6Q|)$>5p)ht)_f91>@8?B=|I@YW^~&TtFX?kseyk=i)QgYuB&^py8<(ct25N)Q;THQEAr1XkVJX5FSuE z-Hy-HptyS)x>n0>@7MA_1;!&Zkjjc4K-N%>@Sh){(!Vj5wQQKL=8=9)-*7bDxRN15 zA#|c*^cWc4!uUIIuHBfrrHKgH{LRy-RiJ3j6ooL?fU=xnP2J+F2OeU=AL#HYhQd$8 zN5>V$ks@A)xYGp>K{%IJ7BoE$38L)=I=haf7Akd}Wk=1TWK?)u<~3IMoxg>5iUxet z-}7KQZxD>g2=fY;aQ4p>y8Gh0r*r!F!`6M}21Tg%W`FL_O>FTL+D$h$fEy(sWRQ@_ z`5sfjTh95nI>?KT^BdUR*{8Y90?%bIoyr6fCpW>5?B6;%dIUTK31TIv!*yjmH~>&a ztgr{PP)OKPRh1jOA0SF z-6q6avAJC}gI>94lH3n7VidD*hNNFf6~(4kdiIGt#$<@~F^`j>Q!ZVB8y{EQzg%Bo zJ{&8;t&55#yv4sk?tevyu!8HNebCr)g#GVE-7vYT+HZ%5u?;ONUv~|CuN^~y7tszk zqCKV2yz`(z&Iqr!r#q%C@DSJ?1I!YMTYY6VFrs9L%3$slhQ{UJX(N znT~;Ot-2DVBum-%gHZ~%zS$8n+3(z@zdeWujTMbzVqMt=ksIs*CDx7&Mqd)AT}CVJLUTRZnZ}MI%uJq~wCYyrcOxDmh^! z?OollJNN3uN8+1fVEj;A5(UI17bR>To--pP-drS!&@gZ19gC(;kIUeOhuxX9EPLIM zrrC<7e?)dqUq)M`0Tv$*yX7H}#=)|gZnR`cvJe)hD@6KiOVBhF5~lW?qF`oTB`iOf z^#ydRe~IsK_lL{Y!uX!}zpm52iB1@2K|SP6m3QOB=iE$YgQJ+LcPHdOc^pUEW{BD+ zGfTFhMt)w>d56knfkch;zm4R5QC2n({E@C%kw!$1Xxtmt&;v4XGOFSBgJ0*hf!tLP ziwcXUsGIk$*VFQT{ql9v*6V8*!&B)EAcPf7L%P=Y3I87DT71*Z(cSN9LXN+=bn4U?V1a4LH-G~o_?h`T7W{b`K3um z5d0_*eo0I0QK=N_TYgXa@_yoLjI}``Z;Dq1b76MgNm*>_TydFb%*TDdfp@@w8+M51 z%-k1OHbmwT#Ua5EhORSe&yne#0iCF1?q4rHzBF8#a9Jh#{zi%)2ri~BCRg=WsfwXdUW9p5p!K}0YCk)jY1D?@!9A~p=n(Dw zDeJM&vN^+v^s#tHxe6+P2HFNw*%o+dDU$JVM2iy^viWh;BWL>LR`}5)Epk^-kBIqA z#SfoLLGRyg)L~r3!icES&YFXuH)XZgD_>#W5~$W69{?nM1Ac>#$nzE~>K`(WDcmVOgV#tB z_j)#~>WA3W)7;avXva-p0?@2T&ao1z$rm||u8N08t0WmYGB8`I;P*r$aPEM)dio_w zI$yxu*;KsR)8I{(^4NEANeDB7c!u*lB&FtKm50F zrIl-vW3TqiqSIcc6{opbzt?T^o#Qfzo&XjC0!LUBiGxcEvrn)6Q4kU;&DaO=a(c68 zJ3=ztOG>?fiGwFuF{I1?p^#&inYyXVC(&Yi-34A3q=j%&kDvL>Wh0ZHXe-Z&O2~oy z5@8qgbX556$MprM=OVMGQ(CS4{7?No$#Lve0QI9QP=CAD1S6qGfGcvzVI_- zfXP_k%K3yYSBX!IO__xlf+*em=Skmk;3lB$^kEm>-;1VuhH^&-OCvZV4RIp6xAhaZ zuw1fCd0#~cP=~8Wp--=$kLoH*hH8EpPXIad^YJAMne@uwpfh*JJXD@Pk$JY91~~vT zr4?I?D5wSd3Y@!qK(CtraMSgCp0zHZFKL*qdZ7+qfcg$w-HNQ0ZRA4igH4u!x_uq! z$y^qG@HA0%pK2IIAAH*` zbGqp>w>ir|9J`B=kV2WnJKvVr1iZlD>mu{+;duA92q-YOqGhafan7QPEK#N!`Ch_^ zq1%j}K(xkx)IgK!#*mXIG2<%Jpbw|Y9>R)Rq4<3nFj7}{H4q;x=Ri*ldbg2zTeO#3 zYcERelJXL+(l%tJ4@YI?uMk`5JKgH$B3_Sk;3_z8XHd`jJ-vrz_ZSD`7E(NYOMkZE zw*_82{%fDDWIH6o{)gLE>LoPCtAM2W!)&Nnv|C?!qG%%IOSUFo)?$F<>|e&$l|gZr zo(n9D{Y8Y=R0?^8oQL2;eZwU#E{j&niPA*VZmw6`lF-4_5E&kDnKO2!46ZmDEqjlMb^q55kr&#S5UJ8mxd@Atb?_c{Y75y;M3$D`bH#3IA53yXm zT-X^ET4CLyb=L6oC%1(;W32n5Qm;8jQQBdne zF;6k&fePXH+G3;|8wOS5iPE&EP~Gxe|*({Bz*a}RC@MME6{s; z;-U{o-Bq26M}KxDVHCSC5sYuSG8))YJiEH$DQ1vcWd~)PpC&eD=`p$91_mfG zzP6%O%i}~Ks5bQ?fbAk6YBGP>PjmwjH>;N@^F;ZK>5%D7X<~}kac=i{w35~@|AAlr zE#ZLt)lo{PQ1_IphR{2&yX^K9`9owUR$#l$?sYj@_89zLt@378y|~(S*Q)MZ@wd!h z@O#YB_@jTw0&o|{?fS6YbTn%nA%LqIzoxtXaej=BDAJCwV-Y4g4=hbyd_B!k$--xA zg@&xoK2~P?Nz&(;P~Aivm!2WENmH(*moC43GF>i*UV1J5#RPo1Cjqds0)1tCfa+@CSN&`a2<0RwBw1 zmL}sLqm9(d=2X%`tT#3} zP_e-?WQScuoDBpEcxivEAsQjMYq?tOp0)?xos#jfr*>DgHYH$VUkaTm*)6!BSmwQo zj-*HmUMno(t)6v+mmO||9??G_H30VFJVl1|WP!J-WUXr#Km4gS%Q7G>c5cdbaUsT5 zKJ=nY@@>(KTzS~b$ng6)vu48aYn5C;<_&WrwDT*hbA3(IRo7|)MprwkuP$b#kTj*+ zmCYKiyzmN@Aw53-^dGX9Q?Y@W|ByYzy#~DEPmr0H9pjgyx!2J`tSNO)R!Qoay8JPZ^ytVc_)%3r;a5oX;+~~zuk);Hc4j|y4sIKD zMjF6%v(lH{@d@TWy*2g*%f}jKDEDj6&Z|h=e!l7fduQ{uPj$7dJnr%x6CcxEpXM|9 zt!4;CZP8TLDOMe-3?^nVw3yiGPM6#xHIkaQyQOSV0>oG#U5Glyb#(0=c~+a-G$ z6mi&cQ;fTjaN4)9oV-;{N|HTklTSQsVa8*0gepL}+_ZN3^?$|rzdn`TdZX(RDFI<- zZ?s4IK>@-tCM+16)*G4tB07I)Js#oGIsFk&wxlJuo;?Z@6_OuYa zmf9pCU!jwi1L!L(VHz(-Nk|!_aJmq9>1CL|32t289tq!8&Jll`Ay#WR#hZ-~s6N|W z7w}^e6;%L`$qk(H#FBBG^@xAS(x!N$8^mXPCI3RhRR{j})Vdb*73Nxk2UwL0alk?8vJ>>oQ{!6hX> zzoXhwZD~nD`MR+&Tsu-s z5=b-&G}YxUJ}mAa#jYk~_{8%aR%mG4>zCIi6SbcB@auC*wNSO-K$e-u0mY5(UPVFh z{qbo6VPP&zN-;{Nc~pwJXjC0AJAQsQ0 zGv6UzP%@_S8}QR5`*wC@%vH-q$oj$p#Phx-O$|bh>eBF9^mqM3c+}Knu=r)Q3`mC~ zGtP1@t2F|63icTfLMU1?&D<&B?D%~RZaxnsrWaX_3d|;Yq1`>)-KQ7^yNYyKNLhvk ziGS9{YQIpA{C{MwgaASSF}u~MzTerrnMnz) z2^TqyPxy@kBMDbMtFbb&*8ku>CT<#YqRO5rSuYi!jw;hdM@E%VJ3BUK^WmH&Fmhqf z#!wZ`k#`GI6vmI*iiOl@vnvxRspTfCS}QvOeI{35-)kMoT3M*j;pEQozP`wnv0&oF z#+2ptb~h?*x(E|DprD|Tbqjm5;Oqp)Tnr|H=lVv*q|le*2(;xL=JDiyrKWSD1|w_B znwQIIw;9BEaY;j{z((*4Y2%zV8eWfnJKxP;w zQgRC4QO&6IOsZ(-0N(&O)4)z@l_Fp%lyBL}R~CrcOylSSF~5gs9qHtA?%nVRw02|5 zCW#8-+?~%#PhiY*3hg3bbp}nrxK;oWc&toD#rm6~mMj5I`u|=Wk7K(cDZhq%dT1M% zbfTn;Cr=e4M$lzgekTHzCyG-&;UkkvPG>x@Vrh_~lWANd()yLx)yyji7#qeNk{fzt zdZ62|kvrCE1kiQz4L1=EjnhfQS~* z){ugBJU|2^b!BrLC4=fjv{7&%6GX`xh8NK_cLT~8=&XY>lFCFTSkz?kN%O8?HbgX! zIHO$hTqs;97lV{y;YSnEU+6~SMH!hs00|+vk<#4N+xtyYl+BK9(Cj=&M1{U6>>XuFNWu*V^cq^=}2vW&A!<3By>tfVL%&7 z`|vCFcs%IEJZS*d`TQ$|z6jr+bF2OZ->d@_=JsgcVdylnGvb+(;1T3DOhl{F^G+xF zt1z(?pgF2{Xv-6RZ*Daq2g1I6P8;5!P4lEO`S6_R&V+bk-ij?j(`)ABRvMHP`ZsB(aBoyz9yoH73=o_G_^_e=?B z6B_N)@OFkKN#az~b*WUT>F&6VwMjCg<5^0F;>#4~qfK~M_xfHR0A+FkCdB@vLQ~F( zRuoQg>7FVE+R1@AqmHKdtdffRmkF z8UaAT)LTWclEag6o0IV@r=gY-r#bcQh8pRL$s9H(BWA>+16YyDag6=FFNH%EwTgriVAO+KASyqX%v5#y2SCZ;^8E=J4C2boi zH<#U%av}aCqe@&)`&kbCuhu(To9l0QhjHUqyfc-+adt=j-ZPlhTMEQ>ru05ylc$RE zyyEk+)LV(|LK`ej0G4n%HGf3~NhdH29-W+z=gAB%E z05rMze-TbvlTV*ljkt1mv0?c_iy8ZZo74@wrT?a#bV8f*x6z&!18jPdD9coxSP`3` z?8m@0Oq$y#{@zuME1O?^X{ zck)tHg!qaxwVOAr8+N|A>Q;^$wL0Zcy!J}Ck!xz6mX>$ zf%n(RswmEgB?CJ8X{<@~k=LK2XM;l)V6Ehj6HTkAz#vk986KL(E!UmPxR!IO_$k&d zjr%8F^;YFvs=~4}&RjM!%jxib9#UV4f7JUqR($BF(v>qFjMeChWuB#eokcEBPAdn| zkbX&3K^|s*QBmH{a`idN8%Vv7oG52)L}%g7xVmR`!#uV@^ShwM3OZ*zC&To?Y@SZ; z1@9h`97i5G(s^8`&1h|v1{*XuDX?U+lW_Hv@9LGTS_M?8ej?T79Hm&Lg0W%<55SSq z42rlN^v8*Q_%e1qD-iH4ct|a#)VorEB|G?i0JNW6E(Vvj=b2kiTU?Huk&cht+G)#L zYoE!5yS25{W?fM**?6C*+3;6#CsrN{*zlliNE$}3at4TdpubQ`dXM>@z%V*~IerPF zly=CCC7s^du7K%}y%~{?`>c%^L85;d!?W zH+eoffxD6Pt2Xi%Vf&0zCguvSH10MV#__Rvma^D%oP8!?AfM8@Vx-H_$WVeXsXOm@ zrq#SsB1=NK58~^?XHEt_9bRx3XG){JoI%d!kaY^mek5TM8_raT0Ze69%GoH#-_MrQ z1!jM$Fprln6%egp{WRN8a$T2g1k9;R&n=1}5)}704(uHstlpow;vkN#*;^~7Gi~x9e)G;hYB#**p1kq|CI%0A% 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μSS(RB$1BBtyo2I0nkP{iQ7oxl|0;r8Qn7HX*nVDD2b4?+^w>HBoj zOmlC&B~O$oTSf+$0(2#+VrOX!C=+gB*+X&@$CIGEBa46DqM^uM6LV)t0Ga* zxAbu`EuLz&!-NG<OOOEu#MpnXuZuh#6EhN7lFiph4At<#${Qw)K=Bbp=VTWTpWI<%Hix~H&)BH zq7S#RYHc-kN5ZG2XTu$P=HPwtE3lgL#{+K(s+j%Ey0iP4tJrH`@{A)WPUINwH1 z86!I^Hb8i5$}2651nl{-ut(?u4E>(zZmJ&Bzy zikgRPKk4y-7_bAcxJYL*vI7aJR>F^U=q23E=-CNBiu?@8#7RA9z||qMFJoAcK#`62 zE9B=vagn7!Ni!QKpr(ga#tkX4Zz8kBHCb7wx$RW!KTTy=3Fv`ANC-*~q$hA~q$V1R z!TQ0p?_q;P3G5MppoiUzn$ufk@1boLW$e^Kbse10FRJ7k%FSxZQvk}?gi|SDBCI5= zbigWOpnjJkB@zL|vz8A_Ak*1U6|Qg@Y6>Od4X8+a#(6ptLA3J3MAKU$`@c7I(NfB$ z5lI!Es4mtqMlfcIhv2b<10(6nqBj$B11;qGZl24QYtW+mnb!CFkQFrI-N{c$i!6_C z)^^t5<~rjO|EGfM3u!8h;@`dA&Fj>8H)r#~;hjkh6PfD|xMF^Ho9imBZc>EMAC(@g zmmVtV#kyOwrui^)mMiY&R%-+q7lelGMU5(C=V1@@ zh+Z8Pb$?*ROaLp%K@;X=h0ck4=YS+0ZZt^4OHFt=zzs1I&=HGyZ3HnOraDZ}gs{DU zW3`X-R2#rb0$x~iN=ZOwIee>VK$BO&88VLm$BI;BNgOPQumO_9MHpH~#QI|@93K2U zVMGRD4Y5gJ1gd=VX9OD>HyG2>PV=FJ!>!xutOEJ{UF9b(kf1X zb1V7*ctY)bhy;LPHp9~b7sEE)=Wq^WYce8F47`Yjt~m7lL>S)HkMp+*Q>5Hi(If4# zp~Q8Ado8cNn?^04!hxc#EPlN!&1FdpR;26;=pT0YUKY>!EBVQ;*(KJd5t~HQyVZnb~l=-sPxtD)FJuxRObdgEmcP z-fGoTVwMb+j_1DGx$h=zMN5OnRxVr4wkI?5H(hOw$`a}E^F#S&N6r^-r`cNJv^$LE z+6G}n*bJ5LCN_LVqrB~H3tk;ljB<79Eos3JPaCJ4s5jL_Pd!`fmAv$@mA}AuNF%Y! zA5Cu+S-YBRUc9(JH&l{!x>Xjo{l_;I$Gy1>^Jj@hBg^48!QP$eb3SjQm5ZcLWX49z z#EjAOL1n7-*SB=fK+Vpj`DOkLwQ3jH;O4;Epy7rurVdw6w>X$oOiM1LFy7$p^`^Qz yrt?RIGH$jM)sM-qp=DuI$jpBq-O_Q_bSD4L=D+cNxljM(Bt0?kXlV8BzlHxTXR6`= literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/ReadMe.md b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/ReadMe.md new file mode 100644 index 0000000..f33e6f5 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/ReadMe.md @@ -0,0 +1,111 @@ +## ESP32 - SX12XX Library Example Programs + +Having originally tested the SX127X part of the library on a ATMega328P based micro controller I decided to check that the library was code compatible with another popular processor that can be programmed in the Arduino IDE, the ESP32. + +Unfortunately there is frequently a misunderstanding about what an 'ESP32' is. The ESP32 is a single surface mountable module to which you need to add various components to make a usable board. However the many different types of 'ESP32' boards may use different types of components and connections which could conflict with the pins used in the library examples here. It is not practical to test the examples on all possible permutations of 'ESP32' assembled boards, there are over 60 different boards supported by the Arduino IDE and I would go poor buying them all. + +Thus the ESP32 examples presented here have been tested against a known standard which is an actual ESP32 Wroom module with no additional hardware, apart from the components shown in the schematic below. It is possible that the examples will not work against your particular 'ESP32' assembled board. If the example programs do not work for you, it is not an issue with the library but a difference between the reference hardware and your particular setup. I am not in a position to assist in resolving issues with particular ESP32 board versions. + +![Picture 1](/pictures/ESP32_Bare_Bones_Schematic.jpg) + + +As well as testing against the bare bones ESP32 schematic shown above, some of the examples will have been tested on a small portable unit I built which follows the same principles of the bare bones schematic. This PCB was developed to create a PCB that could be used for a small ESP32 based GPS tracker node, initially for use on The Things Network. The board has a GPS, I2C SSD1306 OLED and RFM98 lora device. There are options for a micro SD card, DS18B20 temperature sensor and a I2C FRAM. With it all assembled, the node consumes around 31uA in deep sleep with all devices connected. The 'Micro\_Node' contains additional circuitry to power off devices such as the lora module and GPS. Its highly unlikely that a standard ESP32 board will achieve a sleep current anywhere near the 'Micro_Node', this unit is pictured below; + +![Picture 1](/pictures/ESP32_Micro_Node.jpg) + + +### ESP32 Deep Sleep + +The ESP32 does some things with I\O pins when going into deep sleep that you might not expect. There are functions in this SX12XX library for putting the LoRa device into deep sleep, whilst still preserving register settings. The current then taken by the lora device is circa 0.5uA. However these functions will likely not work directly with most ESP32 boards due to the way the ESP32 handles the I\O pins in deep sleep mode. + +When designing a board where a very low deep sleep current it is important, you need to build the design in stages, checking the deep sleep current after adding each component. Debugging a high deep sleep current on a fully assembled board can be from extremely difficult to impossible. Achieving a low deep sleep current for particular ESP32 boards is well outside the scope of this SX12xx library. + +
+ +**3\_LoRa\_Transmitter and 4\_LoRa\_Receiver example programs** + +These example programs work when used with the ESP32 'bare bones' schematic shown earlier and the 'ESP32 Dev Module' board type selected in the Arduino IDE. + +The pins used for SPI were; + + SCK 18 + MISO 19 + MOSI 23 + NSS 5 + NRESET 27 + RFBUSY 25 (SX126X and SX128X devices only) + DIOX 35 (DIOX is DIO0 on the SX127X devices and DIO1 on SX126X and SX128X devices) + +In the example programs for ESP32, you can start the SPI with; + +SPI.begin(); + +And the default SPI pin outs for the ESP32 module shown above will be used. + +Alternatively you can uses this format to start SPI; + +SPI.begin(SCK, MISO, MOSI, NSS); + +And the pin definitions will be taken from those specified in the 'Settings.h' file. If you change these pin allocations from the defaults given you will need to be sure they are valid for your particular ESP32 board. + + +The SX12XX example programs are specifically written for and and tested on ATmega processors such as 328,1284 and 2560. However most will work, with minor modification, on the ESP32. + +This is a run through of the changes that were needed to have the tracker receiver examples; **25\_GPS\_Tracker\_Receiver\_With\_Display\_and\_GPS**, written for the ATmega328P run on the ESP32 bare bones type boards described above. + +The original SX12XX tracker program uses the SPI interface to talk to the lora device, I2C to talk to the OLED display and software serial to talk to the GPS. + +The pins used for SPI were; + + SCK 18 + MISO 19 + MOSI 23 + NSS 5 + NRESET 27 + RFBUSY 25 (SX126X and SX128X devices only) + DIOX 35 (DIOX is DIO0 on the SX127X devices and DIO1 on SX126X and SX128X devices) + +The ESP32 I2C pin connections were; + + SDA 21 + SCL 22 + +There is no need for software serial on the ESP32 as it has an available hardware serial port. These are the pin connections used; + + GPSTX 16 //this is data out from the GPS into the ESP32 + GPSRX 17 //this is data out from the ESP32 into the GPS + +The other pins used were; + + LED1 2 //On board indicator LED, logic high for on + GPSPOWER 26 //Pin that controls power to GPS, set to -1 if not used + +The only software changes required were to change the lines in the Settings.h file from; + + #define USE_SOFTSERIAL_GPS + #define HardwareSerialPort Serial2 + +to; + + //#define USE_SOFTSERIAL_GPS + #define HardwareSerialPort Serial2 + +This removes the definition USE\_SOFTSERIAL\_GPS from the sketch and the effect of this change is then to remove these two lines from the Sketch; + + #include + SoftwareSerial GPSserial(RXpin, Txpin); + +And include this one; + + #define GPSserial HardwareSerialPort + +Which sets the commands to read data from the GPS such as GPSserial.read() to be in effect Serial2.read(), which is the ESP32 hardware serial port used. + + +**Note:** The provided lora settings (in the Settings.h file) may not be optimised for long distance. See the 'What is LoRa' document for information on how LoRa settings affect range. + + +### Stuart Robinson + +### April 2020 + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/17_Sensor_Transmitter_ESP32/17_Sensor_Transmitter_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/17_Sensor_Transmitter_ESP32/17_Sensor_Transmitter_ESP32.ino new file mode 100644 index 0000000..8bc04a2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/17_Sensor_Transmitter_ESP32/17_Sensor_Transmitter_ESP32.ino @@ -0,0 +1,308 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 03/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program transmits a LoRa packet without using a processor buffer, the LoRa + devices internal buffer is filled directly with variables. + + The sensor used is a BME280. The pressure, humidity, and temperature are read and transmitted. There + is also a 16bit value of battery mV (simulated) and and a 8 bit status value at the packet end. + + Although the LoRa packet transmitted and received has its own internal CRC error checking, you could + still receive packets of the same length from another source. If this valid packet were to be used + to recover the sensor values, you could be reading rubbish. To reduce the risk of this, when the packet + is transmitted the CRC value of the actual sensor data is calculated and sent out with the packet. + This CRC value is read by the receiver and used to check that the received CRC matches the supposed + sensor data in the packet. As an additional check there is some addressing information at the beginning + of the packet which is also checked for validity. Thus we can be relatively confident when reading the + received packet that its genuine and from this transmitter. The packet is built and sent in the + sendSensorPacket() function, there is a 'highlighted section' where the actual sensor data is added to + the packet. + + Between readings the LoRa device, BME280 sensor, and Atmel microcontroller are put to sleep in units of + 8 seconds using the Atmel processor internal watchdog. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + The program also has the option of using a logic pin to control the power to the lora and SD card + devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are + powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + + There is also an option of using a logic pin to turn the resistor divider used to read battery voltage on + and off. This reduces current used in sleep mode. To use the feature set the define for pin BATVREADON + in 'Settings.h' to the pin used. If not using the feature set the pin number to -1. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +SX127XLT LT; + +#include //get library here; https://github.com/Seeed-Studio/Grove_BME280 +BME280 bme280; //create an instance of the BME280 senosor +#include + +uint32_t TXpacketCount; +uint8_t TXPacketL; + +float temperature; //the BME280 temperature value +float pressure; //the BME280 pressure value +uint16_t humidity; //the BME280 humididty value +uint16_t voltage; //the battery voltage value +uint8_t statusbyte; //a status byte, not currently used +uint16_t CRCvalue; //the CRC value of the packet data up to this point +uint8_t packetlength; //the packet length that was sent, checked against length received + + +#define uS_TO_S_FACTOR 1000000 //Conversion factor for micro seconds to seconds + +void loop() +{ + TXpacketCount++; + Serial.print(TXpacketCount); //print the numbers of sends + Serial.print(F(" Sending > ")); + + readSensors(); //read the sensor values + printSensorValues(); //print the sensor values + + if (sendSensorPacket()) + { + Serial.println(F("SentOK")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.println(LT.readIrqStatus(), HEX); + } + + Serial.print(F("Sleeping zzzz")); + Serial.flush(); //make sure all serial output has gone + + //now put the sensor, LoRa device and processor to sleep + sleepBME280(); //sleep the BME280 + + digitalWrite(VCCPOWER, HIGH); //VCCOUT off. lora device off + esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); + esp_deep_sleep_start(); + + //wait a bit ................ + Serial.println(F("Why are we here, ESP32 should re-start on wakeup ?")); //the processor has woken up ? + Serial.println(); + normalBME280(); //BME280 sensor to normal mode +} + + +uint8_t sendSensorPacket() +{ + //The SX12XX buffer is filled with variables of a known type and in a known sequence. Make sure the + //receiver uses the same variable types and sequence to read variables out of the receive buffer. + uint8_t len; + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + + LT.writeUint8(Sensor1); //this byte defines the packet type + LT.writeUint8('B'); //this byte identifies the destination node of the packet + LT.writeUint8(1); //this byte identifies the source node of the packet + + /************************************************************************ + Highlighted section - this is where the actual sensor data is added to the packet + ************************************************************************/ + LT.writeFloat(temperature); //add the BME280 temperature + LT.writeFloat(pressure); //add the BME280 pressure + LT.writeUint16(humidity); //add the BME280 humididty + LT.writeUint16(voltage); //add the battery voltage + LT.writeUint8(statusbyte); //add the status byte + /************************************************************************/ + + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + addPacketErrorCheck(len); //add the additional CRC error checking to the packet end + + //now transmit the packet, set a timeout of 5000mS, wait for it to complete sending + digitalWrite(LED1, HIGH); //turn on LED as an indicator + TXPacketL = LT.transmitSXBuffer(0, (len + 2), 5000, TXpower, WAIT_TX); + digitalWrite(LED1, LOW); //turn off indicator LED + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void addPacketErrorCheck(uint8_t len) +{ + //calculate the CRC of packet sensor data + CRCvalue = LT.CRCCCITTSX(3, (len - 1), 0xFFFF); + + Serial.print(F("Calculated CRC value ")); + Serial.println(CRCvalue, HEX); + + LT.startWriteSXBuffer(len); //start the write packet again at location of CRC, past end of sensor data + LT.writeUint16(CRCvalue); //add the actual CRC value + LT.endWriteSXBuffer(); //close the packet +} + + +void readSensors() +{ + //read the sensor values into the global variables + temperature = bme280.getTemperature(); + pressure = bme280.getPressure(); + humidity = bme280.getHumidity(); + + if (BATVREADON >= 0) + { + voltage = readBatteryVoltage(); //read resistor divider across battery + } + else + { + voltage = 9999; //set a default value + } + statusbyte = 0x55; //manually set this for now, its a test +} + + +void printSensorValues() +{ + Serial.print(F("Temperature,")); + Serial.print(temperature, 1); + Serial.print(F("c,Pressure,")); + Serial.print(pressure, 0); + Serial.print(F("Pa,Humidity,")); + Serial.print(humidity); + Serial.print(F("%,Voltage,")); + Serial.print(voltage); + Serial.print(F("mV,Status,")); + Serial.print(statusbyte, HEX); + Serial.print(F(" ")); + Serial.flush(); +} + + +void sleepBME280() +{ + //write this register value to BME280 to put it to sleep + writeBME280reg(BME280_REGISTER_CONTROL, B01111100); +} + + +void normalBME280() +{ + //write this register value to BME280 to put it to read mode + writeBME280reg(BME280_REGISTER_CONTROL, B01111111); +} + + +void writeBME280reg(byte reg, byte regvalue) +{ + //write a register value to the BME280 + Wire.beginTransmission((uint8_t) BME280_ADDRESS); + Wire.write((uint8_t)reg); + Wire.write((uint8_t)regvalue); + Wire.endTransmission(); +} + + +uint16_t readBatteryVoltage() +{ + uint16_t temp; + uint16_t volts = 0; + byte index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn on MOSFET connecting resitor divider in circuit + } + + temp = analogRead(BATTERYAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(BATTERYAD); + volts = volts + temp; + } + volts = ((volts / 5) * ADMultiplier); + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn off MOSFET connecting resitor divider in circuit + } + + return volts; +} + + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //for PCB LED + led_Flash(2, 125); + + if (VCCPOWER >= 0) + { + pinMode(VCCPOWER, OUTPUT); //For controlling power to external devices + digitalWrite(VCCPOWER, LOW); //VCCOUT on. lora device on + } + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); + } + + Serial.begin(9600); + Serial.println(); + Serial.println(F("Reset")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("lora Device pins initialised")); + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates LoRa device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + Serial.println(F("Setup lora device")); + + if (!bme280.init()) + { + Serial.println("BME280 Device error!"); + led_Flash(100, 15); //long very fast speed flash indicates BME280 device error + } + + Serial.println(F("Initialised BME280")); + + Serial.println(); + Serial.println(F("Transmitter ready")); + Serial.println(); + + readSensors(); //do an initial sensor read +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/17_Sensor_Transmitter_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/17_Sensor_Transmitter_ESP32/Settings.h new file mode 100644 index 0000000..96c3255 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/17_Sensor_Transmitter_ESP32/Settings.h @@ -0,0 +1,49 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 03/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of the Tracker boards, the ESP32_Micro_Node, be sure to change +//them to match your own setup. You will also need to connect up the pins for the SPI bus, which on the +//ESP32_Micro_Node are SCK on pin 18, MISO on pin 19 and MOSI on pin 23. Some pins such as DIO1, DIO2 and +//BUZZER may not be in used by this sketch so they do not need to be connected and should be set to -1. + +#define NSS 5 //select pin on LoRa device +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NRESET 27 //reset pin on LoRa device +#define LED1 2 //on board LED, high for on +#define DIO0 35 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define BUZZER -1 //pin for buzzer, set to -1 if not used +#define VCCPOWER 14 //pin controls power to external devices +#define BATTERYAD 36 //pin for reading supply voltage +#define BATVREADON 25 //when high turns on the resistor divider to measure voltage, -1 if not used +#define ADMultiplier 11.65 //Multiplier for conversion of AD reading to mV + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 14; //LoRa transmit power in dBm + +#define BME280_ADDRESS 0x76 //I2C bus address of BME280 +#define BME280_REGISTER_CONTROL 0xF4 //BME280 register number for power control + +const uint16_t TIME_TO_SLEEP = 16; //Time ESP32 will go to sleep (in seconds) diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/18_Sensor_Receiver_ESP32/18_Sensor_Receiver_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/18_Sensor_Receiver_ESP32/18_Sensor_Receiver_ESP32.ino new file mode 100644 index 0000000..037a40b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/18_Sensor_Receiver_ESP32/18_Sensor_Receiver_ESP32.ino @@ -0,0 +1,358 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 21/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program receives a LoRa packet without using a processor buffer, the LoRa devices + internal buffer is read direct for the received sensor data. + + The sensor used in the matching '17_Sensor_Transmiter' program is a BME280 and the pressure, humidity, + and temperature are being and received. There is also a 16bit value of battery mV and and a 8 bit status + value at the end of the packet. + + When a packet is received its printed and assuming the packet is validated, the sensor results are printed + to the serial monitor and screen. + + For the sensor data to be accepted as valid the folowing need to match; + + The 16bit CRC on the received sensor data must match the CRC value transmitted with the packet. + The packet must start with a byte that matches the packet type sent, 'Sensor1' + The RXdestination byte in the packet must match this node ID of this receiver node, defined by 'This_Node' + + In total thats 16 + 8 + 8 = 32bits of checking, so a 1:4294967296 chance (approx) that an invalid + packet is acted on and erroneous values displayed. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +SX127XLT LT; + +#include //get library here > https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 + +uint32_t RXpacketCount; //count of all packets received +uint32_t ValidPackets; //count of packets received with valid data +uint32_t RXpacketErrors; //count of all packets with errors received +bool packetisgood; + +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet + +uint8_t RXPacketType; +uint8_t RXDestination; +uint8_t RXSource; +float temperature; //the BME280 temperature value +float pressure; //the BME280 pressure value +uint16_t humidity; //the BME280 humididty value +uint16_t voltage; //the battery voltage value +uint8_t statusbyte; //a status byte, not currently used +uint16_t TXCRCvalue; //the CRC value of the packet data as transmitted + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout set + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_Received_OK(); //its a valid packet LoRa wise, but it might not be a packet we want + } + + digitalWrite(LED1, LOW); + Serial.println(); + //SPI.endTransaction(); +} + + +void packet_Received_OK() +{ + //a LoRa packet has been received, which has passed the internal LoRa checks, including CRC, but it could be from + //an unknown source, so we need to check that its actually a sensor packet we are expecting, and has valid sensor data + + uint8_t len; + uint8_t contenterrors; //keep a count of errors found in packet + + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(",PacketsReceived,")); + + LT.startReadSXBuffer(0); + RXPacketType = LT.readUint8(); + RXDestination = LT.readUint8(); + RXSource = LT.readUint8(); + + /************************************************************************ + Highlighted section - this is where the actual sensor data is read from + the packet + ************************************************************************/ + temperature = LT.readFloat(); //the BME280 temperature value + pressure = LT.readFloat(); //the BME280 pressure value + humidity = LT.readUint16(); //the BME280 humididty value + voltage = LT.readUint16(); //the battery voltage value + statusbyte = LT.readUint8(); //a status byte, not currently used + /************************************************************************/ + + len = LT.endReadSXBuffer(); //note len is bytes read from packet, may be different to actual packet length + + printreceptionDetails(); //print details of reception, RSSI etc + Serial.println(); + + contenterrors = checkPacketValid(len); //pass length of packet to check routine + + if (contenterrors == 0) + { + Serial.println(F(" Packet is good")); + ValidPackets++; + printSensorValues(); //print the sensor values + Serial.println(); + printPacketCounts(); //print count of valid packets and errors + displayscreen1(); + Serial.println(); + } + else + { + Serial.println(F(" Packet is not valid")); + RXpacketErrors++; + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + } +} + + +uint8_t checkPacketValid(uint8_t len) +{ + //this function checks if the packet is valid and will be displayed + + uint8_t errors = 0; + + if (RXPacketType != Sensor1) //is it a Sensor1 type packet + { + errors++; + } + + if (RXDestination != This_Node) //was the packet sent to this receiver node ? + { + errors++; + } + + if (!checkCRCvalue(len)) //is the sent CRC value of sensor data valid ? + { + errors++; + } + + Serial.println(); + Serial.print(F("Error Check Count = ")); + Serial.print(errors); + return errors; +} + + +bool checkCRCvalue(uint8_t len) +{ + uint16_t CRCSensorData; + //uint8_t msb, lsb; + + CRCSensorData = LT.CRCCCITTSX(3, (len-1), 0xFFFF); //calculate the CRC of packet sensor data + + Serial.print(F("(CRC of Received sensor data ")); + Serial.print(CRCSensorData, HEX); + Serial.print(F(")" )); + + //SPI.endTransaction(); + + TXCRCvalue = ((LT.getByteSXBuffer(17) << 8) + (LT.getByteSXBuffer(16))); + + Serial.print(F("(CRC transmitted ")); + Serial.print(TXCRCvalue, HEX); + Serial.print(F(")" )); + + if (TXCRCvalue != CRCSensorData) + { + Serial.print(F(" Sensor Data Not Valid")); + return false; + } + else + { + Serial.print(F(" Sensor Data is Valid")); + return true; + } + +} + + +void printSensorValues() +{ + Serial.print(F("Temperature,")); + Serial.print(temperature, 1); + Serial.print(F("c,Pressure,")); + Serial.print(pressure, 0); + Serial.print(F("Pa,Humidity,")); + Serial.print(humidity); + Serial.print(F("%,Voltage,")); + Serial.print(voltage); + Serial.print(F("mV,Status,")); + Serial.print(statusbyte, HEX); + Serial.print(F(",CRC,")); + Serial.print(TXCRCvalue, HEX); + Serial.flush(); +} + + +void printreceptionDetails() +{ + Serial.print(F("RSSI")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); +} + + +void printPacketCounts() +{ + Serial.print(F("ValidPackets,")); + Serial.print(ValidPackets); + Serial.print(F(",Errors,")); + Serial.print(RXpacketErrors); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + RXpacketErrors++; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout ")); + } + else + { + Serial.print(F("PacketError ")); + printreceptionDetails(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + Serial.println(); + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + } +} + + +void displayscreen1() +{ + //show sensor data on display + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("Sensor ")); + disp.print(RXSource); + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(temperature, 1); + disp.print(F("c")); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(pressure, 0); + disp.print(F("Pa")); + disp.clearLine(3); + disp.setCursor(0, 3); + disp.print(humidity); + disp.print(F("%")); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(voltage); + disp.print(F("mV")); + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("ValidPkts ")); + disp.print(ValidPackets); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(VCCPOWER, OUTPUT); //this pin switches power for external devices, lora and SD card + digitalWrite(VCCPOWER, LOW); //turn power on + + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Check LoRa")); + disp.setCursor(0, 1); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + disp.print(F("LoRa OK")); + led_Flash(2, 125); + } + else + { + disp.print(F("Device error")); + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/18_Sensor_Receiver_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/18_Sensor_Receiver_ESP32/Settings.h new file mode 100644 index 0000000..3707e40 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sensor/18_Sensor_Receiver_ESP32/Settings.h @@ -0,0 +1,49 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 17/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of the Tracker boards, the ESP32_Micro_Node, be sure to change +//them to match your own setup. You will also need to connect up the pins for the SPI bus, which on the +//ESP32_Micro_Node are SCK on pin 18, MISO on pin 19 and MOSI on pin 23. Some pins such as DIO1, DIO2 and +//BUZZER may not be in used by this sketch so they do not need to be connected and should be set to -1. + +#define NSS 5 //select pin on LoRa device +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NRESET 27 //reset pin on LoRa device +#define LED1 2 //on board LED, high for on +#define DIO0 35 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define BUZZER -1 //pin for buzzer, set to -1 if not used +#define VCCPOWER 14 //pin controls power to external devices +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + + +//*************** Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 2; //LoRa TX power + +#define packet_delay 1000 //mS delay between packets +#define This_Node 'B' //this is the node that the remote sensors send data to + +//**************** Setup Display Parameters Here **************** + +//const uint8_t dispfont = u8x8_font_chroma48medium8_r; //display font from u8g2 library diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32.ino new file mode 100644 index 0000000..bc0de39 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32.ino @@ -0,0 +1,268 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 03/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program tests the sleep mode and register retention of the SX127X in sleep mode, + it assumes an ESP32 processor module. The LoRa settings to use are specified in the 'Settings.h' file. + To retain the program memory, so that the program restarts after sleep at the next instructions after + going into sleep mode the ESP32 light sleep function is used. + + A packet is sent, containing the text 'Before Device Sleep' and the LoRa device and ESP32 are put to sleep. + The ESP32 internal RTC timer should wakeup the processor in 15 seconds (approx) and lora register + values should be retained. The device then attempts to transmit another packet 'After Device Sleep' + without re-loading all the lora settings. The receiver should see 'After Device Sleep' for the first + packet and 'After Device Sleep' for the second. + + For unknown reasons the ESP32 pulses the pin used by NRESET when going into sleep modes, so the program + turns it into an input for the duration of the sleep to avoid resetting the lora device. + + The program also has the option of using a logic pin to control the power to the lora and SD card + devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are + powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + + Tested on a 'bare bones' ESP32 the current in light sleep mode was 1670uA. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" + +SX127XLT LT; + +boolean SendOK; +int8_t TestPower; +uint8_t TXPacketL; + +/*********************************************************** + //ESP32 specific settings +***********************************************************/ +#define uS_TO_S_FACTOR 1000000 //Conversion factor for micro seconds to seconds +#define TIME_TO_SLEEP 15 //Time ESP32 will go to sleep (in seconds) +RTC_DATA_ATTR int16_t bootCount = 0; +RTC_DATA_ATTR uint16_t sleepcount = 0; + + +void loop() +{ + Serial.print(F("Bootcount ")); + Serial.println(bootCount); + Serial.print(F("Sleepcount ")); + Serial.println(sleepcount); + + Serial.println(); + LT.printRegisters(0x00, 0x7F); + Serial.println(); + + digitalWrite(LED1, HIGH); + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("TestPacket1> ")); + Serial.flush(); + + if (Send_Test_Packet1()) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + Serial.println(); + delay(packet_delay); + + pinMode(NRESET, INPUT); //change NRESET pin to INPUT mode + + + LT.setSleep(CONFIGURATION_RETENTION); //preserve register settings in sleep. + + esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); + Serial.println(F("Start Sleep")); + Serial.flush(); + sleepcount++; + + esp_light_sleep_start(); + + Serial.println(); + Serial.println(); + Serial.println(F("Awake !")); + Serial.flush(); + digitalWrite(LED1, HIGH); + + LT.wake(); + + digitalWrite(NRESET, HIGH); //set NRESET high before turning it back to an output + pinMode(NRESET, INPUT); //set NRESET as an output + + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("TestPacket2> ")); + Serial.flush(); + + if (Send_Test_Packet2()) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + Serial.println(); + delay(packet_delay); +} + + +void packet_is_OK() +{ + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes SentOK")); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F("SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); //this leaves the LED on slightly longer for a packet error +} + + +bool Send_Test_Packet1() +{ + uint8_t bufffersize; + + uint8_t buff[] = "Before Device Sleep"; + TXPacketL = sizeof(buff); + buff[TXPacketL - 1] = '*'; + + if (sizeof(buff) > TXBUFFER_SIZE) //check that defined buffer is not larger than TX_BUFFER + { + bufffersize = TXBUFFER_SIZE; + } + else + { + bufffersize = sizeof(buff); + } + + TXPacketL = bufffersize; + + LT.printASCIIPacket( (uint8_t*) buff, bufffersize); + digitalWrite(LED1, HIGH); + + if (LT.transmit( (uint8_t*) buff, TXPacketL, 10000, TXpower, WAIT_TX)) + { + digitalWrite(LED1, LOW); + return true; + } + else + { + return false; + } +} + + +bool Send_Test_Packet2() +{ + uint8_t bufffersize; + + uint8_t buff[] = "After Device Sleep"; + TXPacketL = sizeof(buff); + buff[TXPacketL - 1] = '*'; + + if (sizeof(buff) > TXBUFFER_SIZE) //check that defined buffer is not larger than TX_BUFFER + { + bufffersize = TXBUFFER_SIZE; + } + else + { + bufffersize = sizeof(buff); + } + + TXPacketL = bufffersize; + + LT.printASCIIPacket( (uint8_t*) buff, bufffersize); + digitalWrite(LED1, HIGH); + + if (LT.transmit( (uint8_t*) buff, TXPacketL, 10000, TXpower, WAIT_TX)) + { + digitalWrite(LED1, LOW); + return true; + } + else + { + return false; + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + if (VCCPOWER >= 0) + { + pinMode(VCCPOWER, OUTPUT); //For controlling power to external devices + digitalWrite(VCCPOWER, LOW); //VCCOUT on. lora device on + } + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("5_LoRa_TX_Sleep_Timed_Wakeup_ESP32 Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.print(F("Transmitter ready - TXBUFFER_SIZE ")); + Serial.println(TXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32/Settings.h new file mode 100644 index 0000000..9214976 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_ESP32/Settings.h @@ -0,0 +1,43 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 5 //select pin on LoRa device +#define NRESET 27 //reset pin on LoRa device +#define LED1 2 //on board LED, high for on +#define DIO0 35 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define VCCPOWER 14 //pin controls power to external devices + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup lora Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define TXBUFFER_SIZE 32 //RX buffer size + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/23_Simple_GPS_Tracker_Transmitter_ESP32/23_Simple_GPS_Tracker_Transmitter_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/23_Simple_GPS_Tracker_Transmitter_ESP32/23_Simple_GPS_Tracker_Transmitter_ESP32.ino new file mode 100644 index 0000000..b9de953 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/23_Simple_GPS_Tracker_Transmitter_ESP32/23_Simple_GPS_Tracker_Transmitter_ESP32.ino @@ -0,0 +1,408 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 03/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is an example of a basic GPS tracker. The program reads the GPS, + waits for an updated fix and transmits location and altitude, number of satellites in view, the HDOP + value, the fix time of the GPS and the battery voltage. This transmitter can be also be used to + investigate GPS performance. At startup there should be a couple of seconds of recognisable text from + the GPS printed to the serial monitor. If you see garbage or funny characters its likley the GPS baud + rate is wrong. If the transmitter is turned on from cold, the receiver will pick up the cold fix time, + which is an indication of GPS performance. The GPS will be powered on for around 4 seconds before the + timing of the fix starts. Outside with a good view of the sky most GPSs should produce a fix in around + 45 seconds. The number of satellites and HDOP are good indications to how well a GPS is working. + + The program writes direct to the LoRa devices internal buffer, no memory buffer is used. + + The LoRa settings are configured in the Settings.h file. + + The program has the option of using a pin to control the power to the GPS (GPSPOWER), if the GPS module + or board being used has this feature. To not use this feature set the define for GPSPOWER in the + Settings.h file to '#define GPSPOWER -1'. Also set the GPSONSTATE and GPSOFFSTATE to the appropriate logic + levels. + + The program also has the option of using a logic pin to control the power to the lora and SD card + devices, which can save power in sleep mode. If the hardware is fitted to your board these devices are + powered on by setting the VCCPOWER pin low. If your board does not have this feature set VCCPOWER to -1. + + There is also an option of using a logic pin to turn the resistor divider used to read battery voltage on + and off. This reduces current used in sleep mode. To use the feature set the define for pin BATVREADON + in 'Settings.h' to the pin used. If not using the feature set the pin number to -1. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" +#define authorname "Stuart Robinson" + +#include +#include + +#include "Settings.h" +#include + +SX127XLT LT; + +#include //get library here > http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + + +uint8_t TXStatus = 0; //used to store current status flag bits of Tracker transmitter (TX) +uint8_t TXPacketL; //length of LoRa packet (TX) +float TXLat; //Latitude from GPS on Tracker transmitter (TX) +float TXLon; //Longitude from GPS on Tracker transmitter (TX) +float TXAlt; //Altitude from GPS on Tracker transmitter (TX) +uint8_t TXSats; //number of GPS satellites seen (TX) +uint32_t TXHdop; //HDOP from GPS on Tracker transmitter (TX) +uint16_t TXVolts; //Volts (battery) level on Tracker transmitter (TX) +uint32_t TXGPSFixTime; //GPS fix time in hot fix mode of GPS on Tracker transmitter (TX) +uint32_t TXPacketCount, TXErrorsCount; //keep count of OK packets and send errors + + +void loop() +{ + + if (gpsWaitFix(WaitGPSFixSeconds)) + { + sendLocation(TXLat, TXLon, TXAlt, TXHdop, TXGPSFixTime); + Serial.println(); + Serial.print(F("Waiting ")); + Serial.print(Sleepsecs); + Serial.println(F("s")); + delay(Sleepsecs * 1000); //this sleep is used to set overall transmission cycle time + } + else + { + send_Command(NoFix); //send notification of no GPS fix. + } +} + + +bool gpsWaitFix(uint16_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for good fix + uint32_t endwaitmS, GPSonTime; + bool GPSfix; + float tempfloat; + uint8_t GPSchar; + + GPSonTime = millis(); + GPSserial.begin(9600); //start GPSserial + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.println(F("s")); + + endwaitmS = millis() + (waitSecs * 1000); + + while (millis() < endwaitmS) + { + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated()) + { + GPSfix = true; + Serial.print(F("Have GPS Fix ")); + TXGPSFixTime = millis() - GPSonTime; + Serial.print(TXGPSFixTime); + Serial.println(F("mS")); + + TXLat = gps.location.lat(); + TXLon = gps.location.lng(); + TXAlt = gps.altitude.meters(); + TXSats = gps.satellites.value(); + TXHdop = gps.hdop.value(); + tempfloat = ( (float) TXHdop / 100); + + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F(",")); + Serial.print(TXSats); + Serial.print(F(",")); + Serial.print(tempfloat, 2); + Serial.println(); + + break; //exit while loop reading GPS + } + } + + //if here then there has either been a fix or no fix and a timeout + + if (GPSfix) + { + setStatusByte(GPSFix, 1); //set status bit to flag a GPS fix + } + else + { + setStatusByte(GPSFix, 0); //set status bit to flag no fix + Serial.println(); + Serial.println(F("Timeout - No GPSFix")); + Serial.println(); + GPSfix = false; + } + + GPSserial.end(); //serial RX interrupts interfere with SPI, so stop GPSserial + return GPSfix; +} + + +void sendLocation(float Lat, float Lon, float Alt, uint32_t Hdop, uint32_t fixtime) +{ + uint8_t len; + uint16_t IRQStatus; + + Serial.print(F("Send Location")); + + TXVolts = readSupplyVoltage(); //get the latest supply\battery volts + + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeUint8(LocationPacket); //indentify type of packet + LT.writeUint8(Broadcast); //who is the packet sent too + LT.writeUint8(ThisNode); //tells receiver where is packet from + LT.writeFloat(Lat); //add latitude + LT.writeFloat(Lon); //add longitude + LT.writeFloat(Alt); //add altitude + LT.writeUint8(TXSats); //add number of satellites + LT.writeUint32(Hdop); //add hdop + LT.writeUint8(TXStatus); //add tracker status + LT.writeUint32(fixtime); //add GPS fix time in mS + LT.writeUint16(TXVolts); //add tracker supply volts + len = LT.endWriteSXBuffer(); //close buffer write + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); + digitalWrite(LED1, LOW); + + if (TXPacketL) + { + TXPacketCount++; + Serial.println(F(" - Done ")); + Serial.print(F("SentOK,")); + Serial.print(TXPacketCount); + Serial.print(F(",Errors,")); + Serial.println(TXErrorsCount); + } + else + { + //if here there was an error transmitting packet + TXErrorsCount++; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set + Serial.println(); + } +} + + +void setStatusByte(uint8_t bitnum, uint8_t bitval) +{ + //program the status byte + + if (bitval == 0) + { + bitClear(TXStatus, bitnum); + } + else + { + bitSet(TXStatus, bitnum); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //flash LED to show tracker is alive + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void send_Command(char cmd) +{ + bool SendOK; + uint8_t len; + + Serial.print(F("Send Cmd ")); + Serial.write(cmd); + + LT.startWriteSXBuffer(0); + LT.writeUint8(cmd); //packet addressing used indentify type of packet + LT.writeUint8(Broadcast); //who is the packet sent to + LT.writeUint8(ThisNode); //where is packet from + LT.writeUint16(TXVolts); + len = LT.endWriteSXBuffer(); + + digitalWrite(LED1, HIGH); + SendOK = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); //timeout set at 10 seconds + digitalWrite(LED1, LOW); + + if (SendOK) + { + Serial.println(F(" - Done")); + } + else + { + Serial.println(F(" - Error")); + } +} + + +uint16_t readSupplyVoltage() +{ + //ESP32 uses the 3.3V VCC as reference + + uint16_t temp; + uint16_t volts = 0; + byte index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn on MOSFET connecting resitor divider in circuit + } + + temp = analogRead(SupplyAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(SupplyAD); + volts = volts + temp; + } + volts = ((volts / 5) * ADMultiplier); + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn off MOSFET connecting resitor divider in circuit + } + + return volts; +} + + + +void GPSON() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void setup() +{ + uint32_t endmS; + + if (VCCPOWER >= 0) + { + pinMode(VCCPOWER, OUTPUT); //this pin switches power for external devices, lora and SD card + digitalWrite(VCCPOWER, LOW); //turn device power on + } + + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + GPSON(); + } + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); + } + + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("23_Simple_GPS_Tracker_Transmitter_ESP32 Starting")); + + //SPI.begin(); + SPI.begin(SCK, MISO, MOSI, NSS); //alternative format for SPI3, VSPI; SPI.begin(SCK,MISO,MOSI,SS) + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + + TXVolts = readSupplyVoltage(); + + Serial.print(F("Supply ")); + Serial.print(TXVolts); + Serial.println(F("mV")); + + send_Command(PowerUp); //send power up command, includes supply mV + + Serial.println(F("Startup GPS check")); + + GPSserial.begin(9600); + + endmS = millis() + echomS; + + while (millis() < endmS) + { + while (GPSserial.available() > 0) + Serial.write(GPSserial.read()); + } + Serial.println(); + Serial.println(); + + Serial.println(F("Wait for first GPS fix")); + gpsWaitFix(WaitFirstGPSFixSeconds); + + sendLocation(TXLat, TXLon, TXAlt, TXHdop, TXGPSFixTime); +} diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/23_Simple_GPS_Tracker_Transmitter_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/23_Simple_GPS_Tracker_Transmitter_ESP32/Settings.h new file mode 100644 index 0000000..06a6b63 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/23_Simple_GPS_Tracker_Transmitter_ESP32/Settings.h @@ -0,0 +1,71 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 03/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of the Tracker boards, the ESP32_Micro_Node, be sure to change +//them to match your own setup. You will also need to connect up the pins for the SPI bus, which on the +//ESP32_Micro_Node are SCK on pin 18, MISO on pin 19 and MOSI on pin 23. Some pins such as DIO1, DIO2 and +//BUZZER may not be in used by this sketch so they do not need to be connected and should be set to -1. + + +#define NSS 5 //select on LoRa device +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NRESET 27 //reset on LoRa device +#define DIO0 35 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 2 //On board LED, high for on +#define BUZZER -1 //Buzzer if fitted, high for on. Set to -1 if not used +#define VCCPOWER 14 //controls power to lora and SD card, set to -1 if not used +#define SupplyAD 36 //pin for reading supply\battery voltage +#define BATVREADON 25 //turns on battery resistor divider, high for on +#define ADMultiplier 11.65 //Multiplier for conversion of AD reading to mV + +#define RXpin 17 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin 16 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER 26 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE LOW //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE HIGH //logic level to turn GPS off via pin GPSPOWER +#define GPSserial Serial2 //define GPSserial as ESP32 Serial2 + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +#define ThisNode 'T' //a character that identifies this tracker + +//************************************************************************************************** +// GPS Settings +//************************************************************************************************** + +#define GPSBaud 9600 //GPS Baud rate + +#define WaitGPSFixSeconds 30 //time in seconds to wait for a new GPS fix +#define WaitFirstGPSFixSeconds 1800 //time to seconds to wait for the first GPS fix at startup +#define Sleepsecs 5 //seconds between transmissions, this delay is used to set overall transmission cycle time + +#define echomS 2000 //number of mS to run GPS echo at startup + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32.ino new file mode 100644 index 0000000..853a504 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32.ino @@ -0,0 +1,624 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 21/03/20 + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* +Program Operation - TThis program is an example of a functional GPS tracker receiver using lora. +It is capable of picking up the trackers location packets from many kilometres away with only basic antennas. + +The program receives the location packets from the remote tracker transmitter and writes them on an OLED +display and also prints the information to the Arduino IDE serial monitor. The program can read a locally +attached GPS and when that has a fix, will display the distance and direction to the remote tracker. + +The program writes direct to the lora devices internal buffer, no memory buffer is used. The lora settings +are configured in the Settings.h file. + +The receiver recognises two types of tracker packet, the one from the matching program '23_GPS_Tracker_Transmitter' +(LocationPacket, 27 bytes) which causes these fields to be printed to the serial monitor; + +Latitude, Longitude, Altitude, Satellites, HDOP, TrackerStatusByte, GPS Fixtime, Battery mV, Distance, Direction, +Distance, Direction, PacketRSSI, PacketSNR, NumberPackets, PacketLength, IRQRegister. + +This is a long packet which at the long range LoRa settings takes just over 3 seconds to transmit. + +The receiver also recognises a much shorter location only packet (LocationBinaryPacket, 11 bytes) and when +received this is printed to the serial monitor; + +Latitude, Longitude, Altitude, TrackerStatusByte, Distance, Direction, PacketRSSI, PacketSNR, NumberPackets, +PacketLength, IRQRegister. + +Most of the tracker information (for both types of packet) is shown on the OLED display. If there has been a +tracker transmitter GPS fix the number\identifier of that tracker is shown on row 0 right of screen and if there +is a recent local (receiver) GPS fix an 'R' is displayed row 1 right of screen. + +When the tracker transmitter starts up or is reset its sends a power up message containing the battery voltage +which is shown on the OLED and printer to the serial monitor. + +The program has the option of using a pin to control the power to the GPS, if the GPS module being used has this +feature. To use the option change the define in Settings.h; + +'#define GPSPOWER -1' from -1 to the pin number being used. Also set the GPSONSTATE and GPSOFFSTATE defines to +the appropriate logic levels. + +The program by default uses software serial to read the GPS, you can use hardware serial by commenting out this +line in the Settings.h file; + +#define USE_SOFTSERIAL_GPS + +And then defining the hardware serial port you are using, which defaults to Serial1. + +Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + + +#define Program_Version "V1.1" + +#include +#include +SX127XLT LT; + +#include "Settings.h" +#include + +#include //https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //1.3" OLED often sold as 1.3" SSD1306 + + +#include //http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#ifdef USE_SOFTSERIAL_GPS +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#else +#define GPSserial HardwareSerialPort //hardware serial port (eg Serial1) is configured in the Settings.h file +#endif + +uint32_t RXpacketCount; //count of received packets +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //signal strength (RSSI) dBm of received packet +int8_t PacketSNR; //signal to noise ratio (SNR) dB of received packet +uint8_t PacketType; //for packet addressing, identifies packet type +uint8_t Destination; //for packet addressing, identifies the destination (receiving) node +uint8_t Source; //for packet addressing, identifies the source (transmiting) node +uint8_t TXStatus; //status byte from tracker transmitter +uint8_t TXSats; //number of sattelites in use +float TXLat; //latitude +float TXLon; //longitude +float TXAlt; //altitude +float RXLat; //latitude +float RXLon; //longitude +float RXAlt; //altitude +uint32_t TXHdop; //HDOP, indication of fix quality, horizontal dilution of precision, low is good +uint32_t TXGPSFixTime; //time in mS for fix +uint16_t TXVolts; //supply\battery voltage +uint16_t RXVolts; //supply\battery voltage +float TXdistance; //calculated distance to tracker +uint16_t TXdirection; //calculated direction to tracker +uint16_t RXerrors; +uint32_t TXupTimemS; //up time of TX in mS + +uint32_t LastRXGPSfixCheck; //used to record the time of the last GPS fix + +bool TXLocation = false; //set to true when at least one tracker location packet has been received +bool RXGPSfix = false; //set to true if the local GPS has a recent fix + +uint8_t FixCount = DisplayRate; //used to keep track of number of GPS fixes before display updated + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, NO_WAIT); //returns 0 if packet error of some sort + + while (!digitalRead(DIO0)) + { + readGPS(); //If the DIO pin is low, no packet arrived, so read the GPS + } + + //something has happened in receiver + digitalWrite(LED1, HIGH); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + RXPacketL = LT.readRXPacketL(); + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + } + Serial.println(); +} + + +void readGPS() +{ + if (GPSserial.available() > 0) + { + gps.encode(GPSserial.read()); + } + + + if ( millis() > (LastRXGPSfixCheck + NoRXGPSfixms)) + { + RXGPSfix = false; + LastRXGPSfixCheck = millis(); + dispscreen1(); + } + + + if (gps.location.isUpdated() && gps.altitude.isUpdated()) + { + RXGPSfix = true; + RXLat = gps.location.lat(); + RXLon = gps.location.lng(); + RXAlt = gps.altitude.meters(); + printRXLocation(); + LastRXGPSfixCheck = millis(); + + if ( FixCount == 1) //update screen when FIXcoount counts down from DisplayRate to 1 + { + FixCount = DisplayRate; + dispscreen1(); + } + FixCount--; + } +} + + +bool readTXStatus(byte bitnum) +{ + return bitRead(TXStatus, bitnum); +} + + +void printRXLocation() +{ + Serial.print(F("LocalGPS ")); + Serial.print(RXLat, 5); + Serial.print(F(",")); + Serial.print(RXLon, 5); + Serial.print(F(",")); + Serial.print(RXAlt, 1); + Serial.println(); +} + + +void readPacketAddressing() +{ + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + LT.endReadSXBuffer(); +} + + +void packet_is_OK() +{ + //uint16_t IRQStatus; + float tempfloat; + + RXpacketCount++; + + readPacketAddressing(); + + if (PacketType == PowerUp) + { + LT.startReadSXBuffer(0); + LT.readUint8(); //read byte from SXBuffer, not used + LT.readUint8(); //read byte from SXBuffer, not used + LT.readUint8(); //read byte from SXBuffer, not used + TXVolts = LT.readUint16(); //read tracker transmitter voltage + LT.endReadSXBuffer(); + Serial.print(F("Tracker Powerup - Battery ")); + Serial.print(TXVolts); + Serial.println(F("mV")); + dispscreen2(); + } + + if (PacketType == LocationPacket) + { + //packet has been received, now read from the SX12XX FIFO in the correct order. + Serial.print(F("LocationPacket ")); + TXLocation = true; + LT.startReadSXBuffer(0); //start the read of received packet + PacketType = LT.readUint8(); //read in the PacketType + Destination = LT.readUint8(); //read in the Packet destination address + Source = LT.readUint8(); //read in the Packet source address + TXLat = LT.readFloat(); //read in the tracker latitude + TXLon = LT.readFloat(); //read in the tracker longitude + TXAlt = LT.readFloat(); //read in the tracker altitude + TXSats = LT.readUint8(); //read in the satellites in use by tracker GPS + TXHdop = LT.readUint32(); //read in the HDOP of tracker GPS + TXStatus = LT.readUint8(); //read in the tracker status byte + TXGPSFixTime = LT.readUint32(); //read in the last fix time of tracker GPS + TXVolts = LT.readUint16(); //read in the tracker supply\battery volts + TXupTimemS = LT.readUint32(); //read in the TX uptime in mS + RXPacketL = LT.endReadSXBuffer(); //end the read of received packet + + + if (RXGPSfix) //if there has been a local GPS fix do the distance and direction calculation + { + TXdirection = (int16_t) TinyGPSPlus::courseTo(RXLat, RXLon, TXLat, TXLon); + TXdistance = TinyGPSPlus::distanceBetween(RXLat, RXLon, TXLat, TXLon); + } + else + { + TXdistance = 0; + TXdirection = 0; + } + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F(",")); + Serial.print(TXSats); + Serial.print(F(",")); + + tempfloat = ( (float) TXHdop / 100); //need to convert Hdop read from GPS as uint32_t to a float for display + Serial.print(tempfloat, 2); + + Serial.print(F(",")); + Serial.print(TXStatus); + Serial.print(F(",")); + + Serial.print(TXGPSFixTime); + Serial.print(F("mS,")); + Serial.print(TXVolts); + Serial.print(F("mV,")); + Serial.print((TXupTimemS/1000)); + Serial.print(F("s,")); + + Serial.print(TXdistance, 0); + Serial.print(F("m,")); + Serial.print(TXdirection); + Serial.print(F("d")); + printpacketDetails(); + dispscreen1(); //and show the packet detail it on screen + return; + } + + + if (PacketType == LocationBinaryPacket) + { + //packet from locator has been received, now read from the SX12XX FIFO in the correct order. + TXLocation = true; + Serial.print(F("LocationBinaryPacket ")); + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readInt16(); + TXStatus = LT.readUint8(); + RXPacketL = LT.endReadSXBuffer(); + + if (RXGPSfix) //if there has been a local GPS fix do the distance and direction calculation + { + TXdirection = (int16_t) TinyGPSPlus::courseTo(RXLat, RXLon, TXLat, TXLon); + TXdistance = TinyGPSPlus::distanceBetween(RXLat, RXLon, TXLat, TXLon); + } + else + { + TXdistance = 0; + TXdirection = 0; + } + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 0); + Serial.print(F("m,")); + Serial.print(TXStatus); + Serial.print(F(",")); + Serial.print(TXdistance, 0); + Serial.print(F("m,")); + Serial.print(TXdirection); + Serial.print(F("d")); + printpacketDetails(); + dispscreen1(); + return; + } +} + + +void printpacketDetails() +{ + uint16_t IRQStatus; + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Packets,")); + Serial.print(RXpacketCount); + + Serial.print(F(",Length,")); + Serial.print(RXPacketL); + IRQStatus = LT.readIrqStatus(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + RXerrors++; + Serial.print(F("PacketError,RSSI")); + + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void dispscreen1() +{ + //show received packet data on display + float tempfloat; + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(TXLat, 5); + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(TXLon, 5); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(TXAlt,0); + disp.print(F("m")); + disp.clearLine(3); + disp.setCursor(0, 3); + + disp.print(F("RSSI ")); + disp.print(PacketRSSI); + disp.print(F("dBm")); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(F("SNR ")); + + if (PacketSNR > 0) + { + disp.print(F("+")); + } + + if (PacketSNR == 0) + { + disp.print(F(" ")); + } + + if (PacketSNR < 0) + { + disp.print(F("-")); + } + + disp.print(PacketSNR); + disp.print(F("dB")); + + if (PacketType == LocationPacket) + { + disp.clearLine(5); + disp.setCursor(0, 5); + tempfloat = ((float) TXVolts / 1000); + disp.print(F("Batt ")); + disp.print(tempfloat, 2); + disp.print(F("v")); + } + + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("Packets ")); + disp.print(RXpacketCount); + + disp.clearLine(7); + + if (RXGPSfix) + { + disp.setCursor(15, 1); + disp.print(F("R")); + } + else + { + disp.setCursor(15, 1); + disp.print(F(" ")); + disp.setCursor(0, 7); + disp.print(F("No Local Fix")); + } + + if (RXGPSfix && TXLocation) //only display distance and direction if have received tracker packet and have local GPS fix + { + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(TXdistance, 0); + disp.print(F("m ")); + disp.print(TXdirection); + disp.print(F("d")); + } + + if (readTXStatus(GPSFix)) + { + disp.setCursor(15, 0); + disp.write(Source); + } + +} + + +void dispscreen2() +{ + //show tracker powerup data on display + float tempfloat; + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Tracker Powerup")); + disp.setCursor(0, 1); + disp.print(F("Battery ")); + tempfloat = ((float) TXVolts / 1000); + disp.print(tempfloat, 2); + disp.print(F("v")); +} + + +void GPSON() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void setup() +{ + uint32_t endmS; + + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32 Starting")); + + if (BUZZER >= 0) + { + pinMode(BUZZER, OUTPUT); + } + + SPI.begin(); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + + Serial.print(F("Checking LoRa device - ")); //Initialize LoRa + disp.setCursor(0, 0); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("Receiver ready")); + disp.print(F("Receiver ready")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No LoRa device responding")); + disp.print(F("No LoRa device")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + Serial.println(F("Startup GPS check")); + + endmS = millis() + echomS; + + //now startup GPS + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + } + + GPSON(); + GPSserial.begin(GPSBaud); + + while (millis() < endmS) + { + while (GPSserial.available() > 0) + Serial.write(GPSserial.read()); + } + Serial.println(); + Serial.println(); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32/Settings.h new file mode 100644 index 0000000..0d7598d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS_ESP32/Settings.h @@ -0,0 +1,60 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER SWITCH1 may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 5 //select on LoRa device +#define NRESET 27 //reset on LoRa device +#define DIO0 35 //DIO0 on LoRa device, used for RX and TX done +#define LED1 82 //On board LED, high for on +#define BUZZER -1 //Buzzer if fitted, high for on. Set to -1 if not used + +#define RXpin 16 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin 17 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER 26 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE LOW //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE HIGH //logic level to turn GPS off via pin GPSPOWER + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +//************************************************************************************************** +// GPS Settings +//************************************************************************************************** + +//#define USE_SOFTSERIAL_GPS //need to include this if we are using softserial for GPS +#define HardwareSerialPort Serial2 //if using hardware serial enable this define for hardware serial port + +#define GPSBaud 9600 //GPS Baud rate +#define WaitGPSFixSeconds 30 //time to wait for a new GPS fix +#define echomS 2000 //number of mS to run GPS echo for at startup + +#define NoRXGPSfixms 15000 //max number of mS to allow before no local fix flagged +#define DisplayRate 7 //when working OK the GPS will get a new fix every second or so + //this rate defines how often the display should be updated + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/71_FSKRTTY_Transmitter_Test_ESP32/71_FSKRTTY_Transmitter_Test_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/71_FSKRTTY_Transmitter_Test_ESP32/71_FSKRTTY_Transmitter_Test_ESP32.ino new file mode 100644 index 0000000..4a07ea3 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/71_FSKRTTY_Transmitter_Test_ESP32/71_FSKRTTY_Transmitter_Test_ESP32.ino @@ -0,0 +1,192 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a test program for using the LoRa device to transmit upper side band FSK + RTTY. With the LoRa device in FSK direct mode, the frequency of the generated carrier is shifted up + or down at the correct baud rate depending on whether a logic 0 or 1 is being sent. + + The desired shift in frequency is defined in the Settings.h file as 'FrequencyShift'. When the program + starts the actual frequency shift will be calculated according to the discrete frequency steps the + LoRa device can be set to. There are settings for number of data bits, number of start bits and the + value of parity which can be ParityNone, ParityOdd, ParityEven, ParityZero or ParityOne. + + Before the actual data transmission starts you can send a series of marker pips which are short bursts + of up shifted carrier which will be heard as beeps in a correctly tuned receiver. These pips can aid + in setting the receiver decode frequemcy to match the transmission. on some LoRa devices, such as the SX127x + series there can be considerable temperature induced frequency drift. This drift can be caused by outside + temperature changes or the RF device self heating when transmit is turned on. The duration of the pips, + the gaps between them and the period of leadin carrier before the data starts can all be set. To send no + pips just set the number to 0. + + The FSK RTTY routines use the micros() function for timing, and a check is made at the begging of a + character to send to see if micros() migh overflow during the transmission of the character. This check + assumes the lowest baud rate of 45baud, and if an overflow is likley, there will be a short in transmission + pause to allow the overflow to occur. + + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based +#include //include the appropriate SX12XX library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +//Choose whichever test pattern takes your fancy +//uint8_t testBuffer[] = "0123456789* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"; //This string is sent as AFSK RTTY, 7 bit, 2 Stop bit, no parity, 300 baud. +//uint8_t testBuffer[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789"; +//uint8_t testBuffer[] = "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"; +uint8_t testBuffer[] = "$$MyFlight1,2213,14:54:37,51.48230,-3.18136,15,6,3680,23,66,3,0*2935"; + +uint8_t freqShiftRegs[3]; //to hold returned registers that set frequency + + +void loop() +{ + uint8_t index; + + printRegisterSetup(FrequencyShift); + Serial.println(); + + LT.startFSKRTTY(FrequencyShift, NumberofPips, PipPeriodmS, PipDelaymS, LeadinmS); + + if (micros() > 0xF8000000) + { + Serial.print(F("Waiting micros()")); + while(micros() < 0xFFFB6000); + } + + Serial.print(F("Start RTTY micros() = ")); + Serial.println(micros(),HEX); + + LT.transmitFSKRTTY(13, DataBits, StopBits, Parity, BaudPerioduS, LED1); //send carriage return + LT.transmitFSKRTTY(10, DataBits, StopBits, Parity, BaudPerioduS, LED1); //send line feed + for (index = 0; index < (sizeof(testBuffer)-1); index++) + { + LT.transmitFSKRTTY(testBuffer[index], DataBits, StopBits, Parity, BaudPerioduS, LED1); + Serial.write(testBuffer[index]); + } + LT.transmitFSKRTTY(13, DataBits, StopBits, Parity, BaudPerioduS, LED1); //send carriage return + LT.transmitFSKRTTY(10, DataBits, StopBits, Parity, BaudPerioduS, LED1); //send line feed + + Serial.println(); + Serial.print(F("END RTTY micros() = ")); + Serial.println(micros(),HEX); + digitalWrite(LED1, LOW); + Serial.println(); + Serial.println(); + + + Serial.println(micros(),HEX); + + LT.setMode(MODE_STDBY_RC); + + delay(2000); +} + + +void printRegisterSetup(uint32_t shift) +{ + + uint32_t nonShiftedFreq, ShiftedFreq; + uint32_t freqShift; + float exactfreqShift; + + LT.setRfFrequency(Frequency, Offset); //ensure base frequecy is set + LT.getRfFrequencyRegisters(freqShiftRegs); //fill buffer with frequency setting registers values + nonShiftedFreq = ( (uint32_t) freqShiftRegs[0] << 16 ) + ( (uint32_t) freqShiftRegs[1] << 8 ) + freqShiftRegs[2]; + Serial.print(F("NoShift Registers 0x")); + Serial.println(nonShiftedFreq, HEX); + + LT.setRfFrequency((Frequency + shift), Offset); //set shifted frequecy + LT.getRfFrequencyRegisters(freqShiftRegs); //fill buffer with frequency setting registers values + ShiftedFreq = ( (uint32_t) freqShiftRegs[0] << 16 ) + ( (uint32_t) freqShiftRegs[1] << 8 ) + freqShiftRegs[2]; + Serial.print(F("Shifted Registers 0x")); + Serial.println(ShiftedFreq, HEX); + + freqShift = ShiftedFreq - nonShiftedFreq; + exactfreqShift = freqShift * FREQ_STEP; + Serial.print(F("FSKRTTY register shift ")); + Serial.println(freqShift,HEX); + Serial.print(F("FSKRTTY frequency shift ")); + Serial.print(exactfreqShift, 8); + Serial.println(F("hZ")); + + LT.setRfFrequency(Frequency, Offset); //ensure base frequecy is set +} + + +void printRegisterBuffer() +{ +Serial.print(freqShiftRegs[0],HEX); +Serial.print(F(" ")); +Serial.print(freqShiftRegs[1],HEX); +Serial.print(F(" ")); +Serial.print(freqShiftRegs[2],HEX); +Serial.println(); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("71_FSKRTTY_Transmitter_Test_ESP32 Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupDirect(Frequency, Offset); + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/71_FSKRTTY_Transmitter_Test_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/71_FSKRTTY_Transmitter_Test_ESP32/Settings.h new file mode 100644 index 0000000..afe569e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/71_FSKRTTY_Transmitter_Test_ESP32/Settings.h @@ -0,0 +1,43 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions needed. You will also need to connect up the pins for the SPI bus, which +//are SCK on pin 18, MISO on pin 19 and MOSI on pin 23. + +#define NSS 5 //select pin on LoRa device +#define NRESET 27 //reset pin on LoRa device +#define LED1 2 //on board LED, high for on +#define DIO0 35 //DIO0 pin on LoRa device, used for RX and TX done + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + +//******* Setup Direct Modem Parameters Here ! *************** + +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes +const uint16_t deviation = 10000; //deviation, total frequency shift low to high +const float adjustfreq = 0.9; //adjustment to tone frequency + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +//******* Setup FSKRTTY Settings here ! *************** + +uint32_t FrequencyShift = 400; //hertz frequency shift, approx, in 61.03515625hz steps +uint8_t NumberofPips = 2; //number of marker pips to send +uint16_t PipDelaymS = 500; //mS between pips, carrier off +uint16_t PipPeriodmS = 100; //mS length of pip +uint16_t BaudPerioduS = 10000; //uS period for baud, 10000uS for 100baud +uint16_t LeadinmS = 1000; //ms of leadin, shifted carrier +uint8_t DataBits = 7; //number of databits, normally 7 or 8 +uint8_t StopBits = 2; //number of stopbits, normally 1 or 2 +uint8_t Parity = ParityNone; //parity on data bits, ParityNone, ParityOdd, ParityEven, ParityZero, ParityOne + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/75_Balloon_Tracker_Transmitter_SDLogger_ESP32/75_Balloon_Tracker_Transmitter_SDLogger_ESP32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/75_Balloon_Tracker_Transmitter_SDLogger_ESP32/75_Balloon_Tracker_Transmitter_SDLogger_ESP32.ino new file mode 100644 index 0000000..998c44e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/75_Balloon_Tracker_Transmitter_SDLogger_ESP32/75_Balloon_Tracker_Transmitter_SDLogger_ESP32.ino @@ -0,0 +1,898 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a tracker intended for use as a high altitude balloon (HAB) tracker. The + program sends out a standard format payload with LoRa that is compatible with the HABHUB online tracking + system. + + The HAB payload is constructed thus; + + PayloadID,Sequence,Time,Lat,Lon,Alt,Satellites,Volts,Temperature,Resets,Status,Errors,TXGPSfixms,Checksum + Field 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + + The LoRa and frequency settings can be changed in the Settings.h file. There is the option of sending + out a much shorter Search mode binary location only payload. This is intended for ground based searching + and locating. The frequency and LoRa settings of the Search mode packet can be different to the Tracker + mode used by the HAB payload. There is also the option of sending the HAB payload in FSK RTTY format, + see the Settings.h file for all the options. FSK RTTY gets sent at the same frequency as the Tracker mode + HAB packet. + + There is a matching Balloon Tracker Receiver program which writes received data to the Serial monitor as well + as a small OLED display. + + In the Settings.h file you can set the configuration for either a Ublox GPS or a Quectel L70\L80. The GPSs + are configured for high altitude balloon mode. + + It is strongly recommended that a FRAM option is fitted for this transmitter. The sequence, resets and error + nembers are stred in non-volatile memory. This defaults to EEPROM which has a limited endurance of only + 100,000 writes, so in theory the limt is reached after the transmission of 100,000 hab packets. The use of + a FRAM will extend the life of the tracker to circa 100,000,000,000,000 transmissions. + + Changes: + 240420 - Change to work with Easy Pro Mini style modules + 300420 - Improve error detection for UBLOX GPS library + + ToDo: + + Serial monitor baud rate is set at 115200 +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include +#include //include the appropriate library + +SX127XLT LT; //create a library class instance called LT + +#include "Settings.h" +#include "ProgramLT_Definitions.h" + +//************************************************************************************************** +// HAB tracker data - these are the variables transmitted in payload +//************************************************************************************************** +uint32_t TXSequence; //sequence number of payload +uint8_t TXHours; //Hours +uint8_t TXMinutes; //Minutes +uint8_t TXSeconds; //Seconds +float TXLat; //latitude from GPS +float TXLon; //longitude from GPS +uint16_t TXAlt; //altitude from GPS +uint8_t TXSatellites; //satellites used by GPS +uint16_t TXVolts; //measured tracker supply volts +int8_t TXTemperature; //measured temperature +uint16_t TXResets; //number of tracker resets +uint8_t TXStatus = 0; //used to store current status flag bits +uint16_t TXErrors; //number of tracker Errors +uint32_t TXGPSfixms; //fix time of GPS +uint32_t TXGPSHdop; //HDOP value of GPS +uint8_t hours, mins, secs, day, month; +uint16_t year; +//************************************************************************************************** + +uint8_t TXPacketL; //length of LoRa packet sent +uint8_t TXBUFFER[TXBUFFER_SIZE]; //buffer for packet to send + +#include Memory_Library + +#include +#include +File logFile; +char filename[] = "/LOG0000.txt"; //filename used as base for creating logfile, 0000 replaced with numbers +bool SDfound; + +#include //http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object +#define GPSserial HARDWARESERIALPORT + +#include GPS_Library //include previously defined GPS Library + +#include //get library here > https://github.com/PaulStoffregen/OneWire +OneWire oneWire(ONE_WIRE_BUS); //create instance of OneWire library +#include //get library here > https://github.com/milesburton/Arduino-TXTemperature-Control-Library +DallasTemperature sensor(&oneWire); //create instance of dallas library + +uint32_t GPSstartms; //start time waiting for GPS to get a fix + + +void loop() +{ + Serial.println(F("Start Loop")); + + GPSstartms = millis(); + + if (!gpsWaitFix(WaitGPSFixSeconds)) + { + GPS_OutputOff(); + sendCommand(NoFix); //report a GPS fix error + delay(1000); //give receiver enough time to report NoFix + } + Serial.println(); + + if (SDfound) + { + Serial.print(F("Write data to ")); + Serial.print(filename); + + if (logGPSfix(filename)) //log fix data to SD card + { + setStatusByte(SDError, 0); //write to SD OK, clear error flag + } + else + { + cardFail(2); + setStatusByte(SDError, 1); //flag SD card error + } + + Serial.println(); + } + + do_Transmissions(); //do the transmissions + + Serial.println(F("Sleep")); + LT.setSleep(CONFIGURATION_RETENTION); //put LoRa device to sleep, preserve lora register settings + Serial.flush(); //make sure no serial output pending before goint to sleep + + delay(SleepTimesecs * 1000); + + Serial.println(F("Wake")); + LT.wake(); //wake the LoRa device from sleep +} + + +void do_Transmissions() +{ + //this is where all the transmisions get sent + uint32_t startTimemS; + uint8_t index; + + incMemoryUint32(addr_SequenceNum); //increment Sequence number + + if (readConfigByte(SearchEnable)) + { + setSearchMode(); + TXPacketL = buildLocationOnly(TXLat, TXLon, TXAlt, TXStatus); //put location data in SX12xx buffer + Serial.print(F("Search packet > ")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt); + Serial.print(F(",")); + Serial.print(TXStatus); + digitalWrite(LED1, HIGH); + startTimemS = millis(); + TXPacketL = LT.transmitSXBuffer(0, TXPacketL, 10000, SearchTXpower, WAIT_TX); + printTXtime(startTimemS, millis()); + reportCompletion(); + Serial.println(); + } + + delay(1000); //gap between transmissions + + setTrackerMode(); + + TXPacketL = buildHABPacket(); + Serial.print(F("HAB Packet > ")); + printBuffer(TXBUFFER, (TXPacketL + 1)); //print the buffer (the packet to send) as ASCII + digitalWrite(LED1, HIGH); + startTimemS = millis(); + TXPacketL = LT.transmit(TXBUFFER, (TXPacketL + 1), 10000, TrackerTXpower, WAIT_TX); //will return packet length sent if OK, otherwise 0 if transmit error + digitalWrite(LED1, LOW); + printTXtime(startTimemS, millis()); + reportCompletion(); + Serial.println(); + + delay(1000); //gap between transmissions + + if (readConfigByte(FSKRTTYEnable)) + { + LT.setupDirect(TrackerFrequency, Offset); + LT.startFSKRTTY(FrequencyShift, NumberofPips, PipPeriodmS, PipDelaymS, LeadinmS); + + startTimemS = millis() - LeadinmS; + + Serial.print(F("FSK RTTY > $$")); + Serial.flush(); + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + + for (index = 0; index <= (TXPacketL - 1); index++) //its TXPacketL-1 since we dont want to send the null at the end + { + LT.transmitFSKRTTY(TXBUFFER[index], BaudPerioduS, LED1); + Serial.write(TXBUFFER[index]); + } + + LT.transmitFSKRTTY(13, BaudPerioduS, LED1); //send carriage return + LT.transmitFSKRTTY(10, BaudPerioduS, LED1); //send line feed + LT.endFSKRTTY(); //stop transmitting carrier + digitalWrite(LED1, LOW); //LED off + printTXtime(startTimemS, millis()); + TXPacketL += 4; //add the two $ at beginning and CR/LF at end + reportCompletion(); + Serial.println(); + } +} + + +void printTXtime(uint32_t startmS, uint32_t endmS) +{ + Serial.print(F(" ")); + Serial.print(endmS - startmS); + Serial.print(F("mS")); +} + + +void reportCompletion() +{ + Serial.print(F(" ")); + if (TXPacketL == 0) + { + Serial.println(); + reporttransmitError(); + } + else + { + Serial.print(TXPacketL); + Serial.print(F("bytes")); + setStatusByte(LORAError, 0); + } +} + + +void printBuffer(uint8_t *buffer, uint8_t size) +{ + uint8_t index; + + for (index = 0; index < size; index++) + { + Serial.write(buffer[index]); + } +} + + +uint8_t buildHABPacket() +{ + //build the HAB tracker payload + uint16_t index, j, CRC; + uint8_t Count, len; + char LatArray[12], LonArray[12]; + + TXSequence = readMemoryUint32(addr_SequenceNum); //Sequence number is kept in non-volatile memory so it survives TXResets + TXResets = readMemoryUint16(addr_ResetCount); //reset count is kept in non-volatile memory so it survives TXResets + TXVolts = readSupplyVoltage(); + TXTemperature = (int8_t) readTempDS18B20(); + TXErrors = readMemoryUint16(addr_TXErrors); + + dtostrf(TXLat, 7, 5, LatArray); //format is dtostrf(FLOAT,WIDTH,PRECISION,BUFFER); + dtostrf(TXLon, 7, 5, LonArray); //converts float to character array + + len = sizeof(TXBUFFER); + memset(TXBUFFER, 0, len); //clear array to 0s + Count = snprintf((char*) TXBUFFER, + TXBUFFER_SIZE, + "$$%s,%u,%02d:%02d:%02d,%s,%s,%d,%d,%d,%d,%u,%u,%u,%u", + FlightID, + TXSequence, + TXHours, + TXMinutes, + TXSeconds, + LatArray, + LonArray, + TXAlt, + TXSatellites, + TXVolts, + TXTemperature, + TXResets, + TXStatus, + TXErrors, + TXGPSfixms + ); + + CRC = 0xffff; //start value for CRC16 + + for (index = 2; index < Count; index++) //element 2 is first character after $$ at start (for LoRa) + { + CRC ^= (((uint16_t)TXBUFFER[index]) << 8); + for (j = 0; j < 8; j++) + { + if (CRC & 0x8000) + CRC = (CRC << 1) ^ 0x1021; + else + CRC <<= 1; + } + } + + TXBUFFER[Count++] = '*'; + TXBUFFER[Count++] = Hex((CRC >> 12) & 15); //add the checksum bytes to the end + TXBUFFER[Count++] = Hex((CRC >> 8) & 15); + TXBUFFER[Count++] = Hex((CRC >> 4) & 15); + TXBUFFER[Count] = Hex(CRC & 15); + return Count; +} + + +char Hex(uint8_t lchar) +{ + //used in CRC calculation in buildHABPacket + char Table[] = "0123456789ABCDEF"; + return Table[lchar]; +} + + +uint8_t buildLocationOnly(float Lat, float Lon, uint16_t Alt, uint8_t stat) +{ + uint8_t len; + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeUint8(LocationBinaryPacket); //identify type of packet + LT.writeUint8(Broadcast); //who is the packet sent too + LT.writeUint8(ThisNode); //tells receiver where is packet from + LT.writeFloat(Lat); //add latitude + LT.writeFloat(Lon); //add longitude + LT.writeInt16(Alt); //add altitude + LT.writeUint8(stat); //add tracker status + len = LT.endWriteSXBuffer(); //close buffer write + return len; +} + + +void reporttransmitError() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F("TXError,")); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set + incMemoryUint16(addr_TXErrors); //increase the error count + setStatusByte(LORAError, 1); +} + + +void incMemoryUint32(uint32_t addr) +{ + uint32_t val = readMemoryUint32(addr); + val++; + writeMemoryUint32(addr, val); +} + + +void incMemoryUint16(uint32_t addr) +{ + uint16_t val = readMemoryUint16(addr); + val++; + writeMemoryUint16(addr, val); +} + + +void setStatusByte(uint8_t bitnum, uint8_t bitval) +{ + //program the status byte + + if (bitval == 0) + { + bitClear(TXStatus, bitnum); + } + else + { + bitSet(TXStatus, bitnum); + } +} + + +uint8_t readConfigByte(uint8_t bitnum) +{ + return bitRead(Default_config1, bitnum); +} + + +void setTrackerMode() +{ + Serial.println(F("setTrackerMode")); + LT.setupLoRa(TrackerFrequency, Offset, TrackerSpreadingFactor, TrackerBandwidth, TrackerCodeRate, TrackerOptimisation); +} + + +void setSearchMode() +{ + Serial.println(F("setSearchMode")); + LT.setupLoRa(SearchFrequency, Offset, SearchSpreadingFactor, SearchBandwidth, SearchCodeRate, SearchOptimisation); +} + + +uint8_t sendCommand(char cmd) +{ + uint8_t len; + TXVolts = readSupplyVoltage(); + Serial.print(F("Send Cmd ")); + Serial.write(cmd); + Serial.println(); + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + LT.writeUint8(cmd); //this byte defines the packet type + LT.writeUint8(Broadcast); //destination address of the packet, the receivers address + LT.writeUint8(ThisNode); //source address of this node + LT.writeUint16(TXVolts); //add the battery voltage + LT.writeUint8(TXStatus); //add the status byte + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + //now transmit the packet, set a timeout of 5000mS, wait for it to complete sending + + digitalWrite(LED1, HIGH); //turn on LED as an indicator + TXPacketL = LT.transmitSXBuffer(0, len, 5000, TrackerTXpower, WAIT_TX); + digitalWrite(LED1, LOW); //turn off indicator LED + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //flash LED to show tracker is alive + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void clearAllMemory() +{ + //clears the whole of non-volatile + Serial.println(F("Clear Memory")); + fillMemory(addr_StartMemory, addr_EndMemory, 0); +} + + +float readTempDS18B20() +{ + float DS18B20TXTemperature; + sensor.requestTemperatures(); + DS18B20TXTemperature = sensor.getTempCByIndex(0); + return DS18B20TXTemperature; +} + + +void printTempDS18B20() +{ + float DS18B20TXTemperature; + DS18B20TXTemperature = readTempDS18B20(); + Serial.print(F("Temperature ")); + Serial.print(DS18B20TXTemperature, 1); + Serial.println(F("c")); +} + + +void printSupplyVoltage() +{ + //get and display supply volts on terminal or monitor + Serial.print(F("Volts ")); + Serial.print(readSupplyVoltage()); + Serial.println(F("mV")); +} + + +uint16_t readSupplyVoltage() +{ + //relies on 1V1 internal reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t volts = 0; + uint8_t index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn MOSFET connection resitor divider in circuit + } + + temp = analogRead(SupplyAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(SupplyAD); + volts = volts + temp; + } + volts = ((volts / 5) * ADMultiplier); + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn MOSFET connection resitor divider in circuit + } + + return volts; +} + + +//*********************************************************** +// Start GPS Functions +//*********************************************************** + +void GPSTest() +{ + uint32_t endmS; + + endmS = millis() + 2000; //run GPS echo for 2000mS + + while (millis() < endmS) + { + while (GPSserial.available() > 0) + Serial.write(GPSserial.read()); + } + Serial.println(); + Serial.println(); + Serial.flush(); +} + + +bool gpsWaitFix(uint16_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for good fix + + uint32_t endwaitmS, millistowait, currentmillis; + uint8_t GPSchar; + + Serial.flush(); + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.print(F("s ")); + Serial.flush(); + + GPS_OutputOn(); + Serial.flush(); + + currentmillis = millis(); + millistowait = waitSecs * 1000; + endwaitmS = currentmillis + millistowait; + + while (GPSserial.read() >= 0); //clear the GPS serial input buffer + + while (millis() < endwaitmS) + { + + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated()) + { + TXLat = gps.location.lat(); + TXLon = gps.location.lng(); + TXAlt = (uint16_t) gps.altitude.meters(); + + //Altitude is used as an unsigned integer, so that the binary payload is as short as possible. + //However gps.altitude.meters(); can return a negative value which converts to + //65535 - Altitude, which we dont want. So we will assume any value over 60,000M is zero + + if (TXAlt > 60000) + { + TXAlt = 0; + } + + TXHours = gps.time.hour(), + TXMinutes = gps.time.minute(), + TXSeconds = gps.time.second(), + TXSatellites = gps.satellites.value(); + TXGPSHdop = gps.hdop.value(); + + hours = gps.time.hour(); + mins = gps.time.minute(); + secs = gps.time.second(); + day = gps.date.day(); + month = gps.date.month(); + year = gps.date.year(); + + setStatusByte(GPSFix, 1); + + TXGPSfixms = millis() - GPSstartms; + + Serial.print(F("Have GPS Fix ")); + Serial.print(TXGPSfixms); + Serial.print(F("mS")); + Serial.println(); + + return true; + } + + } + + //if here then there has been no fix and a timeout + GPS_OutputOff(); + setStatusByte(GPSFix, 0); //set status bit to flag no fix + incMemoryUint16(addr_TXErrors); + Serial.println(F("Error No GPS Fix")); + return false; +} + +//*********************************************************** +// End GPS Functions +//*********************************************************** + + +//******************************************************************************* +// Start SD card routines +//******************************************************************************* + +bool logGPSfix(char *buf ) +{ + float tempfloat; + + logFile = SD.open(buf, FILE_APPEND); + + if (!logFile) + { + return false; + } + + tempfloat = ( (float) TXGPSHdop / 100); + + if (hours < 10) + { + logFile.print(F("0")); + } + + logFile.print(hours); + logFile.print(F(":")); + + if (mins < 10) + { + logFile.print(F("0")); + } + + logFile.print(mins); + logFile.print(F(":")); + + if (secs < 10) + { + logFile.print(F("0")); + } + + logFile.print(secs); + logFile.print(F(",")); + + logFile.print(day); + logFile.print(F("/")); + logFile.print(month); + logFile.print(F("/")); + logFile.print(year); + + logFile.print(F(",")); + logFile.print(TXLat, 6); + logFile.print(F(",")); + logFile.print(TXLon, 6); + logFile.print(F(",")); + logFile.print(TXAlt, 1); + logFile.print(F(",")); + logFile.print(TXSatellites); + logFile.print(F(",")); + logFile.print(tempfloat, 2); + logFile.print(F(",")); + logFile.print(TXVolts); + logFile.print(F(",")); + logFile.print(TXTemperature); + logFile.print(F(",")); + logFile.print(TXResets); + logFile.print(F(",")); + logFile.print(TXStatus); + logFile.print(F(",")); + logFile.print(TXErrors); + logFile.print(F(",")); + logFile.print(TXGPSfixms); + logFile.println(); + logFile.close(); + return true; +} + + +uint8_t setupSDLOG(char *buf) +{ + //creats a new filename + + uint16_t index; + + for (index = 1; index <= 9999; index++) { + buf[4] = index / 1000 + '0'; + buf[5] = ((index % 1000) / 100) + '0'; + buf[6] = ((index % 100) / 10) + '0'; + buf[7] = index % 10 + '0' ; + if (! SD.exists(filename)) { + // only open a new file if it doesn't exist + logFile = SD.open(buf, FILE_WRITE); + break; + } + } + + setStatusByte(SDError, 0); //SD card write OK + return index; //return number of logfile created +} + + +void cardFail(uint8_t num) +{ + Serial.print(num); //so we can tell where crd failed + Serial.println(" Card failed, or not present"); + led_Flash(100, 25); +} + +//******************************************************************************* +// End SD card routines +//******************************************************************************* + + +void setup() +{ + uint32_t i; + uint16_t j; + + Serial.begin(115200); //Setup Serial console ouput + Serial.println(); + Serial.println(); + Serial.println(F("75_Balloon_Tracker_Transmitter_SDLogger_ESP32 Starting")); + + memoryStart(Memory_Address); //setup the memory + j = readMemoryUint16(addr_ResetCount); + j++; + writeMemoryUint16(addr_ResetCount, j); + j = readMemoryUint16(addr_ResetCount); + + Serial.print(F("TXResets ")); + Serial.println(j); + + if (GPSPOWER >= 0) //if GPS needs power switching, turn it on + { + pinMode(GPSPOWER, OUTPUT); + digitalWrite(GPSPOWER, GPSONSTATE); + } + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); //for MOSFET controlling battery volts resistor divider + } + +#ifdef QUECTELINUSE + Serial.println(F("Quectel GPS library")); +#endif + +#ifdef UBLOXINUSE + Serial.println(F("UBLOX GPS library")); +#endif + +#ifdef ClearAllMemory + clearAllMemory(); +#endif + + SPI.begin(); //initialize SPI + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("LoRa Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + setTrackerMode(); + + Serial.print(F("Config ")); + Serial.println(Default_config1, BIN); + + j = readMemoryUint16(addr_TXErrors); + Serial.print(F("TXErrors ")); + Serial.println(j); + + Serial.print(F("TXSequence ")); + i = readMemoryUint32(addr_SequenceNum); + Serial.println(i); + + Serial.print(F("ThisNode ")); + Serial.println(ThisNode); + + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + + Serial.println(); + printSupplyVoltage(); + printTempDS18B20(); + Serial.println(); + + j = readSupplyVoltage(); //get supply mV + TXStatus = 0; //clear all TX status bits + + sendCommand(PowerUp); //send power up command, includes supply mV and config, on tracker settings + + + Serial.println(); + Serial.println("Initializing SD card"); + + if (!SD.begin(SDCS)) + { + cardFail(1); + Serial.println(); + SDfound = false; + setStatusByte(SDError, 1); //flag SD card error + } + else + { + Serial.println("Card initialized"); + setupSDLOG(filename); //setup logfile name for writing + Serial.print(F("Write file to ")); + Serial.println(filename); + SDfound = true; + } + + + GPS_OutputOn(); + GPSTest(); + GPS_Setup(); //GPS should have had plenty of time to initialise by now + + delay(2000); + + if (GPS_CheckConfiguration()) //Check that GPS is configured for high altitude mode + { + Serial.println(); + GPS_OutputOff(); //GPS interrupts cause problems with lora device, so turn off for now + setStatusByte(GPSError, 0); + setStatusByte(GPSConfigError, 0); + + //Alert user to GPS OK, turn LED on and send a FM tone. + digitalWrite(LED1, HIGH); + Serial.println(F("GPS Config OK")); //check tone indicates navigation model 6 set + Serial.println(); + Serial.flush(); + LT.setupDirect(TrackerFrequency, Offset); //need direct mode for tones + LT.toneFM(1500, 500, deviation, adjustfreq, TrackerTXpower); //Transmit an FM tone, 1000hz, 3000ms + delay(1000); + digitalWrite(LED1, LOW); + } + else + { + setStatusByte(GPSConfigError, 1); + incMemoryUint16(addr_TXErrors); + Serial.println(F("GPS Error")); + Serial.println(); + setTrackerMode(); + sendCommand(NoGPS); //make sure receiver knows about GPS error + led_Flash(100, 25); //long very rapid flash for GPS error + } + + GPSstartms = millis(); + + setTrackerMode(); //so that commands indicating wait for a GPS go out + + while (!gpsWaitFix(5)) //wait for the initial GPS fix, this could take a while + { + sendCommand(NoFix); + led_Flash(2, 50); //two short LED flashes to indicate GPS waiting for fix + } + + LT.setupDirect(TrackerFrequency, Offset); //need direct mode for tones + digitalWrite(LED1, HIGH); + LT.toneFM(500, 2000, deviation, adjustfreq, TrackerTXpower); + digitalWrite(LED1, LOW); + GPS_OutputOn(); + delay(2000); //GPS may be in software backup allow time for it to wakeup + GPS_SetCyclicMode(); //set this regardless of whether hot fix mode is enabled + GPS_OutputOff(); + + + + + +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/75_Balloon_Tracker_Transmitter_SDLogger_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/75_Balloon_Tracker_Transmitter_SDLogger_ESP32/Settings.h new file mode 100644 index 0000000..2730922 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/ESP32/Tracker/75_Balloon_Tracker_Transmitter_SDLogger_ESP32/Settings.h @@ -0,0 +1,142 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//************************************************************************************************** +// 1) Hardware related definitions and options - specify lora board type and pins here +//************************************************************************************************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. + +#define NSS 5 //select on LoRa device +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NRESET 27 //reset on LoRa device +#define DIO0 35 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 2 //On board LED, high for on +#define BUZZER -1 //Buzzer if fitted, high for on. Set to -1 if not used +#define ONE_WIRE_BUS 32 //for DS18B20 temperature sensor +#define SupplyAD 36 //pin for reading supply\battery voltage +#define BATVREADON 2 //turns on battery resistor divider, high for on +#define ADMultiplier 11.65 //Multiplier for conversion of AD reading to mV +#define SDCS 13 //microSD card select + +#define RXpin 17 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin 16 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER 26 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE LOW //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE HIGH //logic level to turn GPS off via pin GPSPOWER + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//************************************************************************************************** +// 2) Program Options +//************************************************************************************************** + +#define ClearAllMemory //Clears memory of stored tracker information, counts, errors etc + +//************************************************************************************************** +// 3) LoRa modem settings +//************************************************************************************************** + +//LoRa Modem Parameters +const uint32_t Offset = 0; //offset frequency for calibration purposes + +//Tracker mode +const uint32_t TrackerFrequency = 434000000; //frequency of transmissions +const uint8_t TrackerBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t TrackerSpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t TrackerCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t TrackerOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t TrackerTXpower = 10; //LoRa TX power in dBm + +//Search mode +const uint32_t SearchFrequency = 434000000; //frequency of transmissionsconst +uint8_t SearchBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SearchSpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t SearchCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t SearchOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t SearchTXpower = 10; //LoRa TX power in dBm + +const uint16_t deviation = 10000; //deviation in hz for FM tones +const float adjustfreq = 0.9; //adjustment to tone frequency + +const byte TXBUFFER_SIZE = 128; //defines the maximum size of the trasnmit buffer; + + +//************************************************************************************************** +// 4) GPS Options +//************************************************************************************************** + +#define GPSBaud 9600 //GPS Baud rate + +#define HARDWARESERIALPORT Serial2 //if your using hardware serial for the GPS, define it here + +const uint16_t WaitGPSFixSeconds = 60; //when in flight the time to wait for a new GPS fix + +#define GPS_Library //use library file for UBLOX GPS +//#define GPS_Library //use library file for Quectel GPS + + +//************************************************************************************************** +// 5) FSK RTTY Settings +//************************************************************************************************** + +uint32_t FrequencyShift = 500; //hertz frequency shift for audio +uint8_t NumberofPips = 4; //number of marker pips to send +uint16_t PipDelaymS = 1000; //mS between pips when carrier is off +uint16_t PipPeriodmS = 100; //mS length of pip +uint16_t BaudPerioduS = 10000; //uS period for baud, 10000uS for 100baud +uint16_t LeadinmS = 1000; //ms of leadin constant shifted carrier + + +//**************************************************************************************************** +// 6) Program Default Option settings - This section determines which options are on or off by default, +// these are saved in the Default_config1 byte. These options are set in this way so that it is +// possible (in future program changes) to alter the options remotly. +//************************************************************************************************** + +uint8_t OptionOff = 0; +uint8_t OptionOn = 1; + +const char option_SearchEnable = OptionOff; //set to OptionOn to enable transmit of Search mode packet +const char option_FSKRTTYEnable = OptionOff; //set to OptionOn to enable transmit of FSKRTTY + +#define option_SearchEnable_SUM (option_SearchEnable*1) +#define option_FSKRTTYEnable_SUM (option_FSKRTTYEnable*4) + +const unsigned int Default_config1 = (option_SearchEnable_SUM + option_FSKRTTYEnable_SUM); +//const unsigned int Default_config1 = 0x05; //Phew, the default config can always be set manually........ + //0x05 would turn on transmit of search mode and FSKRTTY + + +//************************************************************************************************** +// 7) Memory settings - define the type of memory to use for non-Volatile storage. +// The ESP32 version of the HAB tracker transmitter only supports the use of FRAM here. +//************************************************************************************************** + +//#define Memory_Library +#define Memory_Library + +int16_t Memory_Address = 0x50; //default I2C address of MB85RC16PNF and FM24CL64 FRAM + +//************************************************************************************************** +// 8) HAB Flight Settings +//************************************************************************************************** + +char FlightID[] = "Flight1"; //flight ID for HAB packet + +const unsigned int SleepTimesecs = 13; //sleep time in seconds after each TX loop + +const char ThisNode = '1'; //tracker number for search packet + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/40_LoRa_Transmitter_ImplicitPacket/40_LoRa_Transmitter_ImplicitPacket.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/40_LoRa_Transmitter_ImplicitPacket/40_LoRa_Transmitter_ImplicitPacket.ino new file mode 100644 index 0000000..f8a9451 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/40_LoRa_Transmitter_ImplicitPacket/40_LoRa_Transmitter_ImplicitPacket.ino @@ -0,0 +1,192 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 04/01/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is an example of the use of implicit or fixed length LoRa packets. + Implicit packets have no header so both transmitter and receiver need to be programmed with the packet + length in use. The use of spreading factor 6 requires implicit packets and together with a bandwidth + of 500khz, leads to the shortest possible and lowest air time packets. + + This example sends a buffer that is 23 characters long and that length must be defined in Settings.h + as the constant 'PacketLength'. + + A packet containing ASCII text is sent according to the frequency and LoRa settings specified in the + 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + The details of the packet sent and any errors are shown on the Serial Monitor, together with the transmit + power used, the packet length and the CRC of the packet. The matching receive program, + '41_LoRa_Receiver_ImplicitPackets' can be used to check the packets are being sent correctly, the + frequency and LoRa settings (in Settings.h) must be the same for the Transmit and Receive program. + + Sample Serial Monitor output; + + 10dBm Packet> Hello World 123456* BytesSent,23 CRC,DAAB TransmitTime,8mS PacketsSent,1 + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint8_t TXPacketL; +uint32_t TXPacketCount, startmS, endmS; + +uint8_t buff[] = "Hello World 1234567890"; //buffer length must be 23, and defined in constant PacketLength + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array, must be 19 characters + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on reciver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + startmS = millis(); //start transmit timer + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + endmS = millis(); //packet sent, note end time + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + uint16_t localCRC; + + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + localCRC = LT.CRCCCITT(buff, TXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); //print CRC of sent packet + Serial.print(F(" TransmitTime,")); + Serial.print(endmS - startmS); //print transmit time of packet + Serial.print(F("mS")); + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F("SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setupLoRa() +{ + //this setup is used so as the implicit packet type,LORA_PACKET_FIXED_LENGTH, is used + LT.setMode(MODE_STDBY_RC); //got to standby mode to configure device + LT.setPacketType(PACKET_TYPE_LORA); //set for LoRa transmissions + LT.setRfFrequency(Frequency, Offset); //set the operating frequency + LT.calibrateImage(0); //run calibration after setting frequency + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); //set LoRa modem parameters + LT.setBufferBaseAddress(0x00, 0x00); //where in the SX buffer packets start, TX and RX + LT.setPacketParams(8, LORA_PACKET_FIXED_LENGTH, PacketLength, LORA_CRC_ON, LORA_IQ_NORMAL); //set packet parameters + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword, LORA_MAC_PRIVATE_SYNCWORD = 0x12, or LORA_MAC_PUBLIC_SYNCWORD = 0x34 + LT.setHighSensitivity(); //set for highest sensitivity at expense of slightly higher LNA current + //This is the typical IRQ parameters set, actually excecuted in the transmit function + LT.setDioIrqParams(IRQ_RADIO_ALL, IRQ_TX_DONE, 0, 0); //set for IRQ on TX done +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("40_LoRa_Transmitter_ImplicitPacket Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //this function call sets up the device for LoRa using the settings from the Settings.h file + setupLoRa(); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/40_LoRa_Transmitter_ImplicitPacket/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/40_LoRa_Transmitter_ImplicitPacket/Settings.h new file mode 100644 index 0000000..ce51c92 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/40_LoRa_Transmitter_ImplicitPacket/Settings.h @@ -0,0 +1,40 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF6; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto +const uint8_t PacketLength = 23; //packet length is fixed + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 2000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/41_LoRa_Receiver_ImplicitPacket/41_LoRa_Receiver_ImplicitPacket.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/41_LoRa_Receiver_ImplicitPacket/41_LoRa_Receiver_ImplicitPacket.ino new file mode 100644 index 0000000..9cd43c2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/41_LoRa_Receiver_ImplicitPacket/41_LoRa_Receiver_ImplicitPacket.ino @@ -0,0 +1,258 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is an example of the use of implicit or fixed length LoRa packets. + Implicit packets have no header so both transmitter and receiver need to be programmed with the packet + length in use. The use of spreading factor 6 requires implicit packets and together with a bandwidth + of 500khz, leads to the shortest possible and lowest air time packets. The program listens for incoming + packets using the LoRa settings in the 'Settings.h'. + + This example receives a buffer that is 23 characters long and that length must be defined in Settings.h + as the constant 'PacketLength'. + + The pins to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, + if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. + The LED will flash for each packet received and the buzzer will sound, if fitted. + + Sample serial monitor output; + + 1109s {packet contents} CRC,3882,RSSI,-69dBm,SNR,10dB,Length,19,Packets,1026,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 1189s PacketError,RSSI,-111dBm,SNR,-12dB,Length,0,Packets,1126,Errors,1,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + setupLoRa(); + RXPacketL = LT.receive(RXBUFFER, PacketLength, 0, NO_WAIT); //wait for a packet to arrive with 60seconds (60000mS) timeout + + while (!digitalRead(DIO0)); //wait for DIO0 to go high + + digitalWrite(LED1, HIGH); + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if ( LT.readIrqStatus() == (IRQ_RX_DONE + IRQ_HEADER_VALID)) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + + LT.readPacket(RXBUFFER, PacketLength); + LT.printASCIIPacket(RXBUFFER, PacketLength); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, PacketLength, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(PacketLength); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } + + delay(250); //gives a longer buzzer and LED flash for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setupLoRa() +{ + //this setup is used so as the implicit packet type,LORA_PACKET_FIXED_LENGTH, is used + LT.setMode(MODE_STDBY_RC); //got to standby mode to configure device + LT.setPacketType(PACKET_TYPE_LORA); //set for LoRa transmissions + LT.setRfFrequency(Frequency, Offset); //set the operating frequency + LT.calibrateImage(0); //run calibration after setting frequency + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); //set LoRa modem parameters + LT.setBufferBaseAddress(0x00, 0x00); //where in the SX buffer packets start, TX and RX + LT.setPacketParams(8, LORA_PACKET_FIXED_LENGTH, PacketLength, LORA_CRC_ON, LORA_IQ_NORMAL); //set packet parameters + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword, LORA_MAC_PRIVATE_SYNCWORD = 0x12, or LORA_MAC_PUBLIC_SYNCWORD = 0x34 + LT.setHighSensitivity(); //set for highest sensitivity at expense of slightly higher LNA current + //This is the typical IRQ parameters set, actually excecuted in the receive function + LT.setDioIrqParams(IRQ_RADIO_ALL, IRQ_RX_DONE, 0, 0); //set for IRQ on RX done +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("41_LoRa_Receiver_ImplicitPacket Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //this function call sets up the device for LoRa using the settings from the Settings.h file + setupLoRa(); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/41_LoRa_Receiver_ImplicitPacket/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/41_LoRa_Receiver_ImplicitPacket/Settings.h new file mode 100644 index 0000000..f73d7a5 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Implicit/41_LoRa_Receiver_ImplicitPacket/Settings.h @@ -0,0 +1,45 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define BUZZER 4 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF6; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto +const uint8_t PacketLength = 23; //packet length is fixed + +const int8_t TXpower = 2; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino new file mode 100644 index 0000000..bcd24a4 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino @@ -0,0 +1,99 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 05/11/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program transmits a packet without using a processor buffer, the LoRa device + internal buffer is filled direct with variables. The program is a simulation of the type of packet + that might be sent from a GPS tracker. Note that in this example a buffer of text is part of the + transmitted packet. + + The matching receiving program '9_LoRa_LowMemory_RX' can be used to receive and display the packet + + The contents of the packet received, and printed to serial monitor, should be; + + TR1 (buffer) - trackerID + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Memory use on an Arduino Pro Mini; + Sketch uses 4958 bytes (15%) of program storage space. + Global variables use 224 bytes (10%) of dynamic memory, leaving 1824 bytes for local variables. + + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include +#include "Settings.h" + +SX127XLT LoRa; + + +void loop() +{ + //The SX12XX buffer is filled with variables of a known type and order. Make sure the receiver + //uses the same variable type and order to read variables out of the receive buffer. + + char trackerID[] = "TR1"; + float latitude = 51.23456; + float longitude = -3.12345; + uint16_t altitude = 199; + uint8_t satellites = 8; + uint16_t voltage = 3999; + int16_t temperature = 9; + uint8_t TXPacketL = 0; + uint8_t BytesSent = 0; + + LoRa.startWriteSXBuffer(0); //start the write at SX12XX internal buffer location 0 + LoRa.writeBufferChar(trackerID, sizeof(trackerID)); //+4 bytes (3 characters plus null (0) at end) + LoRa.writeFloat(latitude); //+4 = 8 bytes + LoRa.writeFloat(longitude); //+4 = 12 bytes + LoRa.writeUint16(altitude); //+2 = 14 bytes + LoRa.writeUint8(satellites); //+1 = 15 bytes + LoRa.writeUint16(voltage); //+2 = 17 bytes + LoRa.writeInt8(temperature); //+1 = 18 bytes total to send + TXPacketL = LoRa.endWriteSXBuffer(); //closes packet write and returns the length of the packet to send + + BytesSent = LoRa.transmitSXBuffer(0, TXPacketL, 5000, TXpower, WAIT_TX); //set a TX timeout of 5000mS + + if (BytesSent == 0) //if bytessent is 0, there has been a error + { + Serial.print(F("Send Error")); + } + else + { + Serial.print(BytesSent); + Serial.print(F(" Bytes Sent")); + } + + Serial.println(); + delay(packet_delay); +} + + +void setup() +{ + Serial.begin(9600); + + SPI.begin(); + + if (!LoRa.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("Device error")); + while (1); + } + + LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + Serial.flush(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h new file mode 100644 index 0000000..3a9d0db --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h @@ -0,0 +1,30 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 05/11/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t TXpower = 10; //LoRa TX power in dBm +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino new file mode 100644 index 0000000..2fb3ab6 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino @@ -0,0 +1,168 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 05/11/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program receives a packet without using a memory buffer, the LoRa device + internal buffer is read direct for variables. The program is a simulation of the type of packet + that might be received from a GPS tracker. Note that in this example a buffer of text is part of the + received packet. + + The matching transmitter program '8_LoRa_LowMemory_TX' is used to transmit the packet. + + The contents of the packet received, and printed to serial monitor, should be; + + TR1 (buffer) - trackerID + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + + + Memory use on an Arduino Pro Mini; + + Sketch uses 6290 bytes (19%) of program storage space. + Global variables use 237 bytes (11%) of dynamic memory, leaving 1811 bytes for local variables. + + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX127XLT LoRa; + + +void loop() +{ + uint8_t RXPacketL; + + RXPacketL = LoRa.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + Serial.println(); +} + + +uint8_t packet_is_OK() +{ + char receivebuffer[4]; //create receive buffer, make sure this is big enough for buffer sent !!! + float latitude; + float longitude; + uint16_t altitude; + uint8_t satellites; + uint16_t voltage; + int8_t temperature; + uint8_t RXPacketL; + static uint8_t RXpacketCount; + + //packet has been received, now read from the SX12xx Buffer using the same variable type and + //order as the transmit side used. + + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(" ")); + + LoRa.startReadSXBuffer(0); //start buffer read at location 0 + LoRa.readBufferChar(receivebuffer); //read in the character buffer + latitude = LoRa.readFloat(); //read in the latitude + longitude = LoRa.readFloat(); //read in the longitude + altitude = LoRa.readUint16(); //read in the altitude + satellites = LoRa.readUint8(); //read in the number of satellites + voltage = LoRa.readUint16(); //read in the voltage + temperature = LoRa.readInt8(); //read in the temperature + RXPacketL = LoRa.endReadSXBuffer(); //finish packet read, get received packet length + + Serial.print(receivebuffer); //print the received character buffer + Serial.print(F(",")); + Serial.print(latitude, 5); + Serial.print(F(",")); + Serial.print(longitude, 5); + Serial.print(F(",")); + Serial.print(altitude); + Serial.print(F("m,")); + Serial.print(satellites); + Serial.print(F("sats,")); + Serial.print(voltage); + Serial.print(F("mV,")); + Serial.print(temperature); + Serial.print(F("c ")); + printpacketDetails(); + return RXPacketL; +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LoRa.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + Serial.print(F("PacketError")); + printpacketDetails(); + Serial.print(F("IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void printpacketDetails() +{ + int16_t PacketRSSI; //RSSI of received packet + int8_t PacketSNR; //signal to noise ratio of received packet + + PacketRSSI = LoRa.readPacketRSSI(); + PacketSNR = LoRa.readPacketSNR(); + + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB")); +} + + +void setup() +{ + Serial.begin(9600); + + SPI.begin(); + + if (LoRa.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("Device OK")); + } + else + { + Serial.println(F("Device error")); + while (1); + } + + Serial.flush(); + LoRa.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h new file mode 100644 index 0000000..67f7a9c --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h @@ -0,0 +1,28 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 05/11/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t TXpower = 10; //LoRa TX power in dBm +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino new file mode 100644 index 0000000..81e0489 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino @@ -0,0 +1,306 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a remote control transmitter. When one of four switches are made + (shorted to ground) a packet is transmitted with single byte indicating the state of Switch0 as bit 0, + Switch1 as bit 1 and Switch2 as bit 2. To prevent false triggering at the receiver the packet contains a + 32 bit number called the TXIdentity which in this example is set to 1234554321. The receiver will only + act on, change the state of the outputs, if the identity set in the receiver matches that of the + transmitter. The chance of a false trigger is fairly remote. + + Between switch presses the LoRa device and Atmel microcontroller are put to sleep. A switch press wakes + up the processor from sleep, the switches are read and a packet sent. On a 'bare bones' Arduino setup + the transmitter has a sleep current of approx 2.2uA, so it's ideal for a battery powered remote control + with a potential range of many kilometres. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings + are not necessarily optimised for long range. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +#include //watchdog timer library, integral to Arduino IDE +#include //watchdog timer library, integral to Arduino IDE +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +SX127XLT LT; + +uint32_t TXpacketCount; +uint8_t TXPacketL; + +volatile bool switch0flag = false; +volatile bool switch1flag = false; +volatile bool switch2flag = false; +volatile bool switch3flag = false; + +void loop() +{ + uint8_t switches; + + digitalWrite(LED1, LOW); //turn off indicator LED + Serial.print(F("Sleeping zzzz")); + Serial.flush(); //make sure all serial output has gone + + LT.setSleep(CONFIGURATION_RETENTION); //sleep LoRa device, keeping register settings in sleep. + sleep_permanent(); //sleep Atmel processor permanently for switch wakeup only + + digitalWrite(LED1, HIGH); + + Serial.println(F(" - Awake !!")); //the processor has woken up + switches = readSwitches(); //read the state of the switches + + TXpacketCount++; + Serial.print(TXpacketCount); //print the numbers of sends + Serial.print(F(" Sending > ")); + + Serial.print(switches, BIN); + + if (sendSwitchPacket(switches)) + { + Serial.println(F(" SentOK")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } + + Serial.println(); + delay(500); +} + + +uint8_t sendSwitchPacket(uint8_t switches) +{ + //The SX12XX buffer is filled with variables of a known type and in a known sequence. Make sure the + //receiver uses the same variable types and sequence to read variables out of the receive buffer. + uint8_t len; + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + LT.writeUint8(RControl1); //this byte identifies the type of packet + LT.writeUint32(TXIdentity); //this 32bit integer defines the Identity of the transmiter + LT.writeUint8(switches); //this byte contains the 8 switch values to be sent + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + //now transmit the packet, 10 second timeout, and wait for it to complete sending + TXPacketL = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void sleep_permanent() +{ + attachInterrupts(); + + ADCSRA = 0; //disable ADC + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + + // turn off brown-out enable in software + MCUCR = bit (BODS) | bit (BODSE); //turn on brown-out enable select + MCUCR = bit (BODS); //this must be done within 4 clock cycles of above + interrupts (); //guarantees next instruction executed + + sleep_cpu (); //sleep within 3 clock cycles of above + + /* wake up here */ + + sleep_disable(); + + detachInterrupts(); +} + + +void attachInterrupts() +{ + if (SWITCH0 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH0), wake0, FALLING); + switch0flag = false; + } + + if (SWITCH1 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH1), wake1, FALLING); + switch1flag = false; + } + + if (SWITCH2 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH2), wake2, FALLING); + switch2flag = false; + } + + if (SWITCH3 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH3), wake3, FALLING); + switch3flag = false; + } + +} + + +void detachInterrupts() +{ + if (SWITCH0 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH0)); + } + + if (SWITCH1 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH1)); + } + + if (SWITCH2 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH2)); + } + + if (SWITCH3 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH3)); + } + +} + + + + +void wake0() +{ + switch0flag = true; +} + + +void wake1() +{ + switch1flag = true; +} + + +void wake2() +{ + switch2flag = true; +} + + +void wake3() +{ + switch3flag = true; +} + + +uint8_t readSwitches() +{ + uint8_t switchByte = 0xFF; //start assuming all switches off + + if (switch0flag) + { + bitClear(switchByte, 0); //if the flag is set clear the bit + Serial.println(F("SWITCH0 pressed")); + } + + if (switch1flag) + { + bitClear(switchByte, 1); //if the flag is set clear the bit + Serial.println(F("SWITCH1 pressed")); + } + + if (switch2flag) + { + bitClear(switchByte, 2); //if the flag is set clear the bit + Serial.println(F("SWITCH2 pressed")); + } + + + if (switch3flag) + { + bitClear(switchByte, 3); //if the flag is set clear the bit + Serial.println(F("SWITCH3 pressed")); + } + + return switchByte; +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setupSwitches() +{ + if (SWITCH0 >= 0) + { + pinMode(SWITCH0, INPUT_PULLUP); + } + + if (SWITCH1 >= 0) + { + pinMode(SWITCH1, INPUT_PULLUP); + } + + if (SWITCH2 >= 0) + { + pinMode(SWITCH2, INPUT_PULLUP); + } + + if (SWITCH3 >= 0) + { + pinMode(SWITCH3, INPUT_PULLUP); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + setupSwitches(); + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates LoRa device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Transmitter ready")); + Serial.println(); + +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h new file mode 100644 index 0000000..5ef9b2e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h @@ -0,0 +1,47 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t DIO0 = 3; //DIO0 on LoRa device, used for RX and TX done +const int8_t DIO1 = -1; //DIO1 on LoRa device, normally not used so set to -1 +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +const int8_t SWITCH0 = 2; +const int8_t SWITCH1 = 4; +const int8_t SWITCH2 = A3; +const int8_t SWITCH3 = A2; + +const uint32_t TXIdentity = 1234554321; //define an identity number, the receiver must use the same number +//range is 0 to 4294967296 + + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa transmit power in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino new file mode 100644 index 0000000..a95093f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino @@ -0,0 +1,297 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 31/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a remote control receiver. When a packet is received an 8 bit byte + (SwitchByte) is read and the four outputs (defined in Settings.h) are toggled according to the bits + set in this byte. If the Switch1 byte has bit 0 cleared, then OUTPUT0 is toggled. If the Switch1 byte + has bit 1 cleared, then OUTPUT1 is toggled. If the Switch1 byte has bit 2 cleared, then OUTPUT2 is toggled. + + To prevent false triggering at the receiver the packet contains also contains a 32 bit number called the + TXIdentity which in this example is set to 1234554321. The receiver will only act on, change the state + of the outputs, if the identity set in the receiver matches that of the transmitter. The chance of a + false trigger is fairly remote. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define programversion "V1.0" + +#include +#include +#include "Settings.h" +#include + + +SX127XLT LT; + +uint32_t RXpacketCount; +uint16_t errors; + +uint8_t RXPacketL; //length of received packet +uint8_t RXPacketType; //type of received packet +int16_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet + +uint8_t SwitchByte = 0xFF; //this is the transmitted switch values, bit 0 = Switch0 etc + +void loop() +{ + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); //read the signal strength of the received packet + PacketSNR = LT.readPacketSNR(); //read the signal to noise ratio of the received packet + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +uint8_t packet_is_OK() +{ + //packet has been received, now read from the SX12xx Buffer using the same variable type and + //order as the transmit side used. + uint32_t TXIdentity; + + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(" Packet Received")); + + LT.startReadSXBuffer(0); //start buffer read at location 0 + RXPacketType = LT.readUint8(); //read in the packet type + TXIdentity = LT.readUint32(); //read in the identity of transmitter + SwitchByte = LT.readUint8(); //read in the Switch values + RXPacketL = LT.endReadSXBuffer(); //finish buffer read + + printpacketDetails(); + + if (RXPacketType != RControl1) + { + Serial.print(F(" Wrong packet type")); + led_Flash(5, 25); //short fast speed flash indicates wrong packet type + return 0; + } + + if (TXIdentity != RXIdentity) + { + Serial.print(F(" Transmitter ")); + Serial.print(TXIdentity); + Serial.print(F(" not recognised")); + led_Flash(5, 25); //short fast speed flash indicates transmitter not recognised + return 0; + } + + if (LT.readRXPacketL() != 6) + { + Serial.print(F(" Wrong Packet Length")); + led_Flash(5, 25); //short fast speed flash indicates transmitter not recognised + return 0; + } + + //if we get to here, then the packet is valid so switch outputs accordingly + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, HIGH); + } + + Serial.print(F(",SwitchByte Received ")); + Serial.print(SwitchByte, BIN); //print switch values in binary, if a bit is 0, that switch is active + actionOutputs(SwitchByte); + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + } + + return RXPacketL; +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + printpacketDetails(); + Serial.print(F("IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void printpacketDetails() +{ + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void actionOutputs(uint8_t switches) +{ + //read the recreived switch byte and toggle outputs as required + + if (!bitRead(switches, 0)) + { + //toggle Output state + digitalWrite(OUTPUT0, !digitalRead(OUTPUT0)); //toggle Output state + } + + if (!bitRead(switches, 1)) + { + digitalWrite(OUTPUT1, !digitalRead(OUTPUT1)); //toggle Output state + } + + if (!bitRead(switches, 2)) + { + digitalWrite(OUTPUT2, !digitalRead(OUTPUT2)); //toggle Output state + } + + if (!bitRead(switches, 3)) + { + digitalWrite(OUTPUT3, !digitalRead(OUTPUT3)); //toggle Output state + } +} + + +void setupOutputs() +{ + //configure the output pins, if a pin is defiend in 'Settings.h' as -1, its not configured, so stays as input + + if (OUTPUT0 >= 0) + { + pinMode(OUTPUT0, OUTPUT); + } + + if (OUTPUT1 >= 0) + { + pinMode(OUTPUT1, OUTPUT); + } + + if (OUTPUT2 >= 0) + { + pinMode(OUTPUT2, OUTPUT); + } + + if (OUTPUT3 >= 0) + { + pinMode(OUTPUT3, OUTPUT); + } + + if (BUZZER >= 0) + { + pinMode(BUZZER, OUTPUT); + } + +} + + +void outputCheck(uint8_t number, uint32_t ondelaymS, uint32_t offdelaymS) +{ + uint8_t index; + + Serial.println(F("Toggling outputs")); + + for (index = 1; index <= number; index++) + { + digitalWrite(OUTPUT0, HIGH); + delay(ondelaymS); + digitalWrite(OUTPUT0, LOW); + delay(offdelaymS); + digitalWrite(OUTPUT1, HIGH); + delay(ondelaymS); + digitalWrite(OUTPUT1, LOW); + delay(offdelaymS); + digitalWrite(OUTPUT2, HIGH); + delay(ondelaymS); + digitalWrite(OUTPUT2, LOW); + delay(offdelaymS); + digitalWrite(OUTPUT3, HIGH); + delay(offdelaymS); + digitalWrite(OUTPUT3, LOW); + delay(offdelaymS); + digitalWrite(BUZZER, HIGH); + delay(offdelaymS); + digitalWrite(BUZZER, LOW); + delay(offdelaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + setupOutputs(); + + outputCheck(3, 500, 100); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Receiver ready")); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/22_On_Off_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/22_On_Off_Receiver/Settings.h new file mode 100644 index 0000000..52e8f82 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/22_On_Off_Receiver/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 31/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t DIO0 = 3; //DIO0 on LoRa device, used for RX and TX done +const int8_t DIO1 = -1; //DIO1 on LoRa device, normally not used so set to -1 +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on +const int8_t BUZZER = A5; //buzzer if fitted, set to -1 if not + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +const int8_t OUTPUT0 = 2; +const int8_t OUTPUT1 = 4; +const int8_t OUTPUT2 = A3; +const int8_t OUTPUT3 = A2; + +const uint32_t RXIdentity = 1234554321; //define an identity number, the receiver must use the same number + //range is 0 to 4294967296 + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino new file mode 100644 index 0000000..9ca8468 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino @@ -0,0 +1,229 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 30/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a remote control transmitter that uses a LoRa link to transmit the positions + from a simple joystick to a remote receiver. The receiver uses the sent joystick positions to adjust the + positions of servos. The postions of the joysticks potentiometers on the transmitter are read with the + analogueRead() function. + + If the joystick has a switch, often made by pressing on the joystick, then this can be used to remote + control an output on the receiver. The switch is read by an interrupt, the interrupt routine sets a flag + byte which is read in loop(). + + The program is intended as a proof of concept demonstration of how to remote control servos, the program + is not designed as a practical remote control device for RC model cars for instance. + + It would be straight forward to make the transmitter program send packets continuously, but in most places + in the world that would break a normal limitation of 10% duty cycle for unlicensed use. Therefore the + program was designed to only transmit at a 10% duty cycle. Thus the fastest (lowest air time) packets are + used, spreading factor 6 at a bandwidth of 500khz. This results in an air time for the 5 byte control + packet of around 4mS, so there are around 25 sent per second. + + To have the transmitter program print out the values read from the joystick, comment in the line; + + //#define DEBUG + + Which is just above the loop() function. With the DEBUG enabled the transmission rate, the rate at which + the control packets are transmitted will be slowed down. + + To reduce the risk of the receiver picking up LoRa packets from other sources, the packet sent contains a + 'TXidentity' number, valid values are 0 - 65535. The receiver must be setup with the matching identity + number or the received packets will be ignored. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings + are not necessarily optimised for long range. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +SX127XLT LT; + +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +uint32_t TXpacketCount; +uint8_t TXPacketL; + +uint8_t joystickX1value; //variable to read the value from the analog pin +uint8_t joystickY1value; //variable to read the value from the analog pin + +volatile bool switch1flag = false; + +//#define DEBUG //comment in thie line (remove the two // at the beggining) for debug output + + +void loop() +{ + uint8_t switchByte = 0xFF; + + joystickX1value = (uint8_t) (analogRead(joystickX1) / 4) ; //read the joystick X1 pot, turn 0-1023 into 0 to 255 + joystickY1value = (uint8_t) (analogRead(joystickY1) / 4); //read the joystick Y1 pot + + if (switch1flag) + { + bitClear(switchByte, 1); //if the switch is down clear the bit + digitalWrite(LED1, HIGH); //turn on LED as switch indicator + switch1flag = false; + } + + if (!sendJoystickPacket(joystickX1value, joystickY1value, switchByte)) + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } +} + + +uint8_t sendJoystickPacket(uint16_t X1value, uint16_t Y1value, uint8_t switches) +{ + //The SX12XX buffer is filled with variables of a known type and in a known sequence. Make sure the + //receiver uses the same variable types and sequence to read variables out of the receive buffer. + //uint8_t len; + uint32_t packetStartmS, packettimemS; + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + LT.writeUint8(RControl1); //this is the packet type + LT.writeUint8(TXIdentity); //this value represents the transmitter number + LT.writeUint8(X1value); //this byte contains joystick pot AD X1 value to be sent + LT.writeUint8(Y1value); //this byte contains joystick pot AD Y1 value to be sent + LT.writeUint8(switches); //switches value + LT.endWriteSXBuffer(); //close the packet, thee are 5 bytes to send + + //now transmit the packet, 10 second timeout, and wait for it to complete sending + packetStartmS = millis(); + TXPacketL = LT.transmitSXBuffer(0, PacketLength, 10000, TXpower, WAIT_TX); + packettimemS = millis() - packetStartmS; + +#ifdef DEBUG + Serial.print(TXIdentity); + Serial.print(F(",X1,")); + Serial.print(joystickX1value); + Serial.print(F(",Y1,")); + Serial.print(joystickY1value); + Serial.print(F(",")); + Serial.print(switches, BIN); + Serial.print(F(",")); + Serial.print(packettimemS); + Serial.print(F("mS")); + Serial.println(); +#endif + + digitalWrite(LED1, LOW); //LED off, may have been on due to switch press + + delay(packettimemS * 9); //delay for 9 times packet transmit time to ensure 10% duty cycle + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void attachInterrupts() +{ + if (SWITCH1 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH1), wake1, FALLING); + switch1flag = false; + } +} + + +void detachInterrupts() +{ + if (SWITCH1 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH1)); + } +} + + +void wake1() +{ + switch1flag = true; +} + + +void setupSwitches() +{ + if (SWITCH1 >= 0) + { + pinMode(SWITCH1, INPUT_PULLUP); + } +} + + +void setupLoRa() +{ + //this setup is used so as the implicit packet type,LORA_PACKET_FIXED_LENGTH, is used + LT.setMode(MODE_STDBY_RC); //got to standby mode to configure device + LT.setPacketType(PACKET_TYPE_LORA); //set for LoRa transmissions + LT.setRfFrequency(Frequency, Offset); //set the operating frequency + LT.calibrateImage(0); //run calibration after setting frequency + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); //set LoRa modem parameters + LT.setBufferBaseAddress(0x00, 0x00); //where in the SX buffer packets start, TX and RX + LT.setPacketParams(8, LORA_PACKET_FIXED_LENGTH, PacketLength, LORA_CRC_ON, LORA_IQ_NORMAL); //set packet parameters + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword, LORA_MAC_PRIVATE_SYNCWORD = 0x12, or LORA_MAC_PUBLIC_SYNCWORD = 0x34 + LT.setHighSensitivity(); //set for highest sensitivity at expense of slightly higher LNA current + //This is the typical IRQ parameters set, actually excecuted in the transmit function + LT.setDioIrqParams(IRQ_RADIO_ALL, IRQ_TX_DONE, 0, 0); //set for IRQ on TX done +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + setupSwitches(); + + Serial.begin(115200); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); + } + } + + //this function call sets up the device for LoRa using the settings from the Settings.h file + setupLoRa(); + + attachInterrupts(); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(F("35_Remote_Control_Servo_Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h new file mode 100644 index 0000000..cfa473d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h @@ -0,0 +1,47 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t DIO0 = 3; //DIO0 on LoRa device, used for RX and TX done +const int8_t DIO1 = -1; //DIO1 on LoRa device, normally not used so set to -1 +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +const int8_t joystickX1 = A2; //analog pin for the joystick 1 X pot +const int8_t joystickY1 = A3; //analog pin for the joystick 1 Y pot +const int8_t SWITCH1 = 2; //switch on joystick, set to -1 if not used + +const uint32_t TXIdentity = 123 ; //define a transmitter number, the receiver must use the same number + //range is 0 to 255 + + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_500; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF6; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting +const uint8_t PacketLength = 5; //packet length is fixed + +const int8_t TXpower = 10; //LoRa transmit power in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino new file mode 100644 index 0000000..7b4c027 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino @@ -0,0 +1,275 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 30/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a remote control receiver that uses a LoRa link to control the positions of + servos sent from a remote transmitter. + + If the ttransmitter joystick has a switch, often made by pressing on the joystick, then this can be used + to remote control an output on the receiver. + + The program is intended as a proof of concept demonstration of how to remote control servos, the program + is not designed as a practical remote control device for RC model cars for instance. + + It would be straight forward to make the transmitter program send packets continuously, but in most places + in the world that would break a normal limitation of 10% duty cycle for unlicensed use. Therefore the + program was designed to only transmit at a 10% duty cycle. Thus the fastest (lowest air time) packets are + used, spreading factor 6 at a bandwidth of 500khz. This results in an air time for the 5 byte control + packet of around 4mS, so there are around 25 sent per second. + + To have the receiver program print out the joystick values (0-255) read from the received packet, comment + in the line; + + //#define DEBUG + + Which is just above the loop() function. With the DEBUG enabled then there is a possibility that some + transmitted packets will be missed. With the DEBUG line enabled to servos should also sweep to and fro 3 + times at program start-up. + + To reduce the risk of the receiver picking up LoRa packets from other sources, the packet sent contains a + 'TXidentity' number, valid values are 0 - 255. The receiver must be setup with the matching RXIdentity + number in Settings.h or the received packets will be ignored. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings + are not necessarily optimised for long range. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#define programversion "V1.0" + +#include +#include +#include "Settings.h" +#include + +SX127XLT LT; + +#include +Servo ServoX1; //create the servo object +Servo ServoY1; //create the servo object + +uint8_t joystickX1value; //variable to read the value from the analog pin +uint8_t joystickY1value; //variable to read the value from the analog pin +uint8_t RXPacketL; //length of received packet +uint8_t RXPacketType; //type of received packet + +//#define DEBUG + + +void loop() +{ + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort + + while (!digitalRead(DIO0)); //wait for DIO0 to go high + + if ( LT.readIrqStatus() == (IRQ_RX_DONE + IRQ_HEADER_VALID)) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + +} + + +uint8_t packet_is_OK() +{ + //packet has been received, now read from the SX12xx Buffer using the same variable type and + //order as the transmit side used. + uint8_t TXIdentity; + uint16_t pulseX1, pulseY1; + uint8_t switchByte = 0xFF; //this is the transmitted switch values, bit 0 = Switch0 etc + + LT.startReadSXBuffer(0); //start buffer read at location 0 + RXPacketType = LT.readUint8(); //read in the packet type + TXIdentity = LT.readUint8(); //read in the transmitter number + joystickX1value = LT.readUint8(); //this byte contains joystick pot AD X1 value sent + joystickY1value = LT.readUint8(); //this byte contains joystick pot AD Y1 value sent + switchByte = LT.readUint8(); //read in the Switch values + RXPacketL = LT.endReadSXBuffer(); //end buffer read + +#ifdef DEBUG + Serial.print(TXIdentity); + Serial.print(F(",X1,")); + Serial.print(joystickX1value); + Serial.print(F(",Y1,")); + Serial.print(joystickY1value); + Serial.print(F(",")); + Serial.print(switchByte, BIN); + Serial.println(); +#endif + + + if (RXPacketType != RControl1) + { + Serial.print(F("Packet type ")); + Serial.println(RXPacketType); + led_Flash(5, 25); //short fast speed flash indicates wrong packet type + return 0; + } + + + if (TXIdentity != RXIdentity) + { + Serial.print(F("TX")); + Serial.print(TXIdentity); + Serial.println(F("?")); + return 0; + } + + //actionServos + pulseX1 = map(joystickX1value, 0, 255, 1000, 2000); //scale the numbers from the joystick + ServoX1.writeMicroseconds(pulseX1); + pulseY1 = map(joystickY1value, 0, 255, 1000, 2000); //scale the numbers from the joystick + ServoY1.writeMicroseconds(pulseY1); //move the servo to position + + //actionOutputs + if (!bitRead(switchByte, 1)) + { + digitalWrite(OUTPUT1, !digitalRead(OUTPUT1)); //Toggle Output state + } + + return RXPacketL; +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + int16_t PacketRSSI; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + PacketRSSI = LT.readPacketRSSI(); //read the signal strength of the received packet + Serial.print(F("Err,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm")); + } + Serial.println(); +} + + +void setupOutputs() +{ + //configure the output pins, if a pin is defiend in 'Settings.h' as -1, its not configured, so stays as input + if (OUTPUT1 >= 0) + { + pinMode(OUTPUT1, OUTPUT); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void sweepTest(uint8_t num) +{ + uint16_t index1, index2; + for (index1 = 1; index1 <= num; index1++) + { + for (index2 = 900; index2 <= 2100; index2++) + { + ServoX1.writeMicroseconds(index2); + ServoY1.writeMicroseconds(index2); + } + + delay(1000); + + for (index2 = 2100; index2 >= 900; index2--) + { + ServoX1.writeMicroseconds(index2); + ServoY1.writeMicroseconds(index2); + } + + delay(1000); + } +} + + +void setupLoRa() +{ + //this setup is used so as the implicit packet type,LORA_PACKET_FIXED_LENGTH, is used + LT.setMode(MODE_STDBY_RC); //got to standby mode to configure device + LT.setPacketType(PACKET_TYPE_LORA); //set for LoRa transmissions + LT.setRfFrequency(Frequency, Offset); //set the operating frequency + LT.calibrateImage(0); //run calibration after setting frequency + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); //set LoRa modem parameters + LT.setBufferBaseAddress(0x00, 0x00); //where in the SX buffer packets start, TX and RX + LT.setPacketParams(8, LORA_PACKET_FIXED_LENGTH, PacketLength, LORA_CRC_ON, LORA_IQ_NORMAL); //set packet parameters + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword, LORA_MAC_PRIVATE_SYNCWORD = 0x12, or LORA_MAC_PUBLIC_SYNCWORD = 0x34 + LT.setHighSensitivity(); //set for highest sensitivity at expense of slightly higher LNA current + //This is the typical IRQ parameters set, actually excecuted in the transmit function + LT.setDioIrqParams(IRQ_RADIO_ALL, IRQ_TX_DONE, 0, 0); //set for IRQ on TX done +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + setupOutputs(); + + Serial.begin(115200); + + ServoX1.attach(pinservoX1); //connect pin pinservoX1 to ServoX1 object + ServoY1.attach(pinservoY1); //connect pin pinservoY1 to ServoY1 object + +#ifdef DEBUG + Serial.println(F("Servo sweep test")); + sweepTest(3); +#endif + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + //this function call sets up the device for LoRa using the settings from the Settings.h file + setupLoRa(); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(F("36_Remote_Control_Servo_Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h new file mode 100644 index 0000000..717a1bc --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h @@ -0,0 +1,43 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t DIO0 = 3; //DIO0 on LoRa device, used for RX and TX done +const int8_t DIO1 = -1; //DIO1 on LoRa device, normally not used so set to -1 +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +const int8_t pinservoX1 = 2; //pin for controlling servo X1 +const int8_t pinservoY1 = 4; //pin for controlling servo Y1 +const int8_t OUTPUT1 = 8; //this output toggles when joystick switch is pressed on receiver + +const uint16_t RXIdentity = 123; //define a receiver number, the transmitter must use the same number + //range is 0 to 255 + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_500; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF6; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting +const uint8_t PacketLength = 5; //packet length is fixed + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/86_Buffer_Transmit_Controller/86_Buffer_Transmit_Controller.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/86_Buffer_Transmit_Controller/86_Buffer_Transmit_Controller.ino new file mode 100644 index 0000000..2e7c471 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/86_Buffer_Transmit_Controller/86_Buffer_Transmit_Controller.ino @@ -0,0 +1,127 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/09/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a LoRa transmitter that transmits a buffer containing text to control a + remote device by sending 'LEDOn' to turn the LED on and 'LEDOff' to turn the LED off. + + Sample Serial Monitor output; + + 86_Buffer_Transmit_Controller Starting + LoRa Device found + Transmitter ready + Send Packet> LEDOn + Send Packet> LEDOff + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library + +SX127XLT LT; //create a library class instance called LT + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define DIO0 3 //DIO0 pin on LoRa device, used for sensing RX and TX done +#define LED1 8 //pin for LED + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using +#define TXpower 10 //LoRa transmit power in dBm +#define TXtimeout 10000 //transmit timeout in mS + +uint8_t TXPacketL; + +uint8_t onpacket[] = "LEDOn"; //send this to turn LED on +uint8_t offpacket[] = "LEDOff"; //send this to turn LED off + +#include +#include + + +void loop() +{ + //************************************ + //LED on + //************************************ + + Serial.print(F("Send Packet> (")); + Serial.print(sizeof(onpacket)); + Serial.print(F("chars) ")); + + LT.printASCIIPacket(onpacket, sizeof(onpacket)); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + LT.transmit(onpacket, sizeof(onpacket), TXtimeout, TXpower, WAIT_TX); //will return packet length sent if OK, otherwise 0 if transmit error + + Serial.println(); + delay(5000); //have a delay between packets + + //************************************ + //LED off + //************************************ + + Serial.print(F("Send Packet> (")); + Serial.print(sizeof(offpacket)); + Serial.print(F("chars) ")); + LT.printASCIIPacket(offpacket, sizeof(offpacket)); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, LOW); + LT.transmit(offpacket, sizeof(offpacket), TXtimeout, TXpower, WAIT_TX); //will return packet length sent if OK, otherwise 0 if transmit error + + Serial.println(); + delay(5000); //have a delay between packets +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); + Serial.begin(9600); + Serial.println(); + Serial.println(F("86_Buffer_Transmit_Controller Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupLoRa(434000000, 0, LORA_SF7, LORA_BW_125, LORA_CR_4_5, LDRO_AUTO); //configure frequency and LoRa settings + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/87_Buffer_Receive_Controller/87_Buffer_Receive_Controller.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/87_Buffer_Receive_Controller/87_Buffer_Receive_Controller.ino new file mode 100644 index 0000000..6109d69 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/RemoteControl/87_Buffer_Receive_Controller/87_Buffer_Receive_Controller.ino @@ -0,0 +1,176 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/09/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a LoRa receiver that listens for packets that are text based commands used + when received to turn a LED on or off. When 'LEDOn' is received the LED is turned on and its turned off + when 'LEDOff' is received. + + Sample Serial Monitor output; + + 87_Buffer_Receive_Controller Starting + + LoRa Device found + Receiver ready + 2s Received packet > LEDOn,RSSI,-65dBm,SNR,6dB,Length,5 Turn LED on + 3s Received packet > LEDOff,RSSI,-62dBm,SNR,5dB,Length,6 Turn LED off + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library + +SX127XLT LT; //create a library class instance called LT + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define LED1 8 //pin for LED + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using +#define RXtimeout 60000 //receive timeout in mS + +char RXBUFFER[16]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet + +char onpacket[] = "LEDOn"; //send this to turn LED on +char offpacket[] = "LEDOff"; //send this to turn LED off + + +void loop() +{ + RXPacketL = LT.receive( (uint8_t*) RXBUFFER, sizeof(RXBUFFER), RXtimeout, WAIT_RX); //wait for a packet to arrive with timeout + + PacketRSSI = LT.readPacketRSSI(); //read the received packets RSSI value + PacketSNR = LT.readPacketSNR(); //read the received packets SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + + if (strncmp((char*) RXBUFFER, onpacket, strlen(onpacket)) == 0) + { + Serial.print(F(" Turn LED on")); + digitalWrite(LED1, HIGH); + } + + if ( strncmp( (char*) RXBUFFER, offpacket, strlen(offpacket)) == 0) + { + Serial.print(F(" Turn LED off")); + digitalWrite(LED1, LOW); + } + + } + Serial.println(); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void packet_is_OK() +{ + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" Received packet > ")); + LT.printASCIIPacket((uint8_t*)RXBUFFER, RXPacketL); //print the packet as ASCII characters + + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" Packet error > ")); + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); + Serial.begin(9600); + Serial.println(); + Serial.println(F("87_Buffer_Receive_Controller Starting")); + Serial.println(); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupLoRa(434000000, 0, LORA_SF7, LORA_BW_125, LORA_CR_4_5, LDRO_AUTO); //configure frequency and LoRa settings + + Serial.println(F("Receiver ready")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/1_LED_Blink_STM32/1_LED_Blink_STM32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/1_LED_Blink_STM32/1_LED_Blink_STM32.ino new file mode 100644 index 0000000..c70de07 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/1_LED_Blink_STM32/1_LED_Blink_STM32.ino @@ -0,0 +1,69 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This programs is supplied as is, it is up to the user of the program to decide if the programs are + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program blinks an LED connected the pin number defined below. The pin 13 LED, + fitted to some Arduinos is blinked as well. The blinks should be close to one per second. messages are + sent to the Serial Monitor also. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define LED1 8 //pin number for LED, set logic level high for on + +#define Program_Version "V1.0" + +uint16_t seconds; //used to display time elapsed on Serial Monitor + +void loop() +{ + Serial.print(seconds); + Serial.println(F(" Seconds")); //this message should print on console at close to once per second + seconds++; + digitalWrite(LED1, HIGH); + digitalWrite(13, HIGH); + delay(100); + digitalWrite(LED1, LOW); + digitalWrite(13, LOW); + delay(890); //should give approx 1 second flash +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //general purpose routine for flashing LED as indicator + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); //LED on + digitalWrite(13, HIGH); //Arduino board LED on + delay(delaymS); + digitalWrite(LED1, LOW); //LED off + digitalWrite(13, LOW); //Arduino board LED off + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + pinMode(13, OUTPUT); //setup pin as output for some Arduino boards that include an LED on pin 13 + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("1_LED_Blink_STM32 Starting")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/2_Register_Test_STM32/2_Register_Test_STM32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/2_Register_Test_STM32/2_Register_Test_STM32.ino new file mode 100644 index 0000000..7915fbb --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/2_Register_Test_STM32/2_Register_Test_STM32.ino @@ -0,0 +1,300 @@ +/******************************************************************************************************* + + Programs for Arduino - Copyright of the author Stuart Robinson - 30/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is stand alone, it is not necessary to install the SX12XX-LoRa library + to use it. + + The program checks that a SX127X LoRa device can be accessed by doing a test register write and read. + If there is no device found a message is printed on the serial monitor. The contents of the registers + from 0x00 to 0x7F are printed, there is a copy of a typical printout below. Note that the read back + changed frequency may be slightly different to the programmed frequency, there is a rounding error due + to the use of floats to calculate the frequency. + + The Arduino pin numbers that the NSS and NRESET pins on the LoRa device are connected to must be + specified in the hardware definitions section below. The LoRa device type in use, SX1272, SX1276, + SX1277, SX1278 or SX1279 must be specified also. + + Typical printout; + + 2_Register_Test Starting + SX1276-79 Selected + LoRa Device found + Device version 0x12 + + Frequency at reset 434000000 + Registers at reset + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00 00 09 1A 0B 00 52 6C 80 00 4F 09 2B 20 08 02 0A + 0x10 FF 6F 15 0B 28 0C 12 47 32 3E 00 00 00 00 00 40 + 0x20 00 00 00 00 05 00 03 93 55 55 55 55 55 55 55 55 + 0x30 90 40 40 00 00 0F 00 00 00 F5 20 82 00 02 80 40 + 0x40 00 00 12 24 2D 00 03 00 04 23 00 09 05 84 32 2B + 0x50 14 00 00 12 00 00 00 0F E0 00 0C 00 08 00 5C 78 + 0x60 00 19 0C 4B CC 0F 01 20 04 47 AF 3F CF 00 53 0B + 0x70 D0 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Changed Frequency 434099968 + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00 00 09 1A 0B 00 52 6C 86 66 4F 09 2B 20 08 02 0A + 0x10 FF 6F 15 0B 28 0C 12 47 32 3E 00 00 00 00 00 40 + 0x20 00 00 00 00 05 00 03 93 55 55 55 55 55 55 55 55 + 0x30 90 40 40 00 00 0F 00 00 00 F5 20 82 00 02 80 40 + 0x40 00 00 12 24 2D 00 03 00 04 23 00 09 05 84 32 2B + 0x50 14 00 00 12 00 00 00 0F E0 00 0C 00 08 00 5C 78 + 0x60 00 19 0C 4B CC 0F 01 20 04 47 AF 3F CF 00 53 0B + 0x70 D0 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +const uint8_t REG_FRMSB = 0x06; //register number for setting setting and reading frequency, high byte +const uint8_t REG_FRMID = 0x07; //register number for setting setting and reading frequency, mid byte +const uint8_t REG_FRLSB = 0x08; //register number for setting setting and reading frequency, low byte +const uint8_t REG_VERSION = 0x42; //register containg version number of device + +const uint8_t DEVICE_SX1272 = 0x10; //SX1272 +const uint8_t DEVICE_SX1276 = 0x11; //SX1276 +const uint8_t DEVICE_SX1277 = 0x12; //SX1277 +const uint8_t DEVICE_SX1278 = 0x13; //SX1278 +const uint8_t DEVICE_SX1279 = 0x14; //SX1279 + +//********* Setup hardware definitions here ! ***************** + +//These are the pin definitions for one of the Tracker boards, be sure to change them to match your +//own setup. You will also need to connect up the pins for the SPI bus, which on an Arduino Pro Mini are +//SCK pin 13, MISO pin 12, and MOSI pin 11. + +#define NSS 10 //SX127X device select +#define NRESET 9 //SX127X reset pin +#define DIO0 -1 //DIO0 pin on LoRa device, not used here so set to -1 +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1278 //defines the type of LoRa device used, needed for correct program operation + +//**************************************************************/ + + +#include + + +void setup() +{ + Serial.begin(9600); + Serial.println(F("2_Register_Test_STM32 Starting")); + + SPI.begin(); + SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //The begin function setups the hardware pins used by device and then checks if device is found + //the DIO0, DIO1 and DIO2 pins are not used in this example so are set to -1 + //the LT.begin fuction can define the pins and device type directly in this way (for SX1278); + //LT.begin(10, 9, -1, -1, -1, DEVICE_SX1278) + + if (begin(NSS, NRESET, -1, -1, -1, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + } + else + { + Serial.println(F("No device responding")); + } + + Serial.print(F("Device version 0x")); + uint8_t deviceversion = readRegister(REG_VERSION); + if (deviceversion < 0x10) + { + Serial.print(F("0")); + } + Serial.println(deviceversion, HEX); +} + + +void loop() +{ + uint32_t frequency; + + frequency = getFreqInt(); //read the set frequency following a reset + Serial.print(F("Frequency at reset ")); + Serial.println(frequency); + + Serial.println(F("Registers at reset")); //show the all registers following a reset + printRegisters(0x00, 0x7F); + + Serial.println(); + Serial.println(); + + setRfFrequency(434100000, 0); //change the frequency at reset, in hertz + frequency = getFreqInt(); //read back the changed frequency + Serial.print(F("Changed Frequency ")); + Serial.println(frequency); //print the changed frequency, did the write work (allow for rounding errors) ? + printRegisters(0x00, 0x7F); //show the registers after frequency change + Serial.println(); + delay(5000); + resetDevice(LORA_DEVICE); //reset the device and start again +} + + +uint8_t readRegister(uint8_t address) +{ + uint8_t regdata; + digitalWrite(NSS, LOW); //set NSS low + SPI.transfer(address & 0x7F); //mask address for read + regdata = SPI.transfer(0); //read the byte + digitalWrite(NSS, HIGH); //set NSS high + return regdata; +} + + +void writeRegister(uint8_t address, uint8_t value) +{ + digitalWrite(NSS, LOW); //set NSS low + SPI.transfer(address | 0x80); //mask address for write + SPI.transfer(value); //write the byte + digitalWrite(NSS, HIGH); //set NSS high +} + + +uint32_t getFreqInt() +{ + //get the current set LoRa device frequency, return as long integer + + uint8_t Msb, Mid, Lsb; + uint32_t uinttemp; + float floattemp; + Msb = readRegister(REG_FRMSB); + Mid = readRegister(REG_FRMID); + Lsb = readRegister(REG_FRLSB); + floattemp = ((Msb * 0x10000ul) + (Mid * 0x100ul) + Lsb); + floattemp = ((floattemp * 61.03515625) / 1000000ul); + uinttemp = (uint32_t)(floattemp * 1000000); + return uinttemp; +} + + +void printRegisters(uint16_t Start, uint16_t End) +{ + //prints the contents of SX127x registers to serial monitor + + uint16_t Loopv1, Loopv2, RegData; + + Serial.print(F("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F")); + Serial.println(); + + for (Loopv1 = Start; Loopv1 <= End;) + { + Serial.print(F("0x")); + if (Loopv1 < 0x10) + { + Serial.print(F("0")); + } + Serial.print((Loopv1), HEX); + Serial.print(F(" ")); + for (Loopv2 = 0; Loopv2 <= 15; Loopv2++) + { + RegData = readRegister(Loopv1); + if (RegData < 0x10) + { + Serial.print(F("0")); + } + Serial.print(RegData, HEX); + Serial.print(F(" ")); + Loopv1++; + } + Serial.println(); + } +} + + +void setRfFrequency(uint64_t freq64, int32_t offset) +{ + freq64 = freq64 + offset; + freq64 = ((uint64_t)freq64 << 19) / 32000000; + writeRegister(REG_FRMSB, (uint8_t)(freq64 >> 16)); + writeRegister(REG_FRMID, (uint8_t)(freq64 >> 8)); + writeRegister(REG_FRLSB, (uint8_t)(freq64 >> 0)); +} + + +void resetDevice(uint8_t device) +{ + if (device == DEVICE_SX1272) + { + digitalWrite(NRESET, HIGH); + delay(2); + digitalWrite(NRESET, LOW); + delay(20); + Serial.println(F("SX1272 Selected")); + } + else + { + digitalWrite(NRESET, LOW); + delay(2); + digitalWrite(NRESET, HIGH); + delay(20); + Serial.println(F("SX1276-79 Selected")); + } +} + + +bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinDIO0, int8_t pinDIO1, int8_t pinDIO2, uint8_t device) +{ + pinMode(pinNSS, OUTPUT); + digitalWrite(pinNSS, HIGH); + pinMode(pinNRESET, OUTPUT); + digitalWrite(pinNRESET, LOW); + + if (pinDIO0 >= 0) + { + pinMode( pinDIO0, INPUT); + } + + if (pinDIO1 >= 0) + { + pinMode( pinDIO1, INPUT); + } + + if (pinDIO2 >= 0) + { + pinMode( pinDIO2, INPUT); + } + + resetDevice(device); + + if (checkDevice()) + { + return true; + } + + return false; +} + + +bool checkDevice() +{ + //check there is a device out there, writes a register and reads back + + uint8_t Regdata1, Regdata2; + Regdata1 = readRegister(REG_FRMID); //low byte of frequency setting + writeRegister(REG_FRMID, (Regdata1 + 1)); + Regdata2 = readRegister(REG_FRMID); //read changed value back + writeRegister(REG_FRMID, Regdata1); //restore register to original value + + if (Regdata2 == (Regdata1 + 1)) + { + return true; + } + else + { + return false; + } +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/3_LoRa_Transmitter_STM32/3_LoRa_Transmitter_STM32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/3_LoRa_Transmitter_STM32/3_LoRa_Transmitter_STM32.ino new file mode 100644 index 0000000..bd05cd3 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/3_LoRa_Transmitter_STM32/3_LoRa_Transmitter_STM32.ino @@ -0,0 +1,182 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a simple LoRa test transmitter. A packet containing ASCII text is sent + according to the frequency and LoRa settings specified in the 'Settings.h' file. The pins to access + the SX127X need to be defined in the 'Settings.h' file also. + + The details of the packet sent and any errors are shown on the Serial Monitor, together with the transmit + power used, the packet length and the CRC of the packet. The matching receive program, '4_LoRa_Receive' + can be used to check the packets are being sent correctly, the frequency and LoRa settings (in Settings.h) + must be the same for the Transmit and Receive program. Sample Serial Monitor output; + + 10dBm Packet> {packet contents*} BytesSent,23 CRC,DAAB TransmitTime,54mS PacketsSent,1 + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the SX127X device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint8_t TXPacketL; +uint32_t TXPacketCount, startmS, endmS; + +uint8_t buff[] = "Hello World 1234567890"; + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on reciver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + startmS = millis(); //start transmit timer + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + endmS = millis(); //packet sent, note end time + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + uint16_t localCRC; + + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + localCRC = LT.CRCCCITT(buff, TXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); //print CRC of sent packet + Serial.print(F(" TransmitTime,")); + Serial.print(endmS - startmS); //print transmit time of packet + Serial.print(F("mS")); + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("3_LoRa_Transmitter_STM32 Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); //got to standby mode to configure device + LT.setPacketType(PACKET_TYPE_LORA); //set for LoRa transmissions + LT.setRfFrequency(Frequency, Offset); //set the operating frequency + LT.calibrateImage(0); //run calibration after setting frequency + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); //set LoRa modem parameters + LT.setBufferBaseAddress(0x00, 0x00); //where in the SX buffer packets start, TX and RX + LT.setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); //set packet parameters + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword, LORA_MAC_PRIVATE_SYNCWORD = 0x12, or LORA_MAC_PUBLIC_SYNCWORD = 0x34 + LT.setHighSensitivity(); //set for highest sensitivity at expense of slightly higher LNA current + LT.setDioIrqParams(IRQ_RADIO_ALL, IRQ_TX_DONE, 0, 0); //set for IRQ on RX done + //*************************************************************************************************** + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/3_LoRa_Transmitter_STM32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/3_LoRa_Transmitter_STM32/Settings.h new file mode 100644 index 0000000..ac3018e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/3_LoRa_Transmitter_STM32/Settings.h @@ -0,0 +1,39 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/4_LoRa_Receiver_STM32/4_LoRa_Receiver_STM32.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/4_LoRa_Receiver_STM32/4_LoRa_Receiver_STM32.ino new file mode 100644 index 0000000..55b0f61 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/4_LoRa_Receiver_STM32/4_LoRa_Receiver_STM32.ino @@ -0,0 +1,247 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the SX127X need to be defined in the 'Settings.h' file also. + + There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, + if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. + The LED will flash for each packet received and the buzzer will sound, if fitted. + + Sample serial monitor output; + + 1109s Hello World 1234567890*,CRC,DAAB,RSSI,-61dBm,SNR,9dB,Length,23,Packets,1026,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 1189s PacketError,RSSI,-111dBm,SNR,-12dB,Length,0,Packets,1126,Errors,1,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the SX127X device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } + + delay(250); //gives a longer buzzer and LED flash for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("4_LoRa_Receiver Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in the library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); //got to standby mode to configure device + LT.setPacketType(PACKET_TYPE_LORA); //set for LoRa transmissions + LT.setRfFrequency(Frequency, Offset); //set the operating frequency + LT.calibrateImage(0); //run calibration after setting frequency + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate, LDRO_AUTO); //set LoRa modem parameters + LT.setBufferBaseAddress(0x00, 0x00); //where in the SX buffer packets start, TX and RX + LT.setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); //set packet parameters + LT.setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword, LORA_MAC_PRIVATE_SYNCWORD = 0x12, or LORA_MAC_PUBLIC_SYNCWORD = 0x34 + LT.setHighSensitivity(); //set for highest sensitivity at expense of slightly higher LNA current + LT.setDioIrqParams(IRQ_RADIO_ALL, IRQ_RX_DONE, 0, 0); //set for IRQ on RX done + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x00, 0x4F); //print contents of device registers, normally 0x00 to 0x4F + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/4_LoRa_Receiver_STM32/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/4_LoRa_Receiver_STM32/Settings.h new file mode 100644 index 0000000..a152d0b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Basics/4_LoRa_Receiver_STM32/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define BUZZER 4 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 2; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Pictures/Arduino_IDE_for_STM32.jpg b/lib/SX12XX-LoRa/examples/SX127x_examples/STM32/Pictures/Arduino_IDE_for_STM32.jpg new file mode 100644 index 0000000000000000000000000000000000000000..278a799c3a6f5b46550d0d8a2ec07afa6870de98 GIT binary patch literal 44524 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
5r=7KN>XaO+SvVt#smD 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%0Aj_83XN0wHEk~1ijrUDR{F$e(cXLSl;03!s9 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ℑq6aHwd6TwuV2!1BJRfsPhJ#qu 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 )MtCuk|RBV>S^WG4hiGq8k?SW45- z5hxc$jdpzziAp=4s(ih@p&@{MQw@*+fw!W7kU@kF%Nr4$)Fd1fAf3izqJS900FVuc z$^vBeMXe%9GKe1WBV?=@0X_1JMrU+m%LqvGMSu**>=EnIQ6OWY0fv3ukz#1XaWS2g zXd}`)q3?3wLNH@xppgNeG)_N=4 z&i&A60KgyMgP@>7<=E}f5FqdJM?GRt*vE7+-@#k=Am$Nkd9%?+&s~a=M1kmmIuZ+v z&($lW?U>y$#0}7Q!~Q9Vm}r^%?552UB2bVUC=%1ZPMy#TfR_Wd#V7=xozy)CUcnZ$ z7%+U1bULyP#=!MW$a%MtqhlnZI{-)Mkx?D6F}l5(%Bq}BMD^KR(~Ky2QP%`ZomAnVrJh>9rqHiw#Z6_^e6eUgkI1 z^b@|N;N^Hz_Jx)0T{@``wI204gze-PX3IMD?e0Tf(D6u?n8f+5PNhIvKia6nb0^7H zqw96-3R1W&b4m}ojJ@j3G@)X%U;xI`d>hVz08fj1_04mF2!?L;oQ+4 zYhkR_X=p7y09fAG96;rgaGz14=CQ4G4T<_8a?(AVkcK!(#MgMvj!@k9S^7aUaQ74L zTHLyS`VSwdSZucD%)-mIcZH^F4$r$`S-yqEjQ;?McVSmFe9x#l)5)VlW3Jv=+p?7b zMrnwpRrG_OPJgNZo6#-4%Q7NL!B01#jRmC#RA1BCC^7Re{dF}9A6Olzs|X1eKzRIh1I{inbF z6`+TS@9MXdoyox0a(|`N=nVe=W|d?A01#RXlK_uoFqj_;syXd;5dhW089&8s(7zJ>xMDL@nSRj&U!m`i>#++`+ zy%bvxdz)5eicLX|8hIvX( zEed$lVwuV~4~eD%>iBa~WuBAajd8Y4bDcClXA0jk`ONl%AD1dIl4I_>Ehkc`OG#xm z>)OQ%ZD9tD8zAaq%6Y{4khIRefnwp&jd&e#q}<$=>KBX;&!$Y}Zag=mZ$46DCGkIiox zdrVxsRn(}_AUCz96pJ^%!a0THG{j>Q>^H)(7fVmF4X@i0=#2#rDy`KEmo;uYl&5#Dls} z!c6_sONf;LcVYBeuDKUI1XBcHcG_*04H6gX;#OE z>@VM=-0HP%t}9VzABe4M}UI!e^H zT)(BIT4~nz6=>0*`i=&TM=-mbf_b3kBMUP};;lnW(`i`R8ckzsKB;?|%;)n*Gf6+f zd2+e-JG=5}YN;FjTZVKiCtp75_NA*SYE|u+d3pJnO|;2x`cm@;>bdP-MXb&-zR|S$ zE~xrfE~Lf{;B_aoWR;7qYgP4XCY{4r-xBJz*>iX2s2T|L9E0jsMc)%$+`CbG7|VLv zjdJ^nti?yS9^fZt;!JPV3KCDTIa;$i-pfwXw3c1!G(EnhYr59d#VX~DXr>xm;77$g z;0~BsS~ioW-P+vUTG?D`RlTUOw#K-m`ZS;xxWV^{k~@2=O{Z&q&0xzLn;To(W>tM^ zZLD5AHy92hmBVlZgV`HhYKtn@b;glla$Q)~VQ_N|Bad~Zqv&ywUp`r9svHgK^<1@mKt80qB84IxAyvuzpZ9X8t!9S)-{B=vA@OF$!e~+ zj`pK<7f}?cUFsDJ>(^k17N6h;qkG0mvrpHl+i4WE`>i(K_MJP2T(s4D+YWPD55h)0 z(YhrWwO{Z3LM|xV>l`_0r(1DI@gGv_OS+>;og2wei1j9k1#0fQ5T&6XKGxBmPZtneS;_H-w^9P>b*07fjU9u83g8h%I@`M zmYv!%(_Zv#HEWxS7Xg-(sW>^DuJI-!PRnm^RI-&e_-~j4?76pGdremtIJc3-htIF- zx+^N}xV2?Qf&+(L*U05^c;g4sJtf1iYPAJ@F6x&nnqN)L2eE~Xqu#pi?e*(Xrpm23 zYd#a`msYrSCU|b1TAt+gm4%^I4b)jmWEV`Qj#7phMRdnorDhl6eP16-(&|H_+H1Gp z!2~svj?2%$G6M8}#lq2Lq<{haP!7`BNGto;L z>_<3c5fZC##ubk}V=AY{ag~!TK23J8Kg_CQFt~e&6$IxiyVS(lROT{aaJPea`YbXW znvb~{*DWq#fiSR%Sybxso>%OJdK}_ggVq*Z6A5kL+Hr3ZoqI~tsoYTl^46H3>Y$i? zHi&5D7{`73E? zJ93Er@LQu-ki0aABdXNtl^V;Uz(!b1PeBWDMuTc+ow3qaE?>Ko2HWhxb4%yVa~g~b zhx)F|TA(caCR2MK;t4#aW65#4o0@K_M|hQ3`!vVdhs~T1{MCh~HuY7o4>|Q$40GC83W4;ReSH!q+&o3D_mb1=|e&JqNFsEEj670VtT0R+@u@`Rfd7F$EtQx4Lr^m z^XipJ4x$lIoa~{ha3vv#j3EGSp4p3ROWjn?4C8&(Zm39>^+?eMCw;Pr0%A@P?Z_I6 zCpjDJm{m9uHcH?D#s`vCv%`AbJE$&uCHvL7kSH z8#8Soocf%y96;*{Io$-z`6vh?Auwz^0|?mACtzuCa3JiqcDJ()s9>#diG#57%79Fh z=#QxjYG(Dd80QRw)od(}?_1twm1+6WrJu)6B!^$B4KPTgPZ|tV24Ru8F=9A!{M9Vb-dgiq>iq0kL0;O50GH4 z$5?3p03@`T>CQbM!sKc?0`S@U2cgN!N~oaxYHr8RVu3R4(^B`0cM4Tm>g!>@PdNS` z{fg}myxy?Bgtc+0Z%Okd?GE~hUp!k3kW9`O&>9U-7e%qD)$}R2{Mrik@V<}dEgIXX z$shOGuxJn?bj}cBGEitI(HIdEC3PO4&X6J#HyJyi*=`Tn79Prw5WyJACVGSX)EWfJS8%I>Yv-nh>bWRe2>64V-GK7F&a+*g#k*6XP4UbTj z36L^D**hD`X9xzsf{!3Yf-DCL1Kj+Ahi@RHe3XcpPS`{Y zY$YHtrFMX5DlCo{S9K!LVrxJE!2Q{o#!1W1gcb{<$lCDb#~GDToP>m(41Lfe1ocV`2{9-T zNy&uJI&?q+K%QQyOw4usl)!2Ph%wm%M2Pf3#KD30K)N^RfX_EvrYaN|$=}rh6FWyl zK$3|A{{S>tQCxEz5{p43PCJAN!GZzu2!ca#*MC&NQs{{P0L=$SS;{XYk|)sygMN2Lda;C ziT-ODgmVF$tgCo6X^w@!EalNx!SepEb!k)aA6K~4X)?XDudaz>%r)Ng+9%Tobq?dha-mc{b98XSETal zZ5a1n=7)U(;r0IjZW?6rJ|6+;QhVJaWqm@ED-z%iWD$^7n~F7uS=v!{mqFpwNHv`1 z$Z(9pzTZ;tPlr%*Hess!-wx)VAZ*kkN0+Mk9W!`1lC;luZ*#gabj@1NTC#0MNKW|D+deh;Yh4bqNQ{{VxU(pz%sV7uIkm z;ycGhD%_Fi%NMnZR_w<$2K>f*1#92Q!+O)io@!L-{{VWuKl~+O8cq`-XWcNjgmp6p zOYPh8=W+bNUATVqe8;Nsrjdr-UecYrs=Dg-y;obhgKetituAS7^AivzK#qY~Ut70* zI+skRQ}&g`9|EI^_XA6LNgidm@?7b%xocYEZ&WnkIJAI$3cJ~=2bTCwmW+rv_3XW9 zD6``x+A1bU#L6S$$RsQ4T34XZ>bCV5!eAFPi9+6)sa1^)hdIHy=_PErTo|`p9W^~W zPJV%BRoA!T-s0uV6q`Yg0;5{qXMA0qA62H->;|2z-&i%}?YFGzMf-CWmDbyPHjjMm zBHr^;Wy33YWz45Z)Wu1HKyW91p>f*18k8^YsPnb;Db;%%@@JTb5KQ$-3gk@qDQM+v zG<4lg*fS|wdIU_9lDcc;z-b^)M9p`&Oap|TM+(Yu%qQXMnp_Uf*7DZ<#^UDcgRUvk ztZkokY$!N6?ZFb_9AnXQ`n3mFdmBcRT3ASM6d6bC(q(iyy~PTZuU$gspNz}O#M{gg z1wuK9^NIPccT&`wPVjOXb$P>HO(#ff^q%9Y@}-j^mk9J@ixcgen;JBW*+!MKpK$_M z-7<^*=Ab9$vG|CJnhl*=H3zb~?yWxrTDE~@Mlrm^^04zEQ1$EjKNgT!@0({pifeWPK6PImK!%5%hy`>vx~q2XyPd#GDebDrTf zD)U`n$G#!}?73&bX#!<&E>5<&;GD;?$}Mo^APgWHIYjT>2oWJ6Fm8S9t2c<0gaH}H zM9lcwTz%fDFjk!way`d5k+Dq5Jgjr&zUUa{5`Ia=M);h~$c)HU)jsGLlaqwGA4KNn z$Od-wLFc3sJ=0Jjje`Xmq+^%nodu(5!t@P&PMce6b8{qnmeu?x@6pmBZfM<#E^|hw zcR+&g-PN@fOLA}>IX-yxT8Z{&=tX(-uE8Y!Pk%3s+VRhjg3otV&*p1 zth|6~(GMa3^V=(C%{`R$Di-(lwlyeLw7af3D0psTsZevqLE2;Pw0u2vUc4HW8w`B7 zId=|UHg4ctY z5@dZ=$4aT9e%Im4+WM14?X8ul@WA&6hfm&Xozc=v4YbzAMeOlg z4{Vmi^K#EIA0;gG4NC6B+*{hzeHRCN(%g=u36chOU5$h3omYkRcU{?wJ1sJmD~7te z1H+q5;J21!<-?~%I99bV%^u?Jo|^hLl;M@Vvy!D2iT7PfWydF`WpifBJ(B+beKD}C>6(%{}WVC;R^PUvqO4`p6(6oC4SouEb z?Ungla8E0olHngnv9}vKPCJ)V(`TiB%(M9~o+|_6Jek2VSJA(S&HetK(TS|@lol_X zD>`0CfjI$vx9QLQvE=I))J%o|OquGvAMo<_owb`O*4m?t9+C?4WBn6gfMs{w6=6!| z(%RywacvUf8_<$^{TFYmMLjdzXQlhjhWkmkseMq2)hQP=zmzxt?8FZ>y0E4jX-C7D zEd)3_X@7$0Jq)- zB1jWIHK@__iZ+^$H9Lb`1M!zgg_)?DZnddR(qwHjD+h;K+BIEDn?qXn{5yK16r!~9 zjJpMUxahY%C z0VA^e-$bw4!*552uD@}f-}p`=gGX_9S25(p*N)qDT!-N){{YnATgt!gv32j?W!I7Y zYSBV}Wz01Br^UY==y zgku{OQ9P%8m5wFb0zT@u06M6l{seiEg;54f^;VD@u~j?DR&5qxYB{#54`svMoR!^Q z&$3iKIad#KJ>v_TDaopC(W|Nq$b~6aEdV&+D}y{rp_l~qRNqi#Dv;WN@w{{^KKK+B zqcZbR6s=hLENfoET|G|QE`GJ;YwNyhuJ@ysPBGbEUYT_~oDQoRoe<36lhs0o*TLGP zr4uH>&u#qx$3b($3GyZu`re}|+d0j5C03HhG#;SB=jgc8tlEGHYlDF4Aj-`4hE%Ta z^J;8+i6zW)znmg?<1-e#m)#P|jHDgUR;1+r01OFS6G7jcFI6| zwvzq`Ji<=`oq<bDXG#!5H` zIqrcWz#yU=ZuBy^B_1wBWgP>=`!Zi{|LVJ1n5 zLp-qu7)&sFeGr&;pLk375e(*I=AiQ^4a$dcF^?p_0GSDc=dy#;Pnrf?CTF^kc9`={ z`~q|_B#ds9gE;x33CPA&226fwayy|j1_tXY#5~-vFl5M7i9TLQt>v}p$44b6Xe`Kd zf+u-ONx|7lr<$HJG z>-}@Z~JaPs`hXyq|fK<86!Pp z#AE81A5=PYP;8}*49`80>64I@I3D;!7zQCA=x$ksJ`=ivNY3a>bvJGo9=NGbwB z?x7%pI-~%Zh&k?=yZMB%BPrXrSVICCOwrj4$5jiU1eYLT0VA>i0l&#ie3YDdElk5X z+7NT#OL9;r897jpaA&#zVYx7bfrBUJgN))3=^01{B)DSh8)=O_RZvB^NR zcgkrz$Dr(&s5A3Lg@k;^p6eHgEBG8ctJRn^?5-a^l$=&?k-m?V zbp0ytRi{#|qdCPEHPs1l5Ddos;Qbe;aNegv_k%2~XuSBg)SSly)EBsdJCAvJRXf`D zwCdBhsOn+uXm|zz17Loob(}F?>rboB>u1w-A>qBX8Hjdc<}$qB)VQ8pDletcucS&V zpC)s(pz8e6cb(Pu6{uNiG_7mZV=Gh=&`u9c{zvGtoa%QIQk7RWcLSp4IFmzlYCa`8 zk1ub)_Y!>Iub9%f$!*PPp1ww|{2TpV&DV5V+AnJhe~Knnuv#}r(e4deoX@D^7>6sF@pZuqkC#+m2(Y&%h%paJ!8 zTMb40yv~^ATAmQ7P3=cqG?;J@dKe^qz+2m!VRF~C&kYCazJoH`wkOtl1H2g|yR%!N z+&1@6Wj9G^$sN|t`OOua1OX-h^+}`Yv~>HSTf{CqXQIZ`pZ;;bMKNi}?8>L?HC6<>Ek)}v>mZd>ns zz#nzyt+}k{v!1O-a_uhJWHO~jxVf)zB+tbeUX*bsoOT%ZxPt?3&P4z&w z6-kb77x;hFotGtST5HoLNah=Fx)rV~R|CeYQlJ}iyqNl|?i#fA&LGpRON#|-hBKHP zwq3Ye!aI98n{sDetm;Ea6sW%IRey;{RDo8TOJqzR-N-*Rz+BYjdy85~F2E;0HR&2= zscooTY4+4_bw<>?uIestwNHiwL(jXJ&ODc!Rj4_+_YOY3YH>QW$29x|WPXUm`6-wY z*-yR~N-}HYKSbfsWKWWB{%Z>Q?yb@1xpAoB?zG49 zRgW-%Z!q6(sW`{NDM{I~X1S=-rASi0uG6!c*X*(HFYa$%J*wiZZbp#KXHO_Q$Jt>D zWrvvTj!T?+bW);IcFQV9y&Do0)U^wmEpu6OS*$JOAXG6Oqq4?#QJF6N6kvc!+hCK8 zjXG9T;#%i8$v$OPDu+5C$mtnZhK=3w3p-3tw&g~YV)9F?g*OjOixX7yG{3o~&n?$cpkL&>LDvegqt8qYr zPw$+0WFJib0C{pbGop?_K{y2mdK~)Yc6u9_*YNXQrLwVSk8EV0-@Ni~=?DACG+K;n zxO{%iZNU`l+ZiYK@Aot6ApZb;PWFo0axyvto|_=jI`!Bt!$EZ?yN8-|#@ruft!#7s zJx_Dq^}+t~T^g+ie!>3$qgz})#aXPSsy*eR0dbfehh$qk z!xNsXuO1Y>E$u#ozxv1 z%@`a$IL0qN?kN zt~^5W)}CX{G2behd_!>K9IOlSxkn@%qMr}5>SGD*;Jfh>r!muAP9w#4%i#Y2pOX2} z$F6(sz1xUx>RvL>#R3Ib01ol|mzQwHgv;hO;_9PKHkjmT$v%CT+Ij;}(%IwdBK^ua zl0zv0=bW!m{5WW>q{EC8{Lk`Uf5jn|`pw-cZRMuN02si0vLn>3#Tu0w#sf@{0L-tF&0f@HeEc4b+cmnarD0|(Ql{s*ujUMHMe^YKCTDp+ZCD${*4?H3gJU%u4OH=%*_Tnj`yy8G`$ zhTSX^JUw%~jQ20XHqQq^!S>3(?$ce1glxF)!V&)fsm{(lvaWmkl>Y#l?1&@I7gkTb zq2m6{6u1~6SKbw-N#0d&-*AYr_9Nr3*&%_kpET}?!bflUEm4~*a3`)3yF2U=aRNvZ zP?*W<(P_~F9k7F^Zi%F&$V`!41FWiQe9Ew#fl}xo@`x(e$4!-4oGjT*nAfBH)~FyyD&elO zUB2tOyv?;p>{knUg6`5+HdJzIs^V*^{3ky(j^Gac2St%oVPm+FI14rSCJfrMPVlT5 zUbQmnFNZnP01W9Z9t5*Jz;8NPt@`#uq!pHinaF)~NcAsZ%a4beRzu z^vB6=>x$Jb-g#gXAeE}(qUO<~&Sxj`OC05CUk3Q(?MBXF&K%HEHOD6epz{-zCVC%L z)qEYBPOm}t_%mH@NM&jjKiUobR+{n!l_Ceot;Y>Z_)~~kGFsX$`4@rw&&g@7J?%Y_ z(44~G2Qh!hC(*B-hkwl$#^WC}hH)9}qh@@UQd}3&WC73~#!#H@GN2}V^+Pl0weTcD zcJJ8?Kp=e4Gl&?Jr1U$K41|DG4uk$EMrXQ`de6xe06eFCrcpeiLyCuDBkYGyGrfVfJSnafC+)zqSMoKu^19RL`Wk72g`I==sj?xM&?e^Pym7fQQmlz z2r~`NJd`FplO2b2vIaN#CKeeR#31RIl@2E*7h@!qF1!r`XaEqV0E3c-XB~Se0iNl< z$gDYn2U99ec0=9+8BTM)5nv?yyL{1WxoQI*d!x5S2J!S##2wsE1a3BtvZ3bdGNGtR{tyRG)edtoJ7qs`t*AYF6#ye){wNBL z0%yrEsPW5^Lw*q{JdAXJX}Dyt!P#eNPBgAZFd?qolCWylsOLl8={?YE$b8BAte#dU z6wg+-yRxB4rPWv`0M6>iYT9)gUM)+J$T~!o#Ja1!f{WWEg9{CmE-$Kn^f|PK!EkCN zv7^=W?IbheZkv_S>5byuD_gUtZ9(p~@C%7`cxZIM#(b9^HG0>2h_khzpW9@ip#OCswOj>Ka)7(NAh$ z2FCi@<4$#3TftGadHH~NApF9vj7V9WPj_*3s=BS*nw+2#Oq>OUM2t%5X(Xul?Ne@V zbGB`)wgr1n;dM@a9(XbC)^LxIE5$61LiP{Bdb6fp)1QX0ch#f$eUgV~7Lrw|NbdlX zh)gC9FmjW86S}dj6S-Z7x&S9T1nmQHnNUOwq70k`NGO;XL;(o(1AkNmo`(vA5(WWG zz#3M+w4buUzKmswjPBJl`=rF^% zWI~%9CD>5iQvv1_+)9A?V5MUtVskQ_Y*N8Ro_ivKgRtmIBXI+D32xt5M0rH{scyy) zCDKZcIm1z&UHYI5l0L|>4Id(-XE2}xs*~7CLqQwi6O)jE5;l)DJ{|B79h&z$-hm$^ zTOC1x-4NeT=A)WP&!W`Mtb#W`B&Xsqj>LfpM_E5K0OTCO%20Fxz0 z9PEHaZgC(C%2(iECx59@Anl&;n3p0oqI17#LB?hi5=o4Dl&-{t0aTu08+T0Bh%@Sp zlF%`Y(vu&_Fcgu@B=6A&8F3l&QZ8APRp0i0?jU-fvh4VJ{M_ln+Va0S zV@HJJ)JD_!FAw!nl+;{*x^?((<*xq_YuggzGL#m5v&T+PN zQUUj82cqR}_=59I*56XV{2^3({pK5XD~euPx2}; zJ=0LGO>tW6=JY^4sobDt-Mcicg?j!Q$Rp;p{51C0rs~a@4nLGBte5yCuoB{WFMBnq zCHOwS(OQO%Q(q>^(y6+xDN_P+N$dj0x}+Ucmwk}n{{Rt;x_wzLjWOW&j-CQhAz2I&8Bv z?iijKM&=vf+6>}XXK80XX?rA}kaSmv}0Y&oT0 zrSU(r*;(4$QgvZKymN`VRcOBm0t&;oi|Rl)%}*8jd}&P-Z{FbaOh|5`JMDK-SW5> zF{X_-mkwjQM@$dqw>(dATFKV>bsL+8TYDY7N|Z%c7P_P9wazgyxm@VTkX@A!;Bs7- z9rlF7Fifg*6Ym6CGG)YLw5_)hMZCF?Q6xAHnCPk^S|P>~nJ`0$K8RAXc9%gW0Fr~S z3`ccbnE0Fu1WZYB0W~1o!5n$~o7p}9jic<9q;nH6N1n=H33lBALl^)^J2Z6ei-|cn z>=KD_l064y3!2ag+w(vsG1%wl-EBBly$T#br`5f4TWrdp)FfavYL5Q){Q}=;wpv|% z6H&RoyLB77Ej|SRb5O=TWaAqspDC4&lQ+6W)|+0gk3^aw$5gnush@>}JCJz-c@_E% zTPIv_zlvzJj?}Lz)d%=;d1QZA;QOuvoCyPdrcqBeKvhG`o6yIBV6VHu`nRtQ_Ntt|A)Y(;VAx zD^ahS14QAPR6ZuyJ=F}!=X3H8p$G1{=K}zYjlq>0Q=^G6g{O*@ku0)mgHfhrU?SRX z+-$C>@lZBe8bd%a*Uc>oFRNLU=&-)C$YUEn5heiot=gn!n|vK-0Pvf&+ON2-T~5Z6 zxm@5f_ixHRU01BwaNdh&Z{gF^ElBEJNIajJl9Pn(t?V?>rrNl^rI8e?yOfS z((Gf-*`LV=XqsOrAF}lJ_9EGt`!4_m@w!KvoU=^2dgV8yjb7q?`#t&ECXiY~^s4}N zpJibE6;*UCFg4y5m8FW1LrJuzW919LT~*U9e0dijD%2#B@%`j});~Y=E8TZ=d+k0R z(+?=ArfI~60N^a?fL+w>JI*>SR~BjdE}NiV+zmRyjaE@`rBGVakobmxBRPyO7IkX4 z9wUFetF2O_+b($IZvB-0+CL>Mt(Ci&`_Ut=H&Bmvx1VIqcy`gWMxM9sJgH zl`d`_(U>>(8*v?vgw@1Yadj*0PTtO)C$!h9+F{(nGGbwVO_DjX<;m^zne}+#DM2)E z^{q06EjZL@AhGQ&GDsPZ)p!k^?R_@mo|B76+z?>-74)AU)2By<%RSm{<)+zld4!jM zBz4O9uT`e%T7}IjfxOJ9<`B^hkVr5-tLwi^NzVk6`8J;$C>RtAS%t<)J!q?VU+71C<9_O}{)#lebT7ZMCaby}Sx zNugtY=oK2^>34}Zk5v(#>{8*4Qf9aDc(mGSQLueM^#l1YHwWGm1g}N>TX?HurMj6M zzCBjp%)=QUnfk9fVUxIDpn4K(Neet3dtT!`!|;Uv0P1_7XWHr?_E@_oU}Sxl8Tdj& zxIyEnyr@6ern^T~3521z(Ti3!XM_48G@$;4qX zc@e}!s!5Xv3fBxt-B#h`mx0k+yU)#3GUBapj1sGS4*viom?l@sXFjPPjD+Sp{SrXL zbXmyJ7`Kurymf=J;jaMacdG90qr!MD{uB8w8t{AWT)9p?S7_DL6EdiDj6r~}sAgbg zV?33YB*u)pT|8E|H7XF+6Uq;o#ntr7jUv4!T!7P6yzKzaHd`^NQ>CK{A)r(a4-O`M zLg)1hyPZPo#k#2HI&mieZv(JUo4aRngmzjow91X8dr9taYe6!8MRqjbUbVYiS{pIT zJwoB_X*R9RI&Rs{69sxDI)A%h*anPzmlm$KD>yYOja6#G&Fraj%i+#Q`!0KmH3r?9 zRp?uW(I5zLkN757QK=J0eNv%^x2N3}5_%z)e2o5JdWvr|R z?ADNT%y0*q&*1+6{exWtt+PaT$^J_Quy3#s*%0IBD@;8<qGuaoIUI~867$$Cj=Nn*KN=yNj+Ap@&pG4KI(KMr+%o&<@7_Ea{vOE z0ogb|BnkIOB@@fbv~*LZ(I@~7vV9Q%IEgserT+ATx?nK`?YaVjN4`c(?2hITFe4~V zL=PlBfKWzvOe2twxl!L3$JIlD8=wg6$8<|^I6g{xc2JXu>AD0+CM1HO1d>Ebl15|E zLO)Zw7{YMenS_Ak?wjE=z6gCQ#bku8b!LnnAjFw@JoR8GJHgqs}_ zBh5#iXcMwvM#vBd>nSe8odCd-(DqIuV`6qd4Ub-^zzruUmDsNMkI_nd=c)wr5F6^0 z49PhQ27#Skkp|7OWCv&H7Rs1|(hAdQ51l@#80CYME_DY9rAAFdk45I{;N>KJSLyvs zF=3LQ$+OfcI`WsZ(5C9O7@t*L1D9wwc2acywGS|3WGV~24df~nDK-)L?( z)ZH};n&ZupgCcwSEj`WcYL|uZO-H|&9_1KmMDe{}CQU}6&Pu=dH#M&7+}zZubn1_+ z^UUYENhI!Mtg0@PpSsy=4bs`CSiZ)sXI)fj_KI~zX}CnjHYe3mP)U{{XC6C-@!th2++GA$oV=HC|mp`kdkP8tC+Q_^5|x4jwfTZ1o&Yd!`3H z#>%i{0SdxHU;r0s^c{3q(i%Mbpa=(f^+AA;dA_NL0|)Gy3$h$c;GzgFkPIMW&mGik z6Dq{VX&V6w$pmFple)3L+BiTEm^cZ6CvT#KppB&faUD|tvND)tfDjWI-4J6WA&Sce z!drzlFh0l+T&9?T2@D|45ttZ)m8qS$7y%#%1}C~>*Q!uD1aZ>{D4V!Ku*m{CBoGGq zMsOxlU=U=_R6!ikgal8LAP-c4Gt(t7+HjPR8ldTnqp)oZ*AwWQ2YuCjh%lH4-V&i0 zAoLUJqrPPWgNRVfWUWZBVjyl&$m~>OW@0DJ2U8A-!3PLqOXQR9Y%q*t5%NSf3>=_y zut+Eesf>iCOy)-Rm3>+eG@>?r2M~BH`{a3KxDujREd!w1jRx~1PI%*1;=pN zGZ3u6!1XCf8=}Atejwn&NOQ750Wow=GJKMUe_JIm1m_zlOtxYWCv0ROWzWvY4o97g z4^#;zAr}G;ck@aIY?QkOsC|Y`>ee50K$TE#6^%IaN%T=;W)~Cq)my^+Ke}wet6q&i zUw@na722?qnAv&X)g|% z@wBzi0FR+v{;Q$U(3oi3x$bCrDk_&^m)zzBH8l5dV z*TZ~{NrM~q3m=ZI=;~ZMZpSiP$i|8Enft7rPeWzpV)xX0LEWQefyIocPSTaFJ=zE5 z3F6xeys(eRfn`u^0D~yhW ztkylQ(dgyZtjldtZ3dB1q=URA;#R)39WI?Vyoz?UDmw@Gs=d?c5VI)(%*OS}Fte(L z8>%+0b6HyFLhvbb7##0$F+Vc9w3lbidgQJVM;&QvUZI*)sysN5Ia*A{{{Wa*n|iD+ zCX>f59U?h>S7D%1Hrk&L>Czoq(xt_FmbW~i;jq_#b0g7XXcg$u&~*x$sBx!W@|CS( zRDmx(+#Lx76(IgM-~|VQ)v6 z4~qrd-#%r|2F^N_tYv(Vqg)*i9v6-$yDAKh>d3ev3gac47x$x14T#PBGRT z=?xc7VDxM>eoIW|gUUey4g#1jr3zA8g5I6FS*h;Mf`R*K0RoNfmG zXmjEqpPtGF#_kzYltvr^e1#-J{QdVtK5-7ungNn#LWQ6i4gjIwLg0R2M7RSR0Ee5S z$aX=ki84KVCUkU(=^;Byl6&Yg88e=p(wB0Xc3gTC+@?8(UALk6Q*(w6-8+TfeiZ1&^I3KER)a>!a}PR) z;ra50b}GJ+Pvnx!i9|_-=zFVWN7k znrl$2Z#1d`%CyIZXLgZ=^v^7`gNZAEJs5 zv~2iF>q6D^sz%<%$W)}p(|s4vVRJ|kCmEgPZw8sCVl?|7{{VJoKFh!~%|ho#zwxc! z>UEmvBffSadv6DE6~7Qzb5a49G~iXEIc)VE$C|v@Qu?FNLRZ5!FVeL0{{ZIuGC%#D zpUGD3D^EZBg_q9VpUG^CT>}f3{6y8xjl-7}Sn|u8kK9r296^_Sg#6)S6}iz)l01)# z?=5fmis|-rsZ+M5?*^Epz{Zf=69XACeIa-M0EgT?YePb9)$N_NdyB8$4<3_hax-Ru z%nV2q^ImVmIziU;i+d)|hVl(a{u*QBKTJ<@7tj$+tqrG1tzZz=fq(|VbmwX@P((w$ z#;W4WNw>CrYn?YtZ8~i(arR1Z0N?mmf@+T!m$wz!O_ZBfqn;_XfuW%E0AmZ)e-<@% z(9x;s)Q=@=r*k_yQ)$TgnEDmuC4x*lDe3W~w@^-Y>cYTSB<0v1NotpvRg@ zp%8a$1@m*>edizv`>WSWq7?xI%4 zk_$nmI+qmm{{VEuQO%M8fH1ueSh3O>t??d_ZKhD5G}~JB>0B_pRC%TW_oc)^p21+& zr(r{dZZ7qW8p_tS#TPcgluc1uqliDeAiRLV+>D-!lO}(sqj+QGa(eX^)cuL2vYUZ( zZ0HVe{+n{SeK&z@taVBk)T-4rXw+>+w77D}GCFxKwWq++wEc({v`7B{i)zLfjV>Xe zxQ*d{Nb^n^Cb7D-B)!L&lhl>;w-vF} zbRg=23!hE?nO`$L9d6S;>_}f@`d^F^{!b@g_p1@4x@1Y}7ozYrjSHhrfu)*2RVYE5=W>gk3}?-9YB4#q&V0DN#;Kdi z^r$|wo=E0DHL2nwLY1>ARD3wNd!S}K)m8QORiNU}5wD2lEg4!nlUnYjO4ZE0=3HEI za1o0fllqItfu@|9P2=f0oi#>!wWVwb9UU zXd#-zL=gKIhkZx$D-qbSdF#xxxE`B@<@0&l@7UGY>}9aE{J_~&!Jcm3L2HAif!7AC z;$~Ed^C;!V2}in5MbfLmU&&-+(Ec| zs017oNaiw5$YOU-q$a{SvQ(IMa1wzEW!(} zLYkI!>C6HpW?I^QZL+=PH(LW1c$;1w6 zC*PppKCM5Z^P5s93%mR|s#(O=qC~l`Z~G_tDP5jSew@*NliC^Qgwq>kQXM{twvc}7 zwIk@eE^PAjg*mb}C08PI*)Tl7ZwinWWZ^LQbA#PhOdMybIolD~E1_KtBLEMgcAdZn z=BwW)0a()}X9%Hr4>6Jir(o(rQ0(vaQb0KSqq2);Fh`;tF%pzc>4$N@WQ6G!Fc0F& zJ$8hkljN8jd7wBC&+MCSh(X;J;j&Ks5HvW)bfC$CaG*Jye9?CM_u!+fP-LK1gV8Ikl;-gihux6cI(Ng-Sy7#Jy!9sgap49>cbfz zbwigVxRePZzOXRVBo^m%_(1j92z5dZv-zcVNCO^e%m&~p;!bDLF(Nnsb25}-ViBlk zEy5hIB{B12NjZ}ci+jdTeu(C9e9$f>!5xdH9n2G-RNVZ4hTx=bPF;?uhY9mYujFAP z%{yFU=k!%?zg~*g;CsYC=#Un+Tt8LipZY=74AAsyWBBfBzkZEiAMvkK2bcjlo{Pu7 z^oFP>L8IBX6+ZOG=!`$wM9)&|8$8cTxYTcLDe1OUnN_UjcwuNI(JnB1;FZ^Dx0e<+ zZKZP3+EqQ{OzsbLsQe^%Cl^yLFwt*bCmA%zADLU;Fr@EkbE#7<#>1&!EBcEvdbNT} zhso*sY#YR-7E8)>?&${5uTh3%a=eN4Tw`?mohMqSQqrU8(s7E7=m+W+(?O?o?X}-k zkEYi)8V_~6scYgh-@pTP=i|)qeF@x7nJ#ViWzjU9MUHchMEpye{8!WJTeW6=(>gH&-)ZAgL{`+(HS@zBciS(8Q(CFiv z70CIGW~(L~l><>vOS=Wc(f|<>9^dAX^dDks3zk&FB#_w=w8$%QZFb9w-W4!OB2`zn zCWZ5a2UB%0*Rk6WcTYd0LNT~LP4IgDr*c^lg=KQb22^k$I2Oqf{{WiJ*S}?7*0!xu z{puDK7U27z9S%VR@LaEK;U=I<0iIaf)cZ-iDP$&U}9pybkh&3C^NB33kA5pW66DwKG z%IMFh>Xf*FJp`Qhl?OARe-n?)g^|IXZL@ z@iCPHfq*CIoum=aE&l)s+KSJ=ds^33r$M}h?G6nttxcueyK0$g7_M73~R-s|2GUb%O4yjX3qRh`hl1H8vx}Oie+T_=zaY^+(;}s}1&GYw6 zkFw=jxOQ&z^13+uj^y-^kcSV7MrC!^qN6n59o`4DfJvfs+AMD%>Krrn`K>n-fkTKj zDpZ)-bg5j_rq+xA1Bd`EvE`0VQIz1|=)t1*QW44=4)S(Fq;m8{kLDBK1QZ|e9CcbK zDBFpgr6$#p`845J<`{q@7+Ve+wq@@S=~Qbso{dYYOsG4wxKG+wp=!K6f2HZVRo&H1 zE}cz6myXM3>9onGOzkcsCJ0#N)W-u#UQ3P%WmbbE@ATP-cqDTa~FRoYz_HKH+q&}LLEWEUoq($C5`Dvcy| zRT5x=Px4(RzfrduewA`)P@{2DonIBSX*QTKP_z;nB4MX1Lr@s`uBg50&UJBnO6rbh zf&>T{-3qNZnZRDJ!yFjRQ%9wA%R3Ja+QGMXb?!x17jx9816msJJ{oI5#2R(Kg4R&9 zw4fY)+O*qKoJUN&kFw&Hvm9jN{2g%9PD_Mv^g$H>$q@iG^4q}W>9TGDOCoojab zl3P*csCN4vT7XB%8|!NiWY;LaOR~4QwXG#I3e#NXC9rE$5#nDL{9{w zn`Xc8?@pzu@l63;%oHuZ&R!hgIk`yXAKoM67rQd%mn@2u&Ij=h>*fcNU$!T=RK_xS zKsh@r$%SNg*$R7O!akuFG@!%TJm1y`8uYg3XJ6u9F9IfcTLrkl-4Nl~qI|aXQTs^z zhq~8;)xQ<=T{@Mp)orR$rF7>sRHg`;vyNapjn9(je+sL;ylG`hgpV1OslWXZNzeS+ z7Z%9`_-p|65WQ#cvxaW0>kVH}p!zR$GTU}Jq&Np|=-gsQp(>nK9Bo*Wa+T5PZt)>_ zm*S?jH4Y+mkeAZ1nn3rd{Ac7qA0oX+i#1m18U@|uM-W=xT4H`DlK=(a*3_!%I;AUm zN1o#5sAwl7mmHtDD=joRa()MrUJ>a(gt|ecr`Oes16^F2G>QKJyJ^qW!~K=tyTk+d zKpT_MYc$&?>2#e=jasJEQ>MyPoB$MB#=!zR?q{mxcTKZC!g_#w%u!9xVy`#&xcF>2H<^ierrZwPD$iey~0A6F|3dcO{nljucyCIt8-1J z-qMSz)Tq-ehRlYZr^$1CrTp(K_gV{}43HG5#$7VpUz4KLIKJ~xr+R8y*if%{(#wmA zG)Sver*KIp2ddTUTD8`zeRX|$@NO!CLtF5$0fvFl0kB(}z74*&6~MT%t9Nft#eKCZ z(^0ME4xU)d13Rr_1Dd`Dt_H@%8-82@eEwVWKh$?>J1}xAt2s8Sa)i)TnS8JV$Q0 zW;BYW&UCPkhBjWKz)UXEniw`73=Tg)SrOSWKpKu7kWBFq{ zt%a>x-pfS2f=dLM-wT)aj~1;_UO}erQJIumP~dek5~)jGd;M}DD={&V`>$u zojT!^#68s98bOFKV5|)`_+4E&=7VUwU^ZkE(5iJynv|&?ptv;p6@{W#Z|x>90s(L( zJa5~@C0Amrc4B@fKCN9hN_E9A3EtOs)`^~A8$;gt$@(q@rXw=FH}P~g_I8g@2`s5u zzMR3I_*a>$rH6JB8Pg{cNmXVkted3b#l8YUcsa~ndbgZ{I=tDX$!smw6h*zU8r zi(Mme)4DIF9N!X<$MH9^b;;EXjV-gi)W$f^)*`ct!>Be zO&XPEe}#-Lci0q7(za;@ZjBS!0wdk-t8jox%Fknpw};G-3?LQcxh0KO|85422yekPbZ10K0q+iJQ3Q6Wt(ujL`=fK#+emClk>F zB19k^RSF24gk1(U^$5v`0QxD{KSa=wK*`9Sszl?cPzjLW6cZT}lyo$JWH5eY`Xb3=fg6OprhER0Pyx;j>Ga(rNgKzWQK(E$~-OSbnICaO`3r28%CBANXNc`8GtCb{s{(*&N$vC!R zGf$;P(x)p!UK=tum7#sBt3Q#Xo@21e>Tc`Pqi*hUJ>das?lk+mBTloJY0g?s#dF}d z79XfRjXr3{66U!!bQ^JDq#I8W!ik3#yhN-@WDJt4t$kYSm&P*SAfGhU&%K#l32jt- zhN6Dttk_$-B0g)g;Ae*MCY*X2oWD>);;r$~bzCn~!`8I-Z5IstxDVAz?DFzDn*L9B zVFM!y%(=PGnxtir6E;i$yH`ii_$)hnqw1cRoCKWGak6k@qO>uhAoF1{KK4GS9D(kM zOx*4aszM+cn9oGahR-mbs6oNmFdW1ZQDPW+CU=m^f*?jv4wEGV7;IrPTN|i8FC_^j z+aL=80WiQMD#V@PF=>GkQU-$D;zU1AlL@5!lH?fb$Jnn zsjY`KXR1UvfQd0+ewnR|a0Ab?n-8W-{6S z)bM;dp!=*aK#}VoMWWESj-`6;vc0B7(2&!>7-?{{XawR@7*QyO-5hf2KB#%Py&j zuUVH=oZ5|X4R3Yb*zp5s z>b7hwC-;Dmdkp@IkNiXMuXPre03O!JJ$kG3_%)qwTn{f1v|(vrzl+*u6m28W_%ZpF zPZl}m)~jjBd2k-+%DadE0Q>b?TgopS z6mHBOUw12=QLcSA@|L%qr@L^q6{yyAQ_d>A`IkXIb(wnB=RCU;KP7{y>-zqkc~;uCqjBR?FhL-UfM$INKQ+#qH{j4YggLEties$7@Vry;L<8c3eT7TbsQyUV?H(%R~`iEj6P^@WYdf_r0ST+lvU z9K@bnj-RS7Y#w&Ff*tir(hCPkXu@YfX^fdYP~9O#J1%RS@y$`hxb8|M;sJoV{U3wv zFLZ-SHIgc`!Kg!TGMEg0%ss zI_|wXv|Dt?TfU2G)D*+{Yc=j1#~BQdn&p8Lx>}WaIZHt@V+(0hRLtXP+1g&-U)9tq z-kY03>Y05u-%|N!nQ82GO-kmwsa3kB90c<@>|;iAl3=Yke8zF+w|pg_e%-B93c-$J zngBn<4E;9yu36*91-bNZj&q;W6SdX2lJfh3=Rd8>bB0%b%zdNrrwdf_%A=AYFsa_RA4`{FU*dX z&m;0%thnN~?nWNm+ucX1@V>cyr)X5IEwsz4-9G;SO1Gy{tqMi(=J=t_ByERr)R0t<*V;ixwb5(#u0{scX2V%X#W7oy?6*ZMxq)vBHE|&QT#owoI6a^V|?3G z{!5NnFiKmTk=C;2ilmN@gf45-sit1l(zPu{jT)TwY16iGrucTj!hU$}vUtMc?@+qp zJ!Ynjaim*Z*wCOURHo)P#P+IAJhBAKMEr{3E^RNY%v?~fLEQyPZfX0hN7QizrN%n$ zw8y2yrM@hTndd0&k}9Y`sB`8M5({TN_Sqn7XW|hZl7|)p<n1_9~<4F%2t zzVV3lLzn}NyY@nNk+$*qrcI36g|OzHS2rQuLd^ZGs5(Kuz9Vuyq40j|UgAI`WOweA za*+p?JqibWS(kBlb8;Kw)vsHZZxW$&dQQrkRUJ;FT)_6drbEGk3PMB-G`rBH_eR0L zLX)ODGcGUg?i$uySFcsA$PcJp`K&mY_k9eYK-_Z1`xLU!bIY9dL+CB}7&dvOhE>+{eah&2+GTzMII;R|`08{!E9SunDIE#wx>T5d1O(RXD!+TlHse8=g`m~Kn zBsBay&t+r?K_9yz_gx4B@h;6o}Jt`6mBB1CeCCw%!YHv^TsFazp|06IO% z>$=6`iF?`JYWzt`uPT4~xveJ@3OYTVsY zn3W9zcD7Krg0Tj?V!hvD7_W|OYlYC__rM^Pk(I0)E}$gj$= zEkE@hXU{Z!JREk@I$l4d$~rw7Y!EzJe_(&be8Zn)qT)G2095e@lPuoBWz*!aU_El4cc> z%G@#h(n;<%+7lm4B#gpj^dTSvvPTjm0k>4{`y_Cgi9p#~vT!loRz^>xD;yjKBeJR3 z26}m4G0YtePMKwX?B=%fBrXsWEcU{F_dI325E+tvtJs9p1d-jDG^GvzIL0P(GdUpDS8nxOD zHj^+WBoGXBg9qfS)-GvRp>EF`+HP82UvUI;gB+thp#4=S+Ouds2EEk7+s@BY-pjAx zdbZka-HpX|G*Gyqbw=j1r}tsaENCAi_FQ8eSb2en^Ic^PMwPyd*wiiQIys@CxCf?s z%EL68;dwjsnDl&x_5~gg{rLBs6=c?`>e|M?W?L%dv82m zT8$zAjn!9`DT}JVx*r+-Bb==xHrFAR6)p{3Lz5Vd{L&Ezq>Zw&DpsThX!ZWFRrvIe z{fHiX%QlGQ-~Jw%;>#9*33rIl4|2yU={=quX7Z2=pwC5b0^BrlZkp!O0EfB>$q>gC zL1}Gkwh@5y^2u7^@>E;f!J>GX2iFM_%qf`@+Hc1Hs-j>-TDlNy*Sm>>T9)d*q@F zk=v?)qGVtp2o8b(KSYBaW_tu`d!U`qsv*(L0FNb%_;z28O}2pex^DSf3i^X6IlICpIp>@^Aob+)wGuZ2W;~62;w}_hjV!Z-M~OG+a8I!!6rP_Lur{k6M_U!(E)TEPE!oQJ0RgALT2_z zJd6e*HcDI#w?&c$cS8WTf0|?EvQY&X>Ig~pF`9AYrf11cgmple0UZSfVD(HJgCziZ zNcDvWQc979y{GcbrGm0L`Vz*bD>^+!T5N$f^YXu-jhc}5D3 zfJXiDv?htm*Ci92WkP3t(A*pnJ0vi$=L$IIB0)qL+(J{d3?w8H><-CfktP6AVjy83 z$rLmO{3dqpqqfFEgF7RLl1V@bf+ujH9`E=jcftTmxsJ&U5CKbxz&S(%I5L#ZK-~yH zapg*1CLuQ85fivTlH)&g(2-kOVqM>Il#vEx=em56C00O}5Ha042B`r3Wdh=6WCbt= z`JK|2H*C)7iEu+0Q2V%lAr(pm~Xb-P*MwLui01$gR-m$ zIE*K39p?&KDKf@mf2=Q^KO5>AzlayD216;(X5aiUf8t+8v~&yRU&X4$--vq4gdR(V zHV5`;uzrhOKlY#Gb85cSzb40opK-z(t;U_HGMryAq`i*j4do^e)x;mwXYm%2+Se`> zfor+D!shgrYTCUzcaEh^_Kc`#8h}Azj%X%H*vjYzqFx0n*Yi1}lsU)qUlZ$4-K+N@ zTfCm;id!+$DWPdYMzVEx%{m0ZdB~NP>a_0dzjp9xxL}TYnwP`p49ZIB${=r@?#F|^Ic$Kv2RCDMGt%7&lZn`}w@6S@~ zoXVnZIAy%ruDMsJEv7!F`7B*;QuYV)xz2)i3p0faXIZLub;CsYd;b97vGsis+Np1g zR57ArGQUp3rwuyx`#w`MgVg^3QSfGP<;lObeZeGx?omAwT~!UQTgk;{Xchxbuok7< z{`|_*ToU@OcHzXC?<+~%lHJ*o+wMujqal^NNMpZxa%hoQbcK4mkljk}8SDmS)B#%Q+(>uLV_fFN@ zR*P>LZUxtk&i0t74JvFq1F>oPtsfCwvin-Qxu6NA=K$Vrp{;9wMX9&yHXe%6^UCLn zNY=!A+|EG8Vsf@TJmTGF#3npC6rkRJT|Vih z{yk?tl|XiuhM6BHEfr#7N_$(X)jTRMaWttk@-{Y@m3So6q%6yWFulUF8XF+)dn@U* zxPd#BK*mG@y6?lA8W)^JYHr%oZ%(x$@OC`U13ppAS9GS~7bA`_-6a!!r05hh$~5X+ za#p&jOc%Aykwl(@xdVS+CBHhA^fjXH;5DrcXcY(m4S*AXv7YBtd!GLQjoEjqt?ZYy zoUcFb`lZV@a{lV*$v3xd9`(gz>heITMTT4+y^-VJJHm4`bz z1JP*fXtovZA427Zo7_2@R+tkWoqX2ir5oF4J58y20Rn^C*^SGmA3#sZMpx~v=)6hF zTTeXV4GzAOTvDzYTJgOhnEMrv^YX^X#js@i?8NsK8{x*65bJjzONsuyi0~ zkBQs=09YVPxprsJB_M;dbL5#JX(D(2@{;KpG5(TReBd+70SC463^4qXd=85VEyyR= zoFHZZ%=hP25b#TgU}zb_Q2Y{014e!yqD57gkYjfV{?J=q5}@0jD$!_GXFVFX)EQB( zAcE6TvJc8ul1MoX$>rN}vUJ<}+IFLBd1~OxniX48les~Mw1PhBUsM%Gt5dU~ajDax ztx(cv^J=!Nv~9P99QtOs4r{{?!^yug#NnPDCsfs{YF15cwR@X(+KP?$d0%|So>9

6Hb;2@?(u=AS%`mge7!b#}Ssx`k5um8sl2oJbF2gPRky z7PkYSSUfW7EZ1^JQGfdm>qX+X>H2x0d2H5__dZ)p_~DL z<#B9t0@6-aPZU$q^%^?o>^hovhix^>DQT4Ha#8UEM=fuNkV`?pnOvTy#X6RwX>mfg zveWj5wxw?Z-L&0Mw8$=S5@6>&Q(W;rrKww2x2$cLE+AHYJ9bljXgKF^67QPi%Ysph zY~LKyX(Q;ZUEUSdLPUGUeyd&WY$MHOafL%h+f%B~iInR(pXmm;kGwA?;7w}#P~$rb zYntz=O~vKyG0~1AZ!j0-n6#75vSf?lS@iIQ#@XL-#W%A^qt7jItjukp^J)J8 z*=a{ch4km*j5f`XcB%-ECOi2rn8_Z^NKSc1eoO5?OfUIX{{Sb6u3uUh431^$ABObU z?J%{+a%0n(%gf+k<#v2F?M9#M;il5&X={x~lK^|?a=kxR$G=X}bYrGzWH)uJZ>?_X zwuL8DrstOfA$0nCDmK!aDmRpX0Sg}P@}0d#n#Ijw1{}uo66{7kt0vODSJ7+B0R)l` z(j>u;Me*4b-ZeQ@b@@Fu({y8N)-@_x^U!H^Mv!?&Ri@A>-5o`*txe1T#1lV~>2I#; zQL(L2wB_t$oEy- z@JXa-`jI~mi%dtD5B~rK=x-x!mkIbL0pW|cM)zgDu}!D-UFAs1>B;@0vax?=jA!*# zxK35BY)B_6w-_+fl+fQDl>Nd|caRmX5_9%dI7gb&Jejs39eOI9Je95!9!j@QQ}S9O zA9PYd+p0&5i3-Y{e>GE^oz)~m;H})#=N%Hp%Ex)6a5IbrRv{ed69yG$zRBQ_2Ix3O z;xK`yWmO~rACjrVGb;C)=_-c|N&JunB#1LGt8|^hx#DN+sdfniqJ}ieYRSMR7Z<97 z?KM4DX6QTMt~Xa7=T!*qxit;Z&8p@I(bo|oRx80-R~wo9mMfqfu1hk5DSUNR$H`jp z=Bhst3YRAFeA+L1*~v8I%hvnxwS4-ww|A-xNrLUI6X&67B_A1?L{`V*!SiZE#&)6QZ_CtZLMB9 z=Bvxz*1MnqYOdOSrl=%OF_oq}u1nQ8?zo!z(smmvxlegJk0p^(32}DgXjP7aaE5W& zyWobq{HoIkHe(t2Ly@wAC3I_{!G>b4ADPIf|bV-eo8xm1RTkoOLOAvOwRW>hNfgNpOy+k0G~o zFaQMn(b)A{LGTpVMDqe8vIcoX1tS7BLStwOs7=^2oysI(7ZK2(kWv625cEtr1kB@P zLBWo@N?8*dj*5EqC>mona!*c(^FcB9Nf0m(@>3+o(G(_{8=D>B4wKWWBw%Oklpd2k zB?){0$FT~340g%@5@ewnJx}I}9gJnYB?ccMf%HH)ljq$=nVH+FY0$bGn9c^?NP^4%D3G$7CcpCr5r#;iFU05j&(x(*U2I6z>g0~_Pf2tR;8$(*U3`ljXT z2>Ku!fPU$K0N#E`jlEPRGx{ly`3Zp?v61JLDTh=f(*S@Zl6oTc!1ulEbV|sL;CTg( zO7J!LGjz8GT`->OtoEx`lGpg2B$7aG2hLX?VT$56=(o23I;nU79-uBwMm06bt_^lw z6y8ivlRE0{j%OtfZ|zPNJ(a`lE~#8nj}nu^+sq_LGnI_c34_;U`w%g#sT_LU zer;^`=621-p{Y~2wlr<%8dTp`#(P};cyvZFve#0Xlz^Cy>FMXJLa9l<&IvO%f!Y@0 z&*9GSwDb%B7TU<|v@Fu(;Li9x_eOt-rP)*?;-bSJ>V>cXz%sO)D)Uo^^!$Sq8r*b% zw_+nJt{-Xi7W)%1NHc0$^tf0#3w$kh>VfrW1sO?cEuX*#qclk{}dANd%!d zh}{;?oF!vlfb0k4n8}C+QaVBGm`PJZ8z6vG=jqBoOVEN;n8+xg7zsJq7eGrcAZB`_ z1QU{ufW#zqLC!q0f|L^Igw1SBoU0@`If)1p894Mtca!o^Mnp;hp+-dhie^SJ-7v_3 ziRztAmt^%HDu2H63LzA6Ojc7 z>=9wett8~7CJ#y46AiZ6Kr(TZ!t6jgn4R}ZM3QIjoREKq9%)G@$p9n{Q9C6)os?W< z)A^-#M#+--Wn|0`H5@WyfOn9BK9skOi3YnNUT?3Hh8z4HE0T3+M7MXs6sQh$S+ zRIh3~pTlQT{{SIs(*>@n?Uf$poxn8dfj>j^E0@-2+=`AAX*uD<=2k|7T85j%6}_II z8hnP9(s+EdjLv!$zr757UTb34(>oc7Ujyn1;n&J*oyWhXcZDb5)Nthrw)%ZKb-@(| z@>SZ!0BM)gVKqB9s>9-)WA=S2Zm^{1RA3%3enByq`KQq}c54>Gr3OQX#L{xot1cXn zip=^lz0Qq(iAwb9Ppeo|r%Y2K9DK)R%4_vaZFq?puFkzAeYBgB>Bcxq0VqIm_}YqV7AGxgKMLTZq@8ucJY}p zj-aIByITr&7A?CJMb@RijK?^+g~gyUHpn^d5@=2%Ql(MbQx19m09B;^izJ*XRtq!NDRH4rqds@?<$!PO30U=-e zM((wNwRL0Q7Z>vKkBFHk)>RB=hSrW^=)}s&Ev(t0n~@PRwi+k`+gz#rOA;sjDwNy$ ztPI2tvfF4)=hC>Ck?`2C$NvD-Qc6pJg5=PjjGmMFD!fWf4Uz%QC1epUAWy`9C2hr( zgY^1O72ez8?|zplAdEo=WzU)ot4QA*my-z9AHw0kblx*eOWPyty1fg9#;QT>?92Ttv4>>PJgB?GgU9~00Z4QCRba*75qAvtz)9GrX17O zec%t&g1Ly;?6S1$r&--Lvq6oV2W9EF?u$nXmnoj6ZmHFAaODJ_d848JNr?oAFtk)^ zQPilHP_6|?^q(coUo%p-xqE5*Q^3gZ+(`Nb=E|Vyol%RQC!xTN#QLB}z@H`OkFBm( zaC@!w1Ax$4#{mF9+tF5EtHxP+JIvuqlzvKhTRj=T58}b;C^`W5PFJ0CYNSaw07q9J z=B3k?*k^x^T%7k(Ksh7HdFf`N;z{xM-<$kWK8@pv@eA|a$oF6mO(W3R zUA1m$+fn`X%|i^QPbJ6xdYmq)mXxk-=xFxUDcn<{{{R~1wG+(RwU4?3Pgb?E(0Z3R z_Fgk!+lp1dZ6^{;9Q6E`t!P*N&82c$xEq%ML2g@0MqFL|RU-gw*(WD%ZBAD@j82>w zqm4O8$JYEi4If)`d0fhLYPGq;h+!XcyglRxux>})cf5bE)2zXz*uAc%2z7V2Adqm- ze2z-s>S6TzcV4EG8^@2IC!L;t=1x2m5a(1sS?rzdif{pg&~8x%Kqn>%^+;NIxo0PL zF0PDj#Vj3K;p9*>x^lZ6VGt&HStGgtJEH{sQy-BXL}WvRbVSl0)pT_bh~_``WAaS| zGvJf1_R4)&Xjn8$LrB-FOuVZmgkxrGxI<7mw zYr!Y%oTE#E6X)tT8jlJ(+S5?0M^dx9eS?c`Y0>8nrw-&hfCvtOj;k4*JABOvn$1dF zmgci5G&|yXalr$$h?Tq5E_8|%_^Rg9P-VqkU5=+lgK4~S2Ul%J7ZM}*V;$A);_CXZ z58c{dzJa@S-K%J^v_oc?bGgWY0FQ7FQwz(^@_TIU#-qJa?K;&qRJ^R$9%hHQZ-bSA zZ*4}6R=%A$aLlZ(BC0Llab3?1WisbD_5w*On$mUaZBtOCWodEETA{##FfvclSLB-~ zEzS#r>6BYV--!1nu8!J|84o`=rseZ_Z~3(mISmo?Uq=2a(iXZegwu{^01s2)J$&k1 zYA`X*$Ph8needa?{jD#P#@Bu17><};o4}#Y(qX%X5BHwDv^cQxpGE0>3#n1Cp>Iy% z;iA1E%gwxpKl!fz09Z}wofzr9^DI~K>bzWFQ+FuJ>8!~1T4Z($U45ll+*U8B2y?Pw z#BJ!dmco-MFjH_WjAx?w+>=fzq|Z(A&2F(;rM)6zTH^=iwYmkzE~`qLTu6Jwli76F z^eEigKAlMABRN`K7fPW*{ja28hfZCvRUE6^k{oG1On=4V-(}H0NnU0?DPE8G zu~oILbkj_OPLrxI5N1hhN&JV^dCMkbFTC^=){Yim zJ_dR&5AZ<$0P3aaoQvll{{R-xW>4sZ z45}dG2dbpEaXyOYG>q(3M-$a)5*qmsm^*nYoJ8Y!R}moN0apEttyczp_EPShm2TL_ zqPY3>%0rSBAu}Du1`p&i1?NtaPue?^LLw|*>>F$XQxukKQ z;~87NC}5?<+PfYE3a$|zpUrKj?f(D<(03D#u41FW+?1aY&u)tAvCDB#4&_#GdakRq zY=~kqeR80CHU>|sPGh%EBncu&-CC1MfvX%jpbtK!66rb5nx)eKb?*qckU%n@x*J>M zwg+7HLGPD;2=W~jLBkzm@<*?#7D3m+V`2;$=#Mz)FjaXigVH`~G>~#2Orv}9DxstY z5O+{INSr0fI6jI=iTbDRItoRB!U2du&JtsEn?!<#z0H#|gnNusTGx!`eG>CDpL@3I zvw@MHBxLge<+5AY=?+MY%=bh)JI|s+yotfu5D)jI-Ea}Tjm4G+1JKH6J{o$3S21_G z8a6PYkr@z|xY)WZ@eIRfC>I`S`xFT+fYax)S$8f6(8_z*QW(ArYyx2L01B{XdVJ7u zGXr2H8`}n3&77iN%3?G2RTmQcjHbviWE0dUxr&qMVt?~cIGG)Ml-e*4%?D-Nz(QW* z1remQiQB(r1Bt+d4>SazIQ5D0OWlz6p@E%<-9YTf>DenVd(@1JN`T6WNh!Pm0Kos@>I@5@tpgj@UcJ7$Y*e8;VYCy&8^=T8)wNS!B8~ z%gQOHV9ZGg!5s$bumK_`D#W8{>Qa2jPm zL}Ym&V*&}nafBJr1s#q4$gy-WI}nRqykPmIJA`>;BpjjnvlH@76R{A00zjENrZdq4 zPsjlyGKe5 zH1h&T`6h5c?u({!h8z{V_xU9dL7znKXgwuIU;r?LGP8raM;I#Ec2DN2c1|};o{Grp zIqZ;c=AFQTW>Nrp{M7^Dite+wk|snQ%ABO0o{Ar;KuPXOB#F<_E3$ip)3hIw2V*po z0CniC9K$4z=^Am3;V=Y|-^oiFEn#zNHO|->$3^kY)6464lkYlR^~(DH08pqXX_vKU z6sT0GGuedxi{xuN8u!&XEvUY!r%Ro|fRY*_Pu*{(X>OA_bzIIU^c^FHZRqg5mZ;E8 z&CLfr^5Yn&;0W`87M8xJQpL3hA;5nL>bi~_)!C_Vzf}oDvY!ZCQIvG30YXF3SGfa~(NyF861@!DTj<(#%lVJKe~9jKyxO%!x%Y3GJ{3d1cps|uy&e(wTZx9D z+B!W6fxI`ze7$0^kXUJ)?ItZb0t0 zleU=b<EKrbM`NNswWTLhxwY}Cv&5xu&ov(2n27lkD^%ySv)y!JS4X4Kp{D3Q)oXiXbxM@T zaD80SX&DUwW#`Xj%yROKQ-QYY6yK9dNNOdlrW?)7dM&n*drH$v)~MB=7V?EUG{6%C zRbz=hP|~#k9$*7=Jxc8OYfRI%8(V{3!thqNq2ij1uZoW54S?9+-r9O6DR4Ex$UUtN zkm)8)%QdzP@F`r?ss%7dFpPmCWoHoBGfdVlZ5vQkZ8Px?I52#~!Se-RHN?BaE_0aN zfjE^iQkq*`6$!>Sxmnt9#~ZVU_@htM?XAsQTVUosi_b5J*A7<IWGnxQ~?3%$9J>Equ*B!nJd^h;q{K6t3Fl2u;<;0VY zlYuB)X>{t_yY_vJ>2b?yKD{!-^5q33o|SJ!fa-SF&9BV>(Wy)`?g^dOJf{c4W{`}L zx%@=piabH3Xf|ym)zfL34|EuKhVmKi2Qd0BHLMMQ*Z_20D>QvByLAe5sp@oYXj#;} zZ?ry{YnsCy1n-5-xMb|K(&LO(L|NwK`3M34oF^M&WSqI2$C`Hgu)66qbF#`6s?c{m z8il0xO6^MZ^$GD>;6JXwR`Z8+ni`F^pdqe@w^= zu4WPL#F%E$?w`#p{{WmG(bLg0h&0*<^(JS{C1d-?hMrPL%1)@XSws3L+dTqij-x$t zmWUyyL;`RnI5B_~#^}y{h3x|^Gk}*dpOSIfKuw|o_8Z+*pDU9IbRt*ch$sNUia>k4G{rpA@J zIru&ikVn0Mn27LsKsfzZsOkJYYkhO0)7CUgeKMteQM7%Ym~#ci9!ELEf=R&xI9iSY z;hI{7$Qrey!SU?;KNJ>+Q>f~^-k^pI`C)LZ>z2;1X|4_;ogl}(=6RfQk0|CplI-;h zT@J4gYS%hTYdXCLP;}JVpNUdAM|?HLe8Nr@;p0~CNvWtBbm(eVbgik;cgI0PGA@-YiVa;d!#p0p{3f{25J`6Xc`qu>Npy~r^OOuxG*HH9adJc%a8X* zMi~7_^=>et`^#-Y(w-_-WlCV5;pPNS-Eh1K!dLn~6EyOD_eM7T)0fQBNFC$0%jn<4 z2Nr2o{J@EG$@JtUeB-lQ18^dJ7u|lDU-F;kc-qJHj4A_%#D(ZwF{D~*7PePcjW6&i z5Y|isE+;+x7o8EF@zHwM;q?)5p|P|40DauMePbhrowUsK(TkrZW#!#hJU+^(Wk}_7 zxp}c5#C(F9Kdt7|F`>{{X<}pYmq@o(x^J$L7F4;a-DK82r~>PwgaDFYM8d{C1T~{X)oa#yTpV zCQyoHE{PNW0Gg+iNAp)XqCq)>At3g9#mz9VJpB(L;yGD$Emwdxm$&R1+Hxs?jfkv)t#t z?lKkcXxlyaO6>Urn-v)D0Cn_KR2iO&BA}OKNE-!H&IpVgsj-;O1RSn6U4eN209D$# zvyu)M8LyA?%9Gr#ZAaR3>b}F9ufO;#Q?`4o+WVaOEc;C&g?nv1&bX-Ci`PB0*cb~3 zE6Z}}#@&|+SJ8|vjRXvml0lK5Rk5pWMV4%?=Tzo!_i!RnI&)j6i;v$IIh???vd{EM z=vAcr9V#grvy;4pm0H+a?Pp4(r`c03HN1MPZAOzEhD7@HSo)2voh>HH=7G31E_tLp zh;J!_x0)5citDpTTRo%+LIa0D6S#m<%YE#?QrOH|{{R+MLoxwbHJaktj1AT}h=)j7 zzQ$!~E^9SAClUY^>6a7Ga|vBn7gKAv>se#l5zg=jLK{DlrK5?x1l?69a^v~`0E(x>cTjQIEQ+^0?{kX>G2EqP zsdiW>RCYlT>VTr+>=8YZnua+oW2bq(r%1~ z(j=VBCmJ>3lFCkbksvllIq5_I*u1S&3i}j4bqdYMG0;( zd=uh2pEv-CCNN=BRe`P?t_9QmQlr6Z;Dac+#lzqDo6%15T3$%jvNGaH@>!Z@nNt3{ zsax|o;k?^)SP}0?3#8yj0j&|#(Qp&$59oy3b!1KLR)ud1UV;NyNb@5JzsX%gqZ$G8 zUWO(-isJPSEV!p$r5ZG8u(8FG<0l3d?;R^_!Wktlj4$$9_5=VNcQUD~!?o?$O?uQR z5ADZim6%mtfmbZ9W;8IrF8k>xz(V`y0W41Li(KFC+tpC-!L_RVlEshRA$TC@e_ zwwlTnSbRd^JqE@;%bOZyM(Ity4{2kM!`FCOb}#UmNzN|}4wK4qJi@Kb_F`=Q~wo1E|% zTn^J6p(R~qj(rN0X$oE)yXY3~2zNC4d`G&61aeblp2KW3mtK2VH3PW~0?f~>?6R&j znl|-m(KoF(B(Of2VH~{CkajU4X>F_j0Gd9C5=+D6ZXhkS#r37r8C+7TF92w9E*yuJ z1cAQ6M%ef!QKLGpgI?9O9b6h=mBk5jNDeSegWC%twVATMb+M?*rLaSpvw;#L>Z!Wd zYhQa)Zb5ZA_1jt};E9kvPAfk8{@U*5-o2eV)Spd^Vdro^2mlctKrGx*#hpf(>3@bU z4$-T%#tSQ)dVp7DOoHNI!r^}l>X%txx37#H^>6qt-MfyGw#5FE($P!$F)nF?0lrCr z#&$~p0*rtLR?F~qj?!G{D$GvUOG_C8BqjvA87I1-e*qIa?51)?$TgdiGYNq(NQA(# zr?O#lT6Y8y#`c0E`&`;2|@xK%8Ywj0IvK z#^E_2H(}c;Tc3$Amvq?-2qZL^2%MeJi5_V5j*3RbGq&DJ7ZNau@<1Jnk5qO6ZVnn{ z69Rou+!Q`Y5J}vP`e7`|6DZ(8+@cJeU_u5cnBNLG83z!jFUSU8M1;VJNPSZ`xC3MYv|G@3^Gwqd87B&!VoYWgt#89H0!*V@#nru^ zgf+^ffyrT9kN(Ze=6wfS8Xfh`QAfq`DT;6(%NYlFJ(tm+i4XAj17CAb{_0ul%s~BD z%Qam!9({gmY$-TS(aV*;b~Ja~KVIs=uezgcR@#|7rIj-v=Ra)1&-SZ&=frUhbmS8m zT~8I!rR-GOL<7rBjcIRC8W)*dTin#TV_Qk#q0{OM+fr!=P3G|!??lp$BZc(&5^ z`k=X{@++_a!qEZ*kTL4Ed@)j`&YsWO0E+D%@tFGgt)&(RQ!RJ|5d~dB~ihe@Adnyvr4`zd(s_~0j6{#~+@x+lv0qOAt z>K5kapAJ`b%bXnUa}Mad_UYtV-#EnbG)JqKt>{p3n~mQmKBc9?I@i-Qw7V)irOy`h z!GYJ5E=I2nURBi_a(P2}!-$hHE4RJ(DzT0+4Qobw02VKf!Te)BYxHSgj#V4*e4I5C z#W^_nA67ACzJ1a5S_cCV7V`bkZqX7;zeTU5NrX$HN{o(}@LgG5H*)Bj%ZQ)zcC>u! zQwx`F4nC`?yJ@GQ>NMKYd{@z?9oU+I!~NB<)i`F(-%7lvLCyBN8b+;3z9=Q+d`;vA zS~>z(m6~-oXQzcdha*kyB4RyJlQ~@*E$wdfTl^TZ^*R&u^;i>)+eZWf~T|PpZ+P+HC#HDY@(ilt>=<5i^L#n&Mb$1F(&O zUX!HQR;8rVxW3Ue>yyX@IutG0;=dSmN0Wi|K1iIYCOE1%ZDO6hE848FsOq-itu3hG z1;l_%k5X0@FkM~!`YfGHJNAXcYumQgpgE$!4X2-k1&dD=AL{`)TP4*NkU=2fBdRry zYfgENFqzq$sRBVD5<0DX5$4#!a6;}IPR6yKhiQFtM%DE+);^Q!NS8XjY)@eT{{XDG zU~@l`+*$Dst7UG=7T1*-NrD>K1Of93GGpARaFR~hp3WepV{9pwHmycK!wh4y1Ne`Z z^I1Aq5$Uhgtg7m5V6zpP`n0*F+@R!iS}jLi)U_LL0`ln8r69*6Mn>5IS7obTSx65oR>MsGrPI28FgD38n znp*_G9TX%1kDBPelMnMd9t^13T0W~iLSI1Rz|KV{kL`o|4Y{3EHPvb{>0 zy}_nUv8Bob{ATjF82%6e!OHRr8=+-hr9nd4tB8oNNwpd?&=Cu5Pl+ov?Wi>EAH-}B z{{Ud8Ka%HuoSRbr0Li!Sc%sffbn(u;f&jg!&+eXI(Q0(&wfc>tTqUNxFha*JQ>#Z*5g{&Q5 z+Hb$Izq>b$=l6#-YQMsF%paQMZ?)Yr&Z}wGdxPpQRSjc#pgLk>V~v)P{8v_`YL#83 zXl|=elJ|QHWz)RpD@m(eGf}9=HqOS+kPusEL{bi(qz%uS^MB$009E)qW31Y!^^OZX zJ*jIKb$EjG*;+D;HL>qG;O&X;KPAL#ZezG!=A|+VD)AlRYlg%MZ4x}a<*%i#Q)3w4 z*qQ^mKpfAoYmdy%Ra&;UTT!yMwDx-|TWfc|qbXWZ@SDuw0VGaLG%LZPcxSwv`lXUI zjY%WPR^v62ASPkvC@hFfoV|IEG_l3tj zT8#GB2An5wJ^;Oe+ga@A6f+5D%8?sZp8+Mk5i~X6X8bO)pcnzPAlEs6U;7 z!xIE&>ae8BgE`quqsJvh^TD>K#J4nS=~qo)b!|Mu9P>QKmL&X^&d-l^niVNI@26ef zJ8pk?-HUmEXpNhlmmp`roMqplW}n_sA>B6lu1MmHRZ-I{l9z1T>Dr_0ZLTeuMx9z$ z6)3)=YJx%V%Zo-1-tm=dID+@~^$lXH{7b48TUDIG8aYh0pmYK@3s6a(}@prTSna9+dk#gnn9}JR1h*q024Tjtv0K|nvJci>z@L*hVaJ<6`NGG z#J~hMX*l&;JrjlX8!cZ)xvI(z@bwF3)@>TisQPnNupR;TSDUMNy{T8jJ8*B@lRcx1 zEBhUV_0vZ&pvP1I^@YQ5HxgFYIHHY(c2f*3bwWh{01SUM-)pvwzh!%Rz#26qIlaQu zXdE%7XxfOqvZKjy1-#tub`Ikuep7;YBaUk%Zcn4?VU<2)Gp6D%w3Kx3a4p%h#jK7gwPln;Mx4>(QmYMz0O1BlG)#P@QK?)yg*<{oz1j6z?GeqV zsMDs`Vf6&7#|&!}w<}}jPw6o~5ie@r=$4PIMf8n3`jq)!xdM|+`44#g7XZG`iOX_sMr-=R%UO9qu54!YJxneG1ofsTzYRuTMkPyYbK{y}veVNAN*t`A z!Vbwd*m|oR*qJFEfsCzDAIVFbl6v_iIO&t!UgqLLSB%HiXqgf*xg;DVA~BVa;Tt6_ zp2R9lhKyIVlZ}-xVVuIubb?iN2b3xmCSjJs4r?HN|Tu&9Wdwb&> zW8}H>z7Bm~IPB$aCQJpNOIW+L(yeGQ>@*l^v%)7X<~+on-w}eaZXz(TnMKvBGMi7l z_XdgCaYjuu97bIm%609TZtFt&p{1-L#6gn>?zYx;RqU*fi%E?xVWeub_Zty0%^q82 zZZv)urDuI-ZCa!1UEBtEjb=P2ItT#uAP`ro>beDIfwobxD8W>=-OG`Tr#`ni-)*C_nXWF&(2n@ z%DOX_C)zoRqIS&mO6-3OVLd_&fJdsS{{R*+vqnuA_XOhxO2=_@`@8vtePVJJKPe8( zc`VAI*y7R%5aEzOT~8jjmx*dX_~=uRGoC>!l_Y>jiG!8Zai=5odM^%=Xfx@{kNNUi z>EGA>MvzHa&*@bxs9e?krA^OaY&XdD{T8yI=5vcjTpuNM(|MlfILSTL`a>=1RIBYp zoPm-#oJM@RmA!7@>Ak#~@(%s{kmk+?cU=ajPfQlq9?EG4G&IwP5=rI~H#mtaKm_7< zQqxP3r(kG|AIS-skv}9|hL8Xq)Qm{_gsBZ!B0K#SWqV^r*4pINqW0W#LmKIGIZS)Q zr0zScO^GDOWE;2w4^7o2e1YgZ1mbjx!N=jQcK-lHt8cGczVAr7qfAXlwi*vpAgw3i z`@?kGuuesJ^!G>dT??vn9G{Zv$IMO)ml@hT=I2AZwqUDt(8^O19tl_vYa1+R*vzi$ ziu0xz$Gjqa3gDeR))^tR>&G`J*g0D>%Lioi+!*OB2egSxAhgClN?;Q?oux+w*|V>! z-PgAiobcLhz+*=!`UQ?}>v$}sEi~HV;0s0y!z2^$6j}z~bnUbxk(T?~uRO<;oPP*a zo0_rGdc$E-2?zdigq0~3W>2=(bbKOzvK5TKxL_fVfz}!p5X9nC$QzUi87B7O&*yW= zaXY}2Yic)jS;WZ=@gOxGiq2I{LNUdM@T=N`=7r6~0fjj0va8 zD%5J**Se_tJ9BSA(aTSmyO$Zs->SxfBdPNUjG|g(=6x3{oV2wiTMr7*Icdi6X* z9Ko@&=bWgBV{Oq^p`)_XNEejZP%*A;H@lRQ(srKGwU%`2IgT)1$-|?f^t~;#t6R3E zQOwSMM>Om}+DBZj=SuK78rC(99h-ohG0IBI&9(hBcxAOW)-4pNmwE|XJ9hF^<#2*G z3*4(|79JH!m1$65aQCWmhM4tUORZ28>(i*tP}fM?xBg)%VQZ7k)amLDNu8(RYtKFX zK7>;{f20?nfH{UGdAH$xLNyz2?l zc|kjNL4&3Wkq0|=K$+DBv9h$Z6O^VT z=N*$0MMro7FrX1Io->`2xE+T?44K9in24D5NC_l{fI8(nL>%O##&VyFZ#2IS^<2k4 zgun-#W-kP=(7m%nxzw3(8&Z{j3{oTF5Zuh;E?(dG-BHZoI*r@KxuVVc67Tp|f@p%~ z?@_hWsMo1UmsQzBhMY^B8!^{)ac(##B3h#*Rtu;s4Tr{!|D0w&~Z9#QL0@_Z4nG09MS^(9YYSyUdyZA}?htH~d zI*HRQn@*@c?kA$^&allVhrfdiYD@=G`T5j;GHO0sJx!A8Zx4AcVH%14% z`b;0*E7ka$P^GAFs|jWuOknxApVfK3jcG()+tG0?rBeEX3G4|nz2DR8nroXmwOl53 zTt<;3wXEd>ESp2~7H zV#D^C7gY;5R_^4qIxlU9%y@(kv04MKU7PJ*>Y#6nsfvuOK)?@pagU^WE&icR$Ace9 zjV7gCMv-h}n5R?utuCd**ZO_6I<*ZiebG=c+{TlRLtWUfBP^>Uv)9DZ#*C|;Aaz?C znlvo!ZQpdkPPJN1Aqfk^(`a5?0Zo;i%Fsr3CS&BVd0o7S z3L{PMJ?o)1_UU^12nKXsM47@~{#;(Z6lgl;~6raV+1?WF)i>=5%Z&Y&5sq)6^*YYdP)a z9y-D5w1iva%5JzZyeGyO;)jD#u`F;~=IAaO-CIhP11+eJ7Nv9WiZBm9%n|ci%FJ!$ z{b8rldubUgrcAgr36tjnO3_gdj?7;T`*>~I8_%M2g3MLn{qT2PU3Uov2Je)Ovl2E|I zsDFJ-0|g2n3(1}Ot+m#js#?t(ymjYIMRrXfrR8iB- zOu_wC%&AetHLVUOpk$xPJN$NueJ)t)S&zwG5k@${$JsoNry)CPz5eKHPhra{J>{>5 z(`!#$LLIT9vPnwW=W&xAJeBNtczm&-29qPmEPoM^f&nTT`6{oWi>7?KsQfVU#PpPc zqX+Q-5$9kMOLO0%ZKfocY0n@pKJsqRmzV(rwolnkcFqoeB&}6gsrEz6Dz}slUa0dP zxs<*ma!Z7%&e`~c`lg2Nx*6MLRC944NDV)dl^@Jn&|$quRrBGX4pQyZ%3cbDUuCW_ zN3abNosDT@a3qb@J|LXTA9g&4RHhnP%1I_pFaT4zCu7^emyLk(TfPs_s&NLlM$Vjy ze;sF!;HEb}pz|w7NV(M7dj4zF{{Rkky$6SQYUb+h<)>1r@?S(W^OlS?t?B3U@>QBm zL+D(Y!YRw@Jzmb*m8GpKdv*$RX)rO29RLslw2yPic}2(K-9t=^Z6Sfg?^3xUJp)JP zwD}Ew^Q*pd$vIGU7csi63R>e$OlKgRkC#HQ$N4D>Qy6so+Cs+_&0k0Cti? zhxdVxRmeLsB+qr3sbAgdH*UDUuU6u*5zg}_KR~d{V4P#kdRXA&6&CE{l#^|tV0X$f zzjOvn4x1+ftC1i^BZuyrB#C$Em`HsPHs$VxOwtpXaZdKbsDRV(kDluTqnaLLeb(EA zYf`u3troSz`DjwTp!c}+XsFUp*=X8o7r&XHG)F1wRh7%dKX7w9U<#>IR6smq9$B2;Z{ZX?AVWtLawyW#xr)UjEV7*A2R3syvGA zhBdQ1qGn?&%Nx#O1oT}81Tv%et40iAaBcOw$OE9N+5Fd=tiqNVyYT-2>~-MD^`w#S zx|_`!jwE7oRt*c$R;y{zhdsssl$> zmFU}=4;>epTy6GrzlyX)JtC7#IJ8!eUP1bW;06nb z$X`MJFXK)P-}hGs=g1@T`!Am=0v$OJzU%bI{>^_UgRkQuS=>fK^)JKwx)d#`P_Fj1 z)mRGwI5~IoUR}+Y02SQu-iF;v*sg2UdnwSM1UCi8nZ_rw?DdGoT6U)C8R;L_(bit* zbn9H2jICCfCoN$BeoJ$2L{X&HOn@P3FK}Y;D*@3ViN!-O42S z!uZ^n=&SZBiK7m z(X^^QElRC*)x~aVnIoCBz;F95UVhW>j)1Sd^f<;kSSUM3g|4@ev)q3IBlbqEGRTa(fiPxp7<$s|;n zSpmLt-6R4^un+4JdZ6LZbQoG9Sx^LSp%XH#^72UO>x^M&Ln|U(l!ddhz6?YN@<{QF z1w=BE&~{b1vT`!ANS(s1%}grHSGR0^)kV35mga(RcU6|a*e01KJDZq=OR3=Ctj<#r z87j9nH{YTg9a)N5?w;zWwDehTZsAhqk<-ydkE0dsmYF4Ryj;>X2k)@_m!Ou+`YscU zrMi=ZMhD~m4?0VPsQ$Ry6OgfihY(~d?gTXB=&{^jWNfm1lsmH0H^=C@?kyfZt7zs7RG`8m&ATo%IQA!` z1>IlNxAvSjlc5ufx{-qhEEJ=I;D!K8f(0geU?k@+r+*VFSFb$K1` z2mTZrUce9fx!L~!xr|l|_ zVW!(Q21hTv^@mE>I;mx0$|3+S82hei&YvvW5#xgoow>*n-5(K|=5UWyb@q~NXjQ5q zz_tYDGq_yq!?{LWB4qYi$1G*hueDv9ky4yNaGm!@#ij;a0_VYbkPrS{6^x@${7%8n z6{(A1yfvBBi3kex;(3Rp?6k&3c&PQJl={nCaaI z(lm)}A{raGjHSK&3t~1VwJ-oL0!HdKt2PxIpEahbPnb)L^L6aHO*c!oW}eHtu~75m zG#E;GwkwdAFm7DE%j|IZ!;B#*eIaHgg zv;P3SSN{23D@xy!58P8JIvx2w?VZkl_-FYjbX(?pcGa8aSVlgI_g1dy}UM3cqID^Ja}hp32|ZHwWGDT^*GioWzh_C3U3JOz^tAi&;^ad`~Z1BtNm5Y-?X-cMu1K*V=K|cjx$!7$C7R`G*ulu)3F;$^lb{` z#j~UY`~a^wt~dQc?D$f*H7a0j$S}OzirLo(M|-2(vfoauYXRYhx_s~qE{j5SZ9(j1 zAj%>!%Ow5E;@nBm?n8GKvOm=ewcrQKsd@#flTlPlidN34yf=Gz1vg#u$cBOpd39cC z#0-iwresub{J)a--7UYpVL#Rj!uYzMEnAVXWy_oQKgn$E#qx6MRpMsH@YTV(=>Whq z9rMUurqMYh{MV8G9@XUHOJV(HJ-_V#0D|?#kWVSuV}-Mu(_i$O{EvVjA{sfQwn|A5 zI(n-^gft1l=sga`ZrdW+B#6QU{S|{793JY&UIQax*SdDWnJF42(-`wm7%*q%kgT+I zz))`yC>`*Ozy=8^g$(5|Vs=Xe!PzkzVKLD$I2lcnAOs{G_f8;f`Khp649N#ODDJ}$ zqee``^ikcusaV1=Jdu+!LRw-El6VIb)g1*8OeSo~9aA*@5CJd*DYU^uCvX%F5&9%C zn-L^Rm@x@D(GkE~e7odK)e_E^waZbn0+DJ5DmYFo)EwoaYxdm6n%I&87%zL{8Dsae6op)U@W{hu?$h z14dV8_@krMeJ^UYc@H72a|!a?E<1(j7GCJV;lQ=ks{Go*e?|2F0H+$AJLKWkx{I@S ze{tYAxQS-x$x&yhw&v}WSqGTsJ1RJbnxzVirzPdEA!+y`%I=n`_uVn=Xla7u)U7t0J3?+(FZbgjC-^9SYqdvpynf$U#sTfqj+GCC963*9IWP*j|%!x zzo&6V($ht_x_2c3s2Ywtz-u@E0BLKhaTD(Jo3>n4XN+#CRHD&L4|Tl6lZ^iW4(hHe zF~b@oHV&(B@g7a2KUK-nXmwbJFCKDEZ}~k~N9jjrt81^n;rm)O>o%pyuJ=^;au@@7 zdUxj8bLIz7sFBn-$C@LSL<90oG=aqzwn;e>x(!p@;T-|I!TUh1ZT|p=?zwTu z(g(ae*G+cfB^DaEgt>4bxM~4mp#Z~6bU$EjyZ_xK$!Dg82U1wXIZPc$Az7xP^;qKScW?A zxA>W#a(af67K?=Hy4L#c>WLmDwUv6Uk%{Gj_bUfU)N0sYvga=nMMj_dMxDX+Pe%Hc zy-iENV}(1KE~88nmIpX*@(`ZNwen57wq)H~Q@ec|>o$Q-wMMq3A^!ko@Fbj+}^U( z_CElf7HAEVsy==VKD=kUd4J+o)vWC|K(iL4vc2~`N|iv!V^1hxJu}Qt$t_y;HkKDu zY-?1wt#Z*#^&HMX#sVBb5^_iaRI|IZxzm|rs29@|oLxq;85@>8Qt%##?G zI0znqqo%&rwuN5K=Jl4(H&p92&!*=B8ppK!AjVv#Nwv{PJR;KVWRUi;&9|;v=Q;yj z;X9PiOpu>dcBtDYdv>PE^bB(`w~eYv{{Tc1Qt4vO*82LBuUKhSw5dh4!yU`a;0DA@ z4D?u6)^siD2hqQ_xhEX7gTeLpnh{+80Q4RV`}>-@D}{xdjT+3R!*NxZbKd5M5j}u( zLOOlrZDsz$dr{Ti_PElh^Dbk9Ng3>8WnIH)>m0Sk#VR+Pp|u-X+@Zt(0S9e>!AQ>s zNo`7=(Mq-Jm{`F`^EQ|w2`6G;ox7>+Q(TYoYZ(ruS=a-R`C(!*BlsdamABEXDrz;I zN{bCZd3nH$H<(HKO6#4$wpI0Z(4$)Bm8XPDsz`IW=NpK~TspmGJwNu0=*5Cr7t)VD zR4M z8f~Vr!YyTMduu&X`u6187Ppt*V;X!icZ1AebSo~Fo5PO$M%5`^nhj{Wr$*Ap!y&G5 zrooT|Zg%QqsiyFy=BuaM)~&78rr}lhO>3wJ7QN9g=H>=W1+CV2TZk>S*5BGY+pbMf za4s3h!{P!6Jgz&)9o8`Q9WG3d5}iJO1A2MIUel4{DikPd`js1h4AN!IMz@y$d&dBD zjlN4?f@B8I`b)W~!8b3fMBU!BU0l^^tPLQ1H$3E$4q%+EmB-<2^(t?D4MNtj0z*79 z((llHR`c~uE(W8Lb!06zS@MzQqMJw}0rWDJl*1fD_B%Oxo}c)6YiXekQ;II%V@a%Qe$NI z>QL~SUyr1uWkNmID1E(f3Ma$Y(&_P&&# zlgGG+hP5sqsu`$I0YT^`*GE)NTM{gmW5Gumh{( zZp)Hy;hwop&6i`@omsH|0A}WwV=ze&Pn52VT8f3`bY(nr>O7D5Tf$A%Z>wHw7LInE zPMGk8K@F_(exw1PqJPD17Pr!>Ti@xF1sght!+RSr$K-?L2nXo94jkjFXH^y&dpfZ!*T^lq(+q6MnZfc~Dte7Ur2flmTkQVQ+p_Mw;+vWD zhkcheNN%Kb#qB!F9V1Gw!)(`TTh%nJX&N=Hz%g35j#qPZX^>Bth0bXjEykzgR1T*` z*4@4(Yl^3pl*d3%Qg#4kW>Dg*yLxu?Sjv9MM%~ul%?FX~c3fTE#O78N#L(IAJy(XT<~7U-LcZc6aTiMm&o4ZDrT)$uaopR!Ec}q?{ty(KiFnzT>J+!pjH{IA#6lh$yzny?{5Pm3` zap~1;xEovXbuJmv{f?2NM^&S6>cP(EeOEjJ^V2W~%guS(+;UiF{6GAicsBcqp3`>L zy0PW}kYg&UWvqB}SOM;_xT5Wr7L~6aPc1Mc4zgqQT>k(IaRn`VS!IqWJ zbC2`_`OU+*^IxF;nde9TXMySu`h{Z;0QO$Bz%(6OM*|3e{;;r&YwhM=J@$0o%!R_Hu10<0J(2 z`3+*$)A1b9G9V4W>*}Q{!OiAtNE4Oul5%iTai#KlO{UB}Ufu1WDm2e21c0@AJzAAD zY7iVEpm793=q|6Qvfbt3!L1TxB(k>fZyn>&dtS#Mb;1cFq9ZxHZjj?jT$;bd+5L&8 z7#OZcq_CgSd0Qi!vh;7nO-O3IGihC{b38pk)cIY42&8`5UUJFiU!ZyxH;YRM%d^1O zO7b>)pWrYDNY&>tQitVHdQ0Lr&sFB%fW!WX)&%Fpq1Vcy^rwgzlDe}m>PWmF*`l{Z zXMa^hNSsQ!!y-0SJQ*-f6r$LNAme7K@&YXc-V6(R}?ee8VFmV?&|BbRXrOMB%pS&Zh0DI6nplv|CG zx|kU$Sj&5cNp?1T1Riso)lMZj^uAG8EUSPtt9ujx6pm+do$yai%Ve_^|^?`@oVX&syk^RpYDRadxv!^ zW@uLJeMZP^?ytS4w~cl-w9stp4CRG<{l!x43haw%=y#xWpbUSk)zG>keh)Hs90x+CNa#LEpT655;6z|T=Vv1^Yl1|KL0EMJkUFgT z!19^;tS66pR!;Ad)8JY_hm3A|F3(*7_F6-l=?2E!eEc@g-d7(Rth{_*AJy)Qak;Ipu)-n%VVvTiOH*W{^TzZzFC;MkJvWmt)loE+EDh zqgk+_bxqWRA1MB!!8Oe+X*Z&n2g7{2 zob-*y$!nqs@6viNLi|{?Z3}BE&;yOZ1Jjhx=)C$tGX__=rhDOR^K&hdv0eR;2cn#< z01!-%`GmlH!W$9#E~NzcG29S0BY#wt84YQX25^|~ybaU10?~<4JZT_OF$-BzrzzZK zB`r%Dl`4MdkBsst@lGes>%J%YmI$V*OQIV+C zK&7R)Ng=I?Tw`iB2qc!rRq2|&y^VWwPL+Bt=D`8m1)@4FjN$Z2IF4|yNsekZ(bb`f!B4{ z9XC!5092vPGZGZ`8xhSfqB~_OVeZ7_0S%~J3yJQpsZ>>_8t;j+NeANWx{+gH%Zijc z?wPiB9V*a|GMkGM%8oUT$OL=41>0IuZCx7Lo52kX21n+rZM}m}^wc6{Z7#J9HjbF~ z+t+K}8Oqj%k<+{`O;$O{B@=8pAe(Y$FJ|&@g*ISG2hC+xXDiJ2Sh_QL^-3OZ`tG(@ z%!;l6cV3QyG@3lj`8PWQei1)pTGo*}o`^T#9*Peo?nXQu4e~MANJ!@A37T`24hR5* z+y}U=xJJ}W?a-XS)RKpZ5`q<+@f4c&bg;ftuu`NN?OsUMwm2*y&0aX ziP_a^j#G&&f$-~c32U+l+;(1j#hPV1JEqm9Cf=at0Ce(RnZ`G?0Omj$zv$T4tn-F2 zyU0LX#}-x`OZ}>P6!Ty;GDW$rGq_{h9!u>Az1D3spJwg6Uulzf1XnBFU=d^2c zcTVnuej*rS`^&50#=k}I-B#MAUQvR`KdMRB1v--8^?L@(n==x;PmfwXzfy*VhTXZb z(++T6fc`h7Z%5s+tYg)z*h{~~bgp9AhI;ngOnwP} zFA{0__^K9fOb7x-Qe)&T5IZ2)ghS(GMqw%tXR(At z5u8E?e4+<1B6mO~WG6Y@b@psQ@D+qd3OtG%$i>#6oF;BMDiO zL!DL!-NpfYAku@*Ja zoyTN*~yPG#j4)VVdBeKbKJ^gf&3$KS2v?J#J?kuQXc=%Cz2JUfq?RTEbc+102y4XrGr)9Q5X;hb%w=-yb$oJtM2 zsqVp!vKKS03&y}S0r;7l4L;p1>JEjsYaa#)niRY0}X3LpQ7`QE9a!`8SrV; zeW9vaH(_;07(RJlPx^V!6!%FUcC{(Rt2ebN7Q2+Wle|bltam&#u;UOy=B>47)TG*1 zQb?Gq{72Wi+G*8X4N@E@l=7KhphHrmugUXL(rt9#1=^J&E0FP+8~Q97%wb^-0$Q^z z`QG9#54Zt2=&|l?oYuOn0$oM$cFN7mlHnbyjEn0A>K7b2VOiX?{0obUj_kl?$LUTL z=Sb1pd1I)**qLPKRHIUzI;Hfv#Wy*U;sKICpYdAmD!i>_TS|4g4STe=Q6%=L`k~)A zKXswf^-G&Z(4$YoeF}A5Tam&;oLU-WdIjUOr6>MpsJ_)q-SH(0W2al(*;BIc9az$J zOL{M+dBbOy-f@u`oGxq$@>?xWQ+@pkHtnNA+S-vX6sQ6kTt-}ElRY~;40F3nt5$dFv_Yl9NHY?*ykt+bX)TP?(ybfyXE)~JoA3$I zr?dWwsRn)EKFZ=9f^pgvc7U5>wq9ou(LbROyK$}+ocTFpov?&+9#C>|>QLn*fHF@& z7G15aR^M8-qbfAMt6DjlM^W8U;EJ)TX_xfr_?0ZKSxgD%br%60qe_La4peYK>ajttVA^@Tt~uG0b2@Lf7c+H8jm=xVyunXetJW80Ixtz)0(d zl$5x(70(8(jdO?%X`axUttyn98&;dx?#^ioZKOEa=~{*TJX|&Ot*Jlyh5YU$dBX@E zsQXP_Lx=BuvZLzMwS6j->b#da9PU!tIgEqyNxnf@s2s(-&1o&}0z-}P59Yf58oR#K zZrEE=oh>HV0an$n5b9uL2JYt=Iqp^$47T(3ey?9hp;o2iT}g~<8P4XkxzmjF3U^m! zF3xS!9la8n>jD==uK51|vV*l+LYAUy4+s_pJhBflZ~%4XkDA)P!tu4JQ}!kuCAQR@ z`mUlN15KsJo+f0TzDYT8$Wg<)qMH%|4Lur15&5##5tgWxP_+t9`Ko^YZaVX;u@6cncbiV5%nbh0Cjei+fKV+7P>s; zetG(bQ01#?xLh9Qjx~uwT;ibk99 zhULeD-EP7hSo>O}jlK6gAGia}aJ)0X)$gq;-)cT3y|XDgqZkLjQW{TPhCkWNzI!l*?Xbg+Uj*YJJD}YQ>-u^c}!2A=DzdNA(A>s>^yx| z*O8`qV?lERi*iKAY;<0U_${Q+yw|R;Yk|*UO|PWaWOF=_9r6u7QQ3LXAhDnkfG|PA z+Hn58eZ%)|xYFyki{1lX3ni`tU_rs%eFKj8wPyrm)3fSYZslxqG!G?(PRvu{TKcf5 zL<;syv~bvx+J(UV9UmaPF#ag<6yoO7{{Y>>uzo1<7>KK6KxdKMd*IMQh<|6sekx#X8Ny+30(D73cL^#NUU+x`{fb zh7Kgph9|b|`C)V8@oR{K{v9a!_zwR7#EJY_;s*o!y(p3(j|JcOla8MzJ`+uR3C=H@ zFuxOZEgxTfj;~-|+xno%KJ00U5U6%|R;32GV={`#*P`dW}`u8uTZAjL6 zd1WtkttWUL0>&alkitDm>x2{|Movw>OwA)ry4A0#SZS0UTIQ^f&_E;rNy*p&D{Xzk zn!cTRLe8~~L33O<(_>-YCn%S4k%B-63pen#wXMGrTvWWO$`l)>-Sa#da0ZD60qX?E zn&|6W+y2kteLseEGWt){AAIZkhl*y^&1eJ`$%t&Y=iWZ69GNEx(&)XLm9x(3*;j6# z?T)7A+W5W*szwXqc}JWDji%UFyt<`jr_*&>^+^o_cNiq^0W45Z!0@6Ob+TE zSvcKtHIAKlIGtC9b$uz+8GB(`iLT(vW&^q0b_0~0`7W1)v<*v0;{F?Up=DNuI=A#g zOQ3>zfs7L#m|LzKv8}^)IyJWtzMZ{Vn*F`N4|`mB8A-vehGo^H!IK7^%rwaG`9K7MdY-{_*Ox72Lek<0IZDgMwZ*1neb?rY!>h*$V~0%o zHJgeszY*NiV^iK9^|jo5sY?0w$4#ag3P29m@AUHLTm6G0$Ou+dJHzpXToV zONii|PQ=k|fNeS#2l$+Sq&~l*`%giJOgcTa$>Qr&XOUYx-s+33?kZAoms5FR^#%&{ z9t2>1gv#w_zSj6)#$ZktoazkI7 zbrGX+c{m!v^j0+<;140?$e%T{V_Qyx4=4zIL4+gCK{xXEuQIa^U~{{R7u z5L^Uhe2z_0o;bli5$ZIPZ5mt3%y97QiKO$&;pA1`-rBl_wICP+syWA(sWBw|mG_!i zsc7bxe(;`PV`Z$;bgI<#3U99R^3#khvFf7?62_lo#~N>QEBslc)@ZtShSrX2lIKPw z@*mN8aQOXKsQxdIG_5g!9~j zxK8n&`GjD`UE~&shE^DS_e|EF@hSOpxWXU}m99v9a#scf)m9QiQJ?t6R@7B3Nc>KB~fA6tk(gb zvZt0zbncgdvlYWQ9aU~<21vroErH!tQKaDQD@#3LB|mjlPFBK?y&rax8a#+-Ma z%YghqY+lhP{_GFA{{Wwo%O^)Rulpu>_0vXhv8@^Z07zSNWu~hpKhg_rO~h94+!$F< zhB&wbi62i@XAnA#End%%HLPptJ;Y!E%n#(e_d1r(lMOCC(0MO}c6zlZRMuG9O(oSC zOO=CpEN&M=M})6j*1q7jnT!r%NLYo|CsVP$ZQEN?fMcE_L%B@vJ+bm#g$^y!D${9o z!&(nhwii@wq>ou$+Bm!J7r3V9J%Bu)A|-O##kuw!R*S(7ZIx$7V%GD1i`Q=WpF?FH zXI|1Ej241t?7X*&wM{C8uCH2+(73OEN~H=k+CQTY5J^5kYON|n$#J?zlfeMA0iDSS z3rQIxWsv5{A_89yK6E0{WZt+(`LuX9@X-K1cS%Np&Jon7v>Ynw=! zU?yO8l@c0j!Gh7ro}l(!aC@of`W;z3>}WIJmC!#W%T%OlHm!F0#}u-&)Y+zz=!9+7pQ%b-1-{-kv?5#BKGPKcaD#RABz8pXRSEW0Q=T*N*Z< zIGv?8(|e1Y;9JmkTMK%@?qeI1)cGz_((c%^_pi93S~Ax#6ZHjQ@edP=eK^|a)b2f@ z@*h>~jzQ(VR7Nq#>LYuj_ZknyZCakCtiTN|p+ z2g9o8Ow1Be4KDAkIYt7%+G8Mo8(8HE`0e2>yzw&ti$u?@0s0 ztKT54z4&~XZDu$oa|iWKeid)*Tj|9*pPI{~q+T~31wvAJp!|u&q-if%`|*y7mdW@* z$CBAnj*%DH2HEvP^laxqSi+VL6Vd5|lp7zLiW>7uF4Q?_A$wI>f z6N0iuEWjJ91ujrX3Gy3^0wZEnEdmIg)$gWc;Rdtb%%hM}MK3({3z`5OlJb}&4VFiT z)1rVEOvD7aD{%TK4b^Q+qz@2uTfI`3Eq6zs=CrniQMsj2-wRv`k0QFey82ziTK@oQ z3^BJOjD448k0OMg<2;H@aA+9l5M&=jOUIm^;cZ&oDCFK2^!|vYbuBPQE!&+Z_*i5w zS!*$`CO*?cyi0Fv=d!hWbl%k7vMCRpJ9yr!`Avxc4VNlJlEzd@Dy{%a8ldlcb|-f5tb|Xf}iB>Bau@stEBQ z&(ULkD(e>7h5e?Lr93{zO{d{C?b@YMNvPrEOb9u%R}Z;xK07NkHMHG`tZliZXjwV*Ngnuvm|d2W#voSumRCl-s#AVkJWV~dW?36j_Tcr0wquZ zCkpMdPE!J8PX7Qz#B#vjx)|;~QIDBG1P}ooJkv98ngP9cOax~MfYT&MMXjF6K_F*2 zOw){@ZITm#=$2%r*hct3fr&<72nY-r#G)suQWZpKCnpNt`=QGls3riJ^GF#cH-y6| zG{K&`1YAxq6D&JyQ$*y1iGcuTC$gFVPhcb`pZSCuHzfcL>2M*$a^uxWoE`jSW3nN_22u+i2HU6%80eI5i3sL= z!b4#s&s5orZkBuQkIDj&@Gv->e9#6&$IV8>$8^F$+(H9}Na{MN1~IaL0y?NqZITty z4DG*~EfVeha){e-5o7>l8SaO`=mZZaQbv0tf;)PkfD@aT;0z)fbLO1GU^N2Zb{{kd zLpYDBh>{~2K4^9KN7XKeVt{&nXgRZjtZ+RNmgO+AvNB8w+jP9S+v=|RD#)0WF`}x1 zN69E?JqCKKOC-tNPF%s+AV-w`IOw&mtgLk!4RhOV%}TUp8t5SXN8Na3YJ9FEAWv2H zEm|zAN%ZP9ucqMzt`Z3ByqAl=2|Rj_xZ(Pc4t<`lPxMdoTj}*H65Bq$PE7h~Id6gD z?Z3OFidR<*07N#chvZiKM{ybr*0HKx>NTjc<(9Ol)4O{m<)y@vGXM^q)%Uyyt=c@c z;?kuz4yC3K&LL%Knq}>tol?U4O>Nb)s*1s`s_JF0h?xVjytBoTT9UGH`-`;0W2d`S z(lracFHqGQ_S6*ZsXpo1aA9+szD#Vf%?h@<-7PiG)-P@=+fk{@O7PQjUgq;UcaSH_ zAgim>)SGQig`JH%Ml$c(o8i2e_c;L0`$;J3m9<-{+J#ycp3PSCk*|9nBr&am(0db; z^jIu#yR}d9XtGIlXFsp%msUEhN;*!jsG1CGL2zOK=OYo#+&sdUXM{^880L&R%pvXJ1^YPVx5_R+rYTeVsdR z8r{G?o5S4q79D_egty84vi|_7d@j+b;w?i`(P~;!w7z?-?k&Z7ZL4ojnT;+j9{L`-*PzHneg6!aQ55H(1oE*JV2Hr%Hn;HPwi9NAi~v=MmJG6`}x)ig=#M zsPR%rSMYSz_@7c}Vdl1YARO9JFn_wM98sxq$!(Ln&5aNI3!ebI0s~HZ1pKaULqS4{ zOQrsR<;@%O#afSt0+oz)9>Yif01DJwBi_KG{O9#j0s(OAnzHo@!}_hA~A#inQwg0OpiwJ<|3xG?Tf7YYghNqopUGPjsz)y{%^5b3Bew z+p5oXH!}K}9chrGETX4zrA$<9M(5s;0R4WeeMRBpnZ1^iP`9c8 zbs;m-R&I{Ut)~??f?yT4vm`S`O>o7Imj@~MuUUrD`kN=2w>GVD=?0ZY2>bmPm26;s zOVWQ6jE!9F{(WT6rK8>d021?8PES?${)->^SpNVAiLK`=5zPeqqh*n8r@rN&v4@v_ zXLa-n-7eSLFDcR!?>q+>>5_jX+tbteS_4}E2my9#CnWZvVT!9~9?cS=Y_zbyt|4Jp zwtcl|s@D%9y&9E_P8P3=>>AB5SLOGZ;3w8rSaM1;NwV@mIPh_Stp2IWV8+43p-tTr z7XkkOlJ(L@oZDVS24CmHV<^*DXiHn2?NyP@{2FM=l zf!-6ctya7}jy&x$-lj%EQ1A}>c_8-zP4IdhhmwiIbXpu_)3e;Tk5Z#g$JF1>EXn5Q zt?*sr2pPOx_^4+q0; z0r+VszH z_)dc6ff8g5_g+fe(`=rD@?O8EX?=*e-JBY%&nrv>NhQ)`^UCE1%z9I@Lo40&7~quBKRD5HC5Yq7Pf@({;1 zg%>%~7$g8p7%9&g=?$dkI9&k8ayiC>fDY_?A0@(R9@sb{yR?&A+76}-V?aBYQ}A&y znCAP1xwEQm^c(72M%Ra3>TI^SagJ(EHvG-0>LIi9Uq3%U((Zpxo{sgcogFp57wJ{1 zHI)T+0}dth+|U?*h?3k4@3O+wHSt#d<+@GlDo|sb8{t@0tI8(?y|XxufneWJs@sks z)9AOvrALS8uoj7zHMH8&518gFMPvIxydS2!NA`~u+J$jkP??CQ$;}-y{{XtIev77q z-57(`nPjPFMG%UG%uplwPRm!qT03+-0^Z00&Ix^F4iP>>Zd*w{+@1K$-n8xVn zl&r)o>Q!Zs0U#{5vRZceBrTEICO;-4m;o3coTPIBG6n+5bve&ug@m|cs?EA8j5oB( z;6D)A{&_-w?!Q+rMl3LJxF5tiWL(?seaQQjmQIdsV*ZSeIk|3SVpiG0qj`xKbmT3S z#WD)Ut~^dq;qbSWaW9j}!;@;DaFxcHW5hP+Teo#>GN^SL;iQ7V?Vi#)fT(w(J{K=m+QMPy-tg7apBMQ@te4eHaVuGn1 z*W!hZowlUwdncMS2ZjYEyq^p@lU_iQOiQCHg1HpxR6YeUOh>{n0gu;pvZ+?BKn-e- ztndW6`6R-|@Iy;Z=koGOKTk#KH7g_IqX4oD4yYCwjI8(Dk)O#csFMaECeBZCCLQgo zs1^rulJ*CP%mdM3cqT}dwy8zkWcuY(;1G6F*n6oO-v$UKR$DbKM%LA|?JgYFH)cR1 z?iJ3UW=dCEk+Dbn4c8aa|lT##3cpXQE+4t`HN8=zo7<~4Fb6!8U9W1dBtGfx{{Tn>KV?g(6VSq6XECxApMqYY zzS!TjZ~2S8p}?OZ0T*i8gMn+VTx4XxpR7txW_Mzbvz^0J-zG!N`7NiZUvh!%Rr7*5 z@KHCnq<`%-f&TzZgtYC>236wR_9?849$&~P>Y9rI4ZN%;`+54Rs$JexB-OgOaXBQ^ zCRO6x_9*)?83H~@Z)E$|aowr+;S`zJ#%~ovBP?e2k zHC_=X2~8p~=C>ZmVql^z90uQHD-U@Zj|4`@_;b;1y_A^3VP@^&79RRFBAdBD=$Vw_ z01Id=+)u)v*w65Y`6G~dk*PETiRJ+L6^i{XWYRI@OsCZ3zApkd~pxrfBSR z#{o5o^aX7jNH9YH7+VJ0kT%L9fgKg1C8=`ln(jm96g2*;WNrTdvQrCV*;SM8t2MqJ zV{zu5ohE{Lf!m_b_H!Q!VQxV9QEXqhR%>cdX&D5i@VP*ViIukYcYM({&g_dRYEouC zm#ST9?KA+VTGh@jG%GMUyxgU;p3;7BR_WTy+rsx6GpS1|S7pek9!xo`Ego41>=v+F z=6O}jq(CAjQ@)!v$E|STGf!yBgEd5x(5;mw%ZFuqZTkgvItFqfCoAOT%biHRgJGx8 zuX?a^Uu8n1{B+vjKh+Dv?WxB=y_<&#It_tvGHA3PqSpiDzf70)B}UDgMtu`=lI~rB z30sbmdMi0{ARf!L9@`q5Oi9Tph7UzKkMQ6GT736RTo7(1VL6h0)EWRY1g2;*Pysk@ za)9`te33IgK@kxc-3S5%13M;Z9pE4&{L#-qfEsqfp6qT=1i_8dw_`AnSPnWONhClq z*&V=8lNpeZ1PL>+O#lf90(LtT$6zDqlNu`k$e+zQ5K|c$?0{+Nosak+Iqp$|8>s>( zDVhgel-LGD3Cfa25^P`@P5JgnV?hJe0~5S@qn+}jM{f9(CJCCH`g$uG1a|7H8{=em z%F*&lbUf$h(LnkaC{@VIh(Z2k`o! zF@*?&x|@8^B1XrefKEmf@&^dF0}>DfI57Z~4h{fN?}alt`yp}wcT7MCK#VI4N0(#( zkcP&@j0i(`PBw$Is9I+P4@3ZkkRd5)!OSa#KYCFt&J?ku9JT=fN@fB8I~8XqVxvJP z$xBvfjB50r=2Yd_g0)(Wzx1sQF#{ffbQNlHT84opa<#Qw*Ov!-vNJ1;NxrB zk9nkL(PC1vXmk(&nb~yJ=(Ia*!o;aSHOzJZTcnIzG+x5e;qH%$P8K`#_JHYNGvvBq zL`g|ffN;q9rjix2ldGl!oSiG`ii#-EisTR)DkJAe?nyM(R2(z4fE$ z+*1yw>>uVA3wQW#()V~Zz0Jg&{!6nKr6pMoN#JSyCzZCFDN}10Oh?@Z0~m7j=YAbK zmIX=q{{VuZ{vJF7@0WbX@>;D{BuBG9C!0JM0z2U?Hh5q)q(JjtjDHVm4D-9{6@UIu z>W|`geu(|nFOhpw=^lO?oRu8O1-Q?e^aJ>RT7EZum2co0)9$$am45FN@*d35!7`Yp z#ZTa&<>10(UD z9thM3!1?6^!CH$z_eOnxl7G0w{D-rX;J@&@#Ags(zT6kpr(;^9DUA4z9Hcfn%Jdg; z4Mzrw_BS-D5hahcstEGBD)GJR7>tLhT>-g^ih&cMaU*^k%eahxoK#$J^gS$K;kj~Z^_ec{@0qO8 zCz#TK=Hfpvxb896bsyU{)ijn@e}qGenMuWje^z1)tO23%9WZ%sFB%R=7Y_*j$Krwv zUtbg7+MoQ?Ve}m?Iqp(SscWYCiQU?3l<#dRReiNePk55Y7$Ef-SwFRD(6*}Z!q+vk zoc)JY=O;+wfIs%j>Hh%sm!@W&#cbO8{5zo+FCVJTU#B$AR;Gvh`G17Q;tJKSxN5!S z%UsHrdef?gy`&yS+wzV32n&&L{{YE$7u-F>SH-6F*HafoUYNCH|{{X7` zO*Ap#f{x|E=jEKIE29I3YqHx`qUxohnJ`J|fa}x}^t_PGsb$!-@&+l#ue~ZEcEYIc-X% zYJsc-l41u{=^Q!1G(D75)YxBD;zclEH`MoAUIF0YZd-G8Oq#s`8+tDGos<*G*a_-2 zJHDK6fnmXaRL&Ieew5(OTPBWfobUc<4hz!Bk7k~jd%v3W23SdNyS4(gmKy*UD#<_b zGk^UQsaud41OQ2;xc7Nhw?8Cx5 z(C{vtXZH?|C!gkr`3a_&{nNJoyT6+B<75bQf^Yy!>>_sQ2={0HCMSy@!RDucwCVnh z-TpEWJO!t@*=0??u2-hAzypz(PwW6XVhKs*pZJ*lxO^U8{y=HOfLTyuqsl+z2AS^5 zDvvI5y+O7o9`8iWu>Q(D+5Z5EhqDiZ%vr$JVVr^2J`ny79o)Z7k$(eNxxvmgxx-+Q zP|n>-^xrZfi zD^FN@^pP3!_TkqP^={{Ryw*@wa9cH9R_(frnzcXS-_1k$QlNb@6fhw$>=sUi0l4i0xS zxt}ub=g-Z0tF-eZ#2#24N$F|at!$04lzX%9b04!GgUjh~3u&^esNY@E1+e}~gmW|u z0~?PeUZ23Cjx6tP?YYK$Dy6gQ5WQvgA-1((XKj^QZl2zD^ELnlFM4Tq#_-4B^Sd9y zi%Qj)>X!~Y#!2N~iNo9{rf~g}pJz&Hk;~#cbNrV2-is-{gunx2nw5HPV`DYe&1IR+0shT#%dM4!njFTwn zLbOQ-20N(^lC5iDqXJQkm_X53W=_Z!`YVV7r$q}yd2S&q3NiUdO_D%@vWWL!l4T>A zCmSTjk&fn_x+|g)At;dn}Bobsi{Sk zJ~jt>iCJWhybdCx~BZb!oVQ3x{@z3jy{u2>y-H{)+c!T2i0rsr>?1+RlerLZXT^lY##^rZa#poc73HVKci^$2|NY~mbCnm_StLG zKmKg+vta)K`=^hRb751E4+Zi%kiEIoSm8Xq`X%sN9|L>rjf}teY=6Xfkke|Bif@Vj zsYSYFUR+Xt(R)j++rJ>6MO{iwpN3DGTNy9Y7x<4FI$dWBQh(7e?0Uq{g8u-b_G3s! zz;?m{lS8BdBgrpmkNz7U@iWH%0Ap9*z<<#}qgVd`^m>ux)$C5EBRvv6gHNxbe$f8_ z;jsSz6FhwAw-Y|j{{Tow_7&TVw$`EfFI{UOoNv&m87D1*k7bYk9}n>}$WDWI&6`@2 z=!noR<2JPf{{Urr?|=x%kD3mo-rjPLX^;LN50-f_;oHJT`Lq6rjvTx{`>Xzo)=e)= zBowfA&ys5s5BPpRmU&>|%g7{KyB&gW@^(jnafB8YCldZA+chwL%@v&-4T zmxn$H`Kv$4o8V)@1oqaa{EcWaeor@V z3tj&JRlii<gN~&?Yw`<2hso!k=Sr$+>Cjn+Wd#?aGrR0PPiWvLUY0w5sHjGmwL)ACnM2Y$jRjy^W7fT?1gdg6! z=(`h46DJ-?#W^xNZjWgX_PCe#PCRghtb*#V1FjPe5wq)6%dRv-c}xtTTPNXw5$!}@ zw#EJwa_r$7NRsFG%5lPWCSbgtxm`;dNRzi@d^XO;Q})s;_Sl#AQOuqbvEP=b)i?Pm zr?w1KGv>N;P6&~O8psl59^6O!Y+vC=I^Gh~qF&dZns#ueoL`nWk6-XzBPoIm?4C2W z=un9N0B?)@DCgV4dT%WFPClhx&J@yvhc&#P^org!raFafHpb>vEKv_?3&J$a;awha zZT`}^yf37Ki&*~vSgqAItbKHq&ET*lR`r z38^+al<_2-eKL;`j>Owa){I8m^nGEx~cutdgTM!0pEMj_;Z^hgp5a3VA2m`>>(=66k!1dJf7 zp!fYzZ=O+XAWBS3aUV{JNrSc&1LPAC9pz0GV$ODi?dvDzl=%ct*U17%op4i34CO!s z}s= zW^pDGpdSOOCQimuw2_iRG7d0+2+CjtNy1Si`YHbaI7eU}hqjM&6*((>42{X)$o=^dV9AN-tpkrq#hS}XIJl*9q$N(h400_)MO|qCi zN%6`yNM%tS2cje&;oTVnbsg9YAf)K5{nb?(9iC8p;a+BZl0RdBrNpSS0=)`u&mb6B zwJn-)A2qQp#PwM21_6bekHJ=rZAt;N$}!nv)N91ULfXBaSX9)eBWy}(F&c-x;$oO7 z9Z-$wiCZU85I|JAfE^Z!B-w&$x__eqE2s`B5sUd6q5#?Jx~R> z_Ycg4mT8IDAZUd4ip)Q7WU!@hrqeuz7HOn5O6ov*B%&)u9MN$AvA}nPMLI;`ryto_ zei$PkbWJW%JrS^Wj1xwQ+!;Pd`!x_EC1(nGlubV*oPGgOf$X$TLL;Og>v|wZEKicy z8bUTuQv{=ES}+B@9*z4bEvYrr8!Y}vK1ldL#396Ye4`b#r~o0S*Rm#^Kk6*7-z*QJ zJd!raOJJ=U%C=3acXLMA2@ab;2qDDxD{Lu@c1OblI7Ze!4J)+Ec^>P?9%#2pw%?WH zj)axB@WDNlES|~i4aTSTZ3&Ow{{VRtr&B)(aF3eWctD&^QyN(6jiG4Odkrx@^T;Qx zPvoE2=$Q^_i0vz7;DRzCPY4Mcqiu~>gInxWAbYM?Hp+p41W%I9H0RMXsl+9)c3^uI zxb9%=0tV86KI;rEfv4{5m@-Z}C9qa&MWrDiG(=%vM%sfR#e#hnUxEz6X44ZVvNjIP zDzsTr2_OmD5}PVOd)<1h_R}3igheqCA5^wbxP6#H&V)pL2Iz}=O$U{-vx^6mg#|hE zM#8x8Ve-?7Eh7=Kv9zd$$)-sn1{OeIIT7en3Vl=9Dn>gRz+?h^;WI-U82c-{Fe4u% z?xrIGeG#-Qs+n#e8T3;ezyR*@72JT0vY=^^=9bnDnknXJi4LF4r+rf59m$a$lL}F~ zXET<5sq7a8UMrikP;1OjCRN@PHgP+n$N`>++B!%Ss^%Wteo5-prW`yrmt%4;lfwpf zN5kDMgW$S@>6bgiJg^DdeDL_nDRQM7i7pI)dqYh!(tl&iEfc#JdDgadhoN1`Et#Vw&?zVklqCnaTS)m|XA ztnCTBFn8HIDYd&JV-skwQ>f}i{KFndGNXa;+$MUc+=wK?9I#}3NhmU{K1*I)!r{&* zw~}|clxaXConrFAGY8EPGno>Fj9ZZl-sYJkk>@El8+}v9l8!J0%xqzu6I%RDbgZU?D12L3NHYvadpdM7VZDwg8S_e5;erSAS%stPx(;vti}{z=daB#g_we}oXId^JD?kgjQOKzTB8xvhb~S% z5EOwpS%szy!AKYZ-{zjuOjpqcN8J-l#2oyVQD!{)B@6-#WTKHI3@o=Z zZn6|K;B*P3BOKGo@=M`>F}lhO_hjVxqA3}e5S>t2hvD-WIY+{mLqg09?gz;b0PGMQ zm}Zfm$tk9GoUFGrl20xa6s6sMNd=gqkZ6JkDhfr;J!9^(JU(X`INb_#`+dqNc4Hb~ z4B!-5NI~qfh-l~%hGa+p9_VNpkKu_PYB_>3K5H+Lyxn03GkM#xC|nq=V?cn}BccJF zIbfaENn>PhK8g86$IE0;7`_x4P0JgP@mGe(z>(b^NG4IVXvZuVJ$oi?GI#2-3m^c| zoJt0tOpq{&LNU!C90?^7(9?-Niy$%pBu8Z$z+{jSLq!~g!b^U*N+}L$agRlm0dN2| zNJGMcOi9@~6_ogGB;phl{oR%3jKmZ2PTEi=a;A_jOkWI>BuC_lVF2wbBa%)`k2K+f zpvtNQP#7_sped4M4yz!tB4I$&xWY;!Dtiljf(&e&l4V@x85oo1pkV??gl!9=rnUk7 zQ6QMwe3gNqz#cU;scVz$dTWMuE$= zIwpd47*v9WVC8NUV2K1uVFD-Yh|VHDN|2cWhhh`E2O|izkaiH7J7@A#2?3+9^iI~3 zGk}8z1Q3~{`o#hZK-zGfz(fg2*As->y*8GlME%$ zRfKmX5J7gyL>{P+PSA!ix5ydwQ(-4`0fGFK67IN2L70h=DYhSyO~@rfj0_k+76Y`E zFieD)?o)aqhC`h634teU1djgz$uKYissND#-akZJAYvf{(DfY=p2bKGHYM9BILFB# zJ<24K?Cg~QW_E-(G1(J2$W`+mxRlZzmPS04W-|dp?w;u#mX7IA+Y$4O!l})e1zPBz zRa^oLY?Tc%mk1dOvInZje1C$dh;fwYFW{xk>=iF=H^Rtq>mTBk!@6k+vlKMW!a5-4 zNQIE*M5S=%6EIa0V)E}JC>CQOUf_d*q=Zq+z7h~QhU)OuBYY=9D&@w=IkSjX=ahb` za><^dDj0#MvVdf#Dghgms4PabMD{`s{ne3}5>zrI9-UD`D*&J2L^5V%C)mg4gG3DD z$qf>P5>C(*?;Cek9PE^Vu?bM_fLo2`R6Bl>nlLs{nLRrqg23n=US&ua!fbSy^gv8P zD1DLy34oLl%jMlXNbGM|1E0x5BluHi2*xE84S>e*q)E=mNx=xTee#B=ckoT5 zz=Qx3gOqY{Cv?plWJ**Z9iwFl9nZ-eo}Xku&O0N3um~QdGk@k00&q}>2e0Ug4a5U} z{ZqJ*BcyjqN!Vk}5t)#O5#NF{6Ti_wm>HhiLV#`LiWeck-4bMDaG~gTD2xaL2%x`# z1oIw&Id11*GJtHDMo4TNqK26|Ksh;08N?CwP+(_dMBx-9_@WpB2ueKnMa1>nWd;Vw zpxi(s%p)0zLzZGw2|Yf@q1XV-^@K!55Cf-F+iWnM1%N@p-3SwfC$`}ejk1*mhiBGH`ehy)q(MY-#g*vZQ$bj%(}fb9^LM`Vehk(_PS z5HL4P*fTpQBbNvp=mR5dk>!KBAWp?JA1_pe&yunUK0!z(M`Y*S$U7(mx~kgh|+8Ly&tUG*CyDe9$fF)m-5UveE)p1t3Ty=dxdSsmf}uEG^`0f^!`_(>bCh z6qK1s0mlI=+7q-)^eKx0-|nT27~G(a;a2vI;L6H)-V$;FxRejG9l*xLHR3)Jqlpc8R(dkG0Q|_q5+}O2CQ0(M%9FPJ~eqG1syM zuI=?%0E2)wQ0)M9NDO{lNr8mXJ+QAV9d}2N41pyBG1|$3M2{p(M{KJ|fdlBEXak}E zr2_S>Y1#>mY`)o5HM8bmq;WZ zC3p}voG8}rNI*UcgE51T$xc~!^H#n9fw@RYKMDacc`>wvOLHVZO&p_i&1o%{LIAAE z=%X5BjFoRGJ${JBV1!VY1x1eL2~9K1WXT;0zm^Hez)KDU5J3~s0LA3*`6wxY5}F=n zB|HHVOs7aX6|5227BmCAl-S0~G#ArYO1JE6+paua|+1i&hg@8mqa{{S?= zkv{Cg19;q~&SP~*K@xKYIwlSGLBd~q0H!0D!lW)nacKiN9%f7;W#J*oRR2|3{P!>Q9YHMvY{{_4H{vv(JUF= zSkc@{m%tqTJ)Vm`0-}p^HQ3blDY~elkucN#B^mK3qGc8hf>v}hDzDN{S?b^hWL!{vVo(CNwMe9>1cE-9-`PUJD!iDz;vSRT;91AZTZGO5f=l>Y|7x7gO2Zh<_4_BnV-$gW)PDs9~k>xij`rM1>$b zWgq-Lswj|n12-tY;VLMk7ecqHhu{h*hG`OqAU+ioQo9$Dx_hU0#^n@C$whA1sn95* zOlY9&JybiNRTM%bT7S{}(deRx8|Y8s`66~ID5;WD)cU4B3w=~kA$SLGnp3k>Q8Azm ziZA#_qKY7LEFTZaN`DK~Q7sGQ)`{6o=26$z2Q5cEO6(=*jY6C`Szq0}g%UIt!wQM>%qQ6LrH5K)_a)KLJ7JEHB> yQA;U~eia+CiX{3Nr>ClBZlZ}XjD8}cJ|z@Q08hp}6l#hfPr*AY2FfU +#include +#include +#include "Settings.h" + +#include //watchdog timer library, integral to Arduino IDE +#include //get the library here; https://github.com/rocketscream/Low-Power + +SX127XLT LT; + +#include //get library here; https://github.com/Seeed-Studio/Grove_BME280 +BME280 bme280; //create an instance of the BME280 senosor +#include + +uint32_t TXpacketCount; +uint8_t TXPacketL; + +float temperature; //the BME280 temperature value +float pressure; //the BME280 pressure value +uint16_t humidity; //the BME280 humididty value +uint16_t voltage; //the battery voltage value +uint8_t statusbyte; //a status byte, not currently used +uint16_t CRCvalue; //the CRC value of the packet data up to this point +uint8_t packetlength; //the packet length that was sent, checked against length received + + +void loop() +{ + TXpacketCount++; + Serial.print(TXpacketCount); //print the numbers of sends + Serial.print(F(" Sending > ")); + + readSensors(); //read the sensor values + printSensorValues(); //print the sensor values + + if (sendSensorPacket()) + { + Serial.println(F("SentOK")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.println(LT.readIrqStatus(), HEX); + } + + Serial.print(F("Sleeping zzzz")); + Serial.flush(); //make sure all serial output has gone + + //now put the sensor, LoRa device and processor to sleep + sleepBME280(); //sleep the BME280 + LT.setSleep(CONFIGURATION_RETENTION); //sleep LoRa device, keeping register settings in sleep. + sleep8seconds(sleeps); //sleep Atmel processor in units of approx 8 seconds + + //wait a bit ................ + Serial.println(F(" - Awake !!")); //the processor has woken up + Serial.println(); + normalBME280(); //BME280 sensor to normal mode +} + + +uint8_t sendSensorPacket() +{ + //The SX12XX buffer is filled with variables of a known type and in a known sequence. Make sure the + //receiver uses the same variable types and sequence to read variables out of the receive buffer. + uint8_t len; + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + + LT.writeUint8(Sensor1); //this byte defines the packet type + LT.writeUint8(TXDestination); //destination address of the packet, the receivers address + LT.writeUint8(TXSource); //source address of this node + + /************************************************************************ + Highlighted section - this is where the actual sensor data is added to the packet + ************************************************************************/ + LT.writeFloat(temperature); //add the BME280 temperature + LT.writeFloat(pressure); //add the BME280 pressure + LT.writeUint16(humidity); //add the BME280 humididty + LT.writeUint16(voltage); //add the battery voltage + LT.writeUint8(statusbyte); //add the status byte + /************************************************************************/ + + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + addPacketErrorCheck(len); //add the additional CRC error checking to the packet end + + //now transmit the packet, set a timeout of 5000mS, wait for it to complete sending + digitalWrite(LED1, HIGH); //turn on LED as an indicator + TXPacketL = LT.transmitSXBuffer(0, (len + 2), 5000, TXpower, WAIT_TX); + digitalWrite(LED1, LOW); //turn off indicator LED + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void addPacketErrorCheck(uint8_t len) +{ + //calculate the CRC of packet sensor data + CRCvalue = LT.CRCCCITTSX(3, (len - 1), 0xFFFF); + + Serial.print(F("Calculated CRC value ")); + Serial.println(CRCvalue, HEX); + + LT.startWriteSXBuffer(len); //start the write packet again at location of CRC, past end of sensor data + LT.writeUint16(CRCvalue); //add the actual CRC value + LT.endWriteSXBuffer(); //close the packet +} + + +void readSensors() +{ + //read the sensor values into the global variables + temperature = bme280.getTemperature(); + pressure = bme280.getPressure(); + humidity = bme280.getHumidity(); + + if (BATVREADON >= 0) + { + voltage = readBatteryVoltage(); //read resistor divider across battery + } + else + { + voltage = 9999; //set a default value + } + statusbyte = 0x55; //manually set this for now, its a test +} + + +void printSensorValues() +{ + Serial.print(F("Temperature,")); + Serial.print(temperature, 1); + Serial.print(F("c,Pressure,")); + Serial.print(pressure, 0); + Serial.print(F("Pa,Humidity,")); + Serial.print(humidity); + Serial.print(F("%,Voltage,")); + Serial.print(voltage); + Serial.print(F("mV,Status,")); + Serial.print(statusbyte, HEX); + Serial.print(F(" ")); + Serial.flush(); +} + + +void sleep8seconds(uint32_t sleeps) +{ + //uses the lowpower library + uint32_t index; + + for (index = 1; index <= sleeps; index++) + { + //sleep 8 seconds + LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); + } +} + + +void sleepBME280() +{ + //write this register value to BME280 to put it to sleep + writeBME280reg(BME280_REGISTER_CONTROL, B01111100); +} + + +void normalBME280() +{ + //write this register value to BME280 to put it to read mode + writeBME280reg(BME280_REGISTER_CONTROL, B01111111); +} + + +void writeBME280reg(byte reg, byte regvalue) +{ + //write a register value to the BME280 + Wire.beginTransmission((uint8_t) BME280_ADDRESS); + Wire.write((uint8_t)reg); + Wire.write((uint8_t)regvalue); + Wire.endTransmission(); +} + + +uint16_t readBatteryVoltage() +{ + //relies on 1V1 internal reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t volts = 0; + byte index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn on MOSFET connecting resitor divider in circuit + } + + analogReference(INTERNAL); + temp = analogRead(BATTERYAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(BATTERYAD); + volts = volts + temp; + } + volts = ((volts / 5) * ADMultiplier) + DIODEMV; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn off MOSFET connecting resitor divider in circuit + } + + return volts; +} + + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); + } + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates LoRa device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + if (!bme280.init()) + { + Serial.println("BME280 Device error!"); + led_Flash(100, 15); //long very fast speed flash indicates BME280 device error + } + + Serial.println(F("Transmitter ready")); + Serial.println(); + + readSensors(); //do an initial sensor read +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/17_Sensor_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/17_Sensor_Transmitter/Settings.h new file mode 100644 index 0000000..b01a732 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/17_Sensor_Transmitter/Settings.h @@ -0,0 +1,55 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 17/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +// ******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 8 //On board LED, high for on + +#define BATVREADON 8 //when high turns on the resistor divider to measure voltage, -1 if not used +#define BATTERYAD A7 //Resitor divider for battery connected here, -1 if not used +#define ADMultiplier 10.00 //adjustment to convert AD value read into mV of battery voltage +#define DIODEMV 98 //mV voltage drop accross diode @ low idle current + + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + + +// ******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 14; //LoRa transmit power in dBm + +#define BME280_ADDRESS 0x76 //I2C bus address of BME280 +#define BME280_REGISTER_CONTROL 0xF4 //BME280 register number for power control + +const uint8_t sleeps = 112; //number of 8 second sleeps, gap between transmissions + + +// ******* Setup node addressing here ! *************** + +const uint8_t TXPacketType = Sensor1; //the packet type sent +const uint8_t TXDestination = 'B'; //the destination address of the receiver where the packet is being sent to +const uint8_t TXSource = 2; //the source address, the address of this node, where the packet came from + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino new file mode 100644 index 0000000..484b1c0 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino @@ -0,0 +1,361 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/10/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program receives a LoRa packet without using a processor buffer, the LoRa devices + internal buffer is read direct for the received sensor data. + + The sensor used in the matching '17_Sensor_Transmiter' program is a BME280 and the pressure, humidity, + and temperature are being and received. There is also a 16bit value of battery mV and and a 8 bit status + value at the end of the packet. + + When a packet is received,its printed and assuming the packet is validated, the sensor results are printed to the serial monitor + and screen. + + For the sensor data to be accepted as valid the folowing need to match; + + The 16bit CRC on the received sensor data must match the CRC value transmitted with the packet. + The packet must start with a byte that matches the packet type sent, 'Sensor1' + The RXdestination byte in the packet must match this node ID of this receiver node, defined by 'This_Node' + + In total thats 16 + 8 + 8 = 32bits of checking, so a 1:4294967296 chance (approx) that an invalid + packet is acted on and erroneous values displayed. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + With a standard Arduino Pro Mini and SSD1306 display the current consumption was 20.25mA with the + display and 16.6mA without the display. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +SX127XLT LT; + + +uint32_t RXpacketCount; //count of all packets received +uint32_t ValidPackets; //count of packets received with valid data +uint32_t RXpacketErrors; //count of all packets with errors received +bool packetisgood; + +uint8_t RXPacketL; //length of received packet +int16_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet + +uint8_t RXPacketType; +uint8_t RXDestination; +uint8_t RXSource; +float temperature; //the BME280 temperature value +float pressure; //the BME280 pressure value +uint16_t humidity; //the BME280 humididty value +uint16_t voltage; //the battery voltage value +uint8_t statusbyte; //a status byte, not currently used +uint16_t TXCRCvalue; //the CRC value of the packet data as transmitted + +//for SSD1306 +#include //get library here > https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +#define default_font u8x8_font_chroma48medium8_r + +//for SH1106 +//#include //get library here > https://github.com/olikraus/u8g2 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 +//#define default_font u8x8_font_chroma48medium8_r + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout set + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_Received_OK(); //its a valid packet LoRa wise, but it might not be a packet we want + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +void packet_Received_OK() +{ + //a LoRa packet has been received, which has passed the internal LoRa checks, including CRC, but it could be from + //an unknown source, so we need to check that its actually a sensor packet we are expecting, and has valid sensor data + + uint8_t len; + uint8_t contenterrors; //keep a count of errors found in packet + + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(",PacketsReceived,")); + + LT.startReadSXBuffer(0); + + RXPacketType = LT.readUint8(); //the packet type received + RXDestination = LT.readUint8(); //the destination address the packet was sent to + RXSource = LT.readUint8(); //the source address, where the packet came from + + /************************************************************************ + Highlighted section - this is where the actual sensor data is read from + the packet + ************************************************************************/ + temperature = LT.readFloat(); //the BME280 temperature value + pressure = LT.readFloat(); //the BME280 pressure value + humidity = LT.readUint16(); //the BME280 humididty value + voltage = LT.readUint16(); //the battery voltage value + statusbyte = LT.readUint8(); //a status byte, not currently used + /************************************************************************/ + + len = LT.endReadSXBuffer(); + + printreceptionDetails(); //print details of reception, RSSI etc + Serial.println(); + + contenterrors = checkPacketValid(len); //pass length of packet to check routine + + if (contenterrors == 0) + { + Serial.println(F(" Packet is good")); + ValidPackets++; + printSensorValues(); //print the sensor values + Serial.println(); + printPacketCounts(); //print count of valid packets and errors + displayscreen1(); + Serial.println(); + } + else + { + Serial.println(F(" Packet is not valid")); + RXpacketErrors++; + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + } +} + + +uint8_t checkPacketValid(uint8_t len) +{ + //this function checks if the packet is valid and will be displayed + + uint8_t errors = 0; + + if (RXPacketType != Sensor1) //is it a Sensor1 type packet + { + errors++; + } + + if (RXDestination != This_Node) //was the packet sent to this receiver node ? + { + errors++; + } + + if (!checkCRCvalue(len)) //is the sent CRC value of sensor data valid ? + { + errors++; + } + + Serial.println(); + Serial.print(F("Error Check Count = ")); + Serial.print(errors); + return errors; +} + + +bool checkCRCvalue(uint8_t len) +{ + uint16_t CRCSensorData; + + CRCSensorData = LT.CRCCCITTSX(3, (len-1), 0xFFFF); //calculate the CRC of packet sensor data + + Serial.print(F("(CRC of Received sensor data ")); + Serial.print(CRCSensorData, HEX); + Serial.print(F(")" )); + + TXCRCvalue = ((LT.getByteSXBuffer(len + 1) << 8) + (LT.getByteSXBuffer(len))); + + Serial.print(F("(CRC transmitted ")); + Serial.print(TXCRCvalue, HEX); + Serial.print(F(")" )); + + if (TXCRCvalue != CRCSensorData) + { + Serial.print(F(" Sensor Data Not Valid")); + return false; + } + else + { + Serial.print(F(" Sensor Data is Valid")); + return true; + } + +} + + +void printSensorValues() +{ + Serial.print(F("Temp,")); + Serial.print(temperature, 1); + Serial.print(F("c,Press,")); + Serial.print(pressure, 0); + Serial.print(F("Pa,Humidity,")); + Serial.print(humidity); + Serial.print(F("%,Voltage,")); + Serial.print(voltage); + Serial.print(F("mV,Status,")); + Serial.print(statusbyte, HEX); + Serial.print(F(",CRC,")); + Serial.print(TXCRCvalue, HEX); + Serial.flush(); +} + + +void printreceptionDetails() +{ + Serial.print(F("RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); +} + + +void printPacketCounts() +{ + Serial.print(F("ValidPackets,")); + Serial.print(ValidPackets); + Serial.print(F(",Errors,")); + Serial.print(RXpacketErrors); +} + + +void displayscreen1() +{ + //show sensor data on display + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("Sensor ")); + disp.print(RXSource); + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(temperature, 1); + disp.print(F("c")); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(pressure, 0); + disp.print(F("Pa")); + disp.clearLine(3); + disp.setCursor(0, 3); + disp.print(humidity); + disp.print(F("%")); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(voltage); + disp.print(F("mV")); + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("ValidPkts ")); + disp.print(ValidPackets); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + RXpacketErrors++; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout ")); + } + else + { + Serial.print(F("PacketError ")); + printreceptionDetails(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + Serial.println(); + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + } +} + + + + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + disp.setCursor(0, 0); + disp.print(F("Check LoRa")); + disp.setCursor(0, 1); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + disp.print(F("LoRa OK")); + led_Flash(2, 125); + } + else + { + disp.print(F("Device error")); + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/18_Sensor_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/18_Sensor_Receiver/Settings.h new file mode 100644 index 0000000..5414cdc --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/18_Sensor_Receiver/Settings.h @@ -0,0 +1,45 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, may not be in used by this sketch so they do not need to be /connected and +//should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 8 //On board LED, high for on + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + + +//*************** Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 2; //LoRa TX power + +#define packet_delay 1000 //mS delay between packets + +//******* Setup node addressing here ! *************** + +#define This_Node 'B' //this identifies this node, needs to match TXDestination on senders + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/39_LoRa_SX127x_Temperature_Read/39_LoRa_SX127x_Temperature_Read.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/39_LoRa_SX127x_Temperature_Read/39_LoRa_SX127x_Temperature_Read.ino new file mode 100644 index 0000000..812cb92 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sensor/39_LoRa_SX127x_Temperature_Read/39_LoRa_SX127x_Temperature_Read.ino @@ -0,0 +1,103 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 21/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ +/******************************************************************************************************* + Program Operation - This program reads the internal temperature sensor in the SX127X range of devices. + The temeprature sensor needs calibrating, so run a test, check what the error is (could be +\- 10C or + more) and calculate the value for the temperature_compensate constant. This constant will be different + for each individual device. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +//These are the pin definitions for one of the Tracker boards, be sure to change them to match +//your own setup. + +#define NSS 10 //SX127X device select +#define NRESET 9 //SX127X reset pin +#define LED1 8 //for on board LED, put high for on + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +#include +#include //load the appropriate library + +SX127XLT LT; + +const int8_t temperature_compensate = 0; //value, degrees centigrade, to add to read temperature for calibration. Can be negative + //this compensate value will be different for each LoRa device instance, so best to + //label and record values for each device + + +void loop() +{ + int8_t temperature_register; + LT.resetDevice(); + + temperature_register = LT.getDeviceTemperature(); + + Serial.print(F("Temperature Register Raw ")); + Serial.println(temperature_register); + Serial.print(F("Temperature Compensated ")); + Serial.print(temperature_register + temperature_compensate); + Serial.println(F("c")); + Serial.println(); + delay(2000); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("39_LoRa_SX127x_Temperature_Read Starting")); + + + SPI.begin(); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, -1, -1, -1, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + Serial.println(F("Temperature Sensor Ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/59_Play_Star_Wars_Tune.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/59_Play_Star_Wars_Tune.ino new file mode 100644 index 0000000..48661f2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/59_Play_Star_Wars_Tune.ino @@ -0,0 +1,175 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - A silly program really, but does demonstrate that you can shift a carrier generated + by the LoRa device in FSK mode fast enough to play audio tones that can be picked up on an FM UHF + handheld receiver. The tones are not true FM but the receiver does not know that. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the settings file, frequencies etc +#include "pitches.h" //lookup file for notes + +SX127XLT LT; //create a library class instance called LT + + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.println(F("PlayTune> ")); + Serial.println(); + + playpart1(); + + playpart2(); + + LT.toneFM(NOTE_F4, 250, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_GS4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 350, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 375, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_E5, 650, deviation, adjustfreq, TXpower); + + delay(500); + + playpart2(); + + LT.toneFM(NOTE_F4, 250, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_GS4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 375, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 375, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 650, deviation, adjustfreq, TXpower); + + LT.setMode(MODE_STDBY_RC); //turns off carrier + + Serial.println(); + + delay(2000); //have a delay between packets +} + + +void playpart1() +{ + LT.toneFM(NOTE_A4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 350, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 150, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 350, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 150, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 650, deviation, adjustfreq, TXpower); + + delay(500); + + LT.toneFM(NOTE_E5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_E5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_E5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F5, 350, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 150, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_GS4, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F4, 350, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 150, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 650, deviation, adjustfreq, TXpower); + + delay(500); +} + + +void playpart2() +{ + LT.toneFM(NOTE_A5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 300, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A4, 150, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_A5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_GS5, 325, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_G5, 175, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_FS5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_FS5, 250, deviation, adjustfreq, TXpower); + + delay(500); + + LT.toneFM(NOTE_AS4, 250, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_DS5, 500, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_F5, 325, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_CS5, 175, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_AS4, 125, deviation, adjustfreq, TXpower); + LT.toneFM(NOTE_C5, 250, deviation, adjustfreq, TXpower); + + delay(500); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("59_Play_Star_Wars_Tune Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupDirect(Frequency, Offset); + Serial.print(F("Tone Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/Settings.h new file mode 100644 index 0000000..5fbdee8 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/Settings.h @@ -0,0 +1,32 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2 are not used by this particular sketch so they are set to -1 and not connected. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup Direct Modem Parameters Here ! *************** + +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes +const uint16_t deviation = 10000; //deviation in hz, total frequency shift low to high +const float adjustfreq = 0.9; //adjustment to tone frequency + +const int8_t TXpower = 10; //LoRa transmit power in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/pitches.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/pitches.h new file mode 100644 index 0000000..2f733c7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Silly/59_Play_Star_Wars_Tune/pitches.h @@ -0,0 +1,94 @@ +//File from https://www.arduino.cc/en/Tutorial/toneMelody +//This note table was originally written by Brett Hagman, on whose work the Arduino tone() command was based. + +#define NOTE_B0 31 +#define NOTE_C1 33 +#define NOTE_CS1 35 +#define NOTE_D1 37 +#define NOTE_DS1 39 +#define NOTE_E1 41 +#define NOTE_F1 44 +#define NOTE_FS1 46 +#define NOTE_G1 49 +#define NOTE_GS1 52 +#define NOTE_A1 55 +#define NOTE_AS1 58 +#define NOTE_B1 62 +#define NOTE_C2 65 +#define NOTE_CS2 69 +#define NOTE_D2 73 +#define NOTE_DS2 78 +#define NOTE_E2 82 +#define NOTE_F2 87 +#define NOTE_FS2 93 +#define NOTE_G2 98 +#define NOTE_GS2 104 +#define NOTE_A2 110 +#define NOTE_AS2 117 +#define NOTE_B2 123 +#define NOTE_C3 131 +#define NOTE_CS3 139 +#define NOTE_D3 147 +#define NOTE_DS3 156 +#define NOTE_E3 165 +#define NOTE_F3 175 +#define NOTE_FS3 185 +#define NOTE_G3 196 +#define NOTE_GS3 208 +#define NOTE_A3 220 +#define NOTE_AS3 233 +#define NOTE_B3 247 +#define NOTE_C4 262 +#define NOTE_CS4 277 +#define NOTE_D4 294 +#define NOTE_DS4 311 +#define NOTE_E4 330 +#define NOTE_F4 349 +#define NOTE_FS4 370 +#define NOTE_G4 392 +#define NOTE_GS4 415 +#define NOTE_A4 440 +#define NOTE_AS4 466 +#define NOTE_B4 494 +#define NOTE_C5 523 +#define NOTE_CS5 554 +#define NOTE_D5 587 +#define NOTE_DS5 622 +#define NOTE_E5 659 +#define NOTE_F5 698 +#define NOTE_FS5 740 +#define NOTE_G5 784 +#define NOTE_GS5 831 +#define NOTE_A5 880 +#define NOTE_AS5 932 +#define NOTE_B5 988 +#define NOTE_C6 1047 +#define NOTE_CS6 1109 +#define NOTE_D6 1175 +#define NOTE_DS6 1245 +#define NOTE_E6 1319 +#define NOTE_F6 1397 +#define NOTE_FS6 1480 +#define NOTE_G6 1568 +#define NOTE_GS6 1661 +#define NOTE_A6 1760 +#define NOTE_AS6 1865 +#define NOTE_B6 1976 +#define NOTE_C7 2093 +#define NOTE_CS7 2217 +#define NOTE_D7 2349 +#define NOTE_DS7 2489 +#define NOTE_E7 2637 +#define NOTE_F7 2794 +#define NOTE_FS7 2960 +#define NOTE_G7 3136 +#define NOTE_GS7 3322 +#define NOTE_A7 3520 +#define NOTE_AS7 3729 +#define NOTE_B7 3951 +#define NOTE_C8 4186 +#define NOTE_CS8 4435 +#define NOTE_D8 4699 +#define NOTE_DS8 4978 + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino new file mode 100644 index 0000000..b9c286d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino @@ -0,0 +1,242 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program tests the sleep mode and register retention of the lora device in sleep + mode, it assumes an Atmel ATMega328P processor is in use. The LoRa settings to use are specified in the + 'Settings.h' file. + + A packet is sent, containing the text 'Before Device Sleep' and the LoRa device and Atmel processor are put + to sleep. The processor watchdog timer should wakeup the processor in 15 seconds (approx) and register + values should be retained. The device then attempts to transmit another packet 'After Device Sleep' + without re-loading all the LoRa settings. The receiver should see 'After Device Sleep' for the first + packet and 'After Device Sleep' for the second. + + Tested on a 'bare bones' ATmega328P board, the current in sleep mode was 6.5uA. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //watchdog timer library, integral to Arduino IDE +#include +#include //get the library here; https://github.com/rocketscream/Low-Power + +#include +#include "Settings.h" + +SX127XLT LT; + +bool SendOK; +int8_t TestPower; +uint8_t TXPacketL; + + +void loop() +{ + digitalWrite(LED1, HIGH); + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("TestPacket1> ")); + Serial.flush(); + + if (Send_Test_Packet1()) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + Serial.println(); + delay(packet_delay); + + LT.setSleep(CONFIGURATION_RETENTION); //preserve register settings in sleep. + Serial.println(F("Sleeping zzzzz....")); + Serial.println(); + Serial.flush(); + digitalWrite(LED1, LOW); + + sleep1second(15); //goto sleep for 15 seconds + + Serial.println(F("Awake !")); + Serial.flush(); + digitalWrite(LED1, HIGH); + LT.wake(); + + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("TestPacket2> ")); + Serial.flush(); + + if (Send_Test_Packet2()) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + Serial.println(); + delay(packet_delay); +} + + +void sleep1second(uint32_t sleeps) +{ + //uses the lowpower library + uint32_t index; + + for (index = 1; index <= sleeps; index++) + { + LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF); //sleep in 1 second steps + } +} + + +void packet_is_OK() +{ + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes SentOK")); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F("SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); //this leaves the LED on slightly longer for a packet error +} + + +bool Send_Test_Packet1() +{ + uint8_t bufffersize; + + uint8_t buff[] = "Before Device Sleep"; + TXPacketL = sizeof(buff); + buff[TXPacketL - 1] = '*'; + + if (sizeof(buff) > TXBUFFER_SIZE) //check that defined buffer is not larger than TX_BUFFER + { + bufffersize = TXBUFFER_SIZE; + } + else + { + bufffersize = sizeof(buff); + } + + TXPacketL = bufffersize; + + LT.printASCIIPacket( (uint8_t*) buff, bufffersize); + digitalWrite(LED1, HIGH); + + if (LT.transmit( (uint8_t*) buff, TXPacketL, 10000, TXpower, WAIT_TX)) + { + digitalWrite(LED1, LOW); + return true; + } + else + { + return false; + } +} + + +bool Send_Test_Packet2() +{ + uint8_t bufffersize; + + uint8_t buff[] = "After Device Sleep"; + TXPacketL = sizeof(buff); + buff[TXPacketL - 1] = '*'; + + if (sizeof(buff) > TXBUFFER_SIZE) //check that defined buffer is not larger than TX_BUFFER + { + bufffersize = TXBUFFER_SIZE; + } + else + { + bufffersize = sizeof(buff); + } + + TXPacketL = bufffersize; + + LT.printASCIIPacket( (uint8_t*) buff, bufffersize); + digitalWrite(LED1, HIGH); + + if (LT.transmit( (uint8_t*) buff, TXPacketL, 10000, TXpower, WAIT_TX)) + { + digitalWrite(LED1, LOW); + return true; + } + else + { + return false; + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("5_LoRa_TX_Sleep_Timed_Wakeup_Atmel Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.print(F("Transmitter ready - TXBUFFER_SIZE ")); + Serial.println(TXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h new file mode 100644 index 0000000..cdc4325 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h @@ -0,0 +1,43 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define BUZZER 4 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define TXBUFFER_SIZE 32 //RX buffer size + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/62_LoRa_Wake_on_RX_Atmel.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/62_LoRa_Wake_on_RX_Atmel.ino new file mode 100644 index 0000000..ad6fe6a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/62_LoRa_Wake_on_RX_Atmel.ino @@ -0,0 +1,224 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + When the program starts the LoRa device is setup to recieve packets with pin DIO0 set to go high when a + packet arrives. The receiver remains powered (it cannot receive otherwise) and the processor + (Atmel ATMega328P or 1284P) is put to sleep. When pin DIO0 does go high, indicating a packet is received, + the processor wakes up and prints the packet. It then goes back to sleep. + + There is a printout of the valid packets received, these are assumed to be in ASCII printable text. + The LED will flash for each packet received and the buzzer will sound,if fitted. + + Tested on a 'bare bones' ATmega328P board, the current in sleep mode was 13.07mA. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include +#include +#include "Settings.h" + +#include +#include +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +#include + +SX127XLT LT; + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create a buffer for the received packet + +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 0, NO_WAIT); //setup LoRa device for receive with no timeout + + Serial.println(F("Waiting for RX - Sleeping")); + Serial.flush(); + + attachInterrupt(digitalPinToInterrupt(DIO0), wakeUp, HIGH); + + atmelSleepPermanent(); //sleep the processor + + detachInterrupt(digitalPinToInterrupt(DIO0)); + + //something has happened ? + Serial.println(F("Awake")); + digitalWrite(LED1, HIGH); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + RXPacketL = LT.readPacket(RXBUFFER, RXBUFFER_SIZE); //now read in the received packet to the RX buffer + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + + digitalWrite(BUZZER, LOW); + } + Serial.println(); +} + + +void wakeUp() +{ + //handler for the interrupt +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); + RXpacketCount++; + + RXPacketL = LT.readPacket(RXBUFFER, RXBUFFER_SIZE); //now read in the received packet to the RX buffer + + Serial.print(F("Packet> ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + Serial.println(); + led_Flash(2, 125); //LED flash for approx 10 seconds +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.println(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + Serial.println(); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("62_LoRa_Wake_on_RX_Atmel Starting")); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("Radio Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/Settings.h new file mode 100644 index 0000000..cd4bf39 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/62_LoRa_Wake_on_RX_Atmel/Settings.h @@ -0,0 +1,43 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/06/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define BUZZER -1 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size +const uint16_t packetCRCcheck = 0x3F83; //CRC to check RX packet for +const uint8_t packetCRClengthcheck = 23; //packet length to check for + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/6_LoRa_RX_and_Sleep_Atmel.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/6_LoRa_RX_and_Sleep_Atmel.ino new file mode 100644 index 0000000..37a4f8a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/6_LoRa_RX_and_Sleep_Atmel.ino @@ -0,0 +1,247 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + When the program starts the LoRa device is setup to recieve packets with pin DIO0 set to go high when a + packet arrives. The receiver remains powered (it cannot receive otherwise) and the processor + (Atmel ATMega328P or 1284P) is put to sleep. When pin DIO0 does go high, indicating a packet is received, + the processor wakes up and prints the packet. It then goes back to sleep. + + There is a printout of the valid packets received, these are assumed to be in ASCII printable text. + The LED will flash for each packet received and the buzzer will sound,if fitted. + + Tested on a 'bare bones' ATmega328P board, the current in sleep mode was 12.26mA. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" + +#include +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +SX127XLT LT; + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create a buffer for the received packet + +uint8_t RXPacketL; //stores length of packet received +int16_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + LT.fillSXBuffer(0, 13, '#'); //make sure the first part of FIFO is cleared, so we can tell its a fresh packet + + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 0, NO_WAIT); //setup LoRa device for receive and continue + + //receive is setup + + Serial.println(F("Going to sleep zzzz")); + Serial.println(); + Serial.flush(); //make sure all serial has gone, it can wake up processor + + sleep_permanent(); //put processor to sleep, with LoRa device listening, should + //wakeup when DIO0 goes high + + Serial.println(F("Awake !!!!")); + digitalWrite(LED1, HIGH); //something has happened ? + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + RXPacketL = LT.readPacket(RXBUFFER, RXBUFFER_SIZE); //now read in the received packet to the RX buffer + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + + digitalWrite(BUZZER, LOW); + } + + Serial.println(); +} + + +void sleep_permanent() +{ + LT.clearIrqStatus(IRQ_RADIO_ALL); //ensure the DIO0 low is cleared, otherwise there could be an immediate wakeup + attachPCINT(digitalPinToPCINT(DIO0), wakeUp, HIGH); //This is a hardware interrupt, the LoRa device is set for DIOo goes high on RXdone + ADCSRA = 0; //disable ADC + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + + MCUCR = bit (BODS) | bit (BODSE); //turn on brown-out enable select + MCUCR = bit (BODS); //this must be done within 4 clock cycles of above + interrupts (); //guarantees next instruction executed + + sleep_cpu (); //sleep within 3 clock cycles of above + + /* wake up here */ + + sleep_disable(); + + detachPCINT(digitalPinToPCINT(DIO0)); +} + + +void wakeUp() +{ + //handler for the interrupt +} + + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); + RXpacketCount++; + + RXPacketL = LT.readPacket(RXBUFFER, RXBUFFER_SIZE); //now read in the received packet to the RX buffer + + Serial.print(F("Packet> ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("6_LoRa_RX_and_Sleep_Atmel Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("Radio Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); + +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/Settings.h new file mode 100644 index 0000000..d2ce90e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/6_LoRa_RX_and_Sleep_Atmel/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define BUZZER 4 //pin for buzzer, on when logic high + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel.ino new file mode 100644 index 0000000..8560939 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel.ino @@ -0,0 +1,260 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program tests the sleep mode and register retention of the lora device in sleep + mode, it assumes an Atmel ATMega328P processor is in use. The LoRa settings to use are specified in + the 'Settings.h' file. + + A packet is sent, containing the text 'Before Device Sleep' and the lora device and Atmel processor are put + to sleep. The processor should remain asleep until the pin defined by SWITCH1 in the Settings.h file is + connected to ground and the LoRa device register values should be retained. The LoRa device then + attempts to transmit another packet 'After Device Sleep' without re-loading all the LoRa settings. + The receiver should see 'After Device Sleep' for the first packet and 'After Device Sleep' for the second. + + Tested on an bare bones ATmega328P board, the curent in sleep mode was 2.4uA. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include + +#include +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +#include +#include "Settings.h" + +SX127XLT LT; + +uint8_t TXPacketL; + + +void loop() +{ + digitalWrite(LED1, HIGH); + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("TestPacket1> ")); + Serial.flush(); + + if (Send_Test_Packet1()) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + Serial.println(); + delay(packet_delay); + + LT.setSleep(CONFIGURATION_RETENTION); //preserve register settings in sleep. + Serial.println(F("Sleeping zzzzz....")); + Serial.println(); + Serial.flush(); + digitalWrite(LED1, LOW); + + sleep_permanent(); //goto sleep till woken up by switch press + + Serial.println(F("Awake !")); + Serial.flush(); + digitalWrite(LED1, HIGH); + LT.wake(); + + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("TestPacket2> ")); + Serial.flush(); + + if (Send_Test_Packet2()) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + Serial.println(); + delay(packet_delay); +} + + +void sleep_permanent() +{ + attachPCINT(digitalPinToPCINT(SWITCH1), wakeUp, LOW); //This is a hardware interrupt + + ADCSRA = 0; //disable ADC + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + + //turn off brown-out enable in software + MCUCR = bit (BODS) | bit (BODSE); //turn on brown-out enable select + MCUCR = bit (BODS); //this must be done within 4 clock cycles of above + interrupts (); //guarantees next instruction executed + + sleep_cpu (); //sleep within 3 clock cycles of above + + /* wake up here */ + + sleep_disable(); + + detachPCINT(digitalPinToPCINT(SWITCH1)); +} + + +void wakeUp() +{ + //handler for the interrupt +} + + +void packet_is_OK() +{ + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes SentOK")); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F("SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); //this leaves the LED on slightly longer for a packet error +} + + +bool Send_Test_Packet1() +{ + uint8_t bufffersize; + + uint8_t buff[] = "Before Device Sleep"; + TXPacketL = sizeof(buff); + buff[TXPacketL - 1] = '*'; + + if (sizeof(buff) > TXBUFFER_SIZE) //check that defined buffer is not larger than TX_BUFFER + { + bufffersize = TXBUFFER_SIZE; + } + else + { + bufffersize = sizeof(buff); + } + + TXPacketL = bufffersize; + + LT.printASCIIPacket( (uint8_t*) buff, bufffersize); + digitalWrite(LED1, HIGH); + + if (LT.transmit( (uint8_t*) buff, TXPacketL, 10000, TXpower, WAIT_TX)) + { + digitalWrite(LED1, LOW); + return true; + } + else + { + return false; + } +} + + +bool Send_Test_Packet2() +{ + uint8_t bufffersize; + + uint8_t buff[] = "After Device Sleep"; + TXPacketL = sizeof(buff); + buff[TXPacketL - 1] = '*'; + + if (sizeof(buff) > TXBUFFER_SIZE) //check that defined buffer is not larger than TX_BUFFER + { + bufffersize = TXBUFFER_SIZE; + } + else + { + bufffersize = sizeof(buff); + } + + TXPacketL = bufffersize; + + LT.printASCIIPacket( (uint8_t*) buff, bufffersize); + digitalWrite(LED1, HIGH); + + if (LT.transmit( (uint8_t*) buff, TXPacketL, 10000, TXpower, WAIT_TX)) + { + digitalWrite(LED1, LOW); + return true; + } + else + { + return false; + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + pinMode(SWITCH1, INPUT_PULLUP); //setup switch pin, ground to activate + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("7_LoRa_TX_Sleep_Switch_Wakeup_Atmel Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa device found")); + led_Flash(2, 125); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.print(F("Transmitter ready - TXBUFFER_SIZE ")); + Serial.println(TXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel/Settings.h new file mode 100644 index 0000000..b358b97 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/7_LoRa_TX_Sleep_Switch_Wakeup_Atmel/Settings.h @@ -0,0 +1,43 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 pin on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define SWITCH1 2 //switch pin, used to wake processor up + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting, normally set to auto + +const int8_t TXpower = 2; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define TXBUFFER_SIZE 32 //RX buffer size + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/Atmel Watchdog Sleep Time.jpg b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/Atmel Watchdog Sleep Time.jpg new file mode 100644 index 0000000000000000000000000000000000000000..04ec92d9cec8c863014fa405582dd8e1a74ca8e9 GIT binary patch literal 43600 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 literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/Bare_Bones_Arduino_ATMega328.pdf b/lib/SX12XX-LoRa/examples/SX127x_examples/Sleep/Bare_Bones_Arduino_ATMega328.pdf new file mode 100644 index 0000000000000000000000000000000000000000..57851e276fb5095b726264cab8e7e452a92371d6 GIT binary patch literal 35195 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 +#include + +#include "Settings.h" +#include + +SX127XLT LT; + +#include //get library here > http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#ifdef USESOFTSERIALGPS +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#endif + +#ifdef USEHARDWARESERIALGPS +#define GPSserial HARDWARESERIALPORT +#endif + + +uint8_t TXStatus = 0; //used to store current status flag bits of Tracker transmitter (TX) +uint8_t TXPacketL; //length of LoRa packet (TX) +float TXLat; //Latitude from GPS on Tracker transmitter (TX) +float TXLon; //Longitude from GPS on Tracker transmitter (TX) +float TXAlt; //Altitude from GPS on Tracker transmitter (TX) +uint8_t TXSats; //number of GPS satellites seen (TX) +uint32_t TXHdop; //HDOP from GPS on Tracker transmitter (TX) +uint16_t TXVolts; //Volts (battery) level on Tracker transmitter (TX) +uint32_t TXGPSFixTime; //GPS fix time in hot fix mode of GPS on Tracker transmitter (TX) +uint32_t TXPacketCount, TXErrorsCount; //keep count of OK packets and send errors + + +void loop() +{ + + if (gpsWaitFix(WaitGPSFixSeconds)) + { + sendLocation(TXLat, TXLon, TXAlt, TXHdop, TXGPSFixTime); + Serial.println(); + Serial.print(F("Waiting ")); + Serial.print(Sleepsecs); + Serial.println(F("s")); + delay(Sleepsecs * 1000); //this sleep is used to set overall transmission cycle time + } + else + { + send_Command(NoFix); //send notification of no GPS fix. + } +} + + +bool gpsWaitFix(uint32_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for good fix + uint32_t startmS, waitmS, GPSonTime; + bool GPSfix = false; + float tempfloat; + uint8_t GPSchar; + + GPSonTime = millis(); + GPSserial.begin(9600); //start GPSserial + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.println(F("s")); + + waitmS = waitSecs * 1000; //convert seconds wait into mS + startmS = millis(); + + while ((uint32_t) (millis() - startmS) < waitmS) + { + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + Serial.write(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated() && gps.date.isUpdated()) + { + GPSfix = true; + Serial.println(); + Serial.print(F("Have GPS Fix ")); + TXGPSFixTime = millis() - GPSonTime; + Serial.print(TXGPSFixTime); + Serial.println(F("mS")); + + TXLat = gps.location.lat(); + TXLon = gps.location.lng(); + TXAlt = gps.altitude.meters(); + TXSats = gps.satellites.value(); + TXHdop = gps.hdop.value(); + tempfloat = ( (float) TXHdop / 100); + + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F(",")); + Serial.print(TXSats); + Serial.print(F(",")); + Serial.print(tempfloat, 2); + Serial.println(); + + break; //exit while loop reading GPS + } + } + + //if here then there has either been a fix or no fix and a timeout + + if (GPSfix) + { + setStatusByte(GPSFix, 1); //set status bit to flag a GPS fix + } + else + { + setStatusByte(GPSFix, 0); //set status bit to flag no fix + Serial.println(); + Serial.println(F("Timeout - No GPSFix")); + Serial.println(); + GPSfix = false; + } + + GPSserial.end(); //serial RX interrupts interfere with SPI, so stop GPSserial + return GPSfix; +} + + +void sendLocation(float Lat, float Lon, float Alt, uint32_t Hdop, uint32_t fixtime) +{ + uint8_t len; + uint16_t IRQStatus; + + Serial.print(F("Send Location")); + + TXVolts = readSupplyVoltage(); //get the latest supply\battery volts + + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeUint8(LocationPacket); //identify type of packet + LT.writeUint8(Broadcast); //who is the packet sent too + LT.writeUint8(ThisNode); //tells receiver where is packet from + LT.writeFloat(Lat); //add latitude + LT.writeFloat(Lon); //add longitude + LT.writeFloat(Alt); //add altitude + LT.writeUint8(TXSats); //add number of satellites + LT.writeUint32(Hdop); //add hdop + LT.writeUint8(TXStatus); //add tracker status + LT.writeUint32(fixtime); //add GPS fix time in mS + LT.writeUint16(TXVolts); //add tracker supply volts + LT.writeUint32(millis()); //add uptime in mS + len = LT.endWriteSXBuffer(); //close buffer write + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); + digitalWrite(LED1, LOW); + + if (TXPacketL) + { + TXPacketCount++; + Serial.println(F(" - Done ")); + Serial.print(F("SentOK,")); + Serial.print(TXPacketCount); + Serial.print(F(",Errors,")); + Serial.println(TXErrorsCount); + } + else + { + //if here there was an error transmitting packet + TXErrorsCount++; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set + Serial.println(); + } +} + + +void setStatusByte(uint8_t bitnum, uint8_t bitval) +{ + //program the status byte + + if (bitval == 0) + { + bitClear(TXStatus, bitnum); + } + else + { + bitSet(TXStatus, bitnum); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //flash LED to show tracker is alive + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void send_Command(char cmd) +{ + bool SendOK; + uint8_t len; + + Serial.print(F("Send Cmd ")); + Serial.write(cmd); + + LT.startWriteSXBuffer(0); + LT.writeUint8(cmd); //packet addressing used indentify type of packet + LT.writeUint8(Broadcast); //who is the packet sent to + LT.writeUint8(ThisNode); //where is packet from + LT.writeUint16(TXVolts); + len = LT.endWriteSXBuffer(); + + digitalWrite(LED1, HIGH); + SendOK = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); //timeout set at 10 seconds + digitalWrite(LED1, LOW); + + if (SendOK) + { + Serial.println(F(" - Done")); + } + else + { + Serial.println(F(" - Error")); + } +} + + +uint16_t readSupplyVoltage() +{ + //relies on 1V internal reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t voltage = 0; + uint8_t index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn on MOSFET connecting resitor divider in circuit + } + + analogReference(INTERNAL); + temp = analogRead(SupplyAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(SupplyAD); + voltage = voltage + temp; + } + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn off MOSFET connecting resitor divider in circuit + } + + + voltage = ((voltage / 5) * ADMultiplier) + DIODEMV; + return voltage; +} + + +void GPSON() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void GPSTest() +{ + uint32_t startmS; + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < 2000) //allows for millis() overflow + { + if (GPSserial.available() > 0) + { + Serial.write(GPSserial.read()); + } + } + Serial.println(); + Serial.println(); + Serial.flush(); +} + + +void setup() +{ + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + GPSON(); + } + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); + } + + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(115200); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("23_GPS_Tracker_Transmitter Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + + TXVolts = readSupplyVoltage(); + + Serial.print(F("Supply ")); + Serial.print(TXVolts); + Serial.println(F("mV")); + + send_Command(PowerUp); //send power up command, includes supply mV + + Serial.println(F("Startup GPS check")); + GPSserial.begin(9600); + GPSTest(); + Serial.println(); + Serial.println(); + + Serial.println(F("Wait for first GPS fix")); + gpsWaitFix(WaitFirstGPSFixSeconds); + + sendLocation(TXLat, TXLon, TXAlt, TXHdop, TXGPSFixTime); +} diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h new file mode 100644 index 0000000..7bff166 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h @@ -0,0 +1,65 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done + +#define GPSPOWER -1 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define LED1 8 //On board LED, high for on +#define SupplyAD A7 //pin for reading supply\battery voltage +#define BATVREADON 8 //turns on battery resistor divider, high for on, -1 if not used + +const float ADMultiplier = 10.0; //multiplier for supply volts calculation +#define DIODEMV 98 //mV voltage drop accross diode at approx 8mA + + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa TX power + +#define ThisNode 'T' //a character that identifies this tracker + +//************************************************************************************************** +// GPS Settings +//************************************************************************************************** + +#define USESOFTSERIALGPS //if your using software serial for the GPS, enable this define +//#define USEHARDWARESERIALGPS //if your using hardware serial for the GPS, enable this define +//#define HARDWARESERIALPORT Serial2 //if your using hardware serial for the GPS, define the port here + + +#define GPSBaud 9600 //GPS Baud rate + +#define WaitGPSFixSeconds 30 //time in seconds to wait for a new GPS fix +#define WaitFirstGPSFixSeconds 120 //time to seconds to wait for the first GPS fix at startup +#define Sleepsecs 15 //seconds between transmissions, this delay is used to set overall transmission cycle time + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino new file mode 100644 index 0000000..b892edd --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino @@ -0,0 +1,323 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 28/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is an basic receiver for the '23_Simple_GPS_Tracker_Transmitter' program. + The program reads the received packet from the tracker transmitter and displays the results on + the serial monitor. The LoRa and frequency settings provided in the Settings.h file must + match those used by the transmitter. + + The program receives direct from the LoRa devices internal buffer. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#define Program_Version "V1.2" + +#include +#include + +SX127XLT LT; + +#include "Settings.h" +#include + + +uint32_t RXpacketCount; //count of received packets + +uint8_t RXPacketL; //length of received packet +int16_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet +uint8_t PacketType; //for packet addressing, identifies packet type +uint8_t Destination; //for packet addressing, identifies the destination (receiving) node +uint8_t Source; //for packet addressing, identifies the source (transmiting) node +uint8_t TXStatus; //A status byte +float TXLat; //latitude +float TXLon; //longitude +float TXAlt; //altitude +uint32_t TXHdop; //HDOP, indication of fix quality, horizontal dilution of precision, low is good +uint32_t TXGPSFixTime; //time in mS for fix +uint16_t TXVolts; //supply\battery voltage +uint8_t TXSats; //number of sattelites in use +uint32_t TXupTimemS; //up time of TX in mS + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort + + digitalWrite(LED1, HIGH); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + } + + Serial.println(); +} + + +void readPacketAddressing() +{ + //the transmitter is using packet addressing, so read in the details + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + LT.endReadSXBuffer(); +} + + +void packet_is_OK() +{ + float tempHdop; + + RXpacketCount++; + Serial.print(F("Packet OK > ")); + + readPacketAddressing(); + + if (PacketType == PowerUp) + { + LT.startReadSXBuffer(0); + LT.readUint8(); //read byte from FIFO, not used + LT.readUint8(); //read byte from FIFO, not used + LT.readUint8(); //read byte from FIFO, not used + TXVolts = LT.readUint16(); + LT.endReadSXBuffer(); + Serial.print(F("Tracker transmitter powerup - battery ")); + Serial.print(TXVolts); + Serial.print(F("mV")); + } + + + if (PacketType == LocationPacket) + { + //packet has been received, now read from the SX12XX FIFO in the correct order. + Serial.print(F("LocationPacket ")); + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readFloat(); + TXSats = LT.readUint8(); + TXHdop = LT.readUint32(); + TXStatus = LT.readUint8(); + TXGPSFixTime = LT.readUint32(); + TXVolts = LT.readUint16(); + TXupTimemS = LT.readUint32(); + RXPacketL = LT.endReadSXBuffer(); + + tempHdop = ( (float) TXHdop / 100); //need to convert Hdop read from GPS as uint32_t to a float for display + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F("m,")); + Serial.print(TXSats); + Serial.print(F(",")); + Serial.print(tempHdop, 2); + Serial.print(F(",")); + Serial.print(TXStatus); + Serial.print(F(",")); + Serial.print(TXGPSFixTime); + Serial.print(F("mS,")); + Serial.print(TXVolts); + Serial.print(F("mV,")); + Serial.print((TXupTimemS/1000)); + Serial.print(F("s,")); + printpacketDetails(); + return; + } + + if (PacketType == LocationBinaryPacket) + { + //packet from locator has been received, now read from the SX12XX FIFO in the correct order. + Serial.print(F("LocationBinaryPacket ")); + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readInt16(); + TXStatus = LT.readUint8(); + RXPacketL = LT.endReadSXBuffer(); + + tempHdop = ( (float) TXHdop / 100); //need to convert Hdop read from GPS as uint32_t to a float for display + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 0); + Serial.print(F("m,")); + Serial.print(TXStatus); + printpacketDetails(); + return; + } + + if (PacketType == NoFix) + { + Serial.print(F("No Tracker GPS fix ")); + printpacketDetails(); + return; + } + +} + + +void printpacketDetails() +{ + uint16_t IRQStatus; + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Packets,")); + Serial.print(RXpacketCount); + + Serial.print(F(",Length,")); + Serial.print(RXPacketL); + IRQStatus = LT.readIrqStatus(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F("PacketError,RSSI")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(115200); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("24_GPS_Tracker_Receiver Starting")); + + if (BUZZER >= 0) + { + pinMode(BUZZER, OUTPUT); + Serial.println(F("BUZZER Enabled")); + } + else + { + Serial.println(F("BUZZER Not Enabled")); + } + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DEVICE)) + { + Serial.println(F("LoRa device found")); + led_Flash(2, 125); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h new file mode 100644 index 0000000..8f20a16 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h @@ -0,0 +1,35 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 22/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done + +#define LED1 8 //On board LED, high for on +#define BUZZER -1 //Buzzer if fitted, high for on. Set to -1 if not used + +#define DEVICE DEVICE_SX1278 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino new file mode 100644 index 0000000..c85ec22 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino @@ -0,0 +1,634 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 28/12/20 + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* +Program Operation - This program is an example of a functional GPS tracker receiver using lora. +It is capable of picking up the trackers location packets from many kilometres away with only basic antennas. + +The program receives the location packets from the remote tracker transmitter and writes them on an OLED +display and also prints the information to the Arduino IDE serial monitor. The program can read a locally +attached GPS and when that has a fix, will display the distance and direction to the remote tracker. + +The program writes direct to the lora devices internal buffer, no memory buffer is used. The lora settings +are configured in the Settings.h file. + +The receiver recognises two types of tracker packet, the one from the matching program '23_GPS_Tracker_Transmitter' +(LocationPacket, 27 bytes) which causes these fields to be printed to the serial monitor; + +Latitude, Longitude, Altitude, Satellites, HDOP, TrackerStatusByte, GPS Fixtime, Battery mV, Distance, Direction, +Distance, Direction, PacketRSSI, PacketSNR, NumberPackets, PacketLength, IRQRegister. + +This is a long packet which at the long range LoRa settings takes just over 3 seconds to transmit. + +The receiver also recognises a much shorter location only packet (LocationBinaryPacket, 11 bytes) and when +received this is printed to the serial monitor; + +Latitude, Longitude, Altitude, TrackerStatusByte, Distance, Direction, PacketRSSI, PacketSNR, NumberPackets, +PacketLength, IRQRegister. + +Most of the tracker information (for both types of packet) is shown on the OLED display. If there has been a +tracker transmitter GPS fix the number\identifier of that tracker is shown on row 0 right of screen and if there +is a recent local (receiver) GPS fix an 'R' is displayed row 1 right of screen. + +When the tracker transmitter starts up or is reset its sends a power up message containing the battery voltage +which is shown on the OLED and printer to the serial monitor. + +The program has the option of using a pin to control the power to the GPS, if the GPS module being used has this +feature. To use the option change the define in Settings.h; + +'#define GPSPOWER -1' from -1 to the pin number being used. Also set the GPSONSTATE and GPSOFFSTATE defines to +the appropriate logic levels. + +The program by default uses software serial to read the GPS, you can use hardware serial by commenting out this +line in the Settings.h file; + +#define USE_SOFTSERIAL_GPS + +And then defining the hardware serial port you are using, which defaults to Serial1. + +Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + + +#define Program_Version "V1.2" + +#include +#include +SX127XLT LT; + +#include "Settings.h" +#include + +#include //https://github.com/olikraus/u8g2 +//U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //standard 0.96" SSD1306 +U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //1.3" OLED often sold as 1.3" SSD1306 + + +#include //http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#ifdef USESOFTSERIALGPS +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#endif + +#ifdef USEHARDWARESERIALGPS +#define GPSserial HARDWARESERIALPORT +#endif + + +uint32_t RXpacketCount; //count of received packets +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //signal strength (RSSI) dBm of received packet +int8_t PacketSNR; //signal to noise ratio (SNR) dB of received packet +uint8_t PacketType; //for packet addressing, identifies packet type +uint8_t Destination; //for packet addressing, identifies the destination (receiving) node +uint8_t Source; //for packet addressing, identifies the source (transmiting) node +uint8_t TXStatus; //status byte from tracker transmitter +uint8_t TXSats; //number of sattelites in use +float TXLat; //latitude +float TXLon; //longitude +float TXAlt; //altitude +float RXLat; //latitude +float RXLon; //longitude +float RXAlt; //altitude +uint32_t TXHdop; //HDOP, indication of fix quality, horizontal dilution of precision, low is good +uint32_t TXGPSFixTime; //time in mS for fix +uint16_t TXVolts; //supply\battery voltage +uint16_t RXVolts; //supply\battery voltage +float TXdistance; //calculated distance to tracker +uint16_t TXdirection; //calculated direction to tracker +uint16_t RXerrors; +uint32_t TXupTimemS; //up time of TX in mS + +uint32_t LastRXGPSfixCheck; //used to record the time of the last GPS fix + +bool TXLocation = false; //set to true when at least one tracker location packet has been received +bool RXGPSfix = false; //set to true if the local GPS has a recent fix + +uint8_t FixCount = DisplayRate; //used to keep track of number of GPS fixes before display updated + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, NO_WAIT); //returns 0 if packet error of some sort + + while (!digitalRead(DIO0)) + { + readGPS(); //If the DIO pin is low, no packet arrived, so read the GPS + } + + //something has happened in receiver + digitalWrite(LED1, HIGH); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + RXPacketL = LT.readRXPacketL(); + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + } + Serial.println(); +} + + +void readGPS() +{ + if (GPSserial.available() > 0) + { + gps.encode(GPSserial.read()); + } + + if ( (uint32_t) (millis() - LastRXGPSfixCheck) > NoRXGPSfixms) + { + RXGPSfix = false; + LastRXGPSfixCheck = millis(); + dispscreen1(); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated() && gps.date.isUpdated()) + { + RXGPSfix = true; + RXLat = gps.location.lat(); + RXLon = gps.location.lng(); + RXAlt = gps.altitude.meters(); + printRXLocation(); + LastRXGPSfixCheck = millis(); + + if ( FixCount == 1) //update screen when FIX count counts down from DisplayRate to 1 + { + FixCount = DisplayRate; + dispscreen1(); + } + FixCount--; + } +} + + +bool readTXStatus(byte bitnum) +{ + return bitRead(TXStatus, bitnum); +} + + +void printRXLocation() +{ + Serial.print(F("LocalGPS ")); + Serial.print(RXLat, 5); + Serial.print(F(",")); + Serial.print(RXLon, 5); + Serial.print(F(",")); + Serial.print(RXAlt, 1); + Serial.println(); +} + + +void readPacketAddressing() +{ + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + LT.endReadSXBuffer(); +} + + +void packet_is_OK() +{ + //uint16_t IRQStatus; + float tempfloat; + + RXpacketCount++; + + readPacketAddressing(); + + if (PacketType == PowerUp) + { + LT.startReadSXBuffer(0); + LT.readUint8(); //read byte from SXBuffer, not used + LT.readUint8(); //read byte from SXBuffer, not used + LT.readUint8(); //read byte from SXBuffer, not used + TXVolts = LT.readUint16(); //read tracker transmitter voltage + LT.endReadSXBuffer(); + Serial.print(F("Tracker Powerup - Battery ")); + Serial.print(TXVolts); + Serial.println(F("mV")); + dispscreen2(); + } + + if (PacketType == LocationPacket) + { + //packet has been received, now read from the SX12XX FIFO in the correct order. + Serial.print(F("LocationPacket ")); + TXLocation = true; + LT.startReadSXBuffer(0); //start the read of received packet + PacketType = LT.readUint8(); //read in the PacketType + Destination = LT.readUint8(); //read in the Packet destination address + Source = LT.readUint8(); //read in the Packet source address + TXLat = LT.readFloat(); //read in the tracker latitude + TXLon = LT.readFloat(); //read in the tracker longitude + TXAlt = LT.readFloat(); //read in the tracker altitude + TXSats = LT.readUint8(); //read in the satellites in use by tracker GPS + TXHdop = LT.readUint32(); //read in the HDOP of tracker GPS + TXStatus = LT.readUint8(); //read in the tracker status byte + TXGPSFixTime = LT.readUint32(); //read in the last fix time of tracker GPS + TXVolts = LT.readUint16(); //read in the tracker supply\battery volts + TXupTimemS = LT.readUint32(); //read in the TX uptime in mS + RXPacketL = LT.endReadSXBuffer(); //end the read of received packet + + + if (RXGPSfix) //if there has been a local GPS fix do the distance and direction calculation + { + TXdirection = (int16_t) TinyGPSPlus::courseTo(RXLat, RXLon, TXLat, TXLon); + TXdistance = TinyGPSPlus::distanceBetween(RXLat, RXLon, TXLat, TXLon); + } + else + { + TXdistance = 0; + TXdirection = 0; + } + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F(",")); + Serial.print(TXSats); + Serial.print(F(",")); + + tempfloat = ( (float) TXHdop / 100); //need to convert Hdop read from GPS as uint32_t to a float for display + Serial.print(tempfloat, 2); + + Serial.print(F(",")); + Serial.print(TXStatus); + Serial.print(F(",")); + + Serial.print(TXGPSFixTime); + Serial.print(F("mS,")); + Serial.print(TXVolts); + Serial.print(F("mV,")); + Serial.print((TXupTimemS/1000)); + Serial.print(F("s,")); + + Serial.print(TXdistance, 0); + Serial.print(F("m,")); + Serial.print(TXdirection); + Serial.print(F("d")); + printpacketDetails(); + dispscreen1(); //and show the packet detail it on screen + return; + } + + + if (PacketType == LocationBinaryPacket) + { + //packet from locator has been received, now read from the SX12XX FIFO in the correct order. + TXLocation = true; + Serial.print(F("LocationBinaryPacket ")); + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readInt16(); + TXStatus = LT.readUint8(); + RXPacketL = LT.endReadSXBuffer(); + + if (RXGPSfix) //if there has been a local GPS fix do the distance and direction calculation + { + TXdirection = (int16_t) TinyGPSPlus::courseTo(RXLat, RXLon, TXLat, TXLon); + TXdistance = TinyGPSPlus::distanceBetween(RXLat, RXLon, TXLat, TXLon); + } + else + { + TXdistance = 0; + TXdirection = 0; + } + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 0); + Serial.print(F("m,")); + Serial.print(TXStatus); + Serial.print(F(",")); + Serial.print(TXdistance, 0); + Serial.print(F("m,")); + Serial.print(TXdirection); + Serial.print(F("d")); + printpacketDetails(); + dispscreen1(); + return; + } +} + + +void printpacketDetails() +{ + uint16_t IRQStatus; + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Packets,")); + Serial.print(RXpacketCount); + + Serial.print(F(",Length,")); + Serial.print(RXPacketL); + IRQStatus = LT.readIrqStatus(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + RXerrors++; + Serial.print(F("PacketError,RSSI")); + + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void dispscreen1() +{ + //show received packet data on display + float tempfloat; + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(TXLat, 5); + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(TXLon, 5); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(TXAlt,0); + disp.print(F("m")); + disp.clearLine(3); + disp.setCursor(0, 3); + + disp.print(F("RSSI ")); + disp.print(PacketRSSI); + disp.print(F("dBm")); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(F("SNR ")); + + if (PacketSNR > 0) + { + disp.print(F("+")); + } + + if (PacketSNR == 0) + { + disp.print(F(" ")); + } + + if (PacketSNR < 0) + { + disp.print(F("-")); + } + + disp.print(PacketSNR); + disp.print(F("dB")); + + if (PacketType == LocationPacket) + { + disp.clearLine(5); + disp.setCursor(0, 5); + tempfloat = ((float) TXVolts / 1000); + disp.print(F("Batt ")); + disp.print(tempfloat, 2); + disp.print(F("v")); + } + + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("Packets ")); + disp.print(RXpacketCount); + + disp.clearLine(7); + + if (RXGPSfix) + { + disp.setCursor(15, 1); + disp.print(F("R")); + } + else + { + disp.setCursor(15, 1); + disp.print(F(" ")); + disp.setCursor(0, 7); + disp.print(F("No Local Fix")); + } + + if (RXGPSfix && TXLocation) //only display distance and direction if have received tracker packet and have local GPS fix + { + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(TXdistance, 0); + disp.print(F("m ")); + disp.print(TXdirection); + disp.print(F("d")); + } + + if (readTXStatus(GPSFix)) + { + disp.setCursor(15, 0); + disp.write(Source); + } + +} + + +void dispscreen2() +{ + //show tracker powerup data on display + float tempfloat; + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Tracker Powerup")); + disp.setCursor(0, 1); + disp.print(F("Battery ")); + tempfloat = ((float) TXVolts / 1000); + disp.print(tempfloat, 2); + disp.print(F("v")); +} + + +void GPSON() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void GPSTest() +{ + uint32_t startmS; + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < 2000) //allows for millis() overflow + { + if (GPSserial.available() > 0) + { + Serial.write(GPSserial.read()); + } + } + Serial.println(); + Serial.println(); + Serial.flush(); +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(115200); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("25_GPS_Tracker_Receiver_With_Display_and_GPS Starting")); + + if (BUZZER >= 0) + { + pinMode(BUZZER, OUTPUT); + } + + SPI.begin(); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + + Serial.print(F("Checking LoRa device - ")); //Initialize LoRa + disp.setCursor(0, 0); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("Receiver ready")); + disp.print(F("Receiver ready")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No LoRa device responding")); + disp.print(F("No LoRa device")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + Serial.println(F("Startup GPS check")); + + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + } + + GPSON(); + GPSserial.begin(GPSBaud); + GPSTest(); + + Serial.println(); + Serial.println(); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h new file mode 100644 index 0000000..c393b14 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h @@ -0,0 +1,59 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER SWITCH1 may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define LED1 8 //On board LED, high for on +#define BUZZER -1 //Buzzer if fitted, high for on. Set to -1 if not used + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER 4 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +//************************************************************************************************** +// GPS Settings +//************************************************************************************************** + +#define USESOFTSERIALGPS //if your using software serial for the GPS, enable this define +//#define USEHARDWARESERIALGPS //if your using hardware serial for the GPS, enable this define +//#define HARDWARESERIALPORT Serial2 //if your using hardware serial for the GPS, define the port here + +#define GPSBaud 9600 //GPS Baud rate + +#define NoRXGPSfixms 15000 //max number of mS to allow before no local GPS fix flagged +#define DisplayRate 7 //when working OK the GPS will get a new fix every second or so + //this rate defines how often the display should be updated + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino new file mode 100644 index 0000000..60f8fff --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino @@ -0,0 +1,173 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program will receive a lora packet and relay (re-transmit) it. The receiving + and transmitting can use different frequencies and lora settings, although in this example they are + the same. The receiving and transmitting settings are in the 'Settings.h' file. If the relay is located + in an advantageous position, for instance on top of a tall tree, building or in an radio controlled model + then the range at which trackers or nodes on the ground can be received is considerably increased. + In these circumstances the relay may listen at a long range setting using SF12 for example and then + re-transmit back to the ground at SF7. + + For an example of the use of such a program see this report; + + https://stuartsprojects.github.io/2016/08/15/how-to-search-500-square-kilometres-in-10-minutes.html + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + + +#include +#include +#include "Settings.h" + +SX127XLT LT; + +uint8_t RXPacketL, TXPacketL; +int8_t PacketRSSI, PacketSNR; +uint16_t RXPacketErrors; + + +void loop() +{ + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout set + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + Serial.println(); +} + + +void packet_is_OK() +{ + //a packet has been received, so change to relay settings and transmit buffer + + Serial.print(F("PacketOK ")); + printreceptionDetails(); + delay(packet_delay / 2); + digitalWrite(LED1, LOW); + delay(packet_delay / 2); + + Serial.print(F(" Retransmit")); + LT.setupLoRa(RelayFrequency, RelayOffset, RelaySpreadingFactor, RelayBandwidth, RelayCodeRate, RelayOptimisation); + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitSXBuffer(0, RXPacketL, 10000, TXpower, WAIT_TX); + Serial.print(F(" - Done")); + digitalWrite(LED1, LOW); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + RXPacketErrors++; + IRQStatus = LT.readIrqStatus(); + + led_Flash(5, 50); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout ")); + } + else + { + Serial.print(F("PacketError ")); + printreceptionDetails(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + } +} + + +void printreceptionDetails() +{ + Serial.print(F("RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); +} + + +void led_Flash(uint16_t flashdelay, uint16_t flashes) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + + delay(flashdelay); + digitalWrite(LED1, HIGH); + delay(flashdelay); + digitalWrite(LED1, LOW); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + Serial.print(F("ListenSettings,")); + LT.printModemSettings(); + Serial.println(); + LT.setupLoRa(RelayFrequency, RelayOffset, RelaySpreadingFactor, RelayBandwidth, RelayCodeRate, RelayOptimisation); + Serial.print(F("RelaySettings,")); + LT.printModemSettings(); + Serial.println(); + Serial.println(F("Relay Ready")); +} + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/38_lora_Relay/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/38_lora_Relay/Settings.h new file mode 100644 index 0000000..7f60a2f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/38_lora_Relay/Settings.h @@ -0,0 +1,51 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 -1 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 +#define LED1 8 //On board LED, high for on +#define BUZZER -1 //normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa receiving parameters +const uint32_t Frequency = 434000000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +//LoRa relay (re-transmitting) parameters +const uint32_t RelayFrequency = 434000000; //frequency of transmissions +const uint32_t RelayOffset = 0; //offset frequency for calibration purposes + +const uint8_t RelayBandwidth = LORA_BW_125; //LoRa bandwidth +const uint8_t RelaySpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t RelayCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t RelayOptimisation = LDRO_AUTO; //low data rate optimisation setting + + +const int8_t TXpower = 10; //LoRa TX power in dBm + +#define packet_delay 500 //mS delay before received packet transmitted + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/67_Balloon_Tracker_Transmitter/67_Balloon_Tracker_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/67_Balloon_Tracker_Transmitter/67_Balloon_Tracker_Transmitter.ino new file mode 100644 index 0000000..4db6767 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/67_Balloon_Tracker_Transmitter/67_Balloon_Tracker_Transmitter.ino @@ -0,0 +1,807 @@ +/****************************************************************************************************** + Programs for Arduino - Copyright of the author Stuart Robinson - 28/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a tracker intended for use as a high altitude balloon (HAB) tracker. The + program sends out a standard format payload with LoRa that is compatible with the HABHUB online tracking + system. + + The HAB payload is constructed thus; + + PayloadID,Sequence,Time,Lat,Lon,Alt,Satellites,Volts,Temperature,Resets,Status,Errors,TXGPSfixms,Checksum + Field 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + + The LoRa and frequency settings can be changed in the Settings.h file. There is the option of sending + out a much shorter Search mode binary location only payload. This is intended for ground based searching + and locating. The frequency and LoRa settings of the Search mode packet can be different to the Tracker + mode used by the HAB payload. There is also the option of sending the HAB payload in FSK RTTY format, + see the Settings.h file for all the options. FSK RTTY gets sent at the same frequency as the Tracker mode + HAB packet. The LT.transmitFSKRTTY() function sends at 1 start bit, 7 data bits, no parity and 2 stop bits. + For full control of the FSK RTTY setting you can use the following alternative function; + + LT.transmitFSKRTTY(chartosend, databits, stopbits, parity, baudPerioduS, pin) + + There is a matching Balloon Tracker Receiver program which writes received data to the Serial monitor as well + as a small OLED display. + + In the Settings.h file you can set the configuration for either a Ublox GPS or a Quectel L70\L80. The GPSs + are configured for high altitude balloon mode. + + It is strongly recommended that a FRAM option is fitted for this transmitter. The sequence, resets and error + nembers are stred in non-volatile memory. This defaults to EEPROM which has a limited endurance of only + 100,000 writes, so in theory the limt is reached after the transmission of 100,000 hab packets. The use of + a FRAM will extend the life of the tracker to circa 100,000,000,000,000 transmissions. + + Changes: + 240420 - Change to work with Easy Pro Mini style modules + 300420 - Improve error detection for UBLOX GPS library + + ToDo: + + Serial monitor baud rate is set at 115200 +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include + +#include //include the appropriate library + +SX127XLT LT; //create a library class instance called LT + +#include "Settings.h" +#include "ProgramLT_Definitions.h" + +//************************************************************************************************** +// HAB tracker data - these are the variables transmitted in payload +//************************************************************************************************** +uint32_t TXSequence; //sequence number of payload +uint8_t TXHours; //Hours +uint8_t TXMinutes; //Minutes +uint8_t TXSeconds; //Seconds +float TXLat; //latitude from GPS +float TXLon; //longitude from GPS +uint16_t TXAlt; //altitude from GPS +uint8_t TXSatellites; //satellites used by GPS +uint16_t TXVolts; //measured tracker supply volts +int8_t TXTemperature; //measured temperature +uint16_t TXResets; //number of tracker resets +uint8_t TXStatus = 0; //used to store current status flag bits +uint16_t TXErrors; //number of tracker Errors +uint32_t TXGPSfixms; //fix time of GPS +//************************************************************************************************** + +uint8_t TXPacketL; //length of LoRa packet sent +uint8_t TXBUFFER[TXBUFFER_SIZE]; //buffer for packet to send + +#include Memory_Library + +#include + +#include //http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#ifdef USESOFTSERIALGPS +//#include //https://github.com/SlashDevin/NeoSWSerial +//NeoSWSerial GPSserial(RXpin, TXpin); //The NeoSWSerial library is an option to use and is more relaible + //at GPS init than software serial +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#endif + +#ifdef USEHARDWARESERIALGPS +#define GPSserial HARDWARESERIALPORT +#endif + +#ifdef USEI2CGPS +#include +#endif + + +#include GPS_Library //include previously defined GPS Library + +#include //get library here > https://github.com/PaulStoffregen/OneWire +OneWire oneWire(ONE_WIRE_BUS); //create instance of OneWire library +#include //get library here > https://github.com/milesburton/Arduino-TXTemperature-Control-Library +DallasTemperature sensor(&oneWire); //create instance of dallas library + +uint32_t GPSstartms; //start time waiting for GPS to get a fix + + +void loop() +{ + Serial.println(F("Start Loop")); + + GPSstartms = millis(); + + if (!gpsWaitFix(WaitGPSFixSeconds)) + { + GPS_OutputOff(); + sendCommand(NoFix); //report a GPS fix error + delay(1000); //give receiver enough time to report NoFix + } + Serial.println(); + + do_Transmissions(); //do the transmissions + + Serial.println(F("Sleep")); + LT.setSleep(CONFIGURATION_RETENTION); //put LoRa device to sleep, preserve lora register settings + Serial.flush(); //make sure no serial output pending before goint to sleep + + delay(SleepTimesecs * 1000); + + Serial.println(F("Wake")); + LT.wake(); //wake the LoRa device from sleep +} + + +void do_Transmissions() +{ + //this is where all the transmisions get sent + uint32_t startTimemS; + uint8_t index; + + incMemoryUint32(addr_SequenceNum); //increment Sequence number + + if (readConfigByte(SearchEnable)) + { + setSearchMode(); + TXPacketL = buildLocationOnly(TXLat, TXLon, TXAlt, TXStatus); //put location data in SX12xx buffer + Serial.print(F("Search packet > ")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt); + Serial.print(F(",")); + Serial.print(TXStatus); + digitalWrite(LED1, HIGH); + startTimemS = millis(); + TXPacketL = LT.transmitSXBuffer(0, TXPacketL, 10000, SearchTXpower, WAIT_TX); + printTXtime(startTimemS, millis()); + reportCompletion(); + Serial.println(); + } + + delay(1000); //gap between transmissions + + setTrackerMode(); + + TXPacketL = buildHABPacket(); + Serial.print(F("HAB Packet > ")); + printBuffer(TXBUFFER, (TXPacketL + 1)); //print the buffer (the packet to send) as ASCII + digitalWrite(LED1, HIGH); + startTimemS = millis(); + TXPacketL = LT.transmit(TXBUFFER, (TXPacketL + 1), 10000, TrackerTXpower, WAIT_TX); //will return packet length sent if OK, otherwise 0 if transmit error + digitalWrite(LED1, LOW); + printTXtime(startTimemS, millis()); + reportCompletion(); + Serial.println(); + + delay(1000); //gap between transmissions + + if (readConfigByte(FSKRTTYEnable)) //FSKRTTY is sent last, so that receiver has time to use AFSK upload + { + LT.setupDirect(TrackerFrequency, Offset); + LT.startFSKRTTY(FrequencyShift, NumberofPips, PipPeriodmS, PipDelaymS, LeadinmS); + + startTimemS = millis() - LeadinmS; + + Serial.print(F("FSK RTTY > $$$")); + Serial.flush(); + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + + for (index = 0; index <= (TXPacketL - 1); index++) //its TXPacketL-1 since we dont want to send the null at the end + { + LT.transmitFSKRTTY(TXBUFFER[index], BaudPerioduS, LED1); + Serial.write(TXBUFFER[index]); + } + + LT.transmitFSKRTTY(13, BaudPerioduS, LED1); //send carriage return + LT.transmitFSKRTTY(10, BaudPerioduS, LED1); //send line feed + LT.endFSKRTTY(); //stop transmitting carrier + digitalWrite(LED1, LOW); //LED off + printTXtime(startTimemS, millis()); + TXPacketL += 4; //add the two $ at beginning and CR/LF at end + reportCompletion(); + Serial.println(); + } +} + + +void printTXtime(uint32_t startmS, uint32_t endmS) +{ + Serial.print(F(" ")); + Serial.print(endmS - startmS); + Serial.print(F("mS")); +} + + +void reportCompletion() +{ + Serial.print(F(" ")); + if (TXPacketL == 0) + { + Serial.println(); + reporttransmitError(); + } + else + { + Serial.print(TXPacketL); + Serial.print(F("bytes")); + setStatusByte(LORAError, 0); + } +} + + +void printBuffer(uint8_t *buffer, uint8_t size) +{ + uint8_t index; + + for (index = 0; index < size; index++) + { + Serial.write(buffer[index]); + } +} + + +uint8_t buildHABPacket() +{ + //build the HAB tracker payload + uint16_t index, j, CRC; + uint8_t Count, len; + char LatArray[12], LonArray[12]; + + TXSequence = readMemoryUint32(addr_SequenceNum); //Sequence number is kept in non-volatile memory so it survives TXResets + TXResets = readMemoryUint16(addr_ResetCount); //reset count is kept in non-volatile memory so it survives TXResets + TXVolts = readSupplyVoltage(); + Serial.print(F("TXVolts ")); + Serial.print(TXVolts); + Serial.println(F("mV")); + TXTemperature = (int8_t) readTempDS18B20(); + TXErrors = readMemoryUint16(addr_TXErrors); + + dtostrf(TXLat, 7, 5, LatArray); //format is dtostrf(FLOAT,WIDTH,PRECISION,BUFFER); + dtostrf(TXLon, 7, 5, LonArray); //converts float to character array + + len = sizeof(TXBUFFER); + memset(TXBUFFER, 0, len); //clear array to 0s + Count = snprintf((char*) TXBUFFER, + TXBUFFER_SIZE, + "$%s,%lu,%02d:%02d:%02d,%s,%s,%d,%d,%d,%d,%d,%d,%d,%lu", + FlightID, + TXSequence, + TXHours, + TXMinutes, + TXSeconds, + LatArray, + LonArray, + TXAlt, + TXSatellites, + TXVolts, + TXTemperature, + TXResets, + TXStatus, + TXErrors, + TXGPSfixms + ); + + CRC = 0xffff; //start value for CRC16 + + for (index = 1; index < Count; index++) //element 1 is first character after $ at start (for LoRa) + { + CRC ^= (((uint16_t)TXBUFFER[index]) << 8); + for (j = 0; j < 8; j++) + { + if (CRC & 0x8000) + CRC = (CRC << 1) ^ 0x1021; + else + CRC <<= 1; + } + } + + TXBUFFER[Count++] = '*'; + TXBUFFER[Count++] = Hex((CRC >> 12) & 15); //add the checksum bytes to the end + TXBUFFER[Count++] = Hex((CRC >> 8) & 15); + TXBUFFER[Count++] = Hex((CRC >> 4) & 15); + TXBUFFER[Count] = Hex(CRC & 15); + return Count; +} + + +char Hex(uint8_t lchar) +{ + //used in CRC calculation in buildHABPacket + char Table[] = "0123456789ABCDEF"; + return Table[lchar]; +} + + +uint8_t buildLocationOnly(float Lat, float Lon, uint16_t Alt, uint8_t stat) +{ + uint8_t len; + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeUint8(LocationBinaryPacket); //identify type of packet + LT.writeUint8(Broadcast); //who is the packet sent too + LT.writeUint8(ThisNode); //tells receiver where is packet from + LT.writeFloat(Lat); //add latitude + LT.writeFloat(Lon); //add longitude + LT.writeInt16(Alt); //add altitude + LT.writeUint8(stat); //add tracker status + len = LT.endWriteSXBuffer(); //close buffer write + return len; +} + + +void reporttransmitError() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F("TXError,")); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set + incMemoryUint16(addr_TXErrors); //increase the error count + setStatusByte(LORAError, 1); +} + + +void incMemoryUint32(uint32_t addr) +{ + uint32_t val = readMemoryUint32(addr); + val++; + writeMemoryUint32(addr, val); +} + + +void incMemoryUint16(uint32_t addr) +{ + uint16_t val = readMemoryUint16(addr); + val++; + writeMemoryUint16(addr, val); +} + + +void setStatusByte(uint8_t bitnum, uint8_t bitval) +{ + //program the status byte + + if (bitval == 0) + { + bitClear(TXStatus, bitnum); + } + else + { + bitSet(TXStatus, bitnum); + } +} + + +uint8_t readConfigByte(uint8_t bitnum) +{ + return bitRead(Default_config1, bitnum); +} + + +void setTrackerMode() +{ + Serial.println(F("setTrackerMode")); + LT.setupLoRa(TrackerFrequency, Offset, TrackerSpreadingFactor, TrackerBandwidth, TrackerCodeRate, TrackerOptimisation); +} + + +void setSearchMode() +{ + Serial.println(F("setSearchMode")); + LT.setupLoRa(SearchFrequency, Offset, SearchSpreadingFactor, SearchBandwidth, SearchCodeRate, SearchOptimisation); +} + + +uint8_t sendCommand(char cmd) +{ + uint8_t len; + TXVolts = readSupplyVoltage(); + Serial.print(F("Send Cmd ")); + Serial.write(cmd); + Serial.println(); + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + LT.writeUint8(cmd); //this byte defines the packet type + LT.writeUint8(Broadcast); //destination address of the packet, the receivers address + LT.writeUint8(ThisNode); //source address of this node + LT.writeUint16(TXVolts); //add the battery voltage + LT.writeUint8(TXStatus); //add the status byte + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + //now transmit the packet, set a timeout of 5000mS, wait for it to complete sending + + digitalWrite(LED1, HIGH); //turn on LED as an indicator + TXPacketL = LT.transmitSXBuffer(0, len, 5000, TrackerTXpower, WAIT_TX); + digitalWrite(LED1, LOW); //turn off indicator LED + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //flash LED to show tracker is alive + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void clearAllMemory() +{ + //clears the whole of non-volatile + Serial.println(F("Clear Memory")); + fillMemory(addr_StartMemory, addr_EndMemory, 0); +} + + +float readTempDS18B20() +{ + float DS18B20TXTemperature; + sensor.requestTemperatures(); + DS18B20TXTemperature = sensor.getTempCByIndex(0); + return DS18B20TXTemperature; +} + + +void printTempDS18B20() +{ + float DS18B20TXTemperature; + DS18B20TXTemperature = readTempDS18B20(); + Serial.print(F("Temperature ")); + Serial.print(DS18B20TXTemperature, 1); + Serial.println(F("c")); +} + + +void printSupplyVoltage() +{ + //get and display supply volts on terminal or monitor + Serial.print(F("Volts ")); + Serial.print(readSupplyVoltage()); + Serial.println(F("mV")); +} + + +uint16_t readSupplyVoltage() +{ + //relies on internal reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t volts = 0; + uint8_t index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn MOSFET connection resitor divider in circuit + } + + analogReference(INTERNAL); + temp = analogRead(SupplyAD); + + for (index = 0; index <= 9; index++) //sample AD 10 times + { + temp = analogRead(SupplyAD); + volts = volts + temp; + delay(10); + } + volts = ( (float) (volts / 10) * ADMultiplier); + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn MOSFET connection resitor divider in circuit + } + + return volts; +} + + +//*********************************************************** +// Start GPS Functions +//*********************************************************** + +void GPSTest() +{ + uint8_t GPSchar; + uint32_t startmS; + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < 2000) //allows for millis() overflow + { + GPSchar = GPS_GetByte(); + if (GPSchar != 0xFF) + { + Serial.write(GPSchar); + } + } + Serial.println(); + Serial.println(); + Serial.flush(); +} + + +bool gpsWaitFix(uint16_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for good fix + + uint32_t startmS, waitmS; + uint8_t GPSchar; + + Serial.flush(); + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.println(F("s ")); + Serial.flush(); + + GPS_OutputOn(); + + waitmS = waitSecs * 1000; + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < waitmS) //allows for millis() overflow + { + + do + { + GPSchar = GPS_GetByte(); + if (GPSchar != 0xFF) + { + gps.encode(GPSchar); + Serial.write(GPSchar); + } + } + while (GPSchar != 0xFF); + + if (gps.location.isUpdated() && gps.altitude.isUpdated() && gps.date.isUpdated()) + { + TXLat = gps.location.lat(); + TXLon = gps.location.lng(); + TXAlt = (uint16_t) gps.altitude.meters(); + + //Altitude is used as an unsigned integer, so that the binary payload is as short as possible. + //However gps.altitude.meters(); can return a negative value which converts to + //65535 - Altitude, which we dont want. So we will assume any value over 60,000M is zero + + if (TXAlt > 60000) + { + TXAlt = 0; + } + + TXHours = gps.time.hour(), + TXMinutes = gps.time.minute(), + TXSeconds = gps.time.second(), + TXSatellites = gps.satellites.value(); + + setStatusByte(GPSFix, 1); + + TXGPSfixms = millis() - GPSstartms; + + Serial.flush(); + Serial.print(F("Have GPS Fix ")); + Serial.print(TXGPSfixms); + Serial.print(F("mS")); + Serial.println(); + GPSprintTime(); + GPSprintDate(); + Serial.flush(); + + return true; + } + + } + + //if here then there has been no fix and a timeout + GPS_OutputOff(); + setStatusByte(GPSFix, 0); //set status bit to flag no fix + incMemoryUint16(addr_TXErrors); + Serial.println(F("Error No GPS Fix")); + return false; +} + +void GPSprintTime() +{ + uint8_t hours, mins, secs; + hours = gps.time.hour(); + mins = gps.time.minute(); + secs = gps.time.second(); + + Serial.print(F("Time ")); + + if (hours < 10) + { + Serial.print(F("0")); + } + Serial.print(hours); + Serial.print(F(":")); + + if (mins < 10) + { + Serial.print(F("0")); + } + Serial.print(mins); + Serial.print(F(":")); + + if (secs < 10) + { + Serial.print(F("0")); + } + Serial.println(secs); +} + + +void GPSprintDate() +{ + Serial.print(F("Date ")); + Serial.print(gps.date.day()); + Serial.print(F("/")); + Serial.print(gps.date.month()); + Serial.print(F("/")); + Serial.println(gps.date.year()); +} + +//*********************************************************** +// End GPS Functions +//*********************************************************** + + +void setup() +{ + uint32_t i; + uint16_t j; + + Serial.begin(115200); //Setup Serial console ouput + Serial.println(); + Serial.println(); + Serial.println(F("67_HAB_Balloon_Tracker_Transmitter Starting")); + + memoryStart(Memory_Address); //setup the memory + j = readMemoryUint16(addr_ResetCount); + j++; + writeMemoryUint16(addr_ResetCount, j); + j = readMemoryUint16(addr_ResetCount); + + Serial.print(F("TXResets ")); + Serial.println(j); + + if (GPSPOWER >= 0) //if GPS needs power switching, turn it on + { + pinMode(GPSPOWER, OUTPUT); + digitalWrite(GPSPOWER, GPSONSTATE); + } + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); //for MOSFET controlling battery volts resistor divider + } + +#ifdef QUECTELINUSE + Serial.println(F("Quectel GPS library")); +#endif + +#ifdef UBLOXINUSE + Serial.println(F("UBLOX GPS library")); +#endif + +#ifdef ClearAllMemory + clearAllMemory(); +#endif + + SPI.begin(); //initialize SPI + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("LoRa Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + setTrackerMode(); + + Serial.print(F("Config ")); + Serial.println(Default_config1, BIN); + + j = readMemoryUint16(addr_TXErrors); + Serial.print(F("TXErrors ")); + Serial.println(j); + + Serial.print(F("TXSequence ")); + i = readMemoryUint32(addr_SequenceNum); + Serial.println(i); + + Serial.print(F("ThisNode ")); + Serial.println(ThisNode); + + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + + Serial.println(); + printSupplyVoltage(); + printTempDS18B20(); + Serial.println(); + + TXStatus = 0; //clear all TX status bits + + sendCommand(PowerUp); //send power up command, includes supply mV and config, on tracker settings + + GPS_OutputOn(); + Serial.println(); + Serial.println(F("GPS output test")); + Serial.flush(); + GPSTest(); //copy GPS output to serial monitor as a test + GPS_Setup(); //GPS should have had plenty of time to initialise by now + GPS_SetBalloonMode(); + + delay(2000); + + if (GPS_CheckBalloonMode()) //Check that GPS is configured for high altitude balloon mode + { + Serial.println(); + GPS_OutputOff(); //GPS interrupts cause problems with lora device, so turn off for now + setStatusByte(GPSError, 0); + setStatusByte(GPSConfigError, 0); + + //Alert user to GPS OK, turn LED on and send a FM tone + digitalWrite(LED1, HIGH); + Serial.println(F("GPS Config OK")); //check tone indicates navigation model 6 set + Serial.println(); + Serial.flush(); + LT.setupDirect(TrackerFrequency, Offset); //need direct mode for tones + LT.toneFM(1500, 500, deviation, adjustfreq, TrackerTXpower); //Transmit an FM tone, 1000hz, 3000ms + delay(1000); + digitalWrite(LED1, LOW); + } + else + { + setStatusByte(GPSConfigError, 1); + incMemoryUint16(addr_TXErrors); + Serial.println(F("GPS Error")); + Serial.println(); + setTrackerMode(); + sendCommand(NoGPS); //make sure receiver knows about GPS error + led_Flash(100, 25); //long very rapid flash for GPS error + } + + GPSstartms = millis(); + + setTrackerMode(); //so that commands indicating wait for a GPS go out + + while (!gpsWaitFix(5)) //wait for the initial GPS fix, this could take a while + { + sendCommand(NoFix); + led_Flash(2, 50); //two short LED flashes to indicate GPS waiting for fix + } + + LT.setupDirect(TrackerFrequency, Offset); //need direct mode for tones + digitalWrite(LED1, HIGH); + LT.toneFM(500, 2000, deviation, adjustfreq, TrackerTXpower); + digitalWrite(LED1, LOW); + GPS_OutputOn(); + delay(2000); //GPS may be in software backup allow time for it to wakeup + GPS_SetCyclicMode(); //set this regardless of whether hot fix mode is enabled + GPS_OutputOff(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/67_Balloon_Tracker_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/67_Balloon_Tracker_Transmitter/Settings.h new file mode 100644 index 0000000..52c6bfc --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/67_Balloon_Tracker_Transmitter/Settings.h @@ -0,0 +1,143 @@ + /******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//************************************************************************************************** +// 1) Hardware related definitions and options - specify lora board type and pins here +//************************************************************************************************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define LED1 8 //On board LED, high for on +#define BATVREADON 8 //Pin that turns on the resistor divider to read battery volts +#define ONE_WIRE_BUS 4 //for DS18B20 temperature sensor +#define ADMultiplier 5.25 //adjustment to convert into mV of battery voltage. for 100K\10K divider +#define SupplyAD A0 //Resistor divider for battery connected here + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER -1 //Pin that powers GPS on\off, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//************************************************************************************************** +// 2) Program Options +//************************************************************************************************** + +//#define ClearAllMemory //Clears memory of stored tracker information, counts, errors etc + +//************************************************************************************************** +// 3) LoRa modem settings +//************************************************************************************************** + +//LoRa Modem Parameters +const uint32_t Offset = 0; //offset frequency for calibration purposes + +//Tracker mode +const uint32_t TrackerFrequency = 434000000; //frequency of transmissions +const uint8_t TrackerBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t TrackerSpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t TrackerCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t TrackerOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t TrackerTXpower = 10; //LoRa TX power in dBm + +//Search mode +const uint32_t SearchFrequency = 434000000; //frequency of transmissionsconst +uint8_t SearchBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SearchSpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t SearchCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t SearchOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t SearchTXpower = 10; //LoRa TX power in dBm + +const uint16_t deviation = 10000; //deviation in hz for FM tones +const float adjustfreq = 0.9; //adjustment to tone frequency + +const uint8_t TXBUFFER_SIZE = 128; //defines the maximum size of the trasnmit buffer; + + +//************************************************************************************************** +// 4) GPS Options +//************************************************************************************************** + +#define GPSBaud 9600 //GPS Baud rate + +//#define USEI2CGPS //enable this define if your using a Ublox over the I2C interface +//#define GPS_Library //and define this library file for the UBLOX GPS over I2C + +#define USESOFTSERIALGPS //if your using software serial for the GPS, enable this define + +//#define USEHARDWARESERIALGPS //if your using hardware serial for the GPS, enable this define +#define HARDWARESERIALPORT Serial1 //if your using hardware serial for the GPS, define the port here + +//#define GPS_Library //use library file for UBLOX GPS +#define GPS_Library //use library file for Quectel GPS + +const uint16_t WaitGPSFixSeconds = 60; //when in flight the time to wait for a new GPS fix + +//************************************************************************************************** +// 5) FSK RTTY Settings +//************************************************************************************************** + +uint32_t FrequencyShift = 500; //hertz frequency shift, approx, sent at nearest 61.03515625hz step +uint8_t NumberofPips = 4; //number of marker pips to send +uint16_t PipDelaymS = 1000; //mS between pips when carrier is off +uint16_t PipPeriodmS = 100; //mS length of pip +uint16_t BaudPerioduS = 10000; //uS period for baud, 10000uS for 100baud +uint16_t LeadinmS = 1000; //ms of leadin constant shifted carrier + + +//**************************************************************************************************** +// 6) Program Default Option settings - This section determines which options are on or off by default, +// these are saved in the Default_config1 byte. These options are set in this way so that it is +// possible (in future program changes) to alter the options remotly. +//************************************************************************************************** + +uint8_t OptionOff = 0; +uint8_t OptionOn = 1; + +const char option_SearchEnable = OptionOn; //set to OptionOn to enable transmit of Search mode packet +const char option_FSKRTTYEnable = OptionOn; //set to OptionOn to enable transmit of FSKRTTY + +#define option_SearchEnable_SUM (option_SearchEnable*1) +#define option_FSKRTTYEnable_SUM (option_FSKRTTYEnable*4) + +const uint16_t Default_config1 = (option_SearchEnable_SUM + option_FSKRTTYEnable_SUM); +//const uint16_t Default_config1 = 0x05; //Phew, the default config can always be set manually........ + //0x05 would turn on transmit of search mode and FSKRTTY + + +//************************************************************************************************** +// 7) Memory settings - define the type of memory to use for non-Volatile storage. +// Default is internal ATmega device EEPROM but EEPROM has a limited write endurance of 'only' +// 100,000 writes. Since the non-Volatile memory selected is written to at each transmission loop +// and error, its highly recommended to use one of the FRAM options, these have an endurance of +// 100,000,000,000,000 writes. +//************************************************************************************************** + +#define Memory_Library +//#define Memory_Library +//#define Memory_Library + +int16_t Memory_Address = 0x50; //default I2C address of MB85RC16PNF and FM24CL64 FRAM + +//************************************************************************************************** +// 8) HAB Flight Settings +//************************************************************************************************** + +char FlightID[] = "Flight1"; //flight ID for HAB packet + +const uint16_t SleepTimesecs = 13; //sleep time in seconds after each TX loop + +const char ThisNode = '1'; //tracker number for search packet + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/68_Balloon_Tracker_Receiver.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/68_Balloon_Tracker_Receiver.ino new file mode 100644 index 0000000..45ed8cd --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/68_Balloon_Tracker_Receiver.ino @@ -0,0 +1,1048 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/12/20 + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a LoRa tracker receiver intended to be used with the matching high altitude + balloon (HAB) tracker program '67_Balloon_Tracker_Transmitter'. The program receives a standard format + payload with LoRa that is compatible with the HABHUB online tracking system. + + The HAB payload sent by the tracker transmitter is assumed to be formatted like this; + + PayloadID,Sequence,Time,Lat,Lon,Alt,Satellites,Volts,Temperature,Resets,status,errors,Checksum + Field 0 1 2 3 4 5 6 7 8 9 10 11 12 + + The LoRa and frequency settings can be changed in the Settings.h file. There is the option of the transmitter + sending out a much shorter Search mode binary location only payload. This is intended for ground based searching + and locating. The frequency and LoRa settings of the Search mode packet can be different to the Tracker + mode used by the HAB payload. To switch between standard tracker mode press the switch that is defined in the + Settings.h file. This receiver cannot receive the transmitted FSK RTTY payload. + + There is the option to enable an audio FSK RTTY uplaod into FLDIGI from where it can be sent to the HABHUB + online tracking system. + + The program will drive a SSD1306 or SH1106 OLED display for portable use. + + Not that the distance and direction to the tracker is only displayed when there has been at least one location + fix from the remote tracker and the locally attached GPS has a fix. + + Serial monitor baud rate is set at 9600. + + ToDo: + +*******************************************************************************************************/ + + +#define Program_Version "V1.2" + +#include +#include +SX127XLT LT; + +#include "Settings.h" +#include + +#include //https://github.com/olikraus/u8g2 +//U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //standard 0.96" SSD1306 +U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //1.3" OLED often sold as 1.3" SSD1306 +#define DEFAULTFONT u8x8_font_chroma48medium8_r //font for U8X8 Library + +#include //http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#ifdef USESOFTSERIALGPS +//#include //https://github.com/SlashDevin/NeoSWSerial +//NeoSWSerial GPSserial(RXpin, TXpin); //The NeoSWSerial library is an option to use and is more relaible + //at GPS init than software serial +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#endif + +#ifdef USEHARDWARESERIALGPS +#define GPSserial HARDWARESERIALPORT +#endif + +#ifdef UPLOADHABPACKET +#include //this library supports Arduinos without tone functions +#endif + +//************************************************************************************************** +// HAB tracker data - these are the variables transmitted in payload +//************************************************************************************************** +uint32_t TXSequence; //sequence number of payload +uint8_t TXHours; //Hours +uint8_t TXMinutes; //Minutes +uint8_t TXSeconds; //Seconds +float TXLat; //latitude from GPS +float TXLon; //longitude from GPS +uint16_t TXAlt; //altitude from GPS +uint8_t TXSatellites; //satellites used by GPS +uint16_t TXVolts; //measured tracker supply volts +int8_t TXTemperature; //measured temperature +uint16_t TXResets; //number of tracker resets +uint8_t TXStatus; //used to store current status flag bits +uint16_t TXErrors; //number of tracker Errors +//************************************************************************************************** + +float RXLat; //latitude of RX +float RXLon; //longitude of RX +float RXAlt; //altitude of RX + +uint32_t RXpacketCount; //count of received packets +uint8_t RXPacketL; //length of received packet +int16_t PacketRSSI; //signal strength (RSSI) dBm of received packet +int8_t PacketSNR; //signal to noise ratio (SNR) dB of received packet +uint16_t RXerrors; //count of packets received with errors +uint8_t PacketType; //for packet addressing, identifies packet type +uint8_t Destination; //for packet addressing, identifies the destination (receiving) node +uint8_t Source; //for packet addressing, identifies the source (transmiting) node +float TXdistance; //calculated distance to tracker +uint16_t TXdirection; //calculated direction to tracker +uint16_t RXVolts; //supply\battery voltage of this receiver +uint32_t LastRXGPSfixCheck; //used to record the time of the last GPS fix +bool TXLocation = false; //set to true when at least one tracker location packet has been received +bool RXGPSfix = false; //set to true if the local GPS has a recent fix + +uint8_t FixCount = DisplayRate; //used to keep track of number of GPS fixes before display updated + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into +char FlightID[16]; //buffer for flight ID +uint8_t FlightIDlen; //length of received flight ID + +uint8_t modeNumber = 1; //mode receiver is in default to 1. (1 = Tracker, 2 = Search) + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, NO_WAIT); + + GPSserial.begin(GPSBaud); //startup GPS input + + while (!digitalRead(DIO0)) + { + readGPS(); //If the DIO pin is low, no packet has arrived, so read the GPS + + if (!digitalRead(SWITCH1)) + { + checkModeSwitch(); + break; + } + } + + if (digitalRead(DIO0)) + { + //something has happened in receiver + GPSserial.end(); //stop GPS input to use SPI reliably + digitalWrite(LED1, HIGH); + + RXPacketL = LT.readRXPacketL(); + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + Serial.println(); + printElapsedTime(); //print elapsed time to Serial Monitor + + if (LT.readIrqStatus() == (IRQ_RX_DONE + IRQ_HEADER_VALID)) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + + digitalWrite(LED1, LOW); + + Serial.println(); + } +} + + +void readGPS() +{ + + if (GPSserial.available() > 0) + { + gps.encode(GPSserial.read()); + } + + if ( (uint32_t) (millis() - LastRXGPSfixCheck) > NoRXGPSfixms) + { + RXGPSfix = false; + LastRXGPSfixCheck = millis(); + if (TXLocation) //only display location screen if we have had an update + { + displayscreen1(); //shows the received location data and packet reception on display + displayscreen3(); //show receive mode on display + displayscreen4(); //put RX and TX GPS fix status on display + } + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated() && gps.date.isUpdated()) + { + RXGPSfix = true; + RXLat = gps.location.lat(); + RXLon = gps.location.lng(); + RXAlt = gps.altitude.meters(); + LastRXGPSfixCheck = millis(); + displayscreen4(); //put RX and TX GPS fix status on display + + if (FixCount == 1) //update screen when FIXcoount counts down from DisplayRate to 1 + { + FixCount = DisplayRate; + if (TXLocation) //only display location screen if we have had an update + { + doDistanceDirectionCalc(); + displayscreen1(); //shows the received location data and packet reception on display + displayscreen3(); //show receive mode on display + displayscreen4(); //put RX and TX GPS fix status on display + displayscreen5(); //put distance and direction on display + printDistanceDirection(); + } + } + FixCount--; + } +} + + +void checkModeSwitch() +{ + digitalWrite(LED1, LOW); + Serial.println(); + Serial.print(F("Listening in ")); + + modeNumber++; + + if (modeNumber == 3) + { + modeNumber = 1; + } + + if (modeNumber == 1) + { + setTrackerMode(); + Serial.println(F("Tracker Mode")); + } + + + if (modeNumber == 2) + { + setSearchMode(); + Serial.println(F("Search Mode")); + } + + displayscreen3(); //show receive mode on display + + LT.printModemSettings(); + Serial.println(); + Serial.println(); + delay(1500); //do a bit of switch de-bounce +} + + +bool readTXStatus(uint8_t bitnum) +{ + return bitRead(TXStatus, bitnum); +} + + +void readPacketAddressing() +{ + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + LT.endReadSXBuffer(); +} + + +void packet_is_OK() +{ + uint16_t includedCRC, actualCRC; + + RXpacketCount++; + + readPacketAddressing(); + + if (PacketType == PowerUp) + { + read_Command(); + Serial.print(F("TrackerPowerup,Battery,")); + Serial.print(TXVolts); + Serial.print(F("mV")); + displayscreen2(); + displayscreen3(); //show receive mode on display + displayscreen4(); //put RX and TX GPS fix status on display + displayscreen7(); //display received packet count + return; + } + + if (PacketType == HABPacket) + { + includedCRC = calcIncludedCRC(); + actualCRC = LT.CRCCCITTSX(1, RXPacketL - 6, 0xFFFF); + + if (actualCRC != includedCRC) + { + Serial.print(F("PayloadCRCError")); + Serial.print(F(",includedCRC,")); + Serial.print(includedCRC, HEX); + Serial.print(F(",actualCRC,")); + Serial.print(actualCRC, HEX); + Serial.print(F(",")); + LT.printSXBufferASCII(0, (RXPacketL - 1)); + printpacketDetails(); + displayscreen7(); //display received packet count + return; + } + + extractHABdata(0); + + TXLocation = true; + LT.printSXBufferASCII(0, (RXPacketL - 1)); + + TXdistance = 0; + TXdirection = 0; + + if (RXGPSfix) + { + doDistanceDirectionCalc(); + } + + printpacketDetails(); + Serial.println(); + + displayscreen1(); //shows the received location data and packet reception on display + displayscreen3(); //show receive mode on display + displayscreen4(); //put RX and TX GPS fix status on display + displayscreen5(); //put distance and direction on display + printDistanceDirection(); + +#ifdef UPLOADHABPACKET + if (actualCRC == includedCRC) + { + Serial.println(); + uploadHABpacket(); + } +#endif + + return; + } + + if ((PacketType == LocationBinaryPacket) && (Destination == '*') && (RXPacketL == 14)) + { + //packet from tracker transmitter has been received, now read from the SX12XX FIFO in the correct order. + TXLocation = true; + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readInt16(); + TXStatus = LT.readUint8(); + RXPacketL = LT.endReadSXBuffer(); + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt); + Serial.print(F("m,")); + Serial.println(TXStatus); + + TXdistance = 0; + TXdirection = 0; + + if (RXGPSfix) + { + doDistanceDirectionCalc(); + } + + displayscreen1(); //shows the received location data and packet reception on display + displayscreen5(); //put distance and direction on display + displayscreen7(); //display received packet count + printDistanceDirection(); + return; + } + + if (PacketType == NoFix) + { + read_Command(); + Serial.write(Source); + Serial.print(F(",NoTrackerGPSFix")); + Serial.write(7); //send a BELL to serial terminal + delay(250); + Serial.write(7); + displayscreen6(); //send a note about no tracker GPS fix to screen + displayscreen7(); //display received packet count + return; + } + + if (PacketType == NoGPS) + { + Serial.write(Source); + read_Command(); + Serial.print(F(",GPSError")); + return; + } + + Serial.print(F("PacketNotRecognised")); + printpacketDetails(); + printmorepacketDetails(); +} + +#ifdef UPLOADHABPACKET +void uploadHABpacket() +{ + uint8_t index; + uint8_t chartosend; + + Serial.print(F("Dl-Fldigi Upload $")); + Serial.flush(); + + startAFSKRTTY(AUDIOOUT, CHECK, LOWCYCLES, LOWPERIODUS, HIGHCYCLES, HIGHPERIODUS, ADJUSTUS, leadinmS); + sendAFSKRTTY(13); + sendAFSKRTTY(10); + sendAFSKRTTY('$'); + + for (index = 0; index <= (RXPacketL - 1); index++) + { + chartosend = LT.getByteSXBuffer(index); + sendAFSKRTTY(chartosend); + Serial.write(chartosend); + Serial.flush(); + } + + sendAFSKRTTY(13); + sendAFSKRTTY(10); + Serial.println(); + endAFSKRTTY(AUDIOOUT, CHECK, leadoutmS); +} +#endif + + +uint16_t calcIncludedCRC() +{ + uint8_t high, midhigh, midlow, low; + uint16_t crc; + high = LT.getByteSXBuffer(RXPacketL - 4); + midhigh = LT.getByteSXBuffer(RXPacketL - 3); + midlow = LT.getByteSXBuffer(RXPacketL - 2); + low = LT.getByteSXBuffer(RXPacketL - 1); + + high = convertASCIIbyte(high); + midhigh = convertASCIIbyte(midhigh); + midlow = convertASCIIbyte(midlow); + low = convertASCIIbyte(low); + + crc = (high * 4096) + (midhigh * 256) + (midlow * 16) + low; + return (uint16_t) crc; +} + + +uint8_t convertASCIIbyte(uint8_t val) +{ + if (val > 0x40) + { + val = (val - 0x41) + 10; + return val; + } + else + { + val = (val - 0x30); + return val; + } +} + + +void read_Command() +{ + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXVolts = LT.readUint16(); //read tracker transmitter voltage + LT.endReadSXBuffer(); +} + + +void printDistanceDirection() +{ + if (RXGPSfix && TXLocation) //only display distance and direction if have received tracker packet and have local GPS fix + { + Serial.print(F("Distance,")); + Serial.print(TXdistance, 0); + Serial.print(F("m,Direction,")); + Serial.print(TXdirection); + Serial.println(F("d")); + } +} + + +void doDistanceDirectionCalc() +{ + TXdirection = (int16_t) TinyGPSPlus::courseTo(RXLat, RXLon, TXLat, TXLon); + TXdistance = TinyGPSPlus::distanceBetween(RXLat, RXLon, TXLat, TXLon); +} + + +void printpacketDetails() +{ + int32_t hertzerror; + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,FreqErr,")); + hertzerror = LT.getFrequencyErrorHz(); + Serial.print(hertzerror); + Serial.print(F("hz,PacketErrs,")); + Serial.print(RXerrors); + Serial.print(F(",PacketsOK,")); + Serial.print(RXpacketCount); +} + + +void printmorepacketDetails() +{ + uint16_t IRQStatus; + Serial.print(F(",Length,")); + Serial.print(RXPacketL); + IRQStatus = LT.readIrqStatus(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + RXerrors++; + Serial.print(F(",PacketError,RSSI")); + + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void extractHABdata(uint8_t startaddr) +{ + //extracts data from received HAB packets where first fields are lat,lon,alt + //all varialbles are extracted, not all are used. + + uint8_t ptr = startaddr; //pointer to current location in SXbuffer + uint8_t buffData; + + //Skip leading $ + do + { + buffData = LT.getByteSXBuffer(ptr++); + } + while ( buffData == '$'); + + ptr--; //so ptr is at location of first non $ + + FlightIDlen = extractBuffer(FlightID, sizeof(FlightID), ptr); //extract flight ID + + ptr = nextComma(ptr); //step to next comma in SX buffer + TXSequence = extractUint(ptr); //extract sequence + + ptr = nextComma(ptr); + TXHours = (uint32_t) extractUint(ptr); + + ptr = nextComma(ptr); + TXMinutes = (uint32_t) extractUint(ptr); + + ptr = nextComma(ptr); + TXSeconds = (uint32_t) extractUint(ptr); + + //ptr = nextComma(ptr); + ptr = nextComma(ptr); + TXLat = extractFloat(ptr); + + ptr = nextComma(ptr); + TXLon = extractFloat(ptr); + + ptr = nextComma(ptr); + TXAlt = extractUint(ptr); + + ptr = nextComma(ptr); + TXSatellites = extractUint(ptr); + + ptr = nextComma(ptr); + TXVolts = extractUint(ptr); + + ptr = nextComma(ptr); + TXTemperature = extractUint(ptr); + + ptr = nextComma(ptr); + TXResets = extractUint(ptr); + + ptr = nextComma(ptr); + TXStatus = extractUint(ptr); + + ptr = nextComma(ptr); + TXErrors = extractUint(ptr); +} + + +void printHABdata() +{ + printBuffer(FlightID, FlightIDlen); + Serial.print(F(",")); + Serial.print(TXSequence); + Serial.print(F(",")); + + Serial.print(TXHours); + Serial.print(F(",")); + Serial.print(TXMinutes); + Serial.print(F(",")); + Serial.print(TXSeconds); + Serial.print(F(",")); + + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt); + Serial.print(F(",")); + Serial.print(TXSatellites); + Serial.print(F(",")); + Serial.print(TXVolts); + Serial.print(F(",")); + Serial.print(TXTemperature); + Serial.print(F(",")); + Serial.print(TXResets); + Serial.print(F(",")); + Serial.print(TXStatus); + Serial.print(F(",")); + Serial.print(TXErrors); +} + + +uint8_t extractBuffer(char *mybuffer, size_t bufferSize, uint8_t startptr) +{ + //extracts a character buffer in ASCII format from lora device RX buffer, returns the length of the buffer to , char + uint16_t index; + + memset(mybuffer, 0, bufferSize); //clear array to 0s + + bufferSize--; //last index location is one less than buffer size + + for (index = 0; index <= bufferSize; index++) + { + mybuffer[index] = LT.getByteSXBuffer(startptr++); + if ((mybuffer[index] == ',') || (mybuffer[index] == '*')) + { + break; + } + } + mybuffer[index] = 0; //it was a , so clear it + return index++; //buffer length is one more than index location +} + + +void printBuffer(char *buff, uint8_t len) +{ + //send buffer to serial terminal + uint8_t index; + + for (index = 0; index < len; index++) + { + Serial.write(buff[index]); + } + +} + + +uint8_t nextComma(uint8_t localpointer) +{ + //skips through HAB packet (in SX device buffer) to next comma + uint8_t bufferdata; + do + { + bufferdata = LT.getByteSXBuffer(localpointer++); + } + while ((bufferdata != ',') && (bufferdata != ':') && (localpointer < RXPacketL)); + return localpointer; //note returns start of next field +} + + +uint8_t nextColon(uint8_t localpointer) +{ + //skips through HAB packet (in SX device buffer) to next colon + uint8_t bufferdata; + do + { + bufferdata = LT.getByteSXBuffer(localpointer++); + } + while ((bufferdata != ':') && (localpointer < RXPacketL)); + return localpointer; +} + + +int32_t extractUint(uint16_t localpointer) +{ + //extracts an unsigned int in ASCII format from buffer + + char temp[16]; + uint8_t tempptr = 0; + uint8_t buffdata; + uint32_t tempint; + do + { + buffdata = LT.getByteSXBuffer(localpointer++);; + temp[tempptr++] = buffdata; + } + while ((buffdata != ',') && (buffdata != '*') && (localpointer < 256) ); + temp[tempptr] = 0; //terminator for string + tempint = (int32_t) atof(temp); + return tempint; +} + + +float extractFloat(uint16_t localpointer) +{ + //extracts a float in ASCII format from buffer + char temp[16]; + uint8_t tempptr = 0; + uint8_t buffdata; + float tempfloat; + do + { + buffdata = LT.getByteSXBuffer(localpointer++);; + temp[tempptr++] = buffdata; + } + while ((buffdata != ',') && (buffdata != '*') && (localpointer < 256) ); + temp[tempptr] = 0; //terminator for string + tempfloat = (float)atof(temp); + return tempfloat; +} + + +void setTrackerMode() +{ + LT.setupLoRa(TrackerFrequency, Offset, TrackerSpreadingFactor, TrackerBandwidth, TrackerCodeRate, TrackerOptimisation); +} + + +void setSearchMode() +{ + LT.setupLoRa(SearchFrequency, Offset, SearchSpreadingFactor, SearchBandwidth, SearchCodeRate, SearchOptimisation); +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F(",")); +} + + +void GPSPowerOn(int8_t pin, uint8_t state) +{ + if (pin >= 0) + { + digitalWrite(pin, state); + } +} + +//************************************************************************ +// Display screen functions +//************************************************************************ + +void displayscreen1() +{ + //shows the received location data and packet reception on display + uint8_t index; + + disp.clearLine(0); + disp.setCursor(0, 0); + + if (PacketType == HABPacket) + { + for (index = 0; index < FlightIDlen; index++) + { + disp.write(FlightID[index]); + } + } + + if (PacketType == LocationBinaryPacket) + { + disp.print(Source); + } + + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(F("Lat ")); + disp.print(TXLat, 5); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(F("Lon ")); + disp.print(TXLon, 5); + disp.clearLine(3); + disp.setCursor(0, 3); + disp.print(F("Alt ")); + disp.print(TXAlt); + disp.print(F("m")); + + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(F("RSSI ")); + disp.print(PacketRSSI); + disp.print(F("dBm")); + disp.clearLine(5); + disp.setCursor(0, 5); + disp.print(F("SNR ")); + + if (PacketSNR > 0) + { + disp.print(F("+")); + } + + if (PacketSNR == 0) + { + disp.print(F(" ")); + } + + if (PacketSNR < 0) + { + disp.print(F("-")); + } + + disp.print(PacketSNR); + disp.print(F("dB")); + + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("Packets ")); + disp.print(RXpacketCount); +} + + +void displayscreen2() +{ + //show tracker transmitter powerup data on display + float tempfloat; + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("TXPowerup")); + disp.setCursor(0, 1); + disp.print(F("Battery,")); + tempfloat = ((float) TXVolts / 1000); + disp.print(tempfloat, 2); + disp.print(F("v")); +} + + +void displayscreen3() +{ + //show receive mode on display + disp.setCursor(14, 0); + + if (modeNumber == TrackerMode) + { + disp.print(F("TR")); + return; + } + + if (modeNumber == SearchMode) + { + disp.print(F("SE")); + return; + } + + disp.print(modeNumber); +} + + +void displayscreen4() +{ + //put RX and TX GPS fix status on display + + disp.setCursor(14, 1); + + if (RXGPSfix) + { + disp.print(F("RG")); + } + else + { + disp.setCursor(14, 1); + disp.print(F("R?")); + } + + disp.setCursor(14, 2); + + if (readTXStatus(GPSFix)) + { + disp.print(F("TG")); + } + else + { + disp.print(F("T?")); + } +} + + +void displayscreen5() +{ + //put distance and direction on display + if (RXGPSfix && TXLocation) //only display distance and direction if have received tracker packet and have local GPS fix + { + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("D&D ")); + disp.print(TXdistance, 0); + disp.print(F("m ")); + disp.print(TXdirection); + disp.print(F("d")); + } +} + + +void displayscreen6() +{ + //no GPS fix + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("No GPS Fix")); +} + + +void displayscreen7() +{ + //display received packet count + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("Packets ")); + disp.print(RXpacketCount); +} + + +void GPSTest() +{ + uint32_t startmS; + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < 2000) //allows for millis() overflow + { + if (GPSserial.available() > 0) + { + Serial.write(GPSserial.read()); + } + } + Serial.println(); + Serial.println(); + Serial.flush(); +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + pinMode(SWITCH1, INPUT_PULLUP); //setup pin as switch input + + if (CHECK <= 0) + { + pinMode(CHECK, OUTPUT); + } + + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + } + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("68_Balloon_Tracker_Receiver Starting")); + + SPI.begin(); + + disp.begin(); + disp.setFont(DEFAULTFONT); + + Serial.print(F("Checking LoRa device - ")); + disp.setCursor(0, 0); + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) //Initialize LoRa device + { + Serial.println(F("Receiver ready")); + disp.print(F("Ready")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No LoRa device responding")); + disp.print(F("No LoRa device")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + GPSPowerOn(GPSPOWER, GPSONSTATE); + GPSserial.begin(GPSBaud); + + Serial.println(); + + Serial.println(F("Startup GPS check")); + GPSTest(); + Serial.println(F("Done")); + Serial.println(); + Serial.flush(); + + GPSserial.end(); //software serial interferes with SPI for LoRa device + + setTrackerMode(); + displayscreen3(); //show receive mode on display + displayscreen4(); //put RX and TX GPS fix status on display + + LT.printModemSettings(); + Serial.println(); + Serial.println(F("Listening in Tracker mode")); + Serial.println(); + Serial.write(7); //send a BELL to serial terminal + TXStatus = 4; //set default flag of no TX GPS fix +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/AFSKRTTY2_DL-Fldigi_Settings.jpg b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/AFSKRTTY2_DL-Fldigi_Settings.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b428c9db8994ecd83aee5f1bcbfc0b5f9f475b8d GIT binary patch literal 45618 zcmeFZWmH_-)+o4ff(CbYcXtR<5P}4k;K3$T{{tQt00jUQ z83h#u85I=;6%7p)9pfob~_s_3#D3TJW*6q zRspF#*U{C}H!w7^vbK3?YiIA^;pye=KhuHnp;}Gc6Imk_Vo`8PE1aHpPreWn_pYs*!;1z{c~sc_~i8L{NnQJ`sNS3 z5CEh<(fSLs|G*0mju#>_G7>V{A9x`k`oJ$FJY*C)K2&@;Ei?-^0($;nbVB*~oSH9> z7zDJBh%DX5F^Cxj*O-p~KfV zvw!y9?vufC4|DYEo-Jf%r5Cx@8xDB&IHF?vhT_nkK(%K_4PlL~eeb};JIODmrw6k9 zEeD5lb#L_tU5k#1dSTOVz}*rYS?5<{r~A0;CKs z4Gb3_0Go;_hCzjk5*6nzWREBw%qW+E_4JffSYvaGSJQb%%%E_LemsMxPt?Ab9&$;q zYakA~Of=|6wrWYP$)d@ULy3T2$Rh@W8Ni%WEBtO2<`qg@C15A86m1}#b6evDZj`x7 z4OuhUTWEHo=)7k5F{bKo$5h%f(x(-*GJIbT!E#we^VP&R7am}g zl=8_1wPH+9sKJI6y&vZ=0%mnw7e!DXso0lcBiGjUW8!3{k+U(T=FYcq@xin)x+d{W zAxZ*Ko}_(Z`mj!YOo$oeno1IdC6p<6Lgk~2z7mSTs}S+2EW@)(=3izVruY&O=N7ge z_bE-9v23{W1$0eVc%!`S+i~+1q}%&%WC}_Z6d-U^URnXxe!({B+97N?WP9l zMp#p|kX6CSSOLFf+>?{x9ZD5Ufi{2;CqHqbC8Ofm36=1=eFICx))aGN=v;0Xt1i_M z*}TXu31tZAGwJ5Er^u!14Ee{__sW6XZxj27XTSFEGP2llB!-As-B}|6HVJ55 zxLNLVCvZhJf74Gpacv8qI$Cef!M*Q3=Z7g{HB)DHjm(6Vd1!Wg`-xdHE%)dv@|3|- zt=quzlNG@@^^!$H3J-h~^iG$_I1v{Lv;eC{zoO}tne!4Uo_mN96>dr(D;$FtC-=qh ze=_AE=_EKys{CG0!T%Bddzh?}wwZ1du@PYzm@Q~CAfa1#876)1eEs@+HX6Nm(#Yrovsz3C%oZ>-(?5Ehr9l-r^rm-?0jZ}RsJTM0eh z1_*8lEU?V}2Vm}JgP+Sr^ak5wyM*U0KKFaaQ?K-r>~FhoB1>`#o#`HcDmBRF*=#uX zqM^d}3$yu!o{!)ZsrT6>uku?*)Axvk_Srss1js-vFqrNb9*C%5G}_IBUIE$XZF>T* zuBQ11ti;co!RPcXWL^@fRBph%Au7mL4IzQ(UwUJ zficzG{2d8Z_(zY3G^GX-N`=du8h0cdjm(1wbmNNWyS;$x4EQ#RU#Ua;PLiK#o z&67DZ*ZD6e!fVw&wo$eXHl|l0xYg>C`i*RrLx_qVrHbKtJNgtGk(S;O-g_uPWS@JN zpl#3e*!fyYL)N|I9d5h26CpNIE zU)fVP__X)!sz?3vPL1o$?92X2AEvmbT_$y=;&rI( zrLyd$d~c~b982bmR#@&%G;TMwP9S594?w4)jJ-?9#mi@d!_%JNechA7j<(40RHw#c zr$?+~bP?KJQaGe;rWykefMg=ybU@}XUb!CkrFHmcQlv;>oz{RbQw?oH#H29I z;$~8$p{NHySSHi%?$1wZ%vWF74a_MGPG8GxCqP!{>*@`$j!g!5^=+DyQDr}CF$D3Q zA;#Ku&E?S;jzb&t2h%SOIFw9l7ka80^CZKV)UeJ(Eqd(#0qkFM#3sW0Snf~Q{FSJO z|33G>-oI6%9wC!p{hyJg9)DM-WF$Z88aW&YjL7UILdM8s4iohP_&7$WHdNddyNgA8 z5&Kth`!N>s=6NsdwqOGycgJwd1p^bn*Bfx4j`_vY>m)4+OE04<8FYQZc{xkx=2dnP z=4Yjj0$DGjq|(J6fLe7py{zHnRj0Aefce6y7m&Wg`sa)<&|)dZQ>FO%p3doe#ZkPN zBs}9C#=So$GW;in{y_{D8ww8N%?BV01pjViQN_?jAUVuV7vEri)6+RGgfPzT4^ZrT zc{cXOrRa~7w4Z?(Z972!;_5#TeKshp*YrPe&SGyou{Jtcx`{39vm|@?mfdqYWu;`f z`$uKtrJQz>^)jqt3{u5^%+BAaRon1S87tqnd`_AEs+$P8355njjN16)YxCDSa0ZOv zIpx_nCP#x11wU^Tu>1?$A5Z?y0zK~ER4sBtB>(Hi*MIHdkqJ=r3a<_uI zVoZF++itG7mM^O@enPMQF`M2TeAkfTf*uY*jy;`P;R2Gfa`^*#)mo>>16QKtMpMX` z8u<2P<<3+;;j8-b09J7w_mNt!p|L-zBqI7ImWPqx^NVt+w9E^~!|Oxa2Owzd0qA4B z6}r5;ta|`H!{6CR&QqlR{;p1-^Y^u?**biCWTcPFS=A9)Ii8uLgsNdfwfh4#y?CMe zBWH~CI(kFq^X`$dUq&$>TQ%i?#?FNA&bhoC*Ly@E11UVi!Ks(Neb^r>SbDQq0*SGW z_B|(^u}7jmUSz+7)IYV2K^(56WUsc5Sg79Q7X9_&ec)OLi+;-JFP#a&UkmSDvG5lc z`WU)>W!TJBPY^aH%I2;FlpBvxmRs&K?YXPmcrq>qETaqGUpi7x1-M`~ys~XMFV~B` z*?|SBzk--eze6avTIm&;HzAK7ub``$!fu*lNz+s&OR<}@=UZ%3J87d@p#LHE!d38G zgZS4gNCfA;7H!95UTfpz;g%X6Mn!j*&u@4Q8Q2vm%0H8{q% zM{qg|`tJPo(hbt|_Q#*;hY{1u5`^FDZCkRORo7g=TsG$M3yXw&&Zo6~oV@Oc>>G|= zm5FN)K=`=^VMfOFMt#Gi&J%`4yq0W_FIX+MTtR3bx+wfsH?7dTFTdPipN#T&V$*MG z@HQu@+Y?+T1PATM51Gd&jyT9!#tBv+<0(xvUSpu3ECALUC7+R`gS2BZ9sslHm6$#1 zHnLXzyHKI*n?3mU#lG1Pt-8g7d-uevBaOK}c^Z+4E@z~iN&fWYZdj$y==Glj5oXf&3MRI0yY!=?stJFR{$Kj=swVNAAKcWTcGoZ{)`x zfEQ+)s3alI!$y6PX_Y*M44QbQi>5>Ty`4|MZ}lx7yTr#3;(YQ1u(NR?eHAnJfpB@v z%P%AMd3UzYzjd&oKFR5Hz`YPS+<27lEMWby1t1n`OKOnZo7;)nWZl)}W z@0SWh&&xfN_4~e6;_p~CF2U2rP+&69T=R9%IxisqR4~bQH=%-)Np^E7djMnq^zr^UQDHnH)w_ zjLM>8T;Fyf9|fxGP6cq}IXssW&dc3Nbzdk-#T;{{j3$2oyg-z=7Ee4I;9l1+jc)2D z7_<@^ysf{?c+8zz`b%qe2wdDSUEe4i7nto~`~c*ZLr*5Qgv@1oKMxGN9Jp<$$^G?$ zYpU|Vna6zcEUm(c*j~>Lt5z^(vNR6gvJ~2;+`BEAJa&xJ_yBypkh5Um;lLD8}fo zCSgad(K$|E0#3_F5I-4yU9RDw_ru*uagFAjB>Vha1QwIf?*IJTxx(rLK!B-PwzkkB zL~+}PZ_(5Je6Tba(s~sUL37#@e0#nJqiRSW#-4A(j4;oiNb+3s<}joR8NnCzC2kKu#ud?~X;WY2(Q)po!qwlej(BrEN+{+YkO^^B56^sTxoZa zy3B}u3-=%HQ)Z1Z+X_h%x~&sPRuDS}5&A1V%xWv#Rj(Oj(i*do*md!UxP=8w#BT5OB6#iAjE+n_@P@$hj zN&IS+_G9EJPa;i6W^Ho&WPG%hz3M*6&pf`^rxywkIkiBe(^;yfB9)n{$IB_Y4Vm zT0Ih*0g{|i$B#$)5kek$J2%e~h0`4Q=O1;Bzpfn)rLS*rF3qxBO%w(p1B99R367Iv zZvv#JwG6Xye{M~%A&-A{razgG<)!52r2w$bd!!G)8d3d7m|Oa3q;nDZi#EjAAPorx zox+~9Uz3nVE|NInh6f70!#$-sS9-OpY;hV#Jw|=YQS7mb=oy-l( z#-pUrJRjEErN)mT-B2`d>{mFopi~eiulhj$+5K=*N+O^>Yay+sj{a%ka29qDB4x3lmaed5rC9Ho(3aZP2` z1*1Hm`0}71OXCy6Tv08kFa~51sT?}gJ&eGr!ocoNi7&dRuG9XloG0I>W3s%GYI5jh z3Zwf_Ln?9>JgG+=)zwn4m=`i3sj<%W?1YQVrM}JCDxg9zRorAe{E0mtpC2Fr6NxKC?#&LHcPX>N6Q}$19$$u>$2nnRlKqC#~WJQ^H0W@LOQC zA3jTFxurK1?@UVA4>{wbuV%Pm3?fz~?eYrj%C4s|o5g^QRhw$Q8~M>U)OIMfV03dl zxeh;|`pnO9I_cuCkBQS!YEN7nSnF6;mMIK3T?Sd(*o-fi3@u%nHTZ1-YP2N2i4KRG zuN*&_o7$Vg*5@@9dRX<9sozp#^V-K@ws7}UG`A-3VU9HT)FP^AFR37BiMu=a|KTGv z-l=xZz@YgnYqKX+`?#dL>9Ox-wCd_752ROGCl35QPZovBZ-XN_O4Q5|#y+$xO~$-h z63}az?xcCKw8E(7PTv-yfKD&MS0A4$3!*cAxytW~`+T|z7x2|F>Zm?y4B=YpjwP9; zn;k_!2ww=1wHj+Vui#RC%FuPGCro^-1hw(C2$L!zUp z`eX=ez)FBIUQAgb>UM{;NdHeIVrsMzd*{4}^bcsq5~ ze$Mz4bXZY*Ap=napEqN^CQN`X?v&2;*aBps+3H6PJv|)*b@zLG1D<>tzFE5Zsd37K z<$a9iRxH6LX0he_?R+RnH_it+ZtQ}Sej@3aJzq?WOxE5AhCLZ$VDCCO4iR{fZINrZ zGw;506Bt@h5fX+tk3LQhB&f`bZYE||Xx1LR2)06woAdQoZVFRKRdJ?Qw5qRELCQ}4 z5cr5{rvg}IIqr)bel?wAoBQ=fDs_S0kP$C5dUY8|;C;w`OB`aBYCQCis>P9-;rrdw z_ija?;+?n`3O00`O4N!qa_qHN1`8Nl z{0-9ewcAv;?0crfQ1D`Jj$_Sh0t@C$E95FbBPpwOnH6wnAN8vt=<7J1`jNN~E z00ME3b3!=A^`!e`F46AKWO`%&djAu~neM+wd3zoYfGcE0;m!`RgUIUNqg%=cV3FqX z+XFxjR4E$#ZTQ!mLI3Xq_~y_zyA6Z;FrDwNCYhTOn6T-C1Y$mYSN43`$kk0S_%v*E z{7xzR?HTy?=K~P!-WrovZu+Z8`5RMpgQKItfeUw)^YUuI-e7o5S>ITHcO#4!b@F3W zg;h^_Z=$0=_LPzTuJELMH+^$-#a*_rKJYQ|w&d$QiP$!Wd#V-m>noA_Ge~b;fFxrg zsG+sBC2L(RTw9*T4nsA>tseKq^^WFG@~HWw^p7zNx11u;D0Q^7wYkNXbGrd!yl%lk z(hba!TQZpT2Yku(@&d5c5|_clDdNihseeu!#gYeEQND8=tzRMDy>Us-bAYjDtEH-b z=Zb18uV4yOvwbXhMLWRq0V6Lex`@`*qi08&_6q@5`#eq)b>V1ft3M~T2Zz!L$EI2} z8=i`oxtumSr7p;Q`5wum>0gWYv|#+|pLSpyA8?c92+SWhbo>r87Zee9?z(pggF zeueu(&r=-Qs0gf(e-RZ_6)L+n0h zLgDXdzf?6j($vCCQ>Q^6_f7ImxhVqn$=Me&Q|{+_-_Gv-Nfr?${HH7}SzCT2W9dTS z`xrSkHl&}&6sbVv8kmkVH_!+nJFwvGzVI@Iu_0Y!`{To*G#_azpVfV>5O?7-s!LCv zYqSx#`Sqc>5m$9K&==&*iiYb3&2iqNpDUN}Xo~#A@i4+DvoN*1*V!q`0ivp|ar(GGhwzptjdL z0~{6pWxE%z`K#*^98o91*6i_t2|8YK6jUqrJU{9V%+6Ia8b6+CPjNx2e%;KNzcRal z2SSBA_!^Z%_={tNkG~LR898?)OxQApyrTW7Xm24!0lSgeFuYhf;az?JX7Xij2*1ac z?vjtJgfmVEK62v1Zm5OVmX;SfK1*AP=dyFgf4qxQBi;yplOT(x^)Hno)lhQ1M0r+9rC1XCoHCYdQSU*2SdWC1x>0n7ioBtAVDwhG z>Hw{;O|q6r>%MbM7-ItK|c=Om0jnOf71K z@(`)q#iyST7()y$c>#nFkJ&)hQzQrUw|wj%#yVcg{{0ZR`+$SCqy0~AnrwGdIXEMA zjRb-{tn6Tc?ab@fY6=+b;Zb`?MF5|SuVlKlQlynd$k6GC>`IS^t*JgJ-SIsTHWq5^nE zrSCp)5nj9I7zI~$5SAN`Jr4#wo3eZEeh^f$}QCx;}PW7{v>L-c6YP*{RdXRY)SyD1wH|x^| zXtTn_@r(@qskGC9O?Ocu(DNN!Sg4T4d}<{38Qkcp6^&rg%yBs59v=DhNmj7R_KD60 zbaWtKbyfPR&w5Kx{)c^s4XrCCx%;}0>Af3*e&4V7p%=5h$bGFJL~kFk=dvMzPBn@^ zKiagB^zQ4Wcvzy9ht&Me#0t9U^T$c%p-k7GvDN%l*uOQ>Ds0AFeJda2Jfdeujs7dSd#R zn-+_ELRZ$AQh1#5gVm`pCU#q}iLmP+$Xt-~ZQ^d~!KFaB^A=t?lFI~^il6Ax_lRFR z|EA3A;0p)0{BVi4=7v_jmk|PP^9WXvR3iK<($GC`!6P=Po{h z)bbyj_Eh~oA1T+1;Lvb zdCi?P=oQEH{I*hkmYQEh7HHp~OL*yc%lg{bo`GRD-5WCzVhhg=${8OH^1$e0_pag1 z5gdWniVr}yUMOME7;G#X7SgnmWOk=WGk5v`eABxnE%S;O|pu-Dk_(qky5T(5uLAxk2!pe%m2ft!2c&uRiySl2dcM{c#eo@~<4ne!Zycb?&%ROAcl zjVXS4K{U&wU-IJ;4Aa5^d=TSDpMr03ptWcr9M|cZH?fz*`LM}0IPHY`?$2=_021|B z&E`nK7%6<~Xh2t&Hwf($#n0CltM#+noB>ES)Y()qMJ2oG9UqBD?7`Rz-R}|kog@N2 zOma0vkF*4hq7MvDQ=>ODK_g*@+EWcw@*>D4OM=FOoZ3n?neoN*$hFjIOtqjL;|E~g z2Xcnm4EF(Y;9UCo`yv#kFLzP6kh&VvDL9Nk63KN688#7nxVOzlDRXb`3;R7oGqVs?c##|Lv1Zy>dMdCT4GpV z7%`OREiNP5t8N@07L3TilS^;WrOCcz(>90&NM9Yj_KTJx~whIPhO19 zm}KtxZXaPs+u5o<7nv*f2$eF2#gMC3nfe2`N@Byw7N`L4l=>#X(fxv`ar)rJ%Z|=E z0l5p~f(C2EU97|(oj)wmhVOFpC}OaA?ieuUyG5GFD+{FR@vkp#0-|Z+u`*Oc@@eB% ziNDq*lkI%&E&8vZ)T32yI72rrI^fB6704M|X-vb~c(KEyV#8gZn`fWsPpH?22AK^` zRFn+0rh-7U8w@OH$ZvcdND!6c>=GZ5qHtn~(b^(O+>dS9|4y`@NVw0Aq?3yrynPz0 zycru7>LuH{stFKB4TxVduggF46TKW32#^`ynvkipcFc3$8ezNc)kq^yXewGVm}QUo zqA#{BMe<**{Tw^$5ZBku^ADy9N!%;#Zi(jjao^>HMsd$qae7V%*n|1>Ys%zO!<2s0bdwpFJY)!yjvc1* zwiqj)h?Q3k`9y_wgWTxw&QBR0+2}n0H)8A>f4454b3&LAgq-P&!wA@6yjgPnGLRZz zd=sgmLYPVq-J;o^h#`w2NHfSsX?Ir)Evk5|L1)I(?_4Bw->XAI)%SaRbo|>0B&$Qt zn&$pKWI#zn@;h{24Q{;_|FmA7zw2=B67aR*NI2ETky03cX}(?rZ*gISpz;diNxZYR zPDpt~eHfaU^z0(|n#>cv*vkxf-aRb!@2V+stF$pSNKz3oa7LVA6 zLWgLQII1x(b<6=?KHQ6*%X>r@=wb683f0I1{Ye1#9s|4J`ZR&0&4FpxK=J-8mc*ks zUgb7+xY(e$*Ok|__0@Qhfw6sKFDC0p(1eY7sbvwh`W7;Ji6Yyj@Ah%HVMghr{Jgdn6)A;W6*5I z7ku;NgUV+%bBh5aQL>c-h`_Vog`teM(S*DFG!~ zYpK0sbDgmti{u-|G~|(e6}U>iceSvV%=Y@oN)L^L8f{QS0t!i+y+J7XU$5FredPw$ zM#m-0=OoF-6_az(2F7z_pWp|1krDTBFX?b^qtYZL>puUHM8hgN3d)v0YN&i)JQV2c z<;OHKi03^~o82J27h#Q{Jj$=*JdYUH{I%Fr<~O?hF5?BRI~MTz?rTSx^AqR|Gkb~8 z+7Lwtpb-%#|cHV|}Y1xXJ~aJ@?IYYg;ELR3>iV?TpFRbouy?7_6O#k!)SuMHyIxg=El; zcC{8W$`+fHTN+7aEG?r{{(8@lDj+k4GHB>>Cf`BH~<} zKVfAR0k{qgzwso4ImeAYH1SDV5M6aILBAN<;RP+ZWKf6V^WR0{Hxmc6{QvgxpI+qq z-zcv(a6n?+h`+$P?f;=)dWC~Pmuj4UwuR&-{|3WLnCBdHM!2itRuS@HqO|3y$}qsl z=roJ=Qz}|IFBT?k3*Uicw$8$+;r1m9o~B(%eE={d;SCLva3>nd10!N0M45_g!=VQt6r>0!Z{uEX%iLc>j`BunPDdfPClA1yww5g3br;MJ?)$$s zyJJ0lcXyf%yMtcoBxxeQ{%<({=X6WPkQ^;Yro(IrTB1V>QMQpz0uKMihu?T71G%4- zIoyC83&Nd?+iJ*4FWjZJ1O8%y*1tJ+`2Z}vfx$%J)qCZ`2cQk@{tgUd4#NHKssEcG zf9mw#Bm~r|`LmpsA_kSw@)g1TimV;N&)XPbRp_D8pp4eDVtDF4HVt0QiM{Rs$)d>l z!HNSNUWJ_AYu@~$j>7c*UONy)JmXKJ#L_(dMXL{<8-WMwEi|XE68>(9w5B0`(rL~t zBVZ4At2A5?p#sCc^qgY<0Y8o$ef4?93hE9?w7pNke8*SX6rF~N7uy309|f~?oPWG| z6Ecg2MuqL-uyTSoF7s<29{FcEY|s88M}<}lIp&!BBA!nw@xW@Fd7Kl`4I+z?rCZ9ai>Ul?qK>XL^ zlQQZ+j*ux&^;heIQf&1ShwSr*n$3=F{{0O2`2%j%@@g))x8KP;0UE6l-N`!=NpJG! z8f%!sA!B;>2{U`s@gqMdJV=wrt$D#Xcrm@2-eKtIJ@E9^@Hy@tHUTVX9t=Y)y3)E2 z&V_uVP>1&b>cEZii9ZkGt^lSAP1lfhd_}uaUH!P|_k82x%pyiUTLkUrX^1taZf{0B zVW>w@t*9+vH{wjHd?kw6mv(c0X7y#pY`_UIXDAh+Tc4`^Yv{Y{Hxm~UH2-UGCsEgbQQRUZ3UVXx)OYE+@W~Ui(PL0q^7*sUiTk>!Qi-COP7cfc_Q=HpK#I6lk(Mrd1>~@f& ziIc4egF%M$!CK!oLdpT!m~MCW1mBp+XGdG{e&r@&&Fx`QC0MwEb^V$GPLV>KO9S)m zfCiwK`H|-3x79a$jnl7nYm4S8Vl3ZbMu&S>>Uh$yAE6-?xN9x!3QxoEr_X*p5)y2j zRIi_oSB>DF!>4D|;7fjl%8Sk^YALsYj8r|*KoO|dE72yv7CHnlg|55G(S|7_B!!@V z0lv4M(1gEYhOU>;wNO*Fg!opxH~7$-b!t4)5qnq=SI2@$MlILR_@f!w-pCh&P3eOd zHu=0>;ERJ_ny11t83chvgDYdB&~_6RS{F$i`|Gg2eB zO6s@n{MeuuETvbtIxSP$>hfKBSyO^ri}uk)^(iqfTZzM{n=knXkK@L9!KujDWiKLD zA-I*%MP`yIT3y9UOC|$?EvO*PF$U#SvJFRToH;nGz>GKJvkTCvoZ9xuiULEd)JZ|U zWF&;Q&U8y-Pw95D+;q?Py%!Ygvi!p`GT^C7RM4RdR|bt)o6a(uk|Qqx-Abp+dT^nWjLPdx2U z)K)Kir2a$YJ(ka84FZT30XdTIetKRa()&A)>?@PAdkUTRI$@SI>R>md@Jg;N%uX_l zvVA6yw(FpKxw4d_eDezGmHUd^F;95+kHY75NQQ-v@HP_joQsulEYauK0s`=RDoIQ!eO zq&d5h4-u?J4|dV6o`_2QqTcSg5o#>Fz?JGwRHpCZ=&}{8?qaZd#8@0A=%OEH{@P-f z^_5-M@Jen=1`U<(>x*f}4+k!}VH{R|ty{h8xcqog1EleU{do*b0?}PlFAtj5O(hgt z8k;(9J;q$48Qta$G(3BBiY=e0bOVNdqQ4ZrVW+cmfF^dQIl|w*r!1y=Qd94OKz5xR zWFy^;C^1zJXeko@^U|H-h7HEQ)!_Hb9t@LQ6HVV> zo`p!LB(>Knx=--4?k=cu%kKJ(BEUa0Jsn_}Z~upJ7aCfme`KR(ELItiB-RD`!Qf}U zy4r~FrB6YYqFxU*a|4?~YpN%-{!-c>#K?Lwr$MOQice*g{EeY{evyM9l;x9fm z3W%hJ2eDqSG*N&gJxsCh?;GCYjd*eQZ>EUqRvUBg5h+vW2WKT2U<&FHSFCsNs7+;P zG@Gg#H$>Z5*G+P}gp?6u81zseBvgEnD)XcavIBA>S%G4BAH5kEb_@B5t*zq@scM4f z2%u-EpAcoPuCwpq=YcY&k|`m_r!u$k4}eGs{0lrX-*GE6{+%}Nu7Q+ld@6cw`2gr- z%RS4#B%fs$h4+SUK`t3!U~L_E7O!vk0XR^;TaN4ZqL69mBLiXSKW<9H`mj{fn7rwO zEPyD5i%f}^d7oVY`KAZ=-~o8*HDgVv*>*{#xP$TxKt#L+nudDsHnw`D z52eJ4AArUR=yq7a0}%2n7DgY$0kQpMc18q0EcuxjjzRtJ!v=R#qft@%qydIU9t~Cz zOIM;!pHRYZ(HP!*`BDsRDu=c};Cm1OJRd@cfcD!eeqYn`2LNmcaeshstN_a&7{Hcm zVel9R257)55Sqv|b2l^hC{EBL6zJ9dZv&0{4x4*8eeQs4csZgZTd&@LE=e*0z8w0{ z&DS^-te>jCtZurhprHl{%&1Kh+cS|DWL@e$hrL}Q@s{~fb*q&9JB`0*@0WqE*GeF( zt#C&vyoBXDynpa$Vr+{x{`Cmv*hyBDH&T$RtA+`sryzpbt%yD(#Z+%$NKz#ArGi!G zqqv~gpJJ_yrW=oqjJm)sZFHf?b}cLzsKCHQsmy}+n>73zMyeOaWaEUd>iB_AK8&k+ znE>3p{g)RJRntu{>@NF;LMQpJTTK)$OBNmr_wdn_RBF%2H=O!Io}yI!%Q)hzl-8@1 z64(Oj^%GtaQj!iLM#*QmPN^h%35DGp4TN>97^Qq!+hE|XRD7O8<@-m=HIlr=i04uH z`O{@qBkeY8Py&bf>73G?34?CV&QT|>A8*_1J{XTkFyfc?bbRRbVO#UZP*bOYH^e=g zS#xM znl}?14e;L8`b@?~%)(^`8vZ@t4wZ@K6%yUa(V%GMFOVr%;r3LqG1@0A-uC5Bmi%helb>C}&skKboTJ zcT^F_qnz}k2k8%((o}I&EcNrVRho}A!aWy8o~usRzc)5k3s-@S6Q7X0d|Nf*D8{6s zznWFnzms(k>7wrg`F;q4SK!Px73~p?=6k?+@p%!5Y$;0d{YW$de4KrY2^z0bBH8}3 zs_=6<1gX_6Pi)oHV#O8ZQaFCJQsQia;oSl_{H{3Bga`@--)X-KW-MTe9%A=Q;bQou zxOac5jZb;036KqJOS(S#EV)5tB@6G0b@t@Qv|P|m=C(+gUnk@fjbPyGXY%@b7Zj4w zdAz}}wO5>DcYv1h6Gky3IHK?j+A3lcUH>Y25jt0>LTE&0lW5cJgqVsz)x=OE8W%qp zD*h@ZvE2RzUG4P#kEsBqc+MlKjD~t``Gh3eu9NQQH>AiCwpc1oQPk6s5`JiDTd&66 z-6^b7beL)4E!n%7%znYN_fSVr`)oI_&)@&qCAzu|Ub7*sfS>Me?SN(47SBZ+ zri)duIHOa@q7K9_Y|HclGAA=&q6IDG>qU{pHH)Lut2f;<>5Wx374}*nTQH#(bMeJt z3$^o?p85sjFnN9r>|gPw-$>yEMcDd7Cx|Gs|A-7$!1oC7ZC>O20-nuif(1mSLLls~g7PG-A_KiQ_THjGh}Y3>5XV4IMUUDcSsO!@>_ z+B)V3rvt%^jF!BRWr_&K1VaOMrQCT-!-!P?6(n@(6Cq=-cKQH30$-3dk)>0plkn9n zJU(q-Xk3H}P%6H4LTK@Iu4>ycHQ3M`f3$M}-!>-v$_CHJf~0Y$AZrY_ti&?&Svrl& zI#w{I)1tJNyv>&)%PsGNPmYuas*yV0W?vaS&w`&eWq1ITD%_>9uL_rWufCUFL6owV z!8kjuPBC>|r-7wzjI_;`(z^#<6uy#;21E_sSlE^ts@ayz8!sCU@-dR&MKHL1TrEMS zR%Ce{)ILhhL+L=$L@;NcMG3 zTlGXXMVs#noX)a1rY`ZItSO{)doZWo#R>A6yX9xa`A0x)| zr`Y7O$K4gw-JS#3k6q_Jb9UJe2)&&7jBUkLQ4p3N|5J^tpr@xiWnnAD*jh0v!Y5Q$ z`stI(uFGfWV?-485S}ZU83?UEJYjz%Q`LSDP!C&&>Gtl>@Wk91H^p9^?mPe{(ca13 zJ5eNkFSs~eWxl~<(&ySp5%*azwx(`eoQ}#Gsn)B6zK9Kt2M$B3A6~r3yH2u|oK9KLv0dQNKSr>>4Vv zw^sM!ran2J!8d6>fVAeZIBWw1Qz=#>BdgIOed(MxuNyeu7tQ;o7|&V8zHuey?|iZ9 z{wWnXeiQ)-`2Zm7#JjU}vkyw_YoGKm#)wr)h9?Lspk(2d{}$BI|}48>`eSnnY9UTnLP27<23!p|n3d7iKaD*Ag)Aqh36=ofZa#~_Ma z7iHOg4fg!UpF=vn&;~geUxkTsR?(QMl=E=X-YRbLNj!^JQVaD8463FV7M;f}Cdw?o zdH_^vCeQu$6~AxJd-4xzak1bHTG1J5n{nnn?na$A28;c4NU2y|0&7KHfz~+`Q?=`e zeMaunRoKbp3-Pw@KH$gskn|(Px!V_gCK!@#+uE(}axO7mRQO>qF9_5ZNjnW~k^V?l zLOlxa_F}0Pbn4mX{%f*W5S{C(1tUf*e9i)0X49QtC6ycm`{uNc`ojjK0n74rVR z72qw~X1i(rX^-4&fqP?OYk(t*C;}mz7GH@&+6;MDUmuI7q0FAM(j}*}J%j@>>a?4{ zK6<#wl7P~>1jpBjcZ|U3ilV=%@%^o`Pa;kxPC%6HUINM3GE8z~VL83&#jk^3_mD1v zicYiGb9hsilcO~2q+r>3b#sc9?`}CUhoOzZWsMF$Kbt`pFD(g!sOV2e0(hzFvFE*p z@+6}l;;smvv19~O`%!nV(i1C!Bzjr0&(|Oz4ijsu{3_$C2>DpJvlpolrax1Jm7Z^f zfoZZnr{=R-o`VGjzSh={wq%_jEpwGjPTdb;1xCN6UbPiX>64SpK75`TKje+&o^Y-vje9lRtDNEqTE_OZf97}j7+a0gXcyuUXJ zL;*Zx=2Okikf)%cGFLxn{&p|0PMlM*L;fT^COWY2E4^v%^6r-d>h+iAHNBy>QEV^i z{Lm6LA@;s0ecj?hl?Br8CH42TD!80C4fAy0d5mn4p|dl7-7xR_kmvpCz2+-oXahI& z=cAI>u8`wFt#TzOQ%}V(i8`!wUbfSE<-j5``}J$BA#)E?DR*j)2OxgotJ%dh{0Ltt zUul$E8fZxG7;eD+1i+gT9&_Cv+w}eE7~R0%GC}J5K36N z*W!1#Wj*($2vea#I95=GeS=mDH#z&;(|4PPI6A&_pX8K8FSKm7HF-Dh`pVUn%h?5b zM6*jlwNKQyOD#Ql)&Y4sA*5zlURgEGA7Y$g+CLUYlt(~@JIrgSl`7K{q*76OwSMer z*FeS5)%tOIV#!z$d4j^rxOg*(`{$Pry#113%pqyflh8;Zz}nVQ8}3m zheTqX^rfXNqL&GL_=DESPHrihMPMvBZLhxQT=DOPiTBZ^_nmer>na-Yo#;ji4fe*yIn&r)Y(+7N2VqA0W6+(Itln9DjOxBp2Zzy76+=;K@{-dYGU6u;Zll;yU*{W6|S;dOZFp7at6kbrhq^8i@GbeZqL zumI1oJ6s4%K=yGgj(rP7%C6!;!i%T7%ROq3m1{_astl{7Yw!gVb}pC&DLyE^8s5Zd zlM6=-lXg& zIo(E*;s+@g`uIcW{L1WV#1qG@qF&}?hR?|4z99SSxumU8I8wkfq0lKF?u7HIdB?C= z&jeD*#JKQJ*d0NXw{E6Pgj~XCCH>h*eMEipoI)d(yk7Npm$thM)ES@-Py?tS;% zbKV>GoDs4lD-lOEeecgrj_udX$58I#OYlOuTz8NSL7S-x!>n{_Q z{K)5YiwH#0HyF8V3Rg&Rzyl*&@;4A%$~}|5E%x&d)uO$zM12w@Z*!LxVnTU66J9&0 z^fzuJVqDU}1JmxM^~we6^iO{W$*+}PO6H~Q=<5m7P8SBVPr*-dKXJK&z6`7faouyS zL9x4eZKwBK9<|Oo(RZ{ryJ94_wF#iY_K#?E@<_yzd%jo|e5hSpFuACxU|w&}+@Cx8 zW|i?S?HSLpwq|9#0*f?euB-voNrH9f1@d&~&jA~l5V8`!YImCEEbm|4LaRAxIx=mZ z*&c%fj3w4tg8YgHKpsN<_QO;Wo&w`9O|)hmX+OS*pCZ9vN$gARr_Dnp-F!t)VqZ(L z(+n-m7Zt5J$aBgcTJdI?JJ!j$*cXXaUo?{S<$oKX9 z%3@lwiQmnl>M=zA8P%@5AXbinkKRkP(w?wBBrLnig0K%Ux3E^vU1VB6oN&nPh+S*f zG^e%(d-|<#f_vWa%RctiG_&uR^^dy{qMK^GRaXpW4y&D(li!<_DvAk^wt z4`MX_)fC1?N*gn1U={ktI{s#NnWq(Ot}GuPB_Mk7wdU=PAf5`eVdWMr36!z*_XudH zMo;v{32!zxC0y%DH$Q4wRqABm9#{7bL^fQmvlAc<+iW_s`PONrT&97$B_Az}v>IKJ zv`w(4Al>Jx3Kr-GP^D7$!p(PBFnctfEa(<_wHhWK&R4IZ+%3ZX4sgGK zrU`haLJX$)l7;uj>1tnzBPof2MBrXz!afJeeJfQqYJ=s@Md-rHM^^#WIb?12=tSnv^` zbFfSX?$TQVG19ktYhbCYkOLO!)bt17YMYSimH6|Lt&hc$_LMER38hgq`*zK( zm%WXB2)=dKSgY$6r{%g?BqP&Q4l)q`T z>VB>VzU(t<*f-}PtQK-jTL899oUR?0KBZK&m^AbC^_NG_sM6j(n^>gcut{}Eq~^|} zf4f$!4e5gJVioViLoh|EWiftrA_>kM< zHx4x!(@qsXDtNFMgeO?bpJ~1Efy1|QbO!Bd$gN^y$tP3SHCtH6qETLXotZCI60)_N z1zE_n2|!u6qboif-N6v6Bmwc(;VYZhu#yt%4!&9b80*c}>1tzXt49mU#9EC}A>i1U zAXl{|Vz$?pA8vB{(>o}X&3%d%lm*{9ashI@hhAI{!}BI$9^uAdqlTWhI_Rpau20!^_SsF*#Hc%WJxo{>gC+# zMIN-{W)3I64{hfxAFcvCKduP{2_Gvpi?^S)@}!!Tz|9)i^hsN4N&{%E=8PCcfyok} zF*8_1t)`&&3O}<}Od_%!cu#KcUufgeO|Y6QR23&M#4a)W7Cy}?1OnC*|tq|5d?6e32pA)1N&Uz*b9=HR?oYtqjvCkAjnVEDd`DihXl54L=Xs5#Bt%jP71W!oxg=~<5!hzZ z-&Pj&EyF&lwoC}jD2w0__~FPilm}hSVNI+*!5qsb3MW&UqG`%zcaSeVZAD7fJz~%0 zA>WoMPhLG=#L6R~x$(CuS+JfidBe95Vw)IADs6`k?okmS{9Yasf2O`abY7rxdY4t$bd$a2)=!DgJ8!;XV; z&;$5i{S?1%TeEqhoL8~29LC&;r?a-sizF;Knx@#iq0H5b2V{+z%a}EZ=ubDvn5tt+ zeo|to@d)I=tYu!=4(bwtzJPe0_c<%*SUuP|9l%g>zKMt42@J#h(_wc`r-grR){so! zlhss>K8#W@tRJo=XpR%u8P1lHMi?#u@sHv+4P3fMqp(FCw!W=coy|NE-Hg!0s z;NkG+aXG`@VRC0=e#tAN{s&}!&b##$K#s1C6i1N#knRF3>Z8iA8z8~k(1JIpSRQ_r zL*H)KoMe#0Aq4VX+aNycO82^EyLlkGZfv00>PvQ9a2i|hCO+$&f3TU> zCkBTIx6ApHr?kY=yVzsI;y2Dx>M+{Yb{!U7g@)$|%Gvz2=R_wSxO0|rKe-l4K z$fbDs1$j{`o8=gKYm~h`&I!dSYU9mrdrNNBsSj$9!&is<{e>8Ym<+(vwxT`-Eg{0t zEhW<=*`lP6mL7D}Awtx*6NqpB3GVsNvCdx()zhEdkmb`}Uxmm|47;#@E%kho;3$QN zJ9gA82G5<0Vk2<)W~t#-6~VC0ZB~_w7k|}5U%sV-7~v?mj5uC9=_?g`67tee))@+8 zysVZAZ|`l~Sg{?&T#^kz9CQnK*wmWvCBLIN7f$BI3)7@=l;uXOA>VwlHy z=dvBsD*BW)^Q1Fs!MMfOqn1m}IyN(;{>?+?ssN%C6z&W`;7CFq;eX~^v`fMo~zHQ{n!Nw*o%2sD(B8~ku72oN8ok|>H z{|*ATz@w~Y z7&HO-107JA1ycN7v!c?SJ*o$f1VE>O{~v<8DQN#CBhaDS=)~U5p*0aHh9bzLf>|$S_v3r|pzk4^54c9vFnHL%x8T6t&de<#aktq1+Ya)`b z@S%z`AxvmaE_KuGrb+ou?D<0H!p4k)#jCg}vq?GOM&&?WZVc8B5+}<%Xty4K7S3s> zE_0{*dXzt_!yT&$myYgb=+F=k3Yb8Qa&>kE%KjV{9XBY?+ zr8eG%J51{4Li9@>K*xc{hgA2j3BUdrDIy?;NE}^x@a_H$l&@?|ar%KXqKQ(tDWwm_ zcPf!m{=Goi%E^wbNPBfPipH|ZK6yBKEo!lO00O!kIOdmY&v7d-_B-th^7$Bu>KeFJ zawk)|f5lvBVw0X(@@BKOb(%qt&~<2TE8mqn>25r~|5Dy8~7_*NN+aUvKW(cX0DH`u$;^xLK>J@)+vA%z(a$ViZwto(OOaOPAZE%+7o>B#1 zv8PSyDQ4TZZbB#3+%d!9&Q#^0yY7`lgB1%gGks+hyERPlPKePHS=435kL#3RyOy$? zlRv*g4Uv0b+JL{kF%sII^ti8lBd8_ba=CfG>qsI&ai4-j*HZUow40+6P{&LxNd5$C z664_{JFt>zY@fVW!D>CXsU0oj#zDC#BUOA+Yh55=GIUO^_{n^DDTE++x1FToptY|S z&_k`iM<14I;%$c*^$>-dk}W{vm7bvF!A`%RMSG_1Nbz0%+ zM+TRQS=Ft(VAIz()b|xRUW)yQR9lo>h`eBf4UWxKO?*s2yyON6li>%yIH8?^xb>nM zeecTmFGL#3%t-}rksj5aK^^8FDVV*untATfG;)on#GzF?dgIm2S+M`ynEvkRb<*~O z9u{NsQWDQr{1~cH`+Wg-;^Um;Xl9IG^QDS1h=1#7{z`2VIQshlMl6Y|Xl&`gz((_d zmVMgom~wjklzW9Te(&VxxY?5oY9^ zXAvZB@oCWpw&Gv^e))dC%6|{iLxT< z9&F&gBI`6FDuBvcR(HyMGys!!?pVXWHq?#;ms2SDc|sCkgplC2TloT^qu|rdM7?{6 ze9Y6@XsM#gB&pI}moM5N90fCBg12Hu<{$P%cx{h;=(n~q7tQ9r+9gZ9Sl*P0tGDbo z#Ii=GP$8hG>JjvuG}IqTk!C@TfAVr@UtSu~15wLC?M406YNp*zvXE)m5jgS5VR=mK zW{|wk-pqNBv-R1d>Rt#FG96v<-|46S_FYRic3bR8gtZq5ONpfD>t>ji?ME?Xf0vTo zt7M4M*1+vsoLUH$#!<+}omnM(59MaS3Utq830S)x1I$PbSn9*dy$i7Zxk<2`dW^;R zcsI7{8$oOu%lu29T}wPyc+88v#3X-Cji~Xv?puSiA9#|r z^nJnhHmTW2o6NvpxKdrOUE+nS^1BhKV-!p00>k^lExOPWrw<2Slx(wn8yn&t>`-Xc zj><>f9Ui{C5S7|hAG`PmA3kU>?&JUyaG=|Qz( zyBkDFJW4jtRJuI!#%l{ zhsBE+nBG`s8!Nlexv_vSWV07nEfShj=NAJ1*-skSGZrOUrX(%ggQ?$X71t(=FC~bZ zI?O{PjT*{=6l0Xq?s`jQ3A3D*W*V4kndZM$z}#8iz^Qo*61{rh$J<28q><9eI;< z+5l#N-%{j#@l~V@2gwH*!C+Rr!5xHpN5nzW4TLnY(;@xfiGTVd%%VXx|7>WjXeRj1 zfBb!K|NN{rQy$tszio93f&j$(pIxbc_aK_M&U#|KqOxUOFAx*Ef?;)Yl{0P^39#0F zAb5)g0g?Vcj3fE05oOXRrKkl!|}KfiCI)L9E-uD69w^Pyj?I zKgrVo3odr1o%EWtx}^NWd(>G2{f6d-cKT;Y3*}v(@XcHc(WzOu+a@-3yh`UEs)dFW z&6>m72Q2!aarHHH%`_RfyWZ5Rv4GvI9W12LC)7(J&=d3$&flK5Rfsje9TIBA6`gP^7v{t~{7SA*!N` zN93pOkYv`1M2!IMj;lsb7J5~q3>%;8++_u79YO5Cgs}eWuFY?tFZT{G*^VZTT>+>~ zfz8#J(~GHqhTx* z)JoP}*4(1a2d<(AoI}$xv53fUpoXQp z5?ZTR$WJ%SCO9vx0H4O{LjMnt-aI$`0b_U*6?50gkMu}fT_zY9R5I`1p8goFn_);vz@Wi-4Ov{oX zwNQy%#J%z3yx~G|hO~9CY-RG``{WxQ3}!SP3MJu8lZa_-Gy|JChWMEeHB)I6Sdk{-NY z_lKD+X(qxEaj>KbN;DP%OR0_rf!Wmna+|)Pl4-uF|jZrpK%y#FGw8Es~BNp=Dn_{e0&4nr*pgfn_b*A5`t{f7EQn`x?>y zq-p=EyeZ=5u!@HurFlYP;(LBh1}r6T5NIi~I2|TPlfHe+R9?0Tp*s)<9 zKoM4HyPT5tTZ0kp6_F9kvOo1OBB!$x+IWW^ykC-!1=pjuS*q_}x0 ztCkU&-i(;68+m$_IeTh5PSE3|pf2-tPxK(+O>8;i9Nj719AOP(!AE+!7Fi1Z)*>s zFwl7Wo|T!L?VAC#K#rSHXBfVdNi0N^ba1BKrV6rjFsXmwVr(ciCLndf-|&WZi*AX7 zmRFjE#?`MkO#&TWibcFUdblYqyh-e{(=orv8wqBAW$mekENK@~L)~QsPc)yG(4;A* z#E;+;9G50xVMd8{VDav59g6_6qE_P=@gCBEc|j8Puu4EvIp6pu0U73#GFkDRDgOuH zI=)Ze@b3K7NCq7}j!}049B*Xn%l<@v|C8L7k>Nu*f3snqjwN*z+|R#gh=lD>46i0EyA-;Mkal&M%B6C zbjefUkDnV_9wnJ`cb&DPqL_C)4<;6p`!c3Hrm^B0bl5SSf0q@$T=FO* zi+nXDG!|~d%A2Km2n|1VY21XNPxGs`Tlrz7GeKLvu5#Hu$LZ(n_UFTVbCp2puZ!hILHXgpIfo+>@(*zzF5Ey%n zSRHhM0M1I;wrG)OL^YZzPu6a{7H8Yw?_Mu0)%5aGGY@TQYKxFMrFclbNp`nMh77R| z9(VUTPIggB6FwQ!YHFeqh76Io+KI}GWa{@LM{b=yxkdDP9=TQ5G-KNAY93X-zJTOAwHZHA<#kSVg&WSDEp(HN3l9{C0)08Lo zhplU6^}~xbDVioL+mC7bC_)#BOJ<61yNPF^lBaEKDVgUM}=6 z{{%`1=oKUtfxx^n@RqoU>wo{k#}i)ZQfmF?;tl}(_QYmmDtB8Oj-%N;Wu3`8yA z$wWVkZ{teRY-;NzIowL;6oY z>QRP;8q~M0*?izl;KtN9xKnJUn<$TU2N4rQ??zS%%B>!+#Bm$MTyv1c!21-p&{zjX zn#dg+QBl*i&q8<#3$89HS9?GDTfj+}h z^>yrPaRQw=SZ8Fb?&gsnqj+P&+5#u`J`O~wfa}bny-|>rHqw}lr=rg-Vodq~QkQq* zcQvPDXICE9o?Ue$w$r#6vM&0LgCyFCMBWDlf)(|EJf3M>*24r;N|OPxk}? zKH#Pz2MF{1J4M8P|Bv7h|5U~QLaNYIhxae$)D2w2rq&}?-toEyc?GMw(RhMLL;#oNsG76E6QxX=g zvBDRYbSHV}e1E4TXt6##C1V{GfoLTT`t!?w0WE>!08Axmlr3C=FX+7=H74HFgy*6((+Dc*a$@GL~JIC@z?L6e6>krv(PoKY?)V;$J`gI?NEXLHTWYMrrUSKP_FZPAs!FAAq zlK+M{;oyeS)@L3*R_D{+lEtSd@{8f`v`{02BoE*(oMc0~++V1pP~*5_{)8hm=PsGt z{b|oNTJ%WKbu+DWyVzu6vn+yh`+_(orG!DZlm8GU-~w6YKQTo5_vw$3>g=yur6`vlv>09_Ir<@&yyo~vW}D9(~RN&kpnnX2^ITI=VjXd^b! zEdKGZ&6)ZHB;Ea1qpY+mRZM94IXs*LtpjAli(^}(KJ8v=)EP!|BHtqZMqB%qF3F-U zn?w0YEyGe=Pi*v*N_reTiYJP*0(DYwf=fa6!;w=)RdIs&iR7CPpHA6fC6#-VK0{=O zDWY_5+1=cGs!<_NpJtsnf4?Mnw=Rjc|1E%jrp7&vzp$lQ0kwQ7Lsu3St06JYB9tSU zaX&Dym-Q)C++RCdO{2svN6vT6+cIP{|HiIKHD0*zz0zp%akHgLIx*g%f4-4dEta4K zLW*9iWVlmxO|=7fDrcid;B{QuZy>xn*+{Ww_Dpr+&g}C?EA?3%aI4;k(xFm*`KNU%S1CduFZZd z_{G5BFJz^ESjGH-)#=dJ=o#7?Q=S&5z724g?H1EuSZ&LId<6}z2Jp|#X>DhFTtzWV zJJB_szxNwvV^AN#XEF`f#KK1yB*y@`B@#v;tmh)z9#@R;Ey%S^>Mv~yEPKbc_(p;= zzgr>L$B-qZ)TdjTAu)>yua=P+DYuOxv9dErHYl5yJT+rA8*#Y8j2sC{i`PS%l1bag z^pgCSmLTBDs)L)~K>C5*!8`g2-NSJr;u3ZalqgOP>r+`mNjxbl*61ornTV7!%}G%lI5;zb_9xR*cXWug>$()?Uz&i76J<%s&J#iKhe z{2u?qioUJ9NjgkcyYuo`5Ee$@lhAi}b0J*GFPadZ=@mOoWDf~t$mBJK5IS=sCpYEihBdhO`;Bjiz|${xR+9wZljkk6X>(TOI3^x`9rVxWCp5Ngz zg(;}=L@lC1Ys7Fob7cCviEc-W#`AWeMf-bRWj`|a(!h5zb%ye_tQCAkPVSN-pbldN zYOBT4!r}=31S}KKavW-2!<5IJuV-7h!KlcS$yrByaP%{cf*)A2JdbxkigMIjaw_HC zw9pG}Ah&*FcBg~+fy`g@?x`~Et;MoMWl#CXI5Le}qa~+dM#5B1(Vd(q@|yd$p(DTe$zY?^Cwp z7J6)IJd;f5E}V?ZUb0$C9{Pn2RhjpD?T~%Qxazd~U0VvlVv6|axqe^4vQ}(rkE{Rs z{Fgptx4GhJzrZ{bIs4y0);UxnOO7PPjVVKKh})r`p5gaZKi+0B5ZY-5dJ_Q6?70A8 zEV|0+tEAFJIVZ$#+>67EZxfgFMxAIxNxm%5GDEbeFk>l-5R#$?PE8UnMp@;7d?rn~ zN=Asmdp&;4Cskj-Vx*1e+U7H{qgftQNbIpfB1JJlg!y_E?X{i*%kmcFRQZEn6ymeJ z>q&ant-2ZyZSc!YXP4mhaX`~)dXsvQy-Xfz+3rR8s;27UO~;kX8x!Vhb1O!=(!!<; zcGaWk)y>nTNfpoiZ_jFT)uu4^B$kwkm?p!&c>A@}w#e2WfkUdA5I6Equ9|N*(}@Th z^W9y79bQklxd%oial>jR&(B?@7Q(5?+;!M;Bu}R0_zKk%=b^$XV94h_GH<*zmx@LJ^AxcoKOc-xms6JWo54 zNRqbGwBE28YoZ&3y0D5Wf^_{pkLEH*F0@e-cB!L|gs#gcde{|b+N7%(co)ZOn7VWb z@Tic7o7Qo&WONOOA!KEOj&>_45PA~rNc$Q}6dXM8GadR9jaHM)6Yb>(Jf&KOaf6h) zFejoF?@bCD7Slc{dpU`SD16%P^A=1?lt*)tZg;dKOQ&`up~jV&o7*Qd83269peB8! z5*gF7;PXK*@;K~yD3qZ&v9+M(2c2fGOsi!OL=dE^V!peAbFnkQcFg|{-soB6GXd@M zMG3#iEw`<5LbQ&TgFoKFx7cUF*%AVkUqQ`p?oiQZ5$es&oOawAs7-M@HZiURd{TOY zlcc1mIhE5ix{yXdW zf9)L+V1@tP6YMX^&%e{6lLt1>Y46Bx$wrqW5EXq17mg+Vqcp#LjylSWP~Wc&No>5K z-S_j=4~M3`l!WIxOnF%PcVF~hm6hu*+iB0~Cod?t+`X@mOA?ze)Lt|mity~;>CnFwv#61h8uM*sYD|MQP%Q7{ z*yLhYyAQ0UtLAp=2jn}5jN;FcBF}H6;0}^JrX5XRmysvo@|W)AhEeV-m#pY7c!wau zra}}sb5taKoTrAVxr3>{D`a);I8$T1{8p%;dh9~(kRD1Oquu0NCC{2oIkmlh1{r~@ z%cZ?s3mt5aklr2+`;JWq-pCe5_zDRKE?+U+Bb-aqZsO|3sc&e~)j;EW(=_~rapVHJ z+_YPSjJ&RfVB($Z6WDMG*v}~*nK!skdoIe?(J}O=jCAK=U&~almn&`LL?}#jesVlc zo?DYoaXYU{NDsy+Iez=nTl=7i%DWahOM405{i9D#pxw#srqYLpHa+FY`r>eOP+EWi zn3>NNmw8#X1*fkqNQz5!I&L4)WR5GQsj@cxdOX zkgGHTlZMw>PmDyN^k7VlGdNQOOLl%y?}2aU^{9DLko?ljMxVbgx5Dmm%#1I)j9ixN zW@<7sq74bH%6AqSc%;P;HO68W0ZFu?`eq`HeSLec+P-`a>ZwL0u^2wh%5*kMc3#4m zZDGSBA&A|Kb-wkq5khf19_OP2oR!hC-Wmv|_PUba53I~*48nK>EVVycw2>as#0YAl zI5rrl-uMO?IaXpFVkAl*7)Qxbns6riWQ~r8 z#flI{m=_qc7x%?%JtW&j`dxnQGozW{NgLvIx7~P+qo<{LDDPKY953kAFK&r{>=k%W_<&HHz&#Z}=pUoe)b9HrIvO#&#NP34OU3Pz zckzl?xV#NmLgP`qu$jyEi>n;`JIQFLhTKNSJIxWXTvjr_p5TBhpZ=tr++OIonjA;k z<-HeHlh`-a8zZFL(gnjlch*l)j$j~Cm)EP-T8}ed!dQOE9fGTfC^0vPoTcNn_8@vk z2gof~1jp_u+C5>fmfEgL4L2A``k&3-GA{)IF?i6gM9u%z?SGD${(IdD2Y)@CZ3#MC zK~j@&mEPi|*`hcPhsP>*pd2lQcLa?z3X2O$U66p(QGIX7xys3bk=eBokw}W(-ls(a zF}J4`lzSMHUr;?*_IO^(U=Ou`EGybh;pZuYBnlrZV?38*p-o0jwRld+r678BDRjBP zKfH3|dnaR8R&sKW&OTBmI*3#y27Vf9J5Y>c@88M%hj=jGI`?t`hJ9H#efuNXMuKzZ zGSo3m1g)D#hOyH~#>e3qnuR;1em(t)gu%zf+S8_dA>v!F9^cP;FD)mI5223qjds4d z92VM2jBx@3Qw!zrvZ0ZXXF=ahgZfq4#d(#iIImuO_0{^Gf{kO0O5x?WK)2yuk+mxP zl1D+tCM|&Ej-Clk=aUx&pdl^-seTHf4^{r2fV;y(;1?q4Z!0(| z)o+^=l+u)@aIYfiJl!z6RjCy}mg)t&aE_F#(hX>X(PmFpBAqU4u#J}`Qt0CzTHF;m zw7m;cL&7zy*qH9Hq`>`OFoRrDRQ<`vu}Ub$!=c6$@^Wd13Vz!PmVG2VF%97pIvCB4 zuBZ&f6WgdIJU5OI-vj1Wz<4u6}^xYpi*)Ffu|*dZqrpI0umHdREmaNJOWn z7?K^7u7CE2)HmTQlCX1R;E{4~!B2*m8x1rY6^3TVU`LUTHl`N#`{WIl?qipH@pYdA z)`CH5(WgJOd!Iy@~B0hYnAzMgqk_Q;ldOo(NH+#A(YJ=RjGcl^oVN!=i-dDU?317*8u% zO;F_QEa@v>gRJNA=xsa}TEJnrp)5Uz#RZ@+Ng89R3j$VUfaw63K}*&QBq-m6mhX?T zy8~WMy$?kptD_XNi*u9xTRsYvY3phk&zeWf6Map~(SHLG@4wzkP3?;-sp`n&bl=R` zR-PtbD7LoXuC#r_15J97d@$Qg?N(J}%~@n9cBPL9zt$a*=AiGBefPnQNPgYSohREto(RIFFzjOO+0GR6w}~ut~9kd4Hatxw@sV1eNMZxI6ULy z7^*FjHu4Ije@xQn7B_k^n zt5RfB(6nB+jNZd1>Jj6KEnhRB-b@{=QJ%QOpS>`^lox{VwKc_i$P2TO$iCb-Fzm}M z7%l<4rSz=@_FM$tw~169#%zDI5y+P*YkB*)9?R7?b833w#q=S2W#t7>F|FFrEl_I` zhLm$5J<}7zx=8P~dH^(hAj)e6IXSb3HULY@Q^|!P0gOe`LMQ~FIsr-034iC!EXYAa zx6^SBP(wYo0_;)%C0$?XohNXd)O*Q$OeYw>6xj=H&V`1~Tf}*QC~g;cS}w5!95ulJ z;A9BE_X5hlS%R1=s1qz$An=BC<=!@-$u95$wGI#j0bjfE0YMawZF!t+i9y`G$v!fiezt)^b~NgZxYrW~V-3Jt zV_d=5@I^NQ?DXVks_iv zEl^9RS{aJkXwH}sEH8!D1u~e~)cy&u`)4e4EC-%Q9N#c}3#kG8+7Nwz_}yh#H8=QN zg`KyT<#26c!MzO10`aJV$hEM;I?w2VY4FpY$M!0<6krQXuhvdG9lpV-{=Oc+`ttka=?7;1I!45nPGfEGX;P`d5s3Jzu+zjTzwiCM|G08+j&6@ zIJ?kG_66}XRd(Jex|`f$*sR>(htkEs@iED=2KQq7#;-?ER@s>hFJ2u z^~PU@L(q&K1}Z^+88Yyz>`%82b5&ptzy0aJ@n5I#@4gBMy(VXDc2t);g0LW)4n6=| zL}~oU+dp3fldsrsZHx&lUe*IBZhMmt1w=7{5FF@Ykri3XfgWjYWZrBQhx;DGr$&P? z>&(>^smVMsLSp|>=vnM(c6%b+#EskUo!+*-!QLvVo??8G`#{A!k3Nba_jVQPqhWwt zD#VyH099VQSr!-i5jkMdoMVZ><=I|6oalp$AtV+bk=f$mL@38~LV@JvwWrpYx zahNk2nqgbd#y9;dm>N>sva=f02|@nvp1t*o(DJz4mFK96l>Z@0}XKguxAZcjMWD7@=EU z6IZqyui7LZ=-U3b0-~=UL%51t6Y0e9N?GY?TFnQps>SqGg#1tVGf1>vbEZ zUDG|27Ml(*nTxq>yBEhhhXLzi@PkuB{=T}M2zz*mHMzmER61@1@yqJ`aQ`0_4HWI% z1_r&4+HLT;nVvi$)>7vJQ3Mgt3)=0{``Q9L0-CU2tiS$4kT&{X2KayU8T;RS-2a;& zlgvP=?<#`kxphgE{c_NpcVL8{tOqff;E6Y7H?zv`?O4-)Z^sHWXz7=3@mJV+W)T&| zQQP5?-Nm&bba2TZ_ItBc&flA@R=I{ROOx5^S0Tu_l@&2z z0Lb z!A77b6(P`g3L5ye#e@qm&|8U=JTd&~yqEBoy&#dA{~GN-_e$DmG>KH$l8zWes!!`> zq%#Hy@=0N2!Ht81mxDxd|JA>NeE8gRrh$R7f)w{hvM+#`ff=~X{x2V^r7dH$ zqw6;3!_kkga%clL6sohaSr|d8LSU>*?2N#j;~uITj-PIe6gnvmK<_s|rF0C)2E!;- zn(j}4XK$w+j9}F&kI>lOG#3oJCiOUI&NSW8V`2Tu72U ztcqVBdI`VY-CKU$zkwtJr~v_j8(_(jDfZW0(0==$4_T%^=Tf!KuLBhRy7PbbXbp%O zA#{p~lx_GNfhlj}AN5^m}3Z$Rw zqpYWN?90$mfKm2)4|C<;dzgP{Y)D2$7vCCLjde(Qh1@w*@VmIg7QkzMwqxPM#^d16 z3LM`B^oicK`Vmq9P8(+h>r{ZHbSu6ZJ3o*=KTs(MqG-RJlZ?DPFNh&fk+{}qZqN9F zK^RWm^TrDNjshQp=2(Ar?53@|q=(mf=v)Q!bMdqqtF#Audo$NSsCl0(LOMlP-|GwJ zIK18s3JQ0{29>8UOopW`UsryTWcU<1o_32gh%OJ;4<%$0#CY4$m8c5?kONB8KJKlC;8>RU<877eHqv|6Wgwwa#_z6U2V(;AkH zaSd&z59Pi!oV=$f+k;%T=}7~cZfcqwp_*dAj;#@=^lU3;%d$^w#+@zC@%jAc=S`On zPB)HJ-!30~4&C*2;@gFR&lzosEN7GMdlOC$d*OJw#r7c`84`j@2NBn+N7u8lP^vd` z4X5eiM}l3)y@+w}dgag5hSLx+t`T)|0TV;qb%7}|HTOxy)~Wo-YR0=K?^ok#zh_D= z`Hje?Y$fOGDU8i;C~KUEZ`^neL1y#p+ElbRG`^?T=|%Na2_kuiUpRaUO~g{9OLSA! zUIJjhb>qOf+?|qjFIlZmA|_WE9gQMRsXGsqPdjwdV~#WEsF6R<)M zln#PGqM?e25=t}>1_)7VRuBcHmpISaJ+pvw*7U-!1RE_q)HF=CB+B zylm}eykb^Ijl37@+-@Ve-k>lRr_Z-UiPn({ySiOlO2Ug{?9!SJ~(OXB?S zrd*Z91CiUnG!b>J{S%>=b@A^A;}TY>UOoNJ9IoBt+s)X+P+EIf_mKNf$jy7U2T&VD zElb?yH$l4$A(9NY6->5hHOVLRK)#28~^EZCPO3U<{>m)g_C{xv; zmYu_o<1IQh2-%fB$wd`UEspwzxo3|fWkoZKB@RruR%E?faKAj&d!@kg1=o|~fazPG z)Qf6;m|(I#vPmAAn66hN8%DBcoSz)!V{O`bQDv8p{FCs~4Y+3mPKsE*B(~zVO}|7n z+NW|d(x#Ucf@-H}`&_uJt6bJ?GfWfV$BL_iO3F`fQI&Zv$H2V+=gmUJk%sI#YsXkN z%j+1hl*BI(QxNEiy_YWFZbj31$Z_zg{VmYqWqXisBXZ7vF z#NY@lHmzX!TU>vA0=U-I9fSOsm>ynLF0r(tP<`kkEJrdooc{q2Ls;=NX08?~64%_PPc1e$xPkk_|LX5Wj{ zF29G44K4)1p1{@f1&4eA@P)l#7LmHX{@}ylr(`%AWH|e5fsAum(L+MXxXec}6v#}g z@6(&6UPVhXAWCVa-1WT=Ln%;0e?JB*tZP#p2# z(91<1#7%Z&3O4##8zcD8re=PY{aRxAE;ShUzqJa189*ijL#8#6beZCUkhehN@8h@#};T2wZZzMjj(59MJNB(b{F8 zb8?x4zx#A9%`AM(62S^$RaM2|d?4UO^xRZ2AzIW`zS&;~tNtN9l^P7({;R45rf2r==K^h>d!HQK z51Q*3&Z1nG6Q9S9q+P2W9BV+$z+>jS9E?RcXFWM*y~+=Mg^b*v^f&li?~!$?i}esU zLoCU}Jkx$5vi+w!n0l<_F?0U(fbAmJsADo(&zuiXoX_2bC;Dd}ae3)Skziw`8HHur z5|I^gh3DZB`qQI(4C$(_Z4Hc$R}uIJr*?a7)SFHz&dYN~hr-im)lR6O-xo+Q)T5}TfwcxM{O#bDq+fyy?qxDlQGx4FENoDi=29U-nI1DI_b_Bgl;>E zmT1~p<*$0x_JoY%Q`kVWDNQ&>nrU;XXTGe$T;H!ThrY?_9b>dh!8G3@4nFSTH0Z`j z+H#ZcZLWg#`sS_>UTsmW*sgfTM^zVRSn37pVAS7(gOhZLOK4k0w4ox~uD8AD=$xgM zk$DW^1S{{mTuf z`)qVlHD1#^5T%GakS!un_IBAbU4*;))@2JtoIjV+9_Gz6YgOk}?*I>dJ;dvvC;7Jlsen7uEh*(MkgaA3 zr_SzO1u7>RwJ)zLtep@BnjwKe>eMtRi+!)(e(?9)E z+@N9oPz2DOHH!5IkN9wbRV;k(<{fbm2ZU%_S_>7VbU>_qBe3K>Ecbndm+x4ht-yGG z)twB!LnFp^^y7a4St~a1 z|G(*6&0U(v*~g`iF`CB(%gPgpvHgLRxabXR{}?tGDJJ)~js3?({e``a750AnnMwV; zuw-44os`{X!^8lQc)C93y{Pn^nCFO3!5LpU;NMCCa+vw}wc7m$R(5A40(tl3S33DQ z7@*Xt@%U4Qma`HQuMEmDfMA{n1^028641u?eRF%)z6zA}lJ#%O`bdz>YVsaIePi8g z#kv9&)8x e$nGNE!)|}-NNN(gno-{iqIy}>^!%nT-M<3Rb5YL# literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/Settings.h new file mode 100644 index 0000000..67e1a4a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/68_Balloon_Tracker_Receiver/Settings.h @@ -0,0 +1,103 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//************************************************************************************************** +// 1) Hardware related definitions and options - specify lora board type and pins here +//************************************************************************************************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define LED1 8 //On board LED, high for on +#define SWITCH1 2 //if pin shorted to ground, switch is active + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER -1 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define AUDIOOUT 6 //pin used to output Audio tones for HAB packet upload +#define CHECK 8 //this pin is toggled inside the AFSKRTTY library, high for logic 1, low for logic 0, so it can be used to check the timing. + +#define LORA_DEVICE DEVICE_SX1278 //this is the LoRa device we are using + +//************************************************************************************************** +// 2) Program Options +//************************************************************************************************** + + + +//************************************************************************************************** +// 3) LoRa modem settings +//************************************************************************************************** + +const uint32_t Offset = 0; //offset frequency for calibration purposes + +//Tracker mode +const uint32_t TrackerFrequency = 434000000; //frequency of transmissions +const uint8_t TrackerBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t TrackerSpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t TrackerCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t TrackerOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t TrackerTXpower = 10; //LoRa TX power in dBm +const uint8_t TrackerMode = 1; //used for receiver to tell whatmode it is in + +//Search mode +const uint32_t SearchFrequency = 434000000; //frequency of transmissions +const uint8_t SearchBandwidth = LORA_BW_062;; //LoRa bandwidth +const uint8_t SearchSpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t SearchCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t SearchOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t SearchTXpower = 10; //LoRa TX power in dBm +const uint8_t SearchMode = 2; //used for receiver to tell whatmode it is in + +const uint8_t RXBUFFER_SIZE = 128; //RX buffer size + +//************************************************************************************************** +// 4) GPS Options +//************************************************************************************************** + +const uint16_t GPSBaud = 9600; //GPS Baud rate + +#define USESOFTSERIALGPS //if your using software serial for the GPS, enable this define +//#define USEHARDWARESERIALGPS //if your using hardware serial for the GPS, enable this define +#define HARDWARESERIALPORT Serial1 //if using hardware serial enable this define for hardware serial port + +const uint16_t WaitGPSFixSeconds = 30; //time to wait for a new GPS fix + +const uint16_t NoRXGPSfixms = 15000; //max number of mS to allow before no local fix flagged +const uint8_t DisplayRate = 7; //when working OK the GPS will get a new fix every second or so +//this rate defines how often the display should be updated + + +//************************************************************************************************** +// 6) AFSK RTTY Settings - For PC upload into Dl-Fldigi in HAB mode. +// Sent at 200baud, 7 bit, no parity, 2 stop bits. +// Shift circa 360hz, low tone 1000hz, high tone 1400hz (measured on an Arduino Due) +// See screenshot 'AFSKRTTY2_DL-Fldigi_Settings.jpg' in this folder for the settings used +// +//************************************************************************************************** + +#define UPLOADHABPACKET //comment in define to output HAB packet as AFSKRTTY for PC upload + +const uint16_t leadinmS = 2000; //number of ms for AFSK constant lead in tone +const uint16_t leadoutmS = 0; //number of ms for AFSK constant lead out tone + + +const uint16_t LOWPERIODUS = 1000; //actual period in uS of to give a 200baud +const uint8_t LOWCYCLES = 5; //cycles of low frequency tone for 200baud +const uint16_t HIGHPERIODUS = 714; //actual high period in uS to give a 200baud +const uint8_t HIGHCYCLES = 7; //cycles of high frequency tone for 200baud +const int8_t ADJUSTUS = 0; //uS to subtract from tone generation loop to match frequency + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/69_Balloon_Tracker_Transmitter_GenericGPS/69_Balloon_Tracker_Transmitter_GenericGPS.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/69_Balloon_Tracker_Transmitter_GenericGPS/69_Balloon_Tracker_Transmitter_GenericGPS.ino new file mode 100644 index 0000000..b86094b --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/69_Balloon_Tracker_Transmitter_GenericGPS/69_Balloon_Tracker_Transmitter_GenericGPS.ino @@ -0,0 +1,786 @@ +/****************************************************************************************************** + Programs for Arduino - Copyright of the author Stuart Robinson - 28/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a tracker intended for use as a high altitude balloon (HAB) tracker. The + program sends out a standard format payload with LoRa that is compatible with the HABHUB online tracking + system. + + The HAB payload is constructed thus; + + PayloadID,Sequence,Time,Lat,Lon,Alt,Satellites,Volts,Temperature,Resets,Status,Errors,TXGPSfixms,Checksum + Field 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + + The LoRa and frequency settings can be changed in the Settings.h file. There is the option of sending + out a much shorter Search mode binary location only payload. This is intended for ground based searching + and locating. The frequency and LoRa settings of the Search mode packet can be different to the Tracker + mode used by the HAB payload. There is also the option of sending the HAB payload in FSK RTTY format, + see the Settings.h file for all the options. FSK RTTY gets sent at the same frequency as the Tracker mode + HAB packet. The LT.transmitFSKRTTY() function sends at 1 start bit, 7 data bits, no parity and 2 stop bits. + For full control of the FSK RTTY setting you can use the following alternative function; + + LT.transmitFSKRTTY(chartosend, databits, stopbits, parity, baudPerioduS, pin) + + There is a matching Balloon Tracker Receiver program which writes received data to the Serial monitor as well + as a small OLED display. + + In the Settings.h file you can set the configuration for either a Ublox GPS or a Quectel L70\L80. The GPSs + are configured for high altitude balloon mode. + + It is strongly recommended that a FRAM option is fitted for this transmitter. The sequence, resets and error + nembers are stred in non-volatile memory. This defaults to EEPROM which has a limited endurance of only + 100,000 writes, so in theory the limt is reached after the transmission of 100,000 hab packets. The use of + a FRAM will extend the life of the tracker to circa 100,000,000,000,000 transmissions. + + Changes: + 240420 - Change to work with Easy Pro Mini style modules + 300420 - Improve error detection for UBLOX GPS library + + ToDo: + + Serial monitor baud rate is set at 115200 +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include + +#include //include the appropriate library + +SX127XLT LT; //create a library class instance called LT + +#include "Settings.h" +#include "ProgramLT_Definitions.h" + +//************************************************************************************************** +// HAB tracker data - these are the variables transmitted in payload +//************************************************************************************************** +uint32_t TXSequence; //sequence number of payload +uint8_t TXHours; //Hours +uint8_t TXMinutes; //Minutes +uint8_t TXSeconds; //Seconds +float TXLat; //latitude from GPS +float TXLon; //longitude from GPS +uint16_t TXAlt; //altitude from GPS +uint8_t TXSatellites; //satellites used by GPS +uint16_t TXVolts; //measured tracker supply volts +int8_t TXTemperature; //measured temperature +uint16_t TXResets; //number of tracker resets +uint8_t TXStatus = 0; //used to store current status flag bits +uint16_t TXErrors; //number of tracker Errors +uint32_t TXGPSfixms; //fix time of GPS +//************************************************************************************************** + +uint8_t TXPacketL; //length of LoRa packet sent +uint8_t TXBUFFER[TXBUFFER_SIZE]; //buffer for packet to send + +#include Memory_Library + +#include + +#include //http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#ifdef USESOFTSERIALGPS +//#include //https://github.com/SlashDevin/NeoSWSerial +//NeoSWSerial GPSserial(RXpin, TXpin); //The NeoSWSerial library is an option to use and is more relaible +//at GPS init than software serial +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#endif + +#ifdef USEHARDWARESERIALGPS +#define GPSserial HARDWARESERIALPORT +#endif + +#ifdef USEI2CGPS +#include +#endif + +#include //get library here > https://github.com/PaulStoffregen/OneWire +OneWire oneWire(ONE_WIRE_BUS); //create instance of OneWire library +#include //get library here > https://github.com/milesburton/Arduino-TXTemperature-Control-Library +DallasTemperature sensor(&oneWire); //create instance of dallas library + +uint32_t GPSstartms; //start time waiting for GPS to get a fix + + +void loop() +{ + Serial.println(F("Start Loop")); + + GPSstartms = millis(); + + if (!gpsWaitFix(WaitGPSFixSeconds)) + { + GPSOutputOff(); + sendCommand(NoFix); //report a GPS fix error + delay(1000); //give receiver enough time to report NoFix + } + Serial.println(); + + do_Transmissions(); //do the transmissions + + Serial.println(F("Sleep")); + LT.setSleep(CONFIGURATION_RETENTION); //put LoRa device to sleep, preserve lora register settings + Serial.flush(); //make sure no serial output pending before goint to sleep + + delay(SleepTimesecs * 1000); + + Serial.println(F("Wake")); + LT.wake(); //wake the LoRa device from sleep +} + + +void do_Transmissions() +{ + //this is where all the transmisions get sent + uint32_t startTimemS; + uint8_t index; + + incMemoryUint32(addr_SequenceNum); //increment Sequence number + + if (readConfigByte(SearchEnable)) + { + setSearchMode(); + TXPacketL = buildLocationOnly(TXLat, TXLon, TXAlt, TXStatus); //put location data in SX12xx buffer + Serial.print(F("Search packet > ")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt); + Serial.print(F(",")); + Serial.print(TXStatus); + digitalWrite(LED1, HIGH); + startTimemS = millis(); + TXPacketL = LT.transmitSXBuffer(0, TXPacketL, 10000, SearchTXpower, WAIT_TX); + printTXtime(startTimemS, millis()); + reportCompletion(); + Serial.println(); + } + + delay(1000); //gap between transmissions + + setTrackerMode(); + + TXPacketL = buildHABPacket(); + Serial.print(F("HAB Packet > ")); + printBuffer(TXBUFFER, (TXPacketL + 1)); //print the buffer (the packet to send) as ASCII + digitalWrite(LED1, HIGH); + startTimemS = millis(); + TXPacketL = LT.transmit(TXBUFFER, (TXPacketL + 1), 10000, TrackerTXpower, WAIT_TX); //will return packet length sent if OK, otherwise 0 if transmit error + digitalWrite(LED1, LOW); + printTXtime(startTimemS, millis()); + reportCompletion(); + Serial.println(); + + delay(1000); //gap between transmissions + + if (readConfigByte(FSKRTTYEnable)) //FSKRTTY is sent last, so that receiver has time to use AFSK upload + { + LT.setupDirect(TrackerFrequency, Offset); + LT.startFSKRTTY(FrequencyShift, NumberofPips, PipPeriodmS, PipDelaymS, LeadinmS); + + startTimemS = millis() - LeadinmS; + + Serial.print(F("FSK RTTY > $$$")); + Serial.flush(); + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + LT.transmitFSKRTTY('$', BaudPerioduS, LED1); //send a '$' as sync + + for (index = 0; index <= (TXPacketL - 1); index++) //its TXPacketL-1 since we dont want to send the null at the end + { + LT.transmitFSKRTTY(TXBUFFER[index], BaudPerioduS, LED1); + Serial.write(TXBUFFER[index]); + } + + LT.transmitFSKRTTY(13, BaudPerioduS, LED1); //send carriage return + LT.transmitFSKRTTY(10, BaudPerioduS, LED1); //send line feed + LT.endFSKRTTY(); //stop transmitting carrier + digitalWrite(LED1, LOW); //LED off + printTXtime(startTimemS, millis()); + TXPacketL += 4; //add the two $ at beginning and CR/LF at end + reportCompletion(); + Serial.println(); + } +} + + +void printTXtime(uint32_t startmS, uint32_t endmS) +{ + Serial.print(F(" ")); + Serial.print(endmS - startmS); + Serial.print(F("mS")); +} + + +void reportCompletion() +{ + Serial.print(F(" ")); + if (TXPacketL == 0) + { + Serial.println(); + reporttransmitError(); + } + else + { + Serial.print(TXPacketL); + Serial.print(F("bytes")); + setStatusByte(LORAError, 0); + } +} + + +void printBuffer(uint8_t *buffer, uint8_t size) +{ + uint8_t index; + + for (index = 0; index < size; index++) + { + Serial.write(buffer[index]); + } +} + + +uint8_t buildHABPacket() +{ + //build the HAB tracker payload + uint16_t index, j, CRC; + uint8_t Count, len; + char LatArray[12], LonArray[12]; + + TXSequence = readMemoryUint32(addr_SequenceNum); //Sequence number is kept in non-volatile memory so it survives TXResets + TXResets = readMemoryUint16(addr_ResetCount); //reset count is kept in non-volatile memory so it survives TXResets + TXVolts = readSupplyVoltage(); + Serial.print(F("TXVolts ")); + Serial.print(TXVolts); + Serial.println(F("mV")); + TXTemperature = (int8_t) readTempDS18B20(); + TXErrors = readMemoryUint16(addr_TXErrors); + + dtostrf(TXLat, 7, 5, LatArray); //format is dtostrf(FLOAT,WIDTH,PRECISION,BUFFER); + dtostrf(TXLon, 7, 5, LonArray); //converts float to character array + + len = sizeof(TXBUFFER); + memset(TXBUFFER, 0, len); //clear array to 0s + Count = snprintf((char*) TXBUFFER, + TXBUFFER_SIZE, + "$%s,%lu,%02d:%02d:%02d,%s,%s,%d,%d,%d,%d,%d,%d,%d,%lu", + FlightID, + TXSequence, + TXHours, + TXMinutes, + TXSeconds, + LatArray, + LonArray, + TXAlt, + TXSatellites, + TXVolts, + TXTemperature, + TXResets, + TXStatus, + TXErrors, + TXGPSfixms + ); + + CRC = 0xffff; //start value for CRC16 + + for (index = 1; index < Count; index++) //element 1 is first character after $ at start (for LoRa) + { + CRC ^= (((uint16_t)TXBUFFER[index]) << 8); + for (j = 0; j < 8; j++) + { + if (CRC & 0x8000) + CRC = (CRC << 1) ^ 0x1021; + else + CRC <<= 1; + } + } + + TXBUFFER[Count++] = '*'; + TXBUFFER[Count++] = Hex((CRC >> 12) & 15); //add the checksum bytes to the end + TXBUFFER[Count++] = Hex((CRC >> 8) & 15); + TXBUFFER[Count++] = Hex((CRC >> 4) & 15); + TXBUFFER[Count] = Hex(CRC & 15); + return Count; +} + + +char Hex(uint8_t lchar) +{ + //used in CRC calculation in buildHABPacket + char Table[] = "0123456789ABCDEF"; + return Table[lchar]; +} + + +uint8_t buildLocationOnly(float Lat, float Lon, uint16_t Alt, uint8_t stat) +{ + uint8_t len; + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeUint8(LocationBinaryPacket); //identify type of packet + LT.writeUint8(Broadcast); //who is the packet sent too + LT.writeUint8(ThisNode); //tells receiver where is packet from + LT.writeFloat(Lat); //add latitude + LT.writeFloat(Lon); //add longitude + LT.writeInt16(Alt); //add altitude + LT.writeUint8(stat); //add tracker status + len = LT.endWriteSXBuffer(); //close buffer write + return len; +} + + +void reporttransmitError() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F("TXError,")); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set + incMemoryUint16(addr_TXErrors); //increase the error count + setStatusByte(LORAError, 1); +} + + +void incMemoryUint32(uint32_t addr) +{ + uint32_t val = readMemoryUint32(addr); + val++; + writeMemoryUint32(addr, val); +} + + +void incMemoryUint16(uint32_t addr) +{ + uint16_t val = readMemoryUint16(addr); + val++; + writeMemoryUint16(addr, val); +} + + +void setStatusByte(uint8_t bitnum, uint8_t bitval) +{ + //program the status byte + + if (bitval == 0) + { + bitClear(TXStatus, bitnum); + } + else + { + bitSet(TXStatus, bitnum); + } +} + + +uint8_t readConfigByte(uint8_t bitnum) +{ + return bitRead(Default_config1, bitnum); +} + + +void setTrackerMode() +{ + Serial.println(F("setTrackerMode")); + LT.setupLoRa(TrackerFrequency, Offset, TrackerSpreadingFactor, TrackerBandwidth, TrackerCodeRate, TrackerOptimisation); +} + + +void setSearchMode() +{ + Serial.println(F("setSearchMode")); + LT.setupLoRa(SearchFrequency, Offset, SearchSpreadingFactor, SearchBandwidth, SearchCodeRate, SearchOptimisation); +} + + +uint8_t sendCommand(char cmd) +{ + uint8_t len; + TXVolts = readSupplyVoltage(); + Serial.print(F("Send Cmd ")); + Serial.write(cmd); + Serial.println(); + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + LT.writeUint8(cmd); //this byte defines the packet type + LT.writeUint8(Broadcast); //destination address of the packet, the receivers address + LT.writeUint8(ThisNode); //source address of this node + LT.writeUint16(TXVolts); //add the battery voltage + LT.writeUint8(TXStatus); //add the status byte + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + //now transmit the packet, set a timeout of 5000mS, wait for it to complete sending + + digitalWrite(LED1, HIGH); //turn on LED as an indicator + TXPacketL = LT.transmitSXBuffer(0, len, 5000, TrackerTXpower, WAIT_TX); + digitalWrite(LED1, LOW); //turn off indicator LED + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //flash LED to show tracker is alive + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void clearAllMemory() +{ + //clears the whole of non-volatile + Serial.println(F("Clear Memory")); + fillMemory(addr_StartMemory, addr_EndMemory, 0); +} + + +float readTempDS18B20() +{ + float DS18B20TXTemperature; + sensor.requestTemperatures(); + DS18B20TXTemperature = sensor.getTempCByIndex(0); + return DS18B20TXTemperature; +} + + +void printTempDS18B20() +{ + float DS18B20TXTemperature; + DS18B20TXTemperature = readTempDS18B20(); + Serial.print(F("Temperature ")); + Serial.print(DS18B20TXTemperature, 1); + Serial.println(F("c")); +} + + +void printSupplyVoltage() +{ + //get and display supply volts on terminal or monitor + Serial.print(F("Volts ")); + Serial.print(readSupplyVoltage()); + Serial.println(F("mV")); +} + + +uint16_t readSupplyVoltage() +{ + //relies on internal reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t volts = 0; + uint8_t index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn MOSFET connection resitor divider in circuit + } + + analogReference(INTERNAL); + temp = analogRead(SupplyAD); + + for (index = 0; index <= 9; index++) //sample AD 10 times + { + temp = analogRead(SupplyAD); + volts = volts + temp; + delay(10); + } + volts = ( (float) (volts / 10) * ADMultiplier); + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn MOSFET connection resitor divider in circuit + } + + return volts; +} + + +//*********************************************************** +// Start GPS Functions +//*********************************************************** + +void GPSTest() +{ + uint8_t GPSchar; + uint32_t startmS; + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < 2000) //allows for millis() overflow + { + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + Serial.write(GPSchar); + } + } + Serial.println(); + Serial.println(); + Serial.flush(); +} + + +bool gpsWaitFix(uint16_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for good fix + + uint32_t startmS, waitmS; + uint8_t GPSchar; + + Serial.flush(); + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.println(F("s ")); + Serial.flush(); + + GPSOutputOn(); + + waitmS = waitSecs * 1000; + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < waitmS) //allows for millis() overflow + { + + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + Serial.write(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated() && gps.date.isUpdated()) + { + TXLat = gps.location.lat(); + TXLon = gps.location.lng(); + TXAlt = (uint16_t) gps.altitude.meters(); + + //Altitude is used as an unsigned integer, so that the binary payload is as short as possible. + //However gps.altitude.meters(); can return a negative value which converts to + //65535 - Altitude, which we dont want. So we will assume any value over 60,000M is zero + + if (TXAlt > 60000) + { + TXAlt = 0; + } + + TXHours = gps.time.hour(), + TXMinutes = gps.time.minute(), + TXSeconds = gps.time.second(), + TXSatellites = gps.satellites.value(); + + setStatusByte(GPSFix, 1); + + TXGPSfixms = millis() - GPSstartms; + + Serial.flush(); + Serial.println(); + Serial.print(F("Have GPS Fix ")); + Serial.print(TXGPSfixms); + Serial.print(F("mS")); + Serial.println(); + GPSprintTime(); + GPSprintDate(); + Serial.flush(); + + return true; + } + + } + + //if here then there has been no fix and a timeout + GPSOutputOff(); + setStatusByte(GPSFix, 0); //set status bit to flag no fix + incMemoryUint16(addr_TXErrors); + Serial.println(F("Error No GPS Fix")); + return false; +} + +void GPSprintTime() +{ + uint8_t hours, mins, secs; + hours = gps.time.hour(); + mins = gps.time.minute(); + secs = gps.time.second(); + + Serial.print(F("Time ")); + + if (hours < 10) + { + Serial.print(F("0")); + } + Serial.print(hours); + Serial.print(F(":")); + + if (mins < 10) + { + Serial.print(F("0")); + } + Serial.print(mins); + Serial.print(F(":")); + + if (secs < 10) + { + Serial.print(F("0")); + } + Serial.println(secs); +} + + +void GPSprintDate() +{ + Serial.print(F("Date ")); + Serial.print(gps.date.day()); + Serial.print(F("/")); + Serial.print(gps.date.month()); + Serial.print(F("/")); + Serial.println(gps.date.year()); +} + +void GPSOutputOn() +{ + GPSserial.begin(GPSBaud); + while (GPSserial.available()) GPSserial.read(); //make sure input buffer is empty +} + + +void GPSOutputOff() +{ + //turns off serial output from GPS + GPSserial.end(); +} + + + + +//*********************************************************** +// End GPS Functions +//*********************************************************** + + +void setup() +{ + uint32_t i; + uint16_t j; + + Serial.begin(115200); //Setup Serial console ouput + Serial.println(); + Serial.println(); + Serial.println(F("67_HAB_Balloon_Tracker_Transmitter Starting")); + + memoryStart(Memory_Address); //setup the memory + j = readMemoryUint16(addr_ResetCount); + j++; + writeMemoryUint16(addr_ResetCount, j); + j = readMemoryUint16(addr_ResetCount); + + Serial.print(F("TXResets ")); + Serial.println(j); + + if (GPSPOWER >= 0) //if GPS needs power switching, turn it on + { + pinMode(GPSPOWER, OUTPUT); + digitalWrite(GPSPOWER, GPSONSTATE); + } + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); //for MOSFET controlling battery volts resistor divider + } + +#ifdef QUECTELINUSE + Serial.println(F("Quectel GPS library")); +#endif + +#ifdef UBLOXINUSE + Serial.println(F("UBLOX GPS library")); +#endif + +#ifdef ClearAllMemory + clearAllMemory(); +#endif + + SPI.begin(); //initialize SPI + + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("LoRa Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + setTrackerMode(); + + Serial.print(F("Config ")); + Serial.println(Default_config1, BIN); + + j = readMemoryUint16(addr_TXErrors); + Serial.print(F("TXErrors ")); + Serial.println(j); + + Serial.print(F("TXSequence ")); + i = readMemoryUint32(addr_SequenceNum); + Serial.println(i); + + Serial.print(F("ThisNode ")); + Serial.println(ThisNode); + + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + + Serial.println(); + printSupplyVoltage(); + printTempDS18B20(); + Serial.println(); + + TXStatus = 0; //clear all TX status bits + + sendCommand(PowerUp); //send power up command, includes supply mV and config, on tracker settings + + GPSOutputOn(); + Serial.println(); + Serial.println(F("GPS output test")); + Serial.flush(); + GPSTest(); //copy GPS output to serial monitor as a test + + delay(2000); + + GPSstartms = millis(); + + setTrackerMode(); //so that commands indicating wait for a GPS go out + + while (!gpsWaitFix(5)) //wait for the initial GPS fix, this could take a while + { + sendCommand(NoFix); + led_Flash(2, 50); //two short LED flashes to indicate GPS waiting for fix + } + + LT.setupDirect(TrackerFrequency, Offset); //need direct mode for tones + digitalWrite(LED1, HIGH); + LT.toneFM(500, 2000, deviation, adjustfreq, TrackerTXpower); + digitalWrite(LED1, LOW); + GPSOutputOn(); + delay(2000); //GPS may be in software backup allow time for it to wakeup + GPSOutputOff(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/69_Balloon_Tracker_Transmitter_GenericGPS/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/69_Balloon_Tracker_Transmitter_GenericGPS/Settings.h new file mode 100644 index 0000000..328190c --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/69_Balloon_Tracker_Transmitter_GenericGPS/Settings.h @@ -0,0 +1,137 @@ + /******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//************************************************************************************************** +// 1) Hardware related definitions and options - specify lora board type and pins here +//************************************************************************************************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define DIO0 3 //DIO0 on LoRa device, used for RX and TX done +#define LED1 8 //On board LED, high for on +#define BATVREADON 8 //Pin that turns on the resistor divider to read battery volts +#define ONE_WIRE_BUS 4 //for DS18B20 temperature sensor +#define ADMultiplier 5.25 //adjustment to convert into mV of battery voltage. for 100K\10K divider +#define SupplyAD A0 //Resistor divider for battery connected here + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER -1 //Pin that powers GPS on\off, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//************************************************************************************************** +// 2) Program Options +//************************************************************************************************** + +//#define ClearAllMemory //Clears memory of stored tracker information, counts, errors etc + +//************************************************************************************************** +// 3) LoRa modem settings +//************************************************************************************************** + +//LoRa Modem Parameters +const uint32_t Offset = 0; //offset frequency for calibration purposes + +//Tracker mode +const uint32_t TrackerFrequency = 434000000; //frequency of transmissions +const uint8_t TrackerBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t TrackerSpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t TrackerCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t TrackerOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t TrackerTXpower = 10; //LoRa TX power in dBm + +//Search mode +const uint32_t SearchFrequency = 434000000; //frequency of transmissionsconst +uint8_t SearchBandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SearchSpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t SearchCodeRate = LORA_CR_4_5; //LoRa coding rate +const uint8_t SearchOptimisation = LDRO_AUTO; //low data rate optimisation setting +const int8_t SearchTXpower = 10; //LoRa TX power in dBm + +const uint16_t deviation = 10000; //deviation in hz for FM tones +const float adjustfreq = 0.9; //adjustment to tone frequency + +const uint8_t TXBUFFER_SIZE = 128; //defines the maximum size of the trasnmit buffer; + + +//************************************************************************************************** +// 4) GPS Options - Settings for a generic GPS, no attempt is made to put the GPS into balloon mode +//************************************************************************************************** + +#define GPSBaud 9600 //GPS Baud rate + +#define USESOFTSERIALGPS //if your using software serial for the GPS, enable this define + +//#define USEHARDWARESERIALGPS //if your using hardware serial for the GPS, enable this define +#define HARDWARESERIALPORT Serial1 //if your using hardware serial for the GPS, define the port here + +const uint16_t WaitGPSFixSeconds = 60; //when in flight the time to wait for a new GPS fix + +//************************************************************************************************** +// 5) FSK RTTY Settings +//************************************************************************************************** + +uint32_t FrequencyShift = 500; //hertz frequency shift, approx, sent at nearest 61.03515625hz step +uint8_t NumberofPips = 4; //number of marker pips to send +uint16_t PipDelaymS = 1000; //mS between pips when carrier is off +uint16_t PipPeriodmS = 100; //mS length of pip +uint16_t BaudPerioduS = 10000; //uS period for baud, 10000uS for 100baud +uint16_t LeadinmS = 1000; //ms of leadin constant shifted carrier + + +//**************************************************************************************************** +// 6) Program Default Option settings - This section determines which options are on or off by default, +// these are saved in the Default_config1 byte. These options are set in this way so that it is +// possible (in future program changes) to alter the options remotly. +//************************************************************************************************** + +uint8_t OptionOff = 0; +uint8_t OptionOn = 1; + +const char option_SearchEnable = OptionOn; //set to OptionOn to enable transmit of Search mode packet +const char option_FSKRTTYEnable = OptionOn; //set to OptionOn to enable transmit of FSKRTTY + +#define option_SearchEnable_SUM (option_SearchEnable*1) +#define option_FSKRTTYEnable_SUM (option_FSKRTTYEnable*4) + +const uint16_t Default_config1 = (option_SearchEnable_SUM + option_FSKRTTYEnable_SUM); +//const uint16_t Default_config1 = 0x05; //Phew, the default config can always be set manually........ + //0x05 would turn on transmit of search mode and FSKRTTY + + +//************************************************************************************************** +// 7) Memory settings - define the type of memory to use for non-Volatile storage. +// Default is internal ATmega device EEPROM but EEPROM has a limited write endurance of 'only' +// 100,000 writes. Since the non-Volatile memory selected is written to at each transmission loop +// and error, its highly recommended to use one of the FRAM options, these have an endurance of +// 100,000,000,000,000 writes. +//************************************************************************************************** + +#define Memory_Library +//#define Memory_Library +//#define Memory_Library + +int16_t Memory_Address = 0x50; //default I2C address of MB85RC16PNF and FM24CL64 FRAM + +//************************************************************************************************** +// 8) HAB Flight Settings +//************************************************************************************************** + +char FlightID[] = "Flight1"; //flight ID for HAB packet + +const uint16_t SleepTimesecs = 13; //sleep time in seconds after each TX loop + +const char ThisNode = '1'; //tracker number for search packet + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/70_AFSKRTTY_Upload_Test/70_AFSKRTTY_Upload_Test.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/70_AFSKRTTY_Upload_Test/70_AFSKRTTY_Upload_Test.ino new file mode 100644 index 0000000..422c8d8 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/70_AFSKRTTY_Upload_Test/70_AFSKRTTY_Upload_Test.ino @@ -0,0 +1,98 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This test program has been written to check that the hardware for sending AFSK RTTY on + been connected correctly. AFSKRTTY can be used to upload packets received from a high altitude balloon + tracker as LoRa into DL-FLDIGI running on a PC and from there uploaded to an Internet connected tracking + system. + + The AFSK RTTY library has been tested and will work on an Arduion Pro Mini 8Mhz, Arduino DUE and ESP32 + when used with this startup command; + + startAFSKRTTY(AUDIOOUTpin, CHECKpin, 5, 1000, 7, 714, 0, 1000); + + This outputs AFSKRTTY at 200baud, 7 databits, 1 startbit, 2 stopbits, no parity, low tone 1000hz, hightone 1400hz. + + The audio comes out of the pin passed via AUDIOOUTpin and the bit timing can be checked by looking at + the CHECKpin on a scope or analyser. + + A low pass filter consiting of a deries 47K resistor and parallel 470nF capacitor was used to reduce the output + for feeding into a PC soundcard. + + A screenshot of the FLDIGI settings used is in the folder containing this program; 'AFSKRTTY2_DL-Fldigi_Settings.jpg' + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#include //AFSK RTTY library for microcontrollers without the tone() function + +//Choose whichever test pattern takes your fancy +//uint8_t testBuffer[] = "0123456789* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"; +//uint8_t testBuffer[] = "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"; +uint8_t testBuffer[] = "$$$$MyFlight1,2213,14:54:37,51.48230,-3.18136,15,6,3680,23,66,3,0*2935"; + + + +const uint16_t leadinmS = 1000; //number of ms for AFSK constant lead in tone +const uint16_t leadoutmS = 0; //number of ms for AFSK constant lead out tone + + +const uint16_t LOWPERIODUS = 1000; //actual period in uS of to give a 200baud +const uint8_t LOWCYCLES = 5; //cycles of low frequency tone for 200baud +const uint16_t HIGHPERIODUS = 714; //actual high period in uS to give a 200baud +const uint8_t HIGHCYCLES = 7; //cycles of high frequency tone for 200baud +const int8_t ADJUSTUS = 0; //uS to subtract from tone generation loop to match frequency + +const int8_t AUDIOOUT = 6; //pin used to output Audio tones +const int8_t CHECK = 8; //this pin is toggled inside the AFSKRTTY library, high for logic 1, low for logic 0, so it can be used to check the timing. + + +void loop() +{ + uint8_t index; + uint8_t chartosend; + uint8_t len = sizeof(testBuffer) - 1; //must NOT send null at end of buffer + + Serial.print(F("Sending AFSK RTTY ")); + Serial.flush(); + + startAFSKRTTY(AUDIOOUT, CHECK, LOWCYCLES, LOWPERIODUS, HIGHCYCLES, HIGHPERIODUS, ADJUSTUS, leadinmS); + + sendAFSKRTTY(13); + sendAFSKRTTY(10); + + for (index = 0; index < len; index++) + { + chartosend = testBuffer[index]; + sendAFSKRTTY(chartosend); + Serial.write(chartosend); + } + + sendAFSKRTTY(13); + sendAFSKRTTY(10); + + endAFSKRTTY(AUDIOOUT, CHECK, leadoutmS); + + digitalWrite(CHECK, LOW); + Serial.println(); + delay(1000); +} + + +void setup() +{ + pinMode(CHECK, OUTPUT); //setup pin as output for indicator LED + + Serial.begin(9600); + Serial.println(); + Serial.println(F("70_AFSKRTTY_Upload_Test")); + Serial.println(); +} + + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/70_AFSKRTTY_Upload_Test/AFSKRTTY2_DL-Fldigi_Settings.jpg.jpg b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/70_AFSKRTTY_Upload_Test/AFSKRTTY2_DL-Fldigi_Settings.jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b428c9db8994ecd83aee5f1bcbfc0b5f9f475b8d GIT binary patch literal 45618 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 literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/71_FSKRTTY_Transmitter_Test/71_FSKRTTY_Transmitter_Test.ino b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/71_FSKRTTY_Transmitter_Test/71_FSKRTTY_Transmitter_Test.ino new file mode 100644 index 0000000..e0df1a6 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/71_FSKRTTY_Transmitter_Test/71_FSKRTTY_Transmitter_Test.ino @@ -0,0 +1,183 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a test program for using the LoRa device to transmit upper side band FSK + RTTY. With the LoRa device in FSK direct mode, the frequency of the generated carrier is shifted up + or down at the correct baud rate depending on whether a logic 0 or 1 is being sent. + + The desired shift in frequency is defined in the Settings.h file as 'FrequencyShift'. When the program + starts the actual frequency shift will be calculated according to the discrete frequency steps the + LoRa device can be set to. This example uses the library function for sending FSKRTTY that is fixed at + 7 databits, 1 stop bit and no parity bit. If you want to vary these settings see the example; + '78_FSKRTTY_Transmitter_Test_Configurable.ino' + + Before the actual data transmission starts you can send a series of marker pips which are short bursts + of up shifted carrier which will be heard as beeps in a correctly tuned receiver. These pips can aid + in setting the receiver decode frequemcy to match the transmission. on some LoRa devices, such as the SX127x + series there can be considerable temperature induced frequency drift. This drift can be caused by outside + temperature changes or the RF device self heating when transmit is turned on. The duration of the pips, + the gaps between them and the period of leadin carrier before the data starts can all be set. To send no + pips just set the number to 0. + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based +#include //include the appropriate SX12XX library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX127XLT LT; //create a library class instance called LT + +//Choose whichever test pattern takes your fancy +//uint8_t testBuffer[] = "0123456789* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"; //This string is sent as AFSK RTTY, 7 bit, 2 Stop bit, no parity, 300 baud. + +//uint8_t testBuffer[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789"; +//uint8_t testBuffer[] = "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"; +uint8_t testBuffer[] = "$$$$MyFlight1,2213,14:54:37,51.48230,-3.18136,15,6,3680,23,66,3,0*2935"; + +uint8_t freqShiftRegs[3]; //to hold returned registers that set frequency + + +void loop() +{ + uint8_t index; + + printRegisterSetup(FrequencyShift); + Serial.println(); + + LT.setupDirect(Frequency, Offset); + LT.startFSKRTTY(FrequencyShift, NumberofPips, PipPeriodmS, PipDelaymS, LeadinmS); + + Serial.print(F("Start RTTY micros() = ")); + Serial.println(micros()); + Serial.print(F("Seconds to overflow ")); + Serial.println(((0xFFFFFFFF - micros()) / 1E6),0); + + LT.transmitFSKRTTY(13, BaudPerioduS, LED1); //send carriage return + LT.transmitFSKRTTY(10, BaudPerioduS, LED1); //send line feed + + for (index = 0; index < (sizeof(testBuffer)-1); index++) + { + LT.transmitFSKRTTY(testBuffer[index], BaudPerioduS, LED1); + Serial.write(testBuffer[index]); + } + LT.transmitFSKRTTY(13, BaudPerioduS, LED1); //send carriage return + LT.transmitFSKRTTY(10, BaudPerioduS, LED1); //send line feed + + Serial.println(); + Serial.print(F("END RTTY micros() = ")); + Serial.println(micros()); + digitalWrite(LED1, LOW); + Serial.println(); + Serial.println(); + + LT.setMode(MODE_STDBY_RC); + + delay(2000); +} + + +void printRegisterSetup(uint32_t shift) +{ + + uint32_t nonShiftedFreq, ShiftedFreq; + uint32_t freqShift; + float exactfreqShift; + + LT.setRfFrequency(Frequency, Offset); //ensure base frequecy is set + LT.getRfFrequencyRegisters(freqShiftRegs); //fill buffer with frequency setting registers values + nonShiftedFreq = ( (uint32_t) freqShiftRegs[0] << 16 ) + ( (uint32_t) freqShiftRegs[1] << 8 ) + freqShiftRegs[2]; + Serial.print(F("NoShift Registers 0x")); + Serial.println(nonShiftedFreq, HEX); + + LT.setRfFrequency((Frequency + shift), Offset); //set shifted frequecy + LT.getRfFrequencyRegisters(freqShiftRegs); //fill buffer with frequency setting registers values + ShiftedFreq = ( (uint32_t) freqShiftRegs[0] << 16 ) + ( (uint32_t) freqShiftRegs[1] << 8 ) + freqShiftRegs[2]; + Serial.print(F("Shifted Registers 0x")); + Serial.println(ShiftedFreq, HEX); + + freqShift = ShiftedFreq - nonShiftedFreq; + exactfreqShift = freqShift * FREQ_STEP; + Serial.print(F("FSKRTTY register shift ")); + Serial.println(freqShift,HEX); + Serial.print(F("FSKRTTY frequency shift ")); + Serial.print(exactfreqShift, 8); + Serial.println(F("hZ")); + + LT.setRfFrequency(Frequency, Offset); //ensure base frequecy is set +} + + +void printRegisterBuffer() +{ +Serial.print(freqShiftRegs[0],HEX); +Serial.print(F(" ")); +Serial.print(freqShiftRegs[1],HEX); +Serial.print(F(" ")); +Serial.print(freqShiftRegs[2],HEX); +Serial.println(); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("71_FSKRTTY_Transmitter_Test")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, DIO0, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupDirect(Frequency, Offset); + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/71_FSKRTTY_Transmitter_Test/Settings.h b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/71_FSKRTTY_Transmitter_Test/Settings.h new file mode 100644 index 0000000..2ceaa7f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX127x_examples/Tracker/71_FSKRTTY_Transmitter_Test/Settings.h @@ -0,0 +1,45 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 23/12/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2 are not used by this particular sketch so they are set to -1 and not connected. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 4 //on board LED, high for on +#define DIO0 3 //DIO0 pin on LoRa device, used for RX and TX done + +#define LORA_DEVICE DEVICE_SX1278 //we need to define the device we are using + + +//******* Setup Direct Modem Parameters Here ! *************** + +const uint32_t Frequency = 434000000; //frequency of transmissions in hertz +const uint32_t Offset = 0; //offset frequency for calibration purposes +const uint16_t deviation = 10000; //deviation, total frequency shift low to high +const float adjustfreq = 0.9; //adjustment to tone frequency + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +//******* Setup FSKRTTY Settings here ! *************** + +uint32_t FrequencyShift = 500; //hertz frequency shift, approx, sent at nearest 61.03515625hz step +uint8_t NumberofPips = 2; //number of marker pips to send +uint16_t PipDelaymS = 500; //mS between pips, carrier off +uint16_t PipPeriodmS = 100; //mS length of pip +uint16_t BaudPerioduS = 10000; //uS period for baud, 10000uS for 100baud +uint16_t LeadinmS = 2000; //ms of leadin, shifted carrier +uint8_t DataBits = 7; //number of databits, normally 7 or 8 +uint8_t StopBits = 2; //number of stopbits, normally 1 or 2 +uint8_t Parity = ParityNone; //parity on data bits, ParityNone, ParityOdd, ParityEven, ParityZero, ParityOne + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/103_LoRa_Transmitter_Detailed_Setup.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/103_LoRa_Transmitter_Detailed_Setup.ino new file mode 100644 index 0000000..2671efb --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/103_LoRa_Transmitter_Detailed_Setup.ino @@ -0,0 +1,182 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a program that demonstrates the detailed setup of a LoRa test transmitter. + A packet containing ASCII text is sent according to the frequency and LoRa settings specified in the + 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with + the transmit power used, the packet length and the CRC of the packet. The matching receive program, + '104_LoRa_Receiver' can be used to check the packets are being sent correctly, the frequency and LoRa + settings (in Settings.h) must be the same for the transmitter and receiver programs. Sample Serial + Monitor output; + + 10dBm Packet> Hello World 1234567890* BytesSent,23 CRC,DAAB TransmitTime,64mS PacketsSent,2 + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +uint8_t TXPacketL; +uint32_t TXPacketCount, startmS, endmS; + +uint8_t buff[] = "Hello World 1234567890"; + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on receiver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + startmS = millis(); //start transmit timer + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + endmS = millis(); //packet sent, note end time + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + uint16_t localCRC; + + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + localCRC = LT.CRCCCITT(buff, TXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); //print CRC of transmitted packet + Serial.print(F(" TransmitTime,")); + Serial.print(endmS - startmS); //print transmit time of packet + Serial.print(F("mS")); + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("103_LoRa_Transmitter_Detailed_Setup Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_LDO); + LT.setPacketType(PACKET_TYPE_LORA); + LT.setRfFrequency(Frequency, Offset); + LT.setBufferBaseAddress(0, 0); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate); + LT.setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers, normally 0x900 to 0x9FF + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/Settings.h new file mode 100644 index 0000000..c73ec3e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/103_LoRa_Transmitter_Detailed_Setup/Settings.h @@ -0,0 +1,42 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER are not used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used + +#define BUZZER -1 //connect a buzzer here if wanted + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/104_LoRa_Receiver_Detailed_Setup.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/104_LoRa_Receiver_Detailed_Setup.ino new file mode 100644 index 0000000..5adadb8 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/104_LoRa_Receiver_Detailed_Setup.ino @@ -0,0 +1,247 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a program that demonstrates the detailed setup of a LoRa test receiver. + The program listens for incoming packets using the lora settings in the 'Settings.h' file. The pins + to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout on the Arduino IDE Serial Monitor of the valid packets received, the packet is + assumed to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect + weird things to happen on the Serial Monitor. The LED will flash for each packet received and the + buzzer will sound, if fitted. + + Sample serial monitor output; + + 7s Hello World 1234567890*,CRC,DAAB,RSSI,-52dBm,SNR,9dB,Length,23,Packets,5,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 968s PacketError,RSSI,-87dBm,SNR,-11dB,Length,23,Packets,613,Errors,2,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the device packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } + + delay(250); //gives a longer buzzer and LED flash for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("104_LoRa_Receiver_Detailed_Setup Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in the library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_LDO); + LT.setPacketType(PACKET_TYPE_LORA); + LT.setRfFrequency(Frequency, Offset); + LT.setBufferBaseAddress(0, 0); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate); + LT.setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers, normally 0x900 to 0x9FF + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/Settings.h new file mode 100644 index 0000000..3f4205d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/104_LoRa_Receiver_Detailed_Setup/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER are not used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //busy pin on LoRa device +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define RX_EN -1 //pin for RX enable, used on some SX128X devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX128X devices, set to -1 if not used +#define LED1 8 //on board LED, high for on +#define BUZZER -1 //pin for buzzer, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino new file mode 100644 index 0000000..3592352 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/14_LoRa_Structure_TX/14_LoRa_Structure_TX.ino @@ -0,0 +1,145 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program demonstrates the transmitting of a structure as a LoRa packet. The + contents of the structure are the same as in the '8_LoRa_LowMemory_TX' program. The packet sent is + typical of what might be sent from a GPS tracker. + + The structure type is defined as trackerPacket and an instance called location1 is created. The struture + which includes a character array (text) is filled with values and transmitted. + + The matching receiving program '15_LoRa_RX_Structure' can be used to receive and display the packet, + though the program '9_LoRa_LowMemory_RX' should receive it as well, since the contents are the same. + + Note that the structure definition and variable order (including the buffer size) used in the transmitter + need to match those used in the receiver. + + The contents of the packet transmitted should be; + + "tracker1" (buffer) - trackerID + 1+ (uint32_t) - packet count + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Good luck. + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX128XLT LT; + +uint32_t TXpacketCount = 1; +uint32_t startmS, endmS; + +struct trackerPacket +{ + uint8_t trackerID[13]; + uint32_t txcount; + float latitude; + float longitude; + uint16_t altitude; + uint8_t satellites; + uint16_t voltage; + int8_t temperature; +}; + +struct trackerPacket location1; //define an instance called location1 of the structure trackerPacket + + +void loop() +{ + + //fill the defined structure with values + uint8_t buff[] = "tracker1"; //create the contents to be of location1.trackerID + memcpy (&location1.trackerID, &buff, sizeof(buff)); //copy the contents of buff[] into the structure + location1.txcount = TXpacketCount; + location1.latitude = 51.23456; + location1.longitude = -3.12345; + location1.altitude = 199; + location1.satellites = 8; + location1.voltage = 3999; + location1.temperature = -9; + + digitalWrite(LED1, HIGH); + startmS = millis(); + + if (LT.transmit((uint8_t *) &location1, sizeof(location1), 0, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 + { + endmS = millis(); + digitalWrite(LED1, LOW); + TXpacketCount++; + Serial.print(TXpacketCount); + Serial.print(F(" ")); + Serial.print(sizeof(location1)); + Serial.print(F(" Bytes Sent")); + Serial.print(F(" ")); + Serial.print(endmS - startmS); + Serial.print(F("mS")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/14_LoRa_Structure_TX/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/14_LoRa_Structure_TX/Settings.h new file mode 100644 index 0000000..6e4d27d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/14_LoRa_Structure_TX/Settings.h @@ -0,0 +1,39 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used + +#define BUZZER -1 //connect a buzzer here if wanted + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +#define TXpower 10 //power for transmissions in dBm + +#define packet_delay 1000 //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino new file mode 100644 index 0000000..7e68538 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/15_LoRa_Structure_RX/15_LoRa_Structure_RX.ino @@ -0,0 +1,194 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program demonstrates the receiving of a structure as a LoRa packet. The packet + sent is typical of what might be sent from a GPS tracker. + + The structure type is defined as trackerPacket and an instance called location1 is created. The structure + includes a character array (text). + + The matching receiving program is '15_LoRa_RX_Structure' can be used to receive and display the packet, + though the program '9_LoRa_LowMemory_RX' should receive it as well, since the packet contents are the same. + + Not that the structure definition and variable order (including the buffer size) used in the transmitter + need to match those used in the receiver. Good luck. + + The contents of the packet received, and printed to serial monitor, should be; + + "tracker1" (buffer) - trackerID + 1+ (uint32_t) - packet count + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX128XLT LT; + +uint8_t RXPacketL; //stores length of packet received +uint32_t RXpacketCount; //count of received packets +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet +uint32_t errors; //count of packet errors + + +struct trackerPacket +{ + uint8_t trackerID[13]; + uint32_t txcount; + float latitude; + float longitude; + uint16_t altitude; + uint8_t satellites; + uint16_t voltage; + int8_t temperature; +}; + +struct trackerPacket location1; //define an instance called location1 of the structure trackerPacket + + +void loop() +{ + RXPacketL = LT.receive( (uint8_t *) &location1, sizeof(location1), 0, WAIT_RX); //wait for a packet to arrive with no timeout + + digitalWrite(LED1, HIGH); //something has happened, what I wonder ? + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +void printlocation1() +{ + uint8_t buff[13]; //define a buffer to receive a copy from the structure + memcpy (&buff, &location1.trackerID, sizeof(buff)); //copy the contents of buffer in struture to buff[] + + //now print the contents of the structure + Serial.print((char*) buff); //cast to a char type for printing + Serial.print(F(",")); + Serial.print(location1.txcount); + Serial.print(F(",")); + Serial.print(location1.latitude, 5); + Serial.print(F(",")); + Serial.print(location1.longitude, 5); + Serial.print(F(",")); + Serial.print(location1.altitude); + Serial.print(F("m,")); + Serial.print(location1.satellites); + Serial.print(F("sats,")); + Serial.print(location1.voltage); + Serial.print(F("mV,")); + Serial.print(location1.temperature); + Serial.print(F("c ")); +} + + +void packet_is_OK() +{ + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(" ")); + printlocation1(); + printpacketDetails(); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + printpacketDetails(); + Serial.print(F("IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + +void printpacketDetails() +{ + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup(void) +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.print(F("Receiver ready")); + Serial.println(); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/15_LoRa_Structure_RX/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/15_LoRa_Structure_RX/Settings.h new file mode 100644 index 0000000..77ba17c --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/15_LoRa_Structure_RX/Settings.h @@ -0,0 +1,38 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //connect a buzzer here if wanted + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +#define TXpower 10 //power for transmissions in dBm + +#define packet_delay 1000 //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/1_LED_Blink/1_LED_Blink.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/1_LED_Blink/1_LED_Blink.ino new file mode 100644 index 0000000..2865c0a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/1_LED_Blink/1_LED_Blink.ino @@ -0,0 +1,69 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This programs is supplied as is, it is up to the user of the program to decide if the programs are + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program blinks an LED connected the pin number defined below. The pin 13 LED, + fitted to some Arduinos is blinked as well. The blinks should be close to one per second. messages are + sent to the Serial Monitor also. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define LED1 8 //pin number for LED, set logic level high for on + +#define Program_Version "V1.0" + +uint16_t seconds; //used to display time elapsed on Serial Monitor + +void loop() +{ + Serial.print(seconds); + Serial.println(F(" Seconds")); //this message should print on console at close to once per second + seconds++; + digitalWrite(LED1, HIGH); + digitalWrite(13, HIGH); + delay(100); + digitalWrite(LED1, LOW); + digitalWrite(13, LOW); + delay(890); //should give approx 1 second flash +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //general purpose routine for flashing LED as indicator + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); //LED on + digitalWrite(13, HIGH); //Arduino board LED on + delay(delaymS); + digitalWrite(LED1, LOW); //LED off + digitalWrite(13, LOW); //Arduino board LED off + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + pinMode(13, OUTPUT); //setup pin as output for some Arduino boards that include an LED on pin 13 + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("1_LED_Blink Starting")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/2_Register_Test/2_Register_Test.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/2_Register_Test/2_Register_Test.ino new file mode 100644 index 0000000..90cb393 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/2_Register_Test/2_Register_Test.ino @@ -0,0 +1,375 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 11/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is stand alone, it is not necessary to install the SX12XX-LoRa library + to use it. This test program is for the SX128X LoRa devices. + + The program checks that a SX128X LoRa device can be accessed by doing a test register write and read. + If there is no device found a message is printed on the serial monitor. The contents of the registers + from 0x00 to 0x7F are printed, there is a copy of a typical printout below. Note that the read back + changed frequency may be slightly different to the programmed frequency, there is a rounding error due + to the use of floats to calculate the frequency. + + The Arduino pin numbers that the NSS and NRESET pins on the LoRa device are connected to must be + specified in the hardware definitions section below. The LoRa device type in use, SX1280 or SX1281 + must be specified also. + + Typical printout; + + 2_Register_Test Starting + Reset device + LoRa Device found + Reset device + Registers at reset + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x900 80 0C 7B 02 20 FA C0 00 00 80 00 00 00 00 00 FF + 0x910 FF FF 00 00 00 19 00 00 00 19 87 65 43 21 7F FF + 0x920 FF FF FF 0C 70 37 0A 50 D0 80 00 C0 5F D2 8F 0A + 0x930 00 C0 00 00 00 24 00 21 28 B0 30 09 1A 59 70 08 + 0x940 58 0B 32 0A 14 24 6A 96 00 18 00 00 00 00 00 00 + 0x950 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x960 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF + 0x970 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 04 + 0x980 00 0B 18 70 00 00 00 4C 00 F0 64 00 00 00 00 00 + 0x990 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9A0 00 08 EC B8 9D 8A E6 66 06 00 00 00 00 00 00 00 + 0x9B0 00 08 EC B8 9D 8A E6 66 06 00 00 00 00 00 00 00 + 0x9C0 00 16 00 3F E8 01 FF FF FF FF 5E 4D 25 10 55 55 + 0x9D0 55 55 55 55 55 55 55 55 55 55 55 55 55 00 00 00 + 0x9E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 0x9F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Frequency at reset 2495996672hz + Change Frequency to 2445000000hz + Frequency now 2444999936hz + + Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x900 80 0C 7B 02 20 FA BC 13 C1 80 00 00 00 00 00 61 + +Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +const uint16_t REG_RFFrequency23_16 = 0x906; +const uint16_t REG_RFFrequency15_8 = 0x907; +const uint16_t REG_RFFrequency7_0 = 0x908; +const uint8_t RADIO_WRITE_REGISTER = 0x18; +const uint8_t RADIO_READ_REGISTER = 0x19; +const uint8_t RADIO_SET_RFFREQUENCY = 0x86; //commnad to change frequency +const uint8_t RADIO_SET_PACKETTYPE = 0x8A; //commnad to set packet mode +const float FREQ_STEP = 198.364; +const uint8_t PACKET_TYPE_LORA = 0x01; + +//********* Setup hardware definitions here ! ***************** + +//These are the pin definitions for one of the Tracker boards, be sure to change them to match your +//own setup. You will also need to connect up the pins for the SPI bus, which on an Arduino Pro Mini are +//SCK pin 13, MISO pin 12, and MOSI pin 11. + +#define NSS 10 //SX128X device select +#define NRESET 9 //SX128X reset pin +#define RFBUSY 7 //SX128X busy pin +#define LED1 8 //for on board LED, put high for on + +//**************************************************************/ + + +#include + +uint8_t saveddevice; + + +void setup() +{ + Serial.begin(9600); + Serial.println(F("2_Register_Test Starting")); + + SPI.begin(); + SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //The begin function setups the hardware pins used by device and then checks if device is found + //the DIO1, DIO2 and DIO3 pins are not used in this example so are set to -1 + + if (begin(NSS, NRESET, RFBUSY, -1, -1, -1, 0)) + { + Serial.println(F("LoRa Device found")); + } + else + { + Serial.println(F("No device responding")); + } +} + + +void loop() +{ + uint32_t frequency; + resetDevice(); //reset the device + Serial.println(F("Registers at reset")); //show the all registers following a reset + printRegisters(0x0900, 0x09FF); + Serial.println(); + Serial.println(); + + frequency = getFreqInt(); //read the set frequency following a reset + Serial.print(F(" Frequency at reset ")); + Serial.print(frequency); + Serial.println(F("hz")); + + Serial.print(F("Change Frequency to 2445000000hz")); + setPacketType(PACKET_TYPE_LORA); //this is needed to ensure frequency change is reflected in register print + setRfFrequency(2445000000, 0); //change the frequency to 2445000000hertz + + frequency = getFreqInt(); //read back the changed frequency + Serial.println(); + Serial.print(F(" Frequency now ")); + Serial.print(frequency); //print the changed frequency, did the write work (allow for rounding errors) ? + Serial.println(F("hz")); + Serial.println(); + printRegisters(0x0900, 0x090F); //show the registers after frequency change + Serial.println(); + Serial.println(); + delay(5000); +} + + +void readRegisters(uint16_t address, uint8_t *buffer, uint16_t size) +{ + uint16_t index; + uint8_t addr_l, addr_h; + + addr_h = address >> 8; + addr_l = address & 0x00FF; + checkBusy(); + + digitalWrite(NSS, LOW); + SPI.transfer(RADIO_READ_REGISTER); + SPI.transfer(addr_h); //MSB + SPI.transfer(addr_l); //LSB + SPI.transfer(0xFF); + for (index = 0; index < size; index++) + { + *(buffer + index) = SPI.transfer(0xFF); + } + + digitalWrite(NSS, HIGH); + checkBusy(); +} + + +uint8_t readRegister(uint16_t address) +{ + uint8_t data; + + readRegisters(address, &data, 1); + return data; +} + + +void writeRegisters(uint16_t address, uint8_t *buffer, uint16_t size) +{ + uint8_t addr_l, addr_h; + uint8_t i; + + addr_l = address & 0xff; + addr_h = address >> 8; + checkBusy(); + + digitalWrite(NSS, LOW); + SPI.transfer(RADIO_WRITE_REGISTER); + SPI.transfer(addr_h); //MSB + SPI.transfer(addr_l); //LSB + + for (i = 0; i < size; i++) + { + SPI.transfer(buffer[i]); + } + + digitalWrite(NSS, HIGH); + + checkBusy(); +} + + +void writeRegister(uint16_t address, uint8_t value) +{ + writeRegisters(address, &value, 1 ); +} + + +uint32_t getFreqInt() +{ + //get the current set device frequency, return as long integer + uint8_t Msb, Mid, Lsb; + uint32_t uinttemp; + float floattemp; + Msb = readRegister(REG_RFFrequency23_16); + Mid = readRegister(REG_RFFrequency15_8); + Lsb = readRegister(REG_RFFrequency7_0); + floattemp = ((Msb * 0x10000ul) + (Mid * 0x100ul) + Lsb); + floattemp = ((floattemp * FREQ_STEP) / 1000000ul); + uinttemp = (uint32_t)(floattemp * 1000000); + return uinttemp; +} + + +void printRegisters(uint16_t Start, uint16_t End) +{ + //prints the contents of SX128x registers to serial monitor + + uint16_t Loopv1, Loopv2, RegData; + + Serial.print(F("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F")); + Serial.println(); + + for (Loopv1 = Start; Loopv1 <= End;) //32 lines + { + Serial.print(F("0x")); + Serial.print((Loopv1), HEX); //print the register number + Serial.print(F(" ")); + for (Loopv2 = 0; Loopv2 <= 15; Loopv2++) + { + RegData = readRegister(Loopv1); + if (RegData < 0x10) + { + Serial.print(F("0")); + } + Serial.print(RegData, HEX); //print the register number + Serial.print(F(" ")); + Loopv1++; + } + Serial.println(); + } +} + + +void setRfFrequency(uint32_t frequency, int32_t offset) +{ + frequency = frequency + offset; + uint8_t buffer[3]; + uint32_t freqtemp = 0; + freqtemp = ( uint32_t )( (float) frequency / (float) FREQ_STEP); + buffer[0] = ( uint8_t )( ( freqtemp >> 16 ) & 0xFF ); + buffer[1] = ( uint8_t )( ( freqtemp >> 8 ) & 0xFF ); + buffer[2] = ( uint8_t )( freqtemp & 0xFF ); + writeCommand(RADIO_SET_RFFREQUENCY, buffer, 3); + writeCommand(RADIO_SET_RFFREQUENCY, buffer, 3); +} + + +void checkBusy() +{ + uint8_t busy_timeout_cnt; + busy_timeout_cnt = 0; + + while (digitalRead(RFBUSY)) + { + delay(1); + busy_timeout_cnt++; + + if (busy_timeout_cnt > 10) //wait 10mS for busy to complete + { + busy_timeout_cnt = 0; + Serial.println(F("ERROR - Busy Timeout!")); + break; + } + } +} + + +void resetDevice() +{ + Serial.println(F("Reset device")); + delay(10); + digitalWrite(NRESET, LOW); + delay(2); + digitalWrite(NRESET, HIGH); + delay(25); + checkBusy(); +} + + + +bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinDIO2, int8_t pinDIO3, uint8_t device) +{ + saveddevice = device; + + pinMode(pinNSS, OUTPUT); + digitalWrite(pinNSS, HIGH); + pinMode(pinNRESET, OUTPUT); + digitalWrite(pinNRESET, HIGH); + pinMode(pinRFBUSY, INPUT); + + if (pinDIO1 >= 0) + { + pinMode( pinDIO1, INPUT); + } + + if (pinDIO2 >= 0) + { + pinMode(pinDIO2, INPUT); + } + + if (pinDIO3 >= 0) + { + pinMode(pinDIO3, INPUT); + } + + resetDevice(); + + if (checkDevice()) + { + return true; + } + + return false; +} + + +bool checkDevice() +{ + //check there is a device out there, writes a register and reads back + + uint8_t Regdata1, Regdata2; + Regdata1 = readRegister(0x0908); //low byte of frequency setting + writeRegister(0x0908, (Regdata1 + 1)); + Regdata2 = readRegister(0x0908); //read changed value back + writeRegister(0x0908, Regdata1); //restore register to original value + + if (Regdata2 == (Regdata1 + 1)) + { + return true; + } + else + { + return false; + } +} + + +void writeCommand(uint8_t Opcode, uint8_t *buffer, uint16_t size) +{ + uint8_t index; + checkBusy(); + + digitalWrite(NSS, LOW); + SPI.transfer(Opcode); + + for (index = 0; index < size; index++) + { + SPI.transfer(buffer[index]); + //Serial.println(buffer[index], HEX); + } + digitalWrite(NSS, HIGH); + + checkBusy(); +} + +void setPacketType(uint8_t packettype) +{ + writeCommand(RADIO_SET_PACKETTYPE, &packettype, 1); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino new file mode 100644 index 0000000..1183b43 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/3_LoRa_Transmitter/3_LoRa_Transmitter.ino @@ -0,0 +1,183 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a simple LoRa test transmitter. A packet containing ASCII text is sent + according to the frequency and LoRa settings specified in the 'Settings.h' file. The pins to access + the lora device need to be defined in the 'Settings.h' file also. + + The details of the packet sent and any errors are shown on the Serial Monitor, together with the transmit + power used, the packet length and the CRC of the packet. The matching receive program, '4_LoRa_Receive' + can be used to check the packets are being sent correctly, the frequency and LoRa settings (in Settings.h) + must be the same for the Transmit and Receive program. Sample Serial Monitor output; + + 10dBm Packet> {packet contents*} BytesSent,23 CRC,DAAB TransmitTime,54mS PacketsSent,1 + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the SX128X device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +uint8_t TXPacketL; +uint32_t TXPacketCount, startmS, endmS; + +uint8_t buff[] = "Hello World 1234567890"; + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on reciver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + startmS = millis(); //start transmit timer + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit, timeout 10 seconds + { + endmS = millis(); //packet sent, note end time + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + uint16_t localCRC; + + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + localCRC = LT.CRCCCITT(buff, TXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); //print CRC of sent packet + Serial.print(F(" TransmitTime,")); + Serial.print(endmS - startmS); //print transmit time of packet + Serial.print(F("mS")); + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("3_LoRa_Transmitter Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_LDO); + LT.setPacketType(PACKET_TYPE_LORA); + LT.setRfFrequency(Frequency, Offset); + LT.setBufferBaseAddress(0, 0); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate); + LT.setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); + LT.setHighSensitivity(); + //LT.setLowPowerRX(); + //*************************************************************************************************** + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/3_LoRa_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/3_LoRa_Transmitter/Settings.h new file mode 100644 index 0000000..8409182 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/3_LoRa_Transmitter/Settings.h @@ -0,0 +1,38 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used + + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const uint8_t TXpower = 10; //Power for transmissions in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino new file mode 100644 index 0000000..38148cb --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/4_LoRa_Receiver/4_LoRa_Receiver.ino @@ -0,0 +1,247 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, + if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. + The LED will flash for each packet received and the buzzer will sound, if fitted. + + Sample serial monitor output; + + 1109s Hello World 1234567890*,CRC,DAAB,RSSI,-61dBm,SNR,9dB,Length,23,Packets,1026,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 1189s PacketError,RSSI,-111dBm,SNR,-12dB,Length,0,Packets,1126,Errors,1,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } + + delay(250); //gives a longer buzzer and LED flash for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("4_LoRa_Receiver Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in the library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_LDO); + LT.setPacketType(PACKET_TYPE_LORA); + LT.setRfFrequency(Frequency, Offset); + LT.setBufferBaseAddress(0, 0); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate); + LT.setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); + LT.setHighSensitivity(); + //LT.setLowPowerRX(); + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/4_LoRa_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/4_LoRa_Receiver/Settings.h new file mode 100644 index 0000000..defe33f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/4_LoRa_Receiver/Settings.h @@ -0,0 +1,40 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //pin for BUZZER, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const uint8_t TXpower = 10; //Power for transmissions in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/52_FLRC_Transmitter/52_FLRC_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/52_FLRC_Transmitter/52_FLRC_Transmitter.ino new file mode 100644 index 0000000..bb534d9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/52_FLRC_Transmitter/52_FLRC_Transmitter.ino @@ -0,0 +1,180 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 09/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a test transmitter for the Fast Long Range Communication (FLRC) mode + introduced in the SX128X devices. A packet containing ASCII text is sent according to the frequency and + FLRC settings specified in the 'Settings.h' file. The pins to access the SX128X device need to be defined + in the 'Settings.h' file also. + + The details of the packet sent and any errors are shown on the Serial Monitor, together with the transmit + power used, the packet length and the CRC of the packet. The matching receive program, '53_FLRC_Receiver' + can be used to check the packets are being sent correctly, the frequency and FLRC settings (in Settings.h) + must be the same for the Transmit and Receive program. Sample Serial Monitor output; + + 10dBm Packet> {packet contents*} BytesSent,23 CRC,DAAB TransmitTime,54mS PacketsSent,1 + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the SX128X device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +uint8_t TXPacketL; +uint32_t TXPacketCount, startmS, endmS; + +uint8_t buff[] = "Hello World 1234567890"; + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on reciver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + startmS = millis(); //start transmit timer + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit, timeout 10 seconds + { + endmS = millis(); //packet sent, note end time + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + uint16_t localCRC; + + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + localCRC = LT.CRCCCITT(buff, TXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); //print CRC of sent packet + Serial.print(F(" TransmitTime,")); + Serial.print(endmS - startmS); //print transmit time of packet + Serial.print(F("mS")); + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("52_FLRC_Transmitter Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + + //*************************************************************************************************** + //Setup FLRC + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_LDO); + LT.setPacketType(PACKET_TYPE_FLRC); + LT.setRfFrequency(Frequency, Offset); + LT.setBufferBaseAddress(0, 0); + LT.setModulationParams(BandwidthBitRate, CodingRate, BT); + LT.setPacketParams(PREAMBLE_LENGTH_32_BITS, FLRC_SYNC_WORD_LEN_P32S, RADIO_RX_MATCH_SYNCWORD_1, RADIO_PACKET_VARIABLE_LENGTH, 127, RADIO_CRC_3_BYTES, RADIO_WHITENING_OFF); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + LT.setSyncWord1(Sample_Syncword); + //*************************************************************************************************** + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured modem settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/52_FLRC_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/52_FLRC_Transmitter/Settings.h new file mode 100644 index 0000000..28822db --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/52_FLRC_Transmitter/Settings.h @@ -0,0 +1,40 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //connect a buzzer here if wanted + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//FLRC Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs +const uint8_t CodingRate = FLRC_CR_1_2; //FLRC coding rate +const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT +const uint32_t Sample_Syncword = 0x01234567; //FLRC uses syncword + + +const int8_t TXpower = 0; //power for transmissions in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/53_FLRC_Receiver/53_FLRC_Receiver.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/53_FLRC_Receiver/53_FLRC_Receiver.ino new file mode 100644 index 0000000..272d100 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/53_FLRC_Receiver/53_FLRC_Receiver.ino @@ -0,0 +1,240 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 09/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a test receiver for the Fast Long Range Communication (FLRC) mode introduced + in the SX128X devices. The program listens for incoming packets using the FLRC settings in the 'Settings.h' + file. The pins to access the SX128X device need to be defined in the 'Settings.h' file also. + + There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, + if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. + The LED will flash for each packet received and the buzzer will sound, if fitted. + + Sample serial monitor output; + + 3s Hello World 1234567890*,CRC,DAAB,RSSI,-73dB,Length,23,Packets,1,Errors,0,IRQreg,6 + + If there is a packet error it might look like this, which is showing a CRC error, + + 6s PacketError,RSSI,-103dB,Length,119,Packets,3,Errors,1,IRQreg,46,IRQ_RX_DONE,IRQ_SYNCWORD_VALID,IRQ_CRC_ERROR + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } + + delay(250); //gives a longer buzzer and LED flash for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("53_FLRC_Receiver Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in the library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + + //*************************************************************************************************** + //Setup FLRC + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_LDO); + LT.setPacketType(PACKET_TYPE_FLRC); + LT.setRfFrequency(Frequency, Offset); + LT.setBufferBaseAddress(0, 0); + LT.setModulationParams(BandwidthBitRate, CodingRate, BT); + LT.setPacketParams(PREAMBLE_LENGTH_32_BITS, FLRC_SYNC_WORD_LEN_P32S, RADIO_RX_MATCH_SYNCWORD_1, RADIO_PACKET_VARIABLE_LENGTH, 127, RADIO_CRC_3_BYTES, RADIO_WHITENING_OFF); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + LT.setSyncWord1(Sample_Syncword); + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured modem settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/53_FLRC_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/53_FLRC_Receiver/Settings.h new file mode 100644 index 0000000..bf67dc2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Basics/53_FLRC_Receiver/Settings.h @@ -0,0 +1,42 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //connect a buzzer here if wanted + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//FLRC Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs +const uint8_t CodingRate = FLRC_CR_1_2; //FLRC coding rate +const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT +const uint32_t Sample_Syncword = 0x01234567; //FLRC uses syncword + +const int8_t TXpower = 0; //power for transmissions in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino new file mode 100644 index 0000000..fed73c0 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/10_LoRa_Link_Test_Transmitter.ino @@ -0,0 +1,266 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a program that can be used to test the effectiveness of a LoRa link or its + attached antennas. Simulations of antenna performance are no substitute for real world tests and this + simple program allows both long distance link performance to be evaluated and antenna performance to be + compared. + + The program sends short test packets that reduce in power by 1dBm at a time. The start power is defined + by start_power and the end power is defined by end_power (see Settings.h file). Once the end_power point + is reached, the program pauses a short while and starts the transmit sequence again at start_power. + The packet sent contains the power used to send the packet. By listening for the packets with the basic + LoRa receive program (4_LoRa_Receiver) you can see the reception results, which should look something + like this; + + 11s 1*T+05,CRC,80B8,RSSI,-73dBm,SNR,9dB,Length,6,Packets,9,Errors,0,IRQreg,50 + 12s 1*T+04,CRC,9099,RSSI,-74dBm,SNR,9dB,Length,6,Packets,10,Errors,0,IRQreg,50 + 14s 1*T+03,CRC,E07E,RSSI,-75dBm,SNR,9dB,Length,6,Packets,11,Errors,0,IRQreg,50 + + Above shows 3 packets received, the first at +05dBm (+05 in printout), the second at 4dBm (+04 in + printout) and the third at 3dBm (+03) in printout. + + If it is arranged so that reception of packets fails halfway through the sequence by attenuating either the + transmitter (with an SMA attenuator for instance) or the receiver (by placing it in a tin perhaps) then + if you swap transmitter antennas you can see the dBm difference in reception, which will be the dBm difference + (gain) of the antenna. + + To start the sequence a packet is sent with the number 999, when received it looks like this; + + T*1999 + + This received packet could be used for the RX program to be able to print totals etc. + + LoRa settings to use for the link test are specified in the 'Settings.h' file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include +#include "Settings.h" + +SX128XLT LT; + +int8_t TestPower; +uint8_t TXPacketL; + +void loop() +{ + Serial.println(F("Start Test Sequence")); + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("Start Packet> ")); + + SendTest1ModePacket(); + + Serial.println(); + + for (TestPower = start_power; TestPower >= end_power; TestPower--) + { + Serial.print(TestPower); + Serial.print(F("dBm ")); + Serial.print(F("Test Packet> ")); + Serial.flush(); + SendTestPacket(TestPower); + Serial.println(); + delay(packet_delay); + } + + Serial.println(F("Finished Test Sequence")); + Serial.println(); +} + + +void SendTestPacket(int8_t lpower) +{ + //build and send the test packet in addressed form, 3 bytes will be added to begining of packet + int8_t temppower; + uint8_t buff[3]; //the packet is built in this buffer + TXPacketL = sizeof(buff); + + if (lpower < 0) + { + buff[0] = '-'; + } + else + { + buff[0] = '+'; + } + + if (TestPower == 0) + { + buff[0] = ' '; + } + + temppower = TestPower; + + if (temppower < 0) + { + temppower = -temppower; + } + + if (temppower > 19) + { + buff[1] = '2'; + buff[2] = ((temppower - 20) + 0x30); + } + else if (temppower > 9) + { + buff[1] = '1'; + buff[2] = ((temppower - 10) + 0x30); + } + else + { + buff[1] = '0'; + buff[2] = (temppower + 0x30); + } + + LT.printASCIIPacket(buff, sizeof(buff)); + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitAddressed(buff, sizeof(buff), TestPacket, Broadcast, ThisNode, 5000, lpower, WAIT_TX); + digitalWrite(LED1, LOW); + + if (TXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } +} + + +void SendTest1ModePacket() +{ + //used to allow an RX to recognise the start off the sequence and possibly print totals + + uint8_t buff[3]; //the packet is built in this buffer + + buff[0] = '9'; + buff[1] = '9'; + buff[2] = '9'; + TXPacketL = sizeof(buff); + + LT.printASCIIPacket(buff, sizeof(buff)); + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitAddressed(buff, sizeof(buff), TestMode1, Broadcast, ThisNode, 5000, start_power, WAIT_TX); + delay(mode_delaymS); //longer delay, so that the start test sequence is obvious + digitalWrite(LED1, LOW); + + if (TXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes SentOK")); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + delay(packet_delay); //change LED flash so packet error visible + delay(packet_delay); + digitalWrite(LED1, HIGH); + delay(packet_delay); + delay(packet_delay); + digitalWrite(LED1, LOW); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("10_LoRa_Link_Test_Transmitter Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); + +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h new file mode 100644 index 0000000..9a23e28 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/10_LoRa_Link_Test_Transmitter/Settings.h @@ -0,0 +1,47 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3 may not be in used by this sketch so they do not need to be connected and should +//be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX128X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define RX_EN -1 //pin for RX enable, used on some SX128X devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX128X devices, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +const int8_t TXpower = 10; //Transmit power used when sending packet starting test sequence +const int8_t start_power = 10; //link test starts at this transmit power, maximum +12dBm +const int8_t end_power = -18; //and ends at this power, minimum -18dBm +const uint8_t ThisNode = 'T'; //this identifies the node in transmissions + + +#define packet_delay 250 //mS delay between packets +#define mode_delaymS 2000 //mS delay after sending start test sequence + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino new file mode 100644 index 0000000..885b024 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/11_LoRa_Packet_Logger_Receiver.ino @@ -0,0 +1,223 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 01/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout of the valid packets received in HEX format. Thus the program can be used to receive + and record non-ASCII packets. The LED will flash for each packet received and the buzzer will sound, + if fitted. The measured frequency difference between the frequency used by the transmitter and the + frequency used by the receiver is shown. If this frequency difference gets to 25% of the set LoRa + bandwidth, packet reception will fail. The displayed error can be reduced by using the 'offset' + setting in the 'Settings.h' file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" +#include //get the library here; https://github.com/PaulStoffregen/Time +time_t recordtime; //used to record the current time, preventing displayed rollover on printing + +SX128XLT LT; + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 60000, WAIT_RX); //returns 0 if packet error of some sort, timeout set at 60secs\60000mS + + digitalWrite(LED1, HIGH); //something has happened + recordtime = now(); //stop the time to be displayed rolling over + printtime(); + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + delay(50); //lets have a slightly longer beep + digitalWrite(BUZZER, LOW); + } + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + RXpacketCount++; + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + Serial.print(F(" FreqErrror,")); + Serial.print(LT.getFrequencyErrorHz()); + Serial.print(F("hz ")); + + LT.printSXBufferHEX(0, (RXPacketL - 1)); + + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void printDigits(int8_t digits) +{ + //utility function for digital clock display: prints preceding colon and leading 0 + Serial.print(F(":")); + if (digits < 10) + Serial.print('0'); + Serial.print(digits); +} + + +void printtime() +{ + Serial.print(hour(recordtime)); + printDigits(minute(recordtime)); + printDigits(second(recordtime)); +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("11_LoRa_Packet_Logger_Receiver Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("Radio Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(); + LT.printModemSettings(); + Serial.println(); + LT.printOperatingSettings(); + Serial.println(); + Serial.println(); + printtime(); + Serial.print(F(" Receiver ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h new file mode 100644 index 0000000..05daf7f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/11_LoRa_Packet_Logger_Receiver/Settings.h @@ -0,0 +1,40 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 01/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //pin for buzzer, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const uint8_t TXpower = 10; //Power for transmissions in dBm + +#define packet_delay 1000 //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino new file mode 100644 index 0000000..ea72ff3 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/16_LoRa_RX_Frequency_Error_Check.ino @@ -0,0 +1,183 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program can be used to check the frequency error between a pair of LoRa + devices, a transmitter and receiver. This receiver measures the frequecy error between the receivers + centre frequency and the centre frequency of the transmitted packet. The frequency difference is shown + for each packet and an average over 10 received packets reported. Any transmitter program can be used + to give this program something to listen to, including example program '3_LoRa_Transmit'. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" + +SX128XLT LT; + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //a buffer is needed to receive packets + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet +int32_t totalHzError = 0; //used to keep a running total of hZ error for averaging + + +void loop() +{ + + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 0, WAIT_RX); //wait for a packet to arrive + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + RXpacketCount++; + Serial.print(F("PacketOK > ")); + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + Serial.println(); + printFrequencyError(); +} + + +void printFrequencyError() +{ + int32_t hertzerror, regdata; + regdata = LT.getFrequencyErrorRegValue(); + hertzerror = LT.getFrequencyErrorHz(); + Serial.print(F("ErrorRegValue,")); + Serial.print(regdata, HEX); + Serial.print(F(" PacketHertzError,")); + Serial.print(hertzerror); + Serial.println(F("hz")); + + totalHzError = totalHzError + hertzerror; + + if (RXpacketCount == 10) + { + Serial.print(F("******** AverageHertzerror ")); + Serial.print((totalHzError / 10)); + Serial.println(F("hz")); + RXpacketCount = 0; + totalHzError = 0; + delay(5000); + } +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + errors++; + Serial.print(F("PacketError,RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("16_LoRa_RX_Frequency_Error_Check Starting")); + Serial.println(); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h new file mode 100644 index 0000000..ae41040 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/16_LoRa_RX_Frequency_Error_Check/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const uint8_t TXpower = 10; //Power for transmissions in dBm + +#define packet_delay 1000 //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino new file mode 100644 index 0000000..a33632a --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/20_LoRa_Link_Test_Receiver.ino @@ -0,0 +1,317 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + The program is a matching receiver program for the '10_LoRa_Link_Test_Transmitter'. The packets received + are displayed on the serial monitor and analysed to extract the packet data which indicates the power + used to send the packet. A count is kept of the numbers of each power setting received. When the transmitter + sends the test mode packet at the beginning of the sequence (displayed as 999) the running totals of the + powers received are printed. Thus you can quickly see at what transmit power levels the reception fails. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc +#include + +SX128XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + +uint32_t Test1Count[32]; //buffer where counts of received packets are stored, -18dbm to +12dBm +uint32_t Mode1_Cycles = 0; //count the number of cyles received +bool updateCounts = false; //update counts set to tru when first TestMode1 received, at sequence start + + +void loop() +{ + RXPacketL = LT.receiveAddressed(RXBUFFER, RXBUFFER_SIZE, 15000, WAIT_RX); //wait for a packet to arrive with 15seconds (15000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + delay(25); //gives a slightly longer beep + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus; + + if (BUZZER > 0) //turn buzzer on for a valid packet + { + digitalWrite(BUZZER, HIGH); + } + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL - 3); //print the packet as ASCII characters + + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + + processPacket(); +} + + +void processPacket() +{ + int8_t lTXpower; + uint8_t packettype; + uint32_t temp; + + packettype = LT.readRXPacketType(); //need to know the packet type so we can decide what to do + + if (packettype == TestPacket) + { + if (RXBUFFER[0] == ' ') + { + lTXpower = 0; + } + + if (RXBUFFER[0] == '+') + { + lTXpower = ((RXBUFFER[1] - 48) * 10) + (RXBUFFER[2] - 48); //convert packet text to power + } + + if (RXBUFFER[0] == '-') + { + lTXpower = (((RXBUFFER[1] - 48) * 10) + (RXBUFFER[2] - 48)) * -1; //convert packet text to power + } + + Serial.print(F(" (")); + + if (RXBUFFER[0] != '-') + { + Serial.write(RXBUFFER[0]); + } + + Serial.print(lTXpower); + Serial.print(F("dBm)")); + + if (updateCounts) + { + temp = (Test1Count[lTXpower+18]); + Test1Count[lTXpower+18] = temp + 1; + } + } + + if (packettype == TestMode1) + { + //this is a command to switch to TestMode1 also updates totals and logs + updateCounts = true; + Serial.println(); + Serial.println(F("End test sequence")); + + if (Mode1_Cycles > 0) + { + print_Test1Count(); + } + + Serial.println(); + Mode1_Cycles++; + } + +} + + +void print_Test1Count() +{ + //prints running totals of the powers of received packets + int8_t index; + uint32_t j; + + Serial.print(F("Test Packets ")); + Serial.println(RXpacketCount); + Serial.print(F("Test Cycles ")); + Serial.println(Mode1_Cycles); + + Serial.println(); + for (index = 30; index >= 0; index--) + { + Serial.print(index-18); + Serial.print(F("dBm,")); + j = Test1Count[index]; + Serial.print(j); + Serial.print(F(" ")); + } + Serial.println(); + + Serial.print(F("CSV")); + for (index = 30; index >= 0; index--) + { + Serial.print(F(",")); + j = Test1Count[index]; + Serial.print(j); + } + Serial.println(); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("20_LoRa_Link_Test_Receiver Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + //setup SPI, its external to library on purpose, so settings can be mixed and matched with other SPI devices + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //this function call sets up the device for LoRa using the settings from settings.h + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h new file mode 100644 index 0000000..3f23d16 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/20_LoRa_Link_Test_Receiver/Settings.h @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3 may not be in used by this sketch so they do not need to be connected and should +//be set to -1. + + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define LED1 8 //on board LED, high for on +#define RFBUSY 7 //SX128X busy pin +#define DIO1 3 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define RX_EN -1 //pin for RX enable, used on some SX128X devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX128X devices, set to -1 if not used +#define BUZZER -1 //pin for buzzer, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino new file mode 100644 index 0000000..0196fd7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/33_LoRa_RSSI_Checker_With_Display.ino @@ -0,0 +1,283 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 01/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program listens for incoming packets using the LoRa settings in the 'Settings.h' + file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout of the valid packets received, the packet is assumed to be in ASCII printable text, + if its not ASCII text characters from 0x20 to 0x7F, expect weird things to happen on the Serial Monitor. + The LED will flash for each packet received and the buzzer will sound, if fitted. + + Sample serial monitor output; + + 1109s {packet contents} CRC,3882,RSSI,-69dBm,SNR,10dB,Length,19,Packets,1026,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 1189s PacketError,RSSI,-111dBm,SNR,-12dB,Length,0,Packets,1126,Errors,1,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + A summary of the packet reception is sent to the OLED display as well, useful for portable applications. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +#include //get library here > https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 + + +uint32_t RXpacketCount; +uint32_t RXpacketErrors; +uint16_t IRQStatus; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 0, WAIT_RX); //wait for a packet to arrive with no timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); //buzzer on + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t localCRC; + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(RXpacketErrors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("OK")); + dispscreen1(); +} + + +void packet_is_Error() +{ + printElapsedTime(); //print elapsed time to Serial Monitor + + RXpacketErrors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(RXpacketErrors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("Packet Error")); + dispscreen1(); + + delay(500); //gives longer buzzer and LED falsh for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void dispscreen1() +{ + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(F("RSSI ")); + disp.print(PacketRSSI); + disp.print(F("dBm")); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(F("SNR ")); + + if (PacketSNR > 0) + { + disp.print(F("+")); + } + + disp.print(PacketSNR); + disp.print(F("dB")); + disp.clearLine(3); + disp.setCursor(0, 3); + disp.print(F("Length ")); + disp.print(LT.readRXPacketL()); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(F("Packets ")); + disp.print(RXpacketCount); + disp.clearLine(5); + disp.setCursor(0, 5); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("IRQreg ")); + disp.print(IRQStatus, HEX); +} + + + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("33_LoRa_RSSI_Checker_With_Display Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Check LoRa")); + disp.setCursor(0, 1); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + disp.print(F("LoRa OK")); + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + disp.print(F("Device error")); + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //this function call sets up the device for LoRa using the settings from settings.h + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h new file mode 100644 index 0000000..013c247 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/33_LoRa_RSSI_Checker_With_Display/Settings.h @@ -0,0 +1,40 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 01/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //pin for BUZZER, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const uint8_t TXpower = 10; //Power for transmissions in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino new file mode 100644 index 0000000..db1b7a1 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/42_LoRa_Data_Throughput_Test_Transmitter.ino @@ -0,0 +1,282 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 15/05/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a program that can be used to test the throughput of a LoRa transmitter. + Whilst the various LoRa calculators tell you the on air data rate, in practice the achievable data + rate will be less than that due to the overhead of the software routines to load and send a packet + and internal delays in the LoRa device itself. + + A buffer is filled with characters and that buffer is then transmitted. The total time for a number of + transmissions is recorded and the bit rate calculated. The packet size (1 - 255 bytes) and the number of + packets to send in the test are specified in the 'Settings.h' file, see the 'Setup packet parameters Here !' + section. The setting file also has the lora settings to use. A lower spreading factors and higher + bandwidths will result in higher bitrates. + + There is the option of turning on an a requirement for an acknowledgement from a remote receiver, before + the transmitter sends the next packet, set this; 'const bool waitforACK = true;' definition in the + settings file. The matching receiver program '43_LoRa_Data_Throughput_Acknowledge_Receiver' does then need + to be configured with same lora settings as this transmitter. When this option is set, the program will + keep running until the number of transmissions and acknowledgements has completed without any timeouts + in order to produce a valid average. + + The results of the test are printed out thus; + + SX1280,434000000hz,SF7,BW500000,CR4:5,LDRO_Off,SyncWord_0x12,IQNormal,Preamble_8 + Total transmit time 100 packets = 1382mS + Average 16 byte packet transmit time = 13.82mS + Packets per second 72.36 + Bits per packet sent = 128 + Data rate = 9262bps + + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +uint32_t startmS, endmS, sendtimemS, bitspersecond, bitsPerpacket; +uint32_t TXPacketCount; +float averagePacketTime; +uint8_t packetNumber; +uint8_t RXPacketL; //length of received packet +uint8_t PacketType; //for packet addressing, identifies packet type received +uint32_t packetCheck; +bool loopFail = false; + + +void loop() +{ + uint16_t index, index2; + uint8_t TXBUFFER[TXPacketL + 1]; //create buffer for transmitted packet + loopFail = false; + + Serial.println(F("Start transmit test")); + + startmS = millis(); //start transmit timer + + for (index = 0; index < numberPackets; index++) + { + //fill the buffer + for (index2 = 0; index2 < TXPacketL; index2++) + { + TXBUFFER[index2] = index2; + } + + TXBUFFER[0] = TestPacket; //set first byte to identify this test packet + TXBUFFER[1] = index; //put the index as packet number in second byte of packet + Serial.print(index); //print number of packet sent + + if (waitforACK) + { + packetCheck = ( (uint32_t) TXBUFFER[4] << 24) + ( (uint32_t) TXBUFFER[3] << 16) + ( (uint32_t) TXBUFFER[2] << 8) + (uint32_t) TXBUFFER[1]; + Serial.print(F(",Checkvalue,")); + Serial.print(packetCheck, HEX); + Serial.print(F(",")); + } + + digitalWrite(LED1, HIGH); + + if (LT.transmit(TXBUFFER, TXPacketL, 10000, TXpower, WAIT_TX)) //will return 0 if transmit error + { + digitalWrite(LED1, LOW); + + if (waitforACK) + { + if (!waitAck(packetCheck)) + { + Serial.print(F("NoACKreceived,")); + loopFail = true; + break; + } + } + + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + loopFail = true; + } + + Serial.println(); + + } + + if (!loopFail) + { + endmS = millis(); //all packets sent, note end time + digitalWrite(LED1, LOW); + Serial.println(); + LT.printModemSettings(); + sendtimemS = endmS - startmS; + Serial.println(); + Serial.print(F("Total transmit time ")); + Serial.print(numberPackets); + Serial.print(F(" packets = ")); + Serial.print(sendtimemS); + Serial.println(F("mS")); + + averagePacketTime = (float) ((endmS - startmS) / numberPackets); + + if (waitforACK) + { + Serial.print(F("Average ")); + Serial.print(TXPacketL); + Serial.print(F(" byte packet transmit and acknowledge time = ")); + } + else + { + Serial.print(F("Average ")); + Serial.print(TXPacketL); + Serial.print(F(" byte packet transmit time = ")); + } + + Serial.print(averagePacketTime, 2); + Serial.println(F("mS")); + Serial.print(F("Packets per second ")); + Serial.println((float) (1000/averagePacketTime)); + bitsPerpacket = (uint32_t) (TXPacketL * 8); + Serial.print(F("Bits per packet sent = ")); + Serial.println(bitsPerpacket); + + Serial.print(F("Data rate = ")); + Serial.print((bitsPerpacket / (averagePacketTime / 1000)), 0); + Serial.print(F("bps")); + Serial.println(); + Serial.println(); + delay(10000); //have a delay between loops so we can see result + } + else + { + Serial.println(F("Transmit test failed, trying again")); + Serial.println(); + delay(1000); + } + +} + + +bool waitAck(uint32_t TXnum) +{ + uint32_t RXnum; + uint16_t IRQStatus; + + RXPacketL = LT.receiveSXBuffer(0, 1000, WAIT_RX); //returns 0 if packet error of some sort + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F("RXTimeout,")); + return false; + } + else + { + Serial.print(F("ACKRX,")); + } + + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + RXnum = LT.readUint32(); + RXPacketL = LT.endReadSXBuffer(); + + if ( (PacketType != ACK) || (RXnum != TXnum)) + { + Serial.print(F("NotValidACK,")); + return false; + } + else + { + Serial.print(RXnum, HEX); + Serial.print(F(",OK")); + return true; + } + +} + + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("42_LoRa_Data_Throughput_Test_Transmitter Starting")); + + SPI.begin(); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h new file mode 100644 index 0000000..1ea3ea8 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter/Settings.h @@ -0,0 +1,42 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 26/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER are not used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //RFBUSY pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO1 3 //DIO0 pin on LoRa device, used for RX and TX done + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes + +#define Bandwidth LORA_BW_1600 //LoRa bandwidth +#define SpreadingFactor LORA_SF5 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +//******* Setup packet parameters Here ! *************** +const uint8_t numberPackets = 50; //number of packets to send in transmit loop +const uint8_t TXPacketL = 16; //length of packet to send +const bool waitforACK = false; //set to true to have transmit wait for ack before continuing + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter_FLRC/42_LoRa_Data_Throughput_Test_Transmitter_FLRC.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter_FLRC/42_LoRa_Data_Throughput_Test_Transmitter_FLRC.ino new file mode 100644 index 0000000..cfa2942 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter_FLRC/42_LoRa_Data_Throughput_Test_Transmitter_FLRC.ino @@ -0,0 +1,294 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 15/05/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a program that can be used to test the throughput of a LoRa transmitter. + Whilst the various LoRa calculators tell you the on air data rate, in practice the achievable data + rate will be less than that due to the overhead of the software routines to load and send a packet + and internal delays in the LoRa device itself. + + A buffer is filled with characters and that buffer is then transmitted. The total time for a number of + transmissions is recorded and the bit rate calculated. The packet size (1 - 255 bytes) and the number of + packets to send in the test are specified in the 'Settings.h' file, see the 'Setup packet parameters Here !' + section. The setting file also has the lora settings to use. A lower spreading factors and higher + bandwidths will result in higher bitrates. + + There is the option of turning on an a requirement for an acknowledgement from a remote receiver, before + the transmitter sends the next packet, set this; 'const bool waitforACK = true;' definition in the + settings file. The matching receiver program '43_LoRa_Data_Throughput_Acknowledge_Receiver' does then need + to be configured with same lora settings as this transmitter. When this option is set, the program will + keep running until the number of transmissions and acknowledgements has completed without any timeouts + in order to produce a valid average. + + The results of the test are printed out thus; + + SX1280,434000000hz,SF7,BW500000,CR4:5,LDRO_Off,SyncWord_0x12,IQNormal,Preamble_8 + Total transmit time 100 packets = 1382mS + Average 16 byte packet transmit time = 13.82mS + Packets per second 72.36 + Bits per packet sent = 128 + Data rate = 9262bps + + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +uint32_t startmS, endmS, sendtimemS, bitspersecond, bitsPerpacket; +uint32_t TXPacketCount; +float averagePacketTime; +uint8_t packetNumber; +uint8_t RXPacketL; //length of received packet +uint8_t PacketType; //for packet addressing, identifies packet type received +uint32_t packetCheck; +bool loopFail = false; + + +void loop() +{ + uint16_t index, index2; + uint8_t TXBUFFER[TXPacketL + 1]; //create buffer for transmitted packet + loopFail = false; + + Serial.println(F("Start transmit test")); + + startmS = millis(); //start transmit timer + + for (index = 0; index < numberPackets; index++) + { + //fill the buffer + for (index2 = 0; index2 < TXPacketL; index2++) + { + TXBUFFER[index2] = index2; + } + + TXBUFFER[0] = TestPacket; //set first byte to identify this test packet + TXBUFFER[1] = index; //put the index as packet number in second byte of packet + Serial.print(index); //print number of packet sent + + if (waitforACK) + { + packetCheck = ( (uint32_t) TXBUFFER[4] << 24) + ( (uint32_t) TXBUFFER[3] << 16) + ( (uint32_t) TXBUFFER[2] << 8) + (uint32_t) TXBUFFER[1]; + Serial.print(F(",Checkvalue,")); + Serial.print(packetCheck, HEX); + Serial.print(F(",")); + } + + digitalWrite(LED1, HIGH); + + if (LT.transmit(TXBUFFER, TXPacketL, 10000, TXpower, WAIT_TX)) //will return 0 if transmit error + { + digitalWrite(LED1, LOW); + + if (waitforACK) + { + if (!waitAck(packetCheck)) + { + Serial.print(F("NoACKreceived,")); + loopFail = true; + break; + } + } + + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + loopFail = true; + } + + Serial.println(); + + } + + if (!loopFail) + { + endmS = millis(); //all packets sent, note end time + digitalWrite(LED1, LOW); + Serial.println(); + LT.printModemSettings(); + sendtimemS = endmS - startmS; + Serial.println(); + Serial.print(F("Total transmit time ")); + Serial.print(numberPackets); + Serial.print(F(" packets = ")); + Serial.print(sendtimemS); + Serial.println(F("mS")); + + averagePacketTime = (float) ((endmS - startmS) / numberPackets); + + if (waitforACK) + { + Serial.print(F("Average ")); + Serial.print(TXPacketL); + Serial.print(F(" byte packet transmit and acknowledge time = ")); + } + else + { + Serial.print(F("Average ")); + Serial.print(TXPacketL); + Serial.print(F(" byte packet transmit time = ")); + } + + Serial.print(averagePacketTime, 2); + Serial.println(F("mS")); + Serial.print(F("Packets per second ")); + Serial.println((float) (1000/averagePacketTime)); + bitsPerpacket = (uint32_t) (TXPacketL * 8); + Serial.print(F("Bits per packet sent = ")); + Serial.println(bitsPerpacket); + + Serial.print(F("Data rate = ")); + Serial.print((bitsPerpacket / (averagePacketTime / 1000)), 0); + Serial.print(F("bps")); + Serial.println(); + Serial.println(); + delay(10000); //have a delay between loops so we can see result + } + else + { + Serial.println(F("Transmit test failed, trying again")); + Serial.println(); + delay(1000); + } + +} + + +bool waitAck(uint32_t TXnum) +{ + uint32_t RXnum; + uint16_t IRQStatus; + + RXPacketL = LT.receiveSXBuffer(0, 1000, WAIT_RX); //returns 0 if packet error of some sort + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F("RXTimeout,")); + return false; + } + else + { + Serial.print(F("ACKRX,")); + } + + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + RXnum = LT.readUint32(); + RXPacketL = LT.endReadSXBuffer(); + + if ( (PacketType != ACK) || (RXnum != TXnum)) + { + Serial.print(F("NotValidACK,")); + return false; + } + else + { + Serial.print(RXnum, HEX); + Serial.print(F(",OK")); + return true; + } + +} + + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("42_LoRa_Data_Throughput_Test_Transmitter_FLRC Starting")); + + SPI.begin(); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //*************************************************************************************************** + //Setup FLRC + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_LDO); + LT.setPacketType(PACKET_TYPE_FLRC); + LT.setRfFrequency(Frequency, Offset); + LT.setBufferBaseAddress(0, 0); + LT.setModulationParams(BandwidthBitRate, CodingRate, BT); + LT.setPacketParams(PREAMBLE_LENGTH_32_BITS, FLRC_SYNC_WORD_LEN_P32S, RADIO_RX_MATCH_SYNCWORD_1, RADIO_PACKET_VARIABLE_LENGTH, 127, RADIO_CRC_3_BYTES, RADIO_WHITENING_OFF); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + LT.setSyncWord1(Sample_Syncword); + //*************************************************************************************************** + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter_FLRC/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter_FLRC/Settings.h new file mode 100644 index 0000000..d9bd38e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Diagnostic_and_Test/42_LoRa_Data_Throughput_Test_Transmitter_FLRC/Settings.h @@ -0,0 +1,42 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 26/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER are not used by this sketch so they do not need to be connected and +//should be set to -1. + +#define NSS 10 //select pin on LoRa device +#define NRESET 9 //reset pin on LoRa device +#define RFBUSY 7 //RFBUSY pin on LoRa device +#define LED1 8 //on board LED, high for on +#define DIO1 3 //DIO0 pin on LoRa device, used for RX and TX done + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes + +const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; //FLRC bandwidth and bit rate, 1.3Mbs +const uint8_t CodingRate = FLRC_CR_1_2; //FLRC coding rate +const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; //FLRC BT +const uint32_t Sample_Syncword = 0x01234567; //FLRC uses syncword +const int8_t TXpower = 10; //LoRa transmit power in dBm + + +//******* Setup packet parameters Here ! *************** +const uint8_t numberPackets = 50; //number of packets to send in transmit loop +const uint8_t TXPacketL = 16; //length of packet to send +const bool waitforACK = false; //set to true to have transmit wait for ack before continuing + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/103_LoRa_Transmitter_Detailed_Setup_ESP32.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/103_LoRa_Transmitter_Detailed_Setup_ESP32.ino new file mode 100644 index 0000000..da118b2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/103_LoRa_Transmitter_Detailed_Setup_ESP32.ino @@ -0,0 +1,183 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 04/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - This is a program that demonstrates the detailed setup of a LoRa test transmitter. + A packet containing ASCII text is sent according to the frequency and LoRa settings specified in the + 'Settings.h' file. The pins to access the lora device need to be defined in the 'Settings.h' file also. + + The details of the packet sent and any errors are shown on the Arduino IDE Serial Monitor, together with + the transmit power used, the packet length and the CRC of the packet. The matching receive program, + '104_LoRa_Receiver' can be used to check the packets are being sent correctly, the frequency and LoRa + settings (in Settings.h) must be the same for the transmitter and receiver programs. Sample Serial + Monitor output; + + 10dBm Packet> Hello World 1234567890* BytesSent,23 CRC,DAAB TransmitTime,64mS PacketsSent,2 + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +uint8_t TXPacketL; +uint32_t TXPacketCount, startmS, endmS; + +uint8_t buff[] = "Hello World 1234567890"; + +void loop() +{ + Serial.print(TXpower); //print the transmit power defined + Serial.print(F("dBm ")); + Serial.print(F("Packet> ")); + Serial.flush(); + + TXPacketL = sizeof(buff); //set TXPacketL to length of array + buff[TXPacketL - 1] = '*'; //replace null character at buffer end so its visible on reciver + + LT.printASCIIPacket(buff, TXPacketL); //print the buffer (the sent packet) as ASCII + + digitalWrite(LED1, HIGH); + startmS = millis(); //start transmit timer + if (LT.transmit(buff, TXPacketL, 10000, TXpower, WAIT_TX)) //will return packet length sent if OK, otherwise 0 if transmit error + { + endmS = millis(); //packet sent, note end time + TXPacketCount++; + packet_is_OK(); + } + else + { + packet_is_Error(); //transmit packet returned 0, there was an error + } + + digitalWrite(LED1, LOW); + Serial.println(); + delay(packet_delay); //have a delay between packets +} + + +void packet_is_OK() +{ + //if here packet has been sent OK + uint16_t localCRC; + + Serial.print(F(" BytesSent,")); + Serial.print(TXPacketL); //print transmitted packet length + localCRC = LT.CRCCCITT(buff, TXPacketL, 0xFFFF); + Serial.print(F(" CRC,")); + Serial.print(localCRC, HEX); //print CRC of transmitted packet + Serial.print(F(" TransmitTime,")); + Serial.print(endmS - startmS); //print transmit time of packet + Serial.print(F("mS")); + Serial.print(F(" PacketsSent,")); + Serial.print(TXPacketCount); //print total of packets sent OK +} + + +void packet_is_Error() +{ + //if here there was an error transmitting packet + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("103_LoRa_Transmitter_Detailed_Setup Starting")); + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); //two further quick LED flashes to indicate device found + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_LDO); + LT.setPacketType(PACKET_TYPE_LORA); + LT.setRfFrequency(Frequency, Offset); + LT.setBufferBaseAddress(0, 0); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate); + LT.setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers, normally 0x900 to 0x9FF + Serial.println(); + Serial.println(); + + Serial.print(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/Settings.h new file mode 100644 index 0000000..64e0f06 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/103_LoRa_Transmitter_Detailed_Setup_ESP32/Settings.h @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 02/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, a ESP32 shield base with BBF board shield on +//top. Be sure to change the definitions to match your own setup. Some pins such as DIO2, DIO3, BUZZER +//may not be in used by this sketch so they do not need to be connected and should be included and be +//set to -1. + +#define NSS 5 //select pin on LoRa device +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NRESET 27 //reset pin on LoRa device +#define RFBUSY 25 //busy line + +#define LED1 2 //on board LED, high for on +#define DIO1 35 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define RX_EN -1 //pin for RX enable, used on some SX128X devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX128X devices, set to -1 if not used +#define BUZZER -1 //pin for buzzer, set to -1 if not used +#define VCCPOWER 14 //pin controls power to external devices +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/104_LoRa_Receiver_Detailed_Setup_ESP32.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/104_LoRa_Receiver_Detailed_Setup_ESP32.ino new file mode 100644 index 0000000..49b2924 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/104_LoRa_Receiver_Detailed_Setup_ESP32.ino @@ -0,0 +1,247 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 04/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a program that demonstrates the detailed setup of a LoRa test receiver. + The program listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins + to access the lora device need to be defined in the 'Settings.h' file also. + + There is a printout on the Arduino IDE Serial Monitor of the valid packets received, the packet is + assumed to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect + weird things to happen on the Serial Monitor. The LED will flash for each packet received and the + buzzer will sound, if fitted. + + Sample serial monitor output; + + 7s Hello World 1234567890*,CRC,DAAB,RSSI,-52dBm,SNR,9dB,Length,23,Packets,5,Errors,0,IRQreg,50 + + If there is a packet error it might look like this, which is showing a CRC error, + + 968s PacketError,RSSI,-87dBm,SNR,-11dB,Length,23,Packets,613,Errors,2,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include //the lora device is SPI based so load the SPI library +#include //include the appropriate library +#include "Settings.h" //include the setiings file, frequencies, LoRa settings etc + +SX128XLT LT; //create a library class instance called LT + +uint32_t RXpacketCount; +uint32_t errors; + +uint8_t RXBUFFER[RXBUFFER_SIZE]; //create the buffer that received packets are copied into + +uint8_t RXPacketL; //stores length of packet received +int8_t PacketRSSI; //stores RSSI of received packet +int8_t PacketSNR; //stores signal to noise ratio (SNR) of received packet + + +void loop() +{ + RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL is 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + digitalWrite(LED1, LOW); //LED off + + Serial.println(); +} + + +void packet_is_OK() +{ + uint16_t IRQStatus, localCRC; + + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + RXpacketCount++; + + printElapsedTime(); //print elapsed time to Serial Monitor + Serial.print(F(" ")); + LT.printASCIIPacket(RXBUFFER, RXPacketL); //print the packet as ASCII characters + + localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER + Serial.print(F(",CRC,")); //contents, not the LoRa device internal CRC + Serial.print(localCRC, HEX); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(RXPacketL); + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //read the LoRa device IRQ status register + + printElapsedTime(); //print elapsed time to Serial Monitor + + if (IRQStatus & IRQ_RX_TIMEOUT) //check for an RX timeout + { + Serial.print(F(" RXTimeout")); + } + else + { + errors++; + Serial.print(F(" PacketError")); + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the device packet length + Serial.print(F(",Packets,")); + Serial.print(RXpacketCount); + Serial.print(F(",Errors,")); + Serial.print(errors); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); //print the names of the IRQ registers set + } + + delay(250); //gives a longer buzzer and LED flash for error + +} + + +void printElapsedTime() +{ + float seconds; + seconds = millis() / 1000; + Serial.print(seconds, 0); + Serial.print(F("s")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("104_LoRa_Receiver_Detailed_Setup_ESP32 Starting")); + Serial.println(); + + if (BUZZER > 0) + { + pinMode(BUZZER, OUTPUT); + digitalWrite(BUZZER, HIGH); + delay(50); + digitalWrite(BUZZER, LOW); + } + + SPI.begin(); + + //SPI beginTranscation is normally part of library routines, but if it is disabled in the library + //a single instance is needed here, so uncomment the program line below + //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); + + //setup hardware pins used by device, then check if device is found + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed LED flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device using the information defined in the + //Settings.h file. + //The 'Setup LoRa device' list below can be replaced with a single function call; + //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + //*************************************************************************************************** + //Setup LoRa device + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setRegulatorMode(USE_LDO); + LT.setPacketType(PACKET_TYPE_LORA); + LT.setRfFrequency(Frequency, Offset); + LT.setBufferBaseAddress(0, 0); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate); + LT.setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); + //*************************************************************************************************** + + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operting settings, useful check + Serial.println(); + Serial.println(); + LT.printRegisters(0x900, 0x9FF); //print contents of device registers, normally 0x900 to 0x9FF + Serial.println(); + Serial.println(); + + Serial.print(F("Receiver ready - RXBUFFER_SIZE ")); + Serial.println(RXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/Settings.h new file mode 100644 index 0000000..fed557e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Basics/104_LoRa_Receiver_Detailed_Setup_ESP32/Settings.h @@ -0,0 +1,51 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 04/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, a ESP32 shield base with BBF board shield on +//top. Be sure to change the definitions to match your own setup. Some pins such as DIO2, DIO3, BUZZER +//may not be in used by this sketch so they do not need to be connected and should be included and be +//set to -1. + +#define NSS 5 //select pin on LoRa device +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NRESET 27 //reset pin on LoRa device +#define RFBUSY 25 //busy line + +#define LED1 2 //on board LED, high for on +#define DIO1 35 //DIO1 pin on LoRa device, used for RX and TX done +#define DIO2 -1 //DIO2 pin on LoRa device, normally not used so set to -1 +#define DIO3 -1 //DIO3 pin on LoRa device, normally not used so set to -1 +#define RX_EN -1 //pin for RX enable, used on some SX128X devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX128X devices, set to -1 if not used +#define BUZZER -1 //pin for buzzer, set to -1 if not used +#define VCCPOWER 14 //pin controls power to external devices + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const int8_t TXpower = 10; //LoRa transmit power in dBm + +const uint16_t packet_delay = 1000; //mS delay between packets + +#define RXBUFFER_SIZE 32 //RX buffer size + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Pictures/ESP32_Bare_Bones_Schematic.jpg b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Pictures/ESP32_Bare_Bones_Schematic.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19b2873de7f359235804304b780c2826347e98c1 GIT binary patch literal 38883 zcmeEubwE|!w(q795Trp;T2e}y4Js`l-Jo=LBWyxZ8a5>%-6_%y(jZ;Z-QB(STYl%< zbM(9SJLkUp-uv$%Q}$+$HP;+-j{J?Wfd7Cmf*#09%1D9`5D-AGfqx+Q4Cpxs6&VE; z1sN3;1r-eq6&>RN2FAU67zEfjSPzH^NJxkYh=?9i&{I7mqa`OIqGqM0Wng^F{Fs!A z?Fk#x6MCk{Og|?VW{R7E&OLZAU5;vyj7BEZ{0 zR3H!n3b5Lr3;z2T0wNNyUNrQ37?=P-rEO$vVrph?Vd>=T;_Bw^;TiDmec*?WLBY`InAo`ZPYH<`nOWI6xq0~o zU&|{htEy{i>%O(McXW1j_w2GZPgR?*Jg$wY7h>VPcjP{c+1VlGrAmJjTJmN&f6IVieSV#!K;UVFIz@Y1#V5T(0|MBoY z-zQ^nO<4=>MAv=KDnkdHK2(NUjk{!PNzypU1H#dFBb;KoYkQOA=Dd*&vKZDH-BEwV zYAW@Kl%Pi3?vZ`kV%e)&rG2r?8#_gA)@O^*nI}7B1XoAZ8=doxoP(aA;f!D=(Epv0 zTWD}!bY9@4sr-o)HbN&~jV_Ed4J*uNd0NDFn&nP>cdx%99KH6*S{u6{ZnVGqdfrxI zh_1obP=6lRHkr~!xYjdJw!0FQt1VeBWu~8cSIBEYWqmR;b`D{YXSl z`%;Or8?*3b?$=si`sX`DK3!Qu&SQ99|Gs5w;(d6Zx-p*l8>F{N(t_4hLcn2?@vBU8_b0g=~U(S-X%I zao+UHX^PT~J6~qhjUI%)6R8*W=c=6vTtpR)t~jTiBzVFeX4^jSr2?Au&Xz`+PAoIo zmR?MEkAHwWxZ2F={#ub6(bs#AB5kE8&`ShtD+DU#gvvIivykZwc|LYhmBwM%T7Pbd z%tg!94W2F^Kdq+_9T2jZH>Xs>ctY;)F*_$}@X2YP3CZQHgQ2C7^NoJqIWpE?|@@XE+Q5wI+-foZ4BZ*)cnMF z#3C`qQFx*U`)Y6G9&V}e9A&2=M(?Y*ft6%G)#$dwNQZSff|9rXW_(=iq=>7Z{P%p4 zjmMLEOoA5-_}uuo(JAyFkq6S;6R$+jeZVDFfG6jXv#lN%&lfGXMn~r4*t%}!NL~=> z=GTSBPx&E{XmMaZc)dZuZGDF?>bd)c>%1(#X-xNX+t)LGNPV^euSu?~veN{AFl7XT z{!a&UNu~+K?LC6;+xg70Paj|_1>m&Ne7Th~!%klo!JgjvZcfTh8bxp1gTVPY%X4gR z={{jhq0WFf33$s1a^#ZAlVrixT+;dW*e0v$1P=0j;~s_@Tf7

5~d^d;Fzlj=uPt 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 literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Pictures/ESP32_Micro_Node.jpg b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/Pictures/ESP32_Micro_Node.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ff1845912bdda979f9f7f2cd3a3f57dd28a1b6c4 GIT binary patch literal 89760 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 aBlbjgw5AMM081{5p+Rot3BW>4AOG1u9^F0w literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/ReadMe.md b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/ReadMe.md new file mode 100644 index 0000000..f33e6f5 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/ESP32/ReadMe.md @@ -0,0 +1,111 @@ +## ESP32 - SX12XX Library Example Programs + +Having originally tested the SX127X part of the library on a ATMega328P based micro controller I decided to check that the library was code compatible with another popular processor that can be programmed in the Arduino IDE, the ESP32. + +Unfortunately there is frequently a misunderstanding about what an 'ESP32' is. The ESP32 is a single surface mountable module to which you need to add various components to make a usable board. However the many different types of 'ESP32' boards may use different types of components and connections which could conflict with the pins used in the library examples here. It is not practical to test the examples on all possible permutations of 'ESP32' assembled boards, there are over 60 different boards supported by the Arduino IDE and I would go poor buying them all. + +Thus the ESP32 examples presented here have been tested against a known standard which is an actual ESP32 Wroom module with no additional hardware, apart from the components shown in the schematic below. It is possible that the examples will not work against your particular 'ESP32' assembled board. If the example programs do not work for you, it is not an issue with the library but a difference between the reference hardware and your particular setup. I am not in a position to assist in resolving issues with particular ESP32 board versions. + +![Picture 1](/pictures/ESP32_Bare_Bones_Schematic.jpg) + + +As well as testing against the bare bones ESP32 schematic shown above, some of the examples will have been tested on a small portable unit I built which follows the same principles of the bare bones schematic. This PCB was developed to create a PCB that could be used for a small ESP32 based GPS tracker node, initially for use on The Things Network. The board has a GPS, I2C SSD1306 OLED and RFM98 lora device. There are options for a micro SD card, DS18B20 temperature sensor and a I2C FRAM. With it all assembled, the node consumes around 31uA in deep sleep with all devices connected. The 'Micro\_Node' contains additional circuitry to power off devices such as the lora module and GPS. Its highly unlikely that a standard ESP32 board will achieve a sleep current anywhere near the 'Micro_Node', this unit is pictured below; + +![Picture 1](/pictures/ESP32_Micro_Node.jpg) + + +### ESP32 Deep Sleep + +The ESP32 does some things with I\O pins when going into deep sleep that you might not expect. There are functions in this SX12XX library for putting the LoRa device into deep sleep, whilst still preserving register settings. The current then taken by the lora device is circa 0.5uA. However these functions will likely not work directly with most ESP32 boards due to the way the ESP32 handles the I\O pins in deep sleep mode. + +When designing a board where a very low deep sleep current it is important, you need to build the design in stages, checking the deep sleep current after adding each component. Debugging a high deep sleep current on a fully assembled board can be from extremely difficult to impossible. Achieving a low deep sleep current for particular ESP32 boards is well outside the scope of this SX12xx library. + +
+ +**3\_LoRa\_Transmitter and 4\_LoRa\_Receiver example programs** + +These example programs work when used with the ESP32 'bare bones' schematic shown earlier and the 'ESP32 Dev Module' board type selected in the Arduino IDE. + +The pins used for SPI were; + + SCK 18 + MISO 19 + MOSI 23 + NSS 5 + NRESET 27 + RFBUSY 25 (SX126X and SX128X devices only) + DIOX 35 (DIOX is DIO0 on the SX127X devices and DIO1 on SX126X and SX128X devices) + +In the example programs for ESP32, you can start the SPI with; + +SPI.begin(); + +And the default SPI pin outs for the ESP32 module shown above will be used. + +Alternatively you can uses this format to start SPI; + +SPI.begin(SCK, MISO, MOSI, NSS); + +And the pin definitions will be taken from those specified in the 'Settings.h' file. If you change these pin allocations from the defaults given you will need to be sure they are valid for your particular ESP32 board. + + +The SX12XX example programs are specifically written for and and tested on ATmega processors such as 328,1284 and 2560. However most will work, with minor modification, on the ESP32. + +This is a run through of the changes that were needed to have the tracker receiver examples; **25\_GPS\_Tracker\_Receiver\_With\_Display\_and\_GPS**, written for the ATmega328P run on the ESP32 bare bones type boards described above. + +The original SX12XX tracker program uses the SPI interface to talk to the lora device, I2C to talk to the OLED display and software serial to talk to the GPS. + +The pins used for SPI were; + + SCK 18 + MISO 19 + MOSI 23 + NSS 5 + NRESET 27 + RFBUSY 25 (SX126X and SX128X devices only) + DIOX 35 (DIOX is DIO0 on the SX127X devices and DIO1 on SX126X and SX128X devices) + +The ESP32 I2C pin connections were; + + SDA 21 + SCL 22 + +There is no need for software serial on the ESP32 as it has an available hardware serial port. These are the pin connections used; + + GPSTX 16 //this is data out from the GPS into the ESP32 + GPSRX 17 //this is data out from the ESP32 into the GPS + +The other pins used were; + + LED1 2 //On board indicator LED, logic high for on + GPSPOWER 26 //Pin that controls power to GPS, set to -1 if not used + +The only software changes required were to change the lines in the Settings.h file from; + + #define USE_SOFTSERIAL_GPS + #define HardwareSerialPort Serial2 + +to; + + //#define USE_SOFTSERIAL_GPS + #define HardwareSerialPort Serial2 + +This removes the definition USE\_SOFTSERIAL\_GPS from the sketch and the effect of this change is then to remove these two lines from the Sketch; + + #include + SoftwareSerial GPSserial(RXpin, Txpin); + +And include this one; + + #define GPSserial HardwareSerialPort + +Which sets the commands to read data from the GPS such as GPSserial.read() to be in effect Serial2.read(), which is the ESP32 hardware serial port used. + + +**Note:** The provided lora settings (in the Settings.h file) may not be optimised for long distance. See the 'What is LoRa' document for information on how LoRa settings affect range. + + +### Stuart Robinson + +### April 2020 + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino new file mode 100644 index 0000000..750455d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/8_LoRa_LowMemory_TX/8_LoRa_LowMemory_TX.ino @@ -0,0 +1,154 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program transmits a packet without using a processor buffer, the LoRa device + internal buffer is filled direct with variables. The program is a simulation of the type of packet + that might be sent from a GPS tracker. Note that in this example a buffer of text is part of the + transmitted packet, this does need a processor buffer which is used to fill the LoRa device internal + buffer, if you don't need to transmit text then the uint8_t trackerID[] = "Tracker1"; definition + can be ommited. + + The matching receiving program '9_LoRa_LowMemory_RX' can be used to receive and display the packet, + though the program '15_LoRa_RX_Structure' should receive it as well, since the packet contents are + the same. + + The contents of the packet received, and printed to serial monitor, should be; + + "tracker1" (buffer) - trackerID + 1+ (uint32_t) - packet count + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX128XLT LT; + +uint32_t TXpacketCount = 0; +uint8_t TXPacketL; +uint32_t startmS, endmS; + +void loop() +{ + TXpacketCount++; + + if (Send_Test_Packet()) + { + Serial.print(TXpacketCount); + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes Sent")); + Serial.print(F(" ")); + Serial.print(endmS - startmS); + Serial.print(F("mS")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } + + Serial.println(); + delay(packet_delay); +} + + +uint8_t Send_Test_Packet() +{ + //The SX12XX buffer is filled with variables of a known type and order. Make sure the receiver + //uses the same variable type and order to read variables out of the receive buffer. + + float latitude, longitude; + uint16_t altitude, voltage; + uint8_t satellites; + int16_t temperature; + uint8_t len; + + //test data + uint8_t trackerID[] = "tracker1"; + latitude = 51.23456; + longitude = -3.12345; + altitude = 199; + satellites = 9; + voltage = 3999; + temperature = -9; + + LT.startWriteSXBuffer(0); //start the write at location 0 + LT.writeBuffer(trackerID, sizeof(trackerID)); //= 13 bytes (12 characters plus null (0) at end) + LT.writeUint32(TXpacketCount); //+4 = 17 bytes + LT.writeFloat(latitude); //+4 = 21 bytes + LT.writeFloat(longitude); //+4 = 25 bytes + LT.writeUint16(altitude); //+2 = 27 bytes + LT.writeUint8(satellites); //+1 = 28 bytes + LT.writeUint16(voltage); //+2 = 30 bytes + LT.writeInt8(temperature); //+1 = 31 bytes total to send + len = LT.endWriteSXBuffer(); + + digitalWrite(LED1, HIGH); + startmS = millis(); + + TXPacketL = LT.transmitSXBuffer(0, len, 5000, TXpower, WAIT_TX); //set a TX timeout of 5000mS + + endmS = millis(); + + digitalWrite(LED1, LOW); + + return TXPacketL; +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(F("Transmitter ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h new file mode 100644 index 0000000..8b628ea --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/8_LoRa_LowMemory_TX/Settings.h @@ -0,0 +1,38 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //connect a buzzer here if wanted + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +#define TXpower 10 //power for transmissions in dBm + +#define packet_delay 1000 //mS delay between packets + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino new file mode 100644 index 0000000..cc6e2ed --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/9_LoRa_LowMemory_RX/9_LoRa_LowMemory_RX.ino @@ -0,0 +1,195 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 08/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program receives a packet without using a processor buffer, the LoRa device + internal buffer is read direct and copied to variables. The program is a simulation of the type of packet + that might be received from a GPS tracker. Note that in this example a buffer of text is part of the + received packet, this does need a processor buffer which is filled with data from the LoRa device internal + buffer, if you don't need to send and receive text then the uint8_t receivebuffer[32]; definition can be + ommited. + + The contents of the packet received, and printed to serial monitor, should be; + + "Tracker1" (buffer) - trackerID + 1+ (uint32_t) - packet count + 51.23456 (float) - latitude + -3.12345 (float) - longitude + 199 (uint16_t) - altitude + 8 (uint8_t) - number of satellites + 3999 (uint16_t) - battery voltage + -9 (int8_t) - temperature + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" + +SX128XLT LT; + +uint32_t RXpacketCount; +uint16_t errors; + +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet + + +void loop() +{ + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +uint8_t packet_is_OK() +{ + float latitude, longitude; + uint16_t altitude, voltage; + uint8_t satellites; + int8_t temperature; + uint32_t txcount; + + uint8_t receivebuffer[16]; //create receive buffer, make sure this is big enough for buffer sent !!! + + //packet has been received, now read from the SX12xx Buffer using the same variable type and + //order as the transmit side used. + + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(" ")); + + LT.startReadSXBuffer(0); //start buffer read at location 0 + LT.readBuffer(receivebuffer); //read in the character buffer + txcount = LT.readUint32(); //read in the TXCount + latitude = LT.readFloat(); //read in the latitude + longitude = LT.readFloat(); //read in the longitude + altitude = LT.readUint16(); //read in the altitude + satellites = LT.readUint8(); //read in the number of satellites + voltage = LT.readUint16(); //read in the voltage + temperature = LT.readInt8(); //read in the temperature + RXPacketL = LT.endReadSXBuffer(); + + Serial.print((char*)receivebuffer); //print the received buffer, cast to char needed + Serial.print(F(",")); + Serial.print(txcount); + Serial.print(F(",")); + Serial.print(latitude, 5); + Serial.print(F(",")); + Serial.print(longitude, 5); + Serial.print(F(",")); + Serial.print(altitude); + Serial.print(F("m,")); + Serial.print(satellites); + Serial.print(F("sats,")); + Serial.print(voltage); + Serial.print(F("mV,")); + Serial.print(temperature); + Serial.print(F("c ")); + Serial.print(F(" RSSI")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB")); + return RXPacketL; +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + printpacketDetails(); + Serial.print(F("IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void printpacketDetails() +{ + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB")); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h new file mode 100644 index 0000000..fce77b6 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/LowMemory/9_LoRa_LowMemory_RX/Settings.h @@ -0,0 +1,40 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 06/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //connect a buzzer here if wanted + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +#define TXpower 10 //power for transmissions in dBm + +#define packet_delay 1000 //mS delay between packets + +#define RXBUFFER_SIZE 255 //RX buffer size, not used in this program + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Pictures/Calibration_Values.jpg b/lib/SX12XX-LoRa/examples/SX128x_examples/Pictures/Calibration_Values.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ca0893a6d42587e2cabfa50790959b7ed2890229 GIT binary patch literal 20046 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

JH(CgxD^RlGlI!$Lu^?=1 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�W_e`8zg_;b0252p9w4X(Ok|xiL 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ⅅI?7)>#0PC82XJXH)ZJ1!tvok? zlH?v&rH6Yj9qo{wN_V5PYb<*Eq^uU;6^j+V4lpzb0eTqKBJmM*HbF87!R)Jq@R(}S z_}Nb>dyk^B3GNgbPNtqz-j;&OaphowNNvl_;c9)t+;+4~Yy$A(>b-U7vKBFwuog$n zXpGab-IJRPEEeauGz7AAtPP!4?9v#Z)0)W|H%fOC{FSq+BLhx%hv?L!6HRuSVq-zt z&;qb$#qk<-cO+8z9NE;eeZ}nar8~A!`cFN^cShsbr@c}nlMr!XSF~IyF}0_)!kSNY z&@~)J&4Mg?F8NI+oPC;KC1{~(-fTy-_flv&o3QgXxbbUu!0cSTpm|A1I*WD|NRhk( zs|2S10H|QoJ1!av~4uyU_7vTCdV!_rZn}lnP}R~*taox<<`2_Olz@fSdD9(3#oAIwwPcs zfyhicTUM72U^fsKeoSg{v>!)c>K!?`5fDc{g;De4lNvu;YP@K=v|5%kSoaOoht~9l z%?s#r?zopMZkZ`Yf%-eBBb|dEA;WiWRfkm5L)qg&&(U#ApNZ1-akFse(RKck);wG$ zw}^=-94~c_nugx+Z3u7D(|T0D`LhMF`Vyod*;qBm+cp=7IiRZAD~q%1 z($eSEh@EJ%YZ#1|5H-WmR$q$_EN&u!R()n)4iSFJm8|802nBQapQL6%hbcMWj@G&! zE5q=BKFgVDvVQr$Rnauv#}IuNLzfp!UsNl|#cY!jJ=P%(gC(tLJ631N@N!5#=#v)x zvW`JoN-7qLLMg@HVy>jf$N&hgicyV+E68)6c~;!KQIgO-P!eHFj^SgkTS_kRcF|Hg`-3Zsd9q+Szu9+yp637U~lT3Lw4B0O>ND0=HqMr|K z)vSYDG$t5RN(C{bmFGP9vcHc+ukyk)-6A|Gw>P?vs$V3Sg^_|u;WopDd0VNnM+6Jnmq8j9Op8$m+C6t zXxOngLw3f1TI=k%W_bPKUBj&>@wA!S--}#FQXX=>9|>{L(3Og1;{A$)p2bf$C%*`C zc^>Q4iPDYZkhjY2Tj^ejmKI~+aPwPz3Q=C`Xyq}VcArG**G8kD>6)3`(#LIZ_E&8) zQj0n<6x1K?eODO8%XBUdYl&y_R(&&8vf6bJs~KLH$(Nm1N^9EcXmfq5y5`+O)2@Zi zn-#ozwRC+SS!~P^WE&oiMrs;!Cy2R~&$1GQ>Xnfbp^?myv_hA}|BqD?MVaBUrf)mgPkycn3)@VW9qwNfV)w$LW2fO6&B#@I*n z{)%FlJ;IdQUQ=BgS_PkVVa9V>z*;#{!lhKg@$zFFXxh!7`YAOWfXHdR&@Gg=n?PzA zmvn5Zs9uMpx+{9_Xk^}VckZj%(jB|lI8HT;S7v#DHjriShCPo5gkq9Y*B6FX)y^1eL#&F0E7iC{39wRDUmq@vPd$dEE=;Jm!tZBHMweB{U z=7yGS^;~%1)3B#Zt3EH@IrNz5`NA0h;4I{0Y>bqnApARbo)z3;_JhTCbV+i&OkpHs zSO^$3pz>mrQ@bkupQ=0Id)PzdWHrw50iuz&9W)8wWtvP;knt>i!SqGS@$kS(IUeao zgR8OC8_IL4MdLsWe!j{sGFoKd+8bg>$n?@$K;d(pd4^1vNIli0PWW&OUR7r*c1ZHW zR#g|pXzIuMb}fS;*RiE1*4p;VxOY$4fJC9}=GNMVp3@jFZ=$RzY+B^#sllj9)@(hK zzMX@{kU8F^0X`%_-d+>U3^(nONH7g}$qJU0>1$k67v7Lcv?c+E6tH70!9 zBX+oh!qqnFJU@ff=8O*};#X8XxJbAqk>OM(!)e@q>a?MBYUmX zj}pXdXbNsE=!;&>8IQiVCYVfYP6M)vGSI+Bsuw1AvPPEsq=YV1QYk&Kt#VSxZKYI1 z&YDLC@=CC38}c!!52k5;5ifY;uIu8d9aEA`P^M@c$o#{T-71iiC~u;)YekVXise^h zk9>jW)TZO@k<9G5bnY{JCW1SJFI2!<)3bn@XwPe7XJl5hgfbu`dnJqF?TWginzka- z^H5C5BzD^2SS5lu3#<$IrPE5w;;nQ=yR>M=X6THmUA&DYg!yy6TLrDEQfYM zrP{b;ji=bWRJt|C6Jk1?2??WiurxN$16p}sJJNcyh3oMv+pyR~c>jKjp3}&_Ca_fxh zGzi#sjR2D{JgvHo@i!xDs_7l3V{MqeKEv5lx~DDQw}fQz*-b-~wpm8B#aS~lJxPI} z4pq&Bj8@3cYgEl^8^UaP1JPT0Zg%Erfwl<(l~TCaV6@O)9$RN&7v>1qG6iiarI91_ z?NHy`aZX!5h`}QYllVJb5_;M!_<1`>7KiHW*1^;HQo6To+TW?+C&tUC4tPoa&~+`^ zd>HLNdE1@%cK&IaQ>qztqnl?xbcHOG+lnhG*5ZPB?@QIi8aiT6gu?kp%aqqplfuPu zHaMy!1*J0Gx!ijdA5qV;As8#VDv@aE6{(zGE1Z4QS8Fad4J}7toF*P=r`<1^#FJ>u zgH0)Phs=FeF|VqW_5+0w*W{L0Q7!~WC*K^R85?{(qmY+xw>SgH zQ^zr9b@6^i7meCH_Q+<_n%s{RH=lIE7l|P;p6Vf($2c+GsF&?+#d&t zgWJ(_E}PSapDA%3tEpl_XkzFiYR znEfhdZFPI{x_*VI#i?O7$lXhbYBRm(d&;u(KBq8ul-og6lr9@KNb5+vYn?wHM7}6s zvC6A!dFM=EWPVaT7fI9d8Jh({iHYtGDttdQAG zdaF!srHWe4>Vx)W6pfL=;EG)aT*}DUdnEEOaeSazG}g*|8dI>KEP7X(0Bf2`zUmRS zXo(ecs5-n7M=OmQP>i`f_I2UNlH$s?Rv?Z-_JrS~p>+A%NHMU$4uJihAN(JWz` zdjMThl$2^mK3LQJhPt$ILy*?Ku9EloO_VrN3$5mSdZY%(ZLKDquZe^nX-=w`RO81; zxdJjY;?e3Ab4$fBYD9WkYoJTN7in3HHM)moPcqak9Q;#ey-6ck0JG@1wy%-CT5n~4 z>NsDLHn#r&1xVC1xnr{Co)kPOB^`{TyD_Bg@z_0ANyT@Ef)B}aJr*~7Sg}AA(s77( z7zf!WocPl~lc{K}YLXWhzu6gqnD^r_QkcOEuX6|ol_P#18c5>cA7yNDxDUywhOzz_ z4nE5h<+CBByLk#MyqNq(uvcUjM9ju%c{HMvZkZUmv~4a_v5?5xc_@f*-;i2fJM+p{ zgR0}tEj`Wdt$K9KaJG&D6HW~>koa!OcUx(Xo;cx+yn3nnYetd?TF}}@>v`??lVh@n zOxDMY8Udv0Gn{z7hf9~T*{0}0z!@kHny}*O*m&aIM5IIrHpist`jgl9MZmQ%8cp{lz*_O48XmWpAV#6P)lI zmHllMO5n^ujq+;v7#YxDnYjq}>J@e%&jcQ-*3hWw#_hL%la91ML zTl?qzSAU1wve?*~VMC8pFE)#qb7*eFuUaQcH9h%JyMxM*tsXhF^fBn4h_}WMadc4*2MU&4oznE}d+_$7MKna7E}ky39L18i@mpi?;JSvtpfXmwb2kq|T!lc*(&fRMkW7Asb4@tula>1|y#**{ zdkXJbLqymo<`>WX5C`oD!88|m(WemOfnbVW2bUl54rabqR(gvYY_Dk?Dn@(6Q#a&S zOxEG}my*>;*K}v?bHK8><0T%&y^T71E;-_BY*T_%JRE~~;(ai4>#T9vG#{`~3 zOQX>7vfOQlV{*bu+Zr&*w*Fy8CRDu~UA>BWqgZrq@Zuzs*aV6!Pl<1fJ5J;7oZ@NYJ2QL5@|tRx z41xi)1@1{Tjd2<6{>5V=CP$1~2hlFU#(eVZZ>n#Rhm$Kmbr~bwG3neFM)3LDP6;u; ziK5@2%q$4B>|YVh0M(_hY`OUy0vT%Ck&fK91`a}>4@zV$6a}p(sv1yn7il_kVgR@% z?;(w*@DVM?+6NpcGMPexPBbHsERs3QJ5s<2n1^zk>mkg&&VW2R#_ zXJm_2#D{U-0(ps&*`T)jB>I*k@@3+{w3#U}^;;|BNp3^WgeWZh5yN;+!1xHPr5Sn6 z$#))zILWK@Mp5<`hpKok4RU1snCGGjr|J%&V}hpA=aJw6cWA|YY@}PNWx>(4@OrAo zzZrBcY-g1ADV)eaH_BD3vCLtTxKoZqnjL=Nnk-TrQpbaIg3f%fxxt~y*G$gY9ZvuP zQ;kPThC{iv&ncxkp2Hrg6pS3H86Y5#S4!!8!hYbBGNN-^_ktG}6l6f=%-=Fwaglx-NTb847Ug(E z9uudLlZ;2LC3?1y{A}|iV!!4i^23r;g3D#RYHf;+-sX-9(kQQQ>;~itNNX28@0*rBcwk?Vm8Fx4) z4lRxn53(OuoMO441r(Axe19);r_SZRjOXY8!r8KXz1DLyh7q~K%`Ne9-3*(_Hn!9= z2ow{vr#%wYYzi!9Zg5E z?H)$S2HNyqMqB9T+H`G#=9$Y034`(98`?)=t9qNH#Lf?8)vFelnej0gB5e zB(DRoRb4_q44MhNOwLN5VT9?^7~^YlvH;LcgyT-vTQ8Dt)l+oo1i1l+-7?R^d?Uq< z>X$}bc;2Lr?2!AA=M~Xxk_=qFM%yVKyQDqPkqwWcu0^cmVoAv*wyb)8P{)$M$q2ui zi}7+|`Qm31kKnCoI60AL086%m-CX6*ZX_ebJg-J#d-pJMgVl7$W;4AmxcO6?N_q== zpp1P}A1KfvNjofq8R1qjbUciCOQUm<@ky5x!(P%rU9wo^(5{O{owScSS*+@;#+WsS zoAg@aW;l6VD$rTlcf?8J6KWrACuVQ(UV1|`O#v_U2^Uvr?#a&hh(7S;8PcqDF&3Xd zh|J6k$Spjm%i}9W<25Q$E82!RpGbQg4(5+!2SR2Ta$Sz|RB2ss+mDjhvDIWM{M|V& zCa3WjpwTxU3Z(w7O$zF9FM`md5WR0g5u2jg5 zos{^`6y+LD;!bL$UP(WBy_GkrvP`*UwrQe*e}kz;>|?lsKvbPtV$tmra>!I5JgB2 zJR=#MwapfQ58l0AVQbEwK05RxPC6`5$l04kK{lSk>jRWN8Fw3dqNm9sKbS%|q_-9n zfif{jXOJsjPq6;Y+Wz1whwV0lPxMc;U0x@!wWN0UUAS{fKN*!u#9D-Z4XZiBj#E6? zMrNJ}{XtOmf5Qx^c?vml}LZka2?`G2BsIH$Zb{)7wVK3!U{oBcaO= zVB*z-qBRm+AdvS-k_hO+)3nCjNsKaaBp<{F3ybQ7(Pcg8G?=Z;Uh0xW9*q*zR5lw)9!4G_BJK2f5- zb#k;_H0-^*UlIC+Vt0k8;+DFsk5$Y1Z&e}m^Tn%l`7%Win-uN`xC@4IaeO2sxXp6^ z0Fl0vr87@-j%z@*1MK9z-phOqP)~wO_{Zoo2GLww+mfE z4Vf@Hha@4Pi~V9l^GxG)Mpv7X;gj|iHkrZBRU@_hF`J0+-U?UH z*w4eA?2`CAF4dEi9lfElWaUWTtzu)RVLk0v8rK)l?4I?ziOYx%#HDk7<$=}cl{%tn z=zpNiv73EU64T@F9fGZCSs%P@8dk|Hx^g~PS~C()sx`UZ8bM=_<-XffUerW9x^xkp<8quVC<T5+ih`;?pvEaQ?7A-1}^_-MoDBzP|i*EE1wtRCvdLnY;7tmV`Ph4nc0I8dyhXfiz{1n z&N~f{L}H}SSLK#g2rr5>w0@@)4%#Zv7!7&EGCZC_RLprYbIIpztrnYh2KbiTT(5~1 z2MTHCEgM_i3yQ|$z#87_c$h&BcdM? zbsk%$&n*PdDPo&^i@2nz5zg@VpY2PPYfY?&{YvSqcaVeLmpSSf_T|a-UEUnNVTz|k zdQJgCJRV9aUJuC*{{U6&MCp%2WkyteY0*3uy1y09A(OCoZ$s>uV&%Rk4r~s{#%&ohxM62Os73>b z4@)(T8L~8%G#`?qX*#=@TTdX5ResVn$nsd&@(8oQa;r*6Em>E=#de>l0z9q1UKd>H z>0K@hcbhb-nwFT%hHh_XE+)@RWs5N0*c#yoBv+x^#z=lC*`<&5W^I@&Li@Gh+` z$T7;&*92r4F=fIef=0<3g@V$8%4lG51C`(>T9G9cA`ERlHWK*#RPejPlWWiBmVVU` zyK9k<%FM6`Jf@$=^%|TVAk#BEK+{~NJ{)bw+}gB?VX^H#p%iC+z&#YB@=WQJ+FL-m zZY9JNi&G?m2Xdat$t0jM;NI)>T>dT&c`xvBsO;ZBfJYwV-A|btvoZu@vYSbdaM~06 zO+AsyBp@$xc_SFnSjNq??G_mD#`cS(pHRbN9`6iNxV7L$N1~}~6Ya?$4hl?E%TG|y zE-U;1OVdmYrcYocWrBF?-4&%{cn`Ij+07}~)?No>w;=9vVUpk@B_(|RViE|?Om{SZ zs#?w`@An?+;MRiD;%Qd3X&C8EI@76fR#GaXvK=QO(Ks-k6N#n$(Q`Yh{4++=>1nnE z?U}A=rOnTquqSL=mSo8e58x&7W5W~3CzQ)mbD(>GX*R04Cnw@bA;Gq~Rn1k=n_k9Q zZH&-u6lRvjGFI;Fmg(#=q^ zn4-2G7Wj^P+DYuVC55&I*Wj&7h};fa#U@x1J0XCR*%_UJ%#cAVOiWXZ&8n-ESIe^| z*Fn%Rl0BdRN_R6EkAh8C2u%|J_?{#A9n`%?kQo7taJr)v`if(leTXz^#G0d<=t{6= z4*=T-byMlsoHaK*o-JDSX`A7+@y82I{H+}^zqH@7wX5U^Ck8>~DojaXl0V&%%9B^c z8MsBmyF#hOG? ztTPS6CpoPG=%Tu&XJTPfY;d#DIHqZzEQ6IVR^z)XDUdLF@~8J)@>ip;7FP^1kyX-T zpE=+HXshN9H^JhIJkrxh(xcPyLx}TNC>=vn{o#$hlhTto6jJ#}6IhwE$^ky8y*x%^ z4F}OD%;Xn~wFaM;AdP0itIFjzeD znrQQuNmfTGKsQBCk0FiVgc*yBz|9G=c?!8ZJUK{DwE2XvfKvTlDt7jc61|~Xv@cuxu>6+CKnrA(?pqX>1Z3B;^Mf|h}_j^!NrpW+awByqGr1fJ{j14>E(5tIbqab zX*Ig6736s^lAr8GQMN71#cou53n+~@2*fs?5l)5B8aXUV*In^KG2OXo?d3m_YB2S_z%1eq6>AJ|wl zZuU~ojboC@kD^jSZ8|ZJnYr9@t&J?pi*E!{uIJ;tyUEGnD^b^E{G2>xap-b2 zqlnKqqKU%lKN#{&CFc>jEgPHP+xLpOb$cZ4`P`>PlQeRf}bRnu*Ob39X-v)$j3V?vf}-DrOgJaWQnA(pG9rZ z^=FAhZg(A%?L!U>*u&%iq!rmLaRitM$J(^kXgp`FwbW&@qpR6%UxWnctCZaJ>>w)9vWEVz8> zV;Ku0y3{w0RIs$UzyZl9HfD5hvdultblH1P6vm(Cx>h{Sk03SOlpklBD=|7_4}A$G z9wWaV-=!y+SS>Hy2O)Uba;aoz23xxY)FN!LU$S2#TPz?B;W3mQg`VgPd}p#pUt*z| zCanstz@nT*F4u%>;@=+jaw$QZh$Q59S;WaW>=C?JT--S#u#|Ex6YQT%%J8xSAK5n4 zAh`TU`xRf!WjIM6)i~0#n?5;)L(OodRnZp~pBLIIdZP<>7T8KLbt$nXu;WV2s>An% zDvo3&vK#EJj(oJi88pibd0!b%C(Ny#IT>R&IdgqJV-vw?G_RU|sLz89ZEj5k!bK@c z+Zk*4!fbaq4o51drR6&pP-}-2Rn`ZLoNj$WsA|~58#jSKo)t4H+7&uyOOtMPoXH!> z+Nj+{gW@!TISSpi+K21I zHY~x-u*+NY&PbaD-t@bh{{Rb|LxM5wgJZ~I4%%pib?F^hv$NsmHhyY@Jliu|&vK2y zk@%SnmAnh(E?g@lX_H!!)=mIxgQMYx**MC4INDL#eAJWl)}YMXMzzixse4_;m_|!n zOFfsD8N6?diYiIo#n5E_`uzz_<9l2M9_a)T7qF4{g7jH#idu2(n>1x8**LVmEH_i1 z;Rz@1mF*?*J>DY-PY-t*uJSgx&(1C zSo7jCWVOf9SNg1Mh#zF|cO-!l9W2mmg0~5Y(@TeN6*EVE_{A$^LbT^J`i0pSYk*{u zDp{JXGDe=`ghxxb2Dce*S!nes{{Tt2MI$3Vs`+71(Ua0DKWU}$XAPu^Sm+&D z%znvT`KEtq_-1Uj?e$e%CmGmp^#xuoIXr$WvS~(+`_oxby@kNpAtve{`%~oX63&Lo zl132Y!kek4CwoV8!tq=jC&cIEx+@Z8HcY26gUxEgrNCJTbGZ7XnnnXzA)arqDUM#K zH;AF<(}mfQ@?2sUv@sbW*)5HUmS$5RjlAQ!KHwFDHS(JDv_?#mD~HiiZNd(K^jeG}JXXd%kkPT2;F=qe8FPr+ z-_awVG0&2jWpiVP6>L|wJi?SoFDZc|fPE63HKnZHM<|DxC9u^wIb?b+nv7^;o31k- z6vWf$sj^Gs&goA!j?BildC4c{gOV{2pt>O@mlRn|@!B+L^OZ;HcGhFh&0b+)CxA&* z-mZA}B5LfZOc0OZ$k~u@ZT9ZHs2vgLh^<}7D|d8KHwV9n2xX`z|e~>JEj&o)ynU*NjD_ zuH)1nRb?e1uFD=gxtbP8 z34T_eE;QnNM;1ulMK3$d`gAj^T9GN@wV}qdr-sdJ^+joTl12l)5E@>*%%?UDLTYpH9dYL3g!V{Z9ZcpG-cjF%IyoUipg_uMLePEeODhw?2k)G}Qj zoX|VctgyNAZavfE*F$HWB@)R2ZCFR+7P^uwXSgr|i)6)u?P;8yjC0=K4y_@WQ?Abn zJiOOBN)F@Xgr6jU0V-*J2CHFuj&X+x+fOv95oOJllKZ{3pXN>^X2aN}Fv2n9aRY+1 z-9@8G^oYwGAKa$8X)#LD;81U4m^k%vP&e zI725VaTn~grNz-jNodnb$8&K!VmYfp{?UBXxsSS5XU5nti!0Gma-(#4E^xXfB37DZ z{{T!ZAc|F;Zwa{hPWhBZm!4pp0!QJ2>c@~6&-J*EBlLFn>UL1|SY zjLtA{+Eimt5Hz?1WTC;pwM8yjjBS5ntq7w+mt(kNmkRVDN-xD3pdj$@4#7=#J8LnH#i-3l5z0CyVvqozF(jy={7VZzV1y47}$W7S}Vp6eE{ z2;af5iN%n#ggb?=FqXJlV2&7UaHRxGX`0UxA_=uu!QQW$ccDYDsLc~-X9KttuDvau_Cx?Tw9!;39rIQW zpULp@+SeZ|_*@=NJBb;s9d8RJJh-;VM&h8!z%jpY<%vEdhAA3s@U1vGab!obLM*Q{ zwM2W+3uI(%6h)w?e&^rK6|CS%9mxo`_u(pu<-(jOrTc1w)`vmuF;kDCTbGT#%T2F% zgfp93vUfJ`vS{BLE}N|YGa154+CpO%8*6ivkrv3|*-tZE)~QXjMo!4v;ls)^7cs8# z`lS=@cXFY`kVQ2(sm4ylY_erWB#e>W1p`_`nD10mavR|8?v_oI&lcP6x?>k0i#3j) zZd=9nwdG@h!gdSp7nM25X)`H=>*VS1nBi#ye5@)PTRCSHCfW**ItEy60+Q(72DHmMkaZ zux|vF2uimGOZ97_zYY#mbX-YRO)fV&I)9?`oB*O0Vwf<{;{6d>r+*}7>qU!7gf`qM zMUqSBcnfV6?szI@!1I#-04e2c{{T4K3V$R~oL?TO{`5+Jhb{5$w$wD41doxd)3v% zJZ;4_*kel7oad>t4pw~9INvq-aI12-QTe0u+goUcZkDibq5d5${RHI}CWC z!t1%OM5-}zv?KB+Fyl$I)rww(7K=o#YLb~UTz+T*c~Z>n(h^F6#Z+U&p)y@Vx+&jv z12OXuhqDoXHM@IvA!s=~t=r%ECJDWESzZgv7|0f!^v<5`OIgIlj9aCVt?lkoh7NnH z*V%5!HOTU${{XxDzfz-`Tw>Z1ZjssbIHkdXgA`LEXlMi2c1dwMiO!$Gp2NOs^BY;2 zir04pDyF&9%`*6~ig4$J;KL%ik;5s;X@r-FKzmeK6EZQ$qJ_bG;i#k^Brd-Mu8VY6 zeVN0*!Ia5&h&tLoryl~ z!AYRK^TJ-?NSv72lE*Z63z__7VQs&cvU_H1R?;_mq}q<3xvmvOM~ghVepqXdlGU8H zJgiR@g56i$QcL{N4JrJQO2@%;Ejxl^hI}wRk#B2WT+`^6W*1|N9hP>@c3ZSezc67u z74D_OGR7rf(%&f2M(b@nW?(*tAS!Q52SV8+b%Ga1na#*-k-O-skH8qY zG8T>;Q+WAmFB2mPQz_bu+}&XJt5z5sAC4>8BkBf-a|68zMq%VuFmNl=YT}t(iqb4v z9oP8yx$ZXdY3}ZJ3U1rJY_MjfU9;`Bxqcj%h%hrm2%)=lD+zap5qYKxy&zDuaco zd`w1xMQWIu45O|{M(8a)?hH1aAY zyGmbx88zt5`1 z+$R=0)6r8U)ZLMrv=VHnrIKet*qdaO)2H+&8Cxu(&!T8XIMbAYDI|uNSWcO(uz6No z7@LI`1I(qw!Yp8+K}Z_GAZv=`o7KLE?XVfllfRn{M8Ot3G46YTDSuJtGYx<;tyRBh zby%&UIAJw*AKWW6)(@3SfI-rTA#Us+RspSFA+)8~GhUq9e%F~uO zX&N@2&?K2MWw^R9X%&T~%a!+mr27v@ixLgeQ28M9wTN*6Ya9i8B#fnzd=cA^3!1rU z62pN86E{gkz;;~IuS|00mq%T_6^Byl0PyR0X!@#VwUr)NFOAn+61u!>5`_6lha!w4 z#wB8I$CMkqsLYRB?t^u&as=W;{&`T4rYbjA;;JzbTDeH!z)_wS2@&qMYSG=+%s!Tx zvl99#YswUD?xZJ<5}3*Lc;JF5 zIqARqY#{dB7fI?ql*HBX81T^^M$u9M{MQuxzq2}*M4nzQiUyIaJ~Gz_kV3v`I_z0d zwV4E2{w2$`EF8@WNpZ5G;p7&`J!{yk>DrlNg_>TDEYX^@?H7!cm7}d{8f0^~b1RWy z!6TkbyiyqeaQRI6-pW-79+#D62 zD45o7Z*@R+Hcpv8jfPLnzK80pnm)e{YeZ6h>mYY)y?%@4e6Oa<3vydUu{6Fced9UI zAO%s@@H|<`t8rG?Bx4(9lphYLT$p)BrYDgbiA8ToPm*^MTHj!c(Ry}GKRKnq zk?vPXjj_QP00bsMVRe8No=LSw9C5CNnt%^$7=xb^!U4c=QCV)?C_c%UfMj4;7DBXi zkhvYnp#)Dh0?u^7ksf(lJ)Rji=#y$W7be?Fkebpw*Y0(ahRbn#z#Z1R>#Me$zJ&?AW2p| zFGiOsfzpDXV07d#J8@`JYWdM=aCvA_<8!S*Si$=z(y*I}O|YfRYawxY0U^^hp_MQ< z!g;HH^8mfQ={!`Y8ul)pkb_d{2=R$%a7r>YS7f(zZs+Q&TDWxCu4yE4qQ-Q%@&HYR zd~o2l%ED{(%cA+9Y6b7`<#&D-`q3D&2ewBLtGzXjAYd*`H5{P-03azXV?rV(69HZtOe zk0$d28huclt#ULj>@TRFBv^Z!X#)quW9~nBTlF~GKAT6LIaMhy8ZrlDyy+L6-o=x| z=E=J1ll?a_&c@hz;VO)YnCtGR)7{~`gV_f+E{L@CAPrBF_PA2hry<*z-QJ}&)-gn( zvPM3uT_+HU+Wr*VXh~aT=30+W@}EV{HCg8xg#dQnqUe#ZIsn%j;VINK*ugH^FWp-T zq+PdED22vMP#xTasyN>cR_3R%v;bNV-wInHc?r}V=o|Jk!9FWS;mRLe#<)pmaEirq zTF+`)W#qKjaIm1QD*;c~HmMKe3@KuKgILH9Bw<)o1E^Q&O=h>1d@Kx_(trsY! zY^>T(DtG~DqEHx6xy}-HYB|ydymFx?oU}62wiw8qe8DBvCwnIi=9BR=TL?b1m1nWC zw|}Tizi_!|Taz4l7;-}}1(GzhgX#!hHFai~?Jro!$8h1UJ5RV@M@=AbEd;J#_O+uI zMHZtIZoH5Fi?iwZ)-1dqvGL^jQOMoFSSOXc!R_j!x-00;qaBLE$?UL+j4cn9)}&gv za8X7*ioyD%nQSr4Y;aWMtpZ5eH&rGW(v~@{2eC?+dEpxqxC@?Y$h34!9Y9Nn#}YRI zMHDmK+H`I=YM-NId0iWie#*n7W6M08-rt(?a$`_zgxwdt6I5nYehx=|+^lLEP-~2k z_CCoJ(94EAMl{&=PP7eaxh(LfJoZWZpjE15BL{ms(Lu?M=*a_+ta-Su+u}j$mwY^r zjm5*1s*ur_9Wh=b>Pq>hujW2~G^jAgkw@J;iL90&J!vlfE^Oz% zDQ~iWr0S9iYmW6K%Rf#xBSB-9k?0NNlPH zS?iI$XT}O^p}_ozA2g)doO}p@l49q4f009?ghYNu-BV4~pF% zqPJt@!<5blvX|;we*4-i6)0t=QbZF;)PnjVS4)iNNI411@>Q;6!#Pyj-SLUGdFRdfp((!j{z@!L&*i8n@3|}%aFi)GJgfgvN)@vT%Y72 znz1bNcS(AyRCJgE4cs__Q#rJ`+Hb>kDtA)ITasfuY!pK1@$&uIaiWlEqPddmatcBT zuvF8#lSpS;_VJ^pB=~LJAtSg^>vHUfGVGLIEPG+Pd@sh-L&lCc2wU zXK@}>CXXC2%nWt-C%KJ|Xb3&&c(QKph<>ihr0_>L-oeTqLFL-;w#uE@nFWAbZB9!v zBe7PV869XxNJX6lWKjSFGAH^M7bW7-@CWmuCoe0}40WK(r}i_^o5q8do=qUKqHNI+Vb1u(+zS z^OWHHcY1KCxEW?V>ES1Wcvm4Giw_>kv6H5lwGH5RT)s?OPK8oxgt;^PKB7T19KD{v z`1b`TP~bW5Y>c}Ic-%}I9d}ANWy%U((2dight<&%H#Bk(I#iB!KGSN|s^SNik}YRK z$HgaPtN{HNu?)#$sxQjpp*(ob$!i+GONpgpFNa6d#n62#HkqVhgSnIn6I|(U+6T(x zw05N#c@cV-L&GA*oSN7#OKT0ucm(pTJx7J{x;v;}meoH_=}fIUW|*8v2eRp!W+UX& z!!iYMxF;B?^TxB`6c^<8Vo;|?(?Zn(r%r)Z$lw*D&5hm9690d)#d zQ6G zqxfHZa=E;Ts%Z%AlX2sYcXUz=&0sW}ef*O{$8WOmYA<^mbx%@wF}bl4 zPswx1^+~cMCC=`R)HMj_ckd}H$S}Bm?!N`;;*SW@MQ5IJ*Pu+s%{#zruF+FqzZVIm z_vT0R&I!oN4kFk+)2^+6Lx#}S(6rxqsi+c-yJtAb?r8=`G>^)nN&)8AsREHAaPFda z*Oejf*+%7I%?Y&|l!ZoC0T~5lot6oumXtzDw;YA1b+F(dV`k7(!}o$5a#0X>w6X&% zx&f@XkIXL19M_2-{2_6D4d3fA{X*=R?FR}k_(Jgf_e|o>AlISxdDMM3 zHk^E1VqXt>^=j&q3UWoPvGQ1NA#-@XT(P=BLo12RFm(wrpWKcpt7eU_x)Q?I(H+v< zzMg(jd!PZ2?_Pyho+rVavSzuiY3x_g;PRtBp3eMv6pq=Z>bTm217snx{{S@}rKJaw z_Hgdu!E+5WR&zGfs+IFX>NdTtk09=Ezo=bKq>6Sj)3v$qUNQA1x$$-q4&(c)hMg== zV3!_2ByQvotCHj6=Eb@#uW3ry%-tey7YXFE_@h0K;JmLV>9Lma>66HtjTiiknjj%^ zGTO#+QfwW68#~J6GTbNT}jFg>=9Ws-u;%v>39`=^Z(${i3 zD_>e;KNmPi2b7go&Mqz7)z!wsX1IEQgCU{Dg{!6u##}(K+UC+V(mYV*UphImM+h=@ z`J~B-Eugu2W3iAihTl@5gg$Jb?slwdP#lRrbKm5c=}?&B0zt_6E>{&Nv1Xq{`4Wh6 zi1{l1ohg%2Jo=^@%-&J~q_0UKxjTtNJNVK!w2Pci-xr2`MJm>&112$^#HTn}BjNxj zg|4fnjET3mx{q<9@_f20FNnk2u}jWrieT3tRQ~`c9nH9&IUS0vWn%vTt<$~zP{*#w z&iX}}vk92w8wa1tKCA=CL@b|lR`?YE<(*WCDS#) zA@ZK)WEQxQ?4r=$;lIp_*4s^NjR;>R*>x2>P5T3#p~8tG1riJl%+d#X-UiS|~r;V{f;Db=*s>e_BbGn--UHTCpXZGH!Q zOG8Hm9f69*V}jr=lwodsfRj?Be2c2$KNB)oWNB-d;?fEOn;~^Swv8E>`p?F|&?BFh zSNNALdwJn~kB^h?V^TdBO3|fr-FWv|?e$phRmL!|R#r&k(PJVZ!@KZ~#($-toZ3>r z?XFA~FlnzWwZ`eIgciAS=!V1<#&N+*+z0u?A)!2^Wq;8T-6gKk|_lfNsE{t&vJmtgzmp@ zMUTB-NN>%3^|}|0{znVlTK4$lj4p_?#8+8m8yIRAj0t48hB9V z>8+FxRNim)3s}D5)w>d?5+)2h9koFX7NhX-7crLPcB}sYWhpFw?Ee4-*%xZrY*{6Z z+)W?VS#5KG^3tmHA5!N}8=Jq6UR3(e6Pi`5{z1N1P9&vGqqbQkB&~|wosjZQ;z?WM zWaapRwO)WzfNjd5tfMwrGC?M20&;$ss@_TxNtkG zrj7t2g7SQhGTj`~mA#jE8X2d&w!3;NleDSzEb>E1Xg&QEzZ0E3$%d5HhH%h71d?4O zEpllKG|rlW76)ViNcT+4xH$esHI^3Hjyqk+{M9wiCpq>?XhJE`OHp-0eOFn=k8@7J zONwimCnGXxO>Nit5D(^0XBz7fN_4A@_$1Xa4|I=zrQb>b*Q@ zymlMEWCz#amQ0Wp2^R^a9_?*!}RcVzSTBc%DAD3&`@ucb~y_PZ+v7Yl`MS zW}oeHPp;AY*F+puFALAe{^foSdCl^m?K>u1XOdmHSakf?Wr8k>s)NF9)5Q4w%H@pg zMy(_rpNYN1ydawsqC9q1Owc?r^hqE0ie3qv(v2`2#)()#9IB>n0p39z60TBrO0$5o zz)cq8pjf2BaP-GHox{1L&DkPR@wSbUwn=ej6vl zkogHsH4Iquami&H3aO}l_Wf0xK_tK|m8BHpVx?%H>QGIl=5A;pU!uA6p0wPk#2E&` z;RV($Z-nEten|&OANAm^j(nlBLA2=T`MNWAc(L+3g%`Cl25}0zCj_&L0*`Tx;G4D((QFpmoGqx(e93{ z`ygW{#A<(z~caPp=zNZdzA zYKnTmH+BgQxI3ce+u7Fgw~Ck}oae%2^(w6MWa2XbA?K+-wum-BWo`2Rtvws4+h09^f4la=- z1+8Z1_gI%&iW9MfZuUTB5+gK9L#O$*FE>2;t0*mvid7-DblEwM@-)%)Of-I-YvgRR zjmN*qF~)0lx5_^x^B;(XaJl7?@zK`zwoi!!SnLe|3R#~rC_7RS#M$JeI)k0#_dlBF zj1`i=NYiWfe#glaaoYrpKqi zI|7#VRLqwQjyO&=f<$JCA4OT!xxjU#!SZ~cW8-{b{YoZh-!zvzQ6VwS{{U!E6L((b zF`^E%aQQ9W+^rqmSaLnQTGWm>3Q_*eN9wRhoHuzb>o|8w&TGS#^6Bo^-r)teMrwm&9NNjXGgKcw(2x#be^5%rupG~znzQh=1DV> zYiSlNrVPy`&F@b-@1sXCU-J#0QH02X)M)+8UFIU4u&S-N;OFU=bP! z_xCCh_m>7tlD^3`MzhEwjJTD~daqe|d!uPJ@>gwSp9J!`p0)vy*!5kW3Q~5)lchk~ zOrYfg?&-#JW0}nbSM<;OP*y1b^6(dSS5C?LHiHe3v&-t9^n|U0Pz|swr0qYu{1o~b zKeXA;suaI z+hSz+&xvit4jDl>$;dFeGVaut$NvCnVxL?80Aj=wxS`gpVJT;TWRC8Z|s8+WJ;T`9YPy z>`rKpn%D4KxqH;FWauNWlJ}z6`YjKl#x-!@tTfT3v0Ah56j6g40Qv+UM+H7J3jhs3fBYXE-^2i;>YHKz z01we}`50C@YuPM|k3(z-NvjFG&JBRHcS33Z0CX>8e-iNgi9ST&uVgsc1WG|XCIO;W z-w6;W#qa8y;1lKLR}{HvgQ5(*(T)cRWUMlSY5sT~8Q>(W>{iQh=w%QB+9j?$qoKutOrn_IU-`VH5&pqe-_kZucd+&H}<5;76*|ln|nse4S=lo{X!vDliLZ@#k zt0_YS1O(6n@DIX|LN_21;u9n%h)GCJkdTs+kexhz@+3L=NjfU(Q>Pi}n3x#p7#LXC z&vLS`^0F~7aGmGkmeF40xd!*A_5+WkcNPWh5+9LaY7Km3GlSX5B!%u0zxA2yrg90Cn-RKveOVD0TB@) zG0}+=#KfSr5BMD-ra3{&Ev7(nM*ATtj|1H$znBkXyw{7r(%>+S0w7##XJJTf&s zGdnlGu(-60+S=aP-P=DnL?4ff03!OI!}{yU{%%|}U|fX6#6-lT$KxU(bOQ$w4e<$X zF%nt@ZPJGhXLv68k0h1%MHd? zq(4;*IT}^nA;3Tujj+Q7_JP!T$T2WvHIDk-gL%6dros3rC6n+O+!wS}1t0HVZ2QpA=6$()dF|0^hI?f9Oi(mzI!e z+P}UjMEDHq5}5ecjs8oXtiJ6kGn@H;!w2J{X(L5Q-1tSqQEOhBXm>nRZv{I@gb+$v zxS>)!lofSE@(K@yrvK9NxoD3rtZWUoG=ql-KH{Ni%Re-IPqA~C`q$3>*p?7W|G#?M z=X3xHpF#g0*6?4U=z8cKbh&;1zu}W@UZNsVVgBQT^awp@?r^I-xOfi7zByvKugB=LtcNbx>G{Y%f6!X{=vG{~hvm!g61Ew>6t()6 zH9CUiY3joh2(0rh@;zR;fPhZ%B-iVu~O5|7MGQ8kg(Bg+$}YBQX8_JO;zk)brIx+ zi_Df(s>K;HkKBwU_{yd|OXwbeQ?SQF#yoha=cR$fABEgaphTzjUv`){pwh9XFA%Ge zjJTJvRYeR3filu0NxY)~k!%q`O%TSFnVhxCDcre;dq~1-%|HUzQ)z{3oyjTSY9o5A z#%gOq`ZT3x=pM(rq=iK%z3hVa{{0YGe=BYgr;C2fxon1qoZ&(~j>-IbVRqux8nj-? zu4D_JLt~CAf;-3gQFDQ}*y^y=Uh6HwFEoUf`rQ9K*ih(oa|B}L@m5LHp4-f*MBjZYPoJo`Jz-WuI6sX& z=c|*MZNc0mq$g#s>3UuW&9Kx`BMHsaU+QkdnYCKkMdw`+s|+L(@arTum{8xbG41N- zsMv-V;Gs!+7fh(ei<~{_=V_`q@791&Mr{7}P9E}UD$cJ0zTa!!Rt79Uj`qMjTYh{d)363DUtl#zQ-l)p)2b4fvA9Kb|hn z(Gh5_0T0xDWdnx>0Pr&X4`+F=wf?;MpJM;T`u-m_xI5DMmOobM=^htfg*+3XoV|8D zw8#p)laCS}YVP^t>45OYnZ?0NV=7K?mKp|uJJCWV(_^%!0xYGNN&ULY983fcLcJae zgmB)B{9$7#!jA+V-^{!1v9~w!;tbmiH25i9s+!N@TDO0-#Rs3ya5Tv{9ztrvQ7+#P zkS&ZXRIm1uq&Y9SRQZ$B>RmsrTJK1|MMceABzEBEB|DeebT3g^$hOpq*ekMMgy)st z$VXJ_QQ(mW9(tI}3w)`;QdXeeZB6t({QG=PsH?Rwdgv&Elj$H4bHCEiFpn!{sqXw| ze@;g0Xv36r!Y^N*I970d6$(HRPJO<)>0Pq5yMp!_Oew-c+&+f5RxeI0@m*#- zG%yYyzWxO`J@cwZug#udEuGpcgb^z`i|+r{L3ohn(rGI}$A`s#7k^2wwd_hzp0r|g zh=$E5bi3l$P5wSuO;RXee( z2gfzC?6^qI9=_<}m6Z4ReEp&Mso7McnJ|Qzv~(b#pYyVC=Par)qj2d{^ECzfy7Vcp>Ls%XPt{B5#P4R<^GF-30w;2) z8}mAq=MD3|IIlgZ`CM%Nl#oQTo8WE@3=eUz2w+3q5NPJY#fLMLyYNjsH0$uKCaWB| ziJYeZzfwu>;tBu0> z3x(sM<#ojB2P4p~M9mvE^sp8W6)tpd!gHdqJx4h^ZNJ+Us!Km;Z<&Hst+0q+)pqL6 zmg>UE_bz;cHx5cvOsNfQ9!*<(su>aa?)tgrbNpf+ZBBFwdCS;ya#>2#!J^VN$=k6T z234h{pF{G*haAs4d>FeD+gUGN9ObPSbD(0>OC4B4kQ-sB=_9QcuevdB_~LP?6?cr* zH-!dA`3|!UY6Cqiw3|d}uYu@+mlYItFHISbSdhTZb8|NaK{TU&u=;PQ+#S0d z-e*uYU$@3Ow2-CYJ0*K&3+`KfRCX=(G`vj&53PJOMT^-ee=6JSmutPSxaJ)taq&j^ zs=bjzxaYmdH5HtP0UX(KoPRPqhGkW2v1aUnvREL3Sb*EfT1Ax^GZP5{3)UT9+vQZPy&@cVq#>f{@|q*^$5RG+}lCe zhAqWAU%Fn~Ve-;4b1Z$+vWeX1zO}M*3zlI^n+i@>wYj5FdZESf%;h&5Gka^;N-@h;N5& zzlbymcU=PyJz;@i8EjNqIk@_F-M$o82X*rH$*4UfR=!kU{tS7=DMeF@w6}Op5;h{T zbtCmaAIv)%4DS9+mL|Nn+VajbJ)hgx2I$5U6wstc@Ws@9Qk@n;h|5N zMMo*Qmi!BABUk!^VViGF%+CAn^`(ppX`V<*EjyC=8J&9YcDede>#N`&?ga}ahqyZz zgI4Aan_hl3-J&?K!9x$mLH=f*-iU`#Q)%#@30?ro69c|x&R{N2{b&w}T3s_W*WZ%* z;hwG_$@#FSJYOFVy|}W?Y_{*lwzqOu1lhG6o&NdIS6jgJU=xnvUpc=1WfSKX8Hy1b zX5fZL6*C58kG* z0rDf*khvi$>&V?(5f2rK055jq=9d}RA;rNquJM`*&cE#K%o&G0&Jfl?F1gmUjYbZ+ z$1PX$QpA8DC!{8#1F*#(*Bd#g23oFWaHNW2KRK>!17Fm>`_J5vT)rK=tjy>mkm-xw z4wx8bRGIX4+-)7X(c}&Wc6yE5B1H%oBi>h&IGSL#l|KM}rZ67xRci+Ddf)amTI7L}7Qnlxf87UsR9=?M-XX2b@7Oq`7z3Rv08XM?$%;+bvjS^{Gk0vtNHZNo%zEbg zO7_eqbL*vgSx2Quji(+QD8$0X_Y0kF9t3;kOdPoIey;ErxpAku_>GvwgHJi!uTlQ0 z>y!@Vur0=NJoGsL#O?1BqrRr1KuV6lFiF6$&>&Lpg${i1d+TUt8P-KbZ3@Y@Vi1?* zukk&4K|e}YQi=B~Kjnl8^7cFQkaWhfUd0}cuBx^w#VfQ%D!V3DI`&87Rla=*YJd1l z*OVL9aR{EI>&p&QZV`ZoRt@?Q1y84#DxM(mko_f{*Rj=Wc&PlP_4hr)o{Hz1Xdgefl6oKZEA?sbQL_5*2!^ftcVcsEVg}!9U4XC7Og!-u;(}oyHjqG zns#nu8+)I_4-avo1+b9`2sA;C3=KF-rGl@X%fmwg**nw+AP<18I)jLfq?W z31lD|riZd;hL7;jxs$9#2@ABT{)kz|%^}>^s#aX53WywM8o?wlzOWA_xwyM9P618{ zUuDgQulBpcaWdkX7}^aG!2SX>CqPh23Y2OG;@<2U1w6zRMFantkp*A8p7GBxNYIqA zYxg>wdUrVXUk9yOGnxmR2cdY##DEhQ6z9|e-(o~9V1t~auqUikvO4h4Q?@EFn7vJ) zw^`9V_|FZ1pYOxcVd&~2jG!J2eSTog6n7n6fsNdQp~-SDD3PN$E%`Z{;O?9F2Nz|Z z2@)BEb#zNq+uEpMyw8bL<<;mYJtJ%<4rw;#Bo6nd=8E?8S0E{ok)4s&u+j1#ek#E# zQ5{(L)S^nY$*uStRO4kFGWB|@q4*RT8yovml8Qf&!1wZ z>e);;nbUdX-TeBajNH<`IyTcMc@-|jN6W-cj20WZRndG5`+?%>xK!vUze^wK?UhFM zB57g7IRK;txiHk)I}7Z(=iOXKFCKQ)g5d#eqbpxf4BxtL4?HO@OPQYT(0Hjsc#B7k zEqIQ(oePaJY7LihDHzpvA72%S94;NzxaOs-KM=+of=sRt+L^r<-FVyAv=6RjGN&Fq9;nA%UmiU9)(qo9pz|U0#Vf)6;f^{FSKw%bMq-8}qEL z3l6vIf4wVUx%R4yZu}QD7kmgbkb8gCJc?;{%Nw(DJPg!DVi<9AZem5XKl9y2>zk$IaECG1{4N6ETMz8zT}J&!Fb!QCX^t zT$sq|1XBcXgv93n@GS4c-mKO#&e6_Q z?_%}!@lcz|D7XlT^S6eP9HXQ^fV&6%049m<{L5bz>lm@l(eW$j<&dK@AghK`AXeh5 zk4DlrH;w{iIPuVNjf&lTdKE7?b2fLvVwsD75^zzuTjaPW7{lnEVM z&+o%qpMwXqm&64tgS?un))d!dbL~80rXD^lnFLTUT?@#`1W+xHB$CD^~{~QH?$rFm0PRfqpF#%Cx`JVQbN~xq^?UyF5H)la2Y($%In=Qos`)PiPZ@e3 z5JV7Sk8%*UXD#z9#gQAov2fU?J?xDT1bS-?0O5k9DfU#q$+72B2|=vh0gj9NPSKG& z4ExFjOqb4MVx8FiC9YWnsNy=E=*tE42N;On+P#5oaB$MVRyuBgcD;q}U0|BYfN=Up z%&`gS{Hw^bG`(*QcycOZUpU6y8rVDI0Akgx81$p0or*Oc!oC(x#WGm$0rmY8rY!aV zUfAdb{6N=-$-maEqW?m0?9QZo(mOY!8#|=Cxn4GFG-ha3af$UsncqS*U(6*Evgaia z!)65qh*O>q=_x@Da`ff#8Wg&b^AvgaB|54*-q0GlQL@=LyuFGY<|VaxupRk6tYb6L z*ksw4rrcHzH*f@d$j8{_WW%R@SZAO}EBS!ID9(H=FadIEE4rEXq9AQlvr&=S*aVHc zR+5BzWHP$Mm_T?SZh1GJmuzN%iFt3jrml5bFJ%4vs=0?Pk!EJ6d%Z%vv+#4NrEMqV zM6FW`ksMo17Y2rADkAOX315=q_-^UuuA}9&6{#=w6}<`LHhyF6@Y*mIm;d9I;v6$y z>aO$CiY6pY$c@AJx7P>;5dH8D6FC!vTWV3Q90uHKZL&J%Y$-pxko4|%cH@_iIea%d zcK7qm{Y5{0*m{Li>wt}OdO2?I?q>{5h3MN{jUbU>HJ_Pd6SLDCdev3yoGZRjvFq$q z)dwQMfe2%pY5S1XGp1tdmaVt_C-nCf_QQiIt5Z_mw9edffrZrZlvNJ#M%vstf5G-c zcqym*O&te2&xkQI`X2{_Oo3TV*LmgjZuP!>mhOM^aa5s(rF$<^?DjN&Ta2rc=4 zs{;8uZI+9Dw~-i&!%r?*g&-W-+t^Z$=xVa3Q5cI}48DZcw%J2M%I zNZKPsK8pE~8I1E1d>J+u-|gmtL1vZd3_b|8E1N9lqL`uJ)1rG;d7HF?D9SptU<%_% z%`XFQXtqR^`iuw6p}p?tKD0&TaaWbpfkAe9nSya7F8A^$_MfkcXilOb<>sYQVB3}U zSnIl}Av`S^Qn*pdbT+>K!Uu^lNBV%c)9a%ZDHx9*@RL8fABL9MQwK$m+0!oz4r_l~ zv!kvM*iNtSR^l((T<}&M-6(rMtmOt3h4;oW@$GCJsviZjlvE8>EO2g%SR&8JsQ6f= ztMiqI4V4dFR%M`~YmwKITIz!JCNnk_(bN`{W*Xk=NN>-M=33%et7pR^lU6qLeJO)@ zKe{uDeHc$){*Dx!4Z12Y_Q3b<*%xB$m({*oI!(T;fC+^IwnpL#X z$SAUJXZd`jfnrgpQL{>2CH~ENbW#(F#<4!j8k*CZA#2?I#=v?;Z-=4W6b62N;@a+; zLt_R-W@#wSgaF)&b)vxb>{qy;F}(+Ag3VE(#VnL{jGr8~BFPO)tt z7D&s&^Ddj|AH+>dpG9#;8Y*A27?vsM_Sum7H2P(2lDPcX&CE zliq!0uC`G0!=W$Z(Lpa+{Lr+Ael%Uw4Wa9iCcV$1C`1t~wq?T}}8s411^2CqHP$aX)5p1d)!snb-9#*w|NR=bv1;7n{_hf;gsotKjlobh{9sOk=-t^ zJEc8&fL^|IB~s7o&^M*ui^5yVdY zs|zbyR&d%H6<74>SVM)qZBM>Y(($d^jP|ma1z4cYW|Np)!IUg2-5y1iT@Xk5?Bp1y zo#w*bBTnL5zU=X#rV;I#0il+(vR zW3O$t6{))Dn@+SvcjJ`Gt@7_yU5zWjFhNWsV#u#kQaO1KSJ;$ydlV1dQ?6}oLeR9H zD*QY`njCH-Zf^s`L_3% z0wb1a-t*SG{*cq&f9dF3lVbg&B%?Fu#*B@|wa&r|Nafz|+KNeUif5xqeZL%8>RK(i zV2z3LbkcG8o$PvGkomKyldU2-xX#!fo^jF%+O_fOl+nr-7)$wt6zb^^%L@Z%E9b$A zPhZQWug@xe3rsFvLKHz}iC+r5<@$HkQ&}k7ZAJQYUTJtqoqc-hdfxnkJF@TJVq0!Y`XH^5rPZh;kwz2arS9|6>(J@-ONQQ~VqH`YKiu~U zF{_)C0EdXT016@3v9j40uiqe|dwj{mVven7-}haLaLU~?!@Mpe6&3{WjOA_9=IrH> z+@x{(MEw)_3cL=2!oj3`(eHgFl58g}o!{h7RrD8zSqknAj>gD6`NHeFel225FS~|D z4vXTohpyknPWMkbgEn1V7PC8=KUc-Qn@IHI7+t@vbQt*0O{pl%qO{XBQ%Rf#>@nTo_+np5)O$^oa09;%#jdbK;d zy`jn-dNYQW@MByA$+5PzUS+6oq~DjO~hH|>*YfuB$*=yW(cnBiDZ@GU`)l}1jl zq9JqQ8pqC(3`2s*-dhRb^x`s#L0LF zt4qC2%F$n2iUHALFZC0u);H9oJd!CAtjW_$H5&V8aB?>%i!`ev2YB3t zqzNmut@Y0C9H!n6r@O_RIl;ssx51{D;giB<)*i(wgQ$|**au)SAH;u(g^&9+FJXEd z^CZR*eoeL%6Z|>YtfSzFh9P9HW$4Zqv-hQ+N9`34Bf5Ds#$3DT%5lWO8j&MR62fVT z;pdm3*Qw9{F}R<6@JWxwv$Q`L{^b7IO3hU)^!* zsG{AX!%w;ossQsED^uyt!0JBj2l|TNTYJy1eR)=uz+A>>-3m5E5;$W?mErPPU1@Xp z@C?jCUezwuE3#~vpDC2D=|w!1ulD|LobqA+_N0?u0weeX|6%KkD<`349bz$5%+_{bePp;AEN75U|f^FDALvp`EVFuf94g z$ErEVWjGe)c5-A~)V*~++LnxzU1~lNN2z9PXQ=%JDe}!d{CQ|pDVLF`W_8!iojHF# zG6p`a=ebkzBh*^5q=Zo=4G@Q}JNcYn=`+MQ+Mze0JoVY0)~ZLCy9AHx7h~lGZ#~gz zvDdeJJ~Te@^qIJ(P3mvfdrQY~S?~OaVo`Pj^O^bBXJ5b0WU=XG`J~X2ZKZ$JJRgxB zhui2&c`Z(J^;r)uDS@F7)#NpzWhrs&UvxaVsc3NRzKU&*@udUzoAK2#au1#19@UW! zDd)Nms95RCDqiYToy%-;&Q?=w5A&&RpUEC?_H&nIA?bKQTE;dr|EBV87SGbS0%p4??d1%^&Bu?RT^c|;H@POUpixsb~EHo1Wijl z@VU8owz;yKlkDvlFL54~+&=v>30Y`+n9TZLrfoNO$Z17YvPUy>obJ0Y6HW2oRm?a6 z1>7;Lwu!jC$X}TEP)b++x*@|)y~wNwowNr+vX5*+FGvW-*}6<8&R7e4vwa41?CoUo zo;!q|NBo4^lS38a1wOBLsXt=40$layC7#f3F_e#!f{8F+mXXgG3US@_gVy7Q`p zIZ6a7CmrhdCVh30C5_=8Vlx&A`zqr=D-wpw%Lt%D#!GACJ_Hi z?WK?E`oN2pf{Xo^0OoD=Oj=%lgD5R5PZFC-txQF&%Gr*~;UQV8UUx!@#X_#*dFVmA z*!%E|+%1B)$6y;Ev4m5+-xbH)?WCb}lXqPIK%;c4-xf-IX16u#8XHE0wH7>LMmXEl*5|_Z91i$(bP;n>`KPi~@ z{P++%=014VkVk-NxIsT<0@#RXm0$%`})^p+6Io=pQgsJ)VS+-h!T{&@sJ@%%?+YJ38~GrF?H|CG7L?g zUJqE^-3z=smI(6FW_#z<#=lXY9eH{&h+*5eVhz6_LVz)=bDTTU>W2D0DGqgI_Yj^ z1Zytft4WPOay`?j2PhN_8&(Ae(vAeglHV#==Mes?1&LZfso=g?ns!>ZzC_Gyw3_a5 z^uc29!dc<1sm+||xesuOJdO)Mq6l{QPp?}zH61|jd^@3D)V2MVqX39)AGrrJgf9|) z1e`>4Z3Xr_8qHm|Tg}l95 zwOTcOLhHSBxQ9id{O)u~oW2s^IPzGOUw_;&9q5wUECOU3_c7U)1DKLBgUv|4yrw!L ztJ03Pz^uKl5t+98w@l2vYwIH=U3ZHtiu=w#+Vh_vrkSQjFyAj(W$r7Yk-8d>_|Nc+PAVRyX=W{y#=<`j{v< zIN7Ixn`@qAYviG&;E=cTbe*nF6qcH{5ETXH^7iA!&8XB}JOq7@lJDi^DonO^cv@~M z{;-5_Cww6{>SnK^evdYtkYK&mMQDev^d4o!m(OZX0jJF!_#e6O`R^7o=UJz2ovH@< z6$LU&gcAQj-B6x>mo=gEbG&82Tvd!p`ziggRd>h&avFO_mH9;)&OA2ZZqYbYZeS#u zXQT3z;g0d^P_lb&B7)(`xw#(XvCqW1SoIu`RGO+jiVVH?_|=rowQIaFFHKCUe7 zT&+}5Eh*N1h8E$TcKTa~|CO1zuTobVf*Y&iKTIM|TP3(gS$O2vHZO|uh(D7?w4aH4 zqz=&)Yw5l6cQ1fZea%^x6aF&fq9NqNeXsevSw&_1IrdQxH}96!4<1^V96j3Do)ipV zY-U&Y4wr^BYcwtF&>&ZxwQ)z_ltvK;)VLtwhT~OHTv1d2qB62U@k+TitGWDSmB{LZ zPmchp)!VSG#xnOHKf5foO%zhFD^P-DstZJovU%0yn~0V4t0+@iTvggAywx9YvG&+g z+SAkrn}dKRRtKq4ceDK9${yVz;QwNsd*S)AZd7t9* zof{e~m9cu7=Z2!Dy#?qbE#~NVvjV0DzpkW81$Y*PI1T?f_v9!(-5#*OnwWC{>E53- zFL|1_VTzp{bFb@^ibjjVOg$qChSu6K?TFd5QuqqBMtW)y!a#czy?R`M`iu-du3$}d zVOU+jYQCOHP+mU*P+f$z2M}reNAzmIvj zA-fLNRAb-~jve&Em@0PdM&$`qiw!3gZ~r7pNbVyl+4lV1wh&H%)t@a=3$C)o216+% zy`pCa_$o0s*p-)AzXZR1&KE+N_7Pe4}#Ddx{D*c!5`M)J$AbpY8ySN6b zL{Gmc>EOCF4>tf00v|oH{N=k1@AWx)&0ntlwV(GCukXTEcfca8efT~W(GvB`_vaV1 zb@5P1AI@tWtRV%Zc^25OC(u8?r6u8?cl=YTzZ%QmSr3YAV&4yiJFf~ z2eIwZacuiggZJ>P;Lag@Eg-=(6*s|xJn}gRz_kjC&X$4j3=JPvwZ}rbiU07w??fVp z9)op5*Quj}*_$6BlDn$nYrFOm<6n{aFlEanJ>TA8_A%baPH-675{*QhZ zqpb*>nhgeCRK>V-!iT<{{>iJ2b$O>C8w@eL^1Yhx$ta`&DzFTI@%tHAN z)y!X|pvmaQfCblUKfbuQaRgI+aXQavP+C4XG8sfIH5oRh#-+u(_4dVw^zjlV((v%^ zvOe?@?!k`i&9KQwD7o~kBN&Qutv}Q&$8y_ft zGnQ;lxWNHfvw;BZR2EuuE2Z~tblULbFHR1zP`&&FQ1W(P4#t$qkkMqD;) z2DQNp0D|k`J7OmE#|a9tyBnxQs(`dYV!aN;f-$ZeIHF_A-7KO8(}A}Uv3JfIhNdZ8 z6=*ol2Bw?6)zQnLhB%osxb|w0ih&AkYfuHqMT`?b+VIazzp%LqSCt;K1{QFe9Gmq& zNxeFqZq^R2%fPd&?yWg^$-T`Wo4!Nv)K(x?ev#6(!k4&uPy)_OF)Y_^kz@RG%_pAo99RU)-CT;w|jz7`brF3St#A{dXBmA@r#LzxGc8#XfKYDP-0lq3~6`rPiH zgvIC62|LQ^G0DQN^}lSWx}@nPe4nqt&9&n5;TMMmK>{P9?r_(Fs_DFt`Ka^og2*ao zDN}JoK|`2eq1i`Znb3dMAyf`B8?Fun8f4CB@;nJc;7p^RAh8Z`EO9q+_rF<(4$akQ zOp!@YQ0idF^eso~(u?g}=t?O0+L#zr1lv}b6^u5BiCQH{k9b)cmj(%TWjWZ@Z@rOA z70G;!QlNb*_GaRYG{c>Im2XQ?34JLIVOP4T?sPTqx%=WF)qZ>0d3Ggpul76C7gSR2 zo&9*Ltsa{g3@bwAa=sKdaEjBAu&ao8QDbk`h5? zDUo4UaEKyJjVnYcD3`#rMYj?PLx*!C>LD#N@yrYCD~-++hOWKdG6rO!_9v~W)D${M zu7bdEAHn#q0NVA;6nse8;ZfaLtji69LFB5ScSs2yI=PgA3D{WYlpLCf@Eja}>Js}& zk!;g8DV%HSxeFyrmMHXSCeGmKZ6iqvu6`Vb2ZeYm5d16_lA zF;-a>sq8hvK73-$GRu`#*DsK0K9jx|;Spz^C0r!>l_O5@s&d!&ENomByelvZduY0u zjuF0%hf*mtFJ#_SZRBIyGD;Rq<=8ibQUEK$Q>=1k4 zZ6Pa6cD^{v{+ngNw8|f!(!vCx|Lh}oBz4yFkdWBf@}_a?I3* zAztrV$$DuDPRGaQ4teQ))8`i!)`DjK_y#+m>WMCh7}nZ3V{*!BvtIu*N!84UXmMb)TDf=x$x2_=_$+ab&VM)C;j>ym;Z& z)Z`O+y@o`84o@*NKa+hBFZ_*Z=Q%JSvXO&M5s)fW5p#g;*o9-4OZo6n!Q1u_y?1)M zRkDmz<>?ljjT@BdV@>&0&XE5&b-eILTt8rKQ?n5px#hXpAGA&Yo|1_${)@l`E6K6t zu(>cL*qS=$A^If_osPNFit`0*XkG3c{aHFZRCXS|uq=vMj{3dDZ`-+lKl!ye*aiOA zi?jcB$K(Cue>;Qy-rw;a;6Ho+Q!@Y2Hj_INneiCy(_8ReDf|{h**`h>zu_>xdUI`V zjEKWX4}#YWeLFJMZT8@z(iLsD*M?`9mcll?K{4mAMY6z6L!6p@Fk)p4;Hj-)!)@^l zyOk~TGhuOeeY>_*Y?SEkkxczpy|Uwv9LxPbV*hjg->H<$-(erTT*Lu)5%nYW{n9>W zLft0YSY#$1J!0r(dgVI3X38?}6XtfIR)}TW^u&dTJfC3sD6P_=(opXkp8H7|>MyEz z7-LvWRJq?VLYp^Hy{~=d*Ra-zF7%@MI1=3ha?}l7y}11CB{+qQ8A<%s4Te+~4_k9H zfs5BU*FUVDzDy8RMMuzE?k(|g32b5wbjh@2h}O62v(h$Fv$gZ%-B!#`(@2kKb{82a z4_~0`yFFpOvi_GGIWnPH^+|W3nUUS2u*>T!x^?W9Lna)Xw_jzLzUdsuSK<>Z*OLyN zob>Fk+Qr$P4PH$!qkTVNc6gi3ZNJ&d2Z2ilK5+vHoO|Bet*uwzzI=HJSW%E3_hlfl z7~n2@0Qre_t_A@-9P1hTuYNoxMKN&fHNb5l5x81Tz<_6|BQX1b>iUgp{b!y2dEGx2 z_MdY3zgRADzfvN7a$o}9PJr)kfXdO`GS=}CpI&VIqB%da*ydI7e@p@wUpNx^Xnfzu zpxu_IRko+rB5Ydq*Bdq zeD-A?E&H2T^(*?bvxyeLHIkX`-kR7XwyF6M4)vEgc8`VL2O4jcT-1IQNJ=C?^vaM< zi{y{*xVrxzv%TqmzR2R=za8oyx2OMAmIH~uFr$`)mcPI1;BW6(g6QrV>u7)Oefp$! zKvX0@f4cRp?Dsr2t5~Q*404n|vbn~|aNy{};bXvg+KSdn3zi@dAINt5`NNXg=pRl zo*QssMY39sxK{1yB#Yj)OquV!62|=4OVZshBKccoJJT4l!M%;Bw`g_Eg+i)8bnQb` z>D!xXrxw}SeV8?geXRZ6Efh3pb{_YLH}{Oq>L(ZSk3t1@_loZaI3}hur!cZ#Kdb);v?O zgnf93Q03rWG9AI&y;@D2pA{bR<2@3@m}G%hRRM~uJ_bjhr39J#6oExA?+xISs(?U~ z8v;~nw+L*L4<2aMBhQ>_(+>hJhSURESVL6VGJJ+y`I^>P@c%S+HWd$(qfHkhL)tYh5`8=~Qlb8kImWmQY3Bba_0!+YP z05c6d1aPn8;No1z#l^wJ!^6FSe+wV~=1qJGViLkz)D$!{)D%=ybd2oGbo8tYR8%ay zEUX-yce(GVJo|6pCi2KS41<0d`<_(Ih!;2IV-_OGb=kMH!r`SyrQzIx~8_SzP+RKTUU2a@Au)6(XsJ~$tlF*((=mc+WN-k*8aia z(ecS|)altDxv&82|0dRdm+XpMq@Y~au3yK#j`v3{tZQ!Ig-v=L=e8g&nZh%?R}SQN z?)%-Kco3CQ)^?Ls=sA+|wc`*z6`SxP`~Dx%{w3LePB8!fl4So~u>V&s1VD(51sV^V z6p#bXazeP$ul;AP+Ha`b1*OlAnt6vp1FHk6Rg>3gN(LJ742LWEgqC;9SgoqVpwNY0 z`yW&%WpVfUTBU{@B80Gk<8%`1eX;k#Erix&Hi^fGsY#{pVM09jaNa*__xFfWelM+% zjq>2RE0R=IrP`NO?pt2r9wY0T-YWCfUE0BzU>?@3H`XY)WGi`Y)I2IY^^0$BoTu;5 zoxRb2i@ochuyV1NJCh-NIlRSSfzBsqom3DF$kin#NZd{ zzFdu0$HHu>#ad|HSI8fS2uvSE$cfkz_N`N(kxzEJn|W5}eVh6BT`Ts5)V)nRONER? zhHWIZW;c1WwL;$Sgkar#o|z#*yV+BM0knsmFL_SVDw--3tVPfP4o)tT(T)qh<^v+{ z6-W{~$TPK-Z0p(i<;tL|yzNcR-tnkOy_1Ku(h5)ad#aR=I-e6XDUH+5K6IO6aR=Oy z_g%v;6}>L^CA#1H04PoM%6;Bu7WCx!LYbkSr)yviA6t{DbXd0Q;e;LZyUEyvzEdH} zva`l@Wgh0UCP1XCpInx>B)-BXcCHwLz1@KDmEHW(OgGeg8&$wapw-;8Zb8=t|orxhIHo-GyRCqcd?}fyKPJ0%&16|-1qwZrtOYgn;Mqv=7J%^xppd+ zmzv)~Og|b?yM6tg;`OrSpV%*oFO)L}7kjFXM{P%x;%-aMJ(+vqNutDF7ST<)f4LV@ zRjjHYX&=_;n+6Vcjy_FIb_>-^k=6Wktz#E&4|TOGFIo$_!vRDgc47;m*qN1@EK*>`xZ;S z3Qs45MjOmp9by2>t_)TVK_OnxG?agOw**t{T4G5PeCY;mVg3y5-rBJ6K5r*so$Al@ zH25dWd10)R-@#sBScH!+tt&R@=dsj!>e6b(zsdg{A1P_MsTaF%{I04njQP31;yi^- zW~UIUTHoy_>!&1ZzLysV!muvb`RM|2r$xTe_Stc82b{x_K1qq&hc$a?>?t)F-de>bK5S_Xnh5=?5$Okh@KR5CfqoODDhmKFBwTcH%2I-muIno?lhY=rLS$)IO zKHG`Q)V9(T+as33j&TXuJlM`jqH9iE#LIfLYnw1`294;-&UQ-{y>Qcy@C_sxd!S_o40U+#mq?B0QyV zrEidYi-{3cmuuz2Jno`z%iZy!=BK_|ee*2C*T9!k<1<`Zn!Us?qhFHF)6U`SXAcH? zu|yeeyVZM#O}rE6O8&s}PAuolq{t6OX2P80kLLlL8-<`^rCpXAN^e=4H$LM}ae|uX z$YhBn1}GB!glSYr+RD1SO7K2aQa75*J(onqoxec&ElDw|cj^VdXBS=DlB(T%QjJ5H z4t$NprC#4X!)|Tu!TWjiOc9{?|LCG*j-p0LV*qYBIlDn2tJxZfE{|oI0u`=nA=Y#@ zmKG@%b=&VLER)YX4xcOM>MG-RFs770DqZ8|RiP03?b~haQKbmT$7z?%HjTRe(B1NekzG(Fy3H{5Y8E% zj8to6D@wKcPRx8hZ|sp80_0yNnbt2hf3vM$cx}2Wu!nbU;Dj1cipmL8r1MT4HTf-5Ev}#eAO0;6o|-kmBu)=fW@L5x2PT@pA4SY2Z)}9ewkCQ19~j zo!n>ETl};0b6t|>BSG=Gfvsg#*nnxTk?)G@@mk3K?4YhuR1?oeiFUe zOWS45tX2lQ?EYHWYG8#o zJytezIO~fqcW>^?K1VzDifYe*h`CA?{qW>v>F2TuCi{x?lZZ+Ov#uQW+d>oA3>L*h z@|4Ev#l`mL)Zef57O?eE^<}7!i=KM(mPwfA^^}o3$KA@t#$>zJcCKlZ04U*qr?x#L6XtTEEsS#-z0ht@Q-jgX2*S1|MJ>Acz z_3o(y$%IGwviGg(OW%vsy&7($hPlAJGICJw2+>Ms@ql%@bFuogC|7VePL)Dlhvy2yUel zE?}j-GKx2#4rp1)M8%-nB<<1R%_S$k_*`H9zLuH=AJoX=-Fs|ylRr0ym1f0i5*uzu zO#7JIyY0Gb$#^XW_{e;~rHlby*$QBQr&LE+KfqdpHX8#Bj<;ZdfbBiBjPk|pMB|G~ zC|n6ay@ronwHAXMe5qC*h`{Q!w6lk9{|ges2A12JiK544JT>=u`5bA# z92|QqJqhgc`IXD4Xa#%P(ILw-H1T3cgoIShY@mm+s#xK^vJstzbsC+>bg@_m>87OY z)4I|p!rLNvIBv+jU)NHbKH%1;-Q3Wgqeaf8WWJ1;(QRiX3Z<6Oji_Ub*|oYBe|7Wl zD~@dl7HR!|>H4J)9&#)vccGMIe%=^*Fgt5~a@fy#vbweF8m1e5J)JNb#-Nj~BZeG7 zL%IXyevw;afG^sDv5Bu8-eJo8Z@^o6dHQ&j}J)BN>iWJ&aYuve09Cc4TIY04#nbNU@0o(4nNC72m{ zrpdOXtm1ClrvQIEypSXOH8eX0=oQ~LzVx@l07t4CSpPL!=twLHEzA?!c=)6gLM4Cc z{NByCzqXwi&|qhq9?33FyUdAQn(ZOuZrq&&la9bMI2F9iMU(M}U(5#h%sh5oFA+lc zl6QL4_%=$EbZ6O|?KTl@(1TC(j{U z-fM>OXrmIo*iii@w`soPDLNcwVtM`;?@OfM0R|8>R@o%PFQ-yxc_lj&@VP8;KnZRk+(tFSKcEml2>L8`y8hZ?Ujjm1hK;tx{Wpeqccy? z%f4VR9VK8q_G_54Zsh3vV{M#!}SbEoVGau0+=g~JgF~CZ_?DTTP86nCZ16+^600)LvL-Mrqw)@?e zxL#mM?hnBLr~DCD7S$65>vg>MUJe59_bN+w)QKX>5&E<4Et7{AbI>0R4kyimE9(;L zZf~4&Pv#lmKP@}pD7G{V5WRiB1Fa%khXG7dA;(HI7xuo0tE53VBE=8@R5%XnX@ zjqEa9@$to>TPyCXH3gpt zf561Qr{3qWu=?_``(T5#n+_#&@sQBgMs9f?KBY=ieS|Q+Ui##uKm`L#;(S|qUB+nplaR&|7Fc~p_+9-H8{f;le#k2X1?gns9+ou zV+3#FuP4U$pX_4*ESS?bAuBgE`z0bWHoB6mY_p+k$Ct)WX;uu039iX^1AZ}~sS#IK zmkUZe;5UL#Lcz*zBBsF#vS?X`Yvs{nBS!zFf}V``zoEh4`r%bYzfc zZG@2HFS3U<$Mwc}s_NSl7vVL3wze*ZjJBc$kD)(K0G_~CJZg+f*zi8sA9;!SFja<& zjP!S(d^=mY%z;o}oBDitnYGKZ!a+f3iIqx@90=g-^fuP`d1NT2RxxZM`MN2rTtC5% znM9D+m%PfWis@Ywfj-=LLcNhGDEO-TDPMi?-0NP2fBRmSXm#Fmn&TXN0xG%vJuJ3p z2wQSAq_<=({KId^<<{a@Nq=cws@zf9lUlzp9sbb&^?Y zOL*_bNYuMUnJhxT9}eD{JQDn@d`tHztD0 zAx1i~H*X40yq%m;qa6E5|LjR#;DZdDfdh%Dy$2J<>fem_4E+()6G+z!buixF8Gi$h z)%#$ldnXCZs~dkk*$zdOmgHo(ytXSA>8ZSwTvx9JtN0IUb5m>XOT4S^U^us8Z#;fI z&_R2xsa}jssU*YK73iYF#Vf0p0m)^Q^_OnKH0;z`fJ+U7-{Q8#xV*aciahf1ZazXm9 z%+~|6LkwE_SHb*AZf)tsj_PG?O(vQhtl!THmEaD|_x0JOrYTk@`3B82gvEbjA{}N! zcZ7el-6<|wvXK0=@#D$j3$l*L2Bhk%2-fR`zv=a+;Cj>Oo8ZZHCAj{BbWOh?QOTCn zeGe5_YF=euK4;tyH+8Zp@BK>M6GgGme64%-EpVq{Fwo;s{nG$gzm@a3{`8dHj_{J; ziMT$N>GTk!Ybn$BW%!sjx}`L2M(wEO_!6F=b47tAj^`bzcbQ3rA18Iv-LAFezKmAL zRH1yKW3vz>o=7l_3B~goG*HqULRjgqZ0ld4_HPO4WqY6cxQIIL zW-4nCK?*0DbiG#9Gm=pK{wYv~=l?zQu}t~nB1jSMg#vQ0<3vawZiOm1``MP=&m+`p zd&{YM+d+;+3o(u2buEFgX!Yt&4<190&fkAr(?M6OA4~n^rGE)iw-+L+eNuDgQ&3&+ zRcz{RCLr>to-gmgqmb(kol3x49TxE(3n>y0Rh)`by*02GeOH94oJu$ZTW2%rW;OzoS7~qWd|M6nys}Ee} zhU6Kjucq0*@~&>L9YXE{`c89bIYC+0^o){fW%!6~9n0zmwiQ6umzan3(a432Y2F6z zQ1Q};2s81ZQ;|yfC-S@l{C=TpH(%>1G$tp1?PuGAZ)d{O=hDEkDCMsY`RjqfcxnX- z-r=#9k!~H2a7`*PR%S}GH8QD~SAdOhT5(FyinAO*i*qh~5BIPqZS>qa z)ycjU8SBo?h{ooW_d>14zf%u10hHEkY%5HTlHuaLM|7*s^QYWCw?Px}Cx9K^-gZG2 z9?!lh8e$pR@{7O|?1PyK$09&PNmUv=giQ43FK-~h~Ds{f)G8qVq!lZs+GR#!oV*?YQV-K;$2dvnakq4rc_H73$jPTJgI%ExwU#WfGj z0u$Xf-dBps|CVc2pPijuiG*0^_w;0^*-g-dotd05d$dmYP;cuP;Uafq%?qcii*sqe zM6Wq6J|XAfjmmG6z6)5MH|6sjoMHfwS8FwOZHd1SyP!N2X_VSmk#UkA64*J)QEgIv zf$kR#lM{UVvPa*Zg@q`TxOULvsNhhIdU0$ZVV`%KbVi@(8oQ7_BHvz+pN{Rr=TKAn z7rzHZHeH^MkDQ@X=6}Tcv`icoH7k?4lv(-+mz4R?7OSW{yXbt z9O!{+tpLV5=JGkw+$Sl(!ZvT7RK)}9T8)@{{)YY}`U?NKk$cwmv=2ewec0T>`8 zZygn|AoZRtX!+Y5O&l#nX4ly#!lEn_e*(uFJBBXz-K-X)hQ(rIA*2~tpU(&*?|b)9 z+*3~7^q6Q@W9QdY8EwRS9S>O@s2)K)VmIEc9u^~3$BjBpduN};|LH8=cUo0fdaObS zK?C9%Fb>!CdCH$CXlK0MKurEja}=A7tB#YNceKRN)Rf?bvW>sfw%?WwBTtv$4D62a ztu3mciSO=TpjIn~s?v^?3NIGyc)tTQTdf)=ExD)Z#`%Glg~)(&Q5mTnG!}X=+*pFI zaa-=L85;FuL9AIwd+$OW+3+lJ*!H15QfqV^zW?iR7SW)9UgTWeko3^Jjq-C63ldh{ z&(d$pvvG5rBJfPwE<4$p{AgjgbX|VLOi8FLHwiB944lLL=9Km*&hy|>X!EQY;VgY`}&XGIrLjOtQqsZ*Htbu}?EV!>9({e_5lzV9*zg0PpJ#AX(D7f=Wzr!86 z&b>@SlG18g4Yf4hr|gO;-EhTz;%-~TlU6}raX&Atv5o;~j9U^)-eCwO@44Q5r}ksl zNg`pQX%@BUh&to)lNP~;ehU(pzqlVMlzt22%bw+$@t_Y&jm zX(LeT>BzC{4=HpK(tRELqv7WAXzkftPUu}uX*)(wG1YyJdi1@I55|sc`H99OV28Rk zjt1^3G~Bz|{&J0wZn^fkPkhekmSU{U(&I}T3#-Jvc5hsF&Y#D|*ETC2H!!J6eSEAN zFVV)Wz$#N{dDWEp&BXiBs5ISiTb42_Ti$o-_Ks$=CwViqbJWaSKP(xN{Yhv`%6+9f zzNN|{ozH&zuLOKcWsa8e)`M_>HoPM&N^L{?-VS$7t9Q56jS_6k+Pjf*m&8VYu$3(4 z3oRz_=#GkLO*^3jPbamB*f9+}+C0?q;##gbUuyPJ#LYQ96+t#?y1>S$+HL>r_agDQ zrO1$Z)ikx5QE42il(%hbRghofcWIn?eHT(CqYTj*!s927eYQ5gow!b4DQWTg6v;EM zm#VB5Uzj>MA$O$Ja6^ZGyo$D{)Q`pY6EjP=!Qj>OAf{(2#+qYt*fuM)U)cbqmamII z*hr@N@$c~APQxr~E2C~Rz0X?oAWn_7W2U|k34qYOkiCs`_IUY_rb2c4)eX&F!=&ov zY0B&(RGITUo<8(Je{Nkvh-h`W?mhFUiMMK#3s}+m3^<$&I&z--9FJF!M{o75Tn}u{ z9F0p)O3F?|aH`RpHv%uZPMh#l3S}*sYA!$sg+s;WyCEOS8g0=u5#fd9b|Yl_n-66w z%lAdLQQIlEyM(zo_5N6RW!J#7{7TGOhDvMZ!EC?ON($@H=_C+7A3XUP9T(M(MWIi8 zaP#_VyiA3mEq{Q7@zvy50$q#;1JV*X-R@eG%8R_9zGp2O)kjhPiF?1k-M8@C)b(2m zxJ|Nk#_vF^OH~>)rHQ`4<*iQu{EBg3%T^)d)XZ!SY<#ZUz7&tyDVE7dgKf-NxLJnR zl`59F%8@?xdMkX>xjTruB6^D_Cjk;;`X%y~CHC(W2wp(92i4Xz26)0Wr^IH&i83a- zoITQ8_m4i=Q86&!o6UhkxSF1BSK<=nk8UdfT=V2WXQT&`%Sirg{;W=4V_&C(QH4Iwh0(Ju-oY?d?550W z(MLh!hDP#3Q*7Ez$QM}dVo<>K$PK+c|D#DW+damV)gtcxP_sZ*PR0$nTg>Xg#_FWj zQ7`ALO)G^E?vF9R+|6X{y%0lFJrtxEbghBx1+@o{BbDiA2d(cuLT~Us#jAP)3;s5; zX>_Z6!ep3fOJX@lmwX5RBYuyDV<(yVU>9|=Q;6~V=KJb+)&x$BF*elpbP|}}6mkUu0bJN^e)XdhEhj+ASG>Vh+u+tg2 z&G(Hv*vIL$4L#cew9F^%6<14>b!fUScDtl3szuaE2VX6Pk*i3WR#j*1(KK3r!ZH7_ z(y9;*d&01@C75axUc96Rf3nXM|4Q^jb}uwmj4man8G`S){)9v_$1|wBZkBe0d*vUh!>JRj(Fc>$Q~+D@u0} zL_g2*bdgF4>!$tZlw(Cw`^k0)C5M=a@$9;N%MP9XozZdC|(Oj4cf~%nmfKfn8YPJThUf z#IIxqlCiEmlx0;?wA7$U)A>MSNn48@|K7UAuVmYdCVKez{8~_4zQ8sJtylXT7}e@=tZqAJx(Ym`TpRRg7|hw{(1g%Hppyd-20u_qVIFp z;Dk}~Wd7OL>ZbCT{;JwA^AAX+)mM>k-5J34t|M`^q{Z4n=1U)KO+ckQD>*b;~CGpt9IH~u*vdEOzy*?8!2$em`&EFoSLB~8qqR7cP1NotE zZ-qWjhBrE$IvE^~N_Mu89>ag>@5qkOsRbvUG;r3=o^SuKo#yp)7;s8naqgCcn}R(y z9tc5@fgEeE7LlQDTuRE&`dcr~(YW#LU&T4rYy=RN^6FIG^yno#qV>nRFZ^3=#qmlY zBDn$`mo{q7sPw*^F!(t{K(=qKW$Ge3`1`>{e^Q12)14Gg9n%xJKft}yAI^ez{vuX+ z@(*mT&=0!k^$Pfbme&070sITc{KVo}7?LD77r7hd#Q3XB)zi~m zIrGj}TTU@f7SWrGt*opPH!N#MIPVvc(K{=DzROkeO)H7vn!GseUciya5AR@*CcF19 zUQ7tA70j7mft(oOzlb$LkXTEQr)Z?jg{3K1PlTbCpL-yu4CvvAGrPQz_RwO)=Kf_)$!nuZiY9$J}Ez>+7|6@6z2QL(V{+uLS zZsQf$h}-}fq?PZo9Uw19^Dhn#g!WJGhtFwWV7H`z<8z;a7f-67i(yhv>*~t)ufH`m zn3=IYOZJ!Y42+tJqzY$JiD@AW6(bJ?YE!%0d{~C%D(rqGL7JOFK=(rnk0yI@7r3|IOIru`Bfnm)}k^%=t! z-xhPQ}gM?oPm~3thg8uXA>cX0~4Q1@+CMYnN@2sw0236T#iqGh18NEWD40iFQ z&(a&Zb%2U4&i603s*vCpSf6v`@5U+r!^FgOsjFUDJxF_hAZdwD`Q=ZJA>PV_;>zhV zkA62e8}ZI+FRgqbwyz+IRK+yS9;9HinXMKTkhL{FE~zm5DQSE_ZLYyY4#SIpo z*nGaO<_5^w)vtj zZ@0gg#P;*cfABLEg#L=uwqtyN7p*rqG`{6_upO^6PChk_Xd~>s7PY!}-Y%{IWcV5| z?L@-5?F~w=Zu!~2>nv7n-tqZYkPudqy9S@MQNI*3`lJ4(J~^5&&$idzZXJ89Hp4e` zg>^r%Ui$B``!z@f&2|8#Yz#<+O`j`H895OA%(LfLwyh|E=f|&dZj_x}G?VAG%B<*9 zk+w}bypSRv8ui8X@*#Q+LDe^*B6q#MMBDYnoy#iVH17@*%OZ%+X z-m1?l46^(_`g#xGls*3bPphj{|0aXHanbsI!TIOBT$n$ZGOa{+?W?>LQUTjw`IyN3P96cW&~tL<$BxztZV)sJo8hrx&3zO=~s^?cyUWFLw{+2mx9Y8^qfz7{&9p(;ueu&sp3)m zo!vDF%KbZ^S*Mt8aS=2SOow}v;%!Wvh@Fk+7ZxXr)n^U$fL*k3)FZNc0qf?^e>gHO{b$oB6QZr%m;0M$4+>{73s{CtrIh#(nL0m44UUH;L&r%vx}0{il%QPFi#G zjG_Eq6vA|S=t51jVKL!o!?|zT_V$lx17qV#Ap@ZY`=ZaK90T_3dRUk~>rH%oR2Sc^ zxg9_JU>vDOpn4-RTGL@xs9Fi@^({inz~A3={z1xk{qR{(^Tz*%qaR&8Ih>5i-(0+I zA!>X%k+>JUT1}@m6(?S)o9U{~en1&Ejl*VnJs%`ii&p4kcnyp2dh+8@C~7aUMt=+34G7t9speKYNkD`@vv`hj&gHQI?qN&muxTOY6u$0GF^QIN6eB zd35~SKdVj)QYTgZx=t(7y5-=-1&9)x7=i_o^8AFBfxx?^u~(>=CB+GWH}?EbV{&4n z2ZnYcIa9oSwX!2)k`HTL7#w0||ARiEq+p|G;r`b)ybMbCl5OShG@?dbs3l>=%wX1R zH^sBtmQe_5tQW)(HeaAb6HS(+euf7FQMa4h;3$ZY(t_2-nR!lFj{fG^8zZ%p`d=Y3 zKc;m?*kW{`cvoLmzF-$kJ0jlaM3YZ|b(}?5>s*1PY^-@ELZmtg2%?HzdsSOz>B{>9 z0^*n=;2CsNE%y?;5dzYJ#~1(u&}Lp;QU2B>yjOVw9E=3Xjg4)@s;r20A6qKbsNT}H zvt)lV1c)K^8a694+UdIQfj?e3J}gJKRkN3ciquojooZu9o1~r>>Spf~?~X~m{$cPb zY{SO(wLRhX^KOocMMHIsXu&GF?e_TSSSH^}ylY{J-ifb9=&K(8Bw6xt`E$ z8&a#5S5)IV$jZ>?Pt01~fbf@TNl-F*O7$0RiR&-NlH`yv3QLoGd%~_MPN^CgPiY2SFgzt%oRn$8`}xfx(ZQA zhT$jQzf0NjbcXM5d3bk_pp^H5SVMMc84gLzl*G3j{9=?CuQH61nf$bn4-!1Sf9$rFi2=H}B`$0;W)#@O=-}_ogA}>;TY{=^wV&Tp zz$I2-%rglUz?+uhi$@KIw;Uu2_QxArPC78N;wWNJW zB~KHpqS!N=o_(uwBbuY1{baG>VmI8p3I<+u$=WY6LOt5ITP3}yH6~#XcE{SQgZ1hpY}9qOml0^k@=A$+rIslMRQ7#mgI_hiV< zq#H+w&~;MjIX-fM?W~O!+lAH)^4wOyzWuCP zI;s}$4OU8Kw8(Laax3BwH{(OuJZkss0q=wlNn^s80vph8_b+lLL8Jwbqx7;Um0@%O z^npofb`Op&!Qbe?hFnIg@GvQI`e}OD z+VF0a?IJ$q4U5p*yi=|wkAXbSkob1QqdCU?@RXdnwoHA26OlffcvrD#sz!NJ0*ALM z{9T;xgt`#|L9HcQ#xxt`1`nkX7A%mjXSbx`j&)g7?m) z#9MJ1Gh`Ow)9r3UMRjPTzC&tmFo`3v$iRLScYTD!iv(heO9j zCj=4JPVVE`*pjD3-!x8()Fl)m(^ucSDHhksIcVgv@D$rAc9%DdQ7 z5jVaf5u^}wll3<12)Dwe`=*iUqPOh$`66zB*_mrhUT+?H;kBS_Qia?gve*D`ekGI7DVgEJ~!4x7M6=E9^#rl zkk{#LosL4}jE$)52X|cJ^LUdDNkj5=PwC8VVVAdQeYIoly6#X)NCT*UZ(PCO+;g$V znV-_vorhUItE&Jfww_&|BW;3#fVP8LGc_{r7@Rq{27UgJqij)X_Q7KfMS4S+?7{Q* zlH74M;!N#HV;7y363idQQ+=9>W=>syf8C_>&Tp!(Wii-X5R!-+k6TeV-%x9QK|EOuzoPlv;r6mI z9Uf#FI0+E||3zR9PC}4@eBZyBrv$#Z_mQ_U0zg64Muqpt9(r2l=<5$MgSOo%fAm2s z=n)&kEq6SMAP=p#VWv2xU7nC7ox>7mXk2Wf((C_W_G>)if-_P1;uQ7g#P3(e0HNBf zTTD1Hx^#`H6wUvQomn{Vkli0@9IV(KWORBk3^_yJEgfYlgN_{51=$u=&WF&sjJhJs z1H&_Mep!3Ja$9d&L@+cBnbd0GcRDUGi4g})bfhrNx1WJE!JR&6`V-FK>+71syy$MJ zg7WmkkOnG?*;?^e(expc72{s?rk+mj=8H{F=MGj#rT1Yf$84XN8b0f`&7DQV*Uzvg&cJlYN)ACD=VULWQEsR@ zSex5(v~VlgEgybiw+QpUA7^+TvJf^UQ4+EECaMFsE=w`jYTQ({`JQm1%-5Rglu)bH zckWUz?eIG|qsA-vI8ok~_D=Se3)`Pr>NP65sRQiB%z+iWYtw{S$Hlf{-%{y^e=S#z z2!}Zi49nFKwFK@hE1q$;0y&k&Y|Y#7_~jK6J;ldet-)g0lHMh4T@uQ=zZ%Gjn0ELlLS%?H}&sC8r@ z##b!oavat^Dn2kMf6}?H9Zyw^TvrMdo~71oiBKV7&`X(m|DgH#+6%)XTEA(3*X8pL zk?sqH@ewVQbq=D@V|(+NRg^J7+{k*&j^@raLAI#EJ3DQzGqWm=?j<`VUcflj3Ev_? zPI=91HkUF7pIpqScEai$Gk& z%T{MWZ_+dCej-sbBPZ#B3TRg?5-qC7EfUc-3_5~6KOf);HHA<8Hf5|(krckzLzNy} z`0N)Jy8DFr)SNQTI=1|e-v`+E`{Z0l--ea<=^#lyIM2#G-DjJqNi^?oBn4Yu8J~s~ zSi%dlcA%Fv+TpQf_A`UhtAY6p6lE<61IYS--#%z}0&kZouwobccx0rO1i~mGA$aBG zan@`JA9}^;KQ}NO{a(Nd#WLs)E_&lUNDHl|*7J$U(KEK%X45_q`(#@#{RNI1f zCH}&cTvjb<6Ts#A&HLFu*ZGBB0pPS5I3 z!b~-){eQh)68B>D_$YS1pugBc2k|jp6Dyr-Q9qCXCowk9`0Dn%l9s~gmHpcXUfQpY zyw<84*;ziQHJiy?m!eJLYEoucS?kDvm@bkK>rEl7>!0{=jwi0|j;p@WSBfAEqYxmw zZ}#dAjwALxmSc$>K9N$~DQm~gI3N1aoukc)HN9_(Jfj~DC%E{Ka8m6m0ZJJ3MVTj` zy7KrQ?@B9-Us?dpW*eTMUBfa0egg;`pSuljyMQlFnJ&KD7X@t*Kx zW1+RE1kUo4l=S2kvxR`(lIq%XIa(_!(K5X$>%_~ZO4Vxd4CFxi`+%QVxIli(qV%xb z`e0`o>Zy&~#p@(DG6j%m@73HVx%6`$ykv$OYB?BDHX9muqgh6ai$%EJBt03~8CRW^ j^;A1xs|js;)RX;{?MbBIA^wgvA=j4>;Q!AHGxdJ~%o9_y literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/54_Ranging_Master/54_Ranging_Master.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/54_Ranging_Master/54_Ranging_Master.ino new file mode 100644 index 0000000..fbc09ed --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/54_Ranging_Master/54_Ranging_Master.ino @@ -0,0 +1,212 @@ +/***************************************************************************************************** + Programs for Arduino - Copyright of the author Stuart Robinson - 16/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include +#include +#include "Settings.h" + +SX128XLT LT; + + +#ifdef ENABLEOLED +#include //https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //1.3" OLED often sold as 1.3" SSD1306 +#endif + + + +uint16_t rangeing_errors, rangeings_valid, rangeing_results; +uint16_t IrqStatus; +uint32_t endwaitmS, startrangingmS, range_result_sum, range_result_average; +float distance, distance_sum, distance_average; +bool ranging_error; +int32_t range_result; + + +void loop() +{ + uint8_t index; + distance_sum = 0; + range_result_sum = 0; + rangeing_results = 0; //count of valid results in each loop + + for (index = 1; index <= rangeingcount; index++) + { + + startrangingmS = millis(); + + LT.transmitRanging(RangingAddress, TXtimeoutmS, RangingTXPower, WAIT_TX); + + IrqStatus = LT.readIrqStatus(); + + if (IrqStatus & IRQ_RANGING_MASTER_RESULT_VALID) + { + rangeing_results++; + rangeings_valid++; + digitalWrite(LED1, HIGH); + Serial.print(F("Valid")); + range_result = LT.getRangingResultRegValue(RANGING_RESULT_RAW); + Serial.print(F(",Register,")); + Serial.print(range_result); + + if (range_result > 800000) + { + range_result = 0; + } + range_result_sum = range_result_sum + range_result; + + distance = LT.getRangingDistance(RANGING_RESULT_RAW, range_result, distance_adjustment); + distance_sum = distance_sum + distance; + + Serial.print(F(",Distance,")); + Serial.print(distance, 1); + digitalWrite(LED1, LOW); + } + else + { + rangeing_errors++; + distance = 0; + range_result = 0; + Serial.print(F("NotValid")); + Serial.print(F(",Irq,")); + Serial.print(IrqStatus, HEX); + } + delay(packet_delaymS); + + if (index == rangeingcount) + { + range_result_average = (range_result_sum / rangeing_results); + + if (rangeing_results == 0) + { + distance_average = 0; + } + else + { + distance_average = (distance_sum / rangeing_results); + } + + Serial.print(F(",TotalValid,")); + Serial.print(rangeings_valid); + Serial.print(F(",TotalErrors,")); + Serial.print(rangeing_errors); + Serial.print(F(",AverageRAWResult,")); + Serial.print(range_result_average); + Serial.print(F(",AverageDistance,")); + Serial.print(distance_average, 1); + +#ifdef ENABLEDISPLAY + display_screen1(); +#endif + + delay(2000); + + } + Serial.println(); + } + +} + +#ifdef ENABLEDISPLAY +void display_screen1() +{ + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Distance ")); + disp.print(distance_average, 1); + disp.print(F("m")); + disp.setCursor(0, 2); + disp.print(F("OK,")); + disp.print(rangeings_valid); + disp.print(F(",Err,")); + disp.print(rangeing_errors); +} +#endif + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(4, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("54_Ranging_Master Starting")); + + SPI.begin(); + + led_Flash(2, 125); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + Serial.println(F("Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast flash indicates device error + } + } + + LT.setupRanging(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, RangingAddress, RANGING_MASTER); + + //LT.setRangingCalibration(Calibration); //override automatic lookup of calibration value from library table + +#ifdef ENABLEDISPLAY + Serial.println("Display Enabled"); + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + disp.setCursor(0, 0); + disp.print(F("Ranging RAW Ready")); + disp.setCursor(0, 1); + disp.print(F("Power ")); + disp.print(RangingTXPower); + disp.print(F("dBm")); + disp.setCursor(0, 2); + disp.print(F("Cal ")); + disp.print(Calibration); + disp.setCursor(0, 3); + disp.print(F("Adjust ")); + disp.print(distance_adjustment, 4); +#endif + + + Serial.print(F("Address ")); + Serial.println(RangingAddress); + Serial.print(F("CalibrationValue ")); + Serial.println(LT.getSetCalibrationValue()); + Serial.println(F("Ranging master RAW ready")); + + delay(2000); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/54_Ranging_Master/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/54_Ranging_Master/Settings.h new file mode 100644 index 0000000..d625341 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/54_Ranging_Master/Settings.h @@ -0,0 +1,47 @@ +/***************************************************************************************************** + Programs for Arduino - Copyright of the author Stuart Robinson - 16/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions in hz +const int32_t Offset = 0; //offset frequency in hz for calibration purposes +const uint8_t Bandwidth = LORA_BW_0800; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint16_t Calibration = 11350; //Manual Ranging calibrarion value + +const uint8_t RangingTXPower = 10; //Transmit power used +const uint32_t RangingAddress = 16; //must match address in recever + +const uint16_t waittimemS = 10000; //wait this long in mS for packet before assuming timeout +const uint16_t TXtimeoutmS = 5000; //ranging TX timeout in mS +const uint16_t packet_delaymS = 0; //forced extra delay in mS between ranging requests +const uint16_t rangeingcount = 5; //number of times ranging is cqarried out for each distance measurment +float distance_adjustment = 1.0000; //adjustment factor to calculated distance + + +#define ENABLEOLED //enable this define to use display +#define ENABLEDISPLAY //enable this define to use display + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/55_Ranging_Slave/55_Ranging_Slave.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/55_Ranging_Slave/55_Ranging_Slave.ino new file mode 100644 index 0000000..1c4cc14 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/55_Ranging_Slave/55_Ranging_Slave.ino @@ -0,0 +1,147 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/******************************************************************************************************* + Program Operation - + + Serial monitor baud rate is set at 9600 +*******************************************************************************************************/ + +#define programversion "V1.0" + +#include +#include +#include "Settings.h" + +SX128XLT LT; + +uint32_t endwaitmS; +uint16_t IrqStatus; +uint32_t response_sent; + + +void loop() +{ + LT.receiveRanging(RangingAddress, 0, TXpower, NO_WAIT); + + endwaitmS = millis() + rangingRXTimeoutmS; + + while (!digitalRead(DIO1) && (millis() <= endwaitmS)); //wait for Ranging valid or timeout + + if (millis() >= endwaitmS) + { + Serial.println("Error - Ranging Receive Timeout!!"); + led_Flash(2, 100); //single flash to indicate timeout + } + else + { + IrqStatus = LT.readIrqStatus(); + digitalWrite(LED1, HIGH); + + if (IrqStatus & IRQ_RANGING_SLAVE_RESPONSE_DONE) + { + response_sent++; + Serial.print(response_sent); + Serial.print(" Response sent"); + } + else + { + Serial.print("Slave error,"); + Serial.print(",Irq,"); + Serial.print(IrqStatus, HEX); + LT.printIrqStatus(); + } + digitalWrite(LED1, LOW); + Serial.println(); + } + +} + + +void led_Flash(unsigned int flashes, unsigned int delaymS) +{ + //flash LED to show board is alive + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + Serial.begin(9600); //setup Serial console ouput + Serial.println(); + Serial.println(__FILE__); + Serial.print(F("Compiled ")); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(programversion)); + Serial.println(F("Stuart Robinson")); + Serial.println(); + + Serial.println("55_Ranging_Slave Starting"); + + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + Serial.println(F("Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device for ranging using the information + //defined in the Settings.h file. + //The 'Setup LoRa device for Ranging' list below can be replaced with a single function call, note that + //the calibration value will be loaded automatically from the table in the library; + //LT.setupRanging(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, RangingAddress, RangingRole); + + LT.setupRanging(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, RangingAddress, RANGING_SLAVE); + + //*************************************************************************************************** + //Setup LoRa device for Ranging Slave + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setPacketType(PACKET_TYPE_RANGING); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate); + LT.setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 0, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0); + LT.setRfFrequency(Frequency, Offset); + LT.setTxParams(TXpower, RADIO_RAMP_02_US); + LT.setRangingMasterAddress(RangingAddress); + LT.setRangingSlaveAddress(RangingAddress); + LT.setRangingCalibration(LT.lookupCalibrationValue(SpreadingFactor, Bandwidth)); + LT.setRangingRole(RANGING_SLAVE); + LT.writeRegister(REG_RANGING_FILTER_WINDOW_SIZE, 8); //set up window size for ranging averaging + LT.setHighSensitivity(); + //*************************************************************************************************** + + LT.setRangingCalibration(11300); //override automatic lookup of calibration value from library table + + Serial.print(F("Calibration,")); + Serial.println(LT.getSetCalibrationValue()); //reads the calibratuion value currently set + delay(2000); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/55_Ranging_Slave/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/55_Ranging_Slave/Settings.h new file mode 100644 index 0000000..1f516c2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/55_Ranging_Slave/Settings.h @@ -0,0 +1,38 @@ +/***************************************************************************************************** + Programs for Arduino - Copyright of the author Stuart Robinson - 16/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions in hz +const int32_t Offset = 0; //offset frequency in hz for calibration purposes +const uint8_t Bandwidth = LORA_BW_0800; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint16_t Calibration = 11350; //Manual Ranging calibrarion value + +const uint8_t TXpower = 10; //Transmit power used +const uint32_t RangingAddress = 16; //must match address in recever + +const uint16_t rangingRXTimeoutmS = 0xFFFF; //ranging RX timeout in mS + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/56_Ranging_Calibration_Checker/56_Ranging_Calibration_Checker.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/56_Ranging_Calibration_Checker/56_Ranging_Calibration_Checker.ino new file mode 100644 index 0000000..0d4b9d4 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/56_Ranging_Calibration_Checker/56_Ranging_Calibration_Checker.ino @@ -0,0 +1,204 @@ +/***************************************************************************************************** + Programs for Arduino - Copyright of the author Stuart Robinson - 17/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +#define programversion "V1.1" +#define Serial_Monitor_Baud 9600 + +#include +#include +SX128XLT LT; +#include "Settings.h" + +uint8_t distance_zero_count; +uint16_t calvalue, calibrationstart, calibrationend; +uint16_t rangeing_error_count, rangeing_valid_count; +uint16_t IrqStatus; +uint32_t endwaitmS, startrangingmS, range_result; +float distance; + + +void loop() +{ + uint16_t index; + distance_zero_count = 0; + + for (index = calibrationstart; index <= calibrationend; index = index + 10) + { + digitalWrite(LED1, HIGH); + LT.setRangingCalibration(index); + Serial.print(F("TransmitRanging,Calibration,")); + Serial.print(index); + LT.transmitRanging(RangingAddress, TXtimeoutmS, TXpower, NO_WAIT); + digitalWrite(LED1, LOW); + + endwaitmS = millis() + waittimemS; + startrangingmS = millis(); + + while (!(digitalRead(DIO1)) && (millis() < endwaitmS)); //wait for Ranging valid or timeout + + delay(10); + + IrqStatus = LT.readIrqStatus(); + Serial.print(F(",IRQ,")); + Serial.print(IrqStatus, HEX); + + if (IrqStatus & IRQ_RANGING_MASTER_RESULT_TIMEOUT) + { + rangeing_error_count++; + Serial.print(F(", RangingTimeout! ")); + } + + if (millis() > endwaitmS) + { + Serial.print(F(",ProgramTimeout")); + } + + if (IrqStatus & IRQ_RANGING_MASTER_RESULT_VALID) + { + rangeing_valid_count++; + digitalWrite(LED1, HIGH); + Serial.print(F(",Valid")); + range_result = LT.getRangingResultRegValue(RANGING_RESULT_RAW); + Serial.print(F(",RAW,")); + Serial.print(range_result, HEX); + + distance = LT.getRangingDistance(RANGING_RESULT_RAW, range_result, 1); + + Serial.print(F(",Distance,")); + Serial.print(distance, 1); + Serial.print(F("m")); + Serial.print(F(",Time,")); + Serial.print(millis() - startrangingmS); + Serial.print("mS"); + digitalWrite(LED1, LOW); + } + + Serial.print(F(",OKCount,")); + Serial.print(rangeing_valid_count); + Serial.print(F(",ErrorCount,")); + Serial.print(rangeing_error_count); + + if (distance == 0) + { + Serial.print(F(", Distance is Zero!")); + distance_zero_count++; + } + + if (distance_zero_count >= 3) + { + delay(5000); + break; + } + + Serial.println(); + delay(packet_delaymS); + + } + + Serial.println(); + Serial.println(); + Serial.println(); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + uint16_t Remainder; + Serial.println(); + Serial.println(); + Serial.begin(Serial_Monitor_Baud); //setup Serial console ouput + Serial.println(); + Serial.println(__FILE__); + Serial.print(F("Compiled ")); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(programversion)); + Serial.println(F("Stuart Robinson")); + Serial.println(); + + Serial.println(F("56_Ranging_Calibration_Checker Starting")); + + pinMode(LED1, OUTPUT); + + led_Flash(2, 125); + + Serial.println(F("Checking device")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + //The function call list below shows the complete setup for the LoRa device for ranging using the information + //defined in the Settings.h file. + //The 'Setup LoRa device for Ranging' list below can be replaced with a single function call, note that + //the calibration value will be loaded automatically from the table in the library; + + //LT.setupRanging(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, RangingAddress, RangingRole); + + //*************************************************************************************************** + //Setup LoRa device for Ranging Master + //*************************************************************************************************** + LT.setMode(MODE_STDBY_RC); + LT.setPacketType(PACKET_TYPE_RANGING); + LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate); + LT.setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 0, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0); + LT.setRfFrequency(Frequency, Offset); + LT.setTxParams(TXpower, RADIO_RAMP_02_US); + LT.setRangingMasterAddress(RangingAddress); + LT.setRangingSlaveAddress(RangingAddress); + LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RANGING_MASTER_RESULT_VALID + IRQ_RANGING_MASTER_RESULT_TIMEOUT), 0, 0); //set for IRQ on RX done + LT.setRangingCalibration(LT.lookupCalibrationValue(SpreadingFactor, Bandwidth)); + LT.setRangingRole(RANGING_MASTER); + LT.writeRegister(REG_RANGING_FILTER_WINDOW_SIZE, 8); //set up window size for ranging averaging + LT.setHighSensitivity(); + //*************************************************************************************************** + + //LT.setRangingCalibration(Calibration); //override automatic lookup of calibration value from library table + + calvalue = LT.getSetCalibrationValue(); + Remainder = calvalue / 10; + calibrationstart = (Remainder * 10) - 1000; + calibrationend = (Remainder * 10) + 1000; + Serial.print(F("CalibrationStart,")); + Serial.print(calibrationstart); + Serial.print(F(",CalibrationEnd,")); + Serial.println(calibrationend); + Serial.println(); + + delay(2000); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/56_Ranging_Calibration_Checker/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/56_Ranging_Calibration_Checker/Settings.h new file mode 100644 index 0000000..0a4bb42 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/56_Ranging_Calibration_Checker/Settings.h @@ -0,0 +1,48 @@ +/***************************************************************************************************** + Programs for Arduino - Copyright of the author Stuart Robinson - 17/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used + +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions in hz +const int32_t Offset = 0; //offset frequency in hz for calibration purposes +const uint8_t Bandwidth = LORA_BW_0800; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF8; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate +const uint16_t Calibration = 11426; //Manual Ranging calibrarion value + +const uint8_t RangingRole = RANGING_SLAVE; //Ranging role, RANGING_MASTER or RANGING_SLAVE +const uint8_t TXpower = 10; //Transmit power used +const uint32_t RangingAddress = 16; //must match address in recever + +const uint16_t waittimemS = 10000; //wait this long in mS for packet before assuming timeout +const uint16_t TXtimeoutmS = 5000; //ranging TX timeout in mS +const uint16_t RXtimeoutmS = 0xFFFF; //ranging RX timeout in mS +const uint16_t packet_delaymS = 250; //forced extra delay in mS between ranging requests +const uint16_t rangeingcount = 5; //number of times ranging is cqarried out for each distance measurment +float distance_adjustment = 1; //adjustment factor to calculated distance diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/Pictures/Calibration_Values.jpg b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/Pictures/Calibration_Values.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ca0893a6d42587e2cabfa50790959b7ed2890229 GIT binary patch literal 20046 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
JH(CgxD^RlGlI!$Lu^?=1 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�W_e`8zg_;b0252p9w4X(Ok|xiL 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ⅅI?7)>#0PC82XJXH)ZJ1!tvok? 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 literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/ReadMe.md b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/ReadMe.md new file mode 100644 index 0000000..ce8c2d7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Ranging/ReadMe.md @@ -0,0 +1,113 @@ +## Ranging Calibration + +**Note:** The ranging feature of the SX128X is not supported if the module uses external RX and TX switching. + +To measure a distance the master device transmits a ranging request which a slave receives and then sends a response, if the request was for that particular slave. The master receives the slaves response and knows by use of a timer how long the master slave response exchange took. + +The total time for the master slave exchange includes a fixed processing time for the master and slave to transmit and receive the appropriate packets. This fixed time should be static and should be the same no matter how far apart the master initiator and slave receiver are. + +This fixed time then needs to be subtracted from the total round trip time. The remainder of the time is then proportional to the distance in a linear way. + +Thus the ranging function needs a calibration value (the fixed time mentioned above) which changes according to the bandwidth and spreading factor used. Bandwidths of 400khz, 800khz and 1600khz are supported for ranging. The calibration value can also vary slightly between different modules or antennas used. So for the best results its suggested to calibrate a pair of modules together. The SX128XLT library does, by default, do a lookup from a pre-determined range of calibration values. + +Its been noticed in testing that variations in distance measurements of +\-10m are not unusual and this is typically the sort of adjustment that calibration might correct for. If your using the SX128x to measure long distances, into several kilometres, then the standard values provided by the library are probably accurate enough, a variation of 10m over 4km is hardly significant. + +There is a calibration method described in a Semtech document; + +[Introduction to Ranging](https://semtech.my.salesforce.com/sfc/p/#E0000000JelG/a/44000000MDiH/OF02Lve2RzM6pUw9gNgSJXbDNaQJ_NtQ555rLzY3UvY) + +My own approach to the ranging calibration has been simplified. The starting base for the calibration numbers was this table produced by Semtech; + +![Picture 1](Pictures/SX128X_Ranging_Calibration_Values.jpg) + +After running some checks on my own set-ups, I came up with the following set of values, and the ranging programs pick up these numbers by default, you can of course use your own. + +![Picture 1](Pictures/Calibration_Values.jpg) + + +To check the the calibration values from the library a ranging slave was programmed with the **'55\_Ranging\_Slave'** program. Normally the ranging example programs automatically lookup the calibration value from a table in the file 'SX128XLT_Definitions.h'. However the value can be manually configured using the command; + +setRangingCalibration(Calibration); + +where Calibration is the value to use, normally in the range of 10,000 to 13,500. + +Lets follow and example of testing the calibration of the modules for ranging at spreading factor 8, bandwidth 800khz. From the initial table shown above, the calibration value is around 11350. Set the appropriate LoRa settings in the 'Settings.h' file for the slave and ranging calibration programs to; + + const uint8_t Bandwidth = LORA_BW_0800; //LoRa bandwidth + const uint8_t SpreadingFactor = LORA_SF8; //LoRa spreading factor + const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + + +Program the slave device and at start-up the serial monitor should show; + + 55_Ranging_Slave Starting + Device found + Calibration,11350 + RangingListen, + +The example program **'56\_Ranging\_Calibration\_Checker'** varies the calibration value for each master ranging measurement starting at a value 1000 less than the calibration value provided and changing it in steps to a value that is 1000 more than the mid value. At each change of calibration value the distance is measured and sent to the serial monitor. When the distance goes to zero 3 times in a row the process stops. You can then see on the serial monitor which calibration value gives close to a 0m reading. + +When the ranging calibration program starts you should see this; + + 56_Ranging_Calibration_Checker Starting + Checking device + Device found + CalibrationStart,10350,CalibrationEnd,12350 + +After a while the program reported this; + TransmitRanging,Calibration,11220,IRQ,200,Valid,RAW,47,Distance,3.2m,Time,33mS,OKCount,88,ErrorCount,0 + TransmitRanging,Calibration,11230,IRQ,200,Valid,RAW,1F,Distance,1.4m,Time,31mS,OKCount,89,ErrorCount,0 + TransmitRanging,Calibration,11240,IRQ,200,Valid,RAW,19,Distance,1.1m,Time,31mS,OKCount,90,ErrorCount,0 + TransmitRanging,Calibration,11250,IRQ,200,Valid,RAW,36,Distance,2.4m,Time,30mS,OKCount,91,ErrorCount,0 + TransmitRanging,Calibration,11260,IRQ,200,Valid,RAW,FFFFFF,Distance,0.0m,Time,31mS,OKCount,92,ErrorCount,0, Distance is Zero! + TransmitRanging,Calibration,11270,IRQ,200,Valid,RAW,2B,Distance,1.9m,Time,33mS,OKCount,93,ErrorCount,0 + TransmitRanging,Calibration,11280,IRQ,200,Valid,RAW,1D,Distance,1.3m,Time,30mS,OKCount,94,ErrorCount,0 + TransmitRanging,Calibration,11290,IRQ,200,Valid,RAW,0,Distance,0.0m,Time,31mS,OKCount,95,ErrorCount,0, Distance is Zero! + TransmitRanging,Calibration,11300,IRQ,200,Valid,RAW,FFFFD7,Distance,0.0m,Time,33mS,OKCount,96,ErrorCount,0, Distance is Zero! + + +So with the slave using an initial calibration value of 11350 the master got to around 0m at a calibration value of 11300. Lets re-program the slave with a calibration value of 11300. To override the automatic lookup of the calibration value from the table in SX128XLT_Definitions.h ensure this command is enabled in setup() function after the main sequence functions calls configuring the SX128X device; + +**LT.setRangingCalibration(11300); //override automatic lookup of calibration value from library table** + + +The ranging calibration program then produced these results; + + TransmitRanging,Calibration,11300,IRQ,400, RangingTimeout! ,OKCount,94,ErrorCount,2 + TransmitRanging,Calibration,11310,IRQ,200,Valid,RAW,73,Distance,5.2m,Time,31mS,OKCount,95,ErrorCount,2 + TransmitRanging,Calibration,11320,IRQ,200,Valid,RAW,59,Distance,4.0m,Time,31mS,OKCount,96,ErrorCount,2 + TransmitRanging,Calibration,11330,IRQ,200,Valid,RAW,FFFF46,Distance,0.0m,Time,30mS,OKCount,97,ErrorCount,2, Distance is Zero! + TransmitRanging,Calibration,11340,IRQ,200,Valid,RAW,FFFFD0,Distance,0.0m,Time,33mS,OKCount,98,ErrorCount,2, Distance is Zero! + TransmitRanging,Calibration,11350,IRQ,200,Valid,RAW,FFFFCB,Distance,0.0m,Time,33mS,OKCount,99,ErrorCount,2, Distance is Zero! + + +So a calibration value of maybe 11320 seems about right. This value can then be used to manually configure a particular set-up. + +The example program **54_Ranging_Master** can then be programmed with the same calibration value just determined. This program will carry out 5 ranging attempts and average the results to a distance shown on the serial monitor. This program will also drive a SSD1306 OLED for use as a portable distance measuring device. + +The master ranging programs calculates the distance and then adjusts the measured value with this setting in the Settings.h file; + +**const float distance_adjustment = 1.0; //adjustment to calculated distance** + +The adjustment value need to be determined by using the ranging programs to measure the distance over a long known path. I used a location when the master has line of sight over a long path, 4.405km in this case. This distance measurement was obtained from the 1:25000 Ordnance Survey map. + +Over this long path the average distance reported by the SX1280 4.424km, so the adjustment factor is 4.405/4.424 = 0.99571 + + +### Variances at short distances. + +Regardless of the derived calibration value the conversion of the time of flight result from the values reported in the SX1280 registers to distance is a linear one, so any variation in register value over a fixed distance will represent a distance variation. Whilst you can average results the distance variations will be down to the limitations of the internal timing measurements that the SX128x takes. + +I set-up a outdoor test in a large open areas, my local playing field. The slave was placed on a pole about 1.8M off the ground and I stood with the master hand-held, away from my body, at 100m distance. The calibration and adjustment values were determined as mentioned above, so the master ought to be recoding a distance of 100m. The results of around 140 ranging measurements are below; + + +![Picture 1](Pictures/SX128XLT_Ranging_100m.jpg) + +You can see a variation in distance of +\- 10m at 100m distance is not unusual. Apart from long term averaging its difficult to see what can be done to reduce these variances. + +It has also been noted that where there is a possibility of reflections such as in urban areas, larger variances than this have been seen particularly if the antenna orientations are moving or otherwise changed. + + + +### Stuart Robinson +### March 2020 \ No newline at end of file diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/ReadMe.md b/lib/SX12XX-LoRa/examples/SX128x_examples/ReadMe.md new file mode 100644 index 0000000..f0fc777 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/ReadMe.md @@ -0,0 +1,88 @@ +# SX128X Library + + + +This part of the SX12XX library supports the SX1280 and SX1281 2.4Ghz LoRa modules. + +The objective of the SX12XX library is to allow the same program sketches to be used across the range of UHF LoRa modules SX126x and SX127x (UHF) as well as the 2.4Ghz SX128x modules. + + +###SX128X Considerations for pin usage + +There is a range of SX128X modules available and they have slightly different pins usage. + +The library only supports the SPI based LoRa modules and these all require that the SPI bus pins, SCK, MOSI and MISO are connected. All modules also need a NSS (chip select pin) and NRESET (reset) pin. All devices need the RFBUSY pin to be used also. + +The basic SX1280 modules from NiceRF and Ebyte were used to test the examples in this library. There may be newer devices out there that have additional features, such as TCXOs, that may require different set-ups or pin usage. + +Of the LoRa devices DIO pins the SX128X library in standard form only uses DIO1. Some SX128x modules have RX and TX enable pins that need to be appropriately activated when receiving or transmitting. + +Thus a begin function that initialised all possible permutations of pins would look like this; + +begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX\_EN, TX\_EN, LORA\_DEVICE); + +Clearly the above begin statement is somewhat cumbersome and could potentially be shortened for the NiceRF SX128X devices to; + +begin(NSS, NRESET, RFBUSY, DIO1, LORA\_DEVICE); + +And shortened for the Ebyte devices to; + +begin(NSS, NRESET, RFBUSY, DIO1, RX\_EN, TX\_EN, LORA\_DEVICE); + +Which is a lot more manageable. + +You can use the shorter formats of the begin commands can be used in your own programs if you have the appropriate module, NiceRF or Ebyte. + +The full format of begin, see above, is still valid and is used in most of the example programs. If you have written your own programs using the earlier library these programs do not need changing. You could not have used the newer constructs of the begin command (to support the newer devices) in your programs since the newer constructs did not exist in the older version library. + +Valid values for LORA_DEVICE are DEVICE_SX1280 and DEVICE_SX1281. + +Accepted its all a bit confusing, but regrettably module manufacturers have different ideas about design. + +# SX128X Library - LoRa Settings + +In the setModulationParams() function the Spreading factor, Bandwidth, CodeRate LoRa parameters can be set; + + //LoRa Spreading factors + LORA_SF5 + LORA_SF6 + LORA_SF7 + LORA_SF8 + LORA_SF9 + LORA_SF10 + LORA_SF11 + LORA_SF12 + + //LoRa Bandwidths + LORA_BW_0200 //actually 203125hz + LORA_BW_0400 //actually 406250hz + LORA_BW_0800 //actually 812500hz + LORA_BW_1600 //actually 1625000hz + + //LoRa Coding rates + LORA_CR_4_5 + LORA_CR_4_6 + LORA_CR_4_7 + LORA_CR_4_8 + + +The device types for the SX128X part of the library are; + + DEVICE_SX1280 + DEVICE_SX1281 + +In the transmit() function the TXpower can be set from -18dBm to +12dBm. + +In the setPacketType() function valid packet type are; + + PACKET_TYPE_LORA + PACKET_TYPE_FLRC + PACKET_TYPE_RANGING + PACKET_TYPE_BLE (not implemented) + PACKET_TYPE_NONE + + +### Stuart Robinson + +### April 2020 + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino new file mode 100644 index 0000000..6baf463 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/21_On_Off_Transmitter/21_On_Off_Transmitter.ino @@ -0,0 +1,307 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a remote control transmitter. When one of four switches are made + (shorted to ground) a packet is transmitted with single byte indicating the state of Switch0 as bit 0, + Switch1 as bit 1 and Switch2 as bit 2. To prevent false triggering at the receiver the packet contains a + 32 bit number called the TXIdentity which in this example is set to 1234554321. The receiver will only + act on, change the state of the outputs, if the identity set in the receiver matches that of the + transmitter. The chance of a false trigger is fairly remote. + + Between switch presses the LoRa device and Atmel microcontroller are put to sleep. A switch press wakes + up the processor from sleep, the switches are read and a packet sent. On a 'bare bones' Arduino setup + the transmitter has a sleep current of approx 2.2uA, so it's ideal for a battery powered remote control + with a potential range of many kilometres. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings + are not necessarily optimised for long range. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +#include //watchdog timer library, integral to Arduino IDE +#include //watchdog timer library, integral to Arduino IDE +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +SX128XLT LT; + +uint32_t TXpacketCount; +uint8_t TXPacketL; + +volatile bool switch0flag = false; +volatile bool switch1flag = false; +volatile bool switch2flag = false; +volatile bool switch3flag = false; + +void loop() +{ + uint8_t switches; + + digitalWrite(LED1, LOW); //turn off indicator LED + Serial.print(F("Sleeping zzzz")); + Serial.flush(); //make sure all serial output has gone + + LT.setSleep(CONFIGURATION_RETENTION); //sleep LoRa device, keeping register settings in sleep. + sleep_permanent(); //sleep Atmel processor permanently for switch wakeup only + LT.wake(); //wake up the lora device - nicely + + digitalWrite(LED1, HIGH); + + Serial.println(F(" - Awake !!")); //the processor has woken up + switches = readSwitches(); //read the state of the switches + + TXpacketCount++; + Serial.print(TXpacketCount); //print the numbers of sends + Serial.print(F(" Sending > ")); + + Serial.print(switches, BIN); + + if (sendSwitchPacket(switches)) + { + Serial.println(F(" SentOK")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } + + Serial.println(); + delay(500); +} + + +uint8_t sendSwitchPacket(uint8_t switches) +{ + //The SX12XX buffer is filled with variables of a known type and in a known sequence. Make sure the + //receiver uses the same variable types and sequence to read variables out of the receive buffer. + uint8_t len; + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + LT.writeUint8(RControl1); //this byte identifies the type of packet + LT.writeUint32(TXIdentity); //this 32bit integer defines the Identity of the transmiter + LT.writeUint8(switches); //this byte contains the 8 switch values to be sent + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + //now transmit the packet, 10 second timeout, and wait for it to complete sending + TXPacketL = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void sleep_permanent() +{ + attachInterrupts(); + + ADCSRA = 0; //disable ADC + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + + // turn off brown-out enable in software + MCUCR = bit (BODS) | bit (BODSE); //turn on brown-out enable select + MCUCR = bit (BODS); //this must be done within 4 clock cycles of above + interrupts (); //guarantees next instruction executed + + sleep_cpu (); //sleep within 3 clock cycles of above + + /* wake up here */ + + sleep_disable(); + + detachInterrupts(); +} + + +void attachInterrupts() +{ + if (SWITCH0 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH0), wake0, FALLING); + switch0flag = false; + } + + if (SWITCH1 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH1), wake1, FALLING); + switch1flag = false; + } + + if (SWITCH2 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH2), wake2, FALLING); + switch2flag = false; + } + + if (SWITCH3 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH3), wake3, FALLING); + switch3flag = false; + } + +} + + +void detachInterrupts() +{ + if (SWITCH0 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH0)); + } + + if (SWITCH1 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH1)); + } + + if (SWITCH2 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH2)); + } + + if (SWITCH3 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH3)); + } + +} + + + + +void wake0() +{ + switch0flag = true; +} + + +void wake1() +{ + switch1flag = true; +} + + +void wake2() +{ + switch2flag = true; +} + + +void wake3() +{ + switch3flag = true; +} + + +uint8_t readSwitches() +{ + uint8_t switchByte = 0xFF; //start assuming all switches off + + if (switch0flag) + { + bitClear(switchByte, 0); //if the flag is set clear the bit + Serial.println(F("SWITCH0 pressed")); + } + + if (switch1flag) + { + bitClear(switchByte, 1); //if the flag is set clear the bit + Serial.println(F("SWITCH1 pressed")); + } + + if (switch2flag) + { + bitClear(switchByte, 2); //if the flag is set clear the bit + Serial.println(F("SWITCH2 pressed")); + } + + + if (switch3flag) + { + bitClear(switchByte, 3); //if the flag is set clear the bit + Serial.println(F("SWITCH3 pressed")); + } + + return switchByte; +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setupSwitches() +{ + if (SWITCH0 >= 0) + { + pinMode(SWITCH0, INPUT_PULLUP); + } + + if (SWITCH1 >= 0) + { + pinMode(SWITCH1, INPUT_PULLUP); + } + + if (SWITCH2 >= 0) + { + pinMode(SWITCH2, INPUT_PULLUP); + } + + if (SWITCH3 >= 0) + { + pinMode(SWITCH3, INPUT_PULLUP); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + setupSwitches(); + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates LoRa device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(F("Transmitter ready")); + Serial.println(); + +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h new file mode 100644 index 0000000..bab1eb9 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/21_On_Off_Transmitter/Settings.h @@ -0,0 +1,49 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t RFBUSY = 7; //RF busy on LoRa device +const int8_t DIO1 = 3; //DIO1 on LoRa device, used for RX and TX done +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t DIO3 = -1; //DIO3 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on +const int8_t RX_EN = -1; //pin for RX enable, used on some SX1280 devices, set to -1 if not used +const int8_t TX_EN = -1; //pin for TX enable, used on some SX1280 devices, set to -1 if not used + + +#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using + +const int8_t SWITCH0 = 2; +const int8_t SWITCH1 = 4; +const int8_t SWITCH2 = A3; +const int8_t SWITCH3 = A2; + +const uint32_t TXIdentity = 1234554321; //define an identity number, the receiver must use the same number +//range is 0 to 4294967296 + + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +#define TXpower 10 //power for transmissions in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino new file mode 100644 index 0000000..f75c219 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/22_On_Off_Receiver/22_On_Off_Receiver.ino @@ -0,0 +1,297 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is a remote control receiver. When a packet is received an 8 bit byte + (SwitchByte) is read and the four outputs (defined in Settings.h) are toggled according to the bits + set in this byte. If the Switch1 byte has bit 0 cleared, then OUTPUT0 is toggled. If the Switch1 byte + has bit 1 cleared, then OUTPUT1 is toggled. If the Switch1 byte has bit 2 cleared, then OUTPUT2 is toggled. + + To prevent false triggering at the receiver the packet contains also contains a 32 bit number called the + TXIdentity which in this example is set to 1234554321. The receiver will only act on, change the state + of the outputs, if the identity set in the receiver matches that of the transmitter. The chance of a + false trigger is fairly remote. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define programversion "V1.0" + +#include +#include +#include "Settings.h" +#include + + +SX128XLT LT; + +uint32_t RXpacketCount; +uint16_t errors; + +uint8_t RXPacketL; //length of received packet +uint8_t RXPacketType; //type of received packet +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet + +uint8_t SwitchByte = 0xFF; //this is the transmitted switch values, bit 0 = Switch0 etc + +void loop() +{ + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); //read the signal strength of the received packet + PacketSNR = LT.readPacketSNR(); //read the signal to noise ratio of the received packet + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +uint8_t packet_is_OK() +{ + //packet has been received, now read from the SX12xx Buffer using the same variable type and + //order as the transmit side used. + uint32_t TXIdentity; + + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(" Packet Received")); + + LT.startReadSXBuffer(0); //start buffer read at location 0 + RXPacketType = LT.readUint8(); //read in the packet type + TXIdentity = LT.readUint32(); //read in the identity of transmitter + SwitchByte = LT.readUint8(); //read in the Switch values + RXPacketL = LT.endReadSXBuffer(); //finish buffer read + + printpacketDetails(); + + if (RXPacketType != RControl1) + { + Serial.print(F(" Wrong packet type")); + led_Flash(5, 25); //short fast speed flash indicates wrong packet type + return 0; + } + + if (TXIdentity != RXIdentity) + { + Serial.print(F(" Transmitter ")); + Serial.print(TXIdentity); + Serial.print(F(" not recognised")); + led_Flash(5, 25); //short fast speed flash indicates transmitter not recognised + return 0; + } + + if (LT.readRXPacketL() != 6) + { + Serial.print(F(" Wrong Packet Length")); + led_Flash(5, 25); //short fast speed flash indicates transmitter not recognised + return 0; + } + + //if we get to here, then the packet is valid so switch outputs accordingly + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, HIGH); + } + + Serial.print(F(",SwitchByte Received ")); + Serial.print(SwitchByte, BIN); //print switch values in binary, if a bit is 0, that switch is active + actionOutputs(SwitchByte); + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + } + + return RXPacketL; +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + errors++; + Serial.print(F("PacketError")); + printpacketDetails(); + Serial.print(F("IRQreg,")); + Serial.print(IRQStatus, HEX); + } +} + + +void printpacketDetails() +{ + Serial.print(F(" RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void actionOutputs(uint8_t switches) +{ + //read the recreived switch byte and toggle outputs as required + + if (!bitRead(switches, 0)) + { + //toggle Output state + digitalWrite(OUTPUT0, !digitalRead(OUTPUT0)); //toggle Output state + } + + if (!bitRead(switches, 1)) + { + digitalWrite(OUTPUT1, !digitalRead(OUTPUT1)); //toggle Output state + } + + if (!bitRead(switches, 2)) + { + digitalWrite(OUTPUT2, !digitalRead(OUTPUT2)); //toggle Output state + } + + if (!bitRead(switches, 3)) + { + digitalWrite(OUTPUT3, !digitalRead(OUTPUT3)); //toggle Output state + } +} + + +void setupOutputs() +{ + //configure the output pins, if a pin is defiend in 'Settings.h' as -1, its not configured, so stays as input + + if (OUTPUT0 >= 0) + { + pinMode(OUTPUT0, OUTPUT); + } + + if (OUTPUT1 >= 0) + { + pinMode(OUTPUT1, OUTPUT); + } + + if (OUTPUT2 >= 0) + { + pinMode(OUTPUT2, OUTPUT); + } + + if (OUTPUT3 >= 0) + { + pinMode(OUTPUT3, OUTPUT); + } + + if (BUZZER >= 0) + { + pinMode(BUZZER, OUTPUT); + } + +} + + +void outputCheck(uint8_t number, uint32_t ondelaymS, uint32_t offdelaymS) +{ + uint8_t index; + + Serial.println(F("Toggling outputs")); + + for (index = 1; index <= number; index++) + { + digitalWrite(OUTPUT0, HIGH); + delay(ondelaymS); + digitalWrite(OUTPUT0, LOW); + delay(offdelaymS); + digitalWrite(OUTPUT1, HIGH); + delay(ondelaymS); + digitalWrite(OUTPUT1, LOW); + delay(offdelaymS); + digitalWrite(OUTPUT2, HIGH); + delay(ondelaymS); + digitalWrite(OUTPUT2, LOW); + delay(offdelaymS); + digitalWrite(OUTPUT3, HIGH); + delay(offdelaymS); + digitalWrite(OUTPUT3, LOW); + delay(offdelaymS); + digitalWrite(BUZZER, HIGH); + delay(offdelaymS); + digitalWrite(BUZZER, LOW); + delay(offdelaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + setupOutputs(); + + outputCheck(3, 500, 100); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(F("Receiver ready")); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/22_On_Off_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/22_On_Off_Receiver/Settings.h new file mode 100644 index 0000000..f1748dc --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/22_On_Off_Receiver/Settings.h @@ -0,0 +1,49 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t RFBUSY = 7; //RF busy on LoRa device +const int8_t DIO1 = 3; //DIO1 on LoRa device, used for RX and TX done +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t DIO3 = -1; //DIO3 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on +const int8_t RX_EN = -1; //pin for RX enable, used on some SX1280 devices, set to -1 if not used +const int8_t TX_EN = -1; //pin for TX enable, used on some SX1280 devices, set to -1 if not used +const int8_t BUZZER = -1; //pin for buzzer, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using + +const int8_t OUTPUT0 = 2; +const int8_t OUTPUT1 = 4; +const int8_t OUTPUT2 = A3; +const int8_t OUTPUT3 = A2; + +const uint32_t RXIdentity = 1234554321; //define an identity number, the receiver must use the same number + //range is 0 to 4294967296 + + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +#define TXpower 10 //power for transmissions in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino new file mode 100644 index 0000000..7f7182f --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/35_Remote_Control_Servo_Transmitter.ino @@ -0,0 +1,190 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a remote control transmitter that uses a LoRa link to transmit the positions + from a simple joystick to a remote receiver. The receiver uses the sent joystick positions to adjust the + positions of servos. The postions of the joysticks potentiometers on the transmitter are read with the + analogueRead() function. + + If the joystick has a switch, often made by pressing on the joystick, then this can be used to remote + control an output on the receiver. The switch is read by an interrupt, the interrupt routine sets a flag + byte which is read in loop(). + + The program is intended as a proof of concept demonstration of how to remote control servos, the program + is not designed as a practical remote control device for RC model cars for instance. + + To have the transmitter program print out the values read from the joystick, comment in the line; + + //#define DEBUG + + Which is just above the loop() function. With the DEBUG enabled the transmission rate, the rate at which + the control packets are transmitted will be slowed down. + + To reduce the risk of the receiver picking up LoRa packets from other sources, the packet sent contains a + 'TXidentity' number, valid values are 0 - 65535. The receiver must be setup with the matching identity + number or the received packets will be ignored. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings + are not necessarily optimised for long range. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +SX128XLT LT; + +#include "PinChangeInterrupt.h" //get the library here; https://github.com/NicoHood/PinChangeInterrupt + +uint32_t TXpacketCount; +uint8_t TXPacketL; + +uint8_t joystickX1value; //variable to read the value from the analog pin +uint8_t joystickY1value; //variable to read the value from the analog pin + +volatile bool switch1flag = false; + +//#define DEBUG //comment in thie line (remove the two // at the beggining) for debug output + + +void loop() +{ + uint8_t switchByte = 0xFF; + + joystickX1value = (uint8_t) (analogRead(joystickX1) / 4) ; //read the joystick X1 pot, turn 0-1023 into 0 to 255 + joystickY1value = (uint8_t) (analogRead(joystickY1) / 4); //read the joystick Y1 pot + + if (switch1flag) + { + bitClear(switchByte, 1); //if the switch is down clear the bit + digitalWrite(LED1, HIGH); //turn on LED as switch indicator + switch1flag = false; + } + + if (!sendJoystickPacket(joystickX1value, joystickY1value, switchByte)) + { + Serial.print(F("Send Error - IRQreg,")); + Serial.print(LT.readIrqStatus(), HEX); + } +} + + +uint8_t sendJoystickPacket(uint16_t X1value, uint16_t Y1value, uint8_t switches) +{ + //The SX12XX buffer is filled with variables of a known type and in a known sequence. Make sure the + //receiver uses the same variable types and sequence to read variables out of the receive buffer. + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + LT.writeUint8(RControl1); //this is the packet type + LT.writeUint8(TXIdentity); //this value represents the transmitter number + LT.writeUint8(X1value); //this byte contains joystick pot AD X1 value to be sent + LT.writeUint8(Y1value); //this byte contains joystick pot AD Y1 value to be sent + LT.writeUint8(switches); //switches value + LT.endWriteSXBuffer(); //close the packet, thee are 5 bytes to send + + //now transmit the packet, 10 second timeout, and wait for it to complete sending + TXPacketL = LT.transmitSXBuffer(0, PacketLength, 10000, TXpower, WAIT_TX); + +#ifdef DEBUG + Serial.print(TXIdentity); + Serial.print(F(",X1,")); + Serial.print(joystickX1value); + Serial.print(F(",Y1,")); + Serial.print(joystickY1value); + Serial.print(F(",")); + Serial.print(switches, BIN); + Serial.println(); +#endif + + digitalWrite(LED1, LOW); //LED off, may have been on due to switch press + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void attachInterrupts() +{ + if (SWITCH1 >= 0) + { + attachPCINT(digitalPinToPCINT(SWITCH1), wake1, FALLING); + switch1flag = false; + } +} + + +void detachInterrupts() +{ + if (SWITCH1 >= 0) + { + detachPCINT(digitalPinToPCINT(SWITCH1)); + } +} + + +void wake1() +{ + switch1flag = true; +} + + +void setupSwitches() +{ + if (SWITCH1 >= 0) + { + pinMode(SWITCH1, INPUT_PULLUP); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + setupSwitches(); + + Serial.begin(115200); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + attachInterrupts(); + + Serial.println(F("35_Remote_Control_Servo_Transmitter ready")); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h new file mode 100644 index 0000000..a2961b3 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/35_Remote_Control_Servo_Transmitter/Settings.h @@ -0,0 +1,49 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t RFBUSY = 7; //RF busy on LoRa device +const int8_t DIO1 = 3; //DIO1 on LoRa device, used for RX and TX done +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t DIO3 = -1; //DIO3 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on +const int8_t RX_EN = -1; //pin for RX enable, used on some SX1280 devices, set to -1 if not used +const int8_t TX_EN = -1; //pin for TX enable, used on some SX1280 devices, set to -1 if not used + + +#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using + +const int8_t joystickX1 = A2; //analog pin for the joystick 1 X pot +const int8_t joystickY1 = A3; //analog pin for the joystick 1 Y pot +const int8_t SWITCH1 = 2; //switch on joystick, set to -1 if not used + +const uint32_t TXIdentity = 123 ; //define a transmitter number, the receiver must use the same number + //range is 0 to 255 + + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +const uint8_t PacketLength = 5; //packet length is fixed +const int8_t TXpower = 10; //LoRa transmit power in dBm + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino new file mode 100644 index 0000000..69dc3f3 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/36_Remote_Control_Servo_Receiver.ino @@ -0,0 +1,256 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This is a remote control receiver that uses a LoRa link to control the positions of + servos sent from a remote transmitter. + + If the ttransmitter joystick has a switch, often made by pressing on the joystick, then this can be used + to remote control an output on the receiver. + + The program is intended as a proof of concept demonstration of how to remote control servos, the program + is not designed as a practical remote control device for RC model cars for instance. + + It would be straight forward to make the transmitter program send packets continuously, but in most places + in the world that would break a normal limitation of 10% duty cycle for unlicensed use. Therefore the + program was designed to only transmit at a 10% duty cycle. Thus the fastest (lowest air time) packets are + used, spreading factor 6 at a bandwidth of 500khz. This results in an air time for the 5 byte control + packet of around 4mS, so there are around 25 sent per second. + + To have the receiver program print out the joystick values (0-255) read from the received packet, comment + in the line; + + //#define DEBUG + + Which is just above the loop() function. With the DEBUG enabled then there is a possibility that some + transmitted packets will be missed. With the DEBUG line enabled to servos should also sweep to and fro 3 + times at program start-up. + + To reduce the risk of the receiver picking up LoRa packets from other sources, the packet sent contains a + 'TXidentity' number, valid values are 0 - 255. The receiver must be setup with the matching RXIdentity + number in Settings.h or the received packets will be ignored. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. These settings + are not necessarily optimised for long range. + + Serial monitor baud rate is set at 115200. +*******************************************************************************************************/ + +#define programversion "V1.0" + +#include +#include +#include "Settings.h" +#include + +SX128XLT LT; + +#include +Servo ServoX1; //create the servo object +Servo ServoY1; //create the servo object + +uint8_t joystickX1value; //variable to read the value from the analog pin +uint8_t joystickY1value; //variable to read the value from the analog pin +uint8_t RXPacketL; //length of received packet +uint8_t RXPacketType; //type of received packet + +//#define DEBUG + + +void loop() +{ + uint16_t IRQStatus; + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort + + while (!digitalRead(DIO1)); //wait for DIO1 to go high + + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & (IRQ_RX_DONE + IRQ_HEADER_VALID) ) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + +} + + +uint8_t packet_is_OK() +{ + //packet has been received, now read from the SX12xx Buffer using the same variable type and + //order as the transmit side used. + uint8_t TXIdentity; + uint16_t pulseX1, pulseY1; + uint8_t switchByte = 0xFF; //this is the transmitted switch values, bit 0 = Switch0 etc + + LT.startReadSXBuffer(0); //start buffer read at location 0 + RXPacketType = LT.readUint8(); //read in the packet type + TXIdentity = LT.readUint8(); //read in the transmitter number + joystickX1value = LT.readUint8(); //this byte contains joystick pot AD X1 value sent + joystickY1value = LT.readUint8(); //this byte contains joystick pot AD Y1 value sent + switchByte = LT.readUint8(); //read in the Switch values + RXPacketL = LT.endReadSXBuffer(); //end buffer read + +#ifdef DEBUG + Serial.print(TXIdentity); + Serial.print(F(",X1,")); + Serial.print(joystickX1value); + Serial.print(F(",Y1,")); + Serial.print(joystickY1value); + Serial.print(F(",")); + Serial.print(switchByte, BIN); + Serial.println(); +#endif + + + if (RXPacketType != RControl1) + { + Serial.print(F("Packet type ")); + Serial.println(RXPacketType); + led_Flash(5, 25); //short fast speed flash indicates wrong packet type + return 0; + } + + + if (TXIdentity != RXIdentity) + { + Serial.print(F("TX")); + Serial.print(TXIdentity); + Serial.println(F("?")); + return 0; + } + + //actionServos + pulseX1 = map(joystickX1value, 0, 255, 1000, 2000); //scale the numbers from the joystick + ServoX1.writeMicroseconds(pulseX1); + pulseY1 = map(joystickY1value, 0, 255, 1000, 2000); //scale the numbers from the joystick + ServoY1.writeMicroseconds(pulseY1); //move the servo to position + + //actionOutputs + if (!bitRead(switchByte, 1)) + { + digitalWrite(OUTPUT1, !digitalRead(OUTPUT1)); //Toggle Output state + } + + return RXPacketL; +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + int8_t PacketRSSI; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout")); + } + else + { + PacketRSSI = LT.readPacketRSSI(); //read the signal strength of the received packet + Serial.print(F("Err,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm")); + } + Serial.println(); +} + + +void setupOutputs() +{ + //configure the output pins, if a pin is defiend in 'Settings.h' as -1, its not configured, so stays as input + if (OUTPUT1 >= 0) + { + pinMode(OUTPUT1, OUTPUT); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void sweepTest(uint8_t num) +{ + uint16_t index1, index2; + for (index1 = 1; index1 <= num; index1++) + { + for (index2 = 900; index2 <= 2100; index2++) + { + ServoX1.writeMicroseconds(index2); + ServoY1.writeMicroseconds(index2); + } + + delay(1000); + + for (index2 = 2100; index2 >= 900; index2--) + { + ServoX1.writeMicroseconds(index2); + ServoY1.writeMicroseconds(index2); + } + + delay(1000); + } +} + + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + setupOutputs(); + + Serial.begin(115200); + + ServoX1.attach(pinservoX1); //connect pin pinservoX1 to ServoX1 object + ServoY1.attach(pinservoY1); //connect pin pinservoY1 to ServoY1 object + +#ifdef DEBUG + Serial.println(F("Servo sweep test")); + sweepTest(3); +#endif + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(F("36_Remote_Control_Servo_Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h new file mode 100644 index 0000000..8fc2ae3 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/RemoteControl/36_Remote_Control_Servo_Receiver/Settings.h @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO2, +//DIO3, may not be in used by this sketch so they do not need to be connected and +//should be set to -1. + +const int8_t NSS = 10; //select on LoRa device +const int8_t NRESET = 9; //reset on LoRa device +const int8_t RFBUSY = 7; //RF busy on LoRa device +const int8_t DIO1 = 3; //DIO1 on LoRa device, used for RX and TX done +const int8_t DIO2 = -1; //DIO2 on LoRa device, normally not used so set to -1 +const int8_t DIO3 = -1; //DIO3 on LoRa device, normally not used so set to -1 +const int8_t LED1 = 8; //On board LED, logic high is on +const int8_t RX_EN = -1; //pin for RX enable, used on some SX1280 devices, set to -1 if not used +const int8_t TX_EN = -1; //pin for TX enable, used on some SX1280 devices, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using + +const int8_t pinservoX1 = 2; //pin for controlling servo X1 +const int8_t pinservoY1 = 4; //pin for controlling servo Y1 +const int8_t OUTPUT1 = 8; //this output toggles when joystick switch is pressed on receiver + +const uint16_t RXIdentity = 123; //define a receiver number, the transmitter must use the same number + //range is 0 to 255 + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +#define Frequency 2445000000 //frequency of transmissions +#define Offset 0 //offset frequency for calibration purposes +#define Bandwidth LORA_BW_0400 //LoRa bandwidth +#define SpreadingFactor LORA_SF7 //LoRa spreading factor +#define CodeRate LORA_CR_4_5 //LoRa coding rate + +const uint8_t PacketLength = 5; //packet length is fixed + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/17_Sensor_Transmitter/17_Sensor_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/17_Sensor_Transmitter/17_Sensor_Transmitter.ino new file mode 100644 index 0000000..45bbde3 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/17_Sensor_Transmitter/17_Sensor_Transmitter.ino @@ -0,0 +1,315 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 20/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program transmits a LoRa packet without using a processor buffer, the LoRa + devices internal buffer is filled directly with variables. + + The sensor used is a BME280. The pressure, humidity, and temperature are read and transmitted. There + is also a 16bit value of battery mV (simulated) and and a 8 bit status value at the packet end. + + Although the LoRa packet transmitted and received has its own internal CRC error checking, you could + still receive packets of the same length from another source. If this valid packet were to be used + to recover the sensor values, you could be reading rubbish. To reduce the risk of this, when the packet + is transmitted the CRC value of the actual sensor data is calculated and sent out with the packet. + This CRC value is read by the receiver and used to check that the received CRC matches the supposed + sensor data in the packet. As an additional check there is some addressing information at the beginning + of the packet which is also checked for validity. Thus we can be relatively confident when reading the + received packet that its genuine and from this transmitter. The packet is built and sent in the + sendSensorPacket() function, there is a 'highlighted section' where the actual sensor data is added to + the packet. + + Between readings the LoRa device, BME280 sensor, and Atmel microcontroller are put to sleep in units of + 8 seconds using the Atmel processor internal watchdog. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + There is also an option of using a logic pin to turn the resistor divider used to read battery voltage on + and off. This reduces current used in sleep mode. To use the feature set the define for pin BATVREADON + in 'Settings.h' to the pin used. If not using the feature set the pin number to -1. + + The Atmel watchdog timer is a viable option for a very low current sensor node. A 'bare bones' ATmega328P + with regulator and LoRa device has a sleep current of 6.6uA, add the LoRa devices and BME280 sensor + module and the average sleep current only rises to 6.8uA. + + One of these transmitter programs is running on a long term test with a 150mAh battery, to see how long + the battery actually lasts. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +#include //watchdog timer library, integral to Arduino IDE +#include //get the library here; https://github.com/rocketscream/Low-Power + +SX128XLT LT; + +#include //get library here; https://github.com/Seeed-Studio/Grove_BME280 +BME280 bme280; //create an instance of the BME280 senosor +#include + +uint32_t TXpacketCount; +uint8_t TXPacketL; + +float temperature; //the BME280 temperature value +float pressure; //the BME280 pressure value +uint16_t humidity; //the BME280 humididty value +uint16_t voltage; //the battery voltage value +uint8_t statusbyte; //a status byte, not currently used +uint16_t CRCvalue; //the CRC value of the packet data up to this point +uint8_t packetlength; //the packet length that was sent, checked against length received + + +void loop() +{ + TXpacketCount++; + Serial.print(TXpacketCount); //print the numbers of sends + Serial.print(F(" Sending > ")); + + readSensors(); //read the sensor values + printSensorValues(); //print the sensor values + + if (sendSensorPacket()) + { + Serial.println(F("SentOK")); + } + else + { + Serial.print(F("Send Error - IRQreg,")); + Serial.println(LT.readIrqStatus(), HEX); + } + + Serial.print(F("Sleeping zzzz")); + Serial.flush(); //make sure all serial output has gone + + //now put the sensor, LoRa device and processor to sleep + sleepBME280(); //sleep the BME280 + LT.setSleep(CONFIGURATION_RETENTION); //sleep LoRa device, keeping register settings in sleep. + sleep8seconds(sleeps); //sleep Atmel processor in units of approx 8 seconds + + //wait a bit ................ + Serial.println(F(" - Awake !!")); //the processor has woken up + Serial.println(); + + LT.wake(); + normalBME280(); //BME280 sensor to normal mode +} + + +uint8_t sendSensorPacket() +{ + //The SX12XX buffer is filled with variables of a known type and in a known sequence. Make sure the + //receiver uses the same variable types and sequence to read variables out of the receive buffer. + uint8_t len; + + LT.startWriteSXBuffer(0); //start the write packet to buffer process + + LT.writeUint8(Sensor1); //this byte defines the packet type + LT.writeUint8('B'); //this byte identifies the destination node of the packet + LT.writeUint8(1); //this byte identifies the source node of the packet + + /************************************************************************ + Highlighted section - this is where the actual sensor data is added to the packet + ************************************************************************/ + LT.writeFloat(temperature); //add the BME280 temperature + LT.writeFloat(pressure); //add the BME280 pressure + LT.writeUint16(humidity); //add the BME280 humididty + LT.writeUint16(voltage); //add the battery voltage + LT.writeUint8(statusbyte); //add the status byte + /************************************************************************/ + + len = LT.endWriteSXBuffer(); //close the packet, get the length of data to be sent + + addPacketErrorCheck(len); //add the additional CRC error checking to the packet end + + //now transmit the packet, set a timeout of 5000mS, wait for it to complete sending + digitalWrite(LED1, HIGH); //turn on LED as an indicator + TXPacketL = LT.transmitSXBuffer(0, (len + 2), 5000, TXpower, WAIT_TX); + digitalWrite(LED1, LOW); //turn off indicator LED + + return TXPacketL; //TXPacketL will be 0 if there was an error sending +} + + +void addPacketErrorCheck(uint8_t len) +{ + //calculate the CRC of packet sensor data + CRCvalue = LT.CRCCCITTSX(3, (len - 1), 0xFFFF); + + Serial.print(F("Calculated CRC value ")); + Serial.println(CRCvalue, HEX); + + LT.startWriteSXBuffer(len); //start the write packet again at location of CRC, past end of sensor data + LT.writeUint16(CRCvalue); //add the actual CRC value + LT.endWriteSXBuffer(); //close the packet +} + + +void readSensors() +{ + //read the sensor values into the global variables + temperature = bme280.getTemperature(); + pressure = bme280.getPressure(); + humidity = bme280.getHumidity(); + + if (BATVREADON >= 0) + { + voltage = readBatteryVoltage(); //read resistor divider across battery + } + else + { + voltage = 9999; //set a default value + } + statusbyte = 0x55; //manually set this for now, its a test +} + + +void printSensorValues() +{ + Serial.print(F("Temperature,")); + Serial.print(temperature, 1); + Serial.print(F("c,Pressure,")); + Serial.print(pressure, 0); + Serial.print(F("Pa,Humidity,")); + Serial.print(humidity); + Serial.print(F("%,Voltage,")); + Serial.print(voltage); + Serial.print(F("mV,Status,")); + Serial.print(statusbyte, HEX); + Serial.print(F(" ")); + Serial.flush(); +} + + +void sleep8seconds(uint32_t sleeps) +{ + //uses the lowpower library + uint32_t index; + + for (index = 1; index <= sleeps; index++) + { + //sleep 8 seconds + LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); + } +} + + +void sleepBME280() +{ + //write this register value to BME280 to put it to sleep + writeBME280reg(BME280_REGISTER_CONTROL, B01111100); +} + + +void normalBME280() +{ + //write this register value to BME280 to put it to read mode + writeBME280reg(BME280_REGISTER_CONTROL, B01111111); +} + + +void writeBME280reg(byte reg, byte regvalue) +{ + //write a register value to the BME280 + Wire.beginTransmission((uint8_t) BME280_ADDRESS); + Wire.write((uint8_t)reg); + Wire.write((uint8_t)regvalue); + Wire.endTransmission(); +} + + +uint16_t readBatteryVoltage() +{ + //relies on 1V1 internal reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t volts = 0; + byte index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn on MOSFET connecting resitor divider in circuit + } + + analogReference(INTERNAL1V1); + temp = analogRead(BATTERYAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(BATTERYAD); + volts = volts + temp; + } + volts = ((volts / 5) * ADMultiplier) + DIODEMV; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn off MOSFET connecting resitor divider in circuit + } + + return volts; +} + + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); + } + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates LoRa device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + if (!bme280.init()) + { + Serial.println("BME280 Device error!"); + led_Flash(100, 15); //long very fast speed flash indicates BME280 device error + } + + Serial.println(F("Transmitter ready")); + Serial.println(); + + readSensors(); //do an initial sensor read +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/17_Sensor_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/17_Sensor_Transmitter/Settings.h new file mode 100644 index 0000000..34a7679 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/17_Sensor_Transmitter/Settings.h @@ -0,0 +1,45 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //pin for BUZZER, set to -1 if not used + +#define BATVREADON 8 //when high turns on the resistor divider to measure voltage, -1 if not used +#define BATTERYAD A7 //Resitor divider for battery connected here, -1 if not used +#define ADMultiplier 10.00 //adjustment to convert AD value read into mV of battery voltage +#define DIODEMV 98 //mV voltage drop accross diode @ low idle current + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const uint8_t TXpower = 10; //Power for transmissions in dBm + +#define BME280_ADDRESS 0x76 //I2C bus address of BME280 +#define BME280_REGISTER_CONTROL 0xF4 //BME280 register number for power control + +const uint8_t sleeps = 2; //number of 8 second sleeps, gap between transmissions diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino new file mode 100644 index 0000000..ddda0a7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/18_Sensor_Receiver/18_Sensor_Receiver.ino @@ -0,0 +1,358 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - The program receives a LoRa packet without using a processor buffer, the LoRa devices + internal buffer is read direct for the received sensor data. + + The sensor used in the matching '17_Sensor_Transmiter' program is a BME280 and the pressure, humidity, + and temperature are being and received. There is also a 16bit value of battery mV and and a 8 bit status + value at the end of the packet. + + When the program starts, the LoRa device is setup to set the DIO1 pin high when a packet is received. When + a packet is received, its printed and assuming the packet is validated, the sensor results are printed to + the serial monitor and screen. + + For the sensor data to be accepted as valid the folowing need to match; + + The 16bit CRC on the received sensor data must match the CRC value transmitted with the packet. + The packet must start with a byte that matches the packet type sent, 'Sensor1' + The RXdestination byte in the packet must match this node ID of this receiver node, defined by 'This_Node' + + In total thats 16 + 8 + 8 = 32bits of checking, so a 1:4294967296 chance (approx) that an invalid + packet is acted on and erroneous values displayed. + + The pin definitions, LoRa frequency and LoRa modem settings are in the Settings.h file. + + With a standard Arduino Pro Mini and SSD1306 display the current consumption was 20.25mA with the + display and 16.6mA without the display. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#include +#include +#include "Settings.h" +#include + +SX128XLT LT; + +#include //get library here > https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //use this line for 1.3" OLED often sold as 1.3" SSD1306 + +uint32_t RXpacketCount; //count of all packets received +uint32_t ValidPackets; //count of packets received with valid data +uint32_t RXpacketErrors; //count of all packets with errors received +bool packetisgood; + +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet + +uint8_t RXPacketType; +uint8_t RXDestination; +uint8_t RXSource; +float temperature; //the BME280 temperature value +float pressure; //the BME280 pressure value +uint16_t humidity; //the BME280 humididty value +uint16_t voltage; //the battery voltage value +uint8_t statusbyte; //a status byte, not currently used +uint16_t TXCRCvalue; //the CRC value of the packet data as transmitted + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout set + + digitalWrite(LED1, HIGH); //something has happened + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_Received_OK(); //its a valid packet LoRa wise, but it might not be a packet we want + } + + digitalWrite(LED1, LOW); + Serial.println(); +} + + +void packet_Received_OK() +{ + //a LoRa packet has been received, which has passed the internal LoRa checks, including CRC, but it could be from + //an unknown source, so we need to check that its actually a sensor packet we are expecting, and has valid sensor data + + uint8_t len; + uint8_t contenterrors; //keep a count of errors found in packet + + RXpacketCount++; + Serial.print(RXpacketCount); + Serial.print(F(",PacketsReceived,")); + + LT.startReadSXBuffer(0); + RXPacketType = LT.readUint8(); + RXDestination = LT.readUint8(); + RXSource = LT.readUint8(); + + /************************************************************************ + Highlighted section - this is where the actual sensor data is read from + the packet + ************************************************************************/ + temperature = LT.readFloat(); //the BME280 temperature value + pressure = LT.readFloat(); //the BME280 pressure value + humidity = LT.readUint16(); //the BME280 humididty value + voltage = LT.readUint16(); //the battery voltage value + statusbyte = LT.readUint8(); //a status byte, not currently used + /************************************************************************/ + + len = LT.endReadSXBuffer(); + + printreceptionDetails(); //print details of reception, RSSI etc + Serial.println(); + + contenterrors = checkPacketValid(len); //pass length of packet to check routine + + if (contenterrors == 0) + { + Serial.println(F(" Packet is good")); + ValidPackets++; + printSensorValues(); //print the sensor values + Serial.println(); + printPacketCounts(); //print count of valid packets and errors + displayscreen1(); + Serial.println(); + } + else + { + Serial.println(F(" Packet is not valid")); + RXpacketErrors++; + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + } +} + + +uint8_t checkPacketValid(uint8_t len) +{ + //this function checks if the packet is valid and will be displayed + + uint8_t errors = 0; + + if (RXPacketType != Sensor1) //is it a Sensor1 type packet + { + errors++; + } + + if (RXDestination != This_Node) //was the packet sent to this receiver node ? + { + errors++; + } + + if (!checkCRCvalue(len)) //is the sent CRC value of sensor data valid ? + { + errors++; + } + + Serial.println(); + Serial.print(F("Error Check Count = ")); + Serial.print(errors); + return errors; +} + + +bool checkCRCvalue(uint8_t len) +{ + uint16_t CRCSensorData; + + Serial.print(F("len = ")); + Serial.println(len); + + CRCSensorData = LT.CRCCCITTSX(3, (len-1), 0xFFFF); //calculate the CRC of packet sensor data + + Serial.print(F("(CRC of Received sensor data ")); + Serial.print(CRCSensorData, HEX); + Serial.print(F(")" )); + + TXCRCvalue = ((LT.getByteSXBuffer(len + 1) << 8) + (LT.getByteSXBuffer(len))); + + Serial.print(F("(CRC transmitted ")); + Serial.print(TXCRCvalue, HEX); + Serial.print(F(")" )); + + if (TXCRCvalue != CRCSensorData) + { + Serial.print(F(" Sensor Data Not Valid")); + return false; + } + else + { + Serial.print(F(" Sensor Data is Valid")); + return true; + } + +} + + +void printSensorValues() +{ + Serial.print(F("Temp,")); + Serial.print(temperature, 1); + Serial.print(F("c,Press,")); + Serial.print(pressure, 0); + Serial.print(F("Pa,Humidity,")); + Serial.print(humidity); + Serial.print(F("%,Voltage,")); + Serial.print(voltage); + Serial.print(F("mV,Status,")); + Serial.print(statusbyte, HEX); + Serial.print(F(",CRC,")); + Serial.print(TXCRCvalue, HEX); + Serial.flush(); +} + + +void printreceptionDetails() +{ + Serial.print(F("RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); +} + + +void printPacketCounts() +{ + Serial.print(F("ValidPackets,")); + Serial.print(ValidPackets); + Serial.print(F(",Errors,")); + Serial.print(RXpacketErrors); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + RXpacketErrors++; + IRQStatus = LT.readIrqStatus(); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout ")); + } + else + { + Serial.print(F("PacketError ")); + printreceptionDetails(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + Serial.println(); + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); + } +} + + +void displayscreen1() +{ + //show sensor data on display + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(F("Sensor ")); + disp.print(RXSource); + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(temperature, 1); + disp.print(F("c")); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(pressure, 0); + disp.print(F("Pa")); + disp.clearLine(3); + disp.setCursor(0, 3); + disp.print(humidity); + disp.print(F("%")); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(voltage); + disp.print(F("mV")); + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("ValidPkts ")); + disp.print(ValidPackets); + disp.setCursor(0, 7); + disp.print(F("Errors ")); + disp.print(RXpacketErrors); +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Check LoRa")); + disp.setCursor(0, 1); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + disp.print(F("LoRa OK")); + led_Flash(2, 125); + } + else + { + disp.print(F("Device error")); + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/18_Sensor_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/18_Sensor_Receiver/Settings.h new file mode 100644 index 0000000..9833711 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Sensor/18_Sensor_Receiver/Settings.h @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //pin for BUZZER, set to -1 if not used + +#define BATVREADON 8 //when high turns on the resistor divider to measure voltage, -1 if not used +#define BATTERYAD A7 //Resistor divider for battery connected here, -1 if not used +#define ADMultiplier 10.00 //adjustment to convert AD value read into mV of battery voltage +#define DIODEMV 98 //mV voltage drop accross diode @ low idle current + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + + +//*************** Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const uint8_t TXpower = 10; //Power for transmissions in dBm + +#define packet_delay 1000 //mS delay between packets +#define This_Node 'B' //this is the node that the remote sensors send data to + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino new file mode 100644 index 0000000..dbcef88 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel.ino @@ -0,0 +1,242 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program tests the sleep mode and register retention of the lora device in sleep + mode, it assumes an Atmel ATMega328P processor is in use. The LoRa settings to use are specified in the + 'Settings.h' file. + + A packet is sent, containing the text 'Before Device Sleep' and the LoRa device and Atmel processor are put + to sleep. The processor watchdog timer should wakeup the processor in 15 seconds (approx) and register + values should be retained. The device then attempts to transmit another packet 'After Device Sleep' + without re-loading all the LoRa settings. The receiver should see 'After Device Sleep' for the first + packet and 'After Device Sleep' for the second. + + Tested on a 'bare bones' ATmega328P board, the current in sleep mode was 12.2uA. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.0" + +#include //watchdog timer library, integral to Arduino IDE +#include +#include //get the library here; https://github.com/rocketscream/Low-Power + +#include +#include "Settings.h" + +SX128XLT LT; + +boolean SendOK; +int8_t TestPower; +uint8_t TXPacketL; + + +void loop() +{ + digitalWrite(LED1, HIGH); + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("TestPacket1> ")); + Serial.flush(); + + if (Send_Test_Packet1()) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + Serial.println(); + delay(packet_delay); + + LT.setSleep(CONFIGURATION_RETENTION); //preserve register settings in sleep. + Serial.println(F("Sleeping zzzzz....")); + Serial.println(); + Serial.flush(); + digitalWrite(LED1, LOW); + + sleep1second(15); //goto sleep for 15 seconds + + Serial.println(F("Awake !")); + Serial.flush(); + digitalWrite(LED1, HIGH); + LT.wake(); + + Serial.print(TXpower); + Serial.print(F("dBm ")); + Serial.print(F("TestPacket2> ")); + Serial.flush(); + + if (Send_Test_Packet2()) + { + packet_is_OK(); + } + else + { + packet_is_Error(); + } + Serial.println(); + delay(packet_delay); +} + + +void sleep1second(uint32_t sleeps) +{ + //uses the lowpower library + uint32_t index; + + for (index = 1; index <= sleeps; index++) + { + LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF); //sleep in 1 second steps + } +} + + +void packet_is_OK() +{ + Serial.print(F(" ")); + Serial.print(TXPacketL); + Serial.print(F(" Bytes SentOK")); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F("SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); //this leaves the LED on slightly longer for a packet error +} + + +bool Send_Test_Packet1() +{ + uint8_t bufffersize; + + uint8_t buff[] = "Before Device Sleep"; + TXPacketL = sizeof(buff); + buff[TXPacketL - 1] = '*'; + + if (sizeof(buff) > TXBUFFER_SIZE) //check that defined buffer is not larger than TX_BUFFER + { + bufffersize = TXBUFFER_SIZE; + } + else + { + bufffersize = sizeof(buff); + } + + TXPacketL = bufffersize; + + LT.printASCIIPacket( (uint8_t*) buff, bufffersize); + digitalWrite(LED1, HIGH); + + if (LT.transmit( (uint8_t*) buff, TXPacketL, 10000, TXpower, WAIT_TX)) + { + digitalWrite(LED1, LOW); + return true; + } + else + { + return false; + } +} + + +bool Send_Test_Packet2() +{ + uint8_t bufffersize; + + uint8_t buff[] = "After Device Sleep"; + TXPacketL = sizeof(buff); + buff[TXPacketL - 1] = '*'; + + if (sizeof(buff) > TXBUFFER_SIZE) //check that defined buffer is not larger than TX_BUFFER + { + bufffersize = TXBUFFER_SIZE; + } + else + { + bufffersize = sizeof(buff); + } + + TXPacketL = bufffersize; + + LT.printASCIIPacket( (uint8_t*) buff, bufffersize); + digitalWrite(LED1, HIGH); + + if (LT.transmit( (uint8_t*) buff, TXPacketL, 10000, TXpower, WAIT_TX)) + { + digitalWrite(LED1, LOW); + return true; + } + else + { + return false; + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(__TIME__); + Serial.print(F(" ")); + Serial.println(__DATE__); + Serial.println(F(Program_Version)); + Serial.println(); + Serial.println(F("5_LoRa_TX_Sleep_Timed_Wakeup_Atmel Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.print(F("Transmitter ready - TXBUFFER_SIZE ")); + Serial.println(TXBUFFER_SIZE); + Serial.println(); +} + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h new file mode 100644 index 0000000..7e0ffa2 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Sleep/5_LoRa_TX_Sleep_Timed_Wakeup_Atmel/Settings.h @@ -0,0 +1,40 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 29/02/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitions to match your own setup. Some pins such as DIO2, +//DIO3, BUZZER may not be in used by this sketch so they do not need to be +//connected and should be included and be set to -1. + +#define NSS 10 +#define RFBUSY 7 +#define NRESET 9 +#define LED1 8 +#define DIO1 3 +#define DIO2 -1 //not used +#define DIO3 -1 //not used +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define BUZZER -1 //pin for BUZZER, set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const uint8_t TXpower = 10; //Power for transmissions in dBm +const uint16_t packet_delay = 1000; //mS delay between packets + +#define TXBUFFER_SIZE 32 //RX buffer size + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/23_GPS_Tracker_Transmitter/23_GPS_Tracker_Transmitter.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/23_GPS_Tracker_Transmitter/23_GPS_Tracker_Transmitter.ino new file mode 100644 index 0000000..54d745d --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/23_GPS_Tracker_Transmitter/23_GPS_Tracker_Transmitter.ino @@ -0,0 +1,407 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 21/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is an example of a basic GPS tracker. The program reads the GPS, + waits for an updated fix and transmits location and altitude, number of satellites in view, the HDOP + value, the fix time of the GPS and the battery voltage. This transmitter can be also be used to + investigate GPS performance. At startup there should be a couple of seconds of recognisable text from + the GPS printed to the serial monitor. If you see garbage or funny characters its likley the GPS baud + rate is wrong. If the transmitter is turned on from cold, the receiver will pick up the cold fix time, + which is an indication of GPS performance. The GPS will be powered on for around 4 seconds before the + timing of the fix starts. Outside with a good view of the sky most GPSs should produce a fix in around + 45 seconds. The number of satellites and HDOP are good indications to how well a GPS is working. + + The program writes direct to the LoRa devices internal buffer, no memory buffer is used. + + The LoRa settings are configured in the Settings.h file. + + The program has the option of using a pin to control the power to the GPS (GPSPOWER), if the GPS module + or board being used has this feature. To not use this feature set the define for GPSPOWER in the + Settings.h file to '#define GPSPOWER -1'. Also set the GPSONSTATE and GPSOFFSTATE to the appropriate logic + levels. + + There is also an option of using a logic pin to turn the resistor divider used to read battery voltage on + and off. This reduces current used in sleep mode. To use the feature set the define for pin BATVREADON + in 'Settings.h' to the pin used. If not using the feature set the pin number to -1. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" +#define authorname "Stuart Robinson" + +#include +#include + +#include "Settings.h" +#include + +SX128XLT LT; + +#include //get library here > http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + + +#ifdef USE_SOFTSERIAL_GPS +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#else +#define GPSserial HardwareSerialPort //hardware serial port (eg Serial1) is configured in the Settings.h file +#endif + + +uint8_t TXStatus = 0; //used to store current status flag bits of Tracker transmitter (TX) +uint8_t TXPacketL; //length of LoRa packet (TX) +float TXLat; //Latitude from GPS on Tracker transmitter (TX) +float TXLon; //Longitude from GPS on Tracker transmitter (TX) +float TXAlt; //Altitude from GPS on Tracker transmitter (TX) +uint8_t TXSats; //number of GPS satellites seen (TX) +uint32_t TXHdop; //HDOP from GPS on Tracker transmitter (TX) +uint16_t TXVolts; //Volts (battery) level on Tracker transmitter (TX) +uint32_t TXGPSFixTime; //GPS fix time in hot fix mode of GPS on Tracker transmitter (TX) +uint32_t TXPacketCount, TXErrorsCount; //keep count of OK packets and send errors + + +void loop() +{ + + if (gpsWaitFix(WaitGPSFixSeconds)) + { + sendLocation(TXLat, TXLon, TXAlt, TXHdop, TXGPSFixTime); + Serial.println(); + Serial.print(F("Waiting ")); + Serial.print(Sleepsecs); + Serial.println(F("s")); + delay(Sleepsecs * 1000); //this sleep is used to set overall transmission cycle time + } + else + { + send_Command(NoFix); //send notification of no GPS fix. + } +} + + +bool gpsWaitFix(uint32_t waitSecs) +{ + //waits a specified number of seconds for a fix, returns true for good fix + uint32_t endwaitmS, GPSonTime; + bool GPSfix = false; + float tempfloat; + uint8_t GPSchar; + + GPSonTime = millis(); + GPSserial.begin(9600); //start GPSserial + + Serial.print(F("Wait GPS Fix ")); + Serial.print(waitSecs); + Serial.println(F("s")); + + endwaitmS = millis() + (waitSecs * 1000); + + while (millis() < endwaitmS) + { + if (GPSserial.available() > 0) + { + GPSchar = GPSserial.read(); + gps.encode(GPSchar); + } + + if (gps.location.isUpdated() && gps.altitude.isUpdated()) + { + GPSfix = true; + Serial.print(F("Have GPS Fix ")); + TXGPSFixTime = millis() - GPSonTime; + Serial.print(TXGPSFixTime); + Serial.println(F("mS")); + + TXLat = gps.location.lat(); + TXLon = gps.location.lng(); + TXAlt = gps.altitude.meters(); + TXSats = gps.satellites.value(); + TXHdop = gps.hdop.value(); + tempfloat = ( (float) TXHdop / 100); + + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F(",")); + Serial.print(TXSats); + Serial.print(F(",")); + Serial.print(tempfloat, 2); + Serial.println(); + + break; //exit while loop reading GPS + } + } + + //if here then there has either been a fix or no fix and a timeout + + if (GPSfix) + { + setStatusByte(GPSFix, 1); //set status bit to flag a GPS fix + } + else + { + setStatusByte(GPSFix, 0); //set status bit to flag no fix + Serial.println(); + Serial.println(F("Timeout - No GPSFix")); + Serial.println(); + GPSfix = false; + } + + GPSserial.end(); //serial RX interrupts interfere with SPI, so stop GPSserial + return GPSfix; +} + + +void sendLocation(float Lat, float Lon, float Alt, uint32_t Hdop, uint32_t fixtime) +{ + uint8_t len; + uint16_t IRQStatus; + + Serial.print(F("Send Location")); + + TXVolts = readSupplyVoltage(); //get the latest supply\battery volts + + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeUint8(LocationPacket); //indentify type of packet + LT.writeUint8(Broadcast); //who is the packet sent too + LT.writeUint8(ThisNode); //tells receiver where is packet from + LT.writeFloat(Lat); //add latitude + LT.writeFloat(Lon); //add longitude + LT.writeFloat(Alt); //add altitude + LT.writeUint8(TXSats); //add number of satellites + LT.writeUint32(Hdop); //add hdop + LT.writeUint8(TXStatus); //add tracker status + LT.writeUint32(fixtime); //add GPS fix time in mS + LT.writeUint16(TXVolts); //add tracker supply volts + LT.writeUint32(millis()); //add uptime in mS + len = LT.endWriteSXBuffer(); //close buffer write + + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); + digitalWrite(LED1, LOW); + + if (TXPacketL) + { + TXPacketCount++; + Serial.println(F(" - Done ")); + Serial.print(F("SentOK,")); + Serial.print(TXPacketCount); + Serial.print(F(",Errors,")); + Serial.println(TXErrorsCount); + } + else + { + //if here there was an error transmitting packet + TXErrorsCount++; + IRQStatus = LT.readIrqStatus(); //read the the interrupt register + Serial.print(F(" SendError,")); + Serial.print(F("Length,")); + Serial.print(TXPacketL); //print transmitted packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); //print IRQ status + LT.printIrqStatus(); //prints the text of which IRQs set + Serial.println(); + } +} + + +void setStatusByte(uint8_t bitnum, uint8_t bitval) +{ + //program the status byte + + if (bitval == 0) + { + bitClear(TXStatus, bitnum); + } + else + { + bitSet(TXStatus, bitnum); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + //flash LED to show tracker is alive + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void send_Command(char cmd) +{ + bool SendOK; + uint8_t len; + + Serial.print(F("Send Cmd ")); + Serial.write(cmd); + + LT.startWriteSXBuffer(0); + LT.writeUint8(cmd); //packet addressing used indentify type of packet + LT.writeUint8(Broadcast); //who is the packet sent to + LT.writeUint8(ThisNode); //where is packet from + LT.writeUint16(TXVolts); + len = LT.endWriteSXBuffer(); + + digitalWrite(LED1, HIGH); + SendOK = LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); //timeout set at 10 seconds + digitalWrite(LED1, LOW); + + if (SendOK) + { + Serial.println(F(" - Done")); + } + else + { + Serial.println(F(" - Error")); + } +} + + +uint16_t readSupplyVoltage() +{ + //relies on 1V internal reference and 91K & 11K resistor divider + //returns supply in mV @ 10mV per AD bit read + uint16_t temp; + uint16_t voltage = 0; + uint8_t index; + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, HIGH); //turn on MOSFET connecting resitor divider in circuit + } + + analogReference(INTERNAL); + temp = analogRead(SupplyAD); + + for (index = 0; index <= 4; index++) //sample AD 5 times + { + temp = analogRead(SupplyAD); + voltage = voltage + temp; + } + + if (BATVREADON >= 0) + { + digitalWrite(BATVREADON, LOW); //turn off MOSFET connecting resitor divider in circuit + } + + + voltage = ((voltage / 5) * ADMultiplier) + DIODEMV; + return voltage; +} + + +void GPSON() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void setup() +{ + uint32_t endmS; + + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + GPSON(); + } + + if (BATVREADON >= 0) + { + pinMode(BATVREADON, OUTPUT); + } + + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("23_GPS_Tracker_Transmitter Starting")); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + + TXVolts = readSupplyVoltage(); + + Serial.print(F("Supply ")); + Serial.print(TXVolts); + Serial.println(F("mV")); + + send_Command(PowerUp); //send power up command, includes supply mV + + Serial.println(F("Startup GPS check")); + + GPSserial.begin(9600); + + endmS = millis() + echomS; + + while (millis() < endmS) + { + while (GPSserial.available() > 0) + Serial.write(GPSserial.read()); + } + Serial.println(); + Serial.println(); + + Serial.println(F("Wait for first GPS fix")); + gpsWaitFix(WaitFirstGPSFixSeconds); + + sendLocation(TXLat, TXLon, TXAlt, TXHdop, TXGPSFixTime); +} diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h new file mode 100644 index 0000000..a599051 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/23_GPS_Tracker_Transmitter/Settings.h @@ -0,0 +1,67 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define RFBUSY 7 //SX126X busy pin +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used + +#define GPSPOWER 4 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define LED1 8 //On board LED, high for on +#define SupplyAD A7 //pin for reading supply\battery voltage +#define BATVREADON 8 //turns on battery resistor divider, high for on, -1 if not used + +const float ADMultiplier = 10.0; //multiplier for supply volts calculation +#define DIODEMV 98 //mV voltage drop accross diode at approx 8mA + + +#define LORA_DEVICE DEVICE_SX1280 //we need to define the device we are using + + + +//******* Setup LoRa Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0200; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +const uint8_t TXpower = 10; //Power for transmissions in dBm + +#define ThisNode '2' //a character that identifies this tracker + +//************************************************************************************************** +// GPS Settings +//************************************************************************************************** + +#define USE_SOFTSERIAL_GPS //need to include this if we are using softserial for GPS +//#define HardwareSerialPort Serial1 //if using hardware serial enable this define for hardware serial port + +#define GPSBaud 9600 //GPS Baud rate + +#define WaitGPSFixSeconds 30 //time in seconds to wait for a new GPS fix +#define WaitFirstGPSFixSeconds 1800 //time to seconds to wait for the first GPS fix at startup +#define Sleepsecs 5 //seconds between transmissions, this delay is used to set overall transmission cycle time + +#define echomS 2000 //number of mS to run GPS echo at startup + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino new file mode 100644 index 0000000..749a668 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/24_GPS_Tracker_Receiver/24_GPS_Tracker_Receiver.ino @@ -0,0 +1,322 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 22/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is an basic receiver for the '23_Simple_GPS_Tracker_Transmitter' program. + The program reads the received packet from the tracker transmitter and displays the results on + the serial monitor. The LoRa and frequency settings provided in the Settings.h file must + match those used by the transmitter. + + The program receives direct from the LoRa devices internal buffer. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +#define Program_Version "V1.1" + +#include +#include + +SX128XLT LT; + +#include "Settings.h" +#include + + +uint32_t RXpacketCount; //count of received packets + +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //RSSI of received packet +int8_t PacketSNR; //signal to noise ratio of received packet +uint8_t PacketType; //for packet addressing, identifies packet type +uint8_t Destination; //for packet addressing, identifies the destination (receiving) node +uint8_t Source; //for packet addressing, identifies the source (transmiting) node +uint8_t TXStatus; //A status byte +float TXLat; //latitude +float TXLon; //longitude +float TXAlt; //altitude +uint32_t TXHdop; //HDOP, indication of fix quality, horizontal dilution of precision, low is good +uint32_t TXGPSFixTime; //time in mS for fix +uint16_t TXVolts; //supply\battery voltage +uint8_t TXSats; //number of sattelites in use +uint32_t TXupTimemS; //up time of TX in mS + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort + + digitalWrite(LED1, HIGH); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + } + + Serial.println(); +} + + +void readPacketAddressing() +{ + //the transmitter is using packet addressing, so read in the details + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + LT.endReadSXBuffer(); +} + + +void packet_is_OK() +{ + float tempHdop; + + RXpacketCount++; + Serial.print(F("Packet OK > ")); + + readPacketAddressing(); + + if (PacketType == PowerUp) + { + LT.startReadSXBuffer(0); + LT.readUint8(); //read byte from FIFO, not used + LT.readUint8(); //read byte from FIFO, not used + LT.readUint8(); //read byte from FIFO, not used + TXVolts = LT.readUint16(); + LT.endReadSXBuffer(); + Serial.print(F("Tracker transmitter powerup - battery ")); + Serial.print(TXVolts); + Serial.print(F("mV")); + } + + + if (PacketType == LocationPacket) + { + //packet has been received, now read from the SX12XX FIFO in the correct order. + Serial.print(F("LocationPacket ")); + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readFloat(); + TXSats = LT.readUint8(); + TXHdop = LT.readUint32(); + TXStatus = LT.readUint8(); + TXGPSFixTime = LT.readUint32(); + TXVolts = LT.readUint16(); + TXupTimemS = LT.readUint32(); + RXPacketL = LT.endReadSXBuffer(); + + tempHdop = ( (float) TXHdop / 100); //need to convert Hdop read from GPS as uint32_t to a float for display + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F("m,")); + Serial.print(TXSats); + Serial.print(F(",")); + Serial.print(tempHdop, 2); + Serial.print(F(",")); + Serial.print(TXStatus); + Serial.print(F(",")); + Serial.print(TXGPSFixTime); + Serial.print(F("mS,")); + Serial.print(TXVolts); + Serial.print(F("mV,")); + Serial.print((TXupTimemS / 1000)); + Serial.print(F("s,")); + printpacketDetails(); + return; + } + + if (PacketType == LocationBinaryPacket) + { + //packet from locator has been received, now read from the SX12XX FIFO in the correct order. + Serial.print(F("LocationBinaryPacket ")); + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readInt16(); + TXStatus = LT.readUint8(); + RXPacketL = LT.endReadSXBuffer(); + + tempHdop = ( (float) TXHdop / 100); //need to convert Hdop read from GPS as uint32_t to a float for display + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 0); + Serial.print(F("m,")); + Serial.print(TXStatus); + printpacketDetails(); + return; + } + + if (PacketType == NoFix) + { + Serial.print(F("No Tracker GPS fix ")); + printpacketDetails(); + return; + } + +} + + +void printpacketDetails() +{ + uint16_t IRQStatus; + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Packets,")); + Serial.print(RXpacketCount); + + Serial.print(F(",Length,")); + Serial.print(RXPacketL); + IRQStatus = LT.readIrqStatus(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + Serial.print(F("PacketError,RSSI")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("24_GPS_Tracker_Receiver Starting")); + + if (BUZZER >= 0) + { + pinMode(BUZZER, OUTPUT); + Serial.println(F("BUZZER Enabled")); + } + else + { + Serial.println(F("BUZZER Not Enabled")); + } + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("LoRa device found")); + led_Flash(2, 125); + } + else + { + Serial.println(F("No device responding")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h new file mode 100644 index 0000000..b0cbef3 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/24_GPS_Tracker_Receiver/Settings.h @@ -0,0 +1,44 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 22/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define RFBUSY 7 //SX128X busy pin +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used + +#define LED1 8 //On board LED, high for on + +#define BUZZER -1 //Buzzer if fitted, high for on. Set to -1 if not used + +#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0200; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + + + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino new file mode 100644 index 0000000..f56259e --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/25_GPS_Tracker_Receiver_With_Display_and_GPS.ino @@ -0,0 +1,624 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 21/03/20 + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program is an example of a functional GPS tracker receiver using lora. + It is capable of picking up the trackers location packets from many kilometres away with only basic antennas. + + The program receives the location packets from the remote tracker transmitter and writes them on an OLED + display and also prints the information to the Arduino IDE serial monitor. The program can read a locally + attached GPS and when that has a fix, will display the distance and direction to the remote tracker. + + The program writes direct to the lora devices internal buffer, no memory buffer is used. The lora settings + are configured in the Settings.h file. + + The receiver recognises two types of tracker packet, the one from the matching program '23_GPS_Tracker_Transmitter' + (LocationPacket, 27 bytes) which causes these fields to be printed to the serial monitor; + + Latitude, Longitude, Altitude, Satellites, HDOP, TrackerStatusByte, GPS Fixtime, Battery mV, Distance, Direction, + Distance, Direction, PacketRSSI, PacketSNR, NumberPackets, PacketLength, IRQRegister. + + This is a long packet which at the long range LoRa settings takes just over 3 seconds to transmit. + + The receiver also recognises a much shorter location only packet (LocationBinaryPacket, 11 bytes) and when + received this is printed to the serial monitor; + + Latitude, Longitude, Altitude, TrackerStatusByte, Distance, Direction, PacketRSSI, PacketSNR, NumberPackets, + PacketLength, IRQRegister. + + Most of the tracker information (for both types of packet) is shown on the OLED display. If there has been a + tracker transmitter GPS fix the number\identifier of that tracker is shown on row 0 right of screen and if there + is a recent local (receiver) GPS fix an 'R' is displayed row 1 right of screen. + + When the tracker transmitter starts up or is reset its sends a power up message containing the battery voltage + which is shown on the OLED and printer to the serial monitor. + + The program has the option of using a pin to control the power to the GPS, if the GPS module being used has this + feature. To use the option change the define in Settings.h; + + '#define GPSPOWER -1' from -1 to the pin number being used. Also set the GPSONSTATE and GPSOFFSTATE defines to + the appropriate logic levels. + + The program by default uses software serial to read the GPS, you can use hardware serial by commenting out this + line in the Settings.h file; + + #define USE_SOFTSERIAL_GPS + + And then defining the hardware serial port you are using, which defaults to Serial1. + + Serial monitor baud rate is set at 9600. +*******************************************************************************************************/ + + +#define Program_Version "V1.1" + +#include +#include +SX128XLT LT; + +#include "Settings.h" +#include + +#include //https://github.com/olikraus/u8g2 +U8X8_SSD1306_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //standard 0.96" SSD1306 +//U8X8_SH1106_128X64_NONAME_HW_I2C disp(U8X8_PIN_NONE); //1.3" OLED often sold as 1.3" SSD1306 + + +#include //http://arduiniana.org/libraries/tinygpsplus/ +TinyGPSPlus gps; //create the TinyGPS++ object + +#ifdef USE_SOFTSERIAL_GPS +#include +SoftwareSerial GPSserial(RXpin, TXpin); +#else +#define GPSserial HardwareSerialPort //hardware serial port (eg Serial1) is configured in the Settings.h file +#endif + +uint32_t RXpacketCount; //count of received packets +uint8_t RXPacketL; //length of received packet +int8_t PacketRSSI; //signal strength (RSSI) dBm of received packet +int8_t PacketSNR; //signal to noise ratio (SNR) dB of received packet +uint8_t PacketType; //for packet addressing, identifies packet type +uint8_t Destination; //for packet addressing, identifies the destination (receiving) node +uint8_t Source; //for packet addressing, identifies the source (transmiting) node +uint8_t TXStatus; //status byte from tracker transmitter +uint8_t TXSats; //number of sattelites in use +float TXLat; //latitude +float TXLon; //longitude +float TXAlt; //altitude +float RXLat; //latitude +float RXLon; //longitude +float RXAlt; //altitude +uint32_t TXHdop; //HDOP, indication of fix quality, horizontal dilution of precision, low is good +uint32_t TXGPSFixTime; //time in mS for fix +uint16_t TXVolts; //supply\battery voltage +uint16_t RXVolts; //supply\battery voltage +float TXdistance; //calculated distance to tracker +uint16_t TXdirection; //calculated direction to tracker +uint16_t RXerrors; +uint32_t TXupTimemS; //up time of TX in mS + +uint32_t LastRXGPSfixCheck; //used to record the time of the last GPS fix + +bool TXLocation = false; //set to true when at least one tracker location packet has been received +bool RXGPSfix = false; //set to true if the local GPS has a recent fix + +uint8_t FixCount = DisplayRate; //used to keep track of number of GPS fixes before display updated + + +void loop() +{ + RXPacketL = LT.receiveSXBuffer(0, 0, NO_WAIT); //returns 0 if packet error of some sort + + while (!digitalRead(DIO1)) + { + readGPS(); //If the DIO pin is low, no packet arrived, so read the GPS + } + + //something has happened in receiver + digitalWrite(LED1, HIGH); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, HIGH); + } + + RXPacketL = LT.readRXPacketL(); + PacketRSSI = LT.readPacketRSSI(); + PacketSNR = LT.readPacketSNR(); + + + if (RXPacketL == 0) + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + digitalWrite(LED1, LOW); + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); + } + Serial.println(); +} + + +void readGPS() +{ + if (GPSserial.available() > 0) + { + gps.encode(GPSserial.read()); + } + + + if ( millis() > (LastRXGPSfixCheck + NoRXGPSfixms)) + { + RXGPSfix = false; + LastRXGPSfixCheck = millis(); + dispscreen1(); + } + + + if (gps.location.isUpdated() && gps.altitude.isUpdated()) + { + RXGPSfix = true; + RXLat = gps.location.lat(); + RXLon = gps.location.lng(); + RXAlt = gps.altitude.meters(); + printRXLocation(); + LastRXGPSfixCheck = millis(); + + if ( FixCount == 1) //update screen when FIXcoount counts down from DisplayRate to 1 + { + FixCount = DisplayRate; + dispscreen1(); + } + FixCount--; + } +} + + +bool readTXStatus(byte bitnum) +{ + return bitRead(TXStatus, bitnum); +} + + +void printRXLocation() +{ + Serial.print(F("LocalGPS ")); + Serial.print(RXLat, 5); + Serial.print(F(",")); + Serial.print(RXLon, 5); + Serial.print(F(",")); + Serial.print(RXAlt, 1); + Serial.println(); +} + + +void readPacketAddressing() +{ + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + LT.endReadSXBuffer(); +} + + +void packet_is_OK() +{ + //uint16_t IRQStatus; + float tempfloat; + + RXpacketCount++; + + readPacketAddressing(); + + if (PacketType == PowerUp) + { + LT.startReadSXBuffer(0); + LT.readUint8(); //read byte from SXBuffer, not used + LT.readUint8(); //read byte from SXBuffer, not used + LT.readUint8(); //read byte from SXBuffer, not used + TXVolts = LT.readUint16(); //read tracker transmitter voltage + LT.endReadSXBuffer(); + Serial.print(F("Tracker Powerup - Battery ")); + Serial.print(TXVolts); + Serial.println(F("mV")); + dispscreen2(); + } + + if (PacketType == LocationPacket) + { + //packet has been received, now read from the SX12XX FIFO in the correct order. + Serial.print(F("LocationPacket ")); + TXLocation = true; + LT.startReadSXBuffer(0); //start the read of received packet + PacketType = LT.readUint8(); //read in the PacketType + Destination = LT.readUint8(); //read in the Packet destination address + Source = LT.readUint8(); //read in the Packet source address + TXLat = LT.readFloat(); //read in the tracker latitude + TXLon = LT.readFloat(); //read in the tracker longitude + TXAlt = LT.readFloat(); //read in the tracker altitude + TXSats = LT.readUint8(); //read in the satellites in use by tracker GPS + TXHdop = LT.readUint32(); //read in the HDOP of tracker GPS + TXStatus = LT.readUint8(); //read in the tracker status byte + TXGPSFixTime = LT.readUint32(); //read in the last fix time of tracker GPS + TXVolts = LT.readUint16(); //read in the tracker supply\battery volts + TXupTimemS = LT.readUint32(); //read in the TX uptime in mS + RXPacketL = LT.endReadSXBuffer(); //end the read of received packet + + + if (RXGPSfix) //if there has been a local GPS fix do the distance and direction calculation + { + TXdirection = (int16_t) TinyGPSPlus::courseTo(RXLat, RXLon, TXLat, TXLon); + TXdistance = TinyGPSPlus::distanceBetween(RXLat, RXLon, TXLat, TXLon); + } + else + { + TXdistance = 0; + TXdirection = 0; + } + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 1); + Serial.print(F(",")); + Serial.print(TXSats); + Serial.print(F(",")); + + tempfloat = ( (float) TXHdop / 100); //need to convert Hdop read from GPS as uint32_t to a float for display + Serial.print(tempfloat, 2); + + Serial.print(F(",")); + Serial.print(TXStatus); + Serial.print(F(",")); + + Serial.print(TXGPSFixTime); + Serial.print(F("mS,")); + Serial.print(TXVolts); + Serial.print(F("mV,")); + Serial.print((TXupTimemS / 1000)); + Serial.print(F("s,")); + + Serial.print(TXdistance, 0); + Serial.print(F("m,")); + Serial.print(TXdirection); + Serial.print(F("d")); + printpacketDetails(); + dispscreen1(); //and show the packet detail it on screen + return; + } + + + if (PacketType == LocationBinaryPacket) + { + //packet from locator has been received, now read from the SX12XX FIFO in the correct order. + TXLocation = true; + Serial.print(F("LocationBinaryPacket ")); + LT.startReadSXBuffer(0); + PacketType = LT.readUint8(); + Destination = LT.readUint8(); + Source = LT.readUint8(); + TXLat = LT.readFloat(); + TXLon = LT.readFloat(); + TXAlt = LT.readInt16(); + TXStatus = LT.readUint8(); + RXPacketL = LT.endReadSXBuffer(); + + if (RXGPSfix) //if there has been a local GPS fix do the distance and direction calculation + { + TXdirection = (int16_t) TinyGPSPlus::courseTo(RXLat, RXLon, TXLat, TXLon); + TXdistance = TinyGPSPlus::distanceBetween(RXLat, RXLon, TXLat, TXLon); + } + else + { + TXdistance = 0; + TXdirection = 0; + } + + Serial.write(PacketType); + Serial.write(Destination); + Serial.write(Source); + Serial.print(F(",")); + Serial.print(TXLat, 5); + Serial.print(F(",")); + Serial.print(TXLon, 5); + Serial.print(F(",")); + Serial.print(TXAlt, 0); + Serial.print(F("m,")); + Serial.print(TXStatus); + Serial.print(F(",")); + Serial.print(TXdistance, 0); + Serial.print(F("m,")); + Serial.print(TXdirection); + Serial.print(F("d")); + printpacketDetails(); + dispscreen1(); + return; + } +} + + +void printpacketDetails() +{ + uint16_t IRQStatus; + Serial.print(F(",RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Packets,")); + Serial.print(RXpacketCount); + + Serial.print(F(",Length,")); + Serial.print(RXPacketL); + IRQStatus = LT.readIrqStatus(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } + + IRQStatus = LT.readIrqStatus(); //get the IRQ status + RXerrors++; + Serial.print(F("PacketError,RSSI")); + + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); //get the real packet length + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + digitalWrite(LED1, LOW); + + if (BUZZER >= 0) + { + digitalWrite(BUZZER, LOW); + delay(100); + digitalWrite(BUZZER, HIGH); + } +} + + +void led_Flash(uint16_t flashes, uint16_t delaymS) +{ + unsigned int index; + + for (index = 1; index <= flashes; index++) + { + digitalWrite(LED1, HIGH); + delay(delaymS); + digitalWrite(LED1, LOW); + delay(delaymS); + } +} + + +void dispscreen1() +{ + //show received packet data on display + float tempfloat; + disp.clearLine(0); + disp.setCursor(0, 0); + disp.print(TXLat, 5); + disp.clearLine(1); + disp.setCursor(0, 1); + disp.print(TXLon, 5); + disp.clearLine(2); + disp.setCursor(0, 2); + disp.print(TXAlt, 0); + disp.print(F("m")); + disp.clearLine(3); + disp.setCursor(0, 3); + + disp.print(F("RSSI ")); + disp.print(PacketRSSI); + disp.print(F("dBm")); + disp.clearLine(4); + disp.setCursor(0, 4); + disp.print(F("SNR ")); + + if (PacketSNR > 0) + { + disp.print(F("+")); + } + + if (PacketSNR == 0) + { + disp.print(F(" ")); + } + + if (PacketSNR < 0) + { + disp.print(F("-")); + } + + disp.print(PacketSNR); + disp.print(F("dB")); + + if (PacketType == LocationPacket) + { + disp.clearLine(5); + disp.setCursor(0, 5); + tempfloat = ((float) TXVolts / 1000); + disp.print(F("Batt ")); + disp.print(tempfloat, 2); + disp.print(F("v")); + } + + disp.clearLine(6); + disp.setCursor(0, 6); + disp.print(F("Packets ")); + disp.print(RXpacketCount); + + disp.clearLine(7); + + if (RXGPSfix) + { + disp.setCursor(15, 1); + disp.print(F("R")); + } + else + { + disp.setCursor(15, 1); + disp.print(F(" ")); + disp.setCursor(0, 7); + disp.print(F("No Local Fix")); + } + + if (RXGPSfix && TXLocation) //only display distance and direction if have received tracker packet and have local GPS fix + { + disp.clearLine(7); + disp.setCursor(0, 7); + disp.print(TXdistance, 0); + disp.print(F("m ")); + disp.print(TXdirection); + disp.print(F("d")); + } + + if (readTXStatus(GPSFix)) + { + disp.setCursor(15, 0); + disp.write(Source); + } + +} + + +void dispscreen2() +{ + //show tracker powerup data on display + float tempfloat; + disp.clear(); + disp.setCursor(0, 0); + disp.print(F("Tracker Powerup")); + disp.setCursor(0, 1); + disp.print(F("Battery ")); + tempfloat = ((float) TXVolts / 1000); + disp.print(tempfloat, 2); + disp.print(F("v")); +} + + +void GPSON() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSONSTATE); //power up GPS + } +} + + +void GPSOFF() +{ + if (GPSPOWER >= 0) + { + digitalWrite(GPSPOWER, GPSOFFSTATE); //power off GPS + } +} + + +void setup() +{ + uint32_t endmS; + + pinMode(LED1, OUTPUT); //setup pin as output for indicator LED + led_Flash(2, 125); //two quick LED flashes to indicate program start + + Serial.begin(9600); + Serial.println(); + Serial.print(F(__TIME__)); + Serial.print(F(" ")); + Serial.println(F(__DATE__)); + Serial.println(F(Program_Version)); + Serial.println(); + + Serial.println(F("25_GPS_Tracker_Receiver_With_Display_and_GPS Starting")); + + if (BUZZER >= 0) + { + pinMode(BUZZER, OUTPUT); + } + + SPI.begin(); + + disp.begin(); + disp.setFont(u8x8_font_chroma48medium8_r); + + Serial.print(F("Checking LoRa device - ")); //Initialize LoRa + disp.setCursor(0, 0); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, RX_EN, TX_EN, LORA_DEVICE)) + { + Serial.println(F("Receiver ready")); + disp.print(F("Receiver ready")); + led_Flash(2, 125); + delay(1000); + } + else + { + Serial.println(F("No LoRa device responding")); + disp.print(F("No LoRa device")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + Serial.println(); + Serial.println(F("Startup GPS check")); + + endmS = millis() + echomS; + + //now startup GPS + if (GPSPOWER >= 0) + { + pinMode(GPSPOWER, OUTPUT); + } + + GPSON(); + GPSserial.begin(GPSBaud); + + while (millis() < endmS) + { + while (GPSserial.available() > 0) + Serial.write(GPSserial.read()); + } + Serial.println(); + Serial.println(); + + Serial.println(F("Receiver ready")); + Serial.println(); +} + + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h new file mode 100644 index 0000000..fc36be7 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/25_GPS_Tracker_Receiver_With_Display_and_GPS/Settings.h @@ -0,0 +1,63 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 16/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. Some pins such as DIO1, +//DIO2, BUZZER SWITCH1 may not be in used by this sketch so they do not need to be +//connected and should be set to -1. + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define RFBUSY 7 //SX128X busy pin +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define RX_EN -1 //pin for RX enable, used on some SX1280 devices, set to -1 if not used +#define TX_EN -1 //pin for TX enable, used on some SX1280 devices, set to -1 if not used +#define LED1 8 //On board LED, high for on + +#define BUZZER -1 //Buzzer if fitted, high for on. Set to -1 if not used + +#define RXpin A3 //pin number for GPS RX input into Arduino - TX from GPS +#define TXpin A2 //pin number for GPS TX output from Arduino- RX into GPS + +#define GPSPOWER 4 //Pin that controls power to GPS, set to -1 if not used +#define GPSONSTATE HIGH //logic level to turn GPS on via pin GPSPOWER +#define GPSOFFSTATE LOW //logic level to turn GPS off via pin GPSPOWER + +#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0200; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +//************************************************************************************************** +// GPS Settings +//************************************************************************************************** + +#define USE_SOFTSERIAL_GPS //need to include this if we are using softserial for GPS +//#define HardwareSerialPort Serial1 //if using hardware serial enable this define for hardware serial port + +#define GPSBaud 9600 //GPS Baud rate +#define WaitGPSFixSeconds 30 //time to wait for a new GPS fix +#define echomS 2000 //number of mS to run GPS echo for at startup + +#define NoRXGPSfixms 15000 //max number of mS to allow before no local fix flagged +#define DisplayRate 7 //when working OK the GPS will get a new fix every second or so + //this rate defines how often the display should be updated + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino new file mode 100644 index 0000000..0a72f77 --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/38_lora_Relay/38_lora_Relay.ino @@ -0,0 +1,173 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + +/******************************************************************************************************* + Program Operation - This program will receive a lora packet and relay (re-transmit) it. The receiving + and transmitting can use different frequencies and lora settings, although in this example they are + the same. The receiving and transmitting settings are in the 'Settings.h' file. If the relay is located + in an advantageous position, for instance on top of a tall tree, building or in an radio controlled model + then the range at which trackers or nodes on the ground can be received is considerably increased. + In these circumstances the relay may listen at a long range setting using SF12 for example and then + re-transmit back to the ground at SF7. + + For an example of the use of such a program see this report; + + How to Search 500 Square Kilometres in 10 minutes.pdf in the libraries 'Test_Reports' folder. + + Serial monitor baud rate is set at 9600. + +*******************************************************************************************************/ + + +#include +#include +#include "Settings.h" + +SX128XLT LT; + +uint8_t RXPacketL, TXPacketL; +int8_t PacketRSSI, PacketSNR; +uint16_t RXPacketErrors; + + +void loop() +{ + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + + RXPacketL = LT.receiveSXBuffer(0, 0, WAIT_RX); //returns 0 if packet error of some sort, no timeout set + + digitalWrite(LED1, HIGH); //something has happened + + if (BUZZER > 0) //turn buzzer on + { + digitalWrite(BUZZER, HIGH); + } + + PacketRSSI = LT.readPacketRSSI(); //read the recived RSSI value + PacketSNR = LT.readPacketSNR(); //read the received SNR value + + if (RXPacketL == 0) //if the LT.receive() function detects an error, RXpacketL == 0 + { + packet_is_Error(); + } + else + { + packet_is_OK(); + } + + if (BUZZER > 0) + { + digitalWrite(BUZZER, LOW); //buzzer off + } + + Serial.println(); +} + + +void packet_is_OK() +{ + //a packet has been received, so change to relay settings and transmit buffer + + Serial.print(F("PacketOK ")); + printreceptionDetails(); + delay(packet_delay / 2); + digitalWrite(LED1, LOW); + delay(packet_delay / 2); + + Serial.print(F(" Retransmit")); + LT.setupLoRa(RelayFrequency, RelayOffset, RelaySpreadingFactor, RelayBandwidth, RelayCodeRate); + digitalWrite(LED1, HIGH); + TXPacketL = LT.transmitSXBuffer(0, RXPacketL, 10000, TXpower, WAIT_TX); + Serial.print(F(" - Done")); + digitalWrite(LED1, LOW); +} + + +void packet_is_Error() +{ + uint16_t IRQStatus; + + RXPacketErrors++; + IRQStatus = LT.readIrqStatus(); + + led_Flash(5, 50); + + if (IRQStatus & IRQ_RX_TIMEOUT) + { + Serial.print(F("RXTimeout ")); + } + else + { + Serial.print(F("PacketError ")); + printreceptionDetails(); + Serial.print(F(",IRQreg,")); + Serial.print(IRQStatus, HEX); + LT.printIrqStatus(); + } +} + + +void printreceptionDetails() +{ + Serial.print(F("RSSI,")); + Serial.print(PacketRSSI); + Serial.print(F("dBm,SNR,")); + Serial.print(PacketSNR); + Serial.print(F("dB,Length,")); + Serial.print(LT.readRXPacketL()); +} + + +void led_Flash(uint16_t flashdelay, uint16_t flashes) +{ + uint16_t index; + + for (index = 1; index <= flashes; index++) + { + + delay(flashdelay); + digitalWrite(LED1, HIGH); + delay(flashdelay); + digitalWrite(LED1, LOW); + } +} + + +void setup() +{ + pinMode(LED1, OUTPUT); + led_Flash(2, 125); + + Serial.begin(9600); + + SPI.begin(); + + if (LT.begin(NSS, NRESET, RFBUSY, DIO1, LORA_DEVICE)) + { + led_Flash(2, 125); + } + else + { + Serial.println(F("Device error")); + while (1) + { + led_Flash(50, 50); //long fast speed flash indicates device error + } + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate); + Serial.print("ListenSettings,"); + LT.printModemSettings(); + Serial.println(); + LT.setupLoRa(RelayFrequency, RelayOffset, RelaySpreadingFactor, RelayBandwidth, RelayCodeRate); + Serial.print("RelaySettings,"); + LT.printModemSettings(); + Serial.println(); + Serial.println("Relay Ready"); +} + + diff --git a/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/38_lora_Relay/Settings.h b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/38_lora_Relay/Settings.h new file mode 100644 index 0000000..a84a0ff --- /dev/null +++ b/lib/SX12XX-LoRa/examples/SX128x_examples/Tracker/38_lora_Relay/Settings.h @@ -0,0 +1,46 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 19/03/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +//******* Setup hardware pin definitions here ! *************** + +//These are the pin definitions for one of my own boards, the Easy Pro Mini, +//be sure to change the definitiosn to match your own setup. + +#define NSS 10 //select on LoRa device +#define NRESET 9 //reset on LoRa device +#define RFBUSY 7 //SX128X busy pin +#define DIO1 3 //DIO1 on LoRa device, used for RX and TX done +#define SW -1 //SW pin on Dorji devices is used to turn RF switch on\off, set to -1 if not used +#define LED1 8 //On board LED, high for on +#define BUZZER -1 //normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1280 //this is the device we are using + + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 2445000000; //frequency of transmissions +const int32_t Offset = 0; //offset frequency for calibration purposes +const uint8_t Bandwidth = LORA_BW_0200; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_5; //LoRa coding rate + +//LoRa relay (re-transmitting) parameters +const uint32_t RelayFrequency = 2445000000; //frequency of transmissions +const uint32_t RelayOffset = 0; //offset frequency for calibration purposes + +const uint8_t RelayBandwidth = LORA_BW_0400; //LoRa bandwidth +const uint8_t RelaySpreadingFactor = LORA_SF7; //LoRa spreading factor +const uint8_t RelayCodeRate = LORA_CR_4_5; //LoRa coding rate + + +const int8_t TXpower = 10; //LoRa TX power in dBm + +#define packet_delay 1000 //mS delay before received packet transmitted + diff --git a/lib/SX12XX-LoRa/keywords.txt b/lib/SX12XX-LoRa/keywords.txt new file mode 100644 index 0000000..7ecf5d5 --- /dev/null +++ b/lib/SX12XX-LoRa/keywords.txt @@ -0,0 +1,191 @@ +####################################### +# Syntax Coloring Map For SX12XXXLT +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +SX126XLT KEYWORD1 +SX127XLT KEYWORD1 +SX128XLT KEYWORD1 +ProgramLT_Definitions KEYWORD1 +SX127XLT_Definitions KEYWORD1 +SX126XLT_Definitions KEYWORD1 +SX128XLT_Definitions KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +pinInit KEYWORD2 +spiInit KEYWORD2 +setupLoRa KEYWORD2 +resetDevice KEYWORD2 +setMode KEYWORD2 +checkBusy KEYWORD2 +setSleep KEYWORD2 +setRegulatorMode KEYWORD2 +checkDevice KEYWORD2 +setDevice KEYWORD2 +config KEYWORD2 +readsavedRegulatorMode KEYWORD2 +setDIO2AsRfSwitchCtrl KEYWORD2 +setDIO3AsTCXOCtrl KEYWORD2 +clearDeviceErrors KEYWORD2 +calibrateDevice KEYWORD2 +printDeviceErrors KEYWORD2 +wake KEYWORD2 +calibrateImage KEYWORD2 +CRCCCITTSX KEYWORD2 +CRCCCITT KEYWORD2 +printSXBufferASCII KEYWORD2 +receiveAddressed KEYWORD2 +getByteSXBuffer KEYWORD2 +startWriteSXBuffer KEYWORD2 +transmitAddressed KEYWORD2 +startReadSXBuffer KEYWORD2 +endReadSXBuffer KEYWORD2 +readBuffer KEYWORD2 +receiveSXBuffer KEYWORD2 +transmitSXBuffer KEYWORD2 +endWriteSXBuffer KEYWORD2 +writeBuffer KEYWORD2 +readPacket KEYWORD2 +setSleep KEYBOARD2 +getOperatingMode KEYWORD2 +writeCommand KEYWORD2 +readCommand KEYWORD2 +writeRegisters KEYWORD2 +writeRegister KEYWORD2 +readRegisters KEYWORD2 +readRegister KEYWORD2 +printRegisters KEYWORD2 +setPacketParams KEYWORD2 +setModulationParams KEYWORD2 +printSavedModulationParams KEYWORD2 +setRfFrequency KEYWORD2 +getFreqInt KEYWORD2 +getFrequencyErrorRegValue KEYWORD2 +getFrequencyErrorHz KEYWORD2 +setTxParams KEYWORD2 +setTx KEYWORD2 +readTXDone KEYWORD2 +setRx KEYWORD2 +setLowPowerRX KEYWORD2 +setHighSensitivity KEYWORD2 +setRXGain KEYWORD2 +readRXDone KEYWORD2 +readRXBufferStatus KEYWORD2 +readRXPacketL KEYWORD2 +readPacketRSSI KEYWORD2 +readPacketSNR KEYWORD2 +setPaConfig KEYWORD2 +setRx2 KEYWORD2 +setRxDutyCycle KEYWORD2 +readRXBufferPointer KEYWORD2 +readsavedFrequency KEYWORD2 +readsavedOffset KEYWORD2 +setBufferBaseAddress KEYWORD2 +setPacketType KEYWORD2 +readsavedPacketType KEYWORD2 +clearIrqStatus KEYWORD2 +readIrqStatus KEYWORD2 +setDioIrqParams KEYWORD2 +printIrqStatus KEYWORD2 +readPacketCRCError KEYWORD2 +readPacketHeaderValid KEYWORD2 +readPacketHeaderError KEYWORD2 +readsavedPower KEYWORD2 +printASCIIorHEX KEYWORD2 +printHEXByte KEYWORD2 +printHEXByte0x KEYWORD2 +printASCIIPacket KEYWORD2 +printHEXPacket KEYWORD2 +readsavedModParam1 KEYWORD2 +readsavedModParam2 KEYWORD2 +readsavedModParam3 KEYWORD2 +readsavedModParam4 KEYWORD2 +readRXPacketType KEYWORD2 +readRXDestination KEYWORD2 +readRXSource KEYWORD2 +printAddressInfo KEYWORD2 +returnSF KEYWORD2 +returnbandwidth KEYWORD2 +readPacketAddressedLoRa KEYWORD2 +getOptimisation KEYWORD2 +calcSymbolTime KEYWORD2 +printModemSettings KEYWORD2 +printOperatingSettings KEYWORD2 +setSyncWord KEYWORD2 +startWriteFIFO KEYWORD2 +endWriteFIFO KEYWORD2 +startReadFIFO KEYWORD2 +endReadFIFO KEYWORD2 +writeUint8 KEYWORD2 +readUint8 KEYWORD2 +writeInt8 KEYWORD2 +readInt8 KEYWORD2 +writeInt16 KEYWORD2 +readInt16 KEYWORD2 +writeUint16 KEYWORD2 +readUint16 KEYWORD2 +writeInt32 KEYWORD2 +readInt32 KEYWORD2 +writeUint32 KEYWORD2 +readUint32 KEYWORD2 +writeFloat KEYWORD2 +readFloat KEYWORD2 +rxtxInit KEYWORD2 +rxEnable KEYWORD2 +txEnable KEYWORD2 +getLoRaBandwidth KEYWORD2 +setRangingRangingAddress KEYWORD2 +setRangingRequestAddress KEYWORD2 +setRangingRole KEYWORD2 +setRangingCalibration KEYWORD2 +getRangingResult KEYWORD2 +complement2 KEYWORD2 +getRangingResultRegValue KEYWORD2 +setupRanging KEYWORD2 +transmitRanging KEYWORD2 +receiveRanging KEYWORD2 +getRangingDistance KEYWORD2 +setRangingMasterAddress KEYWORD2 +setRangingSlaveAddress KEYWORD2 +getRangingCalibrationValue KEYWORD2 +toneFM KEYWORD2 +setupDirect KEYWORD2 +printDevice KEYWORD2 +getLoRaSF KEYWORD2 +getLoRaCodingRate KEYWORD2 +getSyncWord KEYWORD2 +getInvertIQ KEYWORD2 +getPreamble KEYWORD2 +printOperatingMode KEYWORD2 +getVersion KEYWORD2 +getPacketMode KEYWORD2 +getHeaderMode KEYWORD2 +getCRCMode KEYWORD2 +getAGC KEYWORD2 +getLNAgain KEYWORD2 +getLNAboostHF KEYWORD2 +getLNAboostLF KEYWORD2 +getOpmode KEYWORD2 +fillSXBuffer KEYWORD2 +writeByteSXBuffer KEYWORD2 +doAFC KEYWORD2 +getOffset KEYWORD2 +startFSKRTTY KEYWORD2 +transmitFSKRTTY KEYWORD2 +printRTTYregisters KEYWORD2 +endFSKRTTY KEYWORD2 +printSXBufferHEX KEYWORD2 +readBufferChar KEYWORD2 +writeBufferChar KEYWORD2 +setLowPowerReceive KEYWORD2 +doAFCPPM KEYWORD2 +setTXDirect KEYWORD2 + + diff --git a/lib/SX12XX-LoRa/library.properties b/lib/SX12XX-LoRa/library.properties new file mode 100644 index 0000000..d78e4eb --- /dev/null +++ b/lib/SX12XX-LoRa/library.properties @@ -0,0 +1,9 @@ +name=SX12XXLT LoRa library +version=1.0.0 +author=Stuart Robinson +maintainer=sorry@no.spam +sentence=Arduino library for SX12xx LoRa device +paragraph=Supports SX1261,SX1262,SX1268,SX1272,SX1276,SX1277,SX1278,SX1279,SX1280,SX1281 devices +category=Communication +url=https://github.com/StuartsProjects/SX12XX-LoRa +architectures=* diff --git a/lib/SX12XX-LoRa/pictures/Atmel Watchdog Sleep Time.jpg b/lib/SX12XX-LoRa/pictures/Atmel Watchdog Sleep Time.jpg new file mode 100644 index 0000000000000000000000000000000000000000..04ec92d9cec8c863014fa405582dd8e1a74ca8e9 GIT binary patch literal 43600 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 literal 0 HcmV?d00001 diff --git a/lib/SX12XX-LoRa/pictures/ESP32_Bare_Bones_circuit.pdf b/lib/SX12XX-LoRa/pictures/ESP32_Bare_Bones_circuit.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d307b14978b4d0d4666a2fccea3e770e37a00114 GIT binary patch literal 34095 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 rKRl&>KCwza*qwhMK8*jORs9 0) + { + pinMode(audiopin, OUTPUT); + } + + if (checkpin > 0) + { + pinMode(checkpin, OUTPUT); + } + + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < leadinmS) //allows for millis() overflow + { + digitalWrite(audiopin, HIGH); + delayMicroseconds(_highperioduS); + digitalWrite(audiopin, LOW); + delayMicroseconds(_highperioduS); + } +} + + +void endAFSKRTTY(int8_t audiopin, int8_t checkpin, uint16_t leadoutmS) +{ + uint32_t startmS; + + startmS = millis(); + + while ( (uint32_t) (millis() - startmS) < leadoutmS) //allows for millis() overflow + { + digitalWrite(audiopin, HIGH); + delayMicroseconds(_highperioduS); + digitalWrite(audiopin, LOW); + delayMicroseconds(_highperioduS); + } + + if (audiopin > 0) + { + pinMode(audiopin, INPUT); + } + + if (checkpin > 0) + { + pinMode(checkpin, INPUT); + } +} + + +void toneHigh() +{ + uint8_t index; + + for (index = 1; index <= _highcycles; index++) + { + digitalWrite(_audiopin, HIGH); + delayMicroseconds(_highperioduS); + digitalWrite(_audiopin, LOW); + delayMicroseconds(_highperioduS); + } + +} + + +void toneLow() +{ + uint8_t index; + + for (index = 1; index <= _lowcycles; index++) + { + digitalWrite(_audiopin, HIGH); + delayMicroseconds(_lowperioduS); + digitalWrite(_audiopin, LOW); + delayMicroseconds(_lowperioduS); + } + +} + + + +void sendAFSKRTTY(uint8_t chartosend) +//send the byte in chartosend as AFSK RTTY, assumes mark condition (idle) is already present +//Format is 7 bits, no parity and 2 stop bits +{ + + uint8_t numbits; + digitalWrite(_checkpin, LOW); + toneLow(); + + for (numbits = 1; numbits <= 7; numbits++) //send 7 bits, LSB first + { + if ((chartosend & 0x01) != 0) + { + digitalWrite(_checkpin, HIGH); + toneHigh(); + } + else + { + digitalWrite(_checkpin, LOW); + toneLow(); + } + chartosend = (chartosend / 2); //get the next bit + } + digitalWrite(_checkpin, HIGH); //start mark condition + toneHigh(); + toneHigh(); +} + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + diff --git a/lib/SX12XX-LoRa/src/AtmelSleep.h b/lib/SX12XX-LoRa/src/AtmelSleep.h new file mode 100644 index 0000000..96bf09f --- /dev/null +++ b/lib/SX12XX-LoRa/src/AtmelSleep.h @@ -0,0 +1,101 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 24/04/20 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. + + These are sleep routines for Atmel processors, tested on ATmega328. When calling the sleep routines be + sure there are no pending interrupts such as from Serial.print(), strange things can happen otherwise. +*******************************************************************************************************/ + + + +#include //sleep library for Atmel processor +#include //watchdog library for Atmel processor + + +//Atmel watchdog sleep times +#define sleep16mS 0x00 +#define sleep32mS 0x01 +#define sleep64mS 0x02 +#define sleep125mS 0x03 +#define sleep250mS 0x04 +#define sleep500mS 0x05 +#define sleep1000mS 0x06 +#define sleep2000mS 0x07 +#define sleep4000mS 0x20 +#define sleep8000mS 0x21 + + +void SleepSeconds(uint32_t secs); +void SleepmS(uint32_t sleeps, uint8_t numbermS); +void atmelSleepPermanent(); + + +void SleepSeconds(uint32_t secs) +{ + //for Atmel processor only + uint16_t sleeps8secs, sleeps1secs; + + sleeps8secs = secs >> 3; + sleeps1secs = secs - (sleeps8secs << 3); + SleepmS(sleeps8secs, sleep8000mS); + SleepmS(sleeps1secs, sleep1000mS); +} + + + +void SleepmS(uint32_t sleeps, uint8_t numbermS) +{ + //for Atmel processor only + uint32_t index; + + for (index = 1; index <= sleeps; index++) + { + ADCSRA = 0; //disable ADC + MCUSR = 0; //clear various "reset" flags + WDTCSR = bit (WDCE) | bit (WDE); //allow changes, disable reset + WDTCSR = bit (WDIE) + numbermS; //set the number of mS for sleep + wdt_reset(); //pat the pet, could be a dog + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + MCUCR = bit (BODS) | bit (BODSE); //turn off brown-out enable in software + MCUCR = bit (BODS); + interrupts (); //guarantees next instruction executed + + sleep_cpu (); + //awake here + sleep_disable(); //cancel sleep as a precaution + } +} + + +void atmelSleepPermanent() +{ + ADCSRA = 0; //disable ADC + set_sleep_mode (SLEEP_MODE_PWR_DOWN); + noInterrupts (); //timed sequence follows + sleep_enable(); + + // turn off brown-out enable in software + MCUCR = bit (BODS) | bit (BODSE); //turn on brown-out enable select + MCUCR = bit (BODS); //this must be done within 4 clock cycles of above + interrupts (); //guarantees next instruction executed + + sleep_cpu (); //sleep within 3 clock cycles of above + + /* wake up here */ + + sleep_disable(); +} + + + +ISR (WDT_vect) +{ + //watchdog interrupt + wdt_disable(); // disable watchdog +} + + diff --git a/lib/SX12XX-LoRa/src/EEPROM_Memory.h b/lib/SX12XX-LoRa/src/EEPROM_Memory.h new file mode 100644 index 0000000..e61f89f --- /dev/null +++ b/lib/SX12XX-LoRa/src/EEPROM_Memory.h @@ -0,0 +1,88 @@ +/* + Copyright 2020 - Stuart Robinson + 24/04/20 +*/ + +/******************************************************************************************************* + Library Operation - This library is for reading from and writing to the EEPROM. These routines provide + a funtional match to the libraries for FRAM. + +*******************************************************************************************************/ + +#include +#define LTUNUSED(v) (void) (v) //add LTUNUSED(variable); in functions to avoid compiler warnings + +void memoryStart(uint16_t addr) //yes I know the EEPROM does not have an address, but the other memory types do +{ + //left empty for future use + LTUNUSED(addr); //avoids compliler warning +} + + +void memoryEnd() +{ + //left empty for future use +} + + +void writeMemoryUint8(uint16_t addr, uint8_t x) +{ + //write a byte to the EEPROM + EEPROM.put(addr, x); +} + + +uint16_t readMemoryUint16(uint16_t addr) +{ + uint16_t x; + EEPROM.get(addr, x); + return x; +} + + +uint32_t readMemoryUint32(uint16_t addr) +{ + uint32_t x; + EEPROM.get(addr, x); + return x; +} + + +void writeMemoryUint32(uint16_t addr, uint32_t x) +{ + EEPROM.put(addr, x); +} + + +void writeMemoryUint16(uint16_t addr, uint16_t x) +{ + EEPROM.put(addr, x); +} + + +float readMemoryFloat(uint16_t addr) +{ + float x; + EEPROM.get(addr, x); + return x; +} + + +void writeMemoryFloat(uint16_t addr, float x) +{ + EEPROM.put(addr, x); +} + + +void fillMemory(uint16_t startaddr, uint16_t endaddr, uint8_t lval) +{ + uint32_t i; + for (i = startaddr; i <= endaddr; i++) + { + writeMemoryUint8(i, lval); + } +} + + + + diff --git a/lib/SX12XX-LoRa/src/FRAM_FM24CL64.h b/lib/SX12XX-LoRa/src/FRAM_FM24CL64.h new file mode 100644 index 0000000..574a4df --- /dev/null +++ b/lib/SX12XX-LoRa/src/FRAM_FM24CL64.h @@ -0,0 +1,462 @@ +/* + Copyright 2020 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + 14/04/20 +*/ + + +/******************************************************************************************************* + Library Operation - This library is for reading from and writing to the 64kbit (8kbyte) I2C FRAMS. + + Take care when writing variables to the end of the address space, the writes and reads may wrap + around back to address 0 +*******************************************************************************************************/ + +#include + +int16_t Memory_I2C_Addr = 0x50; //default I2C address of FM24CL64 FRAM + + +void memoryStart(int16_t addr) +{ + Memory_I2C_Addr = addr; + Wire.begin(); +} + + +void memorySetAddress(int16_t addr) +{ + Memory_I2C_Addr = addr; + +} + + +void memoryEnd() +{ + //left empty for future use +} + + +/*************************************************************************** + Write Routines + *************************************************************************** +*/ + + +void writeMemoryChar(uint16_t addr, char x) +{ + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.write(x); + Wire.endTransmission(); +} + + +void writeMemoryInt8(uint16_t addr, int8_t x) +{ + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.write(x); + Wire.endTransmission(); +} + + +void writeMemoryUint8(uint16_t addr, uint8_t x) +{ + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.write(x); + Wire.endTransmission(); +} + + +void writeMemoryInt16(uint16_t addr, int16_t x) +{ + + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + uint8_t msb_data = highByte(x); + uint8_t lsb_data = lowByte(x); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.write(lsb_data); + Wire.write(msb_data); + Wire.endTransmission(); +} + + +void writeMemoryUint16(uint16_t addr, uint16_t x) +{ + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + uint8_t msb_data = highByte(x); + uint8_t lsb_data = lowByte(x); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.write(lsb_data); + Wire.write(msb_data); + Wire.endTransmission(); +} + + +void writeMemoryInt32(uint16_t addr, int32_t x) +{ + uint8_t index, val; + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + union + { + uint8_t b[4]; + uint32_t f; + } data; + + data.f = x; + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + + for (index = 0; index < 4; index++) + { + val = data.b[index]; + Wire.write(val); //write the data + } + + Wire.endTransmission(); +} + + +void writeMemoryUint32(uint16_t addr, uint32_t x) +{ + uint8_t index, val; + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + union + { + uint8_t b[4]; + uint32_t f; + } data; + + data.f = x; + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + + for (index = 0; index < 4; index++) + { + val = data.b[index]; + Wire.write(val); //write the data + } + + Wire.endTransmission(); +} + + +void writeMemoryFloat(uint16_t addr, float x) +{ + uint8_t index, val; + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + union + { + uint8_t b[4]; + float f; + } data; + + data.f = x; + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + + for (index = 0; index < 4; index++) + { + val = data.b[index]; + Wire.write(val); //write the data + } + + Wire.endTransmission(); +} + + +/*************************************************************************** + Read Routines + ************************************************************************** +*/ + +char readMemoryChar(uint16_t addr) +{ + char data; + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom((Memory_I2C_Addr), 1); + data = Wire.read(); + return data; +} + + +int8_t readMemoryInt8(uint16_t addr) +{ + int8_t data; + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom((Memory_I2C_Addr), 1); + data = Wire.read(); + return data; +} + + +uint8_t readMemoryUint8(uint16_t addr) +{ + uint8_t data; + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom((Memory_I2C_Addr), 1); + data = Wire.read(); + return data; +} + + +int16_t readMemoryInt16(uint16_t addr) +{ + + uint8_t lsb_data, msb_data; + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom(Memory_I2C_Addr, 2); + lsb_data = Wire.read(); + msb_data = Wire.read(); + + return (lsb_data + (msb_data * 256)); +} + + +uint16_t readMemoryUint16(uint16_t addr) +{ + + uint8_t lsb_data, msb_data; + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom(Memory_I2C_Addr, 2); + lsb_data = Wire.read(); + msb_data = Wire.read(); + + return (lsb_data + (msb_data * 256)); +} + + +int32_t readMemoryInt32(uint16_t addr) +{ + uint8_t val; + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom(Memory_I2C_Addr, 4); + + union + { + uint8_t b[4]; + unsigned long f; + } readdata; + + for (int index = 0; index < 4; index++) + { + val = Wire.read(); // read the uint8_t + readdata.b[index] = val; + } + + return readdata.f; +} + + +uint32_t readMemoryUint32(uint16_t addr) +{ + uint8_t val; + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom(Memory_I2C_Addr, 4); + + union + { + uint8_t b[4]; + unsigned long f; + } readdata; + + for (int index = 0; index < 4; index++) + { + val = Wire.read(); // read the uint8_t + readdata.b[index] = val; + } + + return readdata.f; +} + + +float readMemoryFloat(uint16_t addr) +{ + uint8_t val; + + uint8_t msb_addr = highByte(addr); + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr); + Wire.write(msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom(Memory_I2C_Addr, 4); + + union + { + uint8_t b[4]; + float f; + } readdata; + + + for (int index = 0; index < 4; index++) + { + val = Wire.read(); // read the uint8_t + readdata.b[index] = val; + } + return readdata.f; +} + + +/*************************************************************************** + Start of general purpose memory routines +***************************************************************************/ + +uint16_t CRCMemory(uint16_t startaddr, uint16_t endaddr, uint16_t startval) +{ + uint16_t i, libraryCRC; + uint8_t j; + + libraryCRC = startval; //start value for CRC16, often 0xffff + + for (i = startaddr; i <= endaddr; i++) //element 4 is first character after $$$$ at start + { + libraryCRC ^= ((uint16_t)readMemoryUint8(i) << 8); + for (j = 0; j < 8; j++) + { + if (libraryCRC & 0x8000) + libraryCRC = (libraryCRC << 1) ^ 0x1021; + else + libraryCRC <<= 1; + } + } + return libraryCRC; + +} + + +void printMemory(uint16_t start_addr, uint16_t end_addr) +{ + //print the contents of Memory + + uint8_t value; + + for (uint16_t a = start_addr; a <= end_addr; a++) + { + value = readMemoryUint8(a); + if ((a % 16) == 0) + { + Serial.println(); + Serial.print(F("0x")); + if (a < 0x10) + { + Serial.print('0'); + } + Serial.print(a, HEX); + Serial.print(F(": ")); + } + Serial.print(F("0x")); + if (value < 0x10) + Serial.print('0'); + Serial.print(value, HEX); + Serial.print(F(" ")); + } + Serial.println(); +} + + +void fillMemory(uint16_t startaddr, uint16_t endaddr, uint8_t lval) +{ + uint16_t i; + for (i = startaddr; i <= endaddr; i++) + { + writeMemoryUint8(i, lval); + } +} + + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ diff --git a/lib/SX12XX-LoRa/src/FRAM_MB85RC16PNF.h b/lib/SX12XX-LoRa/src/FRAM_MB85RC16PNF.h new file mode 100644 index 0000000..d879ed7 --- /dev/null +++ b/lib/SX12XX-LoRa/src/FRAM_MB85RC16PNF.h @@ -0,0 +1,447 @@ +/* + Copyright 2020 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + 14/04/20 +*/ + +/******************************************************************************************************* + Library Operation - This library is for reading from and writing to the 16kbit (2Kbyte) I2C FRAMS. + These FRAMS present as a series of eight 256 byte pages at I2C addresses 0X50 to 0x57. The library + calculates from the passed address which page to write to. For variables that are larger than one + uint8_t take care not to set the address so that a write would cross a page boundary. The library + does not deal with writes or reads that cross the 256 byte pages of the device. +*******************************************************************************************************/ + +#include + +int16_t Memory_I2C_Addr = 0x50; //default I2C address of MB85RC16PNF FRAM + + +void memoryStart(int16_t addr) +{ + Memory_I2C_Addr = addr; + Wire.begin(); +} + + +void memorySetAddress(int16_t addr) +{ + Memory_I2C_Addr = addr; + +} + + +void memoryEnd() +{ + //left empty for future use +} + + +/*************************************************************************** + Write Routines + **************************************************************************/ + +void writeMemoryChar(uint16_t addr, char x) +{ + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.write(x); + Wire.endTransmission(); +} + + +void writeMemoryInt8(uint16_t addr, int8_t x) +{ + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.write(x); + Wire.endTransmission(); +} + + +void writeMemoryUint8(uint16_t addr, uint8_t x) +{ + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.write(x); + Wire.endTransmission(); +} + + +void writeMemoryInt16(uint16_t addr, int16_t x) +{ + + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + uint8_t msb_data = highByte(x); + uint8_t lsb_data = lowByte(x); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.write(lsb_data); + Wire.write(msb_data); + Wire.endTransmission(); +} + + +void writeMemoryUint16(uint16_t addr, uint16_t x) +{ + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + uint8_t msb_data = highByte(x); + uint8_t lsb_data = lowByte(x); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.write(lsb_data); + Wire.write(msb_data); + Wire.endTransmission(); +} + + +void writeMemoryInt32(uint16_t addr, int32_t x) +{ + uint8_t index, val; + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + union + { + uint8_t b[4]; + uint32_t f; + } data; + + data.f = x; + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + + for (index = 0; index < 4; index++) + { + val = data.b[index]; + Wire.write(val); //write the data + } + + Wire.endTransmission(); +} + + + +void writeMemoryUint32(uint16_t addr, uint32_t x) +{ + uint8_t index, val; + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + union + { + uint8_t b[4]; + uint32_t f; + } data; + + data.f = x; + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + + for (index = 0; index < 4; index++) + { + val = data.b[index]; + Wire.write(val); //write the data + } + + Wire.endTransmission(); +} + + +void writeMemoryFloat(uint16_t addr, float x) +{ + uint8_t index, val; + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + union + { + uint8_t b[4]; + float f; + } data; + + data.f = x; + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + + for (index = 0; index < 4; index++) + { + val = data.b[index]; + Wire.write(val); //write the data + } + + Wire.endTransmission(); +} + +/*************************************************************************** + Read Routines + ************************************************************************** +*/ + + +char readMemoryChar(uint16_t addr) +{ + char data; + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom((Memory_I2C_Addr + msb_addr), 1); + data = Wire.read(); + return data; +} + + +int8_t readMemoryInt8(uint16_t addr) +{ + int8_t data; + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom((Memory_I2C_Addr + msb_addr), 1); + data = Wire.read(); + return data; +} + + +uint8_t readMemoryUint8(uint16_t addr) +{ + uint8_t data; + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom((Memory_I2C_Addr + msb_addr), 1); + data = Wire.read(); + return data; +} + + +int16_t readMemoryInt16(uint16_t addr) +{ + + uint8_t lsb_data, msb_data; + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom(Memory_I2C_Addr + msb_addr, 2); + lsb_data = Wire.read(); + msb_data = Wire.read(); + + return (lsb_data + (msb_data * 256)); +} + + +uint16_t readMemoryUint16(uint16_t addr) +{ + + uint8_t lsb_data, msb_data; + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom(Memory_I2C_Addr + msb_addr, 2); + lsb_data = Wire.read(); + msb_data = Wire.read(); + + return (lsb_data + (msb_data * 256)); +} + + +int32_t readMemoryInt32(uint16_t addr) +{ + uint8_t val, index; + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom(Memory_I2C_Addr + msb_addr, 4); + + union + { + uint8_t b[4]; + uint32_t f; + } readdata; + + for (index = 0; index < 4; index++) + { + val = Wire.read(); // read the uint8_t + readdata.b[index] = val; + } + + return readdata.f; +} + + +uint32_t readMemoryUint32(uint16_t addr) +{ + uint8_t val, index; + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom(Memory_I2C_Addr + msb_addr, 4); + + union + { + uint8_t b[4]; + uint32_t f; + } readdata; + + for (index = 0; index < 4; index++) + { + val = Wire.read(); // read the uint8_t + readdata.b[index] = val; + } + + return readdata.f; +} + + +float readMemoryFloat(uint16_t addr) +{ + uint8_t val, index; + + uint8_t msb_addr = (highByte(addr) & 0x07); //get 3 MSB bits of address + uint8_t lsb_addr = lowByte(addr); + + Wire.beginTransmission(Memory_I2C_Addr + msb_addr); + Wire.write(lsb_addr); + Wire.endTransmission(); + Wire.requestFrom(Memory_I2C_Addr + msb_addr, 4); + + union + { + uint8_t b[4]; + float f; + } readdata; + + + for (index = 0; index < 4; index++) + { + val = Wire.read(); // read the uint8_t + readdata.b[index] = val; + } + return readdata.f; +} + + +/*************************************************************************** + Start of general purpose memory routines +***************************************************************************/ + +uint16_t CRCMemory(uint16_t startaddr, uint16_t endaddr, uint16_t startval) +{ + uint16_t i, libraryCRC; + uint8_t j; + + libraryCRC = startval; //start value for CRC16, often 0xffff + + for (i = startaddr; i <= endaddr; i++) //element 4 is first character after $$$$ at start + { + libraryCRC ^= ((uint16_t)readMemoryUint8(i) << 8); + for (j = 0; j < 8; j++) + { + if (libraryCRC & 0x8000) + libraryCRC = (libraryCRC << 1) ^ 0x1021; + else + libraryCRC <<= 1; + } + } + return libraryCRC; + +} + + +void printMemory(uint16_t start_addr, uint16_t end_addr) +{ + //print the contents of Memory + + uint8_t value; + + for (uint16_t a = start_addr; a <= end_addr; a++) + { + value = readMemoryUint8(a); + if ((a % 16) == 0) + { + Serial.println(); + Serial.print(F("0x")); + if (a < 0x10) + { + Serial.print('0'); + } + Serial.print(a, HEX); + Serial.print(F(": ")); + } + Serial.print(F("0x")); + if (value < 0x10) + Serial.print('0'); + Serial.print(value, HEX); + Serial.print(F(" ")); + } + Serial.println(); +} + + +void fillMemory(uint16_t startaddr, uint16_t endaddr, uint8_t lval) +{ + uint16_t i; + for (i = startaddr; i <= endaddr; i++) + { + writeMemoryUint8(i, lval); + } +} + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + + + diff --git a/lib/SX12XX-LoRa/src/LICENSE.txt b/lib/SX12XX-LoRa/src/LICENSE.txt new file mode 100644 index 0000000..d6dfb1b --- /dev/null +++ b/lib/SX12XX-LoRa/src/LICENSE.txt @@ -0,0 +1,25 @@ +--- Revised BSD License --- +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/lib/SX12XX-LoRa/src/ProgramLT_Definitions.h b/lib/SX12XX-LoRa/src/ProgramLT_Definitions.h new file mode 100644 index 0000000..5284f53 --- /dev/null +++ b/lib/SX12XX-LoRa/src/ProgramLT_Definitions.h @@ -0,0 +1,123 @@ +/******************************************************************************************************* + Programs for Arduino - Copyright of the author Stuart Robinson - 17/12/19 + + This program is supplied as is, it is up to the user of the program to decide if the program is + suitable for the intended purpose and free from errors. +*******************************************************************************************************/ + + +/* +****************************************************************************************************** + Definitions for packet types +****************************************************************************************************** +*/ + +const char Sensor1 = '!'; //Sensor packet1 +const char HABPacket = '$'; //HAB style CSV ASCII packet +const char Broadcast = '*'; //Broadcast destination address +const char RControl1 = 'D'; //Remote Control packet +const char TestMode1 = '1'; //used to switch to Testmode1 settings +const char TestPacket = 'T'; //Test packet +const char TXError = 't'; //Transmitter error +const char PowerUp = 'P'; //sent on tracker start +const char LocationPacket = 'L'; //LT library tracker location packet in binary format +const char LocationBinaryPacket = 's'; //short location packet in binary format +const char NoFix = 'F'; //GPS no fix +const char NoGPS = 'G'; //No GPS found, or GPS error. +const char ACK = 'A'; //Acknowledge +const char NACK = 'N'; //Not Acknowledge, error +const char AFC = 'a'; //Packet sent for AFC purposes + +const uint8_t Reliable = 0x80; //this packet type indicates reliable data packet +const uint8_t ReliableACK = 0x81; //this packet type indicates reliable data packet acknowledge +const uint8_t FTtype = 0xF0; //FTsubtype file transfer type +const uint8_t FTstart = 0xF1; //FTsubtype file transfer start information, filename etc +const uint8_t FTsegment = 0xF2; //FTsubtype packet contains a numbered segment +const uint8_t FTACK = 0xF3; //FTsubtype ACK response for file transfers +const uint8_t FTNACK = 0xF4; //FTsubtype NACK response for file transfers +const uint8_t FTclose = 0xF5; //FTsubtype request from TX to close file, transfer finished +const uint8_t FTrestart = 0xF6; //FTsubtype request from RX to restart current file transfer + +//GPS Tracker Status byte settings +const uint8_t GPSFix = 0; //flag bit set when GPS has a current fix +const uint8_t GPSConfigError = 1; //flag bit set to indicate cannot configure GPS or wrong configuration +const uint8_t CameraError = 2; //flag bit indicating a camera device error +const uint8_t GPSError = 3; //flag bit set to indicate GPS error, response timeout for instance +const uint8_t LORAError = 4; //flag bit indication a lora device error +const uint8_t SDError = 5; //flag bit indication a SD card device error +const uint8_t TrackerLost = 6; //flag bit indication that tracker in lost mode +const uint8_t NoGPSTestMode = 7; //flag bit number to indicate tracker in no GPS test mode + +/********************************************************************* + START Stored Program data +**********************************************************************/ +const uint16_t addr_StartMemory = 0x00; //the start of memory +const uint16_t addr_StartProgramData = 0x100; //the start of program data in memory +const uint16_t addr_ResetCount = 0x100; //unsigned long int 4 bytes +const uint16_t addr_SequenceNum = 0x104; //unsigned long int 4 bytes +const uint16_t addr_TXErrors = 0x108; //uint16_t 2 bytes +const uint16_t addr_EndMemory = 0x3FF; + + +/********************************************************************* + START GPS CoordinateData +**********************************************************************/ +//for storing last received GPS co-ordinates from local and remote tracker GPS +const uint16_t addr_StartCoordinateData = 0x300; +const uint16_t addr_RemoteLat = 0x300; //float 4 bytes +const uint16_t addr_RemoteLon = 0x304; //float 4 bytes +const uint16_t addr_RemoteAlt = 0x308; //uint16_t 2 bytes +const uint16_t addr_RemoteHour = 0x30C; //byte 1 byte; Note times for last tracker co-ordinates come from local GPS time +const uint16_t addr_RemoteMin = 0x310; //byte 1 byte +const uint16_t addr_RemoteSec = 0x311; //byte 1 byte +const uint16_t addr_RemoteDay = 0x312; //byte 1 byte +const uint16_t addr_RemoteMonth = 0x313; //byte 1 byte +const uint16_t addr_RemoteYear = 0x314; //byte 1 byte +const uint16_t addr_LocalLat = 0x318; //float 4 bytes +const uint16_t addr_LocalLon = 0x31C; //float 4 bytes +const uint16_t addr_LocalAlt = 0x320; //uint16_t 2 bytes +const uint16_t addr_LocalHour = 0x322; //byte 1 byte +const uint16_t addr_LocalMin = 0x323; //byte 1 byte +const uint16_t addr_LocalSec = 0x324; //byte 1 byte +const uint16_t addr_LocalDay = 0x325; //byte 1 byte +const uint16_t addr_LocalMonth = 0x326; //byte 1 byte +const uint16_t addr_LocalYear = 0x327; //byte 1 byte +const uint16_t addr_EndCoordinateData = 0x327; + +const uint16_t addr_RemotelocationCRC = 0x340; //the 16 bit CRC of the last tracker location data is saved here +const uint16_t addr_LocallocationCRC = 0x342; //the 16 bit CRC of the last local location data is saved here + +const uint16_t addr_TestLocation_page3 = 0x3FF; //used as a location for read\write tests + +/********************************************************************* + END GPS CoordinateData +**********************************************************************/ + + +/* +****************************************************************************************************** + Bit numbers for current_config byte settings end definitions for packet types +****************************************************************************************************** +*/ + +//Bit numbers for current_config byte settings in transmitter (addr_Default_config1) +const uint8_t SearchEnable = 0; //bit num to set in config byte to enable search mode packet +const uint8_t TXEnable = 1; //bit num to set in config byte to enable transmissions +const uint8_t FSKRTTYEnable = 2; //bit num to set in config byte to enable FSK RTTY +const uint8_t DozeEnable = 4; //bit num to set in config byte to put tracker in Doze mode +const uint8_t GPSHotFix = 7; //bit when set enables GPS Hot Fix mode. + + +/* +****************************************************************************************************** + Values for reliable transmit\receive errors +****************************************************************************************************** +*/ +const uint16_t packettypeErr = 0x01; +const uint16_t destErr = 0x02; +const uint16_t sourceErr = 0x04; +const uint16_t timeoutErr = 0x08; +const uint16_t IDErr = 0x10; +const uint16_t crcErr = 0x20; +const uint16_t seqErr = 0x40; +const uint16_t packetErr = 0x80; \ No newline at end of file diff --git a/lib/SX12XX-LoRa/src/QuectelSerialGPS.h b/lib/SX12XX-LoRa/src/QuectelSerialGPS.h new file mode 100644 index 0000000..79ac20c --- /dev/null +++ b/lib/SX12XX-LoRa/src/QuectelSerialGPS.h @@ -0,0 +1,608 @@ +/* + Copyright 2020 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + Original published 12/05/20 +*/ + + +/******************************************************************************************************* + Program Operation - This is a library file for the Quectel L70,L76,L80 and L86 GPSs + + The routines assume that the GPS has been setup on GPSserial which could be either software serial or + hardware serial. This library file has optimisations that the use of SoftwareSerial requires. + + The calling program should include a define for the GPS baud rate as follows + + #define GPSBaud 9600 + + If this define is missing then 9600 baud is assumed +*******************************************************************************************************/ + +const PROGMEM uint8_t SetBalloonMode[] = {"$PMTK886,3*2B"}; //response should be $PMTK001,886,3*36 +const PROGMEM uint8_t ClearConfig[] = {"$PMTK104*37"}; //no response +const PROGMEM uint8_t PMTK_ACK[] = {"$PMTK001,xxx,?"}; //? = 0 = invalid, 1 = unsupported, 2 = valid failed, 3 = valids succeeded +const PROGMEM uint8_t SoftwareBackup[] = {"$PMTK161,0*28"}; //response should be $PMTK001,161,3*36 +const PROGMEM uint8_t HotStart[] = {"$PMTK101*32"}; +const PROGMEM uint8_t GGARMCOnly[] = {"$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"}; + +uint8_t GPS_GetByte(); +void GPS_OutputOn(); +void GPS_OutputOff(); +void GPS_PowerOn(int8_t pin, uint8_t state); +void GPS_PowerOff(int8_t pin, uint8_t state); +bool GPS_Setup(); +bool GPS_SendConfig(const uint8_t *Progmem_ptr, uint8_t arraysize, uint8_t replylength, uint8_t attempts); +bool GPS_WaitAck(uint32_t waitms, uint8_t length); +bool GPS_WaitChar(uint8_t waitforchar, uint32_t waitmS); +uint8_t GPS_GetNextChar(uint32_t waitmS); +bool GPS_CheckAck(); +bool GPS_SetBalloonMode(); +bool GPS_CheckBalloonMode(); +bool GPS_ClearConfig(); +bool GPS_SetCyclicMode(); +bool GPS_SoftwareBackup(); +bool GPS_HotStart(); +bool GPS_PollNavigation(); +bool GPS_SaveConfig(); + +bool GPS_GLONASSOff(); //not currently implemented on Quectel GPS +bool GPS_GPGLLOff(); //not currently implemented on Quectel GPS +bool GPS_GPGLSOff(); //not currently implemented on Quectel GPS +bool GPS_GPGSAOff(); //not currently implemented on Quectel GPS +bool GPS_GPGSVOff(); //not currently implemented on Quectel GPS +bool GPS_GNSSmode(); //not currently implemented on Quectel GPS +bool GPS_GGARMCOnly(); + + +const uint32_t GPS_WaitAck_mS = 2000; //number of mS to wait for an ACK response from GPS +const uint8_t GPS_attempts = 10; //number of times the sending of GPS config will be attempted. +const uint8_t GPS_Reply_Size = 20; //size of GPS reply buffer +const uint16_t GPS_Clear_DelaymS = 2000; //mS to wait after a GPS Clear command is sent +uint8_t GPS_Reply[GPS_Reply_Size]; //byte array for storing GPS reply to UBX commands + + +#define QUECTELINUSE //so complier can know which GPS library is used //so complier can know which GPS library is used +//#define GPSDebug + + +#ifndef GPSBaud +#define GPSBaud 9600 +#endif + + +void GPS_OutputOn() +{ +#ifdef GPSDebug + Serial.print(F("GPS_OutputOn() ")); +#endif + //turns on serial output from GPS + GPSserial.begin(GPSBaud); + while (GPSserial.available()) GPSserial.read(); //make sure input buffer is empty +} + + +void GPS_OutputOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_OutputOff() ")); +#endif + //turns off serial output from GPS + GPSserial.end(); +} + + +void GPS_PowerOn(int8_t pin, uint8_t state) +{ +#ifdef GPSDebug + Serial.print(F("GPS_PowerOn() ")); +#endif + + if (pin >= 0) + { + digitalWrite(pin, state); + } + +} + + +void GPS_PowerOff(int8_t pin, uint8_t state) +{ +#ifdef GPSDebug + Serial.print(F("GPS_PowerOff() ")); +#endif + +if (pin >= 0) + { + digitalWrite(pin, state); + } + +} + + +bool GPS_Setup() +{ +#ifdef GPSDebug + Serial.print(F("GPS_Setup() ")); +#endif + + if (!GPS_SetBalloonMode()) + { + return false; + } + + if (!GPS_GGARMCOnly()) + { + return false; + } + + return true; +} + + +bool GPS_SendConfig(const uint8_t *Progmem_ptr, uint8_t arraysize, uint8_t replylength, uint8_t attempts) +{ +#ifdef GPSDebug + Serial.print(F("GPS_SendConfig() ")); +#endif + + uint8_t byteread, index; + uint8_t config_attempts = attempts; + + memset(GPS_Reply, 0, sizeof(GPS_Reply)); //clear the reply buffer + + Serial.flush(); //ensure there are no pending interrupts from serial monitor printing + + do + { + if (config_attempts == 0) + { + return false; + } + + GPS_OutputOff(); + + Serial.print(F("GPSSend ")); + + for (index = 0; index < arraysize; index++) + { + byteread = pgm_read_byte_near(Progmem_ptr++); + Serial.write(byteread); + } + + Serial.flush(); //make sure serial out buffer is empty + + GPS_OutputOn(); + + Progmem_ptr = Progmem_ptr - arraysize; //set Progmem_ptr back to start + + for (index = 0; index < arraysize; index++) + { + byteread = pgm_read_byte_near(Progmem_ptr++); + GPSserial.write(byteread); + } + + Progmem_ptr = Progmem_ptr - arraysize; //set Progmem_ptr back to start, in case config command retried + + GPSserial.write(13); + GPSserial.write(10); + + if (replylength == 0) + { + break; + } + GPSserial.flush(); //make sure all of config command has been sent + config_attempts--; + } + while (!GPS_WaitAck(GPS_WaitAck_mS, replylength)); + + delay(100); //GPS can sometimes be a bit slow getting ready for next config + return true; +} + + +bool GPS_WaitChar(uint8_t waitforchar, uint32_t waitmS) +{ + uint8_t GPSchar; + uint32_t startmS; + + startmS = millis(); + + do + { + if (GPSserial.available()) + { + GPSchar = GPSserial.read(); + if (GPSchar == waitforchar) + { + GPS_Reply[0] = GPSchar; + return true; + } + } + } + while ((uint32_t) (millis() - startmS) < waitmS); //use the timeout to ensure a lack of GPS does not cause the program to hang + return false; +} + + +uint8_t GPS_GetNextChar(uint32_t waitmS) +{ + uint8_t GPSchar; + uint32_t startmS; + + startmS = millis(); + do + { + if (GPSserial.available()) + { + GPSchar = GPSserial.read(); + GPS_Reply[1] = GPSchar; + return GPSchar; + } + } + while ((uint32_t) (millis() - startmS) < waitmS); //use the timeout to ensure a lack of GPS does not cause the program to hang + return '*'; +} + + +bool GPS_WaitAck(uint32_t waitmS, uint8_t length) +{ +#ifdef GPSDebug + Serial.print(F("GPS_WaitAck() ")); + Serial.print(F(" Reply length ")); + Serial.print(length); + Serial.print(F(" ")); +#endif + + uint8_t GPSchar; + uint32_t startmS; + + startmS = millis(); + uint8_t ptr = 0; //used as pointer to store GPS reply + bool found; + + Serial.println(); + Serial.print(F("Received ")); + Serial.flush(); + + found = false; + + do + { + if (!GPS_WaitChar('$', waitmS)) + { + Serial.print(F("Timeout Error")); + found = false; + break; + } + + Serial.print(F("$")); + + GPSchar = GPS_GetNextChar(waitmS); + Serial.write(GPSchar); + + if (GPSchar == 'P') + { + found = true; + break; + } + else + { + Serial.print(F(" ")); + found = false; + } + } + while ((uint32_t) (millis() - startmS) < waitmS); + + if (found) + { + ptr = 2; + do + { + if (GPSserial.available()) + { + GPSchar = GPSserial.read(); + GPS_Reply[ptr++] = GPSchar; + } + } + while (((uint32_t) (millis() - startmS) < waitmS) && (ptr < length)); //use the timeout to ensure a lack of GPS does not cause the program to hang + } + + GPS_OutputOff(); + + + for (ptr = 2; ptr < length; ptr++) + { + GPSchar = GPS_Reply[ptr]; + Serial.write(GPSchar); + } + + Serial.println(); + + if (GPS_CheckAck()) + { + return true; + } + + return false; +} + + +bool GPS_CheckBalloonMode() +{ + //Reply to set balloon mode ought to be $PMTK886,3*2B, the 3 is for balloon mode + +#ifdef GPSDebug + Serial.print(F("GPS_CheckBalloonMode() ")); +#endif + + if (GPS_SetBalloonMode()) + { + return true; + } + return false; +} + + +bool GPS_PollNavigation() +{ +#ifdef GPSDebug + Serial.print(F("GPS_PollNavigation() ")); +#endif +//empty function for compatibility reasons with other GPS libraries +return true; +} + + +bool GPS_SaveConfig() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SaveConfig() ")); +#endif +//empty function for compatibility reasons with other GPS libraries +return true; +} + + +bool GPS_CheckAck() +{ + +#ifdef GPSDebug + Serial.print(F("GPS_CheckAck() ")); +#endif + + uint8_t response[] = {"$PMTK001,xxx,?"}; + uint8_t index; + + for (index = 2; index <= 7; index++) //check up to the eighth ([7]) character, which is the ',' + { + if ( GPS_Reply[index] != response[index]) + { + return false; + } + } + + if ((index == 8) && (GPS_Reply[13] == '3') ) //if we got to index 8 there was a match for $PMTK001, so check reply type + { + return true; + } + + Serial.print(index); + Serial.print(F(" ")); + return false; +} + + +/********************************************************************* + // GPS configuration commands +*********************************************************************/ + + +bool GPS_SetBalloonMode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SetBalloonMode() ")); +#endif + + Serial.println(F("SetBalloonMode ")); + size_t SIZE = sizeof(SetBalloonMode); + + if (GPS_SendConfig(SetBalloonMode, SIZE, 17, GPS_attempts)) + { + Serial.println(F("OK")); + Serial.println(); + return true; + } + Serial.println(F("Fail")); + Serial.println(); + return false; +} + + +bool GPS_GGARMCOnly() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GGARMCOnly() ")); +#endif + + Serial.println(F("Set GGA and RMC only ")); + size_t SIZE = sizeof(GGARMCOnly); + + if (GPS_SendConfig(GGARMCOnly, SIZE, 17, GPS_attempts)) + { + Serial.println(F("OK")); + Serial.println(); + return true; + } + Serial.println(F("Fail")); + Serial.println(); + return false; +} + + + +bool GPS_ClearConfig() + { + Serial.println(F("ClearConfig()")); + size_t SIZE = sizeof(ClearConfig); + GPS_SendConfig(ClearConfig, SIZE, 0, GPS_attempts); //full cold start, no response is given, so replylength = 0 + + #ifdef GPSDebug + Serial.print(F("No Response given for full cold start")); + #endif + + Serial.println(); + Serial.println(F("Wait clear")); + delay(GPS_Clear_DelaymS); + return true; + } + + +bool GPS_SetCyclicMode() + { + #ifdef GPSDebug + Serial.print(F("GPS_SetCyclicMode() ")); + #endif + //no clyclic mode config for Quectel + return true; + + } + + + bool GPS_SoftwareBackup() + { + #ifdef GPSDebug + Serial.print(F("GPS_SoftwareBackup() ")); + #endif + + size_t SIZE = sizeof(SoftwareBackup); + Serial.println(F("SoftwareBackup")); + + if (GPS_SendConfig(SoftwareBackup, SIZE, 0, GPS_attempts)) + { + return true; + } + + return false; +} + + +bool GPS_HotStart() + { + #ifdef GPSDebug + Serial.print(F("GPS_HotStart() ")); + #endif + + size_t SIZE = sizeof(HotStart); + Serial.println(F("HotStart")); + + if (GPS_SendConfig(HotStart, SIZE, 0, GPS_attempts)) //reply is $PMTK010,002*2D, so not receognised by check ack + { + return true; + } + + return false; +} + + +bool GPS_GPGLLOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGLLOff() ")); +#endif + + //setup to be added later + //function included for compatibility with other GPS library + return true; +} + + +bool GPS_GPGLSOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGLSOff() ")); +#endif + + //setup to be added later + //function included for compatibility with other GPS library + return true; +} + + +bool GPS_GPGSAOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGSAOff() ")); +#endif + + //setup to be added later + //function included for compatibility with other GPS library + return true; +} + + +bool GPS_GPGSVOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGSVOff() ")); +#endif + + //setup to be added later + //function included for compatibility with other GPS library + return true; +} + +bool GPS_GLONASSOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GLONASSOff() ")); +#endif + + //setup to be added later + //function included for compatibility with other GPS library + return true; +} + + +bool GPS_GNSSmode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GNSSmode() ")); +#endif + + //setup to be added later + //function included for compatibility with other GPS library + return true; +} + + +uint8_t GPS_GetByte() //get a byte for GPS +{ + if (GPSserial.available() == 0) + { + return 0xFF; //for compatibility with I2C reading of GPS + } + else + { + return GPSserial.read(); + } +} + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + diff --git a/lib/SX12XX-LoRa/src/Quectel_HWSerialGPS.h b/lib/SX12XX-LoRa/src/Quectel_HWSerialGPS.h new file mode 100644 index 0000000..344efef --- /dev/null +++ b/lib/SX12XX-LoRa/src/Quectel_HWSerialGPS.h @@ -0,0 +1,474 @@ +/* + Copyright 2020 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + Original published 12/05/20 +*/ + + +/******************************************************************************************************* + Program Operation - This is a library file for the Quectel L70,L76,L80 and L86 GPSs + + The routines assume that the GPS has been setup on GPSserial which could be either software serial or + hardware serial. The calling program should include a define for the GPS baud rate as follows + + #define GPSBaud 9600 + + If this define is missing then 9600 baud is assumed +*******************************************************************************************************/ + +const PROGMEM uint8_t SetBalloonMode[] = {"$PMTK886,3*2B"}; //response should be $PMTK001,886,3*36 +const PROGMEM uint8_t ClearConfig[] = {"$PMTK104*37"}; //no response +const PROGMEM uint8_t PMTK_ACK[] = {"$PMTK001,xxx,?"}; //? = 0 = invalid, 1 = unsupported, 2 = valid failed, 3 = valids succeeded +const PROGMEM uint8_t SoftwareBackup[] = {"$PMTK161,0*28"}; //response should be $PMTK001,161,3*36 +const PROGMEM uint8_t HotStart[] = {"$PMTK101*32"}; + +uint8_t GPS_GetByte(); +void GPS_OutputOn(); +void GPS_OutputOff(); +void GPS_PowerOn(int8_t pin, uint8_t state); +void GPS_PowerOff(int8_t pin, uint8_t state); +bool GPS_Setup(); +bool GPS_SendConfig(const uint8_t *Progmem_ptr, uint8_t arraysize, uint8_t replylength, uint8_t attempts); +bool GPS_WaitChar(uint8_t waitforchar, uint32_t waitmS); +uint8_t GPS_GetNextChar(uint32_t waitmS); +bool GPS_WaitAck(uint32_t waitms, uint8_t length); +bool GPS_CheckConfiguration(); +bool GPS_CheckAck(); +bool GPS_SetBalloonMode(); +bool GPS_ClearConfig(); +bool GPS_SetCyclicMode(); +bool GPS_SoftwareBackup(); +bool GPS_HotStart(); + + +const uint32_t GPS_WaitAck_mS = 2000; //number of mS to wait for an ACK response from GPS +const uint8_t GPS_attempts = 5; //number of times the sending of GPS config will be attempted. +const uint8_t GPS_Reply_Size = 20; //size of GPS reply buffer +const uint16_t GPS_Clear_DelaymS = 2000; //mS to wait after a GPS Clear command is sent + +uint8_t GPS_Reply[GPS_Reply_Size]; //byte array for storing GPS reply to UBX commands + +#define QUECTELINUSE //so complier can know which GPS library is used //so complier can know which GPS library is used +//#define GPSDebug + +#ifndef GPSConfigSerial // if GPSDebugSerial is not defined set to Serial as default + #define GPSConfigSerial Serial +#endif + +#ifndef GPSDebugSerial // if GPSDebugSerial is not defined set to Serial as default + #define GPSDebugSerial Serial +#endif + +#ifndef GPSBaud +#define GPSBaud 9600 +#endif + +void GPS_OutputOn() +{ +#ifdef GPSDebug + Serial.print(F("GPS_OutputOn() ")); +#endif + //turns on serial output from GPS + GPSserial.begin(GPSBaud); +} + + +void GPS_OutputOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_OutputOff() ")); +#endif + //turns off serial output from GPS + GPSserial.end(); +} + + +void GPS_PowerOn(int8_t pin, uint8_t state) +{ +#ifdef GPSDebug + Serial.print(F("GPS_PowerOn() ")); +#endif + + if (pin >= 0) + { + digitalWrite(pin, state); + } + +} + + +void GPS_PowerOff(int8_t pin, uint8_t state) +{ +#ifdef GPSDebug + Serial.print(F("GPS_PowerOff() ")); +#endif + +if (pin >= 0) + { + digitalWrite(pin, state); + } + +} + + +bool GPS_Setup() +{ +#ifdef GPSDebug + Serial.print(F("GPS_Setup() ")); +#endif + + if (!GPS_SetBalloonMode()) + { + return false; + } + + return true; +} + + +bool GPS_SendConfig(const uint8_t *Progmem_ptr, uint8_t arraysize, uint8_t replylength, uint8_t attempts) +{ +#ifdef GPSDebug + Serial.print(F("GPS_SendConfig() ")); +#endif + + uint8_t byteread, index; + uint8_t config_attempts = attempts; + + memset(GPS_Reply, 0, sizeof(GPS_Reply)); //clear the reply buffer + + Serial.flush(); //ensure there are no pending interrupts from serial monitor printing + + do + { + if (config_attempts == 0) + { + return false; + } + + Serial.print(F("GPSSend ")); + + for (index = 0; index < arraysize; index++) + { + byteread = pgm_read_byte_near(Progmem_ptr++); + Serial.write(byteread); + } + + Serial.flush(); //make sure serial out buffer is empty + + Progmem_ptr = Progmem_ptr - arraysize; //set Progmem_ptr back to start + + for (index = 0; index < arraysize; index++) + { + byteread = pgm_read_byte_near(Progmem_ptr++); + GPSserial.write(byteread); + } + + Progmem_ptr = Progmem_ptr - arraysize; //set Progmem_ptr back to start, in case config command retried + + + GPSserial.write(13); + GPSserial.write(10); + + if (replylength == 0) + { + break; + } + + config_attempts--; + } + while (!GPS_WaitAck(GPS_WaitAck_mS, replylength)); + + delay(100); //GPS can sometimes be a bit slow getting ready for next config + return true; +} + + +bool GPS_WaitChar(uint8_t waitforchar, uint32_t waitmS) +{ + uint8_t GPSchar; + + do + { + if (GPSserial.available()) + { + GPSchar = GPSserial.read(); + if (GPSchar == waitforchar) + { + GPS_Reply[0] = GPSchar; + return true; + } + } + } + while ((millis() < waitmS)); //use the timeout to ensure a lack of GPS does not cause the program to hang + return false; +} + + +uint8_t GPS_GetNextChar(uint32_t waitmS) +{ + uint8_t GPSchar; + + do + { + if (GPSserial.available()) + { + GPSchar = GPSserial.read(); + GPS_Reply[1] = GPSchar; + return GPSchar; + } + } + while ((millis() < waitmS)); //use the timeout to ensure a lack of GPS does not cause the program to hang + return '*'; +} + + +bool GPS_WaitAck(uint32_t waitmS, uint8_t length) +{ +#ifdef GPSDebug + Serial.print(F("GPS_WaitAck() ")); + Serial.print(F(" Reply length ")); + Serial.print(length); + Serial.print(F(" ")); +#endif + + uint8_t GPSchar; + + uint32_t endmS; + endmS = millis() + waitmS; + uint8_t ptr = 0; //used as pointer to store GPS reply + bool found; + + Serial.println(); + Serial.print(F("Received ")); + Serial.flush(); + + endmS = millis() + GPS_WaitAck_mS; + + found = false; + + do + { + if (!GPS_WaitChar('$', endmS)) + { + Serial.print(F("Timeout Error")); + found = false; + break; + } + + Serial.print(F("$")); + + GPSchar = GPS_GetNextChar(endmS); + Serial.write(GPSchar); + + if (GPSchar == 'P') + { + found = true; + break; + } + else + { + Serial.print(F(" ")); + found = false; + } + } + while (millis() <= endmS); + + if (found) + { + ptr = 2; + do + { + if (GPSserial.available()) + { + GPSchar = GPSserial.read(); + GPS_Reply[ptr++] = GPSchar; + } + } + while ((millis() < endmS) && (ptr < length)); //use the timeout to ensure a lack of GPS does not cause the program to hang + } + + + for (ptr = 2; ptr < length; ptr++) + { + GPSchar = GPS_Reply[ptr]; + Serial.write(GPSchar); + } + + Serial.println(); + + if (GPS_CheckAck()) + { + return true; + } + + return false; +} + + +bool GPS_CheckConfiguration() +{ + //Reply to set balloon mode ought to be $PMTK886,3*2B, the 3 is for balloon mode + +#ifdef GPSDebug + Serial.print(F("GPS_CheckConfiguration() ")); +#endif + + if (GPS_SetBalloonMode()) + { + return true; + } + return false; +} + + + +bool GPS_CheckAck() +{ + +#ifdef GPSDebug + Serial.print(F("GPS_CheckAck() ")); +#endif + + uint8_t response[] = {"$PMTK001,xxx,?"}; + uint8_t index; + + for (index = 2; index <= 7; index++) //check up to the eighth ([7]) character, which is the ',' + { + if ( GPS_Reply[index] != response[index]) + { + return false; + } + } + + if ((index == 8) && (GPS_Reply[13] == '3') ) //if we got to index 8 there was a match for $PMTK001, so check reply type + { + return true; + } + + Serial.print(index); + Serial.print(F(" ")); + return false; +} + + +/********************************************************************* + // GPS configuration commands +*********************************************************************/ + + +bool GPS_SetBalloonMode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SetBalloonMode() ")); +#endif + + Serial.println(F("SetBalloonMode ")); + size_t SIZE = sizeof(SetBalloonMode); + + if (GPS_SendConfig(SetBalloonMode, SIZE, 17, GPS_attempts)) + { + Serial.println(F("OK")); + Serial.println(); + return true; + } + Serial.println(F("Fail")); + Serial.println(); + return false; +} + + +bool GPS_ClearConfig() + { + Serial.println(F("ClearConfig()")); + size_t SIZE = sizeof(ClearConfig); + GPS_SendConfig(ClearConfig, SIZE, 0, GPS_attempts); //full cold start, no response is given, so replylength = 0 + + #ifdef GPSDebug + Serial.print(F("No Response given for full cold start")); + #endif + + Serial.println(); + Serial.println(F("Wait clear")); + delay(GPS_Clear_DelaymS); + return true; + } + + +bool GPS_SetCyclicMode() + { + #ifdef GPSDebug + Serial.print(F("GPS_SetCyclicMode() ")); + #endif + //no clyclic mode config for Quectel + return true; + + } + + + bool GPS_SoftwareBackup() + { + #ifdef GPSDebug + Serial.print(F("GPS_SoftwareBackup() ")); + #endif + + size_t SIZE = sizeof(SoftwareBackup); + + if (GPS_SendConfig(SoftwareBackup, SIZE, 0, GPS_attempts)) + { + return true; + } + + return false; +} + + +bool GPS_HotStart() + { + #ifdef GPSDebug + Serial.print(F("GPS_HotStart() ")); + #endif + + size_t SIZE = sizeof(HotStart); + Serial.println(F("HotStart")); + + if (GPS_SendConfig(HotStart, SIZE, 0, GPS_attempts)) //reply is $PMTK010,002*2D, so not receognised by check ack + { + return true; + } + + return false; +} + + +uint8_t GPS_GetByte() //get a byte for GPS +{ + if (GPSserial.available() == 0) + { + return 0xFF; //for compatibility with I2C reading of GPS + } + else + { + return GPSserial.read(); + } +} + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + diff --git a/lib/SX12XX-LoRa/src/SX126XLT.cpp b/lib/SX12XX-LoRa/src/SX126XLT.cpp new file mode 100644 index 0000000..8940294 --- /dev/null +++ b/lib/SX12XX-LoRa/src/SX126XLT.cpp @@ -0,0 +1,3494 @@ +/* + Copyright 2019 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + 17/12/19 +*/ + +/* +Parts of code Copyright (c) 2013, SEMTECH S.A. +See LICENSE.TXT file included in the library +*/ + + +#include +#include + +#define LTUNUSED(v) (void) (v) //add LTUNUSED(variable); to avoid compiler warnings +#define USE_SPI_TRANSACTION + +//#define DEBUGBUSY //comment out if you do not want a busy timeout message +//#define SX126XDEBUG //enable debug messages +//#define SX126XDEBUG3 //enable debug messages +//#define SX126XDEBUGPINS //enable pin allocation debug messages +//#define DEBUGFSKRTTY //enable for FSKRTTY debugging + +/* +**************************************************************************** + To Do: + + +**************************************************************************** +*/ + +SX126XLT::SX126XLT() +{ + //Anything you need when instantiating your object goes here +} + +/* Formats for :begin + original > begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinDIO2, int8_t pinDIO3, int8_t pinSW, uint8_t device); +1 All pins > begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinDIO2, int8_t pinDIO3, int8_t pinRXEN, int8_t pinTXEN, int8_t pinSW, uint8_t device) +2 NiceRF > begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, uint8_t device) +3 Dorji > begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinSW, uint8_t device) +4 Ebyte > begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinRXEN, int8_t pinTXEN, uint8_t device) +*/ + + +bool SX126XLT::begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinDIO2, int8_t pinDIO3, int8_t pinRXEN, int8_t pinTXEN, int8_t pinSW, uint8_t device) +{ + + //format 1 pins, assign all available pins + _NSS = pinNSS; + _NRESET = pinNRESET; + _RFBUSY = pinRFBUSY; + _DIO1 = pinDIO1; + _DIO2 = pinDIO2; + _DIO3 = pinDIO3; + _RXEN = pinRXEN; + _TXEN = pinTXEN; + _SW = pinSW; + _Device = device; + + _TXDonePin = pinDIO1; //this is defalt pin for sensing TX done + _RXDonePin = pinDIO1; //this is defalt pin for sensing RX done + + pinMode(_NSS, OUTPUT); + digitalWrite(_NSS, HIGH); + pinMode(_NRESET, OUTPUT); + digitalWrite(_NRESET, LOW); + pinMode(_RFBUSY, INPUT); + + +#ifdef SX126XDEBUGPINS + Serial.println(F("format 1 begin()")); + Serial.println(F("SX126XLT constructor instantiated successfully")); + Serial.print(F("NSS ")); + Serial.println(_NSS); + Serial.print(F("NRESET ")); + Serial.println(_NRESET); + Serial.print(F("RFBUSY ")); + Serial.println(_RFBUSY); + Serial.print(F("DIO1 ")); + Serial.println(_DIO1); + Serial.print(F("DIO2 ")); + Serial.println(_DIO2); + Serial.print(F("DIO3 ")); + Serial.println(_DIO3); + Serial.print(F("RX_EN ")); + Serial.println(_RXEN); + Serial.print(F("TXEN ")); + Serial.println(_TXEN); + Serial.print(F("SW ")); + Serial.println(_SW); +#endif + + + if (_DIO1 >= 0) + { + pinMode( _DIO1, INPUT); + } + + if (_DIO2 >= 0) + { + pinMode( _DIO2, INPUT); + } + + if (_DIO3 >= 0) + { + pinMode( _DIO3, INPUT); + } + + + if ((_RXEN >= 0) && (_TXEN >= 0)) + { + #ifdef SX126XDEBUGPINS + Serial.println(F("RX_EN & TX_EN switching enabled")); + #endif + pinMode(_RXEN, OUTPUT); + pinMode(_TXEN, OUTPUT); + _rxtxpinmode = true; + } + else + { + #ifdef SX126XDEBUGPINS + Serial.println(F("RX_EN & TX_EN not used")); + #endif + _rxtxpinmode = false; + } + + + if (_SW >= 0) + { + pinMode( _SW, OUTPUT); //Dorji devices have an RW pin that needs to be set high to power antenna switch + digitalWrite(_SW, HIGH); + } + + resetDevice(); + if (checkDevice()) + { + return true; + } + + return false; +} + + +bool SX126XLT::begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, uint8_t device) +{ + + //format 2 pins for NiceRF, NSS, NRESET, RFBUSY, DIO1 + _NSS = pinNSS; + _NRESET = pinNRESET; + _RFBUSY = pinRFBUSY; + _DIO1 = pinDIO1; + _DIO2 = -1; + _DIO3 = -1; + _RXEN = -1; + _TXEN = -1; + _SW = -1; + _Device = device; + + _TXDonePin = pinDIO1; //this is defalt pin for sensing TX done + _RXDonePin = pinDIO1; //this is defalt pin for sensing RX done + + pinMode(_NSS, OUTPUT); + digitalWrite(_NSS, HIGH); + pinMode(_NRESET, OUTPUT); + digitalWrite(_NRESET, LOW); + pinMode(_RFBUSY, INPUT); + + +#ifdef SX126XDEBUGPINS + Serial.println(F("format 2 NiceRF begin()")); + Serial.println(F("SX126XLT constructor instantiated successfully")); + Serial.print(F("NSS ")); + Serial.println(_NSS); + Serial.print(F("NRESET ")); + Serial.println(_NRESET); + Serial.print(F("RFBUSY ")); + Serial.println(_RFBUSY); + Serial.print(F("DIO1 ")); + Serial.println(_DIO1); + Serial.print(F("DIO2 ")); + Serial.println(_DIO2); + Serial.print(F("DIO3 ")); + Serial.println(_DIO3); + Serial.print(F("RX_EN ")); + Serial.println(_RXEN); + Serial.print(F("TX_EN ")); + Serial.println(_TXEN); + Serial.print(F("SW ")); + Serial.println(_SW); +#endif + + + if (_DIO1 >= 0) + { + pinMode( _DIO1, INPUT); + } + + + #ifdef SX126XDEBUGPINS + Serial.println(F("RX_EN & TX_EN switching disabled")); + #endif + + _rxtxpinmode = false; + + resetDevice(); + + if (checkDevice()) + { + return true; + } + + return false; +} + + + +bool SX126XLT::begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinSW, uint8_t device) +{ + + //format 3 pins for Dorji, NSS, NRESET, RFBUSY, DIO1, SW + _NSS = pinNSS; + _NRESET = pinNRESET; + _RFBUSY = pinRFBUSY; + _DIO1 = pinDIO1; + _DIO2 = -1; + _DIO3 = -1; + _RXEN = -1; + _TXEN = -1; + _SW = pinSW; + _Device = device; + + _TXDonePin = pinDIO1; //this is defalt pin for sensing TX done + _RXDonePin = pinDIO1; //this is defalt pin for sensing RX done + + pinMode(_NSS, OUTPUT); + digitalWrite(_NSS, HIGH); + pinMode(_NRESET, OUTPUT); + digitalWrite(_NRESET, LOW); + pinMode(_RFBUSY, INPUT); + + +#ifdef SX126XDEBUGPINS + Serial.println(F("format 3 Dorji begin()")); + Serial.println(F("SX126XLT constructor instantiated successfully")); + Serial.print(F("NSS ")); + Serial.println(_NSS); + Serial.print(F("NRESET ")); + Serial.println(_NRESET); + Serial.print(F("RFBUSY ")); + Serial.println(_RFBUSY); + Serial.print(F("DIO1 ")); + Serial.println(_DIO1); + Serial.print(F("DIO2 ")); + Serial.println(_DIO2); + Serial.print(F("DIO3 ")); + Serial.println(_DIO3); + Serial.print(F("RX_EN ")); + Serial.println(_RXEN); + Serial.print(F("TX_EN ")); + Serial.println(_TXEN); + Serial.print(F("SW ")); + Serial.println(_SW); +#endif + + + if (_DIO1 >= 0) + { + pinMode( _DIO1, INPUT); + } + + #ifdef SX126XDEBUGPINS + Serial.println(F("RX_EN & TX_EN switching disabled")); + #endif + + _rxtxpinmode = false; + + + if (_SW >= 0) + { + pinMode( _SW, OUTPUT); //Dorji devices have an RW pin that needs to be set high to power antenna switch + digitalWrite(_SW, HIGH); + } + + resetDevice(); + + if (checkDevice()) + { + return true; + } + + return false; +} + + +bool SX126XLT::begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinRXEN, int8_t pinTXEN, uint8_t device) +{ + + //format 4 pins for Ebyte (not tested) , NSS, NRESET, RFBUSY, DIO1, RXEN, TXEN + _NSS = pinNSS; + _NRESET = pinNRESET; + _RFBUSY = pinRFBUSY; + _DIO1 = pinDIO1; + _DIO2 = -1; + _DIO3 = -1; + _RXEN = pinRXEN; + _TXEN = pinTXEN; + _SW = -1; + _Device = device; + + _TXDonePin = pinDIO1; //this is defalt pin for sensing TX done + _RXDonePin = pinDIO1; //this is defalt pin for sensing RX done + + pinMode(_NSS, OUTPUT); + digitalWrite(_NSS, HIGH); + pinMode(_NRESET, OUTPUT); + digitalWrite(_NRESET, LOW); + pinMode(_RFBUSY, INPUT); + + +#ifdef SX126XDEBUGPINS + Serial.println(F("format 4 Ebyte begin()")); + Serial.println(F("SX126XLT constructor instantiated successfully")); + Serial.print(F("NSS ")); + Serial.println(_NSS); + Serial.print(F("NRESET ")); + Serial.println(_NRESET); + Serial.print(F("RFBUSY ")); + Serial.println(_RFBUSY); + Serial.print(F("DIO1 ")); + Serial.println(_DIO1); + Serial.print(F("DIO2 ")); + Serial.println(_DIO2); + Serial.print(F("DIO3 ")); + Serial.println(_DIO3); + Serial.print(F("RX_EN ")); + Serial.println(_RXEN); + Serial.print(F("TX_EN ")); + Serial.println(_TXEN); + Serial.print(F("SW ")); + Serial.println(_SW); +#endif + + + if (_DIO1 >= 0) + { + pinMode( _DIO1, INPUT); + } + + if ((_RXEN >= 0) && (_TXEN >= 0)) + { + #ifdef SX126XDEBUGPINS + Serial.println(F("RX_EN & TX_EN switching enabled")); + #endif + pinMode(_RXEN, OUTPUT); + pinMode(_TXEN, OUTPUT); + _rxtxpinmode = true; + } + else + { + #ifdef SX126XDEBUGPINS + Serial.println(F("RX_EN & TX_EN switching disabled")); + #endif + _rxtxpinmode = false; + } + + + if (_SW >= 0) + { + pinMode( _SW, OUTPUT); //Dorji devices have an RW pin that needs to be set high to power antenna switch + digitalWrite(_SW, HIGH); + } + + resetDevice(); + if (checkDevice()) + { + return true; + } + + return false; +} + + +void SX126XLT::checkBusy() +{ +#ifdef SX126XDEBUG + //Serial.println(F("checkBusy()")); +#endif + + uint8_t busy_timeout_cnt; + busy_timeout_cnt = 0; + + while (digitalRead(_RFBUSY)) + { + delay(1); + busy_timeout_cnt++; + + + //this function checks for a timeout on the busy pin + //if there is a timeout the device is set back to the saved settings + //the fuction is of limited benefit, since you cannot know at which stage of the + //operation the timeout occurs, so operation could resume + if (busy_timeout_cnt > 10) //wait 10mS for busy to complete + { + busy_timeout_cnt = 0; +#ifdef DEBUGBUSY + Serial.println(F("ERROR - Busy Timeout!")); +#endif + resetDevice(); //reset device + setMode(MODE_STDBY_RC); + config(); //re-run saved config + break; + } + } +} + + +void SX126XLT::writeCommand(uint8_t Opcode, uint8_t *buffer, uint16_t size) +{ +#ifdef SX126XDEBUG + //Serial.println(F("writeCommand()")); +#endif + + uint8_t index; + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(Opcode); + + for (index = 0; index < size; index++) + { + SPI.transfer(buffer[index]); + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + if (Opcode != RADIO_SET_SLEEP) + { + checkBusy(); + } +} + + +void SX126XLT::readCommand(uint8_t Opcode, uint8_t *buffer, uint16_t size) +{ +#ifdef SX126XDEBUG + //Serial.println(F("readCommand()")); +#endif + + uint8_t i; + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(Opcode); + SPI.transfer(0xFF); + + for ( i = 0; i < size; i++ ) + { + + *(buffer + i) = SPI.transfer(0xFF); + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + +} + + +void SX126XLT::writeRegisters(uint16_t address, uint8_t *buffer, uint16_t size) +{ +#ifdef SX126XDEBUG + //Serial.println(F("writeRegisters()")); +#endif + uint8_t addr_l, addr_h; + uint8_t i; + + addr_l = address & 0xff; + addr_h = address >> 8; + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_WRITE_REGISTER); + SPI.transfer(addr_h); //MSB + SPI.transfer(addr_l); //LSB + + for (i = 0; i < size; i++) + { + SPI.transfer(buffer[i]); + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif +} + + +void SX126XLT::writeRegister(uint16_t address, uint8_t value) +{ +#ifdef SX126XDEBUG + //Serial.println(F("writeRegisters()")); +#endif + + writeRegisters( address, &value, 1 ); +} + +void SX126XLT::readRegisters(uint16_t address, uint8_t *buffer, uint16_t size) +{ +#ifdef SX126XDEBUG + //Serial.println(F("readRegisters()")); +#endif + + uint16_t index; + uint8_t addr_l, addr_h; + + addr_h = address >> 8; + addr_l = address & 0x00FF; + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_READ_REGISTER); + SPI.transfer(addr_h); //MSB + SPI.transfer(addr_l); //LSB + SPI.transfer(0xFF); + for (index = 0; index < size; index++) + { + *(buffer + index) = SPI.transfer(0xFF); + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + +} + + +uint8_t SX126XLT::readRegister(uint16_t address) +{ +#ifdef SX126XDEBUG + //Serial.println(F("readRegister()")); +#endif + + uint8_t data; + + readRegisters(address, &data, 1); + return data; +} + +void SX126XLT::resetDevice() +{ +#ifdef SX126XDEBUG + Serial.println(F("resetDevice()")); +#endif + + delay(10); + digitalWrite(_NRESET, LOW); + delay(2); + digitalWrite(_NRESET, HIGH); + delay(25); + checkBusy(); +} + + + +bool SX126XLT::checkDevice() +{ + //check there is a device out there, writes a register and reads back +#ifdef SX126XDEBUG + Serial.println(F("checkDevice()")); +#endif + + uint8_t Regdata1, Regdata2; + Regdata1 = readRegister(0x88e); //low byte of frequency setting + writeRegister(0x88e, (Regdata1 + 1)); + Regdata2 = readRegister(0x88e); //read changed value back + writeRegister(0x88e, Regdata1); //restore register to original value + + if (Regdata2 == (Regdata1 + 1)) + { + return true; + } + else + { + return false; + } +} + +void SX126XLT::setupLoRa(uint32_t frequency, int32_t offset, uint8_t modParam1, uint8_t modParam2, uint8_t modParam3, uint8_t modParam4) +{ + //order of passed parameters is, frequency, offset, spreadingfactor, bandwidth, coderate, optimisation + +#ifdef SX126XDEBUG + Serial.println(F("setupLoRa()")); +#endif + setMode(MODE_STDBY_RC); + setRegulatorMode(USE_DCDC); + setPaConfig(0x04, PAAUTO, _Device); //use _Device, saved by begin. + setDIO3AsTCXOCtrl(TCXO_CTRL_3_3V); + calibrateDevice(ALLDevices); //is required after setting TCXO + calibrateImage(frequency); + setDIO2AsRfSwitchCtrl(); + setPacketType(PACKET_TYPE_LORA); + setRfFrequency(frequency, offset); + setModulationParams(modParam1, modParam2, modParam3, modParam4); + setBufferBaseAddress(0, 0); + setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + setHighSensitivity(); //set for maximum gain + setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); +} + + +void SX126XLT::setMode(uint8_t modeconfig) +{ +#ifdef SX126XDEBUG + Serial.println(F("setMode()")); +#endif + + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_SET_STANDBY); + SPI.transfer(modeconfig); + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + _OperatingMode = modeconfig; +} + + +void SX126XLT::setRegulatorMode(uint8_t mode) +{ +#ifdef SX126XDEBUG + Serial.println(F("setRegulatorMode()")); +#endif + + savedRegulatorMode = mode; + + writeCommand(RADIO_SET_REGULATORMODE, &mode, 1); +} + + +void SX126XLT::setPaConfig(uint8_t dutycycle, uint8_t hpMax, uint8_t device) +{ +#ifdef SX126XDEBUG + Serial.println(F("setPaConfig()")); +#endif + + + uint8_t buffer[4]; + + if (hpMax == PAAUTO) + { + if (device == DEVICE_SX1261) + { + hpMax = 0x00; + } + if (device == DEVICE_SX1262) + { + hpMax = 0x07; + } + if (device == DEVICE_SX1268) + { + hpMax = 0x07; + } + } + + if (_Device == DEVICE_SX1261) + { + device = 1; + } + else + { + device = 0; + } + + buffer[0] = dutycycle; //paDutyCycle + buffer[1] = hpMax; //hpMax:0x00~0x07; 7 for =22dbm + buffer[2] = device; //deviceSel: 0 = SX1262; 1 = SX1261; 0 = SX1268; + buffer[3] = 0x01; //reserved, always 0x01 + + writeCommand(RADIO_SET_PACONFIG, buffer, 4); +} + + +void SX126XLT::setDIO3AsTCXOCtrl(uint8_t tcxoVoltage) +{ +#ifdef SX126XDEBUG + Serial.println(F("setDIO3AsTCXOCtrl()")); +#endif + + uint8_t buffer[4]; + + buffer[0] = tcxoVoltage; + buffer[1] = 0x00; + buffer[2] = 0x00; + buffer[3] = 0x64; + + writeCommand(RADIO_SET_TCXOMODE, buffer, 4); +} + + +void SX126XLT::calibrateDevice(uint8_t devices) +{ +#ifdef SX126XDEBUG + Serial.println(F("calibrateDevice()")); +#endif + + writeCommand(RADIO_CALIBRATE, &devices, 1); + delay(5); //calibration time for all devices is 3.5mS, SX126x + +} + + +void SX126XLT::calibrateImage(uint32_t freq) +{ +#ifdef SX126XDEBUG + Serial.println(F("calibrateImage()")); +#endif + + uint8_t calFreq[2]; + + if ( freq > 900000000 ) + { + calFreq[0] = 0xE1; + calFreq[1] = 0xE9; + } + else if ( freq > 850000000 ) + { + calFreq[0] = 0xD7; + calFreq[1] = 0xD8; + } + else if ( freq > 770000000 ) + { + calFreq[0] = 0xC1; + calFreq[1] = 0xC5; + } + else if ( freq > 460000000 ) + { + calFreq[0] = 0x75; + calFreq[1] = 0x81; + } + else if ( freq > 425000000 ) + { + calFreq[0] = 0x6B; + calFreq[1] = 0x6F; + } + writeCommand( RADIO_CALIBRATEIMAGE, calFreq, 2 ); +} + + +void SX126XLT::setDIO2AsRfSwitchCtrl() +{ +#ifdef SX126XDEBUG + Serial.println(F("setDIO2AsRfSwitchCtrl()")); +#endif + + uint8_t mode = 0x01; + + writeCommand(RADIO_SET_RFSWITCHMODE, &mode, 1); +} + + +void SX126XLT::setPacketType(uint8_t packettype ) +{ +#ifdef SX126XDEBUG + Serial.println(F("setPacketType()")); +#endif + savedPacketType = packettype; + + writeCommand(RADIO_SET_PACKETTYPE, &packettype, 1); +} + + +void SX126XLT::setModulationParams(uint8_t modParam1, uint8_t modParam2, uint8_t modParam3, uint8_t modParam4) +{ + //order for LoRa is spreading factor, bandwidth, code rate, optimisation + +#ifdef SX126XDEBUG + Serial.println(F("setModulationParams()")); +#endif + uint8_t regvalue; + uint8_t buffer[4]; + + regvalue = readRegister(REG_TX_MODULATION); + + savedModParam1 = modParam1; + savedModParam2 = modParam2; + savedModParam3 = modParam3; + + if (modParam2 == LORA_BW_500) + { + writeRegister(REG_TX_MODULATION, (regvalue & 0xFB)); //if bandwidth is 500k set bit 2 to 0, see datasheet 15.1.1 + } + else + { + writeRegister(REG_TX_MODULATION, (regvalue | 0x04)); //if bandwidth is < 500k set bit 2 to 0 see datasheet 15.1.1 + } + + if (modParam4 == LDRO_AUTO) + { + modParam4 = returnOptimisation(modParam1, modParam2); //pass Spreading factor then bandwidth to optimisation calc + } + + savedModParam4 = modParam4; + + buffer[0] = modParam1; + buffer[1] = modParam2; + buffer[2] = modParam3; + buffer[3] = modParam4; + + writeCommand(RADIO_SET_MODULATIONPARAMS, buffer, 4); +} + + +uint8_t SX126XLT::returnOptimisation(uint8_t SpreadingFactor, uint8_t Bandwidth) +{ + //from the passed bandwidth (bandwidth) and spreading factor this routine + //calculates whether low data rate optimisation should be on or off + +#ifdef SX126XDEBUG + Serial.println(F("returnOptimisation()")); +#endif + + uint32_t tempBandwidth; + float symbolTime; + + tempBandwidth = returnBandwidth(Bandwidth); + + symbolTime = calcSymbolTime(tempBandwidth, SpreadingFactor); + + if (symbolTime > 16) + { + return LDRO_ON; + } + else + { + return LDRO_OFF; + } +} + + +uint32_t SX126XLT::returnBandwidth(uint8_t BWregvalue) +{ + +#ifdef SX126XDEBUG + Serial.println(F("returnBandwidth()")); +#endif + + switch (BWregvalue) + { + case 0: + return 7800; + + case 8: + return 10400; + + case 1: + return 15600; + + case 9: + return 20800; + + case 2: + return 31200; + + case 10: + return 41700; + + case 3: + return 62500; + + case 4: + return 125000; + + case 5: + return 250000; + + case 6: + return 500000; + + default: + break; + } + return 0xFFFF; //so that a bandwidth not set can be identified +} + + + +float SX126XLT::calcSymbolTime(float Bandwidth, uint8_t SpreadingFactor) +{ + //calculates symbol time from passed bandwidth (lbandwidth) and Spreading factor (lSF)and returns in mS + +#ifdef SX126XDEBUG + Serial.println(F("calcSymbolTime()")); +#endif + + float symbolTimemS; + symbolTimemS = (Bandwidth / pow(2, SpreadingFactor)); + symbolTimemS = (1000 / symbolTimemS); + return symbolTimemS; +} + + +void SX126XLT::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) +{ +#ifdef SX126XDEBUG + Serial.println(F("setBufferBaseAddress()")); +#endif + + uint8_t buffer[2]; + + buffer[0] = txBaseAddress; + buffer[1] = rxBaseAddress; + writeCommand(RADIO_SET_BUFFERBASEADDRESS, buffer, 2); +} + + +void SX126XLT::setPacketParams(uint16_t packetParam1, uint8_t packetParam2, uint8_t packetParam3, uint8_t packetParam4, uint8_t packetParam5) +{ + //order is preamble, header type, packet length, CRC, IQ + +#ifdef SX126XDEBUG + Serial.println(F("SetPacketParams()")); +#endif + + uint8_t preambleMSB, preambleLSB; + + preambleMSB = packetParam1 >> 8; + preambleLSB = packetParam1 & 0xFF; + + savedPacketParam1 = packetParam1; + savedPacketParam2 = packetParam2; + savedPacketParam3 = packetParam3; + savedPacketParam4 = packetParam4; + savedPacketParam5 = packetParam5; + + uint8_t buffer[9]; + buffer[0] = preambleMSB; + buffer[1] = preambleLSB; + buffer[2] = packetParam2; + buffer[3] = packetParam3; + buffer[4] = packetParam4; + buffer[5] = packetParam5; + buffer[6] = 0xFF; + buffer[7] = 0xFF; + buffer[8] = 0xFF; + writeCommand(RADIO_SET_PACKETPARAMS, buffer, 9); +} + + +void SX126XLT::setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask ) +{ +#ifdef SX126XDEBUG + Serial.println(F("setDioIrqParams()")); +#endif + + savedIrqMask = irqMask; + savedDio1Mask = dio1Mask; + savedDio2Mask = dio2Mask; + savedDio3Mask = dio3Mask; + + uint8_t buffer[8]; + + buffer[0] = (uint8_t) (irqMask >> 8); + buffer[1] = (uint8_t) (irqMask & 0xFF); + buffer[2] = (uint8_t) (dio1Mask >> 8); + buffer[3] = (uint8_t) (dio1Mask & 0xFF); + buffer[4] = (uint8_t) (dio2Mask >> 8); + buffer[5] = (uint8_t) (dio2Mask & 0xFF); + buffer[6] = (uint8_t) (dio3Mask >> 8); + buffer[7] = (uint8_t) (dio3Mask & 0xFF); + writeCommand(RADIO_CFG_DIOIRQ, buffer, 8); +} + + +void SX126XLT::setHighSensitivity() +{ + //set RX Boosted gain mode +#ifdef SX126XDEBUG + Serial.println(F("setHighSensitivity()")); +#endif + writeRegister( REG_RX_GAIN, BOOSTED_GAIN ); //max LNA gain, increase current by ~2mA for around ~3dB in sensivity +} + +void SX126XLT::setLowPowerRX() +{ + //set RX power saving mode +#ifdef SX126XDEBUG + Serial.println(F("setLowPowerRX()")); +#endif + + writeRegister( REG_RX_GAIN, POWER_SAVE_GAIN ); // min LNA gain, reduce current by 2mA for around 3dB loss in sensivity +} + + +void SX126XLT::setSyncWord(uint16_t syncword) +{ +#ifdef SX126XDEBUG + Serial.println(F("setSyncWord()")); +#endif + writeRegister( REG_LR_SYNCWORD, ( syncword >> 8 ) & 0xFF ); + writeRegister( REG_LR_SYNCWORD + 1, syncword & 0xFF ); +} + + +void SX126XLT::printModemSettings() +{ +#ifdef SX126XDEBUG + Serial.println(F("printModemSettings()")); +#endif + + printDevice(); + Serial.print(F(",")); + Serial.print(getFreqInt()); + Serial.print(F("hz,SF")); + Serial.print(getLoRaSF()); + Serial.print(F(",BW")); + Serial.print(returnBandwidth(savedModParam2)); + Serial.print(F(",CR4:")); + Serial.print((getLoRaCodingRate() + 4)); + Serial.print(F(",LDRO_")); + + if (getOptimisation()) + { + Serial.print(F("On")); + } + else + { + Serial.print(F("Off")); + } + + Serial.print(F(",SyncWord_0x")); + Serial.print(getSyncWord(), HEX); + if (getInvertIQ() == LORA_IQ_INVERTED) + { + Serial.print(F(",IQInverted")); + } + else + { + Serial.print(F(",IQNormal")); + } + Serial.print(F(",Preamble_")); + Serial.print(getPreamble()); +} + + +uint32_t SX126XLT::getFreqInt() +{ + //get the current set device frequency from registers, return as long integer +#ifdef SX126XDEBUG + Serial.println(F("getFreqInt()")); +#endif + + uint8_t MsbH, MsbL, Mid, Lsb; + uint32_t uinttemp; + float floattemp; + MsbH = readRegister(REG_RFFrequency31_24); + MsbL = readRegister(REG_RFFrequency23_16); + Mid = readRegister(REG_RFFrequency15_8); + Lsb = readRegister(REG_RFFrequency7_0); + floattemp = ( (MsbH * 0x1000000ul) + (MsbL * 0x10000ul) + (Mid * 0x100ul) + Lsb); + floattemp = ((floattemp * FREQ_STEP) / 1000000ul); + uinttemp = (uint32_t)(floattemp * 1000000); + return uinttemp; +} + + +uint8_t SX126XLT::getLoRaCodingRate() +{ +#ifdef SX126XDEBUG + Serial.println(F("getLoRaCodingRate")); +#endif + + return savedModParam3; +} + + +uint8_t SX126XLT::getOptimisation() +{ +#ifdef SX126XDEBUG + Serial.println(F("getOptimisation")); +#endif + + return savedModParam4; +} + + +uint16_t SX126XLT::getSyncWord() +{ +#ifdef SX126XDEBUG + Serial.println(F("getSyncWord")); +#endif + + uint8_t msb, lsb; + uint16_t syncword; + msb = readRegister(REG_LR_SYNCWORD); + lsb = readRegister(REG_LR_SYNCWORD + 1); + syncword = (msb << 8) + lsb; + + return syncword; +} + + +uint16_t SX126XLT::getPreamble() +{ +#ifdef SX126XDEBUG + Serial.println(F("getPreamble")); +#endif + + return savedPacketParam1; +} + + +void SX126XLT::printOperatingSettings() +{ +#ifdef SX126XDEBUG + Serial.println(F("printOperatingSettings()")); +#endif + + printDevice(); + + Serial.print(F(",PacketMode_")); + + if (savedPacketType == PACKET_TYPE_LORA) + { + Serial.print(F("LoRa")); + } + + if (savedPacketType == PACKET_TYPE_GFSK) + { + Serial.print(F("GFSK")); + } + + if (getHeaderMode()) + { + Serial.print(F(",Implicit")); + } + else + { + Serial.print(F(",Explicit")); + } + + Serial.print(F(",LNAgain_")); + + if (getLNAgain() == BOOSTED_GAIN) + { + Serial.print(F("Boosted")); + } + else + { + Serial.print(F("Powersave")); + } + +} + + +uint8_t SX126XLT::getHeaderMode() +{ +#ifdef SX126XDEBUG + Serial.println(F("getHeaderMode")); +#endif + + return savedPacketParam2; +} + + +uint8_t SX126XLT::getLNAgain() +{ +#ifdef SX126XDEBUG + Serial.println(F("getLNAgain")); +#endif + + return readRegister(REG_RX_GAIN); +} + + +void SX126XLT::setTxParams(int8_t TXpower, uint8_t RampTime) +{ + + //note this routine does not check if power levels are valid for the module in use +#ifdef SX126XDEBUG + Serial.println(F("setTxParams()")); +#endif + + uint8_t buffer[2]; + + savedTXPower = TXpower; + + buffer[0] = TXpower; + buffer[1] = (uint8_t)RampTime; + writeCommand(RADIO_SET_TXPARAMS, buffer, 2); +} + + +void SX126XLT::setTx(uint32_t timeout) +{ + //SX126x base timeout in units of 15.625 µs + //Note: timeout passed to function is in mS + +#ifdef SX126XDEBUG + Serial.println(F("setTx()")); +#endif + uint8_t buffer[3]; + + clearIrqStatus(IRQ_RADIO_ALL); + + if (_rxtxpinmode) + { + txEnable(); + } + + timeout = timeout << 6; //timeout passed in mS, convert to units of 15.625us + + buffer[0] = (timeout >> 16) & 0xFF; + buffer[1] = (timeout >> 8) & 0xFF; + buffer[2] = timeout & 0xFF; + + writeCommand(RADIO_SET_TX, buffer, 3 ); + _OperatingMode = MODE_TX; +} + + +void SX126XLT::clearIrqStatus(uint16_t irqMask) +{ +#ifdef SX126XDEBUG + Serial.println(F("clearIrqStatus()")); +#endif + + uint8_t buffer[2]; + + buffer[0] = (uint8_t) (irqMask >> 8); + buffer[1] = (uint8_t) (irqMask & 0xFF); + writeCommand(RADIO_CLR_IRQSTATUS, buffer, 2); +} + + +uint16_t SX126XLT::readIrqStatus() +{ +#ifdef SX126XDEBUG + Serial.print(F("readIrqStatus()")); +#endif + + uint16_t temp; + uint8_t buffer[2]; + + readCommand(RADIO_GET_IRQSTATUS, buffer, 2); + temp = ((buffer[0] << 8) + buffer[1]); + return temp; +} + + +uint16_t SX126XLT::CRCCCITT(uint8_t *buffer, uint8_t size, uint16_t start) +{ +#ifdef SX126XDEBUG + Serial.println(F("CRCCCITT()")); +#endif + + uint16_t index, libraryCRC; + uint8_t j; + + libraryCRC = start; //start value for CRC16 + + for (index = 0; index < size; index++) + { + libraryCRC ^= (((uint16_t)buffer[index]) << 8); + for (j = 0; j < 8; j++) + { + if (libraryCRC & 0x8000) + libraryCRC = (libraryCRC << 1) ^ 0x1021; + else + libraryCRC <<= 1; + } + } + + return libraryCRC; +} + + +uint8_t SX126XLT::transmit(uint8_t *txbuffer, uint8_t size, uint32_t txtimeout, int8_t txpower, uint8_t wait) +{ +#ifdef SX126XDEBUG + Serial.println(F("transmit()")); +#endif + uint8_t index; + uint8_t bufferdata; + + if (size == 0) + { + return false; + } + + setMode(MODE_STDBY_RC); + setBufferBaseAddress(0, 0); + + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_WRITE_BUFFER); + SPI.transfer(0); + + for (index = 0; index < size; index++) + { + bufferdata = txbuffer[index]; + SPI.transfer(bufferdata); + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + _TXPacketL = size; + writeRegister(REG_LR_PAYLOADLENGTH, _TXPacketL); + setTxParams(txpower, RADIO_RAMP_200_US); + + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + setTx(txtimeout); //this starts the TX + + if (!wait) + { + return _TXPacketL; + } + + while (!digitalRead(_TXDonePin)); //Wait for DIO1 to go high + + if (readIrqStatus() & IRQ_RX_TX_TIMEOUT ) //check for timeout + { + return 0; + } + else + { + return _TXPacketL; + } +} + + +void SX126XLT::printIrqStatus() +{ +#ifdef SX126XDEBUG + Serial.println(F("printIrqStatus()")); +#endif + + uint16_t _IrqStatus; + _IrqStatus = readIrqStatus(); + + //0x0001 + if (_IrqStatus & IRQ_TX_DONE) + { + Serial.print(F(",IRQ_TX_DONE")); + } + + //0x0002 + if (_IrqStatus & IRQ_RX_DONE) + { + Serial.print(F(",IRQ_RX_DONE")); + } + + //0x0004 + if (_IrqStatus & IRQ_PREAMBLE_DETECTED) + { + Serial.print(F(",IRQ_PREAMBLE_DETECTED")); + } + + //0x0008 + if (_IrqStatus & IRQ_SYNCWORD_VALID) + { + Serial.print(F(",IRQ_SYNCWORD_VALID")); + } + + //0x0010 + if (_IrqStatus & IRQ_HEADER_VALID) + { + Serial.print(F(",IRQ_HEADER_VALID")); + } + + //0x0020 + if (_IrqStatus & IRQ_HEADER_ERROR) + { + Serial.print(F(",IRQ_HEADER_ERROR")); + } + + //0x0040 + if (_IrqStatus & IRQ_CRC_ERROR) + { + Serial.print(F(",IRQ_CRC_ERROR")); + } + + //0x0080 + if (_IrqStatus & IRQ_CAD_DONE) + { + Serial.print(F(",IRQ_CAD_DONE")); + } + + //0x0100 + if (_IrqStatus & IRQ_CAD_ACTIVITY_DETECTED) + { + Serial.print(",IRQ_CAD_ACTIVITY_DETECTED"); + } + + //0x0200 + if (_IrqStatus & IRQ_RX_TX_TIMEOUT) + { + Serial.print(F(",IRQ_RX_TX_TIMEOUT")); + } + +} + + +void SX126XLT::printRegisters(uint16_t Start, uint16_t End) +{ + //prints the contents of SX1262 registers to serial monitor + +#ifdef SX126XDEBUG + Serial.println(F("printRegisters()")); +#endif + + uint16_t Loopv1, Loopv2, RegData; + + Serial.print(F("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F")); + Serial.println(); + + for (Loopv1 = Start; Loopv1 <= End;) //32 lines + { + Serial.print(F("0x")); + Serial.print((Loopv1), HEX); //print the register number + Serial.print(F(" ")); + for (Loopv2 = 0; Loopv2 <= 15; Loopv2++) + { + RegData = readRegister(Loopv1); + if (RegData < 0x10) + { + Serial.print(F("0")); + } + Serial.print(RegData, HEX); //print the register number + Serial.print(F(" ")); + Loopv1++; + } + Serial.println(); + } +} + + +void SX126XLT::printDevice() +{ +#ifdef SX126XDEBUG + Serial.println(F("printDevice()")); +#endif + + + switch (_Device) + { + case DEVICE_SX1261: + Serial.print(F("SX1261")); + break; + + case DEVICE_SX1262: + Serial.print(F("SX1262")); + break; + + case DEVICE_SX1268: + Serial.print(F("SX1268")); + break; + + default: + Serial.print(F("Unknown Device")); + + } +} + + +bool SX126XLT::config() +{ +#ifdef SX126XDEBUG + Serial.println(F("config()")); +#endif + + resetDevice(); + setMode(MODE_STDBY_RC); + setRegulatorMode(savedRegulatorMode); + setPacketType(savedPacketType); + setRfFrequency(savedFrequency, savedOffset); + setModulationParams(savedModParam1, savedModParam2, savedModParam3, LDRO_ON); + setPacketParams(savedPacketParam1, savedPacketParam2, savedPacketParam3, savedPacketParam4, savedPacketParam5); + setDioIrqParams(savedIrqMask, savedDio1Mask, savedDio2Mask, savedDio3Mask); //set for IRQ on RX done on DIO1 + _TXPacketL = 0; + _RXPacketL = 0; + return true; +} + + +void SX126XLT::setRfFrequency( uint32_t frequency, int32_t offset ) +{ + //Note RF_Freq = freq_reg*32M/(2^25)-----> freq_reg = (RF_Freq * (2^25))/32 + +#ifdef SX126XDEBUG + Serial.print(F("setRfFrequency() ")); + Serial.println(frequency + offset); +#endif + + uint8_t buffer[4]; + uint32_t localfrequencyRegs; + + savedFrequency = frequency; + savedOffset = offset; + + localfrequencyRegs = frequency + offset; + + localfrequencyRegs = ( uint32_t )( ( double )localfrequencyRegs / ( double )FREQ_STEP ); + + savedFrequencyReg = localfrequencyRegs; + + buffer[0] = (localfrequencyRegs >> 24) & 0xFF; //MSB + buffer[1] = (localfrequencyRegs >> 16) & 0xFF; + buffer[2] = (localfrequencyRegs >> 8) & 0xFF; + buffer[3] = localfrequencyRegs & 0xFF;//LSB + + _freqregH = buffer[0]; + _freqregMH = buffer[1]; + _freqregML = buffer[2]; + _freqregL = buffer[3]; + + writeCommand(RADIO_SET_RFFREQUENCY, buffer, 4); +} + + +uint8_t SX126XLT::getLoRaSF() +{ +#ifdef SX126XDEBUG + Serial.println(F("getLoRaSF()")); +#endif + + return savedModParam1; +} + + +uint8_t SX126XLT::getInvertIQ() +{ + //IQ mode reg 0x33 +#ifdef SX126XDEBUG + Serial.println(F("getInvertIQ")); +#endif + + return readRegister(REG_IQ_POLARITY_SETUP); +} + + +void SX126XLT::rxEnable() +{ +#ifdef SX126XDEBUG + Serial.println(F("rxEnable()")); +#endif + + digitalWrite(_RXEN, HIGH); + digitalWrite(_TXEN, LOW); +} + + +void SX126XLT::txEnable() +{ +#ifdef SX126XDEBUGPINS + Serial.println(F("txEnable()")); +#endif + + digitalWrite(_RXEN, LOW); + digitalWrite(_TXEN, HIGH); +} + +void SX126XLT::printASCIIPacket(uint8_t *buffer, uint8_t size) +{ +#ifdef SX126XDEBUGPINS + Serial.println(F("printASCIIPacket()")); +#endif + + uint8_t index; + + for (index = 0; index < size; index++) + { + Serial.write(buffer[index]); + } + +} + + +uint8_t SX126XLT::receive(uint8_t *rxbuffer, uint8_t size, uint32_t rxtimeout, uint8_t wait) +{ +#ifdef SX126XDEBUG + Serial.println(F("receive()")); +#endif + + uint8_t index, RXstart, RXend; + uint16_t regdata; + uint8_t buffer[2]; + + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on RX done or timeout + setRx(rxtimeout); + + if (!wait) + { + return 0; //not wait requested so no packet length to pass + } + + while (!digitalRead(_RXDonePin)); //Wait for DIO1 to go high + + setMode(MODE_STDBY_RC); //ensure to stop further packet reception + + regdata = readIrqStatus(); + + if ( (regdata & IRQ_HEADER_ERROR) | (regdata & IRQ_CRC_ERROR) | (regdata & IRQ_RX_TX_TIMEOUT ) ) //check if any of the preceding IRQs is set + { + //packet is errored somewhere so return 0 + return 0; + } + + readCommand(RADIO_GET_RXBUFFERSTATUS, buffer, 2); + _RXPacketL = buffer[0]; + + if (_RXPacketL > size) //check passed buffer is big enough for packet + { + _RXPacketL = size; //truncate packet if not enough space + } + + RXstart = buffer[1]; + + RXend = RXstart + _RXPacketL; + + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(RXstart); + SPI.transfer(0xFF); + + for (index = RXstart; index < RXend; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + +uint8_t SX126XLT::readPacketRSSI() +{ +#ifdef SX126XDEBUG + Serial.println(F("readPacketRSSI()")); +#endif + + uint8_t status[5]; + + readCommand(RADIO_GET_PACKETSTATUS, status, 5) ; + _PacketRSSI = -status[0] / 2; + + return _PacketRSSI; +} + + +uint8_t SX126XLT::readPacketSNR() +{ +#ifdef SX126XDEBUG + Serial.println(F("readPacketSNR()")); +#endif + + uint8_t status[5]; + + readCommand(RADIO_GET_PACKETSTATUS, status, 5) ; + + if ( status[1] < 128 ) + { + _PacketSNR = status[1] / 4 ; + } + else + { + _PacketSNR = (( status[1] - 256 ) / 4); + } + + return _PacketSNR; +} + +uint8_t SX126XLT::readRXPacketL() +{ +#ifdef SX126XDEBUG + Serial.println(F("readRXPacketL()")); +#endif + + uint8_t buffer[2]; + + readCommand(RADIO_GET_RXBUFFERSTATUS, buffer, 2); + _RXPacketL = buffer[0]; + return _RXPacketL; +} + + +void SX126XLT::setRx(uint32_t timeout) +{ + //SX126x base timeout in units of 15.625 µs + //timeout passed to function in mS + //range is 1mS to 262 seconds + +#ifdef SX126XDEBUG + Serial.println(F("setRx()")); +#endif + uint8_t buffer[3]; + clearIrqStatus(IRQ_RADIO_ALL); + + if (_rxtxpinmode) + { + rxEnable(); + } + + timeout = timeout << 6; //timeout passed in mS, multiply by 64 to convert units of 15.625us to 1mS + + buffer[0] = (timeout >> 16) & 0xFF; + buffer[1] = (timeout >> 8) & 0xFF; + buffer[2] = timeout & 0xFF; + writeCommand(RADIO_SET_RX, buffer, 3 ); +} + +/*************************************************************************** +//Start direct access SX buffer routines +***************************************************************************/ + +void SX126XLT::startWriteSXBuffer(uint8_t ptr) +{ +#ifdef SX126XDEBUG + Serial.println(F("startWriteSXBuffer()")); +#endif + + _TXPacketL = 0; //this variable used to keep track of bytes written + setMode(MODE_STDBY_RC); + setBufferBaseAddress(ptr, 0); //TX,RX + + checkBusy(); + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_WRITE_BUFFER); + SPI.transfer(ptr); + //SPI interface ready for byte to write to buffer +} + + +uint8_t SX126XLT::endWriteSXBuffer() +{ +#ifdef SX126XDEBUG + Serial.println(F("endWriteSXBuffer()")); +#endif + + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _TXPacketL; + +} + + +void SX126XLT::startReadSXBuffer(uint8_t ptr) +{ +#ifdef SX126XDEBUG + Serial.println(F("startReadSXBuffer")); +#endif + + _RXPacketL = 0; + + checkBusy(); + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); + #endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(ptr); + SPI.transfer(0xFF); + + //next line would be data = SPI.transfer(0); + //SPI interface ready for byte to read from +} + + +uint8_t SX126XLT::endReadSXBuffer() +{ +#ifdef SX126XDEBUG + Serial.println(F("endReadSXBuffer()")); +#endif + + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; +} + + +void SX126XLT::writeUint8(uint8_t x) +{ +#ifdef SX126XDEBUG + Serial.println(F("writeUint8()")); +#endif + + SPI.transfer(x); + + _TXPacketL++; //increment count of bytes written +} + +uint8_t SX126XLT::readUint8() +{ +#ifdef SX126XDEBUG + Serial.println(F("readUint8()")); +#endif + byte x; + + x = SPI.transfer(0); + + _RXPacketL++; //increment count of bytes read + return (x); +} + + +void SX126XLT::writeInt8(int8_t x) +{ +#ifdef SX126XDEBUG + Serial.println(F("writeInt8()")); +#endif + + SPI.transfer(x); + + _TXPacketL++; //increment count of bytes written +} + + +int8_t SX126XLT::readInt8() +{ +#ifdef SX126XDEBUG + Serial.println(F("readInt8()")); +#endif + int8_t x; + + x = SPI.transfer(0); + + _RXPacketL++; //increment count of bytes read + return (x); +} + + +void SX126XLT::writeInt16(int16_t x) +{ +#ifdef SX126XDEBUG + Serial.println(F("writeInt16()")); +#endif + + SPI.transfer(lowByte(x)); + SPI.transfer(highByte(x)); + + _TXPacketL = _TXPacketL + 2; //increment count of bytes written +} + + +int16_t SX126XLT::readInt16() +{ +#ifdef SX126XDEBUG + Serial.println(F("readInt16()")); +#endif + byte lowbyte, highbyte; + + lowbyte = SPI.transfer(0); + highbyte = SPI.transfer(0); + + _RXPacketL = _RXPacketL + 2; //increment count of bytes read + return ((highbyte << 8) + lowbyte); +} + + +void SX126XLT::writeUint16(uint16_t x) +{ +#ifdef SX126XDEBUG + Serial.println(F("writeUint16()")); +#endif + + SPI.transfer(lowByte(x)); + SPI.transfer(highByte(x)); + + _TXPacketL = _TXPacketL + 2; //increment count of bytes written +} + + +uint16_t SX126XLT::readUint16() +{ +#ifdef SX126XDEBUG + Serial.println(F("writeUint16()")); +#endif + byte lowbyte, highbyte; + + lowbyte = SPI.transfer(0); + highbyte = SPI.transfer(0); + + _RXPacketL = _RXPacketL + 2; //increment count of bytes read + return ((highbyte << 8) + lowbyte); +} + + +void SX126XLT::writeInt32(int32_t x) +{ +#ifdef SX126XDEBUG + Serial.println(F("writeInt32()")); +#endif + + byte i, j; + + union + { + byte b[4]; + int32_t f; + } data; + data.f = x; + + for (i = 0; i < 4; i++) + { + j = data.b[i]; + SPI.transfer(j); + } + + _TXPacketL = _TXPacketL + 4; //increment count of bytes written +} + + +int32_t SX126XLT::readInt32() +{ +#ifdef SX126XDEBUG + Serial.println(F("readInt32()")); +#endif + + byte i, j; + + union + { + byte b[4]; + int32_t f; + } readdata; + + for (i = 0; i < 4; i++) + { + j = SPI.transfer(0); + readdata.b[i] = j; + } + _RXPacketL = _RXPacketL + 4; //increment count of bytes read + return readdata.f; +} + + +void SX126XLT::writeUint32(uint32_t x) +{ +#ifdef SX126XDEBUG + Serial.println(F("writeUint32()")); +#endif + + byte i, j; + + union + { + byte b[4]; + uint32_t f; + } data; + data.f = x; + + for (i = 0; i < 4; i++) + { + j = data.b[i]; + SPI.transfer(j); + } + + _TXPacketL = _TXPacketL + 4; //increment count of bytes written +} + + +uint32_t SX126XLT::readUint32() +{ +#ifdef SX126XDEBUG + Serial.println(F("readUint32()")); +#endif + + byte i, j; + + union + { + byte b[4]; + uint32_t f; + } readdata; + + for (i = 0; i < 4; i++) + { + j = SPI.transfer(0); + readdata.b[i] = j; + } + _RXPacketL = _RXPacketL + 4; //increment count of bytes read + return readdata.f; +} + + +void SX126XLT::writeFloat(float x) +{ +#ifdef SX126XDEBUG + Serial.println(F("writeFloat()")); +#endif + + byte i, j; + + union + { + byte b[4]; + float f; + } data; + data.f = x; + + for (i = 0; i < 4; i++) + { + j = data.b[i]; + SPI.transfer(j); + } + + _TXPacketL = _TXPacketL + 4; //increment count of bytes written +} + + +float SX126XLT::readFloat() +{ +#ifdef SX126XDEBUG + Serial.println(F("readFloat()")); +#endif + + byte i, j; + + union + { + byte b[4]; + float f; + } readdata; + + for (i = 0; i < 4; i++) + { + j = SPI.transfer(0); + readdata.b[i] = j; + } + _RXPacketL = _RXPacketL + 4; //increment count of bytes read + return readdata.f; +} + + +uint8_t SX126XLT::transmitSXBuffer(uint8_t startaddr, uint8_t length, uint32_t txtimeout, int8_t txpower, uint8_t wait) +{ +#ifdef SX126XDEBUG + Serial.println(F("transmitSXBuffer()")); +#endif + + setBufferBaseAddress(startaddr, 0); //TX, RX + + setPacketParams(savedPacketParam1, savedPacketParam2, length, savedPacketParam4, savedPacketParam5); + setTxParams(txpower, RAMP_TIME); + + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + setTx(txtimeout); //this starts the TX + + if (!wait) + { + return _TXPacketL; + } + + while (!digitalRead(_TXDonePin)); //Wait for DIO1 to go high + + if (readIrqStatus() & IRQ_RX_TX_TIMEOUT ) //check for timeout + { + return 0; + } + else + { + return _TXPacketL; + } +} + + +void SX126XLT::writeBuffer(uint8_t *txbuffer, uint8_t size) +{ +#ifdef SX126XDEBUG1 + Serial.println(F("writeBuffer()")); +#endif + + uint8_t index, regdata; + + _TXPacketL = _TXPacketL + size; //these are the number of bytes that will be added + + size--; //loose one byte from size, the last byte written MUST be a 0 + + for (index = 0; index < size; index++) + { + regdata = txbuffer[index]; + SPI.transfer(regdata); + } + + SPI.transfer(0); //this ensures last byte of buffer written really is a null (0) + +} + + +uint8_t SX126XLT::receiveSXBuffer(uint8_t startaddr, uint32_t rxtimeout, uint8_t wait ) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("receiveSXBuffer()")); +#endif + + uint16_t regdata; + uint8_t buffer[2]; + + setMode(MODE_STDBY_RC); + + setBufferBaseAddress(0, startaddr); //order is TX RX + + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on RX done or timeout + setRx(rxtimeout); //no actual RX timeout in this function + + if (!wait) + { + return 0; + } + + while (!digitalRead(_RXDonePin)); //Wait for DIO1 to go high + + setMode(MODE_STDBY_RC); //ensure to stop further packet reception + + regdata = readIrqStatus(); + + if ( (regdata & IRQ_HEADER_ERROR) | (regdata & IRQ_CRC_ERROR) | (regdata & IRQ_RX_TX_TIMEOUT ) ) + { + return 0; //no RX done and header valid only, could be CRC error + } + + readCommand(RADIO_GET_RXBUFFERSTATUS, buffer, 2); + _RXPacketL = buffer[0]; + + return _RXPacketL; +} + + +uint8_t SX126XLT::readBuffer(uint8_t *rxbuffer) +{ +#ifdef SX126XDEBUG1 + Serial.println(F("readBuffer()")); +#endif + + uint8_t index = 0, regdata; + + do //need to find the size of the buffer first + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; //fill the buffer. + index++; + } while (regdata != 0); //keep reading until we have reached the null (0) at the buffer end + //or exceeded size of buffer allowed + _RXPacketL = _RXPacketL + index; //increment count of bytes read + return index; //return the actual size of the buffer, till the null (0) detected + +} + +/*************************************************************************** +//End direct access SX buffer routines +***************************************************************************/ + +uint16_t SX126XLT::CRCCCITTSX(uint8_t startadd, uint8_t endadd, uint16_t startvalue) +{ + //genrates a CRC of an area of the internal SX buffer + +#ifdef SX126XDEBUG1 + Serial.println(F("CRCCCITTSX()")); +#endif + + + uint16_t index, libraryCRC; + uint8_t j; + + libraryCRC = startvalue; //start value for CRC16 + + startReadSXBuffer(startadd); //begin the buffer read + + for (index = startadd; index <= endadd; index++) + { + libraryCRC ^= (((uint16_t) readUint8() ) << 8); + for (j = 0; j < 8; j++) + { + if (libraryCRC & 0x8000) + libraryCRC = (libraryCRC << 1) ^ 0x1021; + else + libraryCRC <<= 1; + } + } + + endReadSXBuffer(); //end the buffer read + + return libraryCRC; +} + + +void SX126XLT::setSleep(uint8_t sleepconfig) +{ +#ifdef SX126XDEBUG + Serial.println(F("setSleep()")); +#endif + setMode(MODE_STDBY_RC); + checkBusy(); + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_SET_SLEEP); + SPI.transfer(sleepconfig); + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); + #endif + + if (_SW >= 0) + { + digitalWrite(_SW, LOW); //turn off antenna switch if SW pin in use, saves 9uA. + } + + delay(1); //allow time for shutdown +} + + +void SX126XLT::wake() +{ +#ifdef SX126XDEBUG + Serial.println(F("wake()")); +#endif + +if (_SW >= 0) + { + digitalWrite(_SW, HIGH); //turn on antenna switch if SW pin in use + } + +digitalWrite(_NSS, LOW); +delay(1); +digitalWrite(_NSS, HIGH); +delay(1); +} + + +void SX126XLT::setupDirect(uint32_t frequency, int32_t offset) +{ + //setup LoRa device for direct modulation mode +#ifdef SX126XDEBUG1 + Serial.print(F("setupDirect()")); +#endif + setMode(MODE_STDBY_RC); + setRegulatorMode(USE_DCDC); + setPaConfig(0x04, PAAUTO, _Device); //use _Device, saved by begin. + setDIO3AsTCXOCtrl(TCXO_CTRL_3_3V); + calibrateDevice(ALLDevices); //is required after setting TCXO + calibrateImage(frequency); + setDIO2AsRfSwitchCtrl(); + setRfFrequency(frequency, offset); +} + + +void SX126XLT::setTXDirect() +{ + //turns on transmitter,in direct mode for FSK and audio power level is from 2 to 17 +#ifdef SX127XDEBUG1 + Serial.print(F("setTxFSK()")); +#endif + writeCommand(RADIO_SET_TXCONTINUOUSWAVE, 0, 0); +} + + +void SX126XLT::toneFM(uint16_t frequency, uint32_t length, uint32_t deviation, float adjust, uint8_t txpower) +{ +#ifdef SX126XDEBUG1 + Serial.print(F("toneFM()")); +#endif + uint16_t index; + uint32_t ToneDelayus; + uint32_t registershift; + uint32_t shiftedfreqregH, shiftedfreqregL; + uint32_t loopcount; + + registershift = deviation/FREQ_STEP; + shiftedfreqregH = savedFrequencyReg + registershift; + shiftedfreqregL = savedFrequencyReg - registershift; + + uint8_t HighShiftH = shiftedfreqregH >> 24; + uint8_t HighShiftMH = shiftedfreqregH >> 16; + uint8_t HighShiftML = shiftedfreqregH >> 8; + uint8_t HighShiftL = shiftedfreqregH; + uint8_t LowShiftH = shiftedfreqregL >> 24; + uint8_t LowShiftMH = shiftedfreqregL >> 16; + uint8_t LowShiftML = shiftedfreqregL >> 8; + uint8_t LowShiftL = shiftedfreqregL; + uint8_t freqregH = savedFrequencyReg >> 24; + uint8_t freqregMH = savedFrequencyReg >> 16; + uint8_t freqregML = savedFrequencyReg >> 8; + uint8_t freqregL = savedFrequencyReg; + + + ToneDelayus = ((500000/frequency)); + loopcount = (length * 500) / (ToneDelayus); + ToneDelayus = ToneDelayus * adjust; + + + #ifdef SX126XDEBUG3 + Serial.print(F("frequency ")); + Serial.println(frequency); + Serial.print(F("length ")); + Serial.println(length); + + Serial.print(F("savedFrequencyReg ")); + Serial.println(savedFrequencyReg, HEX); + Serial.print(F("registershift ")); + Serial.println(registershift); + shiftedfreqregH = savedFrequencyReg + (registershift/2); + shiftedfreqregL = savedFrequencyReg - (registershift/2); + Serial.print(F("shiftedfreqregH ")); + Serial.println(shiftedfreqregH, HEX); + Serial.print(F("shiftedfreqregL ")); + Serial.println(shiftedfreqregL, HEX); + + Serial.print(F("ShiftedHigh,")); + Serial.print(HighShiftH,HEX); + Serial.print(F(",")); + Serial.print(HighShiftMH,HEX); + Serial.print(F(",")); + Serial.print(HighShiftML,HEX); + Serial.print(F(",")); + Serial.println(HighShiftL,HEX); + + Serial.print(F("ShiftedLow,")); + Serial.print(LowShiftH,HEX); + Serial.print(F(",")); + Serial.print(LowShiftMH,HEX); + Serial.print(F(",")); + Serial.print(LowShiftML,HEX); + Serial.print(F(",")); + Serial.println(LowShiftL,HEX); + Serial.print(F("ToneDelayus,")); + Serial.println(ToneDelayus); + Serial.print(F("loopcount,")); + Serial.println(loopcount); + Serial.println(); + Serial.println(); + #endif + + setTxParams(txpower, RADIO_RAMP_200_US); + setTXDirect(); + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); + #endif + + for (index = 1; index <= loopcount; index++) +{ + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_SET_RFFREQUENCY); + SPI.transfer(HighShiftH); + SPI.transfer(HighShiftMH); + SPI.transfer(HighShiftML); + SPI.transfer(HighShiftL); + digitalWrite(_NSS, HIGH); + + + delayMicroseconds(ToneDelayus); + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_SET_RFFREQUENCY); + SPI.transfer(LowShiftH); + SPI.transfer(LowShiftMH); + SPI.transfer(LowShiftML); + SPI.transfer(LowShiftL); + digitalWrite(_NSS, HIGH); + + delayMicroseconds(ToneDelayus); +} + + //now set the frequency registers back to centre + digitalWrite(_NSS, LOW); //set NSS low + SPI.transfer(0x86); //address for write to REG_FRMSB + SPI.transfer(freqregH); + SPI.transfer(freqregMH); + SPI.transfer(freqregML); + SPI.transfer(freqregL); + digitalWrite(_NSS, HIGH); //set NSS high + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); + #endif + + setMode(MODE_STDBY_RC); //turns off carrier +} + + +uint8_t SX126XLT::getByteSXBuffer(uint8_t addr) +{ +#ifdef SX126XDEBUG1 + Serial.println(F("getByteSXBuffer()")); +#endif + + uint8_t regdata; + setMode(MODE_STDBY_RC); //this is needed to ensure we can read from buffer OK. + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(addr); + SPI.transfer(0xFF); + regdata = SPI.transfer(0); + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return regdata; +} + + +void SX126XLT::printSXBufferHEX(uint8_t start, uint8_t end) +{ +#ifdef SX126XDEBUG + Serial.println(F("printSXBufferHEX()")); +#endif + + uint8_t index, regdata; + + setMode(MODE_STDBY_RC); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(start); + SPI.transfer(0xFF); + + for (index = start; index <= end; index++) + { + regdata = SPI.transfer(0); + printHEXByte(regdata); + Serial.print(F(" ")); + + } + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + +} + + +int32_t SX126XLT::getFrequencyErrorHz() +{ + //Note: Semtech appear to have stated that the frequency error function that this code uses, + //is not supported for SX126X, for reasons that have not been given, so use at your own risk. + //The fuctions here are a replication of the routines for the very similar SX128X + + #ifdef SX126XDEBUG + Serial.println(F("getFrequencyErrorHz()")); + #endif + + int32_t error, regvalue; + uint32_t bandwidth; + float divider; + + bandwidth = returnBandwidth(savedModParam2); //gets the last configured bandwidth in hz + divider = (float) 1625000 / bandwidth; //why the values from the SX1280 datasheet work I have no idea + + regvalue = getFrequencyErrorRegValue(); + + error = (FREQ_ERROR_CORRECTION * regvalue) / divider; + + return error; +} + + +int32_t SX126XLT::getFrequencyErrorRegValue() +{ + #ifdef SX126XDEBUG + Serial.println(F("getFrequencyErrorRegValue()")); +#endif + +int32_t FrequencyError; + uint32_t regmsb, regmid, reglsb, allreg; + + setMode(MODE_STDBY_XOSC); + + regmsb = readRegister( REG_FREQUENCY_ERRORBASEADDR ); + regmsb = regmsb & 0x0F; //clear bit 20 which is always set + + regmid = readRegister( REG_FREQUENCY_ERRORBASEADDR + 1 ); + + reglsb = readRegister( REG_FREQUENCY_ERRORBASEADDR + 2 ); + + setMode(MODE_STDBY_RC); + + #ifdef SX126XDEBUG + Serial.println(); + Serial.print(F("Registers ")); + Serial.print(regmsb,HEX); + Serial.print(F(" ")); + Serial.print(regmid,HEX); + Serial.print(F(" ")); + Serial.println(reglsb,HEX); + #endif + + allreg = (uint32_t) ( regmsb << 16 ) | ( regmid << 8 ) | reglsb; + + if (allreg & 0x80000) + { + FrequencyError = (0xFFFFF - allreg) * -1; + } + else + { + FrequencyError = allreg; + } + + return FrequencyError; +} + + + +void SX126XLT::printHEXByte(uint8_t temp) +{ + if (temp < 0x10) + { + Serial.print(F("0")); + } + Serial.print(temp, HEX); +} + + + +uint8_t SX126XLT::transmitAddressed(uint8_t *txbuffer, uint8_t size, char txpackettype, char txdestination, char txsource, uint32_t txtimeout, int8_t txpower, uint8_t wait) +{ +#ifdef SX126XDEBUG + Serial.println(F("transmitAddressed()")); +#endif + + uint8_t index; + uint8_t bufferdata; + + if (size == 0) + { + return false; + } + + setMode(MODE_STDBY_RC); + + checkBusy(); + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_WRITE_BUFFER); + SPI.transfer(0); + + SPI.transfer(txpackettype); //Write the packet type + SPI.transfer(txdestination); //Destination node + SPI.transfer(txsource); //Source node + _TXPacketL = 3 + size; //we have added 3 header bytes to size + + for (index = 0; index < size; index++) + { + bufferdata = txbuffer[index]; + SPI.transfer(bufferdata); + } + + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + //checkBusy(); + + writeRegister(REG_LR_PAYLOADLENGTH, _TXPacketL); + setTxParams(txpower, RAMP_TIME); + setTx(txtimeout); //this starts the TX + + if (!wait) + { + return _TXPacketL; + } + + while (!digitalRead(_TXDonePin)); //Wait for DIO1 to go high + + if (readIrqStatus() & IRQ_RX_TX_TIMEOUT ) //check for timeout + { + return 0; + } + else + { + return _TXPacketL; + } +} + + +uint8_t SX126XLT::readRXPacketType() +{ + #ifdef SX126XDEBUG + Serial.println(F("readRXPacketType()")); +#endif +return _RXPacketType; +} + + +uint8_t SX126XLT::readRXDestination() +{ + #ifdef SX126XDEBUG + Serial.println(F("readRXDestination()")); +#endif + return _RXDestination; +} + + +uint8_t SX126XLT::readRXSource() +{ +#ifdef SX126XDEBUG + Serial.println(F("readRXSource()")); +#endif +return _RXSource; +} + + + +uint8_t SX126XLT::receiveAddressed(uint8_t *rxbuffer, uint8_t size, uint32_t rxtimeout, uint8_t wait) +{ +#ifdef SX126XDEBUG + Serial.println(F("receiveAddressed()")); +#endif + + uint8_t index, RXstart, RXend; + uint16_t regdata; + uint8_t buffer[2]; + + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on RX done or timeout + setRx(rxtimeout); + + if (!wait) + { + return 0; //not wait requested so no packet length to pass + } + + while (!digitalRead(_RXDonePin)); //Wait for DIO1 to go high + + setMode(MODE_STDBY_RC); //ensure to stop further packet reception + + regdata = readIrqStatus(); + + if ( (regdata & IRQ_HEADER_ERROR) | (regdata & IRQ_CRC_ERROR) | (regdata & IRQ_RX_TX_TIMEOUT ) ) //check if any of the preceding IRQs is set + { + //packet is errored somewhere so return 0 + return 0; + } + + readCommand(RADIO_GET_RXBUFFERSTATUS, buffer, 2); + _RXPacketL = buffer[0]; + + if (_RXPacketL > size) //check passed buffer is big enough for packet + { + _RXPacketL = size; //truncate packet if not enough space + } + + RXstart = buffer[1]; + + RXend = RXstart + _RXPacketL; + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(RXstart); + SPI.transfer(0xFF); + + _RXPacketType = SPI.transfer(0); + _RXDestination = SPI.transfer(0); + _RXSource = SPI.transfer(0); + + for (index = RXstart; index < RXend; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; + } + + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + +void SX126XLT::clearDeviceErrors() +{ + #ifdef SX126XDEBUG + Serial.println(F("clearDeviceErrors()")); + #endif + + + uint8_t buffer[2]; + + buffer[0] = 0x00; //can only clear all errors + buffer[1] = 0x00; + + writeCommand(RADIO_CLEAR_ERRORS, buffer, 2); +} + + +void SX126XLT::printDeviceErrors() +{ +#ifdef SX126XDEBUG + Serial.println(F("printDeviceErrors()")); +#endif + + uint16_t errors; + uint8_t buffer[2]; + + readCommand(RADIO_GET_ERROR, buffer, 2); + + errors = (buffer[0] << 8) + buffer[1]; + + //0x0001 + if (errors & RC64K_CALIB_ERR) + { + Serial.print(F(",RC64K_CALIB_ERR")); + } + + //0x0002 + if (errors & RC13M_CALIB_ERR) + { + Serial.print(F(",RC13M_CALIB_ERR")); + } + + //0x0004 + if (errors & PLL_CALIB_ERR) + { + Serial.print(F(",PLL_CALIB_ERR")); + } + + //0x0008 + if (errors & ADC_CALIB_ERR) + { + Serial.print(F(",ADC_CALIB_ERR")); + } + + //0x0010 + if (errors & IMG_CALIB_ERR) + { + Serial.print(F(",IMG_CALIB_ERR")); + } + + //0x0020 + if (errors & XOSC_START_ERR) + { + Serial.print(F(",XOSC_START_ERR")); + } + + //0x0040 + if (errors & PLL_LOCK_ERR) + { + Serial.print(F(",PLL_LOCK_ERR")); + } + + //0x0080 + if (errors & RFU) + { + Serial.print(F(",RFU")); + } + + //0x0100 + if (errors & PA_RAMP_ERR) + { + Serial.print(",PA_RAMP_ERR"); + } +} + + +void SX126XLT::printHEXPacket(uint8_t *buffer, uint8_t size) +{ +#ifdef SX126XDEBUG + Serial.println(F("printHEXPacket()")); +#endif + + uint8_t index; + + for (index = 0; index < size; index++) + { + //Serial.print(F("[")); + //Serial.print(index); + //Serial.print(F("],")); + printHEXByte(buffer[index]); + Serial.print(F(" ")); + } +} + + +void SX126XLT::printHEXByte0x(uint8_t temp) +{ + //print a byte, adding 0x + Serial.print(F("0x")); + if (temp < 0x10) + { + Serial.print(F("0")); + } + Serial.print(temp, HEX); +} + + +uint8_t SX126XLT::readsavedModParam1() +{ +//return previously set spreading factor +#ifdef SX126XDEBUG + Serial.println(F("readsavedModParam1()")); +#endif +return savedModParam1; +} + + +uint8_t SX126XLT::readsavedModParam2() +{ +//return previously set bandwidth +#ifdef SX126XDEBUG + Serial.println(F("readsavedModParam2()")); +#endif + return savedModParam2; +} + + +uint8_t SX126XLT::readsavedModParam3() +{ +//return previously set code rate + #ifdef SX126XDEBUG + Serial.println(F("readsavedModParam3()")); +#endif +return savedModParam3; +} + + +uint8_t SX126XLT::readsavedModParam4() +{ +//return previously set optimisation +#ifdef SX126XDEBUG + Serial.println(F("readsavedModParam4()")); +#endif + return savedModParam4; +} + +uint8_t SX126XLT::readsavedPower() +{ + #ifdef SX126XDEBUG + Serial.println(F("readsavedPower()")); +#endif +return savedTXPower; +} + +uint8_t SX126XLT::getPacketMode() +{ + //its either LoRa or FSK + + #ifdef SX126XDEBUG + Serial.println(F("getPacketMode()")); + #endif + + return savedPacketType; +} + + +uint8_t SX126XLT::readsavedPacketParam1() +{ +//return previously set preamble +#ifdef SX126XDEBUG + Serial.println(F("readsavedPacketParam1()")); +#endif + return savedPacketParam1; +} + + +uint8_t SX126XLT::readsavedPacketParam2() +{ +//return previously set header type +#ifdef SX126XDEBUG + Serial.println(F("readsavedPacketParam2()")); +#endif + return savedPacketParam2; +} + + +uint8_t SX126XLT::readsavedPacketParam3() +{ +//return previously set packet length +#ifdef SX126XDEBUG + Serial.println(F("readsavedPacketParam3()")); +#endif + return savedPacketParam3; +} + + + +uint8_t SX126XLT::readsavedPacketParam4() +{ +//return previously set CRC +#ifdef SX126XDEBUG + Serial.println(F("readsavedPacketParam4()")); +#endif + return savedPacketParam4; +} + + +uint8_t SX126XLT::readsavedPacketParam5() +{ +//return previously set IQ +#ifdef SX126XDEBUG + Serial.println(F("readsavedPacketParam5()")); +#endif + return savedPacketParam5; +} + +uint8_t SX126XLT::getOpmode() +{ +//return last saved opmode +#ifdef SX126XDEBUG + Serial.println(F("getOpmode()")); +#endif + return _OperatingMode; +} + +uint8_t SX126XLT::getCRCMode() +{ +//return last saved opmode +#ifdef SX126XDEBUG + Serial.println(F("getCRCMode()")); +#endif + return savedPacketParam4; +} + + + +void SX126XLT::fillSXBuffer(uint8_t startaddress, uint8_t size, uint8_t character) +{ +#ifdef SX126XDEBUG1 + Serial.println(F("fillSXBuffer()")); +#endif + uint8_t index; + + setMode(MODE_STDBY_RC); + //writeRegister(REG_FIFOADDRPTR, startaddress); //and save in FIFO access ptr + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_WRITE_BUFFER); + SPI.transfer(startaddress); + //SPI interface ready for byte to write to buffer + + for (index = 0; index < size; index++) + { + SPI.transfer(character); + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + +} + + +uint8_t SX126XLT::readPacket(uint8_t *rxbuffer, uint8_t size) +{ +#ifdef SX126XDEBUG + Serial.println(F("readPacket()")); +#endif + + uint8_t index, regdata, RXstart, RXend; + uint8_t buffer[2]; + + readCommand(RADIO_GET_RXBUFFERSTATUS, buffer, 2); + _RXPacketL = buffer[0]; + + if (_RXPacketL > size) //check passed buffer is big enough for packet + { + _RXPacketL = size; //truncate packet if not enough space + } + + RXstart = buffer[1]; + + RXend = RXstart + _RXPacketL; + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(RXstart); + SPI.transfer(0xFF); + + for (index = RXstart; index < RXend; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; + } + + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + +void SX126XLT::writeByteSXBuffer(uint8_t addr, uint8_t regdata) +{ +#ifdef SX126XDEBUG1 + Serial.println(F("writeByteSXBuffer")); +#endif + + setMode(MODE_STDBY_RC); //this is needed to ensure we can write to buffer OK. + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_WRITE_BUFFER); + SPI.transfer(addr); + SPI.transfer(regdata); + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); + #endif + +} + + +void SX126XLT::printSXBufferASCII(uint8_t start, uint8_t end) +{ +#ifdef SX126XDEBUG1 + Serial.println(F("printSXBufferASCII)")); +#endif + + uint8_t index, regdata; + setMode(MODE_STDBY_RC); + + for (index = start; index <= end; index++) + { + regdata = getByteSXBuffer(index); + Serial.write(regdata); + } + +} + + +void SX126XLT::startFSKRTTY(uint32_t freqshift, uint8_t pips, uint16_t pipPeriodmS, uint16_t pipDelaymS, uint16_t leadinmS) +{ + + #ifdef SX126XDEBUG1 + Serial.print(F("startFSKRTTY()")); + #endif + + uint32_t shiftedFrequencyRegisters; + uint8_t index; + uint32_t endmS; + uint32_t calculatedRegShift; + + calculatedRegShift = (uint32_t) (freqshift/FREQ_STEP); + + shiftedFrequencyRegisters = savedFrequencyReg + calculatedRegShift; + + _ShiftfreqregH = (shiftedFrequencyRegisters >> 24) & 0xFF; //MSB + _ShiftfreqregMH = (shiftedFrequencyRegisters >> 16) & 0xFF; + _ShiftfreqregML = (shiftedFrequencyRegisters >> 8) & 0xFF; + _ShiftfreqregL = shiftedFrequencyRegisters & 0xFF; //LSB + + #ifdef DEBUGFSKRTTY + Serial.print(F("NotShiftedFrequencyRegisters ")); + Serial.println(savedFrequencyReg, HEX); + Serial.print(F("calculatedRegShift ")); + Serial.println(calculatedRegShift, HEX); + Serial.print(F("ShiftedFrequencyRegisters ")); + Serial.print((uint32_t) shiftedFrequencyRegisters, HEX); + Serial.print(F(" (")); + Serial.print(_ShiftfreqregH,HEX); + Serial.print(F(" ")); + Serial.print(_ShiftfreqregMH,HEX); + Serial.print(F(" ")); + Serial.print(_ShiftfreqregML,HEX); + Serial.print(F(" ")); + Serial.print(_ShiftfreqregL,HEX); + Serial.print(F(" )")); + Serial.println(); + #endif + + setTxParams(10, RADIO_RAMP_200_US); + + for (index = 1; index <= pips; index++) + { + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregMH, _ShiftfreqregML, _ShiftfreqregL); //set carrier frequency + setTXDirect(); //turn on carrier + delay(pipPeriodmS); + setMode(MODE_STDBY_RC); //turns off carrier + delay(pipDelaymS); + } + + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregMH, _ShiftfreqregML,_ShiftfreqregL); //set carrier frequency + endmS = millis() + leadinmS; + setTXDirect(); //turn on carrier + while (millis() < endmS); //leave leadin on + +} + + + void SX126XLT::transmitFSKRTTY(uint8_t chartosend, uint8_t databits, uint8_t stopbits, uint8_t parity, uint16_t baudPerioduS, int8_t pin) +{ + //micros() will rollover at 4294967295 or 71mins 35secs + //assume slowest baud rate is 45 (baud period of 22222us) then with 11 bits max to send if routine starts + //when micros() > (4294967295 - (22222 * 11) = 4294722855 = 0xFFFC4525 then it could overflow during send + //Rather than deal with rolloever in the middle of a character lets wait till it overflows and then + //start the character + + + #ifdef SX126XDEBUG1 + Serial.print(F("transmitFSKRTTY()")); + #endif + + uint8_t numbits; + uint32_t enduS; + uint8_t bitcount = 0; //set when a bit is 1 + + if (micros() > 0xFFFB6000) //check if micros would overflow within circa 300mS, approx 1 char at 45baud + { + + #ifdef DEBUGFSKRTTY + Serial.print(F("Overflow pending - micros() = ")); + Serial.println(micros(),HEX); + #endif + + while (micros() > 0xFFFB6000); //wait a short while until micros overflows to 0 + + #ifdef DEBUGFSKRTTY + Serial.print(F("Paused - micros() = ")); + Serial.println(micros(),HEX); + #endif + + } + + enduS = micros() + baudPerioduS; + setRfFrequencyDirect(_freqregH, _freqregMH, _freqregML, _freqregL); //set carrier frequency (low) + + if (pin >= 0) + { + digitalWrite(pin, LOW); + } + + while (micros() < enduS); //start bit + + for (numbits = 1; numbits <= databits; numbits++) //send bits, LSB first + { + enduS = micros() + baudPerioduS; //start the timer + if ((chartosend & 0x01) != 0) //test for bit set, a 1 + { + bitcount++; + if (pin >= 0) + { + digitalWrite(pin, HIGH); + } + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregMH, _ShiftfreqregML, _ShiftfreqregL); //set carrier frequency for a 1 + } + else + { + if (pin >= 0) + { + digitalWrite(pin, LOW); + } + setRfFrequencyDirect(_freqregH, _freqregMH, _freqregML, _freqregL); //set carrier frequency for a 0 + } + chartosend = (chartosend >> 1); //get the next bit + while (micros() < enduS); + } + + enduS = micros() + baudPerioduS; //start the timer for possible parity bit + + switch (parity) + { + case ParityNone: + break; + + case ParityZero: + setRfFrequencyDirect(_freqregH, _freqregMH, _freqregML, _freqregL); //set carrier frequency for a 0 + while (micros() < enduS); + break; + + case ParityOne: + + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregMH, _ShiftfreqregML, _ShiftfreqregL); //set carrier frequency for a 1 + while (micros() < enduS); + break; + + case ParityOdd: + if (bitRead(bitcount, 0)) + { + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregMH, _ShiftfreqregML, _ShiftfreqregL); //set carrier frequency for a 1 + } + else + { + setRfFrequencyDirect(_freqregH, _freqregMH, _freqregML, _freqregL); //set carrier frequency for a 0 + } + while (micros() < enduS); + break; + + case ParityEven: + if (bitRead(bitcount, 0)) + { + setRfFrequencyDirect(_freqregH, _freqregMH, _freqregML, _freqregL); //set carrier frequency for a 0 + } + else + { + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregMH, _ShiftfreqregML, _ShiftfreqregL); //set carrier frequency for a 1 + } + while (micros() < enduS); + break; + + default: + break; + } + + //stop bits, normally 1 or 2 + enduS = micros() + (baudPerioduS * stopbits); + + if (pin >= 0) + { + digitalWrite(pin, HIGH); + } + + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregMH, _ShiftfreqregML, _ShiftfreqregL); //set carrier frequency for a 1 + + while (micros() < enduS); + +} + + + +void SX126XLT::transmitFSKRTTY(uint8_t chartosend, uint16_t baudPerioduS, int8_t pin) +{ + //micros() will rollover at 4294967295 or 71mins 35secs + //assume slowest baud rate is 45 (baud period of 22222us) then with 11 bits max to send if routine starts + //when micros() > (4294967295 - (22222 * 11) = 4294722855 = 0xFFFC4525 then it could overflow during send + //Rather than deal with rolloever in the middle of a character lets wait till it overflows and then + //start the character + //This overloaded version of transmitFSKRTTY() uses 1 start bit, 7 data bits, no parity and 2 stop bits. + + + #ifdef SX126XDEBUG1 + Serial.print(F("transmitFSKRTTY()")); + #endif + + uint8_t numbits; + uint32_t enduS; + + if (micros() > 0xFFFB6000) //check if micros would overflow within circa 300mS, approx 1 char at 45baud + { + #ifdef DEBUGFSKRTTY + Serial.print(F("Overflow pending - micros() = ")); + Serial.println(micros(),HEX); + #endif + while (micros() > 0xFFFB6000); //wait a short while until micros overflows to 0 + #ifdef DEBUGFSKRTTY + Serial.print(F("Paused - micros() = ")); + Serial.println(micros(),HEX); + #endif + } + + enduS = micros() + baudPerioduS; + setRfFrequencyDirect(_freqregH, _freqregMH, _freqregML, _freqregL); //set carrier frequency (low) + + if (pin >= 0) + { + digitalWrite(pin, LOW); + } + + while (micros() < enduS); //start bit + + for (numbits = 1; numbits <= 7; numbits++) //send bits, LSB first + { + enduS = micros() + baudPerioduS; //start the timer + if ((chartosend & 0x01) != 0) //test for bit set, a 1 + { + if (pin >= 0) + { + digitalWrite(pin, HIGH); + } + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregMH, _ShiftfreqregML, _ShiftfreqregL); //set carrier frequency for a 1 + } + else + { + if (pin >= 0) + { + digitalWrite(pin, LOW); + } + setRfFrequencyDirect(_freqregH, _freqregMH, _freqregML, _freqregL); //set carrier frequency for a 0 + } + chartosend = (chartosend >> 1); //get the next bit + while (micros() < enduS); + } + + //stop bits, normally 1 or 2 + enduS = micros() + (baudPerioduS * 2); + + if (pin >= 0) + { + digitalWrite(pin, HIGH); + } + + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregMH, _ShiftfreqregML, _ShiftfreqregL); //set carrier frequency + + while (micros() < enduS); + +} + + + + + +void SX126XLT::printRTTYregisters() +{ + + #ifdef SX126XDEBUG1 + Serial.print(F("printRTTYregisters()")); + #endif + +Serial.print(F("NoShift Registers ")); +Serial.print(_freqregH, HEX); +Serial.print(F(" ")); +Serial.print(_freqregMH, HEX); +Serial.print(F(" ")); +Serial.print(_freqregML, HEX); +Serial.print(F(" ")); +Serial.println(_freqregL, HEX); + +Serial.print(F("Shifted Registers ")); +Serial.print(_ShiftfreqregH, HEX); +Serial.print(F(" ")); +Serial.print(_ShiftfreqregMH, HEX); +Serial.print(F(" ")); +Serial.print(_ShiftfreqregML, HEX); +Serial.print(F(" ")); +Serial.println(_ShiftfreqregL, HEX); + +} + + +void SX126XLT::endFSKRTTY() +{ + #ifdef SX126XDEBUG1 + Serial.print(F("endFSKRTTY()")); + #endif + + setMode(MODE_STDBY_RC); + +} + + +void SX126XLT::getRfFrequencyRegisters(uint8_t *buff) +{ + //returns the register values for the current set frequency + + #ifdef SX126XDEBUG1 + Serial.print(F("getRfFrequencyRegisters()")); + #endif + + buff[0] = _freqregH; + buff[1] = _freqregMH; + buff[2] = _freqregML; + buff[3] = _freqregL; + +} + + +void SX126XLT::setRfFrequencyDirect(uint8_t high, uint8_t midhigh, uint8_t midlow, uint8_t low) +{ + + #ifdef SX126XDEBUG1 + Serial.print(F("setRfFrequencyDirect()")); + #endif + + uint8_t buffer[4]; + + buffer[0] = high; //MSB + buffer[1] = midhigh; + buffer[2] = midlow; + buffer[3] = low;//LSB + + writeCommand(RADIO_SET_RFFREQUENCY, buffer, 4); + +} + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ diff --git a/lib/SX12XX-LoRa/src/SX126XLT.h b/lib/SX12XX-LoRa/src/SX126XLT.h new file mode 100644 index 0000000..400f966 --- /dev/null +++ b/lib/SX12XX-LoRa/src/SX126XLT.h @@ -0,0 +1,229 @@ + +#ifndef SX126XLT_h +#define SX126XLT_h + +#include "Arduino.h" +#include + +/************************************************************************** + + ToDO + + DONE - Check setDIOIRQ is before all setTX SetRX calls + DONE - Check for checkbusy, before all uses of SPI + DONE - Match printlorasettings and printdevice settings with sx127x library + DONE - Check if SX126X has AGCauto_ + DONE - Check correct setting of optimisation + DONE - Check in addressed send txpacketL = 3 + size; //we have added 3 header bytes to size + DONE - Investigate use of clearDeviceErrors() - Not used in Semtech sample code + ABANDONED - Investigate if setPacketParams(savedPacketParam1, savedPacketParam2 in send routine can be avoided - TXpacketL + DONE - Test rxEnable and txenable functionality + DONE - Check TX power settings at 17dBm + + + Description of how to include RxGain register in the retention memory, see Section 9.6 - manual p58 + + Add a library function for SetRxDutyCycle ? + Add a library function to allow changing of ramptime from RADIO_RAMP_200_US ? + Check use of RADIO_RAMP_DEFAULT + + +**************************************************************************/ + +class SX126XLT { + public: + + SX126XLT(); + + bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinDIO2, int8_t pinDIO3, int8_t pinRXEN, int8_t pinTXEN, int8_t pinSW, uint8_t device); + bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, uint8_t device); + bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinSW, uint8_t device); + bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinRXEN, int8_t pinTXEN, uint8_t device); + + void checkBusy(); + void writeCommand(uint8_t Opcode, uint8_t *buffer, uint16_t size ); + void readCommand( uint8_t Opcode, uint8_t *buffer, uint16_t size ); + void writeRegisters( uint16_t address, uint8_t *buffer, uint16_t size ); + void writeRegister( uint16_t address, uint8_t value ); + void readRegisters( uint16_t address, uint8_t *buffer, uint16_t size ); + uint8_t readRegister( uint16_t address ); + void resetDevice(); + bool checkDevice(); + void setupLoRa(uint32_t frequency, int32_t offset, uint8_t modParam1, uint8_t modParam2, uint8_t modParam3, uint8_t modParam4); + void setMode(uint8_t modeconfig); + void setRegulatorMode(uint8_t mode); + + void setPaConfig(uint8_t dutycycle, uint8_t hpMax, uint8_t device); + void setDIO3AsTCXOCtrl(uint8_t tcxoVoltage); + void calibrateDevice(uint8_t devices); + void calibrateImage(uint32_t freq); + void setDIO2AsRfSwitchCtrl(); + void setPacketType(uint8_t PacketType); + void setRfFrequency( uint32_t frequency, int32_t offset ); + void setModulationParams(uint8_t modParam1, uint8_t modParam2, uint8_t modParam3, uint8_t modParam4); + uint8_t returnOptimisation(uint8_t SpreadingFactor, uint8_t Bandwidth); + uint32_t returnBandwidth(uint8_t BWregvalue); + float calcSymbolTime(float Bandwidth, uint8_t SpreadingFactor); + void setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress); + void setPacketParams(uint16_t packetParam1, uint8_t packetParam2, uint8_t packetParam3, uint8_t packetParam4, uint8_t packetParam5); + void setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask ); + void setHighSensitivity(); + void setLowPowerRX(); + void setSyncWord(uint16_t syncword); + void printModemSettings(); + void printDevice(); + uint32_t getFreqInt(); //this reads the SX126x registers to get the current frequency + uint8_t getLoRaSF(); + + uint8_t getLoRaCodingRate(); + uint8_t getOptimisation(); + + uint16_t getSyncWord(); + uint8_t getInvertIQ(); + uint16_t getPreamble(); + void printOperatingSettings(); + uint8_t getHeaderMode(); + uint8_t getLNAgain(); + void printRegisters(uint16_t Start, uint16_t End); + void printASCIIPacket(uint8_t *buff, uint8_t tsize); + uint8_t transmit(uint8_t *txbuffer, uint8_t size, uint32_t txtimeout, int8_t txpower, uint8_t wait); + void setTxParams(int8_t TXpower, uint8_t RampTime); + void setTx(uint32_t timeout); + void clearIrqStatus( uint16_t irq ); + uint16_t readIrqStatus(); + uint16_t CRCCCITT(uint8_t *buffer, uint8_t size, uint16_t start); + uint8_t receive(uint8_t *rxbuffer, uint8_t size, uint32_t rxtimeout, uint8_t wait); + uint8_t readPacketRSSI(); + uint8_t readPacketSNR(); + uint8_t readRXPacketL(); + void setRx(uint32_t timeout); + + void printIrqStatus(); + void rxEnable(); + void txEnable(); + bool config(); + +/*************************************************************************** +//Start direct access SX buffer routines +***************************************************************************/ + + void startWriteSXBuffer(uint8_t ptr); + uint8_t endWriteSXBuffer(); + void startReadSXBuffer(uint8_t ptr); + uint8_t endReadSXBuffer(); + + void writeUint8(uint8_t x); + uint8_t readUint8(); + + void writeInt8(int8_t x); + int8_t readInt8(); + + void writeInt16(int16_t x); + int16_t readInt16(); + + void writeUint16(uint16_t x); + uint16_t readUint16(); + + void writeInt32(int32_t x); + int32_t readInt32(); + + void writeUint32(uint32_t x); + uint32_t readUint32(); + + void writeFloat(float x); + float readFloat(); + + uint8_t transmitSXBuffer(uint8_t startaddr, uint8_t length, uint32_t txtimeout, int8_t txpower, uint8_t wait); + void writeBuffer(uint8_t *txbuffer, uint8_t size); + uint8_t receiveSXBuffer(uint8_t startaddr, uint32_t rxtimeout, uint8_t wait); + uint8_t readBuffer(uint8_t *rxbuffer); + void setupDirect(uint32_t frequency, int32_t offset); + void toneFM(uint16_t frequency, uint32_t length, uint32_t deviation, float adjust, uint8_t txpower); + void setTXDirect(); + uint8_t getByteSXBuffer(uint8_t addr); + void printSXBufferHEX(uint8_t start, uint8_t end); + int32_t getFrequencyErrorHz(); + int32_t getFrequencyErrorRegValue(); + void printHEXByte(uint8_t temp); + uint8_t transmitAddressed(uint8_t *txbuffer, uint8_t size, char txpackettype, char txdestination, char txsource, uint32_t txtimeout, int8_t txpower, uint8_t wait); + uint8_t readRXPacketType(); + uint8_t readRXDestination(); + uint8_t readRXSource(); + uint8_t receiveAddressed(uint8_t *rxbuffer, uint8_t size, uint32_t rxtimeout, uint8_t wait); + void clearDeviceErrors(); + void printDeviceErrors(); + void printHEXPacket(uint8_t *buffer, uint8_t size); + void printHEXByte0x(uint8_t temp); + uint8_t readsavedModParam1(); //spreading factor + uint8_t readsavedModParam2(); //bandwidth + uint8_t readsavedModParam3(); //code rate + uint8_t readsavedModParam4(); //optimisation + uint8_t readsavedPower(); + uint8_t getPacketMode(); + uint8_t readsavedPacketParam1(); //preamble + uint8_t readsavedPacketParam2(); //header type + uint8_t readsavedPacketParam3(); //packet length + uint8_t readsavedPacketParam4(); //CRC + uint8_t readsavedPacketParam5(); //IQ + uint8_t getOpmode(); + uint8_t getCRCMode(); + void fillSXBuffer(uint8_t startaddress, uint8_t size, uint8_t character); + uint8_t readPacket(uint8_t *rxbuffer, uint8_t size); + void writeByteSXBuffer(uint8_t addr, uint8_t regdata); + void printSXBufferASCII(uint8_t start, uint8_t end); + void startFSKRTTY(uint32_t freqshift, uint8_t pips, uint16_t pipPeriodmS, uint16_t pipDelaymS, uint16_t leadinmS); + void transmitFSKRTTY(uint8_t chartosend, uint8_t databits, uint8_t stopbits, uint8_t parity, uint16_t baudPerioduS, int8_t pin); + void transmitFSKRTTY(uint8_t chartosend, uint16_t baudPerioduS, int8_t pin); + void printRTTYregisters(); + void endFSKRTTY(); + void getRfFrequencyRegisters(uint8_t *buff); + void setRfFrequencyDirect(uint8_t high, uint8_t midhigh, uint8_t midlow, uint8_t low); + +/*************************************************************************** +//End direct access SX buffer routines +***************************************************************************/ + + uint16_t CRCCCITTSX(uint8_t startadd, uint8_t endadd, uint16_t startvalue); + void setSleep(uint8_t sleepconfig); + void wake(); + + private: + + int8_t _NSS, _NRESET, _RFBUSY, _DIO1, _DIO2, _DIO3, _SW; + int8_t _RXEN, _TXEN; + uint8_t _RXPacketL; //length of packet received + uint8_t _RXPacketType; //type number of received packet + uint8_t _RXDestination; //destination address of received packet + uint8_t _RXSource; //source address of received packet + int8_t _PacketRSSI; //RSSI of received packet + int8_t _PacketSNR; //signal to noise ratio of received packet + int8_t _TXPacketL; + uint8_t _RXcount; //used to keep track of the bytes read from SX126X buffer during readFloat() etc + uint8_t _TXcount; //used to keep track of the bytes written to SX126X buffer during writeFloat() etc + uint8_t _RXBufferPointer; //pointer to first byte of packet in buffer + uint8_t _OperatingMode; //current operating mode + bool _rxtxpinmode = false; //set to true if RX and TX enable pin mode is used. + uint8_t _Device; //saved device type + uint8_t _TXDonePin; //the pin that will indicate TX done + uint8_t _RXDonePin; //the pin that will indicate RX done + + uint32_t savedFrequency; + int32_t savedOffset; + uint8_t savedPacketType; + uint8_t savedRegulatorMode; + + uint8_t savedModParam1, savedModParam2, savedModParam3, savedModParam4; + uint16_t savedPacketParam1; + uint8_t savedPacketParam2, savedPacketParam3, savedPacketParam4, savedPacketParam5; + uint16_t savedIrqMask, savedDio1Mask, savedDio2Mask, savedDio3Mask; + int8_t savedTXPower; + + uint32_t savedFrequencyReg; + uint8_t _freqregH, _freqregMH, _freqregML,_freqregL; //the registers values for the set frequency + uint8_t _ShiftfreqregH, _ShiftfreqregMH, _ShiftfreqregML, _ShiftfreqregL; //register values for shifted frequency, used in FSK + +}; +#endif + + + + diff --git a/lib/SX12XX-LoRa/src/SX126XLT_Definitions.h b/lib/SX12XX-LoRa/src/SX126XLT_Definitions.h new file mode 100644 index 0000000..a5f2b1c --- /dev/null +++ b/lib/SX12XX-LoRa/src/SX126XLT_Definitions.h @@ -0,0 +1,305 @@ +/* + Copyright 2019 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + 17/12/19 +*/ + + +#define XTAL_FREQ 32000000 +#define FREQ_DIV 33554432 +#define FREQ_STEP 0.95367431640625 +#define FREQ_ERR 0.47683715820312 +#define FREQ_ERROR_CORRECTION 1.55 //this was measured on SX1280 for bandwidth of 400khz + +#define AUTO_RX_TX_OFFSET 2 + +#define CRC_IBM_SEED 0xFFFF +#define CRC_CCITT_SEED 0x1D0F +#define CRC_POLYNOMIAL_IBM 0x8005 +#define CRC_POLYNOMIAL_CCITT 0x1021 + +//Registers - Used, tested +#define REG_LR_PAYLOADLENGTH 0x0702 +#define REG_IQ_POLARITY_SETUP 0x0736 +#define REG_LR_SYNCWORD 0x0740 +#define REG_RX_GAIN 0x08AC +#define REG_TX_MODULATION 0x0889 +#define REG_RFFrequency31_24 0x088B +#define REG_RFFrequency23_16 0x088C +#define REG_RFFrequency15_8 0x088D +#define REG_RFFrequency7_0 0x088E + +//Registers - Not used, not tested +#define REG_LR_WHITSEEDBASEADDR_MSB 0x06B8 +#define REG_LR_WHITSEEDBASEADDR_LSB 0x06B9 +#define REG_LR_CRCSEEDBASEADDR 0x06BC +#define REG_LR_CRCPOLYBASEADDR 0x06BE +#define REG_LR_PACKETPARAMS 0x0704 +#define REG_LR_SYNCWORDBASEADDRESS 0x06C0 + +#define REG_FREQUENCY_ERRORBASEADDR 0x076B +#define RANDOM_NUMBER_GENERATORBASEADDR 0x0819 +#define REG_OCP 0x08E7 +#define REG_XTA_TRIM 0x0911 + + +#define LORA_MAC_PRIVATE_SYNCWORD 0x1424 +#define LORA_MAC_PUBLIC_SYNCWORD 0x3444 + +#define PRINT_LOW_REGISTER 0x680 +#define PRINT_HIGH_REGISTER 0x920 + + +#define POWER_SAVE_GAIN 0x94 +#define BOOSTED_GAIN 0x96 + + +//radio operatine modes +#define MODE_SLEEP 0x07 +#define MODE_STDBY_RC 0x00 //Device running on RC13M, set STDBY_RC mode +#define MODE_STDBY_XOSC 0x01 //Device running on XTAL 32MHz, set STDBY_XOSC mode +#define MODE_FS 0x02 // The radio is in frequency synthesis mode +#define MODE_TX 0x03 //TX mode +#define MODE_RX 0x04 //RX mode +#define MODE_RX_DC 0x05 //RX duty cycle mode +#define MODE_CAD 0x06 //RX CAD mode + + +//Sleep Mode Definition +#define COLD_START 0x00 +#define WARM_START 0x04 +#define RTC_TIMEOUT_DISABLE 0x00 +#define RTC_TIMEOUT_ENABLE 0x01 + +#define USE_LDO 0x00 //default +#define USE_DCDC 0x01 + +#define PACKET_TYPE_GFSK 0x00 +#define PACKET_TYPE_LORA 0x01 +#define PACKET_TYPE_NONE 0x0F + + +#define RADIO_RAMP_10_US 0x00 +#define RADIO_RAMP_20_US 0x01 +#define RADIO_RAMP_40_US 0x02 +#define RADIO_RAMP_80_US 0x03 +#define RADIO_RAMP_200_US 0x04 +#define RADIO_RAMP_800_US 0x05 +#define RADIO_RAMP_1700_US 0x06 +#define RADIO_RAMP_3400_US 0x07 + +#define LORA_CAD_01_SYMBOL 0x00 +#define LORA_CAD_02_SYMBOL 0x01 +#define LORA_CAD_04_SYMBOL 0x02 +#define LORA_CAD_08_SYMBOL 0x03 +#define LORA_CAD_16_SYMBOL 0x04 + +#define LORA_CAD_ONLY 0x00 +#define LORA_CAD_RX 0x01 +#define LORA_CAD_LBT 0x10 + +#define LORA_SF5 0x05 +#define LORA_SF6 0x06 +#define LORA_SF7 0x07 +#define LORA_SF8 0x08 +#define LORA_SF9 0x09 +#define LORA_SF10 0x0A +#define LORA_SF11 0x0B +#define LORA_SF12 0x0C + +#define LORA_BW_500 6 //actual 500000hz +#define LORA_BW_250 5 //actual 250000hz +#define LORA_BW_125 4 //actual 125000hz +#define LORA_BW_062 3 //actual 62500hz +#define LORA_BW_041 10 //actual 41670hz +#define LORA_BW_031 2 //actual 31250hz +#define LORA_BW_020 9 //actual 20830hz +#define LORA_BW_015 1 //actual 15630hz +#define LORA_BW_010 8 //actual 10420hz +#define LORA_BW_007 0 //actual 7810hz + +#define LORA_CR_4_5 0x01 +#define LORA_CR_4_6 0x02 +#define LORA_CR_4_7 0x03 +#define LORA_CR_4_8 0x04 + +#define WAIT_RX 0x01 +#define WAIT_TX 0x01 +#define NO_WAIT 0x00 + +#define RADIO_PREAMBLE_DETECTOR_OFF 0x00 //!< Preamble detection length off +#define RADIO_PREAMBLE_DETECTOR_08_BITS 0x04 //!< Preamble detection length 8 bits +#define RADIO_PREAMBLE_DETECTOR_16_BITS 0x05 //!< Preamble detection length 16 bits +#define RADIO_PREAMBLE_DETECTOR_24_BITS 0x06 //!< Preamble detection length 24 bits +#define RADIO_PREAMBLE_DETECTOR_32_BITS 0x07 //!< Preamble detection length 32 bit + +#define RADIO_ADDRESSCOMP_FILT_OFF 0x00 //!< No correlator turned on i.e. do not search for SyncWord +#define RADIO_ADDRESSCOMP_FILT_NODE 0x01 +#define RADIO_ADDRESSCOMP_FILT_NODE_BROAD 0x02 + +#define RADIO_PACKET_FIXED_LENGTH 0x00 //!< The packet is known on both sides no header included in the packet +#define RADIO_PACKET_VARIABLE_LENGTH 0x01 //!< The packet is on variable size header included + +#define RADIO_CRC_OFF 0x01 //!< No CRC in use +#define RADIO_CRC_1_BYTES 0x00 +#define RADIO_CRC_2_BYTES 0x02 +#define RADIO_CRC_1_BYTES_INV 0x04 +#define RADIO_CRC_2_BYTES_INV 0x06 +#define RADIO_CRC_2_BYTES_IBM 0xF1 +#define RADIO_CRC_2_BYTES_CCIT 0xF2 + +#define RADIO_DC_FREE_OFF 0x00 +#define RADIO_DC_FREEWHITENING 0x01 + +#define LORA_PACKET_VARIABLE_LENGTH 0x00 //!< The packet is on variable size header included +#define LORA_PACKET_FIXED_LENGTH 0x01 //!< The packet is known on both sides no header included in the packet +#define LORA_PACKET_EXPLICIT LORA_PACKET_VARIABLE_LENGTH +#define LORA_PACKET_IMPLICIT LORA_PACKET_FIXED_LENGTH + + +#define LORA_CRC_ON 0x01 //!< CRC activated +#define LORA_CRC_OFF 0x00 //!< CRC not used + +#define LORA_IQ_NORMAL 0x00 +#define LORA_IQ_INVERTED 0x01 + +#define TCXO_CTRL_1_6V 0x00 +#define TCXO_CTRL_1_7V 0x01 +#define TCXO_CTRL_1_8V 0x02 +#define TCXO_CTRL_2_2V 0x03 +#define TCXO_CTRL_2_4V 0x04 +#define TCXO_CTRL_2_7V 0x05 +#define TCXO_CTRL_3_0V 0x06 +#define TCXO_CTRL_3_3V 0x07 + +#define IRQ_RADIO_NONE 0x0000 +#define IRQ_TX_DONE 0x0001 +#define IRQ_RX_DONE 0x0002 +#define IRQ_PREAMBLE_DETECTED 0x0004 +#define IRQ_SYNCWORD_VALID 0x0008 +#define IRQ_HEADER_VALID 0x0010 +#define IRQ_HEADER_ERROR 0x0020 +#define IRQ_CRC_ERROR 0x0040 +#define IRQ_CAD_DONE 0x0080 +#define IRQ_CAD_ACTIVITY_DETECTED 0x0100 +#define IRQ_RX_TX_TIMEOUT 0x0200 +#define IRQ_TX_TIMEOUT 0x0200 +#define IRQ_RX_TIMEOUT 0x0200 +#define IRQ_RADIO_ALL 0xFFFF + +#define RADIO_GET_STATUS 0xC0 +#define RADIO_WRITE_REGISTER 0x0D +#define RADIO_READ_REGISTER 0x1D +#define RADIO_WRITE_BUFFER 0x0E +#define RADIO_READ_BUFFER 0x1E +#define RADIO_SET_SLEEP 0x84 +#define RADIO_SET_STANDBY 0x80 +#define RADIO_SET_FS 0xC1 +#define RADIO_SET_TX 0x83 +#define RADIO_SET_RX 0x82 +#define RADIO_SET_RXDUTYCYCLE 0x94 +#define RADIO_SET_CAD 0xC5 +#define RADIO_SET_TXCONTINUOUSWAVE 0xD1 +#define RADIO_SET_TXCONTINUOUSPREAMBLE 0xD2 +#define RADIO_SET_PACKETTYPE 0x8A +#define RADIO_GET_PACKETTYPE 0x11 +#define RADIO_SET_RFFREQUENCY 0x86 +#define RADIO_SET_TXPARAMS 0x8E +#define RADIO_SET_PACONFIG 0x95 +#define RADIO_SET_CADPARAMS 0x88 +#define RADIO_SET_BUFFERBASEADDRESS 0x8F +#define RADIO_SET_MODULATIONPARAMS 0x8B +#define RADIO_SET_PACKETPARAMS 0x8C +#define RADIO_GET_RXBUFFERSTATUS 0x13 +#define RADIO_GET_PACKETSTATUS 0x14 +#define RADIO_GET_RSSIINST 0x15 +#define RADIO_GET_STATS 0x10 +#define RADIO_RESET_STATS 0x00 +#define RADIO_CFG_DIOIRQ 0x08 +#define RADIO_GET_IRQSTATUS 0x12 +#define RADIO_CLR_IRQSTATUS 0x02 +#define RADIO_CALIBRATE 0x89 +#define RADIO_CALIBRATEIMAGE 0x98 +#define RADIO_SET_REGULATORMODE 0x96 +#define RADIO_GET_ERROR 0x17 +#define RADIO_CLEAR_ERRORS 0x07 +#define RADIO_SET_TCXOMODE 0x97 +#define RADIO_SET_TXFALLBACKMODE 0x93 +#define RADIO_SET_RFSWITCHMODE 0x9D +#define RADIO_SET_STOPRXTIMERONPREAMBLE 0x9F +#define RADIO_SET_LORASYMBTIMEOUT 0xA0 + + +//Table 13-2 SX126X Sleep modes +#define CONFIGURATION_RETENTION 0x04 +#define RTC_TIMEOUT_ENABLE 0x01 + + +#define LDRO_OFF 0x00 +#define LDRO_ON 0x01 +#define LDRO_AUTO 0x02 //when used causes LDRO to be automatically calculated + +#define DEVICE_SX1261 0x01 +#define DEVICE_SX1262 0x00 +#define DEVICE_SX1268 0x02 + +#define PAAUTO 0xFF +#define HPMAXAUTO 0xFF + +#ifndef RAMP_TIME +#define RAMP_TIME RADIO_RAMP_40_US +#endif + +#define RC64KEnable 0x01 //calibrate RC64K clock +#define RC13MEnable 0x02 //calibrate RC13M clock +#define PLLEnable 0x04 //calibrate PLL +#define ADCPulseEnable 0x08 //calibrate ADC Pulse +#define ADCBulkNEnable 0x10 //calibrate ADC bulkN +#define ADCBulkPEnable 0x20 //calibrate ADC bulkP +#define ImgEnable 0x40 //calibrate image +#define ALLDevices 0x7F //calibrate all devices + +#define RC64K_CALIB_ERR 0x0001 +#define RC13M_CALIB_ERR 0x0002 +#define PLL_CALIB_ERR 0x0004 +#define ADC_CALIB_ERR 0x0008 +#define IMG_CALIB_ERR 0x0010 +#define XOSC_START_ERR 0x0020 +#define PLL_LOCK_ERR 0x0040 +#define RFU 0x0080 +#define PA_RAMP_ERR 0x0100 + + +//SPI settings +#define LTspeedMaximum 8000000 +#define LTdataOrder MSBFIRST +#define LTdataMode SPI_MODE0 + +//FSKRTTY Settings +#define ParityNone 0 +#define ParityOdd 1 +#define ParityEven 2 +#define ParityZero 0xF0 +#define ParityOne 0xF1 + +#define ToneMinuS 52 //timed constant for ATmega328P at 8Mhz with FM Tone delayus at 0, period for half loop + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + diff --git a/lib/SX12XX-LoRa/src/SX127XLT.cpp b/lib/SX12XX-LoRa/src/SX127XLT.cpp new file mode 100644 index 0000000..6de9cad --- /dev/null +++ b/lib/SX12XX-LoRa/src/SX127XLT.cpp @@ -0,0 +1,4272 @@ +/* + Copyright 2019 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + Original published 17/12/19 + New version 23/12/20 +*/ + +/* + Parts of code Copyright (c) 2013, SEMTECH S.A. + See LICENSE.TXT file included in the library +*/ + +#include +#include + +#define LTUNUSED(v) (void) (v) //add LTUNUSED(variable); in functions to avoid compiler warnings +#define USE_SPI_TRANSACTION //this is the standard behaviour of library, use SPI Transaction switching + +//#define SX127XDEBUG1 //enable level 1 debug messages +//#define SX127XDEBUG2 //enable level 2 debug messages +//#define SX127XDEBUG3 //enable level 3 debug messages +//#define DEBUGPHANTOM //used to set bebuging for Phantom packets +//#define SX127XDEBUGPINS //enable pin allocation debug messages +//#define DEBUGFSKRTTY //enable for FSKRTTY debugging +//#define SX127XDEBUGRELIABLE +//#define PACONFIGDEBUG +//#define APPLYERRATANOTE_2_3 //if enabled the changes suggested in SX1276_77_8_ErrataNote_1_1 are applied + + +SX127XLT::SX127XLT() +{ + +} + +/* Formats for :begin + 1 original > begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinDIO0, int8_t pinDIO1, int8_t pinDIO2, uint8_t device); + 2 Simplified > begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinDIO0, uint8_t device); + 3 Bare minimum, no NRESET or DIO0 pins > begin(int8_t pinNSS, uint8_t device); +*/ + + +bool SX127XLT::begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinDIO0, int8_t pinDIO1, int8_t pinDIO2, uint8_t device) +{ + //format 1 pins, assign the all available pins +#ifdef SX127XDEBUG1 + Serial.println(F("1 begin() ")); +#endif + + //assign the passed pins to the class private variabled + _NSS = pinNSS; + _NRESET = pinNRESET; + _DIO0 = pinDIO0; + _DIO1 = pinDIO1; + _DIO2 = pinDIO2; + _Device = device; //device type needs to be assigned before reset + _TXDonePin = pinDIO0; //this is defalt pin for sensing TX done + _RXDonePin = pinDIO0; //this is defalt pin for sensing RX done + + pinMode(_NSS, OUTPUT); + digitalWrite(_NSS, HIGH); + pinMode(_NRESET, OUTPUT); + digitalWrite(_NRESET, LOW); + + if (_DIO0 >= 0) + { + pinMode( _DIO0, INPUT); + } + + if (_DIO1 >= 0) + { + pinMode( _DIO1, INPUT); + } + + if (_DIO2 >= 0) + { + pinMode( _DIO2, INPUT); + } + + resetDevice(); + +#ifdef SX127XDEBUGPINS + Serial.println(F("1 begin()")); + Serial.println(F("SX127XLT constructor instantiated successfully")); + Serial.print(F("NSS ")); + Serial.println(_NSS); + Serial.print(F("NRESET ")); + Serial.println(_NRESET); + Serial.print(F("DIO0 ")); + Serial.println(_DIO0); + Serial.print(F("DIO1 ")); + Serial.println(_DIO1); + Serial.print(F("DIO2 ")); + Serial.println(_DIO2); +#endif + + if (checkDevice()) +{ + return true; +} + +return false; +} + + + +bool SX127XLT::begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinDIO0, uint8_t device) +{ + //format 2 pins, simplified +#ifdef SX127XDEBUG1 + Serial.println(F("2 begin() ")); +#endif + + //assign the passed pins to the class private variabled + _NSS = pinNSS; + _NRESET = pinNRESET; + _DIO0 = pinDIO0; + _DIO1 = -1; //pin not used + _DIO2 = -1; //pin not used + _Device = device; //device type needs to be assigned before reset + _TXDonePin = pinDIO0; //this is defalt pin for sensing TX done + _RXDonePin = pinDIO0; //this is defalt pin for sensing RX done + + pinMode(_NSS, OUTPUT); + digitalWrite(_NSS, HIGH); + pinMode(_NRESET, OUTPUT); + digitalWrite(_NRESET, HIGH); + + if (_DIO0 >= 0) + { + pinMode( _DIO0, INPUT); + } + + resetDevice(); + + +#ifdef SX127XDEBUGPINS + Serial.println(F("format 2 begin() ")); + Serial.println(F("SX127XLT constructor instantiated successfully")); + Serial.print(F("NSS ")); + Serial.println(_NSS); + Serial.print(F("NRESET ")); + Serial.println(_NRESET); + Serial.print(F("DIO0 ")); + Serial.println(_DIO0); +#endif + + if (checkDevice()) +{ + return true; +} + +return false; +} + + +bool SX127XLT::begin(int8_t pinNSS, uint8_t device) +{ + //format 3 pins, assign the all available pins +#ifdef SX127XDEBUG1 + Serial.println(F("3 begin() ")); +#endif + + //assign the passed pins to the class private variabled + _NSS = pinNSS; + _Device = device; //device type needs to be assigned before reset + + pinMode(_NSS, OUTPUT); + digitalWrite(_NSS, HIGH); + + +#ifdef SX127XDEBUGPINS + Serial.println(F("2 begin()")); + Serial.println(F("SX127XLT constructor instantiated successfully")); + Serial.print(F("NSS ")); + Serial.println(_NSS); +#endif + + if (checkDevice()) +{ + return true; +} + +return false; +} + + +void SX127XLT::resetDevice() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("resetDevice() ")); +#endif + + if (_Device == DEVICE_SX1272) + { + digitalWrite(_NRESET, HIGH); + delay(2); + digitalWrite(_NRESET, LOW); + delay(20); + } + else + { + digitalWrite(_NRESET, LOW); + delay(2); + digitalWrite(_NRESET, HIGH); + delay(20); + } +} + + +void SX127XLT::setMode(uint8_t modeconfig) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setMode() ")); +#endif + + uint8_t regdata; + + regdata = modeconfig + _PACKET_TYPE; + writeRegister(REG_OPMODE, regdata); +} + + +void SX127XLT::setSleep(uint8_t sleepconfig) +{ + //settings passed via sleepconfig are ignored, feature retained for compatibility with SX128x,SX126x + +#ifdef SX127XDEBUG1 + Serial.println(F("setSleep() ")); +#endif + + LTUNUSED(sleepconfig); + + uint8_t regdata; + + regdata = readRegister(REG_OPMODE); + writeRegister(REG_OPMODE, (regdata & 0xF8)); //clear bits 0,1,2 to set sleepmode + delay(1); //allow time for shutdown +} + + +bool SX127XLT::checkDevice() +{ + //check there is a device out there, writes a register and reads back + +#ifdef SX127XDEBUG1 + Serial.println(F("checkDevice() ")); +#endif + + uint8_t Regdata1, Regdata2; + Regdata1 = readRegister(REG_FRMID); //low byte of frequency setting + writeRegister(REG_FRMID, (Regdata1 + 1)); + Regdata2 = readRegister(REG_FRMID); //read changed value back + writeRegister(REG_FRMID, Regdata1); //restore register to original value + + if (Regdata2 == (Regdata1 + 1)) + { + return true; + } + else + { + return false; + } +} + + +void SX127XLT::wake() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("wake() ")); +#endif + uint8_t regdata; + + regdata = readRegister(REG_OPMODE); + writeRegister(REG_OPMODE, (regdata | 0x01)); //set bit 0 to goto stdby mode +} + + +void SX127XLT::calibrateImage(uint8_t null) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("calibrateImage() ")); +#endif + + LTUNUSED(null); + + uint8_t regdata, savedmode; + savedmode = readRegister(REG_OPMODE); + writeRegister(REG_OPMODE, 0x00); //sleep + writeRegister(REG_OPMODE, 0x00); //sleep + writeRegister(REG_OPMODE, 0x01); //standby FSK mode + regdata = (readRegister(REG_IMAGECAL) | 0x40); + writeRegister(REG_IMAGECAL, regdata); //start calibration + delay(15); //calibration time 10mS + writeRegister(REG_OPMODE, 0x00); //sleep + writeRegister(REG_OPMODE, savedmode & 0xFE); + writeRegister(REG_OPMODE, savedmode); +} + + +void SX127XLT::printRegister(uint8_t reg) +{ +uint8_t regdata; + + Serial.print(F("Register 0x")); + + if (reg < 0x10) + { + Serial.print(F("0")); + } + + Serial.print(reg,HEX); + regdata = readRegister(reg); + Serial.print(F(" 0x")); + + if (regdata < 0x10) + { + Serial.print(F("0")); + } + + Serial.print(regdata,HEX); +} + + +uint16_t SX127XLT::CRCCCITT(uint8_t *buffer, uint16_t size, uint16_t startvalue) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("CRCCCITT() ")); +#endif + + uint16_t index, libraryCRC; + uint8_t j; + + libraryCRC = startvalue; //start value for CRC16 + + for (index = 0; index < size; index++) + { + libraryCRC ^= (((uint16_t)buffer[index]) << 8); + for (j = 0; j < 8; j++) + { + if (libraryCRC & 0x8000) + libraryCRC = (libraryCRC << 1) ^ 0x1021; + else + libraryCRC <<= 1; + } + } + + return libraryCRC; +} + + +uint16_t SX127XLT::CRCCCITTSX(uint8_t startadd, uint8_t endadd, uint16_t startvalue) +{ + //genrates a CRC of an area of the internal SX buffer + +#ifdef SX127XDEBUG1 + Serial.println(F("CRCCCITTSX() ")); +#endif + + + uint16_t index, libraryCRC; + uint8_t j; + + libraryCRC = startvalue; //start value for CRC16 + + startReadSXBuffer(startadd); //begin the buffer read + + for (index = startadd; index <= endadd; index++) + { + libraryCRC ^= (((uint16_t) readUint8() ) << 8); + for (j = 0; j < 8; j++) + { + if (libraryCRC & 0x8000) + libraryCRC = (libraryCRC << 1) ^ 0x1021; + else + libraryCRC <<= 1; + } + } + + endReadSXBuffer(); //end the buffer read + + return libraryCRC; +} + + +void SX127XLT::setDevice(uint8_t type) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setDevice() ")); +#endif + + _Device = type; +} + + +void SX127XLT::printDevice() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printDevice() ")); +#endif + + switch (_Device) + { + case DEVICE_SX1272_PABOOST: + Serial.print(F("SX1272_PABOOST")); + break; + + case DEVICE_SX1276_PABOOST: + Serial.print(F("SX1276_PABOOST")); + break; + + case DEVICE_SX1277_PABOOST: + Serial.print(F("SX1277_PABOOST")); + break; + + case DEVICE_SX1278_PABOOST: + Serial.print(F("SX1278_PABOOST")); + break; + + case DEVICE_SX1279_PABOOST: + Serial.print(F("SX1279_PABOOST")); + break; + + case DEVICE_SX1276_RFO: + Serial.print(F("SX1276_RFO")); + break; + + case DEVICE_SX1277_RFO: + Serial.print(F("SX1277_RFO")); + break; + + case DEVICE_SX1278_RFO: + Serial.print(F("SX1278_RFO")); + break; + + case DEVICE_SX1279_RFO: + Serial.print(F("SX1279_RFO")); + break; + + default: + Serial.print(F("Unknown Device")); + } +} + + +uint8_t SX127XLT::getOperatingMode() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getOperatingMode() ")); +#endif + + return readRegister(REG_OPMODE); +} + + +bool SX127XLT::isReceiveDone() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("isReceiveDone()")); +#endif + + return digitalRead(_RXDonePin); +} + + +bool SX127XLT::isTransmitDone() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("isTransmitDone() ")); +#endif + + return digitalRead(_TXDonePin); +} + + +void SX127XLT::writeRegister(uint8_t address, uint8_t value) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeRegister() ")); +#endif + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //set NSS low + SPI.transfer(address | 0x80); //mask address for write + SPI.transfer(value); //write the byte + digitalWrite(_NSS, HIGH); //set NSS high + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + +#ifdef SX127XDEBUG2 + Serial.print(F("Write register ")); + printHEXByte0x(address); + Serial.print(F(" ")); + printHEXByte0x(value); + Serial.println(); + Serial.flush(); +#endif +} + + +uint8_t SX127XLT::readRegister(uint8_t address) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readRegister() ")); +#endif + + uint8_t regdata; + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //set NSS low + SPI.transfer(address & 0x7F); //mask address for read + regdata = SPI.transfer(0); //read the byte + digitalWrite(_NSS, HIGH); //set NSS high + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + +#ifdef SX127XDEBUG2 + Serial.print(F("Read register ")); + printHEXByte0x(address); + Serial.print(F(" ")); + printHEXByte0x(regdata); + Serial.println(); + Serial.flush(); +#endif + + return regdata; +} + + +void SX127XLT::printRegisters(uint16_t Start, uint16_t End) +{ + //prints the contents of SX127x registers to serial monitor + +#ifdef SX127XDEBUG + Serial.println(F("printRegisters() ")); +#endif + + uint16_t Loopv1, Loopv2, RegData; + + Serial.print(F("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F")); + Serial.println(); + + for (Loopv1 = Start; Loopv1 <= End;) //32 lines + { + Serial.print(F("0x")); + if (Loopv1 < 0x10) + { + Serial.print(F("0")); + } + Serial.print((Loopv1), HEX); //print the register number + Serial.print(F(" ")); + for (Loopv2 = 0; Loopv2 <= 15; Loopv2++) + { + RegData = readRegister(Loopv1); + if (RegData < 0x10) + { + Serial.print(F("0")); + } + Serial.print(RegData, HEX); //print the register number + Serial.print(F(" ")); + Loopv1++; + } + Serial.println(); + } +} + + +void SX127XLT::printOperatingMode() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printOperatingMode() ")); +#endif + + uint8_t regdata; + + regdata = getOpmode(); + + switch (regdata) + { + case 0: + Serial.print(F("SLEEP")); + break; + + case 1: + Serial.print(F("STDBY")); + break; + + case 2: + Serial.print(F("FSTX")); + break; + + case 3: + Serial.print(F("TX")); + break; + + case 4: + Serial.print(F("FSRX")); + break; + + case 5: + Serial.print(F("RXCONTINUOUS")); + break; + + case 6: + Serial.print(F("RXSINGLE")); + break; + + case 7: + Serial.print(F("CAD")); + break; + + default: + Serial.print(F("NOIDEA")); + break; + } +} + + +void SX127XLT::printOperatingSettings() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printOperatingSettings() ")); +#endif + + printDevice(); + Serial.print(F(",")); + + printOperatingMode(); + + Serial.print(F(",Version_")); + Serial.print(getVersion(), HEX); + + Serial.print(F(",PacketMode_")); + + if (getPacketMode()) + { + Serial.print(F("LoRa")); + } + else + { + Serial.print(F("FSK")); + } + + if (getHeaderMode()) + { + Serial.print(F(",Implicit")); + } + else + { + Serial.print(F(",Explicit")); + } + + Serial.print(F(",CRC_")); + if (getCRCMode()) + { + Serial.print(F("On")); + } + else + { + Serial.print(F("Off")); + } + + + Serial.print(F(",AGCauto_")); + if (getAGC()) + { + Serial.print(F("On")); + } + else + { + Serial.print(F("Off")); + } + + Serial.print(F(",LNAgain_")); + Serial.print(getLNAgain()); + + if (!bitRead(_Device,4)) //if bit 4 of _Device is 0 then either RFO LF_ANT or HF_ANT pin used for RF output + { + Serial.print(F(",LNAboostHF_")); + if (getLNAboostHF()) + { + Serial.print(F("On")); + } + else + { + Serial.print(F("Off")); + } + + Serial.print(F(",LNAboostLF_")); + if (getLNAboostLF()) + { + Serial.print(F("Reserved")); + } + else + { + Serial.print(F("Default")); + } + } +} + + +void SX127XLT::setTxParams(int8_t txPower, uint8_t rampTime) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setTxParams() ")); +#endif + uint8_t MaxPower, OutputPower, OcpTrim, boostval; + + if (_Device & 0x10) + { + //start setTxParams() for PABOOST + + #ifdef PACONFIGDEBUG + Serial.print(F(" PABOOST Device ")); + #endif + + boostval = PABOOSTON; + MaxPower = MAXPOWER17dBm; + + if (txPower > 20) //upper power limit for + { + txPower = 20; + } + + if (txPower < 2) + { + txPower = 2; + } + + if (txPower > 17) + { + writeRegister(REG_PADAC, 0x87); //Reg 0x4D this is same for SX1272 and SX1278 + OutputPower = txPower - 5; //power range is 15dBm, max is now 20dBm min is 5dBm + } + else + { + writeRegister(REG_PADAC, 0x84); //Reg 0x4D this is same for SX1272 and SX1278 + OutputPower = txPower - 2; //power range is 15dBm, max is 17dBm min is 2dBm + } + + if (_Device == DEVICE_SX1272_PABOOST) //power calculation for SX1272 is the same for < 17dbm and > 17dBm + { + OutputPower = txPower - 2; + } + + //end setTxParams() for PABOOST + } + else + { + //start setTxParams() for RFO + + #ifdef PACONFIGDEBUG + Serial.print(F(" RFO Device ")); + #endif + + boostval = PABOOSTOFF; + MaxPower = MAXPOWER14dBm; + + if (txPower > 14) //14dBm is upper power limit for RFO + { + txPower = 14; + } + + if (txPower < 0) + { + txPower = 0; + } + + OutputPower = txPower + 1; //power range is 15dBm, max is 14dBm min is 0dBm + writeRegister(REG_PADAC, 0x84); //must turn off high power setting for RFO mode + + //end setTxParams() for RFO + } + + //common routines for PABOOST and RFO + OcpTrim = OCP_TRIM_110MA; //value for OcpTrim 11dBm to 16dBm + + if (txPower >= 17) + { + OcpTrim = OCP_TRIM_150MA; + } + + if (txPower <= 10) + { + OcpTrim = OCP_TRIM_80MA; + } + + writeRegister(REG_PARAMP, rampTime); + writeRegister(REG_OCP, (OcpTrim+0x20)); + writeRegister(REG_PACONFIG, (boostval + MaxPower + OutputPower)); //MaxPower does not care for SX1272 + +#ifdef PACONFIGDEBUG + Serial.print(F("txPower,")); + Serial.print(txPower); + Serial.print(F(",REG_PACONFIG,")); + Serial.print(readRegister(REG_PACONFIG),HEX); + Serial.print(F(",PABOOST,")); + + if (readRegister(REG_PACONFIG) & 0x80) + { + Serial.print(F("ON,")); + } + else + { + Serial.print(F("OFF,")); + } + + Serial.print(F("MaxPower,")); + Serial.print(MaxPower,HEX); + Serial.print(F(",OutputPower,")); + Serial.print(OutputPower,HEX); + Serial.print(F(",")); + printOCPTRIM(); + #endif +} + + +void SX127XLT::printOCPTRIM() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printOCPTRIM() ")); +#endif + +uint8_t regdata; +regdata = readRegister(REG_OCP); + +Serial.print(F("REG_OCP,")); +Serial.print(regdata,HEX); +Serial.print(F(",")); + +if (!(regdata & 0x20)) +{ +Serial.print(F("OCP_TRIM_OFF ")); +return; +} + +//OCP must be on, so print trim value + +regdata = regdata & 0x1F; //mask off trim value + +switch (regdata) +{ +case OCP_TRIM_45MA: + Serial.print(F("OCP_TRIM_45MA ")); + break; + +case OCP_TRIM_80MA: + Serial.print(F("OCP_TRIM_80MA ")); + break; + +case OCP_TRIM_100MA: + Serial.print(F("OCP_TRIM_100MA ")); + break; + +case OCP_TRIM_110MA: + Serial.print(F("OCP_TRIM_110MA ")); + break; + +case OCP_TRIM_120MA: + Serial.print(F("OCP_TRIM_120MA ")); + break; + +case OCP_TRIM_130MA: + Serial.print(F("OCP_TRIM_130MA ")); + break; + +case OCP_TRIM_140MA: + Serial.print(F("OCP_TRIM_140MA ")); + break; + +case OCP_TRIM_150MA: + Serial.print(F("OCP_TRIM_150MA ")); + break; +} + +return; +} + + +void SX127XLT::setPacketParams(uint16_t packetParam1, uint8_t packetParam2, uint8_t packetParam3, uint8_t packetParam4, uint8_t packetParam5) +{ + //format is PreambleLength, Fixed\Variable length packets, Packetlength, CRC mode, IQ mode + +#ifdef SX127XDEBUG1 + Serial.println(F("SetPacketParams() ")); +#endif + + uint8_t preambleMSB, preambleLSB, regdata; + + + //******************************************************* + //These changes are the same for SX1272 and SX127X + //PreambleLength reg 0x20, 0x21 + preambleMSB = packetParam1 >> 8; + preambleLSB = packetParam1 & 0xFF; + writeRegister(REG_PREAMBLEMSB, preambleMSB); + writeRegister(REG_PREAMBLELSB, preambleLSB); + + //TX Packetlength reg 0x22 + writeRegister(REG_PAYLOADLENGTH, packetParam3); //when in implicit mode, this is used as receive length also + + //IQ mode reg 0x33 and 0x3B + if (packetParam5 == LORA_IQ_INVERTED) + { + writeRegister(REG_INVERTIQ, 0x66); + writeRegister(REG_INVERTIQ2, 0x19); + } + + if (packetParam5 == LORA_IQ_NORMAL) + { + writeRegister(REG_INVERTIQ, 0x27); + writeRegister(REG_INVERTIQ2, 0x1d); + } + + //regdata = ( (readRegister(REG_INVERTIQ)) & 0xBE ); //mask off invertIQ bit 6 and bit 0 + //writeRegister(REG_INVERTIQ, (regdata + packetParam5)); + //******************************************************* + + + //CRC mode + _UseCRC = packetParam4; //save CRC use status + + if (_Device != DEVICE_SX1272) + { + //for all devices apart from SX1272 + //Fixed\Variable length packets + regdata = ( (readRegister(REG_MODEMCONFIG1)) & (~READ_IMPLCIT_AND_X)); //mask off bit 0 + writeRegister(REG_MODEMCONFIG1, (regdata + packetParam2)); //write out with bit 0 set appropriatly + + //CRC on payload + regdata = ( (readRegister(REG_MODEMCONFIG2)) & (~READ_HASCRC_AND_X)); //mask off all bits bar CRC on - bit 2 + writeRegister(REG_MODEMCONFIG2, (regdata + (packetParam4 << 2))); //write out with CRC bit 2 set appropriatly + } + else + { + //for SX1272 + //Fixed\Variable length packets + regdata = ( (readRegister(REG_MODEMCONFIG1)) & (~READ_IMPLCIT_AND_2)); //mask off bit 2 + writeRegister(REG_MODEMCONFIG1, (regdata + (packetParam2 << 2))); //write out with bit 2 set appropriatly + + //CRC on payload + regdata = ( (readRegister(REG_MODEMCONFIG1)) & (~READ_HASCRC_AND_2)); //mask of all bits bar CRC on - bit 1 + writeRegister(REG_MODEMCONFIG1, (regdata + (packetParam4 << 1))); //write out with CRC bit 1 set appropriatly + } +} + + +void SX127XLT::setModulationParams(uint8_t modParam1, uint8_t modParam2, uint8_t modParam3, uint8_t modParam4) +{ + //order is SpreadingFactor, Bandwidth, CodeRate, Optimisation + +#ifdef SX127XDEBUG1 + Serial.println(F("setModulationParams() ")); +#endif + + uint8_t regdata, bw; + + //Spreading factor - same for SX1272 and SX127X - reg 0x1D + regdata = (readRegister(REG_MODEMCONFIG2) & (~READ_SF_AND_X)); + writeRegister(REG_MODEMCONFIG2, (regdata + (modParam1 << 4))); + + if (_Device != DEVICE_SX1272) + { + //for all devices apart from SX1272 + + //bandwidth + regdata = (readRegister(REG_MODEMCONFIG1) & (~READ_BW_AND_X)); + writeRegister(REG_MODEMCONFIG1, (regdata + modParam2)); + + //Coding rate + regdata = (readRegister(REG_MODEMCONFIG1) & (~READ_CR_AND_X)); + writeRegister(REG_MODEMCONFIG1, (regdata + (modParam3))); + + //Optimisation + if (modParam4 == LDRO_AUTO) + { + modParam4 = returnOptimisation(modParam2, modParam1); + } + + regdata = (readRegister(REG_MODEMCONFIG3) & (~READ_LDRO_AND_X)); + writeRegister(REG_MODEMCONFIG3, (regdata + (modParam4 << 3))); + + } + else + { + + //for SX1272 + regdata = (readRegister(REG_MODEMCONFIG1) & (~READ_BW_AND_2)); //value will be LORA_BW_500 128, LORA_BW_250 64, LORA_BW_125 0 + + switch (modParam2) + { + case LORA_BW_125: + bw = 0x00; + break; + + case LORA_BW_500: + bw = 0x80; + break; + + case LORA_BW_250: + bw = 0x40; + break; + + default: + bw = 0x00; //defaults to LORA_BW_125 + } + + writeRegister(REG_MODEMCONFIG1, (regdata + bw)); + + //Coding rate + regdata = (readRegister(REG_MODEMCONFIG1) & (~READ_CR_AND_2)); + writeRegister(REG_MODEMCONFIG1, (regdata + (modParam3 << 2))); + + //Optimisation + if (modParam4 == LDRO_AUTO) + { + modParam4 = returnOptimisation(modParam2, modParam1); + } + + regdata = (readRegister(REG_MODEMCONFIG1) & (~READ_LDRO_AND_2)); + writeRegister(REG_MODEMCONFIG1, (regdata + modParam4)); + + } + + #ifdef APPLYERRATANOTE_2_3 + //optimisations called by SX1276_77_8_ErrataNote_1_1 + + //ERRATA 2.3 - Receiver Spurious Reception of a LoRa Signal + if( modParam2 < LORA_BW_500 ) + { + writeRegister( REG_DETECTOPTIMIZE, readRegister(REG_DETECTOPTIMIZE) & 0x7F ); + writeRegister( REG_LRTEST30, 0x00 ); + + switch(modParam2) + { + case LORA_BW_007: // 7.8 kHz + writeRegister(REG_LRTEST2F,0x48); + _savedOffset = _savedOffset + 7800; + break; + case LORA_BW_010: // 10.4 kHz + writeRegister(REG_LRTEST2F,0x44); + _savedOffset = _savedOffset + 10400; + break; + case LORA_BW_015: // 15.6 kHz + writeRegister(REG_LRTEST2F,0x44); + _savedOffset = _savedOffset + 15600; + break; + case LORA_BW_020: // 20.8 kHz + writeRegister(REG_LRTEST2F,0x44); + _savedOffset = _savedOffset + 20800; + break; + case LORA_BW_031: // 31.2 kHz + writeRegister(REG_LRTEST2F,0x44); + _savedOffset = _savedOffset + 31200; + break; + case LORA_BW_041: // 41.4 kHz + writeRegister(REG_LRTEST2F, 0x44 ); + _savedOffset = _savedOffset + 41400; + break; + case LORA_BW_062: // 62.5 kHz + writeRegister(REG_LRTEST2F,0x40); + break; + case LORA_BW_125: // 125 kHz + writeRegister(REG_LRTEST2F,0x40); + break; + case LORA_BW_250: // 250 kHz + writeRegister(REG_LRTEST2F,0x40); + break; + } + } + else + { + writeRegister(REG_DETECTOPTIMIZE, readRegister(REG_DETECTOPTIMIZE ) | 0x80 ); + } + + + setRfFrequency(_savedFrequency,_savedOffset); //apply the updated frequency +#endif + + //ERRATA 2.1 - SX1276_77_8_ErrataNote_1_1 + if( ( modParam2 == LORA_BW_500 ) && ( _savedFrequency >= 862000000 ) ) + { + //ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth >= 862Mhz + writeRegister(REG_HIGHBWOPTIMIZE1,0x02); + writeRegister(REG_HIGHBWOPTIMIZE2,0x64); + } + else if( modParam2 == LORA_BW_500 ) + { + //ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth 410Mhz to 525Mhz + writeRegister(REG_HIGHBWOPTIMIZE1,0x02); + writeRegister(REG_HIGHBWOPTIMIZE2,0x7F); + } + else + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + writeRegister(REG_HIGHBWOPTIMIZE1,0x03); + } + + +//Datasheet SX1276-7-8-9_May_2020, page 115, REG_DETECTOPTIMIZE = 0x31, REG_LRDETECTIONTHRESHOLD = 0x37 + if( modParam1 == LORA_SF6 ) + { + writeRegister(REG_DETECTOPTIMIZE, ( readRegister( REG_DETECTOPTIMIZE ) & 0xF8 ) | 0x05 ); + writeRegister(REG_DETECTIONTHRESHOLD, 0x0C); + } + else + { + writeRegister( REG_DETECTOPTIMIZE, ( readRegister( REG_DETECTOPTIMIZE ) & 0xF8 ) | 0x03 ); + writeRegister( REG_DETECTIONTHRESHOLD, 0x0A ); + } +} + + +void SX127XLT::setRfFrequency(uint64_t freq64, int32_t offset) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setRfFrequency() ")); +#endif + + _savedFrequency = freq64; + _savedOffset = offset; + + freq64 = freq64 + offset; + freq64 = ((uint64_t)freq64 << 19) / 32000000; + _freqregH = freq64 >> 16; + _freqregM = freq64 >> 8; + _freqregL = freq64; + + writeRegister(REG_FRMSB, _freqregH); + writeRegister(REG_FRMID, _freqregM); + writeRegister(REG_FRLSB, _freqregL); +} + + +uint32_t SX127XLT::getFreqInt() +{ + //get the current set LoRa device frequency, return as long integer +#ifdef SX127XDEBUG1 + Serial.println(F("getFreqInt() ")); +#endif + + uint8_t Msb, Mid, Lsb; + uint32_t uinttemp; + float floattemp; + Msb = readRegister(REG_FRMSB); + Mid = readRegister(REG_FRMID); + Lsb = readRegister(REG_FRLSB); + floattemp = ((Msb * 0x10000ul) + (Mid * 0x100ul) + Lsb); + floattemp = ((floattemp * 61.03515625) / 1000000ul); + uinttemp = (uint32_t)(floattemp * 1000000); + return uinttemp; +} + + +int32_t SX127XLT::getFrequencyErrorRegValue() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getFrequencyErrorRegValue() ")); +#endif + + int32_t FrequencyError; + uint32_t msb, mid, lsb; + uint32_t allreg; + + setMode(MODE_STDBY_RC); + + msb = readRegister(REG_FEIMSB); + mid = readRegister(REG_FEIMID); + lsb = readRegister(REG_FEILSB); + +#ifdef SX127XDEBUG1 + Serial.println(); + Serial.print(F("Registers ")); + Serial.print(msb, HEX); + Serial.print(F(" ")); + Serial.print(mid, HEX); + Serial.print(F(" ")); + Serial.println(lsb, HEX); +#endif + + allreg = (uint32_t) ( msb << 16 ) | ( mid << 8 ) | lsb; + + if (allreg & 0x80000) + { + FrequencyError = (0xFFFFF - allreg) * -1; + } + else + { + FrequencyError = allreg; + } + + return FrequencyError; +} + + +int32_t SX127XLT::getFrequencyErrorHz() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getFrequencyErrorHz() ")); +#endif + + uint16_t msb, mid, lsb; + int16_t freqerr; + uint8_t bw; + float bwconst; + + msb = readRegister(REG_FEIMSB); + mid = readRegister(REG_FEIMID); + lsb = readRegister(REG_FEILSB); + + if (_Device != DEVICE_SX1272) + { + //for all devices apart from SX1272 + bw = (readRegister(REG_MODEMCONFIG1)) & (0xF0); + + switch (bw) + { + case LORA_BW_125: //ordered with most commonly used first + bwconst = 2.097; + break; + + case LORA_BW_062: + bwconst = 1.049; + break; + + case LORA_BW_041: + bwconst = 0.6996; + break; + + case LORA_BW_007: + bwconst = 0.1309; + break; + + case LORA_BW_010: + bwconst = 0.1745; + break; + + case LORA_BW_015: + bwconst = 0.2617; + break; + + case LORA_BW_020: + bwconst = 0.3490; + break; + + case LORA_BW_031: + bwconst = 0.5234; + break; + + case LORA_BW_250: + bwconst = 4.194; + break; + + case LORA_BW_500: + bwconst = 8.389; + break; + + default: + bwconst = 0x00; + } + } + else + { + //for the SX1272 + bw = (readRegister(REG_MODEMCONFIG1)) & (0xF0); + + switch (bw) + { + case 0: //this is LORA_BW_125 + bwconst = 2.097; + break; + + case 64: //this is LORA_BW_250 + bwconst = 4.194; + break; + + case 128: //this is LORA_BW_250 + bwconst = 8.389; + break; + + default: + bwconst = 0x00; + } + } + + freqerr = msb << 12; //shift lower 4 bits of msb into high 4 bits of freqerr + mid = (mid << 8) + lsb; + mid = (mid >> 4); + freqerr = freqerr + mid; + + freqerr = (int16_t) (freqerr * bwconst); + + return freqerr; +} + + +void SX127XLT::setTx(uint32_t timeout) +{ + //There is no TX timeout function for SX127X, the value passed is ignored + +#ifdef SX127XDEBUG1 + Serial.println(F("setTx() ")); +#endif + + LTUNUSED(timeout); //unused TX timeout passed for compatibility with SX126x, SX128x + + clearIrqStatus(IRQ_RADIO_ALL); + + /* This function not used on current SX127x + if (_rxtxpinmode) + { + rxEnable(); + } + */ + + writeRegister(REG_OPMODE, (MODE_TX + 0x88)); //TX on LoRa mode +} + + +void SX127XLT::setRx(uint32_t timeout) +{ + //no timeout in this routine, left in for compatibility +#ifdef SX127XDEBUG1 + Serial.println(F("setRx()")); +#endif + + LTUNUSED(timeout); + + clearIrqStatus(IRQ_RADIO_ALL); + + /* This function not used on current SX127x + if (_rxtxpinmode) + { + rxEnable(); + } + */ + + writeRegister(REG_OPMODE, (MODE_RXCONTINUOUS + 0x80)); //RX on LoRa mode +} + + +bool SX127XLT::readTXIRQ() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readTXIRQ()")); +#endif + + uint16_t IRQreg; + IRQreg = readIrqStatus(); + + if (IRQreg & IRQ_TX_DONE) + { + return true; + } + else + { + return false; + } +} + + +bool SX127XLT::readRXIRQ() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readRXIRQ() ")); +#endif + + uint16_t IRQreg; + IRQreg = readIrqStatus(); + + if (IRQreg & IRQ_RX_DONE) + { + return true; + } + else + { + return false; + } +} + + +void SX127XLT::setLowPowerReceive() +{ + //set min LNA gain, AGC off +#ifdef SX127XDEBUG1 + Serial.println(F("setLowPowerReceive() ")); +#endif + uint8_t regdata; + + regdata = readRegister(REG_MODEMCONFIG3); + bitClear(regdata,2); //set bit 2 to 0 turns off AGC + writeRegister(REG_MODEMCONFIG3, regdata ); //write data back + + writeRegister(REG_LNA, 0xC0 ); //Minimum gain for PA_BOOST and default for RFO_HF default LNA current +} + + +void SX127XLT::setHighSensitivity() +{ + //set max LNA gain and Boosted LNA for HF mode + +#ifdef SX127XDEBUG1 + Serial.println(F("setHighSensitivity() ")); +#endif + + writeRegister(REG_LNA, 0x23 ); //MAX gain for PA_BOOST and for RFO_HF set 150% LNA current. + } + + +void SX127XLT::setRXGain(uint8_t config) +{ + //set RX power saving mode + +#ifdef SX127XDEBUG1 + Serial.println(F("setRXGain() ")); +#endif + + uint8_t regdata; + + if (_Device != DEVICE_SX1272) + { + //for all devices apart from SX1272 + regdata = readRegister(REG_MODEMCONFIG3); + writeRegister(REG_MODEMCONFIG3, (regdata & (~READ_AGCAUTO_AND_X))); //clear bit AgcAutoOn (2) to ensure RegLNA is controlling gain + writeRegister(REG_LNA, config); + } + else + { + regdata = readRegister(REG_MODEMCONFIG2); + writeRegister(REG_MODEMCONFIG2, (regdata & (~READ_AGCAUTO_AND_2))); //clear bit AgcAutoOn (2) to ensure RegLNA is controlling gain + writeRegister(REG_LNA, config); + } +} + + +uint8_t SX127XLT::getAGC() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getAGC() ")); +#endif + + uint8_t regdata; + + if (_Device != DEVICE_SX1272) + { + regdata = readRegister(REG_MODEMCONFIG3); + regdata = (regdata & READ_AGCAUTO_AND_X); + } + else + { + regdata = readRegister(REG_MODEMCONFIG2); + regdata = (regdata & READ_AGCAUTO_AND_2); + } + + return (regdata >> 2); +} + + +uint8_t SX127XLT::getLNAgain() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getLNAgain() ")); +#endif + + uint8_t regdata; + regdata = readRegister(REG_LNA); + regdata = (regdata & READ_LNAGAIN_AND_X); + return (regdata >> 5); +} + + +uint8_t SX127XLT::getCRCMode() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getCRCMode() ")); +#endif + + uint8_t regdata; + + regdata = readRegister(REG_MODEMCONFIG2); + + if (_Device != DEVICE_SX1272) + { + regdata = readRegister(REG_MODEMCONFIG2); + regdata = ((regdata & READ_HASCRC_AND_X) >> 2); + } + else + { + regdata = readRegister(REG_MODEMCONFIG1); + regdata = ((regdata & READ_HASCRC_AND_2) >> 1); + } + + return regdata; +} + + +uint8_t SX127XLT::getHeaderMode() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getHeaderMode() ")); +#endif + + uint8_t regdata; + + regdata = readRegister(REG_MODEMCONFIG1); + + if (_Device != DEVICE_SX1272) + { + regdata = (regdata & READ_IMPLCIT_AND_X); + } + else + { + regdata = ((regdata & READ_IMPLCIT_AND_2) >> 2); + } + + return regdata; +} + + +uint8_t SX127XLT::getLNAboostLF() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getLNAboostLFLF() ")); +#endif + + uint8_t regdata; + + regdata = readRegister(REG_LNA); + + if (_Device != DEVICE_SX1272) + { + regdata = (regdata & READ_LNABOOSTLF_AND_X); + } + else + { + regdata = (regdata & READ_LNABOOSTLF_AND_2); + } + + return (regdata >> 3); +} + + +uint8_t SX127XLT::getLNAboostHF() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getLNAboostHF() ")); +#endif + + uint8_t regdata; + + regdata = readRegister(REG_LNA); + + if (_Device != DEVICE_SX1272) + { + regdata = (regdata & READ_LNABOOSTHF_AND_X); + } + else + { + regdata = (regdata & READ_LNABOOSTHF_AND_2); + } + + return regdata; +} + + +uint8_t SX127XLT::getOpmode() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getOpmode()")); +#endif + + uint8_t regdata; + + regdata = (readRegister(REG_OPMODE) & READ_OPMODE_AND_X); + + return regdata; +} + + +uint8_t SX127XLT::getPacketMode() +{ + //its either LoRa or FSK + +#ifdef SX127XDEBUG1 + Serial.println(F("getPacketMode() ")); +#endif + + uint8_t regdata; + + regdata = (readRegister(REG_OPMODE) & READ_RANGEMODE_AND_X); + + return (regdata >> 7); +} + + +uint8_t SX127XLT::readRXPacketL() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readRXPacketL() ")); +#endif + + _RXPacketL = readRegister(REG_RXNBBYTES); + return _RXPacketL; +} + + + +uint8_t SX127XLT::readTXPacketL() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readTXPacketL() ")); +#endif + + return _TXPacketL; +} + + +int16_t SX127XLT::readPacketRSSI() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readPacketRSSI() ")); +#endif + + //actual read SNR register values normally seen in practice are from 0 to 48 (0dB to +12dB) + //and 167 to 255 (-22dB to 0dB) + + int16_t _PacketRSSI; //RSSI of received packet + int8_t SNRregdata; + + if (_savedFrequency < 779000000) //779Mhz is lower frequency limit for SX1279 on band1 (HF Port) + { + _PacketRSSI = -164 + readRegister(REG_PKTRSSIVALUE); + } + else + { + _PacketRSSI = -157 + readRegister(REG_PKTRSSIVALUE); + } + + SNRregdata = readRegister(REG_PKTSNRVALUE); + + if (SNRregdata < 0) //check for negative SNR value + { + //Serial.print(F(" (-SNR) ")); + _PacketRSSI = (_PacketRSSI + (SNRregdata >> 2)); //add datasheet fiddle factor + } + + return _PacketRSSI; +} + + +int16_t SX127XLT::readCurrentRSSI() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readCurrentRSSI() ")); +#endif + + return readRegister(REG_CURRENTRSSIVALUE); +} + + +int8_t SX127XLT::readPacketSNR() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readPacketSNR() ")); +#endif + + uint8_t regdata; + int8_t _PacketSNR; + + regdata = readRegister(REG_PKTSNRVALUE); + + if (regdata > 127) + { + _PacketSNR = ((255 - regdata) / 4) * (-1); + } + else + { + _PacketSNR = regdata / 4; + } + + return _PacketSNR; +} + + +bool SX127XLT::readPacketCRCError() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readPacketCRCError() ")); +#endif + + uint16_t IRQreg; + IRQreg = readIrqStatus(); + + if (IRQreg & IRQ_CRC_ERROR) + { + return true; + } + else + { + return false; + } +} + + +bool SX127XLT::readPacketHeaderValid() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readPacketHeaderValid() ")); +#endif + + uint16_t IRQreg; + IRQreg = readIrqStatus(); + + if (IRQreg & IRQ_HEADER_VALID) + { + return true; + } + else + { + return false; + } +} + + +uint8_t SX127XLT::packetOK() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("packetOK() ")); +#endif + + bool packetHasCRC; + + packetHasCRC = (readRegister(REG_HOPCHANNEL) & 0x40); //read the packet has CRC bit in RegHopChannel + +#ifdef DEBUGPHANTOM + Serial.print(F("PacketHasCRC = ")); + Serial.println(packetHasCRC); + Serial.print(F("_UseCRC = ")); + Serial.println(_UseCRC); +#endif + + if ( !packetHasCRC && _UseCRC ) + { + _IRQmsb = _IRQmsb + IRQ_NO_PACKET_CRC; //flag the phantom packet + return 0; + } + + if ( readIrqStatus() != (IRQ_RX_DONE + IRQ_HEADER_VALID) ) + { + return 0; //no RX done and header valid only, could be CRC error + } + + return 1; //packet is OK +} + + +uint8_t SX127XLT::readRXPacketType() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readRXPacketType() ")); +#endif + + return _RXPacketType; +} + + +uint8_t SX127XLT::readRXDestination() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readRXDestination() ")); +#endif + return _RXDestination; +} + + +uint8_t SX127XLT::readRXSource() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readRXSource() ")); +#endif + + return _RXSource; +} + + +void SX127XLT::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setBufferBaseAddress() ")); +#endif + + writeRegister(REG_FIFOTXBASEADDR, txBaseAddress); + writeRegister(REG_FIFORXBASEADDR, rxBaseAddress); +} + + +void SX127XLT::setPacketType(uint8_t packettype ) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setPacketType() ")); +#endif + uint8_t regdata; + + regdata = (readRegister(REG_OPMODE) & 0x7F); //save all register bits bar the LoRa\FSK bit 7 + + if (packettype == PACKET_TYPE_LORA) + { + _PACKET_TYPE = PACKET_TYPE_LORA; + writeRegister(REG_OPMODE, 0x80); //REG_OPMODE, need to set to sleep mode before configure for LoRa mode + writeRegister(REG_OPMODE, (regdata + 0x80)); //back to original standby mode with LoRa set + } + else + { + _PACKET_TYPE = PACKET_TYPE_GFSK; + writeRegister(REG_OPMODE, 0x00); //REG_OPMODE, need to set to sleep mode before configure for FSK + writeRegister(REG_OPMODE, regdata); //back to original standby mode with FSK set + } +} + + +void SX127XLT::clearIrqStatus(uint16_t irqMask) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("clearIrqStatus() ")); +#endif + + uint8_t masklsb; + uint16_t maskmsb; + _IRQmsb = _IRQmsb & 0xFF00; //make sure _IRQmsb does not have LSB bits set. + masklsb = (irqMask & 0xFF); + maskmsb = (irqMask & 0xFF00); + writeRegister(REG_IRQFLAGS, masklsb); //clear standard IRQs + _IRQmsb = (_IRQmsb & (~maskmsb)); //only want top bits set. +} + + +uint16_t SX127XLT::readIrqStatus() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readIrqStatus()")); +#endif + + bool packetHasCRC; + uint8_t regdata; + regdata = readRegister(REG_IRQFLAGS); + + packetHasCRC = (readRegister(REG_HOPCHANNEL) & 0x40); //read the packet has CRC bit in RegHopChannel + + +#ifdef DEBUGPHANTOM + Serial.print(F("PacketHasCRC = ")); + Serial.println(packetHasCRC); + Serial.print(F("_UseCRC = ")); + Serial.println(_UseCRC); +#endif + + if (bitRead(regdata,6)) //check if its a packet receive (IRQ_RX_DONE set) + { + if (!packetHasCRC && _UseCRC) //check if packet header indicates no CRC on packet, byt use CRC set + { + bitSet(_IRQmsb, 10); //flag the phantom packet, set bit 10 + } + } + return (regdata + _IRQmsb); +} + + +void SX127XLT::setDioIrqParams(uint16_t irqMask, uint16_t dio0Mask, uint16_t dio1Mask, uint16_t dio2Mask) +{ + //note the irqmask contains the bit values of the interrupts that are allowed, so for all interrupts value is 0xFFFF + +#ifdef SX127XDEBUG1 + Serial.println(F("setDioIrqParams() ")); +#endif + + uint8_t mask0, mask1, mask2; + + LTUNUSED(dio2Mask); //variable left in call for compatibility with other libraries + + + switch (dio0Mask) + { + case IRQ_RX_DONE: + mask0 = 0; + break; + + case IRQ_TX_DONE: + mask0 = 0x40; + break; + + case IRQ_CAD_DONE: + mask0 = 0x80; + break; + + default: + mask0 = 0x0C; + } + + switch (dio1Mask) //for compatibility with other libraries IRQ_RX_TIMEOUT = IRQ_RX_TX_TIMEOUT + { + case IRQ_RX_TIMEOUT: + mask1 = 0; + break; + + case IRQ_FSHS_CHANGE_CHANNEL: + mask1 = 0x10; + break; + + case IRQ_CAD_ACTIVITY_DETECTED: + mask1 = 0x20; + break; + + default: + mask1 = 0x30; + } + + mask2 = 0x00; //is always IRQ_FSHS_CHANGE_CHANNEL + + writeRegister(REG_IRQFLAGSMASK, ~irqMask); + writeRegister(REG_DIOMAPPING1, (mask0 + mask1 + mask2)); +} + + +void SX127XLT::printIrqStatus() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printIrqStatus() ")); +#endif + + uint8_t _IrqStatus; + _IrqStatus = readIrqStatus(); + + //0x01 + if (_IrqStatus & IRQ_CAD_ACTIVITY_DETECTED) + { + Serial.print(F(",IRQ_CAD_ACTIVITY_DETECTED")); + } + + //0x02 + if (_IrqStatus & IRQ_FSHS_CHANGE_CHANNEL) + { + Serial.print(F(",IRQ_FSHS_CHANGE_CHANNEL")); + } + + //0x04 + if (_IrqStatus & IRQ_CAD_DONE) + { + Serial.print(F(",IRQ_CAD_DONE")); + } + + //0x08 + if (_IrqStatus & IRQ_TX_DONE) + { + Serial.print(F(",IRQ_TX_DONE")); + } + + //0x10 + if (_IrqStatus & IRQ_HEADER_VALID) + { + Serial.print(F(",IRQ_HEADER_VALID")); + } + + //0x20 + if (_IrqStatus & IRQ_CRC_ERROR) + { + Serial.print(F(",IRQ_CRC_ERROR")); + } + + //0x40 + if (_IrqStatus & IRQ_RX_DONE) + { + Serial.print(F(",IRQ_RX_DONE")); + } + + //0x80 + if (_IrqStatus & IRQ_RX_TIMEOUT ) + { + Serial.print(F(",IRQ_RX_TIMEOUT ")); + } + + if (_IRQmsb & IRQ_TX_TIMEOUT ) + { + Serial.print(F(",TX_TIMEOUT")); + } + + if (_IRQmsb & IRQ_RX_TIMEOUT ) + { + Serial.print(F(",RX_TIMEOUT")); + } + + if (_IRQmsb & IRQ_NO_PACKET_CRC ) + { + Serial.print(F(",NO_PACKET_CRC")); + } +} + + +void SX127XLT::printASCIIPacket(uint8_t *buffer, uint8_t size) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printASCIIPacket() ")); +#endif + + uint8_t index; + + for (index = 0; index < size; index++) + { + Serial.write(buffer[index]); + } +} + + +void SX127XLT::printHEXPacket(uint8_t *buffer, uint8_t size) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printHEXPacket() ")); +#endif + + uint8_t index; + + for (index = 0; index < size; index++) + { + printHEXByte(buffer[index]); + Serial.print(F(" ")); + } +} + + +void SX127XLT::printASCIIorHEX(uint8_t temp) +{ + //prints as ASCII if within range, otherwise as HEX. +#ifdef SX127XDEBUG1 + Serial.println(F("printASCIIorHEX() ")); +#endif + + if ((temp < 0x10) || (temp > 0x7E)) + { + Serial.print(F(" (")); + printHEXByte(temp); + Serial.print(F(") ")); + } + else + { + Serial.write(temp); + } +} + + +void SX127XLT::printHEXByte(uint8_t temp) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printHEXByte() ")); +#endif + + if (temp < 0x10) + { + Serial.print(F("0")); + } + Serial.print(temp, HEX); +} + + +void SX127XLT::printHEXByte0x(uint8_t temp) +{ + //print a byte, adding 0x + +#ifdef SX127XDEBUG1 + Serial.println(F("printHEXByte0x()")); +#endif + + Serial.print(F("0x")); + if (temp < 0x10) + { + Serial.print(F("0")); + } + Serial.print(temp, HEX); +} + + +bool SX127XLT::isRXdone() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("isRXdone() ")); +#endif + + return digitalRead(_DIO0); +} + +bool SX127XLT::isTXdone() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("isTXdone()")); +#endif + + return digitalRead(_DIO0); +} + + +bool SX127XLT::isRXdoneIRQ() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("isRXdoneIRQ() ")); +#endif + + return (readRegister(REG_IRQFLAGS) & IRQ_RX_DONE); +} + + +bool SX127XLT::isTXdoneIRQ() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("isTXdoneIRQ() ")); +#endif + + return (readRegister(REG_IRQFLAGS) & IRQ_TX_DONE); +} + + +void SX127XLT::setTXDonePin(uint8_t pin) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setTXDonePin() ")); +#endif + + _TXDonePin = pin; +} + + +void SX127XLT:: setRXDonePin(uint8_t pin) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setRXDonePin() ")); +#endif + + _RXDonePin = pin; +} + + +uint8_t SX127XLT::receive(uint8_t *rxbuffer, uint8_t size, uint32_t rxtimeout, uint8_t wait ) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("receive()")); +#endif + + uint16_t index; + uint32_t startmS; + uint8_t regdata; + + setMode(MODE_STDBY_RC); + regdata = readRegister(REG_FIFORXBASEADDR); //retrieve the RXbase address pointer + writeRegister(REG_FIFOADDRPTR, regdata); //and save in FIFO access ptr + + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_HEADER_VALID), 0, 0); //set for IRQ on RX done + setRx(0); //no actual RX timeout in this function + + if (!wait) + { + return 0; //not wait requested so no packet length to pass + } + + if (rxtimeout == 0) + { + while (!digitalRead(_RXDonePin)); //Wait for DIO0 to go high, no timeout, RX DONE + } + else + { + //change to allow for millis() rollover + //code was endtimeoutmS = millis() + rxtimeout; while (!digitalRead(_RXDonePin) && (millis() < endtimeoutmS)); + startmS = millis(); + while (!digitalRead(_RXDonePin) && ((uint32_t) (millis() - startmS) < rxtimeout)); + } + + setMode(MODE_STDBY_RC); //ensure to stop further packet reception + + if (!digitalRead(_RXDonePin)) //check if DIO still low, is so must be RX timeout + { + _IRQmsb = IRQ_RX_TIMEOUT; + return 0; + } + + + if ( readIrqStatus() != (IRQ_RX_DONE + IRQ_HEADER_VALID) ) + { + return 0; //no RX done and header valid only, could be CRC error + } + + _RXPacketL = readRegister(REG_RXNBBYTES); + + if (_RXPacketL > size) //check passed buffer is big enough for packet + { + _RXPacketL = size; //truncate packet if not enough space in passed buffer + } + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(REG_FIFO); + + for (index = 0; index < _RXPacketL; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + +uint8_t SX127XLT::receiveAddressed(uint8_t *rxbuffer, uint8_t size, uint32_t rxtimeout, uint8_t wait) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("receiveAddressed() ")); +#endif + + uint16_t index; + uint32_t startmS; + uint8_t regdata; + + setMode(MODE_STDBY_RC); + regdata = readRegister(REG_FIFORXBASEADDR); //retrieve the RXbase address pointer + writeRegister(REG_FIFOADDRPTR, regdata); //and save in FIFO access ptr + + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_HEADER_VALID), 0, 0); //set for IRQ on RX done + setRx(0); //no actual RX timeout in this function + + if (!wait) + { + return 0; + } + + if (rxtimeout == 0) + { + while (!digitalRead(_RXDonePin)); //Wait for DIO0 to go high, no timeout, RX DONE + } + else + { + startmS = millis(); + while (!digitalRead(_RXDonePin) && ((uint32_t) (millis() - startmS) < rxtimeout)); + } + + setMode(MODE_STDBY_RC); //ensure to stop further packet reception + + if (!digitalRead(_RXDonePin)) //check if not DIO still low, is so must be RX timeout + { + _IRQmsb = IRQ_RX_TIMEOUT; + return 0; + } + + if ( readIrqStatus() != (IRQ_RX_DONE + IRQ_HEADER_VALID) ) + { + return 0; //no RX done and header valid only, could be CRC error + } + + _RXPacketL = readRegister(REG_RXNBBYTES); + + if (_RXPacketL > size) //check passed buffer is big enough for packet + { + _RXPacketL = size; //truncate packet if not enough space in passed buffer + } + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(REG_FIFO); + + _RXPacketType = SPI.transfer(0); + _RXDestination = SPI.transfer(0); + _RXSource = SPI.transfer(0); + + for (index = 0; index < _RXPacketL; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + +uint8_t SX127XLT::readPacket(uint8_t *rxbuffer, uint8_t size) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readPacket() ")); +#endif + + uint8_t index, regdata; + + if ( readIrqStatus() != (IRQ_RX_DONE + IRQ_HEADER_VALID) ) + { + return 0; //no RX done and header valid only, could be CRC error + } + + setMode(MODE_STDBY_RC); + regdata = readRegister(REG_FIFORXBASEADDR); //retrieve the RXbase address pointer + writeRegister(REG_FIFOADDRPTR, regdata); //and save in FIFO access ptr + + _RXPacketL = readRegister(REG_RXNBBYTES); + + if (_RXPacketL > size) //check passed buffer is big enough for packet + { + _RXPacketL = size; //truncate packet if not enough space + } + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(REG_FIFO); + + for (index = 0; index < _RXPacketL; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + +uint8_t SX127XLT::readPacketAddressed(uint8_t *rxbuffer, uint8_t size) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readPacketAddressed() ")); +#endif + + uint8_t index, regdata; + + setMode(MODE_STDBY_RC); + regdata = readRegister(REG_FIFORXBASEADDR); //retrieve the RXbase address pointer + writeRegister(REG_FIFOADDRPTR, regdata); //and save in FIFO access ptr + + _RXPacketL = readRegister(REG_RXNBBYTES); + + if (_RXPacketL > size) //check passed buffer is big enough for packet + { + _RXPacketL = size; //truncate packet if not enough space + } + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(REG_FIFO); + _RXPacketType = SPI.transfer(0); + _RXDestination = SPI.transfer(0); + _RXSource = SPI.transfer(0); + + for (index = 0; index < _RXPacketL; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + +uint8_t SX127XLT::transmit(uint8_t *txbuffer, uint8_t size, uint32_t txtimeout, int8_t txpower, uint8_t wait) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("transmit()")); +#endif + + uint8_t index, ptr; + uint8_t bufferdata; + uint32_t startmS; + + if (size == 0) + { + return false; + } + + setMode(MODE_STDBY_RC); + ptr = readRegister(REG_FIFOTXBASEADDR); //retrieve the TXbase address pointer + writeRegister(REG_FIFOADDRPTR, ptr); //and save in FIFO access ptr + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(WREG_FIFO); + + for (index = 0; index < size; index++) + { + bufferdata = txbuffer[index]; + SPI.transfer(bufferdata); + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + _TXPacketL = size; + writeRegister(REG_PAYLOADLENGTH, _TXPacketL); + + setTxParams(txpower, RADIO_RAMP_DEFAULT); //TX power and ramp time + + setDioIrqParams(IRQ_RADIO_ALL, IRQ_TX_DONE, 0, 0); //set for IRQ on TX done on first DIO pin + setTx(0); //TX timeout is not handled in setTX() + + if (!wait) + { + return _TXPacketL; + } + + if (txtimeout == 0) + { + while (!digitalRead(_TXDonePin)); //Wait for pin to go high, TX finished + } + else + { + startmS = millis(); + while (!digitalRead(_TXDonePin) && ((uint32_t) (millis() - startmS) < txtimeout)); + } + + setMode(MODE_STDBY_RC); //ensure we leave function with TX off + + if (!digitalRead(_TXDonePin)) + { + _IRQmsb = IRQ_TX_TIMEOUT; + return 0; + } + + return _TXPacketL; //no timeout, so TXdone must have been set +} + + +uint8_t SX127XLT::transmitAddressed(uint8_t *txbuffer, uint8_t size, char txpackettype, char txdestination, char txsource, uint32_t txtimeout, int8_t txpower, uint8_t wait ) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("transmitAddressed() ")); +#endif + + uint8_t index, ptr; + uint8_t bufferdata; + uint32_t startmS; + + if (size == 0) + { + return false; + } + + setMode(MODE_STDBY_RC); + ptr = readRegister(REG_FIFOTXBASEADDR); //retrieve the TXbase address pointer + writeRegister(REG_FIFOADDRPTR, ptr); //and save in FIFO access ptr + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(WREG_FIFO); + SPI.transfer(txpackettype); //Write the packet type + SPI.transfer(txdestination); //Destination node + SPI.transfer(txsource); //Source node + _TXPacketL = 3 + size; //we have added 3 header bytes to size + + for (index = 0; index < size; index++) + { + bufferdata = txbuffer[index]; + SPI.transfer(bufferdata); + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + writeRegister(REG_PAYLOADLENGTH, _TXPacketL); + + setTxParams(txpower, RADIO_RAMP_DEFAULT); //TX power and ramp time + + setDioIrqParams(IRQ_RADIO_ALL, IRQ_TX_DONE, 0, 0); //set for IRQ on TX done + setTx(0); //TX timeout is not handled in setTX() + + if (!wait) + { + return _TXPacketL; + } + + if (txtimeout == 0) + { + while (!digitalRead(_TXDonePin)); //Wait for DIO0 to go high, TX finished + } + else + { + startmS = millis(); + while (!digitalRead(_TXDonePin) && ((uint32_t) (millis() - startmS) < txtimeout)); + } + + setMode(MODE_STDBY_RC); //ensure we leave function with TX off + + if (!digitalRead(_TXDonePin)) //its a timeout if _TXDonepin still high + { + _IRQmsb = IRQ_TX_TIMEOUT; + return 0; + } + + return _TXPacketL; //no timeout, so TXdone must have been set +} + + +void SX127XLT::setupLoRa(uint32_t Frequency, int32_t Offset, uint8_t modParam1, uint8_t modParam2, uint8_t modParam3, uint8_t modParam4) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setupLoRa() ")); +#endif + + setMode(MODE_STDBY_RC); //go into standby mode to configure device + setPacketType(PACKET_TYPE_LORA); //use LoRa packets + setRfFrequency(Frequency, Offset); //set the operating frequncy + calibrateImage(0); //run calibration after setting frequency + setModulationParams(modParam1, modParam2, modParam3, modParam4); + setBufferBaseAddress(0x00, 0x00); //set bases addresses for TX and RX packets in SX buffer + setPacketParams(8, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL); //set the packet options + setSyncWord(LORA_MAC_PRIVATE_SYNCWORD); //syncword to use, 0x12 for standard private, 0x34 for public (LORAWAN) + setHighSensitivity(); +} + + +uint8_t SX127XLT::getLoRaSF() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getLoRaSF() ")); +#endif + + uint8_t regdata; + regdata = readRegister(REG_MODEMCONFIG2); + regdata = ((regdata & READ_SF_AND_X) >> 4); //SX1272 and SX1276 store SF in same place + + return regdata; +} + + +uint8_t SX127XLT::getLoRaCodingRate() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getLoRaCodingRate() ")); +#endif + + uint8_t regdata; + regdata = readRegister(REG_MODEMCONFIG1); + + if (_Device != DEVICE_SX1272) + { + //for all devices apart from SX1272 + regdata = (((regdata & READ_CR_AND_X) >> 1) + 4); + } + else + { + regdata = (((regdata & READ_CR_AND_2) >> 3) + 4); + } + + return regdata; +} + + +uint8_t SX127XLT::getOptimisation() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getOptimisation() ")); +#endif + + uint8_t regdata; + + if (_Device != DEVICE_SX1272) + { + //for all devices apart from SX1272 + regdata = readRegister(REG_MODEMCONFIG3); + regdata = ((regdata & READ_LDRO_AND_X) >> 3); + } + else + { + regdata = readRegister(REG_MODEMCONFIG1); + regdata = (regdata & READ_LDRO_AND_2); + } + + return regdata; +} + + +uint8_t SX127XLT::getSyncWord() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getSyncWord() ")); +#endif + + return readRegister(REG_SYNCWORD); +} + + +uint8_t SX127XLT::getInvertIQ() +{ + //IQ mode reg 0x33 + +#ifdef SX127XDEBUG1 + Serial.println(F("getInvertIQ() ")); +#endif + + uint8_t regdata; + regdata = ( (readRegister(REG_INVERTIQ)) & 0x40 ); + return regdata; +} + + +uint8_t SX127XLT::getVersion() +{ + //IQ mode reg 0x33 + +#ifdef SX127XDEBUG1 + Serial.println(F("getVersion() ")); +#endif + + return readRegister(REG_VERSION); +} + + +uint16_t SX127XLT::getPreamble() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getPreamble() ")); +#endif + + uint16_t regdata; + regdata = ( (readRegister(REG_PREAMBLEMSB) << 8) + readRegister(REG_PREAMBLELSB) ); + return regdata; +} + + +uint32_t SX127XLT::returnBandwidth(byte BWregvalue) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("returnBandwidth() ")); +#endif + + if (_Device == DEVICE_SX1272) + { + switch (BWregvalue) + { + case 0: + return 125000; + + case 64: + return 250000; + + case 128: + return 500000; + + default: + return 0xFF; //so that a bandwidth invalid entry can be identified ? + } + } + else + { + + switch (BWregvalue) + { + case 0: + return 7800; + + case 16: + return 10400; + + case 32: + return 15600; + + case 48: + return 20800; + + case 64: + return 31200; + + case 80: + return 41700; + + case 96: + return 62500; + + case 112: + return 125000; + + case 128: + return 250000; + + case 144: + return 500000; + + default: + return 0xFF; //so that a bandwidth invalid entry can be identified ? + } + } +} + + +uint8_t SX127XLT::returnOptimisation(uint8_t Bandwidth, uint8_t SpreadingFactor) +{ + //from the passed bandwidth (bandwidth) and spreading factor this routine + //calculates whether low data rate optimisation should be on or off + +#ifdef SX127XDEBUG1 + Serial.println(F("returnOptimisation() ")); +#endif + + uint32_t tempBandwidth; + float symbolTime; + tempBandwidth = returnBandwidth(Bandwidth); + symbolTime = calcSymbolTime(tempBandwidth, SpreadingFactor); + +#ifdef SX127XDEBUG1 + Serial.print(F("Symbol Time ")); + Serial.print(symbolTime, 3); + Serial.println(F("mS")); +#endif + + if (symbolTime > 16) + { +#ifdef SX127XDEBUG1 + Serial.println(F("LDR Opt on")); +#endif + return LDRO_ON; + } + else + { +#ifdef SX127XDEBUG1 + Serial.println(F("LDR Opt off")); +#endif + return LDRO_OFF; + } +} + + +float SX127XLT::calcSymbolTime(float Bandwidth, uint8_t SpreadingFactor) +{ + //calculates symbol time from passed bandwidth (lbandwidth) and Spreading factor (lSF)and returns in mS + +#ifdef SX127XDEBUG1 + Serial.println(F("calcSymbolTime() ")); +#endif + + float symbolTimemS; + symbolTimemS = (Bandwidth / pow(2, SpreadingFactor)); + symbolTimemS = (1000 / symbolTimemS); + return symbolTimemS; +} + + +void SX127XLT::printModemSettings() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printModemSettings()")); +#endif + + uint8_t regdata; + + printDevice(); + Serial.print(F(",")); + Serial.print(getFreqInt()); + Serial.print(F("hz,SF")); + Serial.print(getLoRaSF()); + Serial.print(F(",BW")); + + + if (_Device == DEVICE_SX1272) + { + regdata = (readRegister(REG_MODEMCONFIG1) & READ_BW_AND_2); + } + else + { + regdata = (readRegister(REG_MODEMCONFIG1) & READ_BW_AND_X); + } + + Serial.print(returnBandwidth(regdata)); + Serial.print(F(",CR4:")); + Serial.print(getLoRaCodingRate()); + Serial.print(F(",LDRO_")); + + if (getOptimisation()) + { + Serial.print(F("On")); + } + else + { + Serial.print(F("Off")); + } + + Serial.print(F(",SyncWord_0x")); + Serial.print(getSyncWord(), HEX); + if (getInvertIQ() == LORA_IQ_INVERTED) + { + Serial.print(F(",IQInverted")); + } + else + { + Serial.print(F(",IQNormal")); + } + Serial.print(F(",Preamble_")); + Serial.print(getPreamble()); +} + + +void SX127XLT::setSyncWord(uint8_t syncword) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setSyncWord() ")); +#endif + + writeRegister(REG_SYNCWORD, syncword); +} + + +uint8_t SX127XLT::receiveSXBuffer(uint8_t startaddr, uint32_t rxtimeout, uint8_t wait ) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("receiveSXBuffer()")); +#endif + + uint32_t startmS; + + setMode(MODE_STDBY_RC); + writeRegister(REG_FIFORXBASEADDR, startaddr); //set start address of RX packet in buffer + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_HEADER_VALID), 0, 0); //set for IRQ on RX done + setRx(0); //no actual RX timeout in this function + + if (!wait) + { + return 0; + } + + if (rxtimeout == 0) + { + while (!digitalRead(_RXDonePin)); //Wait for DIO0 to go high, no timeout, RX DONE + } + else + { + startmS = millis(); + while (!digitalRead(_RXDonePin) && ((uint32_t) (millis() - startmS) < rxtimeout)); + } + + setMode(MODE_STDBY_RC); //ensure to stop further packet reception + + if (!digitalRead(_RXDonePin)) //check if not DIO still low, is so must be RX timeout + { + _IRQmsb = IRQ_RX_TIMEOUT; + return 0; + } + + if ( readIrqStatus() != (IRQ_RX_DONE + IRQ_HEADER_VALID) ) + { + return 0; //no RX done and header valid only, could be CRC error + } + + _RXPacketL = readRegister(REG_RXNBBYTES); + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + + +uint8_t SX127XLT::transmitSXBuffer(uint8_t startaddr, uint8_t length, uint32_t txtimeout, int8_t txpower, uint8_t wait) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("transmitSXBuffer() ")); +#endif + + uint32_t startmS; + + setMode(MODE_STDBY_RC); + + writeRegister(REG_FIFOTXBASEADDR, startaddr); //set start address of packet in buffer + writeRegister(REG_PAYLOADLENGTH, length); + + setTxParams(txpower, RADIO_RAMP_DEFAULT); //TX power and ramp time + + setDioIrqParams(IRQ_RADIO_ALL, IRQ_TX_DONE, 0, 0); //set for IRQ on TX done + setTx(0); //TX timeout is not handled in setTX() + + if (!wait) + { + return length; + } + + if (txtimeout == 0) + { + while (!digitalRead(_TXDonePin)); //Wait for DIO0 to go high, TX finished + } + else + { + startmS = millis(); + while (!digitalRead(_TXDonePin) && ((uint32_t) (millis() - startmS) < txtimeout)); + } + + setMode(MODE_STDBY_RC); //ensure we leave function with TX off + + + if (!digitalRead(_TXDonePin)) //if _TXDonePin still high then TX timeout + { + _IRQmsb = IRQ_TX_TIMEOUT; + + return 0; + } + + return length; //no timeout, so TXdone must have been set +} + + + +void SX127XLT::printSXBufferHEX(uint8_t start, uint8_t end) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printSXBufferHEX() ")); +#endif + + uint8_t index, regdata; + + setMode(MODE_STDBY_RC); + writeRegister(REG_FIFOADDRPTR, start); //set FIFO access ptr to start + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(REG_FIFO); + + for (index = start; index <= end; index++) + { + regdata = SPI.transfer(0); + printHEXByte(regdata); + Serial.print(F(" ")); + + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + +} + + +void SX127XLT::printSXBufferASCII(uint8_t start, uint8_t end) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("printSXBufferASCII() ")); +#endif + + uint8_t index, regdata; + setMode(MODE_STDBY_RC); + + writeRegister(REG_FIFOADDRPTR, start); //and save in FIFO access ptr + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(REG_FIFO); + + for (index = start; index <= end; index++) + { + regdata = SPI.transfer(0); + Serial.write(regdata); + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif +} + + +void SX127XLT::fillSXBuffer(uint8_t startaddress, uint8_t size, uint8_t character) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("fillSXBuffer() ")); +#endif + uint8_t index; + + setMode(MODE_STDBY_RC); + writeRegister(REG_FIFOADDRPTR, startaddress); //and save in FIFO access ptr + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst write + SPI.transfer(WREG_FIFO); + + for (index = 0; index < size; index++) + { + SPI.transfer(character); + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif +} + + +uint8_t SX127XLT::getByteSXBuffer(uint8_t addr) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getByteSXBuffer() ")); +#endif + + uint8_t regdata; + setMode(MODE_STDBY_RC); //this is needed to ensure we can read from buffer OK. + + writeRegister(REG_FIFOADDRPTR, addr); //set FIFO access ptr to location + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(REG_FIFO); + regdata = SPI.transfer(0); + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return regdata; +} + + +void SX127XLT::writeByteSXBuffer(uint8_t addr, uint8_t regdata) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeByteSXBuffer() ")); +#endif + + setMode(MODE_STDBY_RC); //this is needed to ensure we can write to buffer OK. + + writeRegister(REG_FIFOADDRPTR, addr); //set FIFO access ptr to location + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(WREG_FIFO); + SPI.transfer(regdata); + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif +} + + +void SX127XLT::startWriteSXBuffer(uint8_t ptr) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("startWriteSXBuffer() ")); +#endif + + setMode(MODE_STDBY_RC); + + _TXPacketL = 0; //this variable used to keep track of bytes written + writeRegister(REG_FIFOADDRPTR, ptr); //set buffer access ptr + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(WREG_FIFO); +} + + +uint8_t SX127XLT::endWriteSXBuffer() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("endWriteSXBuffer() ")); +#endif + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _TXPacketL; +} + + +void SX127XLT::startReadSXBuffer(uint8_t ptr) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("startReadSXBuffer() ")); +#endif + + setMode(MODE_STDBY_RC); + _RXPacketL = 0; + writeRegister(REG_FIFOADDRPTR, ptr); //set buffer access ptr + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(REG_FIFO); + + //next line would be data = SPI.transfer(0); + //SPI interface ready for byte to read from +} + + +uint8_t SX127XLT::endReadSXBuffer() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("endReadSXBuffer() ")); +#endif + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; +} + + +void SX127XLT::writeUint8(uint8_t x) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeUint8() ")); +#endif + + SPI.transfer(x); + + _TXPacketL++; //increment count of bytes written +} + + +uint8_t SX127XLT::readUint8() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readUint8() ")); +#endif + + uint8_t x; + + x = SPI.transfer(0); + + _RXPacketL++; //increment count of bytes read + return (x); +} + + +void SX127XLT::writeInt8(int8_t x) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeInt8() ")); +#endif + + SPI.transfer(x); + + _TXPacketL++; //increment count of bytes written +} + + +int8_t SX127XLT::readInt8() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readInt8() ")); +#endif + + int8_t x; + + x = SPI.transfer(0); + + _RXPacketL++; //increment count of bytes read + return (x); +} + + +void SX127XLT::writeChar(char x) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeChar() ")); +#endif + + SPI.transfer(x); + + _TXPacketL++; //increment count of bytes written +} + + +char SX127XLT::readChar() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readChar() ")); +#endif + + char x; + + x = SPI.transfer(0); + + _RXPacketL++; //increment count of bytes read + return (x); +} + + +void SX127XLT::writeUint16(uint16_t x) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeUint16() ")); +#endif + + SPI.transfer(lowByte(x)); + SPI.transfer(highByte(x)); + + _TXPacketL = _TXPacketL + 2; //increment count of bytes written +} + + +uint16_t SX127XLT::readUint16() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeUint16() ")); +#endif + + uint8_t lowbyte, highbyte; + + lowbyte = SPI.transfer(0); + highbyte = SPI.transfer(0); + + _RXPacketL = _RXPacketL + 2; //increment count of bytes read + return ((highbyte << 8) + lowbyte); +} + + +void SX127XLT::writeInt16(int16_t x) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeInt16() ")); +#endif + + SPI.transfer(lowByte(x)); + SPI.transfer(highByte(x)); + + _TXPacketL = _TXPacketL + 2; //increment count of bytes written +} + + +int16_t SX127XLT::readInt16() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readInt16() ")); +#endif + + uint8_t lowbyte, highbyte; + + lowbyte = SPI.transfer(0); + highbyte = SPI.transfer(0); + + _RXPacketL = _RXPacketL + 2; //increment count of bytes read + return ((highbyte << 8) + lowbyte); +} + + +void SX127XLT::writeUint32(uint32_t x) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeUint32() ")); +#endif + + uint8_t i, j; + + union + { + uint8_t b[4]; + uint32_t f; + } data; + data.f = x; + + for (i = 0; i < 4; i++) + { + j = data.b[i]; + SPI.transfer(j); + } + + _TXPacketL = _TXPacketL + 4; //increment count of bytes written +} + + +uint32_t SX127XLT::readUint32() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readUint32() ")); +#endif + + uint8_t i, j; + + union + { + uint8_t b[4]; + uint32_t f; + } readdata; + + for (i = 0; i < 4; i++) + { + j = SPI.transfer(0); + readdata.b[i] = j; + } + _RXPacketL = _RXPacketL + 4; //increment count of bytes read + return readdata.f; +} + + +void SX127XLT::writeInt32(int32_t x) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeInt32() ")); +#endif + + uint8_t i, j; + + union + { + uint8_t b[4]; + int32_t f; + } data; + data.f = x; + + for (i = 0; i < 4; i++) + { + j = data.b[i]; + SPI.transfer(j); + } + + _TXPacketL = _TXPacketL + 4; //increment count of bytes written +} + + +int32_t SX127XLT::readInt32() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readInt32() ")); +#endif + + uint8_t i, j; + + union + { + uint8_t b[4]; + int32_t f; + } readdata; + + for (i = 0; i < 4; i++) + { + j = SPI.transfer(0); + readdata.b[i] = j; + } + _RXPacketL = _RXPacketL + 4; //increment count of bytes read + return readdata.f; +} + + +void SX127XLT::writeFloat(float x) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeFloat() ")); +#endif + + uint8_t i, j; + + union + { + uint8_t b[4]; + float f; + } data; + data.f = x; + + for (i = 0; i < 4; i++) + { + j = data.b[i]; + SPI.transfer(j); + } + + _TXPacketL = _TXPacketL + 4; //increment count of bytes written +} + + +float SX127XLT::readFloat() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readFloat() ")); +#endif + + uint8_t i, j; + + union + { + uint8_t b[4]; + float f; + } readdata; + + for (i = 0; i < 4; i++) + { + j = SPI.transfer(0); + readdata.b[i] = j; + } + _RXPacketL = _RXPacketL + 4; //increment count of bytes read + return readdata.f; +} + + +void SX127XLT::writeBuffer(uint8_t *txbuffer, uint8_t size) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeBuffer() ")); +#endif + + uint8_t index, regdata; + + _TXPacketL = _TXPacketL + size; //these are the number of bytes that will be added + + size--; //loose one byte from size, the last byte written MUST be a 0 + + for (index = 0; index < size; index++) + { + regdata = txbuffer[index]; + SPI.transfer(regdata); + } + + SPI.transfer(0); //this ensures last byte of buffer writen really is a null (0) +} + + + +void SX127XLT::writeBufferChar(char *txbuffer, uint8_t size) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeBufferChar() ")); +#endif + + uint8_t index, regdata; + + _TXPacketL = _TXPacketL + size; //these are the number of bytes that will be added + + size--; //loose one byte from size, the last byte written MUST be a 0 + + for (index = 0; index < size; index++) + { + regdata = txbuffer[index]; + SPI.transfer(regdata); + } + + SPI.transfer(0); //this ensures last byte of buffer writen really is a null (0) +} + + + +void SX127XLT::writeBufferChar(char *txbuffer) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeBufferChar() ")); +#endif + + uint8_t index = 0, regdata; + + do + { + regdata = txbuffer[index]; //read data from txbuffer + SPI.transfer(regdata); //write to device buffer + index++; + } while (regdata != 0); //keep reading until we have reached the null (0) at the buffer end or exceeded size of buffer allowed + + _TXPacketL = _TXPacketL + index; //increment count of bytes written + + SPI.transfer(0); //this ensures last byte of buffer writen really is a null (0) +} + + + +uint8_t SX127XLT::readBuffer(uint8_t *rxbuffer) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readBuffer(")); +#endif + + uint8_t index = 0, regdata; + + do + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; //fill the rxbuffer. + index++; + } while (regdata != 0); //keep reading until we have reached the null (0) at the buffer end or exceeded size of buffer allowed + + _RXPacketL = _RXPacketL + index; //increment count of bytes read + + return index; //return the actual size of the buffer, when the null (0) detected +} + + +uint8_t SX127XLT::readBuffer(uint8_t *rxbuffer, uint8_t size) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readBuffer() ")); +#endif + + uint8_t index, regdata; + + for (index = 0; index <= size; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; //fill the rxbuffer. + } + + _RXPacketL = _RXPacketL + size; //increment count of bytes read + + return size; //return the actual size of the buffer +} + + + +uint8_t SX127XLT::readBufferChar(char *rxbuffer) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readBufferChar() ")); +#endif + + uint8_t index = 0, regdata; + + do + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; //fill the buffer. + index++; + } while (regdata != 0); //keep reading until we have reached the null (0) at the buffer end + + _RXPacketL = _RXPacketL + index; //increment count of bytes read + + return index; //return the actual size of the buffer, when the null (0) detected +} + + +void SX127XLT::rxtxInit(int8_t pinRXEN, int8_t pinTXEN) +{ + //not used on current SX127x modules + +#ifdef SX127XDEBUG1 + Serial.println(F("rxtxInit() ")); +#endif + + _RXEN = pinRXEN; + _TXEN = pinTXEN; + + pinMode(pinRXEN, OUTPUT); + digitalWrite(pinRXEN, LOW); //pins needed for RX\TX switching + pinMode(pinTXEN, OUTPUT); + digitalWrite(pinTXEN, LOW); //pins needed for RX\TX switching +} + + +void SX127XLT::rxEnable() +{ + //not used on current SX127x modules + +#ifdef SX127XDEBUG1 + Serial.println(F("rxEnable() ")); +#endif + + digitalWrite(_RXEN, HIGH); + digitalWrite(_TXEN, LOW); +} + + +void SX127XLT::txEnable() +{ + //not used on current SX127x modules + +#ifdef SX127XDEBUG1 + Serial.println(F("txEnable() ")); +#endif + + digitalWrite(_RXEN, LOW); + digitalWrite(_TXEN, HIGH); +} + + +void SX127XLT::setTXDirect() +{ + //turns on transmitter, in direct mode for FSK and audio power level is from 2 to 17 +#ifdef SX127XDEBUG1 + Serial.println(F("setTXDirect()")); +#endif + writeRegister(REG_OPMODE, 0x0B); //TX on direct mode, low frequency mode +} + + +void SX127XLT::toneFM(uint16_t frequency, uint32_t length, uint32_t deviation, float adjust, int8_t txpower) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("toneFM() ")); +#endif + + uint16_t index; + uint32_t ToneDelayus; + uint32_t registershift; + uint32_t freqreg; + uint32_t shiftedfreqregH, shiftedfreqregL; + uint32_t loopcount; + uint8_t freqregH, freqregM, freqregL; + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //set NSS low + SPI.transfer(REG_FRMSB & 0x7F); //mask address for read + freqregH = SPI.transfer(0); + freqregM = SPI.transfer(0); + freqregL = SPI.transfer(0); + digitalWrite(_NSS, HIGH); //set NSS high + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + freqreg = ( ( (uint32_t) freqregH << 16 ) | ( (uint32_t) freqregM << 8 ) | ( freqregL ) ); + + registershift = deviation / FREQ_STEP; + shiftedfreqregH = freqreg + registershift; + shiftedfreqregL = freqreg - registershift; + + uint8_t ShiftH = shiftedfreqregH >> 16; + uint8_t ShiftM = shiftedfreqregH >> 8; + uint8_t ShiftL = shiftedfreqregH; + uint8_t NoShiftH = shiftedfreqregL >> 16; + uint8_t NoShiftM = shiftedfreqregL >> 8; + uint8_t NoShiftL = shiftedfreqregL; + + ToneDelayus = ((500000 / frequency)); + loopcount = (length * 500) / (ToneDelayus); + ToneDelayus = ToneDelayus * adjust; + +#ifdef SX127XDEBUG3 + Serial.print(F("frequency ")); + Serial.println(frequency); + Serial.print(F("length ")); + Serial.println(length); + + Serial.print(F("freqreg ")); + Serial.println(freqreg, HEX); + Serial.print(F("registershift ")); + Serial.println(registershift); + shiftedfreqregH = freqreg + (registershift / 2); + shiftedfreqregL = freqreg - (registershift / 2); + Serial.print(F("shiftedfreqregH ")); + Serial.println(shiftedfreqregH, HEX); + Serial.print(F("shiftedfreqregL ")); + Serial.println(shiftedfreqregL, HEX); + + Serial.print(F("ShiftedHigh,")); + Serial.print(ShiftH, HEX); + Serial.print(F(",")); + Serial.print(ShiftM, HEX); + Serial.print(F(",")); + Serial.println(ShiftL, HEX); + + Serial.print(F("ShiftedLow,")); + Serial.print(NoShiftH, HEX); + Serial.print(F(",")); + Serial.print(NoShiftM, HEX); + Serial.print(F(",")); + Serial.println(NoShiftL, HEX); + Serial.print(F("ToneDelayus,")); + Serial.println(ToneDelayus); + Serial.print(F("loopcount,")); + Serial.println(loopcount); + Serial.println(); + Serial.println(); +#endif + + writeRegister(REG_PLLHOP, 0xAD); //set fast hop mode, needed for fast changes of frequency + + setTxParams(txpower, RADIO_RAMP_DEFAULT); + setTXDirect(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + for (index = 1; index <= loopcount; index++) + { + digitalWrite(_NSS, LOW); //set NSS low + SPI.transfer(0x86); //address for write to REG_FRMSB + SPI.transfer(ShiftH); + SPI.transfer(ShiftM); + SPI.transfer(ShiftL); + digitalWrite(_NSS, HIGH); //set NSS high + + delayMicroseconds(ToneDelayus); + + digitalWrite(_NSS, LOW); //set NSS low + SPI.transfer(0x86); //address for write to REG_FRMSB + SPI.transfer(NoShiftH); + SPI.transfer(NoShiftM); + SPI.transfer(NoShiftL); + digitalWrite(_NSS, HIGH); //set NSS high + + delayMicroseconds(ToneDelayus); + } + //now set the frequency registers back to centre + digitalWrite(_NSS, LOW); //set NSS low + SPI.transfer(0x86); //address for write to REG_FRMSB + SPI.transfer(freqregH); + SPI.transfer(freqregM); + SPI.transfer(freqregL); + digitalWrite(_NSS, HIGH); //set NSS high + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + writeRegister(REG_PLLHOP, 0x2D); //restore PLLHOP register value + setMode(MODE_STDBY_RC); //turns off carrier +} + + +void SX127XLT::setupDirect(uint32_t frequency, int32_t offset) +{ + //setup LoRa device for direct modulation mode +#ifdef SX127XDEBUG1 + Serial.println(F("setupDirect() ")); +#endif + _PACKET_TYPE = PACKET_TYPE_GFSK; //need to swap packet type + setMode(MODE_SLEEP); //can only swap to direct mode in sleepmode + setMode(MODE_SLEEP); //can only swap to direct mode in sleepmode + setMode(MODE_STDBY_RC); + writeRegister(REG_DETECTOPTIMIZE, 0x00); //set continuous mode + setRfFrequency(frequency, offset); //set the operating frequncy + calibrateImage(0); //run calibration after setting frequency + writeRegister(REG_FDEVLSB, 0); //We are generating a tone by frequency shift so set deviation to 0 +} + + +int8_t SX127XLT::getDeviceTemperature() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("getDeviceTemperature()")); +#endif + + int8_t temperature; + uint8_t regdata; + + regdata = readRegister(REG_IMAGECAL & 0xFE); + + writeRegister(REG_IMAGECAL, 0x00); //register back to power up default + + writeRegister(REG_OPMODE, 0x00); //goto sleep + writeRegister(REG_OPMODE, 0x00); //make sure switch to FSK mode + writeRegister(REG_OPMODE, 0x01); //go into FSK standby + delay(5); //wait for oscillator startup + writeRegister(REG_OPMODE, 0x04); //put device in FSK RX synth mode + + writeRegister(REG_IMAGECAL, 0x00); //set TempMonitorOff = 0 + delay(1); //wait at least 140uS + writeRegister(REG_IMAGECAL, 0x01); //set TempMonitorOff = 1 + setMode(MODE_STDBY_RC); //go back to standby + + writeRegister(REG_IMAGECAL, (regdata + 1)); //register back to previous setting, with TempMonitorOff set + + temperature = readRegister(REG_TEMP); + + if (temperature & 0x80 ) //The sign bit is 1 + { + temperature = ( ( ~temperature + 1 ) & 0xFF ); //Invert and divide by 4 + } + else + { + temperature = ( temperature & 0xFF ); //Divide by 4 + } + + writeRegister(REG_OPMODE, MODE_STDBY); + + return temperature; +} + + +void SX127XLT::fskCarrierOn(int8_t txpower) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("fskCarrierOn() ")); +#endif + + writeRegister(REG_PLLHOP, 0xAD); //set fast hop mode, needed for fast changes of frequency + setTxParams(txpower, RADIO_RAMP_DEFAULT); + setTXDirect(); +} + + +void SX127XLT::fskCarrierOff() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("fskCarrierOff() ")); +#endif + + setMode(MODE_STDBY_RC); //turns off carrier +} + + +void SX127XLT::setRfFrequencyDirect(uint8_t high, uint8_t mid, uint8_t low) +{ + +#ifdef SX127XDEBUG1 + Serial.println(F("setRfFrequencyDirect() ")); +#endif + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //set NSS low + SPI.transfer(0x86); //address for write to REG_FRMSB + SPI.transfer(high); + SPI.transfer(mid); + SPI.transfer(low); + digitalWrite(_NSS, HIGH); //set NSS high + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif +} + + +void SX127XLT::getRfFrequencyRegisters(uint8_t *buff) +{ + //returns the register values for the current set frequency + +#ifdef SX127XDEBUG1 + Serial.println(F("getRfFrequencyRegisters() ")); +#endif + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //set NSS low + SPI.transfer(REG_FRMSB & 0x7F); //mask address for read + buff[0] = SPI.transfer(0); //read the byte into buffer + buff[1] = SPI.transfer(0); //read the byte into buffer + buff[2] = SPI.transfer(0); //read the byte into buffer + digitalWrite(_NSS, HIGH); //set NSS high + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif +} + + +void SX127XLT::startFSKRTTY(uint32_t freqshift, uint8_t pips, uint16_t pipPeriodmS, uint16_t pipDelaymS, uint16_t leadinmS) +{ + +#ifdef SX127XDEBUG1 + Serial.println(F("startFSKRTTY() ")); +#endif + + uint8_t freqShiftRegs[3]; //to hold returned registers for shifted frequency + uint32_t setCentreFrequency; //the configured centre frequency + uint8_t index; + uint32_t startmS; + setCentreFrequency = _savedFrequency; //to avoid using the savedFrequency + + writeRegister(REG_PLLHOP, 0xAD); //set fast hop mode, needed for fast changes of frequency + + setRfFrequency((_savedFrequency + freqshift), _savedOffset); //temporaily set the RF frequency + getRfFrequencyRegisters(freqShiftRegs); //fill first 3 bytes with current frequency registers + setRfFrequency(setCentreFrequency, _savedOffset); //reset the base frequency registers + + _ShiftfreqregH = freqShiftRegs[0]; + _ShiftfreqregM = freqShiftRegs[1]; + _ShiftfreqregL = freqShiftRegs[2]; + + writeRegister(REG_PLLHOP, 0xAD); //set fast hop mode, needed for fast changes of frequency + setTxParams(10, RADIO_RAMP_DEFAULT); + + for (index = 1; index <= pips; index++) + { + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregM, _ShiftfreqregL); //set carrier frequency + setTXDirect(); //turn on carrier + delay(pipPeriodmS); + setMode(MODE_STDBY_RC); //turns off carrier + delay(pipDelaymS); + } + + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregM, _ShiftfreqregL); //set carrier frequency + startmS = millis(); + setTXDirect(); //turn on carrier + while (((uint32_t) (millis() - startmS) < leadinmS)); +} + + +void SX127XLT::transmitFSKRTTY(uint8_t chartosend, uint8_t databits, uint8_t stopbits, uint8_t parity, uint16_t baudPerioduS, int8_t pin) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("transmitFSKRTTY() ")); +#endif + + uint8_t numbits; + uint32_t startuS; + uint8_t bitcount = 0; //set when a bit is 1 + + startuS = micros(); + setRfFrequencyDirect(_freqregH, _freqregM, _freqregL); //set carrier frequency (low) + + if (pin >= 0) + { + digitalWrite(pin, LOW); + } + + while (((uint32_t) (micros() - startuS) < baudPerioduS)); + + for (numbits = 1; numbits <= databits; numbits++) //send bits, LSB first + { + + startuS = micros(); + if ((chartosend & 0x01) != 0) //test for bit set, a 1 + { + bitcount++; + if (pin >= 0) + { + digitalWrite(pin, HIGH); + } + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregM, _ShiftfreqregL); //set carrier frequency for a 1 + } + else + { + if (pin >= 0) + { + digitalWrite(pin, LOW); + } + setRfFrequencyDirect(_freqregH, _freqregM, _freqregL); //set carrier frequency for a 0 + } + chartosend = (chartosend >> 1); //get the next bit + + while (((uint32_t) (micros() - startuS) < baudPerioduS)); + } + + startuS = micros(); + switch (parity) + { + case ParityNone: + break; + + case ParityZero: + setRfFrequencyDirect(_freqregH, _freqregM, _freqregL); //set carrier frequency for a 0 + + while (((uint32_t) (micros() - startuS) < baudPerioduS)); + break; + + case ParityOne: + + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregM, _ShiftfreqregL); //set carrier frequency for a 1 + + while (((uint32_t) (micros() - startuS) < baudPerioduS)); + break; + + case ParityOdd: + if (bitRead(bitcount, 0)) //test odd bit count, i.e. when bit 0 = 1 + { + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregM, _ShiftfreqregL); //set carrier frequency for a 1 + } + else + { + setRfFrequencyDirect(_freqregH, _freqregM, _freqregL); //set carrier frequency for a 0 + } + while (((uint32_t) (micros() - startuS) < baudPerioduS)); + break; + + case ParityEven: + if (bitRead(bitcount, 0)) //test odd bit count, i.e. when bit 0 = 1 + { + setRfFrequencyDirect(_freqregH, _freqregM, _freqregL); //set carrier frequency for a 0 + } + else + { + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregM, _ShiftfreqregL); //set carrier frequency for a 1 + } + while (((uint32_t) (micros() - startuS) < baudPerioduS)); + break; + + default: + break; + } + + startuS = micros(); + + if (pin >= 0) + { + digitalWrite(pin, HIGH); + } + + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregM, _ShiftfreqregL); //set carrier frequency + + while ( (uint32_t) (micros() - startuS) < (baudPerioduS * stopbits)); +} + + +void SX127XLT::transmitFSKRTTY(uint8_t chartosend, uint16_t baudPerioduS, int8_t pin) +{ + //This overloaded version of transmitFSKRTTY() defaults to 1 start bit, 7 data bits, no parity and 2 stop bits. + +#ifdef SX127XDEBUG1 + Serial.println(F("transmitFSKRTTY() ")); +#endif + + uint8_t numbits; + uint32_t startuS; + + //startbit + + startuS = micros(); + setRfFrequencyDirect(_freqregH, _freqregM, _freqregL); //set carrier frequency (low) + + if (pin >= 0) + { + digitalWrite(pin, LOW); + } + + while (((uint32_t) (micros() - startuS) < baudPerioduS)); + + for (numbits = 1; numbits <= 7; numbits++) //send bits, LSB first + { + startuS = micros(); + if ((chartosend & 0x01) != 0) //test for bit set, a 1 + { + if (pin >= 0) + { + digitalWrite(pin, HIGH); + } + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregM, _ShiftfreqregL); //set carrier frequency for a 1 + } + else + { + if (pin >= 0) + { + digitalWrite(pin, LOW); + } + setRfFrequencyDirect(_freqregH, _freqregM, _freqregL); //set carrier frequency for a 0 + } + chartosend = (chartosend >> 1); //get the next bit + while (((uint32_t) (micros() - startuS) < baudPerioduS)); + } + + //stop bits + + startuS = micros(); + + if (pin >= 0) + { + digitalWrite(pin, HIGH); + } + + setRfFrequencyDirect(_ShiftfreqregH, _ShiftfreqregM, _ShiftfreqregL); //set carrier frequency + + while ((uint32_t) (micros() - startuS) < (baudPerioduS * 2)); +} + + +void SX127XLT::printRTTYregisters() +{ + +#ifdef SX127XDEBUG1 + Serial.println(F("printRTTYregisters() ")); +#endif + + Serial.print(F("NoShift Registers ")); + Serial.print(_freqregH, HEX); + Serial.print(F(" ")); + Serial.print(_freqregM, HEX); + Serial.print(F(" ")); + Serial.println(_freqregL, HEX); + + Serial.print(F("Shifted Registers ")); + Serial.print(_ShiftfreqregH, HEX); + Serial.print(F(" ")); + Serial.print(_ShiftfreqregM, HEX); + Serial.print(F(" ")); + Serial.println(_ShiftfreqregL, HEX); +} + + +void SX127XLT::endFSKRTTY() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("endFSKRTTY() ")); +#endif + + setMode(MODE_STDBY_RC); +} + + +void SX127XLT::doAFC() +{ + #ifdef SX127XDEBUG1 + Serial.println(F("doAFC() ")); + #endif + + _savedOffset = _savedOffset - getFrequencyErrorHz(); + setRfFrequency(_savedFrequency, _savedOffset); //adjust the operating frequency for AFC +} + + +void SX127XLT::doAFCPPM() +{ + #ifdef SX127XDEBUG1 + Serial.println(F("doAFCPPM() ")); + #endif + + int32_t frequencyerror; + float Ferr_ppm; //ppm error + int8_t PPM_offset; + + frequencyerror = getFrequencyErrorHz(); + _savedOffset = _savedOffset - frequencyerror; + setRfFrequency(_savedFrequency, _savedOffset); //adjust the operating frequency for AFC + + //now deal with the PPM correction + Ferr_ppm = frequencyerror / (_savedFrequency/1E6); + PPM_offset = 0.95 * Ferr_ppm; + writeRegister(REG_PPMCORRECTION,PPM_offset); //write PPM adjust value to device register +} + + +uint8_t SX127XLT::getPPM() +{ + #ifdef SX127XDEBUG1 + Serial.println(F("getPPM() ")); + #endif + + return readRegister(REG_PPMCORRECTION); +} + + +int32_t SX127XLT::getOffset() +{ + #ifdef SX127XDEBUG1 + Serial.println(F("getOffset() ")); + #endif + return _savedOffset; +} + + +uint8_t SX127XLT::readBufferbytes(uint8_t *buffer, uint8_t size) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("readBufferbytes() ")); +#endif + + uint8_t ptr = 0, regdata, index; + + for (index = 1; index <= size; index++) + { + regdata = SPI.transfer(0); + buffer[ptr] = regdata; //fill the buffer. + ptr++; + } + + _RXPacketL = _RXPacketL + size; //increment count of bytes read and written to buffer + + return index; //return the actual size of the buffer +} + + +uint8_t SX127XLT::writeBufferbytes(uint8_t *buffer, uint8_t size) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeBufferbytes() ")); +#endif + + uint8_t regdata, index; + + for (index = 0; index < size; index++) + { + regdata = buffer[index]; + SPI.transfer(regdata); + } + + _TXPacketL = _TXPacketL + size; //increment count of bytes read and written to buffer + + return index; //return the actual size of the buffer +} + + + +void SX127XLT::setDevicePABOOST() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("writeBufferbytes() ")); +#endif + +bitSet(_Device, 4); //set bit 4 of _Device type which defines device as using PABoost RF output +} + + +void SX127XLT::setDeviceRFO() +{ +#ifdef SX127XDEBUG1 + Serial.println(F("setDeviceRFO() ")); +#endif + +bitClear(_Device, 4); //clear bit 4 of _Device type which defines device as using RFO RF output +} + + + + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ diff --git a/lib/SX12XX-LoRa/src/SX127XLT.h b/lib/SX12XX-LoRa/src/SX127XLT.h new file mode 100644 index 0000000..e42a0ee --- /dev/null +++ b/lib/SX12XX-LoRa/src/SX127XLT.h @@ -0,0 +1,263 @@ +/* + Copyright 2020 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + Original published 17/12/19 + New version 23/12/20 +*/ + + +#ifndef SX127XLT_h +#define SX127XLT_h + +#include +#include + + +class SX127XLT +{ + + public: + + SX127XLT(); + + bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinDIO0, int8_t pinDIO1, int8_t pinDIO2, uint8_t device); + bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinDIO0, uint8_t device); + bool begin(int8_t pinNSS, uint8_t device); + void resetDevice(); + void setMode(uint8_t modeconfig); + void setSleep(uint8_t sleepconfig); + bool checkDevice(); + void wake(); + void calibrateImage(uint8_t null); + uint16_t CRCCCITT(uint8_t *buffer, uint16_t size, uint16_t startvalue); + uint16_t CRCCCITTSX(uint8_t startadd, uint8_t endadd, uint16_t startvalue); + + void setDevice(uint8_t type); + void printDevice(); + uint8_t getOperatingMode(); + bool isReceiveDone(); + bool isTransmitDone(); + + void writeRegister( uint8_t address, uint8_t value ); + uint8_t readRegister( uint8_t address ); + void printRegisters(uint16_t start, uint16_t end); + void printRegister(uint8_t reg); + void printOperatingMode(); + void printOperatingSettings(); + + void setTxParams(int8_t txPower, uint8_t rampTime); + void setPacketParams(uint16_t packetParam1, uint8_t packetParam2, uint8_t packetParam3, uint8_t packetParam4, uint8_t packetParam5); + void setModulationParams(uint8_t modParam1, uint8_t modParam2, uint8_t modParam3, uint8_t modParam4); + void setRfFrequency(uint64_t freq64, int32_t offset); + uint32_t getFreqInt(); + int32_t getFrequencyErrorRegValue(); + int32_t getFrequencyErrorHz(); + + void setTx(uint32_t timeout); + void setRx(uint32_t timeout); + bool readTXIRQ(); + bool readRXIRQ(); + + + void setLowPowerReceive(); + void setHighSensitivity(); + void setRXGain(uint8_t config); + + uint8_t getAGC(); + uint8_t getLNAgain(); + uint8_t getCRCMode(); + uint8_t getHeaderMode(); + uint8_t getLNAboostHF(); + uint8_t getLNAboostLF(); + uint8_t getOpmode(); + uint8_t getPacketMode(); + + uint8_t readRXPacketL(); + uint8_t readTXPacketL(); + int16_t readPacketRSSI(); + int16_t readCurrentRSSI(); + int8_t readPacketSNR(); + bool readPacketCRCError(); + bool readPacketHeaderValid(); + uint8_t packetOK(); + uint8_t readRXPacketType(); + uint8_t readRXDestination(); + uint8_t readRXSource(); + + void setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress); + void setPacketType(uint8_t PacketType); + + void clearIrqStatus( uint16_t irqMask ); + uint16_t readIrqStatus(); + void setDioIrqParams(uint16_t irqMask, uint16_t dio0Mask, uint16_t dio1Mask, uint16_t dio2Mask ); + void printIrqStatus(); + + + void printASCIIPacket(uint8_t *buff, uint8_t tsize); + void printHEXPacket(uint8_t *buff, uint8_t tsize); + void printASCIIorHEX(uint8_t temp); + void printHEXByte(uint8_t temp); + void printHEXByte0x(uint8_t temp); + + bool isRXdone(); + bool isTXdone(); + bool isRXdoneIRQ(); + bool isTXdoneIRQ(); + void setTXDonePin(uint8_t pin); + void setRXDonePin(uint8_t pin); + + //******************************************************************************* + //Packet Read and Write Routines + //******************************************************************************* + + uint8_t receive(uint8_t *rxbuffer, uint8_t size, uint32_t rxtimeout, uint8_t wait); + uint8_t receiveAddressed(uint8_t *rxbuffer, uint8_t size, uint32_t rxtimeout, uint8_t wait); + uint8_t readPacket(uint8_t *rxbuffer, uint8_t size); + uint8_t readPacketAddressed(uint8_t *rxbuffer, uint8_t size); + + uint8_t transmit(uint8_t *txbuffer, uint8_t size, uint32_t txtimeout, int8_t txPower, uint8_t wait); + uint8_t transmitAddressed(uint8_t *txbuffer, uint8_t size, char txpackettype, char txdestination, char txsource, uint32_t txtimeout, int8_t txpower, uint8_t wait); + + //******************************************************************************* + //LoRa specific routines + //******************************************************************************* + + void setupLoRa(uint32_t Frequency, int32_t Offset, uint8_t modParam1, uint8_t modParam2, uint8_t modParam3, uint8_t modParam4); + + uint8_t getLoRaSF(); + uint8_t getLoRaCodingRate(); + uint8_t getOptimisation(); + uint8_t getSyncWord(); + uint8_t getInvertIQ(); + uint8_t getVersion(); + uint16_t getPreamble(); + + uint32_t returnBandwidth(uint8_t BWregvalue); + uint8_t returnOptimisation(uint8_t SpreadingFactor, uint8_t Bandwidth); + float calcSymbolTime(float Bandwidth, uint8_t SpreadingFactor); + void printModemSettings(); + void setSyncWord(uint8_t syncword); + void setTXDirect(); + void setupDirect(uint32_t frequency, int32_t offset); + void toneFM(uint16_t frequency, uint32_t length, uint32_t deviation, float adjust, int8_t txpower); + int8_t getDeviceTemperature(); + void fskCarrierOn(int8_t txpower); + void fskCarrierOff(); + void setRfFrequencyDirect(uint8_t high, uint8_t mid, uint8_t low); + void getRfFrequencyRegisters(uint8_t *buff); + void startFSKRTTY(uint32_t freqshift, uint8_t pips, uint16_t pipDelaymS, uint16_t pipPeriodmS, uint16_t leadinmS); + void transmitFSKRTTY(uint8_t chartosend, uint8_t databits, uint8_t stopbits, uint8_t parity, uint16_t baudPerioduS, int8_t pin); + void transmitFSKRTTY(uint8_t chartosend, uint16_t baudPerioduS, int8_t pin); + void printRTTYregisters(); + void endFSKRTTY(); + void doAFC(); + void doAFCPPM(); + uint8_t getPPM(); + int32_t getOffset(); + void printOCPTRIM(); + uint8_t readBufferbytes(uint8_t *rxbuffer, uint8_t size); + uint8_t writeBufferbytes(uint8_t *txbuffer, uint8_t size); + void setDevicePABOOST(); + void setDeviceRFO(); + + //******************************************************************************* + //Read Write SX12xxx Buffer commands, this is the buffer internal to the SX12xxxx + //******************************************************************************* + + uint8_t receiveSXBuffer(uint8_t startaddr, uint32_t rxtimeout, uint8_t wait); + uint8_t transmitSXBuffer(uint8_t startaddr, uint8_t length, uint32_t txtimeout, int8_t txpower, uint8_t wait); + + void printSXBufferHEX(uint8_t start, uint8_t end); + void printSXBufferASCII(uint8_t start, uint8_t end); + void fillSXBuffer(uint8_t startaddress, uint8_t size, uint8_t character); + uint8_t getByteSXBuffer(uint8_t addr); + void writeByteSXBuffer(uint8_t addr, uint8_t regdata); + + void startWriteSXBuffer(uint8_t ptr); + uint8_t endWriteSXBuffer(); + void startReadSXBuffer(uint8_t ptr); + uint8_t endReadSXBuffer(); + + void writeUint8(uint8_t x); + uint8_t readUint8(); + + void writeInt8(int8_t x); + int8_t readInt8(); + + void writeChar(char x); + char readChar(); + + void writeUint16(uint16_t x); + uint16_t readUint16(); + + void writeInt16(int16_t x); + int16_t readInt16(); + + void writeUint32(uint32_t x); + uint32_t readUint32(); + + void writeInt32(int32_t x); + int32_t readInt32(); + + void writeFloat(float x); + float readFloat(); + + void writeBuffer(uint8_t *txbuffer, uint8_t size); + void writeBufferChar(char *txbuffer, uint8_t size); + void writeBufferChar(char *txbuffer); + uint8_t readBuffer(uint8_t *rxbuffer); + uint8_t readBuffer(uint8_t *rxbuffer, uint8_t size); + uint8_t readBufferChar(char *rxbuffer); + + //******************************************************************************* + //RX\TX Enable routines - Not yet tested as of 02/12/19 + //******************************************************************************* + + void rxtxInit(int8_t pinRXEN, int8_t pinTXEN); //not used on current SX127x modules + void rxEnable(); //not used on current SX127x modules + void txEnable(); //not used on current SX127x modules + + //******************************************************************************* + //Library variables + //******************************************************************************* + + private: + + int8_t _NSS, _NRESET, _DIO0, _DIO1, _DIO2; + uint8_t _RXPacketL; //length of packet received + uint8_t _RXPacketType; //type number of received packet + uint8_t _RXDestination; //destination address of received packet + uint8_t _RXSource; //source address of received packet + uint8_t _TXPacketL; //length of transmitted packet + uint16_t _IRQmsb; //for setting additional flags + uint8_t _Device; //saved device type + int8_t _TXDonePin; //the pin that will indicate TX done + int8_t _RXDonePin; //the pin that will indicate RX done + uint8_t _UseCRC; //when packet parameters are set this flag is set if CRC on packets in use + int8_t _RXEN, _TXEN; //for modules that have RX TX pin switching + uint8_t _PACKET_TYPE; //used to save the set packet type + uint8_t _freqregH, _freqregM, _freqregL; //the registers values for the set frequency + uint8_t _ShiftfreqregH, _ShiftfreqregM, _ShiftfreqregL; //register values for shifted frequency, used in FSK RTTY etc + uint32_t _savedFrequency; //when setRfFrequency() is used the set frequency is saved + int32_t _savedOffset; //when setRfFrequency() is used the set offset is saved +}; +#endif + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ diff --git a/lib/SX12XX-LoRa/src/SX127XLT_Definitions.h b/lib/SX12XX-LoRa/src/SX127XLT_Definitions.h new file mode 100644 index 0000000..24720b0 --- /dev/null +++ b/lib/SX12XX-LoRa/src/SX127XLT_Definitions.h @@ -0,0 +1,314 @@ +/* + Copyright 2019 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + Original published 17/12/19 +*/ + +#define LORA_MAC_PRIVATE_SYNCWORD 0x12 +#define LORA_MAC_PUBLIC_SYNCWORD 0x34 + +//radio operatine modes - see RegOpMode in register map +#define MODE_SLEEP 0x00 +#define MODE_STDBY 0x01 +#define MODE_STDBY_RC 0x01 + +#define MODE_FSTX 0x02 //Frequency synthesis TX mode +#define MODE_TX 0x03 //TX mode +#define MODE_FSRX 0x04 //Frequency synthesis RX mode +#define MODE_RXCONTINUOUS 0x05 //RX continuous mode +#define MODE_RXSINGLE 0x06 //RX single mode + +#define MODE_CAD 0xFF //RX CAD mode + +#define POWERSAVE 0xC0 //select minimum LNA gain +#define BOOSTED 0x38 //mode for booted, max LNA gain + +#define LNAGAING1 0x20 //maximum LNA gain +#define LNAGAING2 0x40 +#define LNAGAING3 0x60 +#define LNAGAING4 0x80 +#define LNAGAING5 0xA0 +#define LNAGAING6 0xC0 //minimum LNA gain + +#define PACKET_TYPE_GFSK 0x00 //regopmode setting for FSK and direct mode +#define PACKET_TYPE_LORA 0x80 //regopmode setting for LoRa +#define PACKET_TYPE_NONE 0x0F + + +#define RADIO_RAMP_10_US 0x0F +#define RADIO_RAMP_12_US 0x0E +#define RADIO_RAMP_15_US 0x0D +#define RADIO_RAMP_20_US 0x0C +#define RADIO_RAMP_25_US 0x0B +#define RADIO_RAMP_31_US 0x0A +#define RADIO_RAMP_40_US 0x09 +#define RADIO_RAMP_50_US 0x08 +#define RADIO_RAMP_62_US 0x07 +#define RADIO_RAMP_100_US 0x06 +#define RADIO_RAMP_125_US 0x05 +#define RADIO_RAMP_250_US 0x04 +#define RADIO_RAMP_500_US 0x03 +#define RADIO_RAMP_1000_US 0x02 +#define RADIO_RAMP_2000_US 0x01 +#define RADIO_RAMP_3400_US 0x00 +#define RADIO_RAMP_DEFAULT 0x09 + +#define OCP_TRIM_ON 0x20 +#define OCP_TRIM_OFF 0x00 +#define OCP_TRIM_45MA 0x00 +#define OCP_TRIM_80MA 0x07 +#define OCP_TRIM_100MA 0x0B +#define OCP_TRIM_110MA 0x0D +#define OCP_TRIM_120MA 0x0F +#define OCP_TRIM_130MA 0x10 +#define OCP_TRIM_140MA 0x11 +#define OCP_TRIM_150MA 0x12 + +//Constant names for bandwidth settings +#define LORA_BW_500 144 //actual 500000hz +#define LORA_BW_250 128 //actual 250000hz +#define LORA_BW_125 112 //actual 125000hz +#define LORA_BW_062 96 //actual 62500hz +#define LORA_BW_041 80 //actual 41670hz +#define LORA_BW_031 64 //actual 31250hz +#define LORA_BW_020 48 //actual 20830hz +#define LORA_BW_015 32 //actual 15630hz +#define LORA_BW_010 16 //actual 10420hz +#define LORA_BW_007 0 //actual 7810hz + + +//for SX127x +#define LORA_SF6 0x06 +#define LORA_SF7 0x07 +#define LORA_SF8 0x08 +#define LORA_SF9 0x09 +#define LORA_SF10 0x0A +#define LORA_SF11 0x0B +#define LORA_SF12 0x0C + + +#define LORA_CR_4_5 0x02 +#define LORA_CR_4_6 0x04 +#define LORA_CR_4_7 0x06 +#define LORA_CR_4_8 0x08 + +#define LDRO_OFF 0x00 +#define LDRO_ON 0x01 +#define LDRO_AUTO 0x02 //when set causes LDRO to be automatically calculated + +#define WAIT_RX 0x01 +#define WAIT_TX 0x01 +#define NO_WAIT 0x00 + +#define FREQ_STEP 61.03515625 + +//These are the &/AND values for reading a parameter from a register. +//For example the Bandwidth on a SX1278 is stored in bits 7-4 and those bits are +//in effect numbers from 0 to 9, where 0 = 7800hz and 9 = 500000hz. Thus to read +//the bandwidth value you need to &/AND the contents of the register with a value +//The registers and values are different for SX1272 and the rest, so they are identified +//by _2 and _X respectivly. To read all the bits of a register, appart from the setting +//use the &/AND value with all bits inverted. + +#define READ_BW_AND_2 0xC0 //register 0x1D +#define READ_BW_AND_X 0xF0 //register 0x1D + +#define READ_CR_AND_2 0x38 //register 0x1D +#define READ_CR_AND_X 0x0E //register 0x1D + +#define READ_SF_AND_2 0xF0 //register 0x1E +#define READ_SF_AND_X 0xF0 //register 0x1E + +#define READ_HASCRC_AND_2 0x02 //register 0x1D +#define READ_HASCRC_AND_X 0x04 //register 0x1E + +#define READ_LDRO_AND_2 0x01 //register 0x1D +#define READ_LDRO_AND_X 0x08 //register 0x26 + +#define READ_AGCAUTO_AND_2 0x04 //register 0x1E +#define READ_AGCAUTO_AND_X 0x04 //register 0x26 + +#define READ_IMPLCIT_AND_2 0x04 //register 0x1D +#define READ_IMPLCIT_AND_X 0x01 //register 0x1D + +#define READ_LNAGAIN_AND_2 0xE0 //register 0x0C +#define READ_LNAGAIN_AND_X 0xE0 //register 0x0C + +#define READ_LNABOOSTHF_AND_2 0x03 //register 0x0C +#define READ_LNABOOSTHF_AND_X 0x03 //register 0x0C + +#define READ_LNABOOSTLF_AND_2 0x18 //register 0x0C +#define READ_LNABOOSTLF_AND_X 0x18 //register 0x0C + +#define READ_OPMODE_AND_2 0x18 //register 0x01 +#define READ_OPMODE_AND_X 0x18 //register 0x01 + +#define READ_RANGEMODE_AND_2 0x80 //register 0x01 +#define READ_RANGEMODE_AND_X 0x80 //register 0x01 + + + +#define LORA_PACKET_VARIABLE_LENGTH 0x00 +#define LORA_PACKET_FIXED_LENGTH 0x01 +#define LORA_PACKET_EXPLICIT LORA_PACKET_VARIABLE_LENGTH +#define LORA_PACKET_IMPLICIT LORA_PACKET_FIXED_LENGTH + +//for SX127x +#define LORA_CRC_ON 0x01 //Packet CRC is activated +#define LORA_CRC_OFF 0x00 //Packet CRC not used + +#define LORA_IQ_NORMAL 0x00 +#define LORA_IQ_INVERTED 0x40 + + +//For SX127x - mapping of these IRQs to the SX126x and SX128x style is not easy +//the values have been fixed to these following DIOs. For instance IRQ_RX_DONE, +//IRQ_TX_DONE and IRQ_CAD_DONE can only be mapped to DIO0. +//For most applications only DIO0, DIO1 and DIO2 are connected. + +#define IRQ_RADIO_NONE 0x00 +#define IRQ_CAD_ACTIVITY_DETECTED 0x01 //active on DIO1 +#define IRQ_FSHS_CHANGE_CHANNEL 0x02 //active on DIO2 +#define IRQ_CAD_DONE 0x04 //active on DIO0 +#define IRQ_TX_DONE 0x08 //active on DIO0 +#define IRQ_HEADER_VALID 0x10 //read from IRQ register only +#define IRQ_CRC_ERROR 0x20 //read from IRQ register only +#define IRQ_RX_DONE 0x40 //active on DIO0 +#define IRQ_RADIO_ALL 0xFFFF + +#define IRQ_TX_TIMEOUT 0x0100 //so that readIrqstatus can return additional detections +#define IRQ_RX_TIMEOUT 0x0200 //so that readIrqstatus can return additional detections +#define IRQ_NO_PACKET_CRC 0x0400 //so that readIrqstatus can return additional detections + + +#define CONFIGURATION_RETENTION 0x04 //these have no effect in SX127x, kept for compatibility +#define RTC_TIMEOUT_ENABLE 0x01 + + +//SX127x Register names +const uint8_t REG_FIFO = 0x00; +const uint8_t WREG_FIFO = 0x80; //this is the write address for the FIFO +const uint8_t REG_OPMODE = 0x01; +const uint8_t REG_FDEVLSB = 0x05; +const uint8_t REG_FRMSB = 0x06; +const uint8_t REG_FRMID = 0x07; +const uint8_t REG_FRLSB = 0x08; +const uint8_t REG_PACONFIG = 0x09; +const uint8_t REG_PARAMP = 0x0A; +const uint8_t REG_OCP = 0x0B; +const uint8_t REG_LNA = 0x0C; +const uint8_t REG_FIFOADDRPTR = 0x0D; +const uint8_t REG_FIFOTXBASEADDR = 0x0E; +const uint8_t REG_FIFORXBASEADDR = 0x0F; +const uint8_t REG_FIFORXCURRENTADDR = 0x10; +const uint8_t REG_IRQFLAGSMASK = 0x11; +const uint8_t REG_IRQFLAGS = 0x12; +const uint8_t REG_RXNBBYTES = 0x13; +const uint8_t REG_RXHEADERCNTVALUEMSB = 0x14; +const uint8_t REG_RXHEADERCNTVALUELSB = 0x15; +const uint8_t REG_RXPACKETCNTVALUEMSB = 0x16; +const uint8_t REG_RXPACKETCNTVALUELSB = 0x17; +const uint8_t REG_PKTSNRVALUE = 0x19; +const uint8_t REG_PKTRSSIVALUE = 0x1A; +const uint8_t REG_RSSIVALUE = 0x1B; +const uint8_t REG_CURRENTRSSIVALUE = 0x1B; +const uint8_t REG_HOPCHANNEL = 0x1C; +const uint8_t REG_MODEMCONFIG1 = 0x1D; +const uint8_t REG_MODEMCONFIG2 = 0x1E; +const uint8_t REG_SYMBTIMEOUTLSB = 0x1F; +const uint8_t REG_PREAMBLEMSB = 0x20; +const uint8_t REG_PREAMBLELSB = 0x21; +const uint8_t REG_PAYLOADLENGTH = 0x22; +const uint8_t REG_FIFORXBYTEADDR = 0x25; +const uint8_t REG_MODEMCONFIG3 = 0x26; +const uint8_t REG_PPMCORRECTION = 0x27; +const uint8_t REG_FEIMSB = 0x28; +const uint8_t REG_FEIMID = 0x29; +const uint8_t REG_FEILSB = 0x2A; +const uint8_t REG_LRRSSIWIDEBAND = 0x2C; +const uint8_t REG_LRTEST2F = 0x2F; +const uint8_t REG_LRTEST30 = 0x30; +const uint8_t REG_DETECTOPTIMIZE = 0x31; +const uint8_t REG_INVERTIQ = 0x33; +const uint8_t REG_LRTEST36 = 0x36; +const uint8_t REG_HIGHBWOPTIMIZE1 = 0x36; +const uint8_t REG_LRDETECTIONTHRESHOLD = 0x37; +const uint8_t REG_DETECTIONTHRESHOLD = 0x37; +const uint8_t REG_SYNCWORD = 0x39; +const uint8_t REG_LRTEST3A = 0x3A; +const uint8_t REG_HIGHBWOPTIMIZE2 = 0x3A; +const uint8_t REG_IMAGECAL = 0x3B; +const uint8_t REG_INVERTIQ2 = 0x3B; +const uint8_t REG_TEMP = 0x3C; +const uint8_t REG_DIOMAPPING1 = 0x40; +const uint8_t REG_DIOMAPPING2 = 0x41; +const uint8_t REG_VERSION = 0x42; +const uint8_t REG_PLLHOP = 0x44; +const uint8_t REG_PADAC = 0x4D; + + +#define PRINT_LOW_REGISTER 0x00 +#define PRINT_HIGH_REGISTER 0x4F + +#define DEVICE_SX1272 0x10 //for modules that use the PA_BOOST pin for RF output +#define DEVICE_SX1276 0x11 //bit 4 set indicates PA_BOOST in use +#define DEVICE_SX1277 0x12 +#define DEVICE_SX1278 0x13 +#define DEVICE_SX1279 0x14 + + +#define DEVICE_SX1272_PABOOST 0x10 //for modules that use the RF_BOOST pin for RF output +#define DEVICE_SX1276_PABOOST 0x11 //bit 4 set indicates PA_BOOST in use +#define DEVICE_SX1277_PABOOST 0x12 +#define DEVICE_SX1278_PABOOST 0x13 +#define DEVICE_SX1279_PABOOST 0x14 + +//no support for SX1272 modules using RFO output, dont have one to test +#define DEVICE_SX1276_RFO 0x01 //for modules that use the RFO LF_ANT or HF_ANT pin for RF output +#define DEVICE_SX1277_RFO 0x02 //bit 4 clear indicates RFO in use +#define DEVICE_SX1278_RFO 0x03 +#define DEVICE_SX1279_RFO 0x04 + + +//power settings +#define MAXPOWER11dBm 0x00 //REG_PACONFIG = x000xxxx +#define MAXPOWER14dBm 0x50 //REG_PACONFIG = x101xxxx +#define MAXPOWER17dBm 0x70 //REG_PACONFIG = x111xxxx +#define PABOOSTON 0x80 +#define PABOOSTOFF 0x00 + + + +//SPI settings +#define LTspeedMaximum 8000000 +#define LTdataOrder MSBFIRST +#define LTdataMode SPI_MODE0 + +#define Deviation5khz 0x52 + + +//FSKRTTY Settings +#define ParityNone 0 +#define ParityOdd 1 +#define ParityEven 2 +#define ParityZero 0xF0 +#define ParityOne 0xF1 + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ diff --git a/lib/SX12XX-LoRa/src/SX128XLT.cpp b/lib/SX12XX-LoRa/src/SX128XLT.cpp new file mode 100644 index 0000000..1b81d32 --- /dev/null +++ b/lib/SX12XX-LoRa/src/SX128XLT.cpp @@ -0,0 +1,2759 @@ +/* + Copyright 2019 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + 06/02/20 +*/ + +/* +Parts of code Copyright (c) 2013, SEMTECH S.A. +See LICENSE.TXT file included in the library +*/ + +#include +#include + +#define LTUNUSED(v) (void) (v) //add LTUNUSED(variable); to avoid compiler warnings +#define USE_SPI_TRANSACTION + +//#define SX128XDEBUG //enable debug messages +//#define RANGINGDEBUG //enable debug messages for ranging +//#define SX128XDEBUGRXTX //enable debug messages for RX TX switching +//#define SX128XDEBUGPINS //enable pin allocation debug messages + +SX128XLT::SX128XLT() +{ +} + +/* Formats for :begin +1 All pins > begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinDIO2, int8_t pinDIO3, int8_t pinRXEN, int8_t pinTXEN, uint8_t device) +2 NiceRF > begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, uint8_t device) +3 Ebyte > begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinRXEN, int8_t pinTXEN, uint8_t device); +*/ + + +bool SX128XLT::begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinDIO2, int8_t pinDIO3, int8_t pinRXEN, int8_t pinTXEN, uint8_t device) +{ + + //format 1 pins, assign all available pins + _NSS = pinNSS; + _NRESET = pinNRESET; + _RFBUSY = pinRFBUSY; + _DIO1 = pinDIO1; + _DIO2 = pinDIO2; + _DIO3 = pinDIO3; + _RXEN = pinRXEN; + _TXEN = pinTXEN; + _Device = device; + _TXDonePin = pinDIO1; //this is defalt pin for sensing TX done + _RXDonePin = pinDIO1; //this is defalt pin for sensing RX done + + pinMode(_NSS, OUTPUT); + digitalWrite(_NSS, HIGH); + pinMode(_NRESET, OUTPUT); + digitalWrite(_NRESET, LOW); + pinMode(_RFBUSY, INPUT); + + +#ifdef SX128XDEBUGPINS + Serial.println(F("begin()")); + Serial.println(F("SX128XLT constructor instantiated successfully")); + Serial.print(F("NSS ")); + Serial.println(_NSS); + Serial.print(F("NRESET ")); + Serial.println(_NRESET); + Serial.print(F("RFBUSY ")); + Serial.println(_RFBUSY); + Serial.print(F("DIO1 ")); + Serial.println(_DIO1); + Serial.print(F("DIO2 ")); + Serial.println(_DIO2); + Serial.print(F("DIO3 ")); + Serial.println(_DIO3); + Serial.print(F("RX_EN ")); + Serial.println(_RXEN); + Serial.print(F("TX_EN ")); + Serial.println(_TXEN); +#endif + + if (_DIO1 >= 0) + { + pinMode( _DIO1, INPUT); + } + + if (_DIO2 >= 0) + { + pinMode( _DIO2, INPUT); + } + + if (_DIO3 >= 0) + { + pinMode( _DIO3, INPUT); + } + + if ((_RXEN >= 0) && (_TXEN >= 0)) + { + #ifdef SX128XDEBUGPINS + Serial.println(F("RX_EN & TX_EN switching enabled")); + #endif + pinMode(_RXEN, OUTPUT); + pinMode(_TXEN, OUTPUT); + _rxtxpinmode = true; + } + else + { + #ifdef SX128XDEBUGPINS + Serial.println(F("RX_EN & TX_EN switching disabled")); + #endif + _rxtxpinmode = false; + } + + resetDevice(); + + if (checkDevice()) + { + return true; + } + + return false; +} + + +bool SX128XLT::begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, uint8_t device) +{ + //format 2 pins for NiceRF, NSS, NRESET, RFBUSY, DIO1 + _NSS = pinNSS; + _NRESET = pinNRESET; + _RFBUSY = pinRFBUSY; + _DIO1 = pinDIO1; + _DIO2 = -1; + _DIO3 = -1; + _RXEN = -1; //not defined, so mark as unused + _TXEN = -1; //not defined, so mark as unused + _Device = device; + _TXDonePin = pinDIO1; //this is defalt pin for sensing TX done + _RXDonePin = pinDIO1; //this is defalt pin for sensing RX done + + pinMode(_NSS, OUTPUT); + digitalWrite(_NSS, HIGH); + pinMode(_NRESET, OUTPUT); + digitalWrite(_NRESET, LOW); + pinMode(_RFBUSY, INPUT); + +#ifdef SX128XDEBUGPINS + Serial.println(F("format 2 NiceRF begin()")); + Serial.println(F("SX128XLT constructor instantiated successfully")); + Serial.print(F("NSS ")); + Serial.println(_NSS); + Serial.print(F("NRESET ")); + Serial.println(_NRESET); + Serial.print(F("RFBUSY ")); + Serial.println(_RFBUSY); + Serial.print(F("DIO1 ")); + Serial.println(_DIO1); + Serial.print(F("DIO2 ")); + Serial.println(_DIO2); + Serial.print(F("DIO3 ")); + Serial.println(_DIO3); + Serial.print(F("RX_EN ")); + Serial.println(_RXEN); + Serial.print(F("TX_EN ")); + Serial.println(_TXEN); +#endif + + if (_DIO1 >= 0) + { + pinMode( _DIO1, INPUT); + } + + #ifdef SX128XDEBUGPINS + Serial.println(F("RX_EN & TX_EN switching disabled")); + #endif + + _rxtxpinmode = false; + + resetDevice(); + + if (checkDevice()) + { + return true; + } + + return false; +} + + +bool SX128XLT::begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinRXEN, int8_t pinTXEN, uint8_t device) +{ + //format 3 pins for Ebyte, NSS, NRESET, RFBUSY, DIO1, RX_EN, TX_EN + _NSS = pinNSS; + _NRESET = pinNRESET; + _RFBUSY = pinRFBUSY; + _DIO1 = pinDIO1; + _DIO2 = -1; + _DIO3 = -1; + _RXEN = pinRXEN; + _TXEN = pinTXEN; + _Device = device; + _TXDonePin = pinDIO1; //this is defalt pin for sensing TX done + _RXDonePin = pinDIO1; //this is defalt pin for sensing RX done + + pinMode(_NSS, OUTPUT); + digitalWrite(_NSS, HIGH); + pinMode(_NRESET, OUTPUT); + digitalWrite(_NRESET, LOW); + pinMode(_RFBUSY, INPUT); + +#ifdef SX128XDEBUGPINS + Serial.println(F("format 3 Ebyte begin()")); + Serial.println(F("SX128XLT constructor instantiated successfully")); + Serial.print(F("NSS ")); + Serial.println(_NSS); + Serial.print(F("NRESET ")); + Serial.println(_NRESET); + Serial.print(F("RFBUSY ")); + Serial.println(_RFBUSY); + Serial.print(F("DIO1 ")); + Serial.println(_DIO1); + Serial.print(F("DIO2 ")); + Serial.println(_DIO2); + Serial.print(F("DIO3 ")); + Serial.println(_DIO3); + Serial.print(F("RX_EN ")); + Serial.println(_RXEN); + Serial.print(F("TX_EN ")); + Serial.println(_TXEN); +#endif + + if (_DIO1 >= 0) + { + pinMode( _DIO1, INPUT); + } + + if ((_RXEN >= 0) && (_TXEN >= 0)) + { + #ifdef SX128XDEBUGPINS + Serial.println(F("RX_EN & TX_EN switching enabled")); + #endif + pinMode(_RXEN, OUTPUT); + pinMode(_TXEN, OUTPUT); + _rxtxpinmode = true; + } + else + { + #ifdef SX128XDEBUGPINS + Serial.println(F("RX_EN & TX_EN switching disabled")); + #endif + _rxtxpinmode = false; + } + + resetDevice(); + + if (checkDevice()) + { + return true; + } + + return false; +} + + +void SX128XLT::rxEnable() +{ + //Enable RX mode on device such as Ebyte E28-2G4M20S which have RX and TX enable pins + #ifdef SX128XDEBUGRXTX + Serial.println(F("rxEnable()")); + #endif + + digitalWrite(_RXEN, HIGH); + digitalWrite(_TXEN, LOW); +} + + +void SX128XLT::txEnable() +{ + //Enable RX mode on device such as Ebyte E28-2G4M20S which have RX and TX enable pins +#ifdef SX128XDEBUGRXTX + Serial.println(F("txEnable()")); +#endif + + digitalWrite(_RXEN, LOW); + digitalWrite(_TXEN, HIGH); +} + + + +void SX128XLT::checkBusy() +{ +#ifdef SX128XDEBUG + //Serial.println(F("checkBusy()")); +#endif + + uint8_t busy_timeout_cnt; + busy_timeout_cnt = 0; + + while (digitalRead(_RFBUSY)) + { + delay(1); + busy_timeout_cnt++; + + if (busy_timeout_cnt > 10) //wait 5mS for busy to complete + { + Serial.println(F("ERROR - Busy Timeout!")); + resetDevice(); + setMode(MODE_STDBY_RC); + config(); //re-run saved config + break; + } + } +} + + +bool SX128XLT::config() +{ +#ifdef SX128XDEBUG + Serial.println(F("config()")); +#endif + + resetDevice(); + setMode(MODE_STDBY_RC); + setRegulatorMode(savedRegulatorMode); + setPacketType(savedPacketType); + setRfFrequency(savedFrequency, savedOffset); + setModulationParams(savedModParam1, savedModParam2, savedModParam3); + setPacketParams(savedPacketParam1, savedPacketParam2, savedPacketParam3, savedPacketParam4, savedPacketParam5, savedPacketParam6, savedPacketParam7); + setDioIrqParams(savedIrqMask, savedDio1Mask, savedDio2Mask, savedDio3Mask); //set for IRQ on RX done on DIO1 + return true; +} + + +void SX128XLT::readRegisters(uint16_t address, uint8_t *buffer, uint16_t size) +{ +#ifdef SX128XDEBUG + //Serial.println(F("readRegisters()")); +#endif + + uint16_t index; + uint8_t addr_l, addr_h; + + addr_h = address >> 8; + addr_l = address & 0x00FF; + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_READ_REGISTER); + SPI.transfer(addr_h); //MSB + SPI.transfer(addr_l); //LSB + SPI.transfer(0xFF); + for (index = 0; index < size; index++) + { + *(buffer + index) = SPI.transfer(0xFF); + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + +} + + +uint8_t SX128XLT::readRegister(uint16_t address) +{ +#ifdef SX128XDEBUG + //Serial.println(F("readRegister()")); +#endif + + uint8_t data; + + readRegisters(address, &data, 1); + return data; +} + + +void SX128XLT::writeRegisters(uint16_t address, uint8_t *buffer, uint16_t size) +{ +#ifdef SX128XDEBUG + //Serial.println(F("writeRegisters()")); +#endif + uint8_t addr_l, addr_h; + uint8_t i; + + addr_l = address & 0xff; + addr_h = address >> 8; + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_WRITE_REGISTER); + SPI.transfer(addr_h); //MSB + SPI.transfer(addr_l); //LSB + + for (i = 0; i < size; i++) + { + SPI.transfer(buffer[i]); + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + //checkBusy(); +} + + +void SX128XLT::writeRegister(uint16_t address, uint8_t value) +{ +#ifdef SX128XDEBUG + //Serial.println(F("writeRegister()")); +#endif + + writeRegisters( address, &value, 1 ); +} + + +void SX128XLT::writeCommand(uint8_t Opcode, uint8_t *buffer, uint16_t size) +{ +#ifdef SX128XDEBUG + //Serial.print(F("writeCommand() ")); + //Serial.println(Opcode, HEX); +#endif + + uint8_t index; + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(Opcode); + + for (index = 0; index < size; index++) + { + SPI.transfer(buffer[index]); + } + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + if (Opcode != RADIO_SET_SLEEP) + { + checkBusy(); + } +} + + +void SX128XLT::readCommand(uint8_t Opcode, uint8_t *buffer, uint16_t size) +{ +#ifdef SX128XDEBUG + //Serial.println(F("readCommand()")); +#endif + + uint8_t i; + checkBusy(); + + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(Opcode); + SPI.transfer(0xFF); + + for ( i = 0; i < size; i++ ) + { + *(buffer + i) = SPI.transfer(0xFF); + } + digitalWrite(_NSS, HIGH); + + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + //checkBusy(); +} + + +void SX128XLT::resetDevice() +{ +#ifdef SX128XDEBUG + Serial.println(F("resetDevice()")); +#endif + + //timings taken from Semtech library + delay(20); + digitalWrite(_NRESET, LOW); + delay(50); + digitalWrite(_NRESET, HIGH); + delay(20); +} + + +bool SX128XLT::checkDevice() +{ + //check there is a device out there, writes a register and reads back +#ifdef SX128XDEBUG + Serial.println(F("checkDevice()")); +#endif + + uint8_t Regdata1, Regdata2; + Regdata1 = readRegister(0x0908); //low byte of frequency setting + writeRegister(0x0908, (Regdata1 + 1)); + Regdata2 = readRegister(0x0908); //read changed value back + writeRegister(0x0908, Regdata1); //restore register to original value + + if (Regdata2 == (Regdata1 + 1)) + { + return true; + } + else + { + return false; + } +} + + +void SX128XLT::setupLoRa(uint32_t frequency, int32_t offset, uint8_t modParam1, uint8_t modParam2, uint8_t modParam3) +{ +#ifdef SX128XDEBUG + Serial.println(F("setupLoRa()")); +#endif + + setMode(MODE_STDBY_RC); + setRegulatorMode(USE_LDO); + setPacketType(PACKET_TYPE_LORA); + setRfFrequency(frequency, offset); + setBufferBaseAddress(0, 0); + setModulationParams(modParam1, modParam2, modParam3); + setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0); + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); +} + + +void SX128XLT::setMode(uint8_t modeconfig) +{ +#ifdef SX128XDEBUG + Serial.println(F("setMode()")); +#endif + + uint8_t Opcode = 0x80; + + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + + digitalWrite(_NSS, LOW); + SPI.transfer(Opcode); + SPI.transfer(modeconfig); + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + _OperatingMode = modeconfig; + +} + +void SX128XLT::setRegulatorMode(uint8_t mode) +{ +#ifdef SX128XDEBUG + Serial.println(F("setRegulatorMode()")); +#endif + + savedRegulatorMode = mode; + + writeCommand(RADIO_SET_REGULATORMODE, &mode, 1); +} + +void SX128XLT::setPacketType(uint8_t packettype ) +{ +#ifdef SX128XDEBUG + Serial.println(F("setPacketType()")); +#endif + savedPacketType = packettype; + + writeCommand(RADIO_SET_PACKETTYPE, &packettype, 1); +} + + +void SX128XLT::setRfFrequency(uint32_t frequency, int32_t offset) +{ +#ifdef SX128XDEBUG + Serial.println(F("setRfFrequency()")); +#endif + + savedFrequency = frequency; + savedOffset = offset; + + frequency = frequency + offset; + uint8_t buffer[3]; + uint32_t freqtemp = 0; + freqtemp = ( uint32_t )( (double) frequency / (double)FREQ_STEP); + buffer[0] = ( uint8_t )( ( freqtemp >> 16 ) & 0xFF ); + buffer[1] = ( uint8_t )( ( freqtemp >> 8 ) & 0xFF ); + buffer[2] = ( uint8_t )( freqtemp & 0xFF ); + writeCommand(RADIO_SET_RFFREQUENCY, buffer, 3); +} + +void SX128XLT::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) +{ +#ifdef SX128XDEBUG + Serial.println(F("setBufferBaseAddress()")); +#endif + + uint8_t buffer[2]; + + buffer[0] = txBaseAddress; + buffer[1] = rxBaseAddress; + writeCommand(RADIO_SET_BUFFERBASEADDRESS, buffer, 2); +} + + +void SX128XLT::setModulationParams(uint8_t modParam1, uint8_t modParam2, uint8_t modParam3) +{ +//sequence is spreading factor, bandwidth, coding rate. + +#ifdef SX128XDEBUG + Serial.println(F("setModulationParams()")); +#endif + + uint8_t buffer[3]; + + savedModParam1 = modParam1; + savedModParam2 = modParam2; + savedModParam3 = modParam3; + + buffer[0] = modParam1; + buffer[1] = modParam2; + buffer[2] = modParam3; + + writeCommand(RADIO_SET_MODULATIONPARAMS, buffer, 3); +} + + +void SX128XLT::setPacketParams(uint8_t packetParam1, uint8_t packetParam2, uint8_t packetParam3, uint8_t packetParam4, uint8_t packetParam5, uint8_t packetParam6, uint8_t packetParam7) +{ +//for LoRa order is PreambleLength, HeaderType, PayloadLength, CRC, InvertIQ/chirp invert, not used, not used +//for FLRC order is PreambleLength, SyncWordLength, SyncWordMatch, HeaderType, PayloadLength, CrcLength, Whitening + +#ifdef SX128XDEBUG + Serial.println(F("SetPacketParams()")); +#endif + + savedPacketParam1 = packetParam1; + savedPacketParam2 = packetParam2; + savedPacketParam3 = packetParam3; + savedPacketParam4 = packetParam4; + savedPacketParam5 = packetParam5; + savedPacketParam6 = packetParam6; + savedPacketParam7 = packetParam7; + + uint8_t buffer[7]; + buffer[0] = packetParam1; + buffer[1] = packetParam2; + buffer[2] = packetParam3; + buffer[3] = packetParam4; + buffer[4] = packetParam5; + buffer[5] = packetParam6; + buffer[6] = packetParam7; + writeCommand(RADIO_SET_PACKETPARAMS, buffer, 7); + +} + + +void SX128XLT::setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask ) +{ +#ifdef SX128XDEBUG + Serial.println(F("setDioIrqParams()")); +#endif + + savedIrqMask = irqMask; + savedDio1Mask = dio1Mask; + savedDio2Mask = dio2Mask; + savedDio3Mask = dio3Mask; + + uint8_t buffer[8]; + + buffer[0] = (uint8_t) (irqMask >> 8); + buffer[1] = (uint8_t) (irqMask & 0xFF); + buffer[2] = (uint8_t) (dio1Mask >> 8); + buffer[3] = (uint8_t) (dio1Mask & 0xFF); + buffer[4] = (uint8_t) (dio2Mask >> 8); + buffer[5] = (uint8_t) (dio2Mask & 0xFF); + buffer[6] = (uint8_t) (dio3Mask >> 8); + buffer[7] = (uint8_t) (dio3Mask & 0xFF); + writeCommand(RADIO_SET_DIOIRQPARAMS, buffer, 8); +} + + +void SX128XLT::setHighSensitivity() +{ + //set bits 7,6 of REG_LNA_REGIME +#ifdef SX128XDEBUG + Serial.println(F("setHighSensitivity()")); +#endif + + writeRegister(REG_LNA_REGIME, (readRegister(REG_LNA_REGIME) | 0xC0)); +} + +void SX128XLT::setLowPowerRX() +{ + //clear bits 7,6 of REG_LNA_REGIME +#ifdef SX128XDEBUG + Serial.println(F("setLowPowerRX()")); +#endif + + writeRegister(REG_LNA_REGIME, (readRegister(REG_LNA_REGIME) & 0x3F)); +} + + +void SX128XLT::printModemSettings() +{ +#ifdef SX128XDEBUG + Serial.println(F("printModemSettings()")); +#endif + + printDevice(); + Serial.print(F(",")); + Serial.print(getFreqInt()); + Serial.print(F("hz")); + + if (savedPacketType == PACKET_TYPE_LORA) + { + Serial.print(F(",SF")); + Serial.print(getLoRaSF()); + Serial.print(F(",BW")); + Serial.print(returnBandwidth(savedModParam2)); + Serial.print(F(",CR4:")); + Serial.print((getLoRaCodingRate() + 4)); + + if (getInvertIQ() == LORA_IQ_INVERTED) + { + Serial.print(F(",IQInverted")); + } + else + { + Serial.print(F(",IQNormal")); + } + + Serial.print(F(",Preamble_")); + Serial.print(getPreamble()); + } + + if (savedPacketType == PACKET_TYPE_FLRC) + { + if (savedPacketParam1 == 0) + { + Serial.print(F(",No_Syncword")); + } + + if (savedPacketParam1 == 4) + { + Serial.print(F(",32bit_Syncword")); + } + + + switch (savedPacketParam3) + { + case RADIO_RX_MATCH_SYNCWORD_OFF: + Serial.print(F(",SYNCWORD_OFF")); + break; + + case RADIO_RX_MATCH_SYNCWORD_1: + Serial.print(F(",SYNCWORD_1")); + break; + + case RADIO_RX_MATCH_SYNCWORD_2: + Serial.print(F(",SYNCWORD_2")); + break; + + case RADIO_RX_MATCH_SYNCWORD_1_2: + Serial.print(F(",SYNCWORD_1_2")); + break; + + case RADIO_RX_MATCH_SYNCWORD_3: + Serial.print(F(",SYNCWORD_3")); + break; + + case RADIO_RX_MATCH_SYNCWORD_1_3: + Serial.print(F(",SYNCWORD_1_3")); + break; + + case RADIO_RX_MATCH_SYNCWORD_2_3: + Serial.print(F(",SYNCWORD_2_3")); + break; + + case RADIO_RX_MATCH_SYNCWORD_1_2_3: + Serial.print(F(",SYNCWORD_1_2_3")); + break; + + default: + Serial.print(F("Unknown_SYNCWORD")); + } + + if (savedPacketParam4 == RADIO_PACKET_FIXED_LENGTH) + { + Serial.print(F(",PACKET_FIXED_LENGTH")); + } + + if (savedPacketParam4 == RADIO_PACKET_VARIABLE_LENGTH) + { + Serial.print(F(",PACKET_VARIABLE_LENGTH")); + } + + switch (savedPacketParam6) + { + case RADIO_CRC_OFF: + Serial.print(F(",CRC_OFF")); + break; + + case RADIO_CRC_1_BYTES: + Serial.print(F(",CRC_1_BYTES")); + break; + + case RADIO_CRC_2_BYTES: + Serial.print(F(",CRC_2_BYTES")); + break; + + case RADIO_CRC_3_BYTES: + Serial.print(F(",CRC_3_BYTES")); + break; + + default: + Serial.print(F(",Unknown_CRC")); + } + + if (savedPacketParam7 == RADIO_WHITENING_ON) + { + Serial.print(F(",WHITENING_ON")); + } + + if (savedPacketParam7 == RADIO_WHITENING_OFF) + { + Serial.print(F(",WHITENING_OFF")); + } + + } + +} + + +void SX128XLT::printDevice() +{ +#ifdef SX128XDEBUG + Serial.println(F("printDevice()")); +#endif + + + switch (_Device) + { + case DEVICE_SX1280: + Serial.print(F("SX1280")); + break; + + case DEVICE_SX1281: + Serial.print(F("SX1281")); + break; + + default: + Serial.print(F("Unknown Device")); + + } +} + + +uint32_t SX128XLT::getFreqInt() +{ + +#ifdef SX128XDEBUG + Serial.println(F("getFreqInt")); +#endif + + //get the current set device frequency, return as long integer + uint8_t Msb = 0; + uint8_t Mid = 0; + uint8_t Lsb = 0; + + uint32_t uinttemp; + float floattemp; + + LTUNUSED(Msb); //to prevent a compiler warning + LTUNUSED(Mid); //to prevent a compiler warning + LTUNUSED(Lsb); //to prevent a compiler warning + + if (savedPacketType == PACKET_TYPE_LORA) + { + Msb = readRegister(REG_RFFrequency23_16); + Mid = readRegister(REG_RFFrequency15_8); + Lsb = readRegister(REG_RFFrequency7_0); + } + + if (savedPacketType == PACKET_TYPE_FLRC) + { + Msb = readRegister(REG_FLRC_RFFrequency23_16); + Mid = readRegister(REG_FLRC_RFFrequency15_8); + Lsb = readRegister(REG_FLRC_RFFrequency7_0); + } + + floattemp = ((Msb * 0x10000ul) + (Mid * 0x100ul) + Lsb); + floattemp = ((floattemp * FREQ_STEP) / 1000000ul); + uinttemp = (uint32_t)(floattemp * 1000000); + return uinttemp; +} + + +uint8_t SX128XLT::getLoRaSF() +{ +#ifdef SX128XDEBUG + Serial.println(F("getLoRaSF()")); +#endif + return (savedModParam1 >> 4); +} + + +uint32_t SX128XLT::returnBandwidth(uint8_t data) +{ +#ifdef SX128XDEBUG + Serial.println(F("returnBandwidth()")); +#endif + + switch (data) + { + case LORA_BW_0200: + return 203125; + + case LORA_BW_0400: + return 406250; + + case LORA_BW_0800: + return 812500; + + case LORA_BW_1600: + return 1625000; + + default: + break; + } + + return 0x0; //so that a bandwidth not set can be identified +} + + +uint8_t SX128XLT::getLoRaCodingRate() +{ +#ifdef SX128XDEBUG + Serial.println(F("getLoRaCodingRate")); +#endif + + return savedModParam3; +} + + +uint8_t SX128XLT::getInvertIQ() +{ +//IQ mode reg 0x33 +#ifdef SX128XDEBUG + Serial.println(F("getInvertIQ")); +#endif + + return savedPacketParam5; +} + + +uint16_t SX128XLT::getPreamble() +{ +#ifdef SX128XDEBUG + Serial.println(F("getPreamble")); +#endif + + return savedPacketParam1; +} + +void SX128XLT::printOperatingSettings() +{ +#ifdef SX128XDEBUG + Serial.println(F("printOperatingSettings()")); +#endif + + printDevice(); + + Serial.print(F(",PacketMode_")); + + switch (savedPacketType) + { + case PACKET_TYPE_GFSK: + Serial.print(F("GFSK")); + break; + + case PACKET_TYPE_LORA: + Serial.print(F("LORA")); + break; + + case PACKET_TYPE_RANGING: + Serial.print(F("RANGING")); + break; + + case PACKET_TYPE_FLRC: + Serial.print(F("FLRC")); + break; + + + case PACKET_TYPE_BLE: + Serial.print(F("BLE")); + break; + + default: + Serial.print(F("Unknown")); + + } + + switch (savedPacketParam2) + { + case LORA_PACKET_VARIABLE_LENGTH: + Serial.print(F(",Explicit")); + break; + + case LORA_PACKET_FIXED_LENGTH: + Serial.print(F(",Implicit")); + break; + + default: + Serial.print(F(",Unknown")); + } + + Serial.print(F(",LNAgain_")); + + + if (getLNAgain() == 0xC0) + { + Serial.print(F("HighSensitivity")); + } + else + { + Serial.print(F("LowPowerRX")); + } + +} + + +uint8_t SX128XLT::getLNAgain() +{ +#ifdef SX128XDEBUG + Serial.println(F("getLNAgain")); +#endif + + return (readRegister(REG_LNA_REGIME) & 0xC0); +} + + + +void SX128XLT::printRegisters(uint16_t Start, uint16_t End) +{ + //prints the contents of SX1280 registers to serial monitor + +#ifdef SX128XDEBUG + Serial.println(F("printRegisters()")); +#endif + + uint16_t Loopv1, Loopv2, RegData; + + Serial.print(F("Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F")); + Serial.println(); + + for (Loopv1 = Start; Loopv1 <= End;) //32 lines + { + Serial.print(F("0x")); + Serial.print((Loopv1), HEX); //print the register number + Serial.print(F(" ")); + for (Loopv2 = 0; Loopv2 <= 15; Loopv2++) + { + RegData = readRegister(Loopv1); + if (RegData < 0x10) + { + Serial.print(F("0")); + } + Serial.print(RegData, HEX); //print the register number + Serial.print(F(" ")); + Loopv1++; + } + Serial.println(); + } +} + + +void SX128XLT::printASCIIPacket(uint8_t *buffer, uint8_t size) +{ +#ifdef SX128XDEBUG + Serial.println(F("printASCIIPacket()")); +#endif + + uint8_t index; + + for (index = 0; index < size; index++) + { + Serial.write(buffer[index]); + } + +} + + +uint8_t SX128XLT::transmit(uint8_t *txbuffer, uint8_t size, uint16_t timeout, int8_t txpower, uint8_t wait) +{ +#ifdef SX128XDEBUG + Serial.println(F("transmit()")); +#endif + uint8_t index; + uint8_t bufferdata; + + if (size == 0) + { + return false; + } + + setMode(MODE_STDBY_RC); + setBufferBaseAddress(0, 0); + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_WRITE_BUFFER); + SPI.transfer(0); + + for (index = 0; index < size; index++) + { + bufferdata = txbuffer[index]; + SPI.transfer(bufferdata); + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + _TXPacketL = size; + + if (savedPacketType == PACKET_TYPE_LORA) + { + writeRegister(REG_LR_PAYLOADLENGTH, _TXPacketL); //only seems to work for lora + } + else if (savedPacketType == PACKET_TYPE_FLRC) + { + setPacketParams(savedPacketParam1, savedPacketParam2, savedPacketParam3, savedPacketParam4, _TXPacketL, savedPacketParam6, savedPacketParam7); + } + + setTxParams(txpower, RAMP_TIME); + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + setTx(timeout); //this starts the TX + + if (!wait) + { + return _TXPacketL; + } + + while (!digitalRead(_TXDonePin)); //Wait for DIO1 to go high + + if (readIrqStatus() & IRQ_RX_TX_TIMEOUT ) //check for timeout + { + return 0; + } + else + { + return _TXPacketL; + } +} + + +void SX128XLT::setTxParams(int8_t TXpower, uint8_t RampTime) +{ +#ifdef SX128XDEBUG + Serial.println(F("setTxParams()")); +#endif + + uint8_t buffer[2]; + + savedTXPower = TXpower; + + //power register is set to 0 to 31 which is -18dBm to +12dBm + buffer[0] = (TXpower + 18); + buffer[1] = (uint8_t)RampTime; + writeCommand(RADIO_SET_TXPARAMS, buffer, 2); +} + + +void SX128XLT::setTx(uint16_t timeout) +{ + +#ifdef SX128XDEBUG + Serial.println(F("setTx()")); +#endif + + if (_rxtxpinmode) + { + txEnable(); + } + + //Serial.print(F("timeout ")); + //Serial.println(timeout); + //Serial.print(F("_PERIODBASE ")); + //Serial.println(_PERIODBASE); + + uint8_t buffer[3]; + + clearIrqStatus(IRQ_RADIO_ALL); //clear all interrupt flags + buffer[0] = _PERIODBASE; + buffer[1] = ( uint8_t )( ( timeout >> 8 ) & 0x00FF ); + buffer[2] = ( uint8_t )( timeout & 0x00FF ); + writeCommand(RADIO_SET_TX, buffer, 3 ); +} + + +void SX128XLT::clearIrqStatus(uint16_t irqMask) +{ +#ifdef SX128XDEBUG + Serial.println(F("clearIrqStatus()")); +#endif + + uint8_t buffer[2]; + + buffer[0] = (uint8_t) (irqMask >> 8); + buffer[1] = (uint8_t) (irqMask & 0xFF); + writeCommand(RADIO_CLR_IRQSTATUS, buffer, 2); +} + + +uint16_t SX128XLT::readIrqStatus() +{ +#ifdef SX128XDEBUG + Serial.print(F("readIrqStatus()")); +#endif + + uint16_t temp; + uint8_t buffer[2]; + + readCommand(RADIO_GET_IRQSTATUS, buffer, 2); + temp = ((buffer[0] << 8) + buffer[1]); + return temp; +} + + +void SX128XLT::printIrqStatus() +{ +#ifdef SX128XDEBUG + Serial.println(F("printIrqStatus()")); +#endif + + uint16_t _IrqStatus; + _IrqStatus = readIrqStatus(); + + //0x0001 + if (_IrqStatus & IRQ_TX_DONE) + { + Serial.print(F(",IRQ_TX_DONE")); + } + + //0x0002 + if (_IrqStatus & IRQ_RX_DONE) + { + Serial.print(F(",IRQ_RX_DONE")); + } + + //0x0004 + if (_IrqStatus & IRQ_SYNCWORD_VALID) + { + Serial.print(F(",IRQ_SYNCWORD_VALID")); + } + + //0x0008 + if (_IrqStatus & IRQ_SYNCWORD_ERROR) + { + Serial.print(F(",IRQ_SYNCWORD_ERROR")); + } + + //0x0010 + if (_IrqStatus & IRQ_HEADER_VALID) + { + Serial.print(F(",IRQ_HEADER_VALID")); + } + + //0x0020 + if (_IrqStatus & IRQ_HEADER_ERROR) + { + Serial.print(F(",IRQ_HEADER_ERROR")); + } + + //0x0040 + if (_IrqStatus & IRQ_CRC_ERROR) + { + Serial.print(F(",IRQ_CRC_ERROR")); + } + + //0x0080 + if (_IrqStatus & IRQ_RANGING_SLAVE_RESPONSE_DONE) + { + Serial.print(F(",IRQ_RANGING_SLAVE_RESPONSE_DONE")); + } + + //0x0100 + if (_IrqStatus & IRQ_RANGING_SLAVE_REQUEST_DISCARDED) + { + Serial.print(",IRQ_RANGING_SLAVE_REQUEST_DISCARDED"); + } + + //0x0200 + if (_IrqStatus & IRQ_RANGING_MASTER_RESULT_VALID) + { + Serial.print(F(",IRQ_RANGING_MASTER_RESULT_VALID")); + } + + //0x0400 + if (_IrqStatus & IRQ_RANGING_MASTER_RESULT_TIMEOUT) + { + Serial.print(F(",IRQ_RANGING_MASTER_RESULT_TIMEOUT")); + } + + //0x0800 + if (_IrqStatus & IRQ_RANGING_SLAVE_REQUEST_VALID) + { + Serial.print(F(",IRQ_RANGING_SLAVE_REQUEST_VALID")); + } + + //0x1000 + if (_IrqStatus & IRQ_CAD_DONE) + { + Serial.print(F(",IRQ_CAD_DONE")); + } + + //0x2000 + if (_IrqStatus & IRQ_CAD_ACTIVITY_DETECTED) + { + Serial.print(F(",IRQ_CAD_ACTIVITY_DETECTED")); + } + + //0x4000 + if (_IrqStatus & IRQ_RX_TX_TIMEOUT) + { + Serial.print(F(",IRQ_RX_TX_TIMEOUT")); + } + + //0x8000 + if (_IrqStatus & IRQ_PREAMBLE_DETECTED) + { + Serial.print(F(",IRQ_PREAMBLE_DETECTED")); + } +} + + +uint16_t SX128XLT::CRCCCITT(uint8_t *buffer, uint8_t size, uint16_t start) +{ +#ifdef SX128XDEBUG + Serial.println(F("CRCCCITT()")); +#endif + + uint16_t index, libraryCRC; + uint8_t j; + + libraryCRC = start; //start value for CRC16 + + for (index = 0; index < size; index++) + { + libraryCRC ^= (((uint16_t)buffer[index]) << 8); + for (j = 0; j < 8; j++) + { + if (libraryCRC & 0x8000) + libraryCRC = (libraryCRC << 1) ^ 0x1021; + else + libraryCRC <<= 1; + } + } + + return libraryCRC; +} + + +uint8_t SX128XLT::receive(uint8_t *rxbuffer, uint8_t size, uint16_t timeout, uint8_t wait) +{ +#ifdef SX128XDEBUG + Serial.println(F("receive()")); +#endif + + uint8_t index, RXstart, RXend; + uint16_t regdata; + uint8_t buffer[2]; + + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on RX done or timeout + setRx(timeout); + + if (!wait) + { + return 0; //not wait requested so no packet length to pass + } + + while (!digitalRead(_RXDonePin)); //Wait for DIO1 to go high + + setMode(MODE_STDBY_RC); //ensure to stop further packet reception + + regdata = readIrqStatus(); + + if ( (regdata & IRQ_HEADER_ERROR) | (regdata & IRQ_CRC_ERROR) | (regdata & IRQ_RX_TX_TIMEOUT ) ) //check if any of the preceding IRQs is set + { + return 0; //packet is errored somewhere so return 0 + } + + readCommand(RADIO_GET_RXBUFFERSTATUS, buffer, 2); + _RXPacketL = buffer[0]; + + if (_RXPacketL > size) //check passed buffer is big enough for packet + { + _RXPacketL = size; //truncate packet if not enough space + } + + RXstart = buffer[1]; + + RXend = RXstart + _RXPacketL; + + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(RXstart); + SPI.transfer(0xFF); + + for (index = RXstart; index < RXend; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + +uint8_t SX128XLT::readPacketRSSI() +{ +#ifdef SX128XDEBUG + Serial.println(F("readPacketRSSI()")); +#endif + + uint8_t status[5]; + + readCommand(RADIO_GET_PACKETSTATUS, status, 5) ; + _PacketRSSI = -status[0] / 2; + + return _PacketRSSI; +} + + +uint8_t SX128XLT::readPacketSNR() +{ +#ifdef SX128XDEBUG + Serial.println(F("readPacketSNR()")); +#endif + + uint8_t status[5]; + + readCommand(RADIO_GET_PACKETSTATUS, status, 5) ; + + if ( status[1] < 128 ) + { + _PacketSNR = status[1] / 4 ; + } + else + { + _PacketSNR = (( status[1] - 256 ) / 4); + } + + return _PacketSNR; +} + + +uint8_t SX128XLT::readRXPacketL() +{ +#ifdef SX128XDEBUG + Serial.println(F("readRXPacketL()")); +#endif + + uint8_t buffer[2]; + + readCommand(RADIO_GET_RXBUFFERSTATUS, buffer, 2); + _RXPacketL = buffer[0]; + return _RXPacketL; +} + + +void SX128XLT::setRx(uint16_t timeout) +{ +#ifdef SX128XDEBUG + Serial.println(F("setRx()")); +#endif + + if (_rxtxpinmode) + { + rxEnable(); + } + + uint8_t buffer[3]; + + clearIrqStatus(IRQ_RADIO_ALL); //clear all interrupt flags + buffer[0] = _PERIODBASE; //use pre determined period base setting + buffer[1] = ( uint8_t ) ((timeout >> 8 ) & 0x00FF); + buffer[2] = ( uint8_t ) (timeout & 0x00FF); + writeCommand(RADIO_SET_RX, buffer, 3); +} + +/*************************************************************************** +//Start direct access SX buffer routines +***************************************************************************/ + +void SX128XLT::startWriteSXBuffer(uint8_t ptr) +{ +#ifdef SX128XDEBUG + Serial.println(F("startWriteSXBuffer()")); +#endif + + _TXPacketL = 0; //this variable used to keep track of bytes written + setMode(MODE_STDBY_RC); + setBufferBaseAddress(ptr, 0); //TX,RX + checkBusy(); + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_WRITE_BUFFER); + SPI.transfer(ptr); //address in SX buffer to write to + //SPI interface ready for byte to write to buffer +} + + +uint8_t SX128XLT::endWriteSXBuffer() +{ +#ifdef SX128XDEBUG + Serial.println(F("endWriteSXBuffer()")); +#endif + + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _TXPacketL; + +} + + +void SX128XLT::startReadSXBuffer(uint8_t ptr) +{ +#ifdef SX128XDEBUG + Serial.println(F("startReadSXBuffer")); +#endif + + _RXPacketL = 0; + + checkBusy(); + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); + #endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(ptr); + SPI.transfer(0xFF); + + //next line would be data = SPI.transfer(0); + //SPI interface ready for byte to read from +} + + +uint8_t SX128XLT::endReadSXBuffer() +{ +#ifdef SX128XDEBUG + Serial.println(F("endReadSXBuffer()")); +#endif + + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; +} + + +void SX128XLT::writeUint8(uint8_t x) +{ +#ifdef SX128XDEBUG + Serial.println(F("writeUint8()")); +#endif + + SPI.transfer(x); + + _TXPacketL++; //increment count of bytes written +} + +uint8_t SX128XLT::readUint8() +{ +#ifdef SX128XDEBUG + Serial.println(F("readUint8()")); +#endif + byte x; + + x = SPI.transfer(0); + + _RXPacketL++; //increment count of bytes read + return (x); +} + + +void SX128XLT::writeInt8(int8_t x) +{ +#ifdef SX128XDEBUG + Serial.println(F("writeInt8()")); +#endif + + SPI.transfer(x); + + _TXPacketL++; //increment count of bytes written +} + + +int8_t SX128XLT::readInt8() +{ +#ifdef SX128XDEBUG + Serial.println(F("readInt8()")); +#endif + int8_t x; + + x = SPI.transfer(0); + + _RXPacketL++; //increment count of bytes read + return (x); +} + + +void SX128XLT::writeInt16(int16_t x) +{ +#ifdef SX128XDEBUG + Serial.println(F("writeInt16()")); +#endif + + SPI.transfer(lowByte(x)); + SPI.transfer(highByte(x)); + + _TXPacketL = _TXPacketL + 2; //increment count of bytes written +} + + +int16_t SX128XLT::readInt16() +{ +#ifdef SX128XDEBUG + Serial.println(F("readInt16()")); +#endif + byte lowbyte, highbyte; + + lowbyte = SPI.transfer(0); + highbyte = SPI.transfer(0); + + _RXPacketL = _RXPacketL + 2; //increment count of bytes read + return ((highbyte << 8) + lowbyte); +} + + +void SX128XLT::writeUint16(uint16_t x) +{ +#ifdef SX128XDEBUG + Serial.println(F("writeUint16()")); +#endif + + SPI.transfer(lowByte(x)); + SPI.transfer(highByte(x)); + + _TXPacketL = _TXPacketL + 2; //increment count of bytes written +} + + +uint16_t SX128XLT::readUint16() +{ +#ifdef SX128XDEBUG + Serial.println(F("writeUint16()")); +#endif + byte lowbyte, highbyte; + + lowbyte = SPI.transfer(0); + highbyte = SPI.transfer(0); + + _RXPacketL = _RXPacketL + 2; //increment count of bytes read + return ((highbyte << 8) + lowbyte); +} + + +void SX128XLT::writeInt32(int32_t x) +{ +#ifdef SX128XDEBUG + Serial.println(F("writeInt32()")); +#endif + + byte i, j; + + union + { + byte b[4]; + int32_t f; + } data; + data.f = x; + + for (i = 0; i < 4; i++) + { + j = data.b[i]; + SPI.transfer(j); + } + + _TXPacketL = _TXPacketL + 4; //increment count of bytes written +} + + +int32_t SX128XLT::readInt32() +{ +#ifdef SX128XDEBUG + Serial.println(F("readInt32()")); +#endif + + byte i, j; + + union + { + byte b[4]; + int32_t f; + } readdata; + + for (i = 0; i < 4; i++) + { + j = SPI.transfer(0); + readdata.b[i] = j; + } + _RXPacketL = _RXPacketL + 4; //increment count of bytes read + return readdata.f; +} + + +void SX128XLT::writeUint32(uint32_t x) +{ +#ifdef SX128XDEBUG + Serial.println(F("writeUint32()")); +#endif + + byte i, j; + + union + { + byte b[4]; + uint32_t f; + } data; + data.f = x; + + for (i = 0; i < 4; i++) + { + j = data.b[i]; + SPI.transfer(j); + } + + _TXPacketL = _TXPacketL + 4; //increment count of bytes written +} + + +uint32_t SX128XLT::readUint32() +{ +#ifdef SX128XDEBUG + Serial.println(F("readUint32()")); +#endif + + byte i, j; + + union + { + byte b[4]; + uint32_t f; + } readdata; + + for (i = 0; i < 4; i++) + { + j = SPI.transfer(0); + readdata.b[i] = j; + } + _RXPacketL = _RXPacketL + 4; //increment count of bytes read + return readdata.f; +} + + +void SX128XLT::writeFloat(float x) +{ +#ifdef SX128XDEBUG + Serial.println(F("writeFloat()")); +#endif + + byte i, j; + + union + { + byte b[4]; + float f; + } data; + data.f = x; + + for (i = 0; i < 4; i++) + { + j = data.b[i]; + SPI.transfer(j); + } + + _TXPacketL = _TXPacketL + 4; //increment count of bytes written +} + + +float SX128XLT::readFloat() +{ +#ifdef SX128XDEBUG + Serial.println(F("readFloat()")); +#endif + + byte i, j; + + union + { + byte b[4]; + float f; + } readdata; + + for (i = 0; i < 4; i++) + { + j = SPI.transfer(0); + readdata.b[i] = j; + } + _RXPacketL = _RXPacketL + 4; //increment count of bytes read + return readdata.f; +} + + +uint8_t SX128XLT::transmitSXBuffer(uint8_t startaddr, uint8_t length, uint16_t timeout, int8_t txpower, uint8_t wait) +{ +#ifdef SX128XDEBUG + Serial.println(F("transmitSXBuffer()")); +#endif + + setBufferBaseAddress(startaddr, 0); //TX, RX + + setPacketParams(savedPacketParam1, savedPacketParam2, length, savedPacketParam4, savedPacketParam5, savedPacketParam6, savedPacketParam7); + setTxParams(txpower, RAMP_TIME); + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + setTx(timeout); //this starts the TX + + if (!wait) + { + return _TXPacketL; + } + + while (!digitalRead(_TXDonePin)); //Wait for DIO1 to go high + + if (readIrqStatus() & IRQ_RX_TX_TIMEOUT ) //check for timeout + { + return 0; + } + else + { + return _TXPacketL; + } +} + + +void SX128XLT::writeBuffer(uint8_t *txbuffer, uint8_t size) +{ +#ifdef SX128XDEBUG1 + Serial.println(F("writeBuffer()")); +#endif + + uint8_t index, regdata; + + _TXPacketL = _TXPacketL + size; //these are the number of bytes that will be added + + size--; //loose one byte from size, the last byte written MUST be a 0 + + for (index = 0; index < size; index++) + { + regdata = txbuffer[index]; + SPI.transfer(regdata); + } + + SPI.transfer(0); //this ensures last byte of buffer written really is a null (0) + +} + + +uint8_t SX128XLT::receiveSXBuffer(uint8_t startaddr, uint16_t timeout, uint8_t wait ) +{ +#ifdef SX127XDEBUG1 + Serial.println(F("receiveSXBuffer()")); +#endif + + uint16_t regdata; + uint8_t buffer[2]; + + setMode(MODE_STDBY_RC); + + setBufferBaseAddress(0, startaddr); //order is TX RX + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on RX done or timeout + + setRx(timeout); //no actual RX timeout in this function + + if (!wait) + { + return 0; + } + + while (!digitalRead(_RXDonePin)); //Wait for DIO1 to go high + + setMode(MODE_STDBY_RC); //ensure to stop further packet reception + + regdata = readIrqStatus(); + + if ( (regdata & IRQ_HEADER_ERROR) | (regdata & IRQ_CRC_ERROR) | (regdata & IRQ_RX_TX_TIMEOUT ) ) + { + return 0; //no RX done and header valid only, could be CRC error + } + + readCommand(RADIO_GET_RXBUFFERSTATUS, buffer, 2); + _RXPacketL = buffer[0]; + + return _RXPacketL; +} + + + +uint8_t SX128XLT::readBuffer(uint8_t *rxbuffer) +{ +#ifdef SX128XDEBUG1 + Serial.println(F("readBuffer()")); +#endif + + uint8_t index = 0, regdata; + + do //need to find the size of the buffer first + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; //fill the buffer. + index++; + } while (regdata != 0); //keep reading until we have reached the null (0) at the buffer end + //or exceeded size of buffer allowed + _RXPacketL = _RXPacketL + index; //increment count of bytes read + return index; //return the actual size of the buffer, till the null (0) detected + +} + + +void SX128XLT::setSyncWord1(uint32_t syncword) +{ +#ifdef SX128XDEBUG1 + Serial.println(F("setSyncWord1()")); +#endif + + //For FLRC packet type, the SyncWord is one byte shorter and + //the base address is shifted by one byte + writeRegister( REG_FLRCSYNCWORD1_BASEADDR, ( syncword >> 24 ) & 0x000000FF ); + writeRegister( REG_FLRCSYNCWORD1_BASEADDR + 1, ( syncword >> 16 ) & 0x000000FF ); + writeRegister( REG_FLRCSYNCWORD1_BASEADDR + 2, ( syncword >> 8 ) & 0x000000FF ); + writeRegister( REG_FLRCSYNCWORD1_BASEADDR + 3, syncword & 0x000000FF ); +} + + +/*************************************************************************** +//End direct access SX buffer routines +***************************************************************************/ + + + +//******************************************************************************* +//Start Ranging routines +//******************************************************************************* + + +void SX128XLT::setRangingSlaveAddress(uint32_t address) +{ +//sets address of ranging slave +#ifdef SX128XDEBUG + Serial.println(F("SetRangingSlaveAddress()")); +#endif + + uint8_t buffer[4]; + + buffer[0] = (address >> 24u ) & 0xFFu; + buffer[1] = (address >> 16u) & 0xFFu; + buffer[2] = (address >> 8u) & 0xFFu; + buffer[3] = (address & 0xFFu); + + writeRegisters(0x916, buffer, 4 ); +} + + +void SX128XLT::setRangingMasterAddress(uint32_t address) +{ +//sets address of ranging master +#ifdef SX128XDEBUG + Serial.println(F("SetRangingMasterAddress()")); +#endif + + uint8_t buffer[4]; + + buffer[0] = (address >> 24u ) & 0xFFu; + buffer[1] = (address >> 16u) & 0xFFu; + buffer[2] = (address >> 8u) & 0xFFu; + buffer[3] = (address & 0xFFu); + + writeRegisters(0x912, buffer, 4 ); +} + + +void SX128XLT::setRangingCalibration(uint16_t cal) +{ + #ifdef SX128XDEBUG + Serial.println(F("setRangingCalibration()")); +#endif + savedCalibration = cal; + writeRegister( REG_LR_RANGINGRERXTXDELAYCAL, ( uint8_t )( ( cal >> 8 ) & 0xFF ) ); + writeRegister( REG_LR_RANGINGRERXTXDELAYCAL + 1, ( uint8_t )( ( cal ) & 0xFF ) ); +} + + +void SX128XLT::setRangingRole(uint8_t role) +{ +#ifdef SX128XDEBUG + Serial.println(F("setRangingRole()")); +#endif + + uint8_t buffer[1]; + + buffer[0] = role; + + writeCommand(RADIO_SET_RANGING_ROLE, buffer, 1 ); +} + + +uint32_t SX128XLT::getRangingResultRegValue(uint8_t resultType) +{ + uint32_t valLsb = 0; + + setMode(MODE_STDBY_XOSC); + writeRegister( 0x97F, readRegister( 0x97F ) | ( 1 << 1 ) ); // enable LORA modem clock + writeRegister( REG_LR_RANGINGRESULTCONFIG, ( readRegister( REG_LR_RANGINGRESULTCONFIG ) & MASK_RANGINGMUXSEL ) | ( ( ( ( uint8_t )resultType ) & 0x03 ) << 4 ) ); + valLsb = ( ( (uint32_t) readRegister( REG_LR_RANGINGRESULTBASEADDR ) << 16 ) | ( (uint32_t) readRegister( REG_LR_RANGINGRESULTBASEADDR + 1 ) << 8 ) | ( readRegister( REG_LR_RANGINGRESULTBASEADDR + 2 ) ) ); + setMode(MODE_STDBY_RC); + return valLsb; +} + + +double SX128XLT::getRangingDistance(uint8_t resultType, int32_t regval, float adjust) +{ + float val = 0.0; + + if (regval >= 0x800000) //raw reg value at low distance can goto 0x800000 which is negative, set distance to zero if this happens + { + regval = 0; + } + + // Conversion from LSB to distance. For explanation on the formula, refer to Datasheet of SX1280 + + switch (resultType) + { + case RANGING_RESULT_RAW: + // Convert the ranging LSB to distance in meter. The theoretical conversion from register value to distance [m] is given by: + // distance [m] = ( complement2( register ) * 150 ) / ( 2^12 * bandwidth[MHz] ) ). The API provide BW in [Hz] so the implemented + // formula is complement2( register ) / bandwidth[Hz] * A, where A = 150 / (2^12 / 1e6) = 36621.09 + val = ( double ) regval / ( double ) returnBandwidth(savedModParam2) * 36621.09375; + break; + + case RANGING_RESULT_AVERAGED: + case RANGING_RESULT_DEBIASED: + case RANGING_RESULT_FILTERED: + Serial.print(F("??")); + val = ( double )regval * 20.0 / 100.0; + break; + default: + val = 0.0; + break; + } + + val = val * adjust; + return val; +} + + +bool SX128XLT::setupRanging(uint32_t frequency, int32_t offset, uint8_t modParam1, uint8_t modParam2, uint8_t modParam3, uint32_t address, uint8_t role) +{ + //sequence is frequency, offset, spreading factor, bandwidth, coding rate, calibration, role. + #ifdef SX128XDEBUG + Serial.println(F("setupRanging()")); +#endif + + setMode(MODE_STDBY_RC); + setPacketType(PACKET_TYPE_RANGING); + setModulationParams(modParam1, modParam2, modParam3); + setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 0, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0); + setRfFrequency(frequency, offset); + setRangingSlaveAddress(address); + setRangingMasterAddress(address); + setRangingCalibration(lookupCalibrationValue(modParam1, modParam2)); + setRangingRole(role); + setHighSensitivity(); + return true; +} + + + +bool SX128XLT::transmitRanging(uint32_t address, uint16_t timeout, int8_t txpower, uint8_t wait) +{ +#ifdef SX128XDEBUG + Serial.println(F("transmitRanging()")); +#endif + + if ((_RXEN >= 0) || (_TXEN >= 0)) + { + return false; + } + + setMode(MODE_STDBY_RC); + setRangingMasterAddress(address); + setTxParams(txpower, RADIO_RAMP_02_US); + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RANGING_MASTER_RESULT_VALID + IRQ_RANGING_MASTER_RESULT_TIMEOUT), 0, 0); + setTx(timeout); //this sends the ranging packet + + if (!wait) + { + return true; + } + + while (!digitalRead(_TXDonePin)); //Wait for DIO1 to go high + + if (readIrqStatus() & IRQ_RANGING_MASTER_RESULT_VALID ) //check for timeout + { + return true; + } + else + { + return false; + } +} + + +uint8_t SX128XLT::receiveRanging(uint32_t address, uint16_t timeout, int8_t txpower, uint8_t wait) +{ +#ifdef SX128XDEBUG + Serial.println(F("receiveRanging()")); +#endif + + setTxParams(txpower, RADIO_RAMP_02_US); + setRangingSlaveAddress(address); + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RANGING_SLAVE_RESPONSE_DONE + IRQ_RANGING_SLAVE_REQUEST_DISCARDED), 0, 0); + setRx(timeout); + + if (!wait) + { + return NO_WAIT; //not wait requested so no packet length to pass + } + + while (!digitalRead(_RXDonePin)); + + setMode(MODE_STDBY_RC); //ensure to stop further packet reception + + if (readIrqStatus() & IRQ_RANGING_SLAVE_REQUEST_VALID) + { + return true; + } + else + { + return false; //so we can check for packet having enough buffer space + } +} + + +uint16_t SX128XLT::lookupCalibrationValue(uint8_t spreadingfactor, uint8_t bandwidth) +{ +//this looks up the calibration value from the table in SX128XLT_Definitions.hifdef SX128XDEBUG +#ifdef SX128XDEBUG +Serial.println(F("lookupCalibrationValue()")); +#endif + + +switch (bandwidth) + { + case LORA_BW_0400: + savedCalibration = RNG_CALIB_0400[(spreadingfactor>>4)-5]; + return savedCalibration; + + case LORA_BW_0800: + savedCalibration = RNG_CALIB_0800[(spreadingfactor>>4)-5]; + return savedCalibration; + + + case LORA_BW_1600: + savedCalibration = RNG_CALIB_1600[(spreadingfactor>>4)-5]; + return savedCalibration; + + default: + return 0xFFFF; + + } + +} + + +uint16_t SX128XLT::getSetCalibrationValue() +{ +#ifdef SX128XDEBUG + Serial.println(F("getCalibrationValue()")); +#endif + +return savedCalibration;; + +} + + +//******************************************************************************* +//End Ranging routines +//******************************************************************************* + + +void SX128XLT::setSleep(uint8_t sleepconfig) +{ +#ifdef SX128XDEBUG + Serial.println(F("setSleep()")); +#endif + + setMode(MODE_STDBY_RC); + checkBusy(); + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + //need to save registers to device RAM first + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_SET_SAVECONTEXT); + digitalWrite(_NSS, HIGH); + + checkBusy(); + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_SET_SLEEP); + SPI.transfer(sleepconfig); + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + delay(1); //allow time for shutdown +} + + +uint16_t SX128XLT::CRCCCITTSX(uint8_t startadd, uint8_t endadd, uint16_t startvalue) +{ + //genrates a CRC of an area of the internal SX buffer + +#ifdef SX126XDEBUG1 + Serial.println(F("CRCCCITTSX()")); +#endif + + + uint16_t index, libraryCRC; + uint8_t j; + + libraryCRC = startvalue; //start value for CRC16 + + startReadSXBuffer(startadd); //begin the buffer read + + for (index = startadd; index <= endadd; index++) + { + libraryCRC ^= (((uint16_t) readUint8() ) << 8); + for (j = 0; j < 8; j++) + { + if (libraryCRC & 0x8000) + libraryCRC = (libraryCRC << 1) ^ 0x1021; + else + libraryCRC <<= 1; + } + } + + endReadSXBuffer(); //end the buffer read + + return libraryCRC; +} + + + +uint8_t SX128XLT::getByteSXBuffer(uint8_t addr) +{ +#ifdef SX128XDEBUG1 + Serial.println(F("getByteSXBuffer()")); +#endif + + uint8_t regdata; + setMode(MODE_STDBY_RC); //this is needed to ensure we can read from buffer OK. + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(addr); + SPI.transfer(0xFF); + regdata = SPI.transfer(0); + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return regdata; +} + + +void SX128XLT::printSXBufferHEX(uint8_t start, uint8_t end) +{ +#ifdef SX128XDEBUG + Serial.println(F("printSXBufferHEX()")); +#endif + + uint8_t index, regdata; + + setMode(MODE_STDBY_RC); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(start); + SPI.transfer(0xFF); + + for (index = start; index <= end; index++) + { + regdata = SPI.transfer(0); + printHEXByte(regdata); + Serial.print(F(" ")); + + } + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + +} + + +void SX128XLT::printHEXByte(uint8_t temp) +{ + if (temp < 0x10) + { + Serial.print(F("0")); + } + Serial.print(temp, HEX); +} + + +void SX128XLT::wake() +{ +#ifdef SX128XDEBUG + Serial.println(F("wake()")); +#endif + +digitalWrite(_NSS, LOW); +delay(1); +digitalWrite(_NSS, HIGH); +delay(1); +} + + +int32_t SX128XLT::getFrequencyErrorRegValue() +{ + #ifdef SX128XDEBUG + Serial.println(F("getFrequencyErrorRegValue()")); +#endif + + int32_t FrequencyError; + uint32_t regmsb, regmid, reglsb, allreg; + + setMode(MODE_STDBY_XOSC); + + regmsb = readRegister( REG_LR_ESTIMATED_FREQUENCY_ERROR_MSB ); + regmsb = regmsb & 0x0F; //clear bit 20 which is always set + + regmid = readRegister( REG_LR_ESTIMATED_FREQUENCY_ERROR_MSB + 1 ); + + reglsb = readRegister( REG_LR_ESTIMATED_FREQUENCY_ERROR_MSB + 2 ); + setMode(MODE_STDBY_RC); + + #ifdef LORADEBUG + Serial.println(); + Serial.print(F("Registers ")); + Serial.print(regmsb,HEX); + Serial.print(F(" ")); + Serial.print(regmid,HEX); + Serial.print(F(" ")); + Serial.println(reglsb,HEX); + #endif + + allreg = (uint32_t) ( regmsb << 16 ) | ( regmid << 8 ) | reglsb; + + if (allreg & 0x80000) + { + FrequencyError = (0xFFFFF - allreg) * -1; + } + else + { + FrequencyError = allreg; + } + + return FrequencyError; +} + + +int32_t SX128XLT::getFrequencyErrorHz() +{ + #ifdef SX128XDEBUG + Serial.println(F("getFrequencyErrorHz()")); +#endif + + int32_t error, regvalue; + uint32_t bandwidth; + float divider; + + bandwidth = returnBandwidth(savedModParam2); //gets the last configured bandwidth + + divider = (float) 1625000 / bandwidth; //data sheet says 1600000, but bandwidth is 1625000 + regvalue = getFrequencyErrorRegValue(); + error = (FREQ_ERROR_CORRECTION * regvalue) / divider; + + return error; +} + +uint8_t SX128XLT::transmitAddressed(uint8_t *txbuffer, uint8_t size, char txpackettype, char txdestination, char txsource, uint32_t timeout, int8_t txpower, uint8_t wait) +{ +#ifdef SX128XDEBUG + Serial.println(F("transmitAddressed()")); +#endif + uint8_t index; + uint8_t bufferdata; + + if (size == 0) + { + return false; + } + + setMode(MODE_STDBY_RC); + setBufferBaseAddress(0, 0); + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); + SPI.transfer(RADIO_WRITE_BUFFER); + SPI.transfer(0); + + SPI.transfer(txpackettype); //Write the packet type + SPI.transfer(txdestination); //Destination node + SPI.transfer(txsource); //Source node + _TXPacketL = 3 + size; //we have added 3 header bytes to size + + for (index = 0; index < size; index++) + { + bufferdata = txbuffer[index]; + SPI.transfer(bufferdata); + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + + if (savedPacketType == PACKET_TYPE_LORA) + { + writeRegister(REG_LR_PAYLOADLENGTH, _TXPacketL); //only seems to work for lora + } + else if (savedPacketType == PACKET_TYPE_FLRC) + { + setPacketParams(savedPacketParam1, savedPacketParam2, savedPacketParam3, savedPacketParam4, _TXPacketL, savedPacketParam6, savedPacketParam7); + } + + setTxParams(txpower, RAMP_TIME); + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1 + setTx(timeout); //this starts the TX + + if (!wait) + { + return _TXPacketL; + } + + while (!digitalRead(_TXDonePin)); //Wait for DIO1 to go high + + if (readIrqStatus() & IRQ_RX_TX_TIMEOUT ) //check for timeout + { + return 0; + } + else + { + return _TXPacketL; + } +} + + +uint8_t SX128XLT::receiveAddressed(uint8_t *rxbuffer, uint8_t size, uint16_t timeout, uint8_t wait) +{ +#ifdef SX128XDEBUG + Serial.println(F("receiveAddressed()")); +#endif + + uint8_t index, RXstart, RXend; + uint16_t regdata; + uint8_t buffer[2]; + + setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on RX done or timeout + setRx(timeout); + + if (!wait) + { + return 0; //not wait requested so no packet length to pass + } + + while (!digitalRead(_RXDonePin)); //Wait for DIO1 to go high + + setMode(MODE_STDBY_RC); //ensure to stop further packet reception + + regdata = readIrqStatus(); + + if ( (regdata & IRQ_HEADER_ERROR) | (regdata & IRQ_CRC_ERROR) | (regdata & IRQ_RX_TX_TIMEOUT ) ) //check if any of the preceding IRQs is set + { + return 0; //packet is errored somewhere so return 0 + } + + readCommand(RADIO_GET_RXBUFFERSTATUS, buffer, 2); + _RXPacketL = buffer[0]; + + if (_RXPacketL > size) //check passed buffer is big enough for packet + { + _RXPacketL = size; //truncate packet if not enough space + } + + RXstart = buffer[1]; + + RXend = RXstart + _RXPacketL; + + checkBusy(); + +#ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(RXstart); + SPI.transfer(0xFF); + + _RXPacketType = SPI.transfer(0); + _RXDestination = SPI.transfer(0); + _RXSource = SPI.transfer(0); + + for (index = RXstart; index < RXend; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; + } + + digitalWrite(_NSS, HIGH); + +#ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + +uint8_t SX128XLT::readRXPacketType() +{ +#ifdef SX128XDEBUG + Serial.println(F("readRXPacketType()")); +#endif +return _RXPacketType; +} + + +uint8_t SX128XLT::readPacket(uint8_t *rxbuffer, uint8_t size) +{ +#ifdef SX128XDEBUG + Serial.println(F("readPacket()")); +#endif + + uint8_t index, regdata, RXstart, RXend; + uint8_t buffer[2]; + + readCommand(RADIO_GET_RXBUFFERSTATUS, buffer, 2); + _RXPacketL = buffer[0]; + + if (_RXPacketL > size) //check passed buffer is big enough for packet + { + _RXPacketL = size; //truncate packet if not enough space + } + + RXstart = buffer[1]; + + RXend = RXstart + _RXPacketL; + + #ifdef USE_SPI_TRANSACTION //to use SPI_TRANSACTION enable define at beginning of CPP file + SPI.beginTransaction(SPISettings(LTspeedMaximum, LTdataOrder, LTdataMode)); +#endif + + digitalWrite(_NSS, LOW); //start the burst read + SPI.transfer(RADIO_READ_BUFFER); + SPI.transfer(RXstart); + SPI.transfer(0xFF); + + for (index = RXstart; index < RXend; index++) + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; + } + + digitalWrite(_NSS, HIGH); + + #ifdef USE_SPI_TRANSACTION + SPI.endTransaction(); +#endif + + return _RXPacketL; //so we can check for packet having enough buffer space +} + + +uint16_t SX128XLT::addCRC(uint8_t data, uint16_t libraryCRC) +{ + uint8_t j; + + libraryCRC ^= ((uint16_t)data << 8); + for (j = 0; j < 8; j++) + { + if (libraryCRC & 0x8000) + libraryCRC = (libraryCRC << 1) ^ 0x1021; + else + libraryCRC <<= 1; + } + return libraryCRC; +} + + +void SX128XLT::writeBufferChar(char *txbuffer, uint8_t size) +{ +#ifdef SX128XDEBUG1 + Serial.println(F("writeBuffer()")); +#endif + + uint8_t index, regdata; + + _TXPacketL = _TXPacketL + size; //these are the number of bytes that will be added + + size--; //loose one byte from size, the last byte written MUST be a 0 + + for (index = 0; index < size; index++) + { + regdata = txbuffer[index]; + SPI.transfer(regdata); + } + + SPI.transfer(0); //this ensures last byte of buffer writen really is a null (0) + +} + + +uint8_t SX128XLT::readBufferChar(char *rxbuffer) +{ +#ifdef SX128XDEBUG1 + Serial.println(F("readBuffer()")); +#endif + + uint8_t index = 0, regdata; + + do //need to find the size of the buffer first + { + regdata = SPI.transfer(0); + rxbuffer[index] = regdata; //fill the buffer. + index++; + } while (regdata != 0); //keep reading until we have reached the null (0) at the buffer end + //or exceeded size of buffer allowed + + _RXPacketL = _RXPacketL + index; //increment count of bytes read + + return index; //return the actual size of the buffer, till the null (0) detected + +} + + + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + + + + + diff --git a/lib/SX12XX-LoRa/src/SX128XLT.h b/lib/SX12XX-LoRa/src/SX128XLT.h new file mode 100644 index 0000000..b58b1b2 --- /dev/null +++ b/lib/SX12XX-LoRa/src/SX128XLT.h @@ -0,0 +1,197 @@ +#ifndef SX128XLT_h +#define SX128XLT_h + +#include "Arduino.h" +#include + +/************************************************************************** + + ToDO + + DONE - Why is SX1280LT.setPacketType(PACKET_TYPE_LORA) required before getFreqInt works with FLRC + - The register addresses where the frequency is stored are different for FLRC and LORA + DONE - Checkbusy at end of setmode ? - not needed, Checkbusy before all SPI activity + DONE - Ranging in complement2 - warning: comparison between signed and unsigned integer + DONE - Trap use of devices with RX\TX switching in ranging mode + DONE - Ensure ranging distance is not negative + + + Add routine to change period_base for RX,TX timeout + Is there a direct register access to packet length for transmit ? + Test RSSI and SNR are realistic for LoRa and FLRC + Review error rate in FLRC mode + Error packets at -99dBm due to noise ? + Add support for printPacketStatus for FLRC + +**************************************************************************/ + +class SX128XLT { + + public: + + SX128XLT(); + + bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinDIO2, int8_t pinDIO3, int8_t pinRXEN, int8_t pinTXEN, uint8_t device); + bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, uint8_t device); + bool begin(int8_t pinNSS, int8_t pinNRESET, int8_t pinRFBUSY, int8_t pinDIO1, int8_t pinRXEN, int8_t pinTXEN, uint8_t device); + + void rxEnable(); + void txEnable(); + + void checkBusy(); + bool config(); + void readRegisters( uint16_t address, uint8_t *buffer, uint16_t size ); + uint8_t readRegister( uint16_t address ); + void writeRegisters( uint16_t address, uint8_t *buffer, uint16_t size ); + void writeRegister( uint16_t address, uint8_t value ); + void writeCommand(uint8_t Opcode, uint8_t *buffer, uint16_t size ); + void readCommand( uint8_t Opcode, uint8_t *buffer, uint16_t size ); + void resetDevice(); + bool checkDevice(); + void setupLoRa(uint32_t frequency, int32_t offset, uint8_t modParam1, uint8_t modParam2, uint8_t modParam3); + void setMode(uint8_t modeconfig); + void setRegulatorMode(uint8_t mode); + void setPacketType(uint8_t PacketType); + void setRfFrequency( uint32_t frequency, int32_t offset ); + void setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress); + void setModulationParams(uint8_t modParam1, uint8_t modParam2, uint8_t modParam3); + void setPacketParams(uint8_t packetParam1, uint8_t packetParam2, uint8_t packetParam3, uint8_t packetParam4, uint8_t packetParam5, uint8_t packetParam6, uint8_t packetParam7); + void setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask ); + void setHighSensitivity(); + void setLowPowerRX(); + void printModemSettings(); + void printDevice(); + uint32_t getFreqInt(); + uint8_t getLoRaSF(); + uint32_t returnBandwidth(uint8_t data); + uint8_t getLoRaCodingRate(); + uint8_t getInvertIQ(); + uint16_t getPreamble(); + void printOperatingSettings(); + uint8_t getLNAgain(); + void printRegisters(uint16_t Start, uint16_t End); + void printASCIIPacket(uint8_t *buff, uint8_t tsize); + uint8_t transmit(uint8_t *txbuffer, uint8_t size, uint16_t timeout, int8_t txpower, uint8_t wait); + void setTxParams(int8_t TXpower, uint8_t RampTime); + void setTx(uint16_t timeout); + void clearIrqStatus( uint16_t irq ); + uint16_t readIrqStatus(); + void printIrqStatus(); + uint16_t CRCCCITT(uint8_t *buffer, uint8_t size, uint16_t start); + uint8_t receive(uint8_t *rxbuffer, uint8_t size, uint16_t timeout, uint8_t wait); + uint8_t readPacketRSSI(); + uint8_t readPacketSNR(); + uint8_t readRXPacketL(); + void setRx(uint16_t timeout); + void setSyncWord1(uint32_t syncword); + void setSleep(uint8_t sleepconfig); + uint16_t CRCCCITTSX(uint8_t startadd, uint8_t endadd, uint16_t startvalue); + uint8_t getByteSXBuffer(uint8_t addr); + int32_t getFrequencyErrorRegValue(); + int32_t getFrequencyErrorHz(); + void printHEXByte(uint8_t temp); + void wake() ; + uint8_t transmitAddressed(uint8_t *txbuffer, uint8_t size, char txpackettype, char txdestination, char txsource, uint32_t timeout, int8_t txpower, uint8_t wait); + uint8_t receiveAddressed(uint8_t *rxbuffer, uint8_t size, uint16_t timeout, uint8_t wait); + uint8_t readRXPacketType(); + uint8_t readPacket(uint8_t *rxbuffer, uint8_t size); +/********************************************************** +***************** +//Start direct access SX buffer routines +***************************************************************************/ + + void startWriteSXBuffer(uint8_t ptr); + uint8_t endWriteSXBuffer(); + void startReadSXBuffer(uint8_t ptr); + uint8_t endReadSXBuffer(); + + void writeUint8(uint8_t x); + uint8_t readUint8(); + + void writeInt8(int8_t x); + int8_t readInt8(); + + void writeInt16(int16_t x); + int16_t readInt16(); + + void writeUint16(uint16_t x); + uint16_t readUint16(); + + void writeInt32(int32_t x); + int32_t readInt32(); + + void writeUint32(uint32_t x); + uint32_t readUint32(); + + void writeFloat(float x); + float readFloat(); + + uint8_t transmitSXBuffer(uint8_t startaddr, uint8_t length, uint16_t timeout, int8_t txpower, uint8_t wait); + void writeBuffer(uint8_t *txbuffer, uint8_t size); + uint8_t receiveSXBuffer(uint8_t startaddr, uint16_t timeout, uint8_t wait); + uint8_t readBuffer(uint8_t *rxbuffer); + void printSXBufferHEX(uint8_t start, uint8_t end); + uint16_t addCRC(uint8_t data, uint16_t libraryCRC); + void writeBufferChar(char *txbuffer, uint8_t size); + uint8_t readBufferChar(char *rxbuffer); +/*************************************************************************** +//End direct access SX buffer routines +***************************************************************************/ + + +/*************************************************************************** +//Start ranging routines +***************************************************************************/ + + + void setRangingSlaveAddress(uint32_t address); + void setRangingMasterAddress(uint32_t address); + void setRangingCalibration(uint16_t cal); + void setRangingRole(uint8_t role); + double getRangingDistance(uint8_t resultType, int32_t regval, float adjust); + uint32_t getRangingResultRegValue(uint8_t resultType); + int32_t complement2( uint32_t num, uint8_t bitCnt ); + bool setupRanging(uint32_t frequency, int32_t offset, uint8_t modParam1, uint8_t modParam2, uint8_t modParam3, uint32_t address, uint8_t role); + bool transmitRanging(uint32_t address, uint16_t timeout, int8_t txpower, uint8_t wait); + uint8_t receiveRanging(uint32_t address, uint16_t timeout, int8_t txpower, uint8_t wait); + uint16_t lookupCalibrationValue(uint8_t spreadingfactor, uint8_t bandwidth); + uint16_t getSetCalibrationValue(); + +/*************************************************************************** +//End ranging routines +***************************************************************************/ + + private: + + int8_t _NSS, _NRESET, _RFBUSY, _DIO1, _DIO2, _DIO3; + int8_t _RXEN, _TXEN; + uint8_t _RXPacketL; //length of packet received + uint8_t _RXPacketType; //type number of received packet + uint8_t _RXDestination; //destination address of received packet + uint8_t _RXSource; //source address of received packet + int8_t _PacketRSSI; //RSSI of received packet + int8_t _PacketSNR; //signal to noise ratio of received packet + int8_t _TXPacketL; //transmitted packet length + uint8_t _RXcount; //used to keep track of the bytes read from SX1280 buffer during readFloat() etc + uint8_t _TXcount; //used to keep track of the bytes written to SX1280 buffer during writeFloat() etc + uint8_t _OperatingMode; //current operating mode + bool _rxtxpinmode = false; //set to true if RX and TX pin mode is used. + + uint8_t _Device; //saved device type + uint8_t _TXDonePin; //the pin that will indicate TX done + uint8_t _RXDonePin; //the pin that will indicate RX done + uint8_t _PERIODBASE = PERIODBASE_01_MS; + + uint8_t savedRegulatorMode; + uint8_t savedPacketType; + uint32_t savedFrequency; + int32_t savedOffset; + uint8_t savedModParam1, savedModParam2, savedModParam3; //sequence is spreading factor, bandwidth, coding rate + uint8_t savedPacketParam1, savedPacketParam2, savedPacketParam3, savedPacketParam4, savedPacketParam5, savedPacketParam6, savedPacketParam7; + uint16_t savedIrqMask, savedDio1Mask, savedDio2Mask, savedDio3Mask; + int8_t savedTXPower; + uint16_t savedCalibration; + uint32_t savedFrequencyReg; + +}; +#endif diff --git a/lib/SX12XX-LoRa/src/SX128XLT_Definitions.h b/lib/SX12XX-LoRa/src/SX128XLT_Definitions.h new file mode 100644 index 0000000..d3e527c --- /dev/null +++ b/lib/SX12XX-LoRa/src/SX128XLT_Definitions.h @@ -0,0 +1,388 @@ +//SX1280LT Includes + + +//************************************************************* +// LoRa Modem Settings +//************************************************************* +// +//LoRa spreading factors +#define LORA_SF5 0x50 +#define LORA_SF6 0x60 +#define LORA_SF7 0x70 +#define LORA_SF8 0x80 +#define LORA_SF9 0x90 +#define LORA_SF10 0xA0 +#define LORA_SF11 0xB0 +#define LORA_SF12 0xC0 + +//LoRa bandwidths +#define LORA_BW_0200 0x34 //actually 203125hz +#define LORA_BW_0400 0x26 //actually 406250hz +#define LORA_BW_0800 0x18 //actually 812500hz +#define LORA_BW_1600 0x0A //actually 1625000hz + +//LoRa coding rates +#define LORA_CR_4_5 0x01 +#define LORA_CR_4_6 0x02 +#define LORA_CR_4_7 0x03 +#define LORA_CR_4_8 0x04 + +//LoRa CAD settings +#define LORA_CAD_01_SYMBOL 0x00 +#define LORA_CAD_02_SYMBOL 0x20 +#define LORA_CAD_04_SYMBOL 0x40 +#define LORA_CAD_08_SYMBOL 0x60 +#define LORA_CAD_16_SYMBOL 0x80 + +//LoRa Header Types +#define LORA_PACKET_VARIABLE_LENGTH 0x00 +#define LORA_PACKET_FIXED_LENGTH 0x80 +#define LORA_PACKET_EXPLICIT LORA_PACKET_VARIABLE_LENGTH +#define LORA_PACKET_IMPLICIT LORA_PACKET_FIXED_LENGTH + +//LoRa packet CRC settings +#define LORA_CRC_ON 0x20 +#define LORA_CRC_OFF 0x00 + +//LoRa IQ Setings +#define LORA_IQ_NORMAL 0x40 +#define LORA_IQ_INVERTED 0x00 + + +#define FREQ_STEP 198.364 +#define FREQ_ERROR_CORRECTION 1.55 + + + + +//************************************************************* +// SX1280 Interrupt flags +//************************************************************* + +#define IRQ_RADIO_NONE 0x0000 +#define IRQ_TX_DONE 0x0001 +#define IRQ_RX_DONE 0x0002 +#define IRQ_SYNCWORD_VALID 0x0004 +#define IRQ_SYNCWORD_ERROR 0x0008 +#define IRQ_HEADER_VALID 0x0010 +#define IRQ_HEADER_ERROR 0x0020 +#define IRQ_CRC_ERROR 0x0040 +#define IRQ_RANGING_SLAVE_RESPONSE_DONE 0x0080 + +#define IRQ_RANGING_SLAVE_REQUEST_DISCARDED 0x0100 +#define IRQ_RANGING_MASTER_RESULT_VALID 0x0200 +#define IRQ_RANGING_MASTER_RESULT_TIMEOUT 0x0400 +#define IRQ_RANGING_SLAVE_REQUEST_VALID 0x0800 +#define IRQ_CAD_DONE 0x1000 +#define IRQ_CAD_ACTIVITY_DETECTED 0x2000 +#define IRQ_RX_TX_TIMEOUT 0x4000 +#define IRQ_TX_TIMEOUT 0x4000 +#define IRQ_RX_TIMEOUT 0x4000 +#define IRQ_PREAMBLE_DETECTED 0x8000 +#define IRQ_RADIO_ALL 0xFFFF + + +//************************************************************* +// SX1280 Commands +//************************************************************* + +#define RADIO_GET_PACKETTYPE 0x03 +#define RADIO_GET_IRQSTATUS 0x15 +#define RADIO_GET_RXBUFFERSTATUS 0x17 +#define RADIO_WRITE_REGISTER 0x18 +#define RADIO_READ_REGISTER 0x19 +#define RADIO_WRITE_BUFFER 0x1A +#define RADIO_READ_BUFFER 0x1B +#define RADIO_GET_PACKETSTATUS 0x1D +#define RADIO_GET_RSSIINST 0x1F +#define RADIO_SET_STANDBY 0x80 +#define RADIO_SET_RX 0x82 +#define RADIO_SET_TX 0x83 +#define RADIO_SET_SLEEP 0x84 +#define RADIO_SET_RFFREQUENCY 0x86 +#define RADIO_SET_CADPARAMS 0x88 +#define RADIO_CALIBRATE 0x89 +#define RADIO_SET_PACKETTYPE 0x8A +#define RADIO_SET_MODULATIONPARAMS 0x8B +#define RADIO_SET_PACKETPARAMS 0x8C +#define RADIO_SET_DIOIRQPARAMS 0x8D +#define RADIO_SET_TXPARAMS 0x8E +#define RADIO_SET_BUFFERBASEADDRESS 0x8F +#define RADIO_SET_RXDUTYCYCLE 0x94 +#define RADIO_SET_REGULATORMODE 0x96 +#define RADIO_CLR_IRQSTATUS 0x97 +#define RADIO_SET_AUTOTX 0x98 +#define RADIO_SET_LONGPREAMBLE 0x9B +#define RADIO_SET_UARTSPEED 0x9D +#define RADIO_SET_AUTOFS 0x9E +#define RADIO_SET_RANGING_ROLE 0xA3 +#define RADIO_GET_STATUS 0xC0 +#define RADIO_SET_FS 0xC1 +#define RADIO_SET_CAD 0xC5 +#define RADIO_SET_TXCONTINUOUSWAVE 0xD1 +#define RADIO_SET_TXCONTINUOUSPREAMBLE 0xD2 +#define RADIO_SET_SAVECONTEXT 0xD5 + + +//************************************************************* +// SX1280 Registers +//************************************************************* + +#define REG_LNA_REGIME 0x0891 +#define REG_LR_PAYLOADLENGTH 0x901 +#define REG_LR_PACKETPARAMS 0x903 + +#define REG_RFFrequency23_16 0x906 +#define REG_RFFrequency15_8 0x907 +#define REG_RFFrequency7_0 0x908 + +#define REG_FLRC_RFFrequency23_16 0x9A3 //found by experiment +#define REG_FLRC_RFFrequency15_8 0x9A4 +#define REG_FLRC_RFFrequency7_0 0x9A5 + +#define REG_RANGING_FILTER_WINDOW_SIZE 0x091E +#define REG_LR_DEVICERANGINGADDR 0x0916 +#define REG_LR_DEVICERANGINGADDR 0x0916 +#define REG_LR_RANGINGRESULTCONFIG 0x0924 +#define REG_LR_RANGINGRERXTXDELAYCAL 0x092C +#define REG_LR_RANGINGIDCHECKLENGTH 0x0931 +#define REG_LR_ESTIMATED_FREQUENCY_ERROR_MSB 0x954 +#define REG_LR_ESTIMATED_FREQUENCY_ERROR_MID 0x955 +#define REG_LR_ESTIMATED_FREQUENCY_ERROR_LSB 0x956 +#define REG_LR_RANGINGRESULTBASEADDR 0x0961 +#define REG_LR_SYNCWORDTOLERANCE 0x09CD +#define REG_LR_SYNCWORDBASEADDRESS1 0x09CE +#define REG_FLRCSYNCWORD1_BASEADDR 0x09CF +#define REG_LR_SYNCWORDBASEADDRESS2 0x09D3 +#define REG_FLRCSYNCWORD2_BASEADDR 0x09D4 +#define REG_LR_SYNCWORDBASEADDRESS3 0x09D8 + +#define REG_LR_ESTIMATED_FREQUENCY_ERROR_MASK 0x0FFFFF + +//SX1280 Packet Types +#define PACKET_TYPE_GFSK 0x00 +#define PACKET_TYPE_LORA 0x01 +#define PACKET_TYPE_RANGING 0x02 +#define PACKET_TYPE_FLRC 0x03 +#define PACKET_TYPE_BLE 0x04 + +//SX1280 Standby modes +#define MODE_STDBY_RC 0x00 +#define MODE_STDBY_XOSC 0x01 + +//TX and RX timeout based periods +#define PERIODBASE_15_US 0x00 +#define PERIODBASE_62_US 0x01 +#define PERIODBASE_01_MS 0x02 +#define PERIODBASE_04_MS 0x03 + +//TX ramp periods +#define RADIO_RAMP_02_US 0x00 +#define RADIO_RAMP_04_US 0x20 +#define RADIO_RAMP_06_US 0x40 +#define RADIO_RAMP_08_US 0x60 +#define RADIO_RAMP_10_US 0x80 +#define RADIO_RAMP_12_US 0xA0 +#define RADIO_RAMP_16_US 0xC0 +#define RADIO_RAMP_20_US 0xE0 + +//SX1280 Power settings +#define USE_LDO 0x00 +#define USE_DCDC 0x01 + + +//************************************************************* +//SX1280 Ranging settings +//************************************************************* + +#define RANGING_IDCHECK_LENGTH_08_BITS 0x00 +#define RANGING_IDCHECK_LENGTH_16_BITS 0x01 +#define RANGING_IDCHECK_LENGTH_24_BITS 0x02 +#define RANGING_IDCHECK_LENGTH_32_BITS 0x03 + +#define RANGING_RESULT_RAW 0x00 +#define RANGING_RESULT_AVERAGED 0x01 +#define RANGING_RESULT_DEBIASED 0x02 +#define RANGING_RESULT_FILTERED 0x03 + + +#define MASK_RANGINGMUXSEL 0xCF + +#define RANGING_SLAVE 0x00 +#define RANGING_MASTER 0x01 + + +//************************************************************* +//GFSK modem settings +//************************************************************* + +#define GFS_BLE_BR_2_000_BW_2_4 0x04 +#define GFS_BLE_BR_1_600_BW_2_4 0x28 +#define GFS_BLE_BR_1_000_BW_2_4 0x4C +#define GFS_BLE_BR_1_000_BW_1_2 0x45 +#define GFS_BLE_BR_0_800_BW_2_4 0x70 +#define GFS_BLE_BR_0_800_BW_1_2 0x69 +#define GFS_BLE_BR_0_500_BW_1_2 0x8D +#define GFS_BLE_BR_0_500_BW_0_6 0x86 +#define GFS_BLE_BR_0_400_BW_1_2 0xB1 +#define GFS_BLE_BR_0_400_BW_0_6 0xAA +#define GFS_BLE_BR_0_250_BW_0_6 0xCE +#define GFS_BLE_BR_0_250_BW_0_3 0xC7 +#define GFS_BLE_BR_0_125_BW_0_3 0xEF + +#define GFS_BLE_MOD_IND_0_35 0 +#define GFS_BLE_MOD_IND_0_50 1 +#define GFS_BLE_MOD_IND_0_75 2 +#define GFS_BLE_MOD_IND_1_00 3 +#define GFS_BLE_MOD_IND_1_25 4 +#define GFS_BLE_MOD_IND_1_50 5 +#define GFS_BLE_MOD_IND_1_75 6 +#define GFS_BLE_MOD_IND_2_00 7 +#define GFS_BLE_MOD_IND_2_25 8 +#define GFS_BLE_MOD_IND_2_50 9 +#define GFS_BLE_MOD_IND_2_75 10 +#define GFS_BLE_MOD_IND_3_00 11 +#define GFS_BLE_MOD_IND_3_25 12 +#define GFS_BLE_MOD_IND_3_50 13 +#define GFS_BLE_MOD_IND_3_75 14 +#define GFS_BLE_MOD_IND_4_00 15 + +#define PREAMBLE_LENGTH_04_BITS 0x00 //4 bits +#define PREAMBLE_LENGTH_08_BITS 0x10 //8 bits +#define PREAMBLE_LENGTH_12_BITS 0x20 //12 bits +#define PREAMBLE_LENGTH_16_BITS 0x30 //16 bits +#define PREAMBLE_LENGTH_20_BITS 0x40 //20 bits +#define PREAMBLE_LENGTH_24_BITS 0x50 //24 bits +#define PREAMBLE_LENGTH_28_BITS 0x60 //28 bits +#define PREAMBLE_LENGTH_32_BITS 0x70 //32 bits + +#define GFS_SYNCWORD_LENGTH_1_BYTE 0x00 //Sync word length 1 byte +#define GFS_SYNCWORD_LENGTH_2_BYTE 0x02 //Sync word length 2 bytes +#define GFS_SYNCWORD_LENGTH_3_BYTE 0x04 //Sync word length 3 bytes +#define GFS_SYNCWORD_LENGTH_4_BYTE 0x06 //Sync word length 4 bytes +#define GFS_SYNCWORD_LENGTH_5_BYTE 0x08 //Sync word length 5 bytes + +#define RADIO_RX_MATCH_SYNCWORD_OFF 0x00 //no search for SyncWord +#define RADIO_RX_MATCH_SYNCWORD_1 0x10 +#define RADIO_RX_MATCH_SYNCWORD_2 0x20 +#define RADIO_RX_MATCH_SYNCWORD_1_2 0x30 +#define RADIO_RX_MATCH_SYNCWORD_3 0x40 +#define RADIO_RX_MATCH_SYNCWORD_1_3 0x50 +#define RADIO_RX_MATCH_SYNCWORD_2_3 0x60 +#define RADIO_RX_MATCH_SYNCWORD_1_2_3 0x70 + +#define RADIO_PACKET_FIXED_LENGTH 0x00 //The packet is fixed length, klnown on both RX and TX, no header +#define RADIO_PACKET_VARIABLE_LENGTH 0x20 //The packet is variable size, header included + +#define RADIO_CRC_OFF 0x00 +#define RADIO_CRC_1_BYTES 0x10 +#define RADIO_CRC_2_BYTES 0x20 +#define RADIO_CRC_3_BYTES 0x30 + +#define RADIO_WHITENING_ON 0x00 +#define RADIO_WHITENING_OFF 0x08 + +//End GFSK **************************************************** + + + +//************************************************************* +//FLRC modem settings +//************************************************************* + +#define FLRC_SYNC_NOSYNC 0x00 +#define FLRC_SYNC_WORD_LEN_P32S 0x04 + +#define FLRC_BR_1_300_BW_1_2 0x45 //1.3Mbs +#define FLRC_BR_1_000_BW_1_2 0x69 //1.04Mbs +#define FLRC_BR_0_650_BW_0_6 0x86 //0.65Mbs +#define FLRC_BR_0_520_BW_0_6 0xAA //0.52Mbs +#define FLRC_BR_0_325_BW_0_3 0xC7 //0.325Mbs +#define FLRC_BR_0_260_BW_0_3 0xEB //0.26Mbs + +#define FLRC_CR_1_2 0x00 //coding rate 1:2 +#define FLRC_CR_3_4 0x02 //coding rate 3:4 +#define FLRC_CR_1_0 0x04 //coding rate 1 + +#define BT_DIS 0x00 //No filtering +#define BT_1 0x10 //1 +#define BT_0_5 0x20 //0.5 + +#define RADIO_MOD_SHAPING_BT_OFF 0x00 +#define RADIO_MOD_SHAPING_BT_1_0 0x10 +#define RADIO_MOD_SHAPING_BT_0_5 0x20 + + +//Table 13-45: PacketStatus2 in FLRC Packet +#define PacketCtrlBusy 0x01 +#define PacketReceived 0x02 +#define HeaderReceived 0x04 +#define AbortError 0x08 +#define CrcError 0x10 +#define LengthError 0x20 +#define SyncError 0x40 +#define Reserved 0x80 + + +//Table 13-46: PacketStatus3 in FLRC Packet +#define PktSent 0x01 +#define rxpiderr 0x08 +#define rx_no_ack 0x10 + +//FLRC default packetparamns +#define FLRC_Default_AGCPreambleLength PREAMBLE_LENGTH_32_BITS //packetParam1 +#define FLRC_Default_SyncWordLength FLRC_SYNC_WORD_LEN_P32S //packetParam2 +#define FLRC_Default_SyncWordMatch RADIO_RX_MATCH_SYNCWORD_1 //packetParam3 +#define FLRC_Default_PacketType RADIO_PACKET_VARIABLE_LENGTH //packetParam4 +#define FLRC_Default_PayloadLength BUFFER_SIZE_DEFAULT //packetParam5 +#define FLRC_Default_CrcLength RADIO_CRC_3_BYTES //packetParam6 +#define FLRC_Default_Whitening RADIO_WHITENING_OFF //packetParam7 + + +//Table 11-15 Sleep modes +#define RETAIN_INSTRUCTION_RAM 0x04 +#define RETAIN_DATABUFFER 0x02 +#define RETAIN_DATA_RAM 0x01 +#define CONFIGURATION_RETENTION 0x01 //included for libray compatibility +#define RETAIN_None 0x00 + + +#ifndef RAMP_TIME +#define RAMP_TIME RADIO_RAMP_02_US +#endif + +#ifndef PERIODBASE +#define PERIODBASE PERIOBASE_01_MS +#endif + +#ifndef PERIODBASE_COUNT_15_8 +#define PERIODBASE_COUNT_15_8 0 +#endif + +#ifndef PERIODBASE_COUNT_7_0 +#define PERIODBASE_COUNT_7_0 0 +#endif + + +#define DEVICE_SX1280 0x20 +#define DEVICE_SX1281 0x21 + + +//SPI settings +#define LTspeedMaximum 8000000 +#define LTdataOrder MSBFIRST +#define LTdataMode SPI_MODE0 + +#define RANGING_VALID 0x03 +#define RANGING_TIMEOUT 0x02 +#define WAIT_RX 0x01 +#define WAIT_TX 0x01 +#define NO_WAIT 0x00 + +#define CalibrationSF10BW400 10180 //calibration value for ranging, SF10, BW400 +#define CalibrationSF5BW1600 13100 //calibration value for ranging, SF5, BW1600 + + +const uint16_t RNG_CALIB_0400[] = { 10260, 10244, 10228, 10212, 10196, 10180 }; //SF5 to SF10 +const uint16_t RNG_CALIB_0800[] = { 11380, 11370, 11360, 11350, 11340, 11330 }; +const uint16_t RNG_CALIB_1600[] = { 13100, 13160, 13220, 13280, 13340, 13400 }; \ No newline at end of file diff --git a/lib/SX12XX-LoRa/src/UBLOXI2CGPS.h b/lib/SX12XX-LoRa/src/UBLOXI2CGPS.h new file mode 100644 index 0000000..032a95a --- /dev/null +++ b/lib/SX12XX-LoRa/src/UBLOXI2CGPS.h @@ -0,0 +1,569 @@ +/* + Copyright 2020 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + Original published 27/06/20 +*/ + +/******************************************************************************************************* + Program Operation - This is a library file for the UBLOX 6,7 and 8 series GPSs. + + This GPS library file is designed for use with the GPSs I2C interface. + +*******************************************************************************************************/ + + + +//For use with I2C the configurations sent must be even numbers of bytes, thus the last byte in some cases may be a 0x00 padding byte +const PROGMEM uint8_t ClearConfig[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0x19, 0x98, 0x00}; //22 +const PROGMEM uint8_t GLONASSOff[] = {0xB5, 0x62, 0x06, 0x3E, 0x0C, 0x00, 0x00, 0x00, 0x20, 0x01, 0x06, 0x08, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x01, 0x8F, 0xB2}; //20 +const PROGMEM uint8_t GPGLLOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A}; //16 +const PROGMEM uint8_t GPGLSOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x46}; //16 +const PROGMEM uint8_t GPGSAOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x31}; //16 +const PROGMEM uint8_t GPGSVOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x38}; //16 +const PROGMEM uint8_t GNSSmode[] = {0xB5, 0x62, 0x06, 0x3E, 0x2C, 0x00, 0x00, 0x00, 0x20, 0x05, 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, +0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x08, 0x10, 0x00, 0x00, 0x00, 0x01, 0x01, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x08, 0x0E, 0x00, +0x00, 0x00, 0x01, 0x01, 0xFC, 0x11}; + +const PROGMEM uint8_t SetBalloonMode[] = {0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, + 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC}; //44 +const PROGMEM uint8_t SaveConfig[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1B, 0xA9, 0x00}; //22 +const PROGMEM uint8_t PollNavigation[] = {0xB5, 0x62, 0x06, 0x24, 0x00, 0x00, 0x2A, 0x84}; //8 +const PROGMEM uint8_t SetCyclicMode[] = {0xB5, 0x62, 0x06, 0x11, 0x02, 0x00, 0x08, 0x01, 0x22, 0x92}; //10 +const PROGMEM uint8_t SoftwareBackup[] = {0xB5, 0x62, 0x06, 0x57, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x4B, 0x43, 0x42, 0x86, 0x46}; //16 +const PROGMEM uint8_t EnableI2C[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x92}; //28 +//const PROGMEM uint8_t PMREQBackup[] = {0xB5, 0x62, 0x02, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4D, 0x3B}; //16 + +uint8_t GPS_GetByte(); +void GPS_OutputOn(); +void GPS_OutputOff(); +void GPS_PowerOn(int8_t pin, uint8_t state); +void GPS_PowerOff(int8_t pin, uint8_t state); +bool GPS_Setup(); +bool GPS_SendConfig(unsigned int Progmem_ptr, byte length, byte replylength); +bool GPS_WaitAck(unsigned long waitms, byte length); +//uint8_t GPS_GetNextChar(uint32_t waitmS); +//bool GPS_CheckAck(); +bool GPS_SetBalloonMode(); +bool GPS_CheckBalloonMode(); +bool GPS_ClearConfig(); +bool GPS_SetCyclicMode(); +bool GPS_SoftwareBackup(); +//bool GPS_HotStart(); +bool GPS_PollNavigation(); +bool GPS_SaveConfig(); +bool GPS_GLONASSOff(); +bool GPS_GPGLLOff(); +bool GPS_GPGLSOff(); +bool GPS_GPGSAOff(); +bool GPS_GPGSVOff(); +bool GPS_GNSSmode(); +bool GPS_GGARMCOnly(); +//void GPS_PMREQBackup(); +//void GPS_StartRead(); +//void GPS_SetGPMode(); +//void GPS_StopMessages(); + +const int16_t GPSI2CAddress = 0x42; +const uint32_t GPS_WaitAck_mS = 1000; //number of mS to wait for an ACK response from GPS +const uint8_t GPS_attempts = 5; //number of times the sending of GPS config will be attempted. +const uint8_t GPS_Reply_Size = 16; //size of GPS reply buffer +const uint16_t GPS_Clear_DelaymS = 2000; //mS to wait after a GPS Clear command is sent +uint8_t GPS_Reply[GPS_Reply_Size]; //byte array for storing GPS reply to UBX commands + + +#define USING_I2CGPS //so the rest of the program knows I2C GPS is in use +#define UBLOX //so the rest of the program knows UBLOX GPS is in use +//#define GPSDEBUG //include define to see some debug messages + + + +uint8_t GPS_GetByte() //get and process output from GPS +{ + uint8_t GPSchar; + + Wire.requestFrom(GPSI2CAddress, 1); + GPSchar = Wire.read(); + + return GPSchar; +} + + + +void GPS_OutputOn() +{ +//not used function for I2C library, included for compatibility with other libraries +} + + +void GPS_OutputOff() +{ +//not used function for I2C library, included for compatibility with other libraries +} + + +/* +void GPS_StartRead() +{ + Wire.beginTransmission(GPSI2CAddress); + Wire.write(0xFF); +} +*/ + +void GPS_PowerOn(int8_t pin, uint8_t state) +{ +#ifdef GPSDebug + Serial.print(F("GPS_PowerOn() ")); +#endif + + if (pin >= 0) + { + digitalWrite(pin, state); + } + +} + + +void GPS_PowerOff(int8_t pin, uint8_t state) +{ +#ifdef GPSDebug + Serial.print(F("GPS_PowerOff() ")); +#endif + +if (pin >= 0) + { + digitalWrite(pin, state); + } + +} + + +bool GPS_WaitAck(uint32_t waitmS, uint8_t length) +{ + //wait for Ack from GPS + byte i, j; + uint32_t startmS; + + startmS = millis(); + + byte ptr = 0; //used as pointer to store GPS reply + + Wire.beginTransmission(GPSI2CAddress); + Wire.write(0xFF); + + do + { + Wire.requestFrom(GPSI2CAddress, 1); + i = Wire.read(); + } + while ((i != 0xb5) && ((uint32_t) (millis() - startmS) < waitmS)); + + if (i != 0xb5) + { + Serial.print(F("Timeout ")); + return false; + } + else + { + Serial.print(F("Ack ")); + Serial.print(i, HEX); + + length--; + + for (j = 1; j <= length; j++) + { + Serial.print(F(" ")); + + Wire.requestFrom(GPSI2CAddress, 1); + i = Wire.read(); + + if (j < 12) + { + GPS_Reply[ptr++] = i; //save reply in buffer, but no more than 10 characters + } + + if (i < 0x10) + { + Serial.print(F("0")); + } + Serial.print(i, HEX); + } + + } + Serial.println(); + return true; +} + + +bool GPS_SendConfig(const uint8_t *Progmem_ptr, uint8_t arraysize, uint8_t replylength, uint8_t attempts) +{ + uint8_t byteread1, byteread2, index, length; + uint8_t config_attempts = attempts; + + do + { + + if (config_attempts == 0) + { + Serial.println(F("Fail")); + Serial.println(); + return false; + } + + length = arraysize / 2; //we are sending messages 2 bytes at a time + + for (index = 0; index < length; index++) + { + byteread1 = pgm_read_byte_near(Progmem_ptr++); //we will read and write 2 bytes at a time + byteread2 = pgm_read_byte_near(Progmem_ptr++); + Wire.beginTransmission(GPSI2CAddress); + Wire.write(byteread1); + Wire.write(byteread2); + Wire.endTransmission(); + if (byteread1 < 0x10) + { + Serial.print(F("0")); + } + Serial.print(byteread1, HEX); + Serial.print(F(" ")); + if (byteread2 < 0x10) + { + Serial.print(F("0")); + } + Serial.print(byteread2, HEX); + Serial.print(F(" ")); + } + + Progmem_ptr = Progmem_ptr - arraysize; //put Progmem_ptr back to start value in case we need to re-send the config + + Serial.println(); + + if (replylength == 0) + { + #ifdef GPSDebug + Serial.println(F("Reply not required")); + #endif + break; + } + + config_attempts--; + } while (!GPS_WaitAck(GPS_WaitAck_mS, replylength)); + + delay(50); //GPS can sometimes be a bit slow getting ready for next config + return true; +} + + +bool GPS_Setup() +{ + #ifdef GPSDebug + Serial.println(F("GPS_Setup()")); + #endif + /* + Wire.begin(); + GPS_ClearConfig(); + GPS_StopMessages(); + GPS_SetBalloonMode(); + GPS_SaveConfig(); + */ + + Wire.begin(); + + if (!GPS_ClearConfig()) + { + return false; + } + + if (!GPS_SetBalloonMode()) + { + return false; + } + + if (!GPS_GNSSmode()) + { + return false; + } + + if (!GPS_GPGLLOff()) + { + return false; + } + + if (!GPS_GPGLSOff()) + { + return false; + } + + if (!GPS_GPGSAOff()) + { + return false; + } + + if (!GPS_GPGSVOff()) + { + return false; + } + + if (!GPS_SaveConfig()) + { + return false; + } + + return true; +} + + +bool GPS_CheckBalloonMode() +{ + #ifdef GPSDebug + Serial.println(F("GPS_CheckBalloonMode()")); + #endif + + uint8_t j; + + GPS_Reply[7] = 0xff; + + GPS_PollNavigation(); + + j = GPS_Reply[7]; + + Serial.print(F("Dynamic Model is ")); + Serial.println(j); + + if (j != 6) + { + Serial.println(F("Dynamic Model 6 not Set !")); + return false; + } + else + { + return true; + } +} + +bool GPS_ClearConfig() +{ + #ifdef GPSDebug + Serial.println(F("GPS_ClearConfig()")); + #endif + + Serial.println(F("ClearConfig")); + size_t SIZE = sizeof(ClearConfig); + + if (!GPS_SendConfig(ClearConfig,SIZE,10,GPS_attempts)) + { + return false; + } + + Serial.println(F("Wait clear")); + delay(GPS_Clear_DelaymS); //wait a while for GPS to clear its settings + + return true; +} + +/* +void GPS_StopMessages() +{ + #ifdef GPSDebug + Serial.print(F("GPS GPGLLOff ")); + #endif + + size_t SIZE; + + SIZE = sizeof(GPGLLOff); + GPS_SendConfig(GPGLLOff, SIZE, 10, GPS_attempts); + + #ifdef GPSDebug + Serial.print(F("GPS GPGLSOff ")); + #endif + + SIZE = sizeof(GPGLSOff); + GPS_SendConfig(GPGLSOff, SIZE, 10, GPS_attempts); + + #ifdef GPSDebug + Serial.print(F("GPS GPGSAOff ")); + #endif + + SIZE = sizeof(GPGSAOff); + GPS_SendConfig(GPGSAOff, SIZE, 10, GPS_attempts); + + #ifdef GPSDebug + Serial.print(F("GPS GPGSVOff ")); + #endif + + SIZE = sizeof(GPGSVOff); + GPS_SendConfig(GPGSVOff, SIZE, 10, GPS_attempts); +} +*/ + +bool GPS_SetBalloonMode() +{ + #ifdef GPSDebug + Serial.print(F("GPS SetBalloonMode ")); + #endif + + size_t SIZE = sizeof(SetBalloonMode); + return GPS_SendConfig(SetBalloonMode, SIZE, 10, GPS_attempts); +} + + +bool GPS_SaveConfig() +{ + #ifdef GPSDebug + Serial.print(F("GPS_SaveConfig()")); + #endif + + size_t SIZE = sizeof(SaveConfig); + return GPS_SendConfig(SaveConfig, SIZE, 10, GPS_attempts); +} + + +bool GPS_PollNavigation() +{ + #ifdef GPSDebug + Serial.print(F("GPS_PollNavigation()")); + #endif + + size_t SIZE = sizeof(PollNavigation); + return GPS_SendConfig(PollNavigation, SIZE, 44, GPS_attempts); +} + +/* +void GPS_SetGPMode() +{ + #ifdef GPSDebug + Serial.print(F("GPS_SetGPMode()")); + #endif + + size_t SIZE = sizeof(SetCyclicMode); + GPS_SendConfig(SetCyclicMode, SIZE, 10, GPS_attempts); +} +*/ + +bool GPS_SetCyclicMode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SetCyclicMode() ")); +#endif + + Serial.println(F("SetCyclicMode")); + size_t SIZE = sizeof(SetCyclicMode); + + return GPS_SendConfig(SetCyclicMode, SIZE, 10, GPS_attempts); +} + + +bool GPS_SoftwareBackup() +{ + #ifdef GPSDebug + Serial.print(F("GPS_SoftwareBackup()")); + #endif + + size_t SIZE = sizeof(SoftwareBackup); + return GPS_SendConfig(SoftwareBackup, SIZE, 0, GPS_attempts); +} + +/* +void GPS_PMREQBackup() +{ + #ifdef GPSDebug + Serial.print(F("GPS_PMREQBackup()")); + #endif + + size_t SIZE = sizeof(PMREQBackup); + GPS_SendConfig(PMREQBackup, SIZE, 0, GPS_attempts); +} +*/ + + +bool GPS_GLONASSOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GLONASSOff() ")); +#endif + + size_t SIZE = sizeof(GLONASSOff); + return GPS_SendConfig(GLONASSOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGLLOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGLLOff() ")); +#endif + + size_t SIZE = sizeof(GPGLLOff); + return GPS_SendConfig(GPGLLOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGLSOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGLSOff() ")); +#endif + + size_t SIZE = sizeof(GPGLSOff); + return GPS_SendConfig(GPGLSOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGSAOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGSAOff() ")); +#endif + + size_t SIZE = sizeof(GPGSAOff); + return GPS_SendConfig(GPGSAOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGSVOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGSVOff() ")); +#endif + + size_t SIZE = sizeof(GPGSVOff); + return GPS_SendConfig(GPGSVOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GNSSmode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GNSSmode() ")); +#endif + + size_t SIZE = sizeof(GNSSmode); + return GPS_SendConfig(GNSSmode, SIZE, 10, GPS_attempts); +} + + +bool GPS_GGARMCOnly() +{ +//null function, not used with Ublox library +return true; +} + + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ diff --git a/lib/SX12XX-LoRa/src/UBLOXSerialGPS.h b/lib/SX12XX-LoRa/src/UBLOXSerialGPS.h new file mode 100644 index 0000000..9b09232 --- /dev/null +++ b/lib/SX12XX-LoRa/src/UBLOXSerialGPS.h @@ -0,0 +1,690 @@ +/* + Copyright 2020 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + Original published 27/06/20 +*/ + +/******************************************************************************************************* + Program Operation - This is a library file for the UBLOX 6,7 and 8 series GPSs. + + The routines assume that the GPS has been setup on GPSserial which could be either software serial or + hardware serial. This library file can be used with both Hardware serial and SoftwareSerial. The + configuration of the GPS prints debug output to the Serial console and mixing thisdebug output with + the use of software serial has required several optimistations, in particular tha the GPS serial + output is turned off at some points in the configuration process to allow prints to the serial monitor + to continue without interruption or missed characters, see the post below for details on the problem; + + https://stuartsprojects.github.io/2020/05/05/softwareserial-problems.html + + The library assumes that the GPS is connected onto a port named GPSserial, this is achived in software + serial with; + + SoftwareSerial GPSserial(RXpin, TXpin); + + And for hardware serial, assuming Serila2 is used for the GPS with; + + #define GPSserial Serial2 + + The calling program should also include a define for the GPS baud rate as follows; + + #define GPSBaud 9600 + + If this define is missing then 9600 baud is assumed +*******************************************************************************************************/ + + + +const PROGMEM uint8_t ClearConfig[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0x19, 0x98}; //21 +const PROGMEM uint8_t SetBalloonMode[] = {0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, + 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC + }; //44 +const PROGMEM uint8_t SaveConfig[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1B, 0xA9}; //22 +const PROGMEM uint8_t SetCyclicMode[] = {0xB5, 0x62, 0x06, 0x11, 0x02, 0x00, 0x08, 0x01, 0x22, 0x92}; //10 +const PROGMEM uint8_t SoftwareBackup[] = {0xB5, 0x62, 0x02, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4D, 0x3B}; //16 +const PROGMEM uint8_t PollNavigation[] = {0xB5, 0x62, 0x06, 0x24, 0x00, 0x00, 0x2A, 0x84}; //8 + +//B5 62 06 57 08 00 01 00 00 00 50 4B 43 42 86 46 + +//these are the commands to turn off NMEA sentences +const PROGMEM uint8_t GLONASSOff[] = {0xB5, 0x62, 0x06, 0x3E, 0x0C, 0x00, 0x00, 0x00, 0x20, 0x01, 0x06, 0x08, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x01, 0x8F, 0xB2}; //20 +const PROGMEM uint8_t GPGLLOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A}; //16 +const PROGMEM uint8_t GPGLSOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x46}; //16 +const PROGMEM uint8_t GPGSAOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x31}; //16 +const PROGMEM uint8_t GPGSVOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x38}; //16 + +const PROGMEM uint8_t GNSSmode[] = {0xB5, 0x62, 0x06, 0x3E, 0x2C, 0x00, 0x00, 0x00, 0x20, 0x05, 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, +0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x08, 0x10, 0x00, 0x00, 0x00, 0x01, 0x01, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x08, 0x0E, 0x00, +0x00, 0x00, 0x01, 0x01, 0xFC, 0x11}; + +//response to PollNavigation should be B5 62 06 24 24 00 FF FF 06 03 +//accepted response to configuaration command should be UBX-ACK-ACK B5 62 05 01 +//fail response to configuaration command should be UBX-ACK-NAK B5 62 05 00 + +uint8_t GPS_GetByte(); +void GPS_OutputOn(); +void GPS_OutputOff(); +void GPS_PowerOn(int8_t pin, uint8_t state); +void GPS_PowerOff(int8_t pin, uint8_t state); +bool GPS_Setup(); +bool GPS_SendConfig(const uint8_t *Progmem_ptr, uint8_t arraysize, uint8_t replylength, uint8_t attempts); +bool GPS_WaitAck(uint32_t waitms, uint8_t length); +bool GPS_WaitChar(uint8_t waitforchar, uint32_t waitmS); +//uint8_t GPS_GetNextChar(uint32_t waitmS); +bool GPS_CheckAck(); +bool GPS_SetBalloonMode(); +bool GPS_CheckBalloonMode(); +bool GPS_ClearConfig(); +bool GPS_SetCyclicMode(); +bool GPS_SoftwareBackup(); +bool GPS_HotStart(); +bool GPS_PollNavigation(); +bool GPS_SaveConfig(); +bool GPS_GLONASSOff(); +bool GPS_GPGLLOff(); +bool GPS_GPGLSOff(); +bool GPS_GPGSAOff(); +bool GPS_GPGSVOff(); +bool GPS_GNSSmode(); +bool GPS_GGARMCOnly(); + +const uint32_t GPS_WaitAck_mS = 1000; //number of mS to wait for an ACK response from GPS +const uint8_t GPS_attempts = 10; //number of times the sending of GPS config will be attempted. +const uint8_t GPS_Reply_Size = 16; //size of GPS reply buffer +const uint16_t GPS_Clear_DelaymS = 2000; //mS to wait after a GPS Clear command is sent +uint8_t GPS_Reply[GPS_Reply_Size]; //byte array for storing GPS reply to UBX commands + + +#define UBLOXINUSE //so complier can know which GPS library is used +//#define GPSDebug + +#ifndef GPSBaud +#define GPSBaud 9600 +#endif + + +void GPS_OutputOn() +{ +#ifdef GPSDebug + Serial.print(F("GPS_On() ")); +#endif + uint8_t GPSchar = 0; + //turns on serial output from GPS + GPSserial.begin(GPSBaud); + GPSserial.write(GPSchar); +} + + +void GPS_OutputOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_Off() ")); +#endif + + GPSserial.end(); //turns off serial output from GPS +} + + +void GPS_PowerOn(int8_t pin, uint8_t state) +{ +#ifdef GPSDebug + Serial.print(F("GPS_PowerOn() ")); +#endif + + if (pin >= 0) + { + digitalWrite(pin, state); + } + +} + + +void GPS_PowerOff(int8_t pin, uint8_t state) +{ +#ifdef GPSDebug + Serial.print(F("GPS_PowerOff() ")); +#endif + +if (pin >= 0) + { + digitalWrite(pin, state); + } + +} + + +bool GPS_Setup() +{ +#ifdef GPSDebug + Serial.print(F("GPS_Setup() ")); +#endif + + if (!GPS_ClearConfig()) + { + return false; + } + + if (!GPS_SetBalloonMode()) + { + return false; + } + + if (!GPS_GNSSmode()) + { + return false; + } + + if (!GPS_GPGLLOff()) + { + return false; + } + + if (!GPS_GPGLSOff()) + { + return false; + } + + if (!GPS_GPGSAOff()) + { + return false; + } + + if (!GPS_GPGSVOff()) + { + return false; + } + + if (!GPS_SaveConfig()) + { + return false; + } + + return true; +} + + +bool GPS_CheckBalloonMode() +{ + //navigation model setting for UBLOX is at GPS_Reply[8] of poll request reply. + +#ifdef GPSDebug + Serial.print(F("GPS_CheckBalloonMode() ")); +#endif + + uint8_t j; + GPS_Reply[8] = 0xff; + + GPS_PollNavigation(); + + if ( (GPS_Reply[0] == 0xB5) && (GPS_Reply[1] == 0x62) && (GPS_Reply[2] == 0x06) && (GPS_Reply[3] == 0X24) ) + { + j = GPS_Reply[8]; + + if (j == 6) + { + return true; + } + else + { + return false; + } + } + + return false; + +} + + +bool GPS_SendConfig(const uint8_t *Progmem_ptr, uint8_t arraysize, uint8_t replylength, uint8_t attempts) +{ +#ifdef GPSDebug + Serial.print(F("GPS_SendConfig() ")); +#endif + + uint8_t byteread, index; + uint8_t config_attempts = attempts; + + Serial.flush(); //ensure there are no pending interrupts from serial monitor printing + + do + { + if (config_attempts == 0) + { + Serial.println(F("Fail")); + Serial.println(); + return false; + } + + GPS_OutputOff(); + + Serial.print(F("GPSSend ")); + + for (index = 0; index < arraysize; index++) + { + byteread = pgm_read_byte_near(Progmem_ptr++); + if (byteread < 0x10) + { + Serial.print(F("0")); + } + Serial.print(byteread, HEX); + Serial.print(F(" ")); + } + + Serial.flush(); //make sure serial out buffer is empty + + GPS_OutputOn(); + + Progmem_ptr = Progmem_ptr - arraysize; //set Progmem_ptr back to start + + for (index = 0; index < arraysize; index++) + { + byteread = pgm_read_byte_near(Progmem_ptr++); + GPSserial.write(byteread); + } + + Progmem_ptr = Progmem_ptr - arraysize; //set Progmem_ptr back to start + + if (replylength == 0) + { +#ifdef GPSDebug + Serial.println(F("Reply not required")); +#endif + break; + } + GPSserial.flush(); //make sure all of config command has been sent + config_attempts--; + } + while (!GPS_WaitAck(GPS_WaitAck_mS, replylength)); + + Serial.println(F("OK")); + Serial.println(); + delay(100); //GPS can sometimes be a bit slow getting ready for next config + + return true; +} + + +bool GPS_WaitAck(uint32_t waitmS, uint8_t length) +{ +#ifdef GPSDebug + Serial.print(F("GPS_WaitAck() ")); + Serial.print(F(" Reply length ")); + Serial.print(length); + Serial.print(F(" ")); +#endif + + //wait for Ack (UBX-ACK-ACK) response from GPS is 0xb5,0x62, 0x05, 0x01 + //Nack (UBX-ACK-NAK) response from GPS is 0xb5,0x62, 0x05, 0x00 + + uint8_t GPSchar; + uint32_t startmS; + + startmS = millis(); + + uint8_t ptr = 0; //used as pointer to store GPS reply + + Serial.println(); + Serial.print(F("Received ")); + Serial.flush(); + + do + { + if (GPSserial.available()) + { + GPSchar = GPSserial.read(); + } + } + while ((GPSchar != 0xb5) && ((uint32_t) (millis() - startmS) < waitmS)); //use the timeout to ensure a lack of GPS does not cause the program to hang + + if (GPSchar != 0xb5) + { + Serial.println(F("Timeout Error")); + return false; + } + + GPS_Reply[ptr++] = 0xB5; //test if a 0xB5 has been received + + startmS = millis(); + + do + { + if (GPSserial.available()) + { + GPS_Reply[ptr++] = GPSserial.read(); + } + } + while ((ptr < length) && ((uint32_t) (millis() - startmS) < waitmS)); //fill buffer, stop when either ptr is (length-1) or timeout + + + if (ptr < length) + { + Serial.print(F("Short Reply Error")); + return false; + } + + GPS_OutputOff(); + + for (ptr = 0; ptr < length; ptr++) + { + GPSchar = GPS_Reply[ptr]; + + if (GPSchar < 0x10) + { + Serial.print(F("0")); + } + + Serial.print(GPSchar , HEX); + Serial.print(F(" ")); + } + + Serial.println(); + + if (GPS_CheckAck()) + { + return true; + } + + return false; +} + + +bool GPS_WaitChar(uint8_t waitforchar, uint32_t waitmS) +{ + uint8_t GPSchar; + uint32_t startmS; + + startmS = millis(); + + do + { + if (GPSserial.available()) + { + GPSchar = GPSserial.read(); + if (GPSchar == waitforchar) + { + GPS_Reply[0] = GPSchar; + return true; + } + } + } + while ((uint32_t) (millis() - startmS) < waitmS); //use the timeout to ensure a lack of GPS does not cause the program to hang + return false; +} + +/* +uint8_t GPS_GetNextChar(uint32_t waitmS) +{ + uint8_t GPSchar; + + do + { + if (GPSserial.available()) + { + GPSchar = GPSserial.read(); + GPS_Reply[1] = GPSchar; + return GPSchar; + } + } + while ((millis() < waitmS)); //use the timeout to ensure a lack of GPS does not cause the program to hang + return '*'; +} +*/ + + +bool GPS_PollNavigation() +{ +#ifdef GPSDebug + Serial.print(F("GPS_PollNavigation() ")); +#endif + //Serial.println(F("PollNavigation")); + size_t SIZE = sizeof(PollNavigation); + if (GPS_SendConfig(PollNavigation, SIZE, 10, GPS_attempts)) + { + return true; + } + return false; +} + + + +bool GPS_CheckAck() +{ + +#ifdef GPSDebug + Serial.print(F("GPS_CheckAck() ")); +#endif + + if ((GPS_Reply[0] == 0xB5) && (GPS_Reply[1] == 0x62)) + { +#ifdef GPSDebug + Serial.println(F(" UBX-ACK-ACK")); +#endif + return true; //there has been a UBX-ACK-ACK response + } + else + { +#ifdef GPSDebug + Serial.println(F(" Not UBX-ACK-ACK ")); +#endif + return false; //there has been a UBX-ACK-ACK response + } +} + + + +/********************************************************************* + // GPS configuration commands +*********************************************************************/ + + +bool GPS_ClearConfig() +{ + #ifdef GPSDebug + Serial.print(F("GPS_ClearConfig() ")); + #endif + + //Serial.println(F("ClearConfig")); + size_t SIZE = sizeof(ClearConfig); + if (GPS_SendConfig(ClearConfig, SIZE, 10, GPS_attempts)) + { + Serial.println(F("Wait clear")); + Serial.println(); + delay(GPS_Clear_DelaymS); //wait a while for GPS to clear its settings + return true; + } + + return false; +} + + +bool GPS_SetBalloonMode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SetBalloonMode() ")); +#endif + + //Serial.println(F("SetBalloonMode")); + size_t SIZE = sizeof(SetBalloonMode); + if (GPS_SendConfig(SetBalloonMode, SIZE, 10, GPS_attempts)) + { + return true; + } + + return false; +} + + +bool GPS_SaveConfig() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SaveConfig() ")); +#endif + + //Serial.println(F("SaveConfig")); + size_t SIZE = sizeof(SaveConfig); + + if (GPS_SendConfig(SaveConfig, SIZE, 10, GPS_attempts)) + { + return true; + } + + return false; +} + + +bool GPS_SetCyclicMode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SetCyclicMode() ")); +#endif + + //Serial.println(F("SetCyclicMode")); + size_t SIZE = sizeof(SetCyclicMode); + + if (GPS_SendConfig(SetCyclicMode, SIZE, 10, GPS_attempts)) + { + return true; + } + + return false; +} + + +bool GPS_SoftwareBackup() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SoftwareBackup() ")); +#endif + + //Serial.println(F("SoftwareBackup")); + size_t SIZE = sizeof(SoftwareBackup); + + if (GPS_SendConfig(SoftwareBackup, SIZE, 0, GPS_attempts)) + { + return true; + } + + return false; +} + + + +bool GPS_HotStart() + { + #ifdef GPSDebug + Serial.print(F("GPS_HotStart() ")); + #endif + + //Serial.println(F("HotStart")); + GPSserial.println(); + + return true; + +} + + + +bool GPS_GLONASSOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GLONASSOff() ")); +#endif + + size_t SIZE = sizeof(GLONASSOff); + return GPS_SendConfig(GLONASSOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGLLOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGLLOff() ")); +#endif + + size_t SIZE = sizeof(GPGLLOff); + return GPS_SendConfig(GPGLLOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGLSOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGLSOff() ")); +#endif + + size_t SIZE = sizeof(GPGLSOff); + return GPS_SendConfig(GPGLSOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGSAOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGSAOff() ")); +#endif + + size_t SIZE = sizeof(GPGSAOff); + return GPS_SendConfig(GPGSAOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGSVOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGSVOff() ")); +#endif + + size_t SIZE = sizeof(GPGSVOff); + return GPS_SendConfig(GPGSVOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GNSSmode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GNSSmode() ")); +#endif + + size_t SIZE = sizeof(GNSSmode); + return GPS_SendConfig(GNSSmode, SIZE, 10, GPS_attempts); +} + + +uint8_t GPS_GetByte() //get a byte for GPS +{ + if (GPSserial.available() == 0) + { + return 0xFF; //for compatibility with I2C reading of GPS + } + else + { + return GPSserial.read(); + } +} + + +bool GPS_GGARMCOnly() +{ +//null function, not used with Ublox library +return true; +} + + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + diff --git a/lib/SX12XX-LoRa/src/UBLOX_HWSerialGPS.h b/lib/SX12XX-LoRa/src/UBLOX_HWSerialGPS.h new file mode 100644 index 0000000..be6c477 --- /dev/null +++ b/lib/SX12XX-LoRa/src/UBLOX_HWSerialGPS.h @@ -0,0 +1,580 @@ +/* + Copyright 2020 - Stuart Robinson + Licensed under a MIT license displayed at the bottom of this document. + Original published 07/08/20 +*/ + +const PROGMEM uint8_t ClearConfig[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0x19, 0x98}; //21 +const PROGMEM uint8_t SetBalloonMode[] = {0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, + 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC + }; //44 +const PROGMEM uint8_t SaveConfig[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1B, 0xA9}; //22 +const PROGMEM uint8_t SetCyclicMode[] = {0xB5, 0x62, 0x06, 0x11, 0x02, 0x00, 0x08, 0x01, 0x22, 0x92}; //10 +const PROGMEM uint8_t SoftwareBackup[] = {0xB5, 0x62, 0x02, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4D, 0x3B}; //16 +const PROGMEM uint8_t PollNavigation[] = {0xB5, 0x62, 0x06, 0x24, 0x00, 0x00, 0x2A, 0x84}; //8 + +//these are the commands to turn off NMEA sentences +const PROGMEM uint8_t GLONASSOff[] = {0xB5, 0x62, 0x06, 0x3E, 0x0C, 0x00, 0x00, 0x00, 0x20, 0x01, 0x06, 0x08, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x01, 0x8F, 0xB2}; //20 +const PROGMEM uint8_t GPGLLOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A}; //16 +const PROGMEM uint8_t GPGLSOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x46}; //16 +const PROGMEM uint8_t GPGSAOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x31}; //16 +const PROGMEM uint8_t GPGSVOff[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x38}; //16 +const PROGMEM uint8_t GNSSmode[] = {0xB5, 0x62, 0x06, 0x3E, 0x2C, 0x00, 0x00, 0x00, 0x20, 0x05, 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, +0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x08, 0x10, 0x00, 0x00, 0x00, 0x01, 0x01, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01, 0x06, 0x08, 0x0E, 0x00, +0x00, 0x00, 0x01, 0x01, 0xFC, 0x11}; + +//response to PollNavigation should be B5 62 06 24 24 00 FF FF 06 03 +//accepted response to configuaration command should be UBX-ACK-ACK B5 62 05 01 +//fail response to configuaration command should be UBX-ACK-NAK B5 62 05 00 + +void GPS_OutputOn(); +void GPS_OutputOff(); +void GPS_PowerOn(); +bool GPS_CheckConfiguration(); +bool GPS_SendConfig(const uint8_t *Progmem_ptr, uint8_t arraysize, uint8_t replylength, uint8_t attempts); +bool GPS_WaitAck(uint32_t waitms, uint8_t length); +bool GPS_PollNavigation(); + +bool GPS_ClearConfig(); +bool GPS_SaveConfig(); +bool GPS_SetBalloonMode(); +bool GPS_SetCyclicMode(); +bool GPS_SoftwareBackup(); +bool GPS_GLONASSOff(); +bool GPS_GPGLLOff(); +bool GPS_GPGLSOff(); +bool GPS_GPGSAOff(); +bool GPS_GPGSVOff(); +bool GPS_GNSSmode(); +bool GPS_CheckAck(); +uint8_t GPS_GetByte(); + + +const uint32_t GPS_WaitAck_mS = 1000; //number of mS to wait for an ACK response from GPS +const uint8_t GPS_attempts = 5; //number of times the sending of GPS config will be attempted. +const uint8_t GPS_Reply_Size = 16; //size of GPS reply buffer +const uint16_t GPS_Clear_DelaymS = 2000; //mS to wait after a GPS Clear command is sent +uint8_t GPS_Reply[GPS_Reply_Size]; //byte array for storing GPS reply to UBX commands + + +#define UBLOXINUSE //so complier can know which GPS library is used +//#define GPSDebug + +#ifndef GPSConfigSerial // if GPSDebugSerial is not defined set to Serial as default + #define GPSConfigSerial Serial +#endif + +#ifndef GPSDebugSerial // if GPSDebugSerial is not defined set to Serial as default + #define GPSDebugSerial Serial +#endif + +#ifndef GPSBaud +#define GPSBaud 9600 +#endif + + +void GPS_OutputOn() +{ +#ifdef GPSDebug + Serial.print(F("GPS_On() ")); +#endif + uint8_t GPSchar = 0; + //turns on serial output from GPS + GPSserial.begin(GPSBaud); + GPSserial.write(GPSchar); +} + + +void GPS_OutputOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_Off() ")); +#endif + + GPSserial.end(); //turns off serial output from GPS +} + + +void GPS_PowerOn(int8_t pin, uint8_t state) +{ +#ifdef GPSDebug + Serial.print(F("GPS_PowerOn() ")); +#endif + + if (pin >= 0) + { + digitalWrite(pin, state); + } + +} + + +void GPS_PowerOff(int8_t pin, uint8_t state) +{ +#ifdef GPSDebug + Serial.print(F("GPS_PowerOff() ")); +#endif + +if (pin >= 0) + { + digitalWrite(pin, state); + } + +} + + + +bool GPS_Setup() +{ +#ifdef GPSDebug + Serial.print(F("GPS_Setup() ")); +#endif + + if (!GPS_ClearConfig()) + { + return false; + } + + if (!GPS_SetBalloonMode()) + { + return false; + } + + if (!GPS_CheckConfiguration()) + { + return false; + } + + if (!GPS_SaveConfig()) + { + return false; + } + + return true; +} + + +bool GPS_CheckConfiguration() +{ + //navigation model setting for UBLOX is at GPS_Reply[8] of poll request reply. + +#ifdef GPSDebug + Serial.print(F("GPS_CheckConfiguration() UBLOX ")); +#endif + + uint8_t j; + GPS_Reply[8] = 0xff; + + GPS_PollNavigation(); + + if ( (GPS_Reply[0] == 0xB5) && (GPS_Reply[1] == 0x62) && (GPS_Reply[2] == 0x06) && (GPS_Reply[3] == 0X24) ) + { + j = GPS_Reply[8]; + + if (j == 6) + { + return true; + } + else + { + return false; + } + } + + return false; + +} + + +bool GPS_SendConfig(const uint8_t *Progmem_ptr, uint8_t arraysize, uint8_t replylength, uint8_t attempts) +{ +#ifdef GPSDebug + Serial.print(F("GPS_SendConfig() ")); +#endif + + + + uint8_t byteread, index; + uint8_t config_attempts = attempts; + + Serial.flush(); //ensure there are no pending interrupts from serial monitor printing + + do + { + if (config_attempts == 0) + { + Serial.println(F("Fail")); + Serial.println(); + return false; + } + + Serial.print(F("GPSSend ")); + + for (index = 0; index < arraysize; index++) + { + byteread = pgm_read_byte_near(Progmem_ptr++); + Serial.print(byteread, HEX); + Serial.print(F(" ")); + } + + Serial.flush(); //make sure serial out buffer is empty + + Progmem_ptr = Progmem_ptr - arraysize; //set Progmem_ptr back to start + + for (index = 0; index < arraysize; index++) + { + byteread = pgm_read_byte_near(Progmem_ptr++); + GPSserial.write(byteread); + } + + Progmem_ptr = Progmem_ptr - arraysize; //set Progmem_ptr back to start + + if (replylength == 0) + { +#ifdef GPSDebug + Serial.println(F("Reply not required")); +#endif + break; + } + + config_attempts--; + } + while (!GPS_WaitAck(GPS_WaitAck_mS, replylength)); + + Serial.println(F("OK")); + Serial.println(); + delay(100); //GPS can sometimes be a bit slow getting ready for next config + + return true; +} + + +bool GPS_WaitAck(uint32_t waitms, uint8_t length) +{ +#ifdef GPSDebug + Serial.print(F("GPS_WaitAck() ")); + Serial.print(F(" Reply length ")); + Serial.print(length); + Serial.print(F(" ")); +#endif + + //wait for Ack (UBX-ACK-ACK) response from GPS is 0xb5,0x62, 0x05, 0x01 + //Nack (UBX-ACK-NAK) response from GPS is 0xb5,0x62, 0x05, 0x00 + + uint8_t GPSchar; + + uint32_t endms; + endms = millis() + waitms; + uint8_t ptr = 0; //used as pointer to store GPS reply + + Serial.println(); + Serial.print(F("Received ")); + Serial.flush(); + + + do + { + if (GPSserial.available()) + { + GPSchar = GPSserial.read(); + } + } + while ((GPSchar != 0xb5) && (millis() < endms)); //use the timeout to ensure a lack of GPS does not cause the program to hang + + if (GPSchar != 0xb5) + { + Serial.println(F("Timeout Error")); + return false; + } + + GPS_Reply[ptr++] = 0xB5; //test if a 0xB5 has been received + + do + { + if (GPSserial.available()) + { + GPS_Reply[ptr++] = GPSserial.read(); + } + } + while ((ptr < length) || (millis() >= endms)); //fill buffer, stop when either ptr is (length-1) or timeout + + + if (millis() >= endms) //check for another timeout + { + Serial.print(F("NoReply Error")); + return false; + } + + for (ptr = 0; ptr < length; ptr++) + { + GPSchar = GPS_Reply[ptr]; + + if (GPSchar < 0x10) + { + Serial.print(F("0")); + } + + Serial.print(GPSchar , HEX); + Serial.print(F(" ")); + } + + Serial.println(); + + if (GPS_CheckAck()) + { + return true; + } + + return false; +} + + +bool GPS_PollNavigation() +{ +#ifdef GPSDebug + Serial.print(F("GPS_PollNavigation() ")); +#endif + Serial.println(F("PollNavigation")); + size_t SIZE = sizeof(PollNavigation); + if (GPS_SendConfig(PollNavigation, SIZE, 10, GPS_attempts)) + { + return true; + } + return false; +} + + + +bool GPS_CheckAck() +{ + +#ifdef GPSDebug + Serial.print(F("GPS_CheckAck() ")); +#endif + + if ((GPS_Reply[0] == 0xB5) && (GPS_Reply[1] == 0x62)) + { +#ifdef GPSDebug + Serial.println(F(" UBX-ACK-ACK")); +#endif + return true; //there has been a UBX-ACK-ACK response + } + else + { +#ifdef GPSDebug + Serial.println(F(" Not UBX-ACK-ACK ")); +#endif + return false; //there has been a UBX-ACK-ACK response + } +} + + + +/********************************************************************* + // GPS configuration commands +*********************************************************************/ + + +bool GPS_ClearConfig() +{ + #ifdef GPSDebug + Serial.print(F("GPS_ClearConfig() ")); + #endif + + Serial.println(F("ClearConfig")); + size_t SIZE = sizeof(ClearConfig); + if (GPS_SendConfig(ClearConfig, SIZE, 10, GPS_attempts)) + { + Serial.println(F("Wait clear")); + Serial.println(); + delay(GPS_Clear_DelaymS); //wait a while for GPS to clear its settings + return true; + } + + return false; +} + + +bool GPS_SetBalloonMode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SetBalloonMode() ")); +#endif + + Serial.println(F("SetBalloonMode")); + size_t SIZE = sizeof(SetBalloonMode); + if (GPS_SendConfig(SetBalloonMode, SIZE, 10, GPS_attempts)) + { + return true; + } + + return false; +} + + +bool GPS_SaveConfig() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SaveConfig() ")); +#endif + + Serial.println(F("SaveConfig")); + size_t SIZE = sizeof(SaveConfig); + + if (GPS_SendConfig(SaveConfig, SIZE, 10, GPS_attempts)) + { + return true; + } + + return false; +} + + +bool GPS_SetCyclicMode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SetCyclicMode() ")); +#endif + + Serial.println(F("SetCyclicMode")); + size_t SIZE = sizeof(SetCyclicMode); + + if (GPS_SendConfig(SetCyclicMode, SIZE, 10, GPS_attempts)) + { + return true; + } + + return false; +} + + +bool GPS_SoftwareBackup() +{ +#ifdef GPSDebug + Serial.print(F("GPS_SoftwareBackup() ")); +#endif + + Serial.println(F("SoftwareBackup")); + size_t SIZE = sizeof(SoftwareBackup); + + if (GPS_SendConfig(SoftwareBackup, SIZE, 0, GPS_attempts)) + { + return true; + } + + return false; +} + + +bool GPS_HotStart() + { + #ifdef GPSDebug + Serial.print(F("GPS_HotStart() ")); + #endif + + Serial.println(F("HotStart")); + GPSserial.println(); + + return true; + +} + + +bool GPS_GLONASSOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GLONASSOff() ")); +#endif + + size_t SIZE = sizeof(GLONASSOff); + return GPS_SendConfig(GLONASSOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGLLOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGLLOff() ")); +#endif + + size_t SIZE = sizeof(GPGLLOff); + return GPS_SendConfig(GPGLLOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGLSOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGLSOff() ")); +#endif + + size_t SIZE = sizeof(GPGLSOff); + return GPS_SendConfig(GPGLSOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGSAOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGSAOff() ")); +#endif + + size_t SIZE = sizeof(GPGSAOff); + return GPS_SendConfig(GPGSAOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GPGSVOff() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GPGSVOff() ")); +#endif + + size_t SIZE = sizeof(GPGSVOff); + return GPS_SendConfig(GPGSVOff, SIZE, 10, GPS_attempts); +} + + +bool GPS_GNSSmode() +{ +#ifdef GPSDebug + Serial.print(F("GPS_GNSSmode() ")); +#endif + + size_t SIZE = sizeof(GNSSmode); + return GPS_SendConfig(GNSSmode, SIZE, 10, GPS_attempts); +} + + +uint8_t GPS_GetByte() //get a byte for GPS +{ + if (GPSserial.available() == 0) + { + return 0xFF; //for compatibility with I2C reading of GPS + } + else + { + return GPSserial.read(); + } +} + + + +/* + MIT license + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..d5317d7 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,22 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino +lib_deps = + adafruit/Adafruit BNO055@^1.4.3 + adafruit/Adafruit BMP3XX Library@^2.0.2 + bblanchon/ArduinoJson@^6.17.3 + adafruit/Adafruit BMP085 Library@^1.2.0 + adafruit/Adafruit SHT31 Library@^2.0.0 + slashdevin/NeoGPS@^4.2.9 +monitor_speed = 115200 diff --git a/src/bmp180.h b/src/bmp180.h new file mode 100644 index 0000000..f2597e6 --- /dev/null +++ b/src/bmp180.h @@ -0,0 +1,45 @@ +#include + +Adafruit_BMP085 bmp180; + +void setup_180() { + if (!bmp180.begin()) { + Serial.println("Could not find a valid BMP085 sensor, check wiring!"); + } +} + +void loop_180(JsonObject &root) { + JsonObject bmp388 = root.createNestedObject("bmp388"); + /* + Serial.print("Temperature = "); + Serial.print(bmp180.readTemperature()); + Serial.println(" *C"); + + Serial.print("Pressure = "); + Serial.print(bmp180.readPressure()); + Serial.println(" Pa"); + + // Calculate altitude assuming 'standard' barometric + // pressure of 1013.25 millibar = 101325 Pascal + Serial.print("Altitude = "); + Serial.print(bmp180.readAltitude()); + Serial.println(" meters"); + + Serial.print("Pressure at sealevel (calculated) = "); + Serial.print(bmp180.readSealevelPressure()); + Serial.println(" Pa"); + + // you can get a more precise measurement of altitude + // if you know the current sea level pressure which will + // vary with weather and such. If it is 1015 millibars + // that is equal to 101500 Pascals. + Serial.print("Real altitude = "); + Serial.print(bmp180.readAltitude(SEALEVELPRESSURE_HPA)); + Serial.println(" meters"); + + Serial.println(); + */ + bmp388["temperature"] = bmp180.readTemperature(); + bmp388["pressure"] = bmp180.readPressure(); + bmp388["altitude"] = bmp180.readAltitude(); +} \ No newline at end of file diff --git a/src/bmp388.h b/src/bmp388.h new file mode 100644 index 0000000..0d53dc6 --- /dev/null +++ b/src/bmp388.h @@ -0,0 +1,40 @@ +#include + +Adafruit_BMP3XX bmp; + +void setup_bmp() { + if (!bmp.begin_I2C()) { // hardware I2C mode, can pass in address & alt Wire + Serial.println("Could not find a valid BMP3 sensor, check wiring!"); + return; + } + bmp.setTemperatureOversampling(BMP3_OVERSAMPLING_8X); + bmp.setPressureOversampling(BMP3_OVERSAMPLING_4X); + bmp.setIIRFilterCoeff(BMP3_IIR_FILTER_COEFF_3); + bmp.setOutputDataRate(BMP3_ODR_50_HZ); +} + +void loop_bmp(JsonObject &root) { +JsonObject bmp388 = root.createNestedObject("bmp388"); + if (! bmp.performReading()) { + Serial.println("Failed to perform reading :("); + return; + } + #ifdef debug + Serial.print("Temperature = "); + Serial.print(bmp.temperature); + Serial.println(" *C"); + + Serial.print("Pressure = "); + Serial.print(bmp.pressure / 100.0); + Serial.println(" hPa"); + + Serial.print("Approx. Altitude = "); + Serial.print(bmp.readAltitude(SEALEVELPRESSURE_HPA)); + Serial.println(" m"); + + Serial.println(); + #endif + bmp388["temperature"] = bmp.temperature; + bmp388["pressure"] = bmp.pressure; + bmp388["altitude"] = bmp.readAltitude(SEALEVELPRESSURE_HPA); +} \ No newline at end of file diff --git a/src/bno055.h b/src/bno055.h new file mode 100644 index 0000000..654b39b --- /dev/null +++ b/src/bno055.h @@ -0,0 +1,160 @@ +#include + +#define BNO055_SAMPLERATE_DELAY_MS (100) +Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x29); + +void displaySensorDetails(void) +{ + sensor_t sensor; + bno.getSensor(&sensor); + Serial.println("------------------------------------"); + Serial.print ("Sensor: "); Serial.println(sensor.name); + Serial.print ("Driver Ver: "); Serial.println(sensor.version); + Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id); + Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println(" xxx"); + Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println(" xxx"); + Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println(" xxx"); + Serial.println("------------------------------------"); + Serial.println(""); +} + +void setup_bno(void) +{ + Serial.println("Orientation Sensor Test"); Serial.println(""); + + /* Initialise the sensor */ + if(!bno.begin()) + { + /* There was a problem detecting the BNO055 ... check your connections */ + Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!"); + return; + } + + /* Use external crystal for better accuracy */ + bno.setExtCrystalUse(true); + + /* Display some basic information on this sensor */ + displaySensorDetails(); +} + +void printEvent(sensors_event_t* event, JsonObject obj) { + double x = -1000000, y = -1000000 , z = -1000000; //dumb values, easy to spot problem + if (event->type == SENSOR_TYPE_ACCELEROMETER) { + #ifdef debug + Serial.print("Accl:"); + #endif + x = event->acceleration.x; + y = event->acceleration.y; + z = event->acceleration.z; + } + else if (event->type == SENSOR_TYPE_ORIENTATION) { + #ifdef debug + Serial.print("Orient:"); + #endif + x = event->orientation.x; + y = event->orientation.y; + z = event->orientation.z; + } + else if (event->type == SENSOR_TYPE_MAGNETIC_FIELD) { + #ifdef debug + Serial.print("Mag:"); + #endif + x = event->magnetic.x; + y = event->magnetic.y; + z = event->magnetic.z; + } + else if (event->type == SENSOR_TYPE_GYROSCOPE) { + #ifdef debug + Serial.print("Gyro:"); + #endif + x = event->gyro.x; + y = event->gyro.y; + z = event->gyro.z; + } + else if (event->type == SENSOR_TYPE_ROTATION_VECTOR) { + #ifdef debug + Serial.print("Rot:"); + #endif + x = event->gyro.x; + y = event->gyro.y; + z = event->gyro.z; + } + else if (event->type == SENSOR_TYPE_LINEAR_ACCELERATION) { + #ifdef debug + Serial.print("Linear:"); + #endif + x = event->acceleration.x; + y = event->acceleration.y; + z = event->acceleration.z; + } + else { + #ifdef debug + Serial.print("Unk:"); + #endif + } + obj["x"] = x; + obj["y"] = y; + obj["z"] = z; + #ifdef debug + Serial.print("\tx= "); + Serial.print(x); + Serial.print(" |\ty= "); + Serial.print(y); + Serial.print(" |\tz= "); + Serial.println(z); + #endif +} + + +void loop_bno(JsonObject &root) +{ + JsonObject bno055 = root.createNestedObject("bno055"); + sensors_event_t orientationData , angVelocityData , linearAccelData, magnetometerData, accelerometerData, gravityData; + bno.getEvent(&orientationData, Adafruit_BNO055::VECTOR_EULER); + bno.getEvent(&angVelocityData, Adafruit_BNO055::VECTOR_GYROSCOPE); + bno.getEvent(&linearAccelData, Adafruit_BNO055::VECTOR_LINEARACCEL); + bno.getEvent(&magnetometerData, Adafruit_BNO055::VECTOR_MAGNETOMETER); + bno.getEvent(&accelerometerData, Adafruit_BNO055::VECTOR_ACCELEROMETER); + bno.getEvent(&gravityData, Adafruit_BNO055::VECTOR_GRAVITY); + JsonObject orientation_data = bno055.createNestedObject("orientation_data"); + printEvent(&orientationData, orientation_data); + JsonObject ang_velocity_data = bno055.createNestedObject("ang_velocity_data"); + printEvent(&angVelocityData, ang_velocity_data); + JsonObject linear_accel_data = bno055.createNestedObject("linear_accel_data"); + printEvent(&linearAccelData, linear_accel_data); + JsonObject magnetometer_data = bno055.createNestedObject("magnetometer_data"); + printEvent(&magnetometerData, magnetometer_data); + JsonObject accelerometer_data = bno055.createNestedObject("accelerometer_data"); + printEvent(&accelerometerData, accelerometer_data); + JsonObject gravity_data = bno055.createNestedObject("gravity_data"); + printEvent(&gravityData, gravity_data); + + int8_t boardTemp = bno.getTemp(); + #ifdef bebug + Serial.println(); + Serial.print(F("temperature: ")); + Serial.println(boardTemp); + #endif + bno055["temperature"] = boardTemp; + + uint8_t system, gyro, accel, mag = 0; + bno.getCalibration(&system, &gyro, &accel, &mag); + JsonObject calibration = bno055.createNestedObject("calibration"); + calibration["system"] = system; + calibration["gyro"] = gyro; + calibration["accel"] = accel; + calibration["mag"] = mag; + #ifdef debug + Serial.println(); + Serial.print("Calibration: Sys="); + Serial.print(system); + Serial.print(" Gyro="); + Serial.print(gyro); + Serial.print(" Accel="); + Serial.print(accel); + Serial.print(" Mag="); + Serial.println(mag); + + Serial.println("--"); + #endif +} \ No newline at end of file diff --git a/src/gps.h b/src/gps.h new file mode 100644 index 0000000..d6e60b5 --- /dev/null +++ b/src/gps.h @@ -0,0 +1,28 @@ +#include +#ifndef GPSport_h +#define GPSport_h + +#define gpsPort Serial2 +#define GPS_PORT_NAME "Serial2" + +#endif + + +static NMEAGPS gps; +gps_fix fix; + +void setup_gps() +{ + gpsPort.begin(9600); +} + +void loop_gps(JsonObject &root) +{ + if (gps.available( gpsPort)) { + fix = gps.read(); + } + JsonObject gps = root.createNestedObject("gps"); + gps["lat"] = fix.latitude(); + gps["lon"] = fix.longitude(); + gps["alt"] = fix.altitude(); +} \ No newline at end of file diff --git a/src/lora.h b/src/lora.h new file mode 100644 index 0000000..b100f5a --- /dev/null +++ b/src/lora.h @@ -0,0 +1,93 @@ +#include +#include + +#define NSS 5 //select on LoRa device +#define SCK 18 //SCK on SPI3 +#define MISO 19 //MISO on SPI3 +#define MOSI 23 //MOSI on SPI3 + +#define NRESET 35 //reset on LoRa device +#define DIO0 33 //DIO0 on LoRa device, used for RX and TX done +#define DIO1 32 //DIO1 on LoRa device, normally not used so set to -1 +#define DIO2 -1 //DIO2 on LoRa device, normally not used so set to -1 + +#define LORA_DEVICE DEVICE_SX1278 //this is the device we are using + +//******* Setup LoRa Test Parameters Here ! *************** + +//LoRa Modem Parameters +const uint32_t Frequency = 429700000; //frequency of transmissions +const uint32_t Offset = 0; //offset frequency for calibration purposes + +const uint8_t Bandwidth = LORA_BW_062; //LoRa bandwidth +const uint8_t SpreadingFactor = LORA_SF12; //LoRa spreading factor +const uint8_t CodeRate = LORA_CR_4_8; //LoRa coding rate +const uint8_t Optimisation = LDRO_AUTO; //low data rate optimisation setting + +const int8_t TXpower = 20; //LoRa transmit power in dBm + +SX127XLT LT; + +void setup_lora() { + SPI.begin(SCK, MISO, MOSI, NSS); +if (LT.begin(NSS, NRESET, DIO0, DIO1, DIO2, LORA_DEVICE)) + { + Serial.println(F("LoRa Device found")); + } + else + { + Serial.println(F("No device responding")); + } + + LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, Optimisation); + + Serial.println(); + LT.printModemSettings(); //reads and prints the configured LoRa settings, useful check + Serial.println(); + LT.printOperatingSettings(); //reads and prints the configured operating settings, useful check + Serial.println(); + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeFloat(0.0); //add latitude + LT.writeFloat(0.0); //add longitude + LT.writeFloat(0.0); //add altitude + LT.writeUint8(0); //add number of satellites + LT.writeUint8(0); //add tracker status + uint8_t len = LT.endWriteSXBuffer(); //close buffer write + + LT.transmitSXBuffer(0, len, 10000, TXpower, WAIT_TX); +} + +void sendLocation(int32_t Lat, int32_t Lon) +{ + uint8_t len; + + LT.startWriteSXBuffer(0); //initialise buffer write at address 0 + LT.writeInt32(Lat); //add latitude + LT.writeInt32(Lon); //add longitude + LT.writeInt16(fix.alt.whole); //add altitude + LT.writeInt8(fix.alt.frac); + LT.writeUint8(fix.satellites); //add number of satellites + LT.writeUint8(fix.status); //add tracker status + len = LT.endWriteSXBuffer(); //close buffer write + /* + Serial.print("Sending data: "); + Serial.print(fix.alt.whole); + Serial.print("."); + Serial.println(fix.alt.frac); + */ + LT.transmitSXBuffer(0, len, 10000, TXpower, NO_WAIT); +} + + +void loop_lora_pre() { + if (gps.available( gpsPort) && digitalRead(DIO0)) { + fix = gps.read(); + sendLocation(fix.latitudeL(), fix.longitudeL()); + } +} + +void loop_lora() { + if (digitalRead(DIO0)) { + sendLocation(fix.latitudeL(), fix.longitudeL()); + } +} diff --git a/src/main copy.old b/src/main copy.old new file mode 100644 index 0000000..2e75c9f --- /dev/null +++ b/src/main copy.old @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "esp_wifi.h" + +String maclist[64][3]; +int listcount = 0; + +uint8_t beacon_raw[] = { + 0x80, 0x00, // 0-1: Frame Control + 0x00, 0x00, // 2-3: Duration + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 4-9: Destination address (broadcast) + 0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06, // 10-15: Source address + 0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06, // 16-21: BSSID + 0x00, 0x00, // 22-23: Sequence / fragment number + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 24-31: Timestamp (GETS OVERWRITTEN TO 0 BY HARDWARE) + 0x64, 0x00, // 32-33: Beacon interval + 0x31, 0x04, // 34-35: Capability info + 0x00, 0x00, /* FILL CONTENT HERE */ // 36-38: SSID parameter set, 0x00:length:content + 0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, // 39-48: Supported rates + 0x03, 0x01, 0x01, // 49-51: DS Parameter set, current channel 1 (= 0x01), + 0x05, 0x04, 0x01, 0x02, 0x00, 0x00, // 52-57: Traffic Indication Map + +}; + +String KnownMac[10][2] = { // Put devices you want to be reconized + {"Will-Phone","BC2DEF21B423"}, + {"Will-PC","E894Fffffff3"}, + {"NAME","MACADDRESS"}, + {"NAME","MACADDRESS"}, + {"NAME","MACADDRESS"}, + {"NAME","MACADDRESS"}, + {"NAME","MACADDRESS"}, + {"NAME","MACADDRESS"}, + {"NAME","MACADDRESS"} + +}; + +String defaultTTL = "60"; // Maximum time (Apx seconds) elapsed before device is consirded offline + +const wifi_promiscuous_filter_t filt={ //Idk what this does + .filter_mask=WIFI_PROMIS_FILTER_MASK_MGMT|WIFI_PROMIS_FILTER_MASK_DATA +}; + +typedef struct { // or this + uint8_t mac[6]; +} __attribute__((packed)) MacAddr; + +typedef struct { // still dont know much about this + int16_t fctl; + int16_t duration; + MacAddr da; + MacAddr sa; + MacAddr bssid; + int16_t seqctl; + unsigned char payload[]; +} __attribute__((packed)) WifiMgmtHdr; + + + +#define maxCh 13 //max Channel -> US = 11, EU = 13, Japan = 14 + + +int curChannel = 10; + + +void sniffer(void* buf, wifi_promiscuous_pkt_type_t type) { //This is where packets end up after they get sniffed + wifi_promiscuous_pkt_t *p = (wifi_promiscuous_pkt_t*)buf; // Dont know what these 3 lines do + int len = p->rx_ctrl.sig_len; + WifiMgmtHdr *wh = (WifiMgmtHdr*)p->payload; + len -= sizeof(WifiMgmtHdr); + if (len < 0){ + Serial.println("Receuved 0"); + return; + } + String packet; + String mac; + int fctl = ntohs(wh->fctl); + for(int i=8;i<=8+6+1;i++){ // This reads the first couple of bytes of the packet. This is where you can read the whole packet replaceing the "8+6+1" with "p->rx_ctrl.sig_len" + packet += String(p->payload[i],HEX); + } + for(int i=4;i<=15;i++){ // This removes the 'nibble' bits from the stat and end of the data we want. So we only get the mac address. + mac += packet[i]; + } + mac.toUpperCase(); + + + int added = 0; + for(int i=0;i<=63;i++){ // checks if the MAC address has been added before + if(mac == maclist[i][0]){ + maclist[i][1] = defaultTTL; + if(maclist[i][2] == "OFFLINE"){ + maclist[i][2] = "0"; + } + added = 1; + } + } + + if(added == 0){ // If its new. add it to the array. + maclist[listcount][0] = mac; + maclist[listcount][1] = defaultTTL; + //Serial.println(mac); + listcount ++; + if(listcount >= 64){ + Serial.println("Too many addresses"); + listcount = 0; + } + } +} + + + +//===== SETUP =====// +void setup() { + + /* start Serial */ + Serial.begin(9600); + + /* setup wifi */ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + esp_wifi_init(&cfg); + esp_wifi_set_storage(WIFI_STORAGE_RAM); + esp_wifi_set_mode(WIFI_MODE_STA); + esp_wifi_start(); + esp_wifi_set_promiscuous(true); + esp_wifi_set_promiscuous_filter(&filt); + esp_wifi_set_promiscuous_rx_cb(&sniffer); + esp_wifi_set_channel(curChannel, WIFI_SECOND_CHAN_NONE); + + Serial.println("starting!"); +} + +void purge(){ // This maanges the TTL + for(int i=0;i<=63;i++){ + if(!(maclist[i][0] == "")){ + int ttl = (maclist[i][1].toInt()); + ttl --; + if(ttl <= 0){ + Serial.println("OFFLINE: " + maclist[i][0]); + maclist[i][2] = "OFFLINE"; + maclist[i][1] = defaultTTL; + }else{ + maclist[i][1] = String(ttl); + } + } + } +} + +void updatetime(){ // This updates the time the device has been online for + for(int i=0;i<=63;i++){ + if(!(maclist[i][0] == "")){ + if(maclist[i][2] == "")maclist[i][2] = "0"; + if(!(maclist[i][2] == "OFFLINE")){ + int timehere = (maclist[i][2].toInt()); + timehere ++; + maclist[i][2] = String(timehere); + } + + Serial.println(maclist[i][0] + " : " + maclist[i][2]); + + } + } +} + +void showpeople(){ // This checks if the MAC is in the reckonized list and then displays it on the OLED and/or prints it to serial. + String forScreen = ""; + for(int i=0;i<=63;i++){ + String tmp1 = maclist[i][0]; + if(!(tmp1 == "")){ + for(int j=0;j<=9;j++){ + String tmp2 = KnownMac[j][1]; + if(tmp1 == tmp2){ + forScreen += (KnownMac[j][0] + " : " + maclist[i][2] + "\n"); + Serial.print(KnownMac[j][0] + " : " + tmp1 + " : " + maclist[i][2] + "\n -- \n"); + } + } + } + } +} + +//===== LOOP =====// +void loop() { + Serial.println("Changed channel:" + String(curChannel)); + if(curChannel > maxCh){ + curChannel = 1; + } + esp_wifi_set_channel(curChannel, WIFI_SECOND_CHAN_NONE); + delay(1000); + updatetime(); + purge(); + showpeople(); + switch(esp_wifi_80211_tx(ESP_IF_WIFI_STA, packet, sizeof(packet), 0)) + { + case ESP_OK: + printf("Packet in the air!\n"); + break; + case ESP_ERR_WIFI_IF: + printf("Invalid interface\n"); + break; + case ESP_ERR_INVALID_ARG: + printf("Invalid parameter\n"); + break; + default: + printf("Some other error I don't want to control now\n"); + break; + } + //curChannel++; +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..c85af92 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,132 @@ +#define SEALEVELPRESSURE_HPA (1013.25) +#include +#include +#include +#include "sdcard.h" +#include "wifi_setup.h" +#include "bmp388.h" +#include "bmp180.h" +#include "bno055.h" +#include "sht31.h" +#include "gps.h" +#include "lora.h" + +#define PWM 34 + +bool deployed = 0; + +void deploy() { + digitalWrite(PWM, HIGH); + delay(1000); + digitalWrite(PWM, LOW); + delay(1000); +} + +void TaskDeploycode( void * pvParameters ){ + deploy(); + deployed = 1; +} +TaskHandle_t TaskDelpoy; + +float original_height; +bool flight = false; +unsigned long start = 0; +File file; +bool save = false; +float previous_height; +bool altitude = false; + +void setup(){ + Serial.begin(115200); + Wire.setClock(800000); + setup_sdcard(); + setup_bmp(); + //setup_180(); + setup_bno(); + setup_sht(); + setup_gps(); + setup_lora(); + Serial.println(); + Serial.println("Configuring access point..."); + + connectToWiFi(networkName, networkPswd); + ESP_ERROR_CHECK(esp_wifi_set_protocol (WIFI_IF_STA, WIFI_PROTOCOL_11B)); + wifi_country_t country_info = {"JP", 1, 14, WIFI_COUNTRY_POLICY_MANUAL}; + ESP_ERROR_CHECK(esp_wifi_set_country(&country_info)); + ESP_ERROR_CHECK(esp_wifi_set_max_tx_power(80)); + ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); + pinMode(PWM, OUTPUT); + + uint8_t buffer[50] = "test.txt"; + while(1) { + if (connected) { + bmp.performReading(); + original_height = bmp.readAltitude(SEALEVELPRESSURE_HPA); + udp.parsePacket(); + if(udp.read(buffer, 50) > 0){ + Serial.print("Server to client: "); + Serial.println((char *)buffer); + start = millis(); + break; + } + } + delay(100); + loop_lora_pre(); + } + file = SD_MMC.open((char *)buffer, FILE_WRITE); + previous_height = original_height; + Serial.println(original_height); +} + +void loop(){ + DynamicJsonDocument doc(1024); + JsonObject obj = doc.to(); + obj["seconds"] = millis()/1000; + obj["deployed"] = deployed; + obj["save"] = save; + obj["flight"] = flight; + obj["altitude"] = altitude; + loop_bmp(obj); + loop_bno(obj); + loop_sht(obj); + loop_gps(obj); + loop_lora(); + serializeJson(doc, file); + file.print('\n'); + file.flush(); + //only send data when connected + if(connected){ + //Send a packet + udp.beginPacket(udpAddress,udpPort); + serializeJson(doc, udp); + udp.println(); + udp.endPacket(); + } + if (!save & !deployed & ((obj["bmp388"]["altitude"].as() - original_height) > 50.0)) { + Serial.println("Deploy is save"); + save = true; + } + if (save & !deployed & (((obj["bmp388"]["altitude"].as() - previous_height) * 5) < 3.0)) { + Serial.println("Start deploy altitude"); + altitude = true; + deployed = true; + digitalWrite(PWM, HIGH); + } + if (!flight & (max(max(abs(obj["bno055"]["linear_accel_data"]["x"].as()), + abs(obj["bno055"]["linear_accel_data"]["y"].as())), + max(abs(obj["bno055"]["linear_accel_data"]["y"].as()), + abs(obj["bno055"]["linear_accel_data"]["z"].as()))) > 5.0)) { + Serial.println("Timer trigger"); + start = millis(); + flight = true; + } + if(flight & ((millis()-start) > 8*1000) & !deployed) { // Вот таймер + Serial.println("Start deploy timeout"); + deployed = 1; + digitalWrite(PWM, HIGH); + //xTaskCreate(TaskDeploycode,"TaskDeploy",10000,NULL,1,&TaskDelpoy); + } + //Wait for 1 second + previous_height = obj["bmp388"]["altitude"].as(); + delay(200); +} diff --git a/src/main.cpp.old b/src/main.cpp.old new file mode 100644 index 0000000..07c7cda --- /dev/null +++ b/src/main.cpp.old @@ -0,0 +1,150 @@ +#include +#include +#include +#include +#include + + +#include "freertos/FreeRTOS.h" + +#include "esp_event_loop.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_wifi.h" +#include "esp_vfs_fat.h" + +#include "nvs_flash.h" +#include "string.h" + +uint8_t beacon_raw[] = { + 0x80, 0x00, // 0-1: Frame Control + 0x00, 0x00, // 2-3: Duration + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 4-9: Destination address (broadcast) + 0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06, // 10-15: Source address + 0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06, // 16-21: BSSID + 0x00, 0x00, // 22-23: Sequence / fragment number + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 24-31: Timestamp (GETS OVERWRITTEN TO 0 BY HARDWARE) + 0x64, 0x00, // 32-33: Beacon interval + 0x31, 0x04, // 34-35: Capability info + 0x00, 0x00, /* FILL CONTENT HERE */ // 36-38: SSID parameter set, 0x00:length:content + 0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, // 39-48: Supported rates + 0x03, 0x01, 0x01, // 49-51: DS Parameter set, current channel 1 (= 0x01), + 0x05, 0x04, 0x01, 0x02, 0x00, 0x00, // 52-57: Traffic Indication Map + +}; + +char *rick_ssids[] = { + "01 Never gonna give you up", + "02 Never gonna let you down", + "03 Never gonna run around", + "04 and desert you", + "05 Never gonna make you cry", + "06 Never gonna say goodbye", + "07 Never gonna tell a lie", + "08 and hurt you" +}; + +#define BEACON_SSID_OFFSET 38 +#define SRCADDR_OFFSET 10 +#define BSSID_OFFSET 16 +#define SEQNUM_OFFSET 22 +#define TOTAL_LINES (sizeof(rick_ssids) / sizeof(char *)) + +esp_err_t event_handler(void *ctx, system_event_t *event) { + return ESP_OK; +} + +void spam_task(void *pvParameter) { + uint8_t line = 0; + + // Keep track of beacon sequence numbers on a per-songline-basis + uint16_t seqnum[TOTAL_LINES] = { 0 }; + + for (;;) { + vTaskDelay(100 / TOTAL_LINES / portTICK_PERIOD_MS); + + // Insert line of Rick Astley's "Never Gonna Give You Up" into beacon packet + printf("%i %i %s\r\n", strlen(rick_ssids[line]), TOTAL_LINES, rick_ssids[line]); + + uint8_t beacon_rick[200]; + memcpy(beacon_rick, beacon_raw, BEACON_SSID_OFFSET - 1); + beacon_rick[BEACON_SSID_OFFSET - 1] = strlen(rick_ssids[line]); + memcpy(&beacon_rick[BEACON_SSID_OFFSET], rick_ssids[line], strlen(rick_ssids[line])); + memcpy(&beacon_rick[BEACON_SSID_OFFSET + strlen(rick_ssids[line])], &beacon_raw[BEACON_SSID_OFFSET], sizeof(beacon_raw) - BEACON_SSID_OFFSET); + + // Last byte of source address / BSSID will be line number - emulate multiple APs broadcasting one song line each + beacon_rick[SRCADDR_OFFSET + 5] = line; + beacon_rick[BSSID_OFFSET + 5] = line; + + // Update sequence number + beacon_rick[SEQNUM_OFFSET] = (seqnum[line] & 0x0f) << 4; + beacon_rick[SEQNUM_OFFSET + 1] = (seqnum[line] & 0xff0) >> 4; + seqnum[line]++; + if (seqnum[line] > 0xfff) + seqnum[line] = 0; + + esp_wifi_80211_tx(WIFI_IF_AP, beacon_rick, sizeof(beacon_raw) + strlen(rick_ssids[line]), false); + + if (++line >= TOTAL_LINES) + line = 0; + } +} + +const wifi_promiscuous_filter_t filt={ //Idk what this does + .filter_mask=WIFI_PROMIS_FILTER_MASK_MGMT|WIFI_PROMIS_FILTER_MASK_DATA +}; + + + +void setup(void) { + //Serial.begin(9600); + nvs_flash_init(); + tcpip_adapter_init(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + + ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + + // Init dummy AP to specify a channel and get WiFi hardware into + // a mode where we can send the actual fake beacon frames. + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); + wifi_config_t ap_config = {}; + memcpy(ap_config.ap.ssid, "esp32-beaconspam", sizeof("esp32-beaconspam")); + memcpy(ap_config.ap.password, "dummypassword", sizeof("dummypassword")); + ap_config.ap.ssid_len = 0; + ap_config.ap.channel = 1; + ap_config.ap.authmode = WIFI_AUTH_WPA2_PSK; + ap_config.ap.ssid_hidden = 1; + ap_config.ap.max_connection = 4; + ap_config.ap.beacon_interval = 60000; + ESP_ERROR_CHECK(esp_wifi_set_protocol (WIFI_IF_AP, WIFI_PROTOCOL_11B)); + wifi_country_t country_info = {"JP", 1, 14, WIFI_COUNTRY_POLICY_MANUAL}; + ESP_ERROR_CHECK(esp_wifi_set_country(&country_info)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); + ESP_ERROR_CHECK(esp_wifi_set_max_tx_power(80)); + /* + gpio_set_pull_mode(GPIO_NUM_15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes + gpio_set_pull_mode(GPIO_NUM_2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes + gpio_set_pull_mode(GPIO_NUM_4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only + gpio_set_pull_mode(GPIO_NUM_12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only + gpio_set_pull_mode(GPIO_NUM_13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes + */ + sdmmc_host_t host_config = SDMMC_HOST_DEFAULT(); + host_config.flags = SDMMC_HOST_FLAG_4BIT; + host_config.max_freq_khz = SDMMC_FREQ_PROBING; + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + slot_config.width = 4; + esp_vfs_fat_mount_config_t mount_config = {false, 16, 0}; + sdmmc_card_t* card; + ESP_ERROR_CHECK(esp_vfs_fat_sdmmc_mount("/sd", &host_config, &slot_config, &mount_config, &card)); + + xTaskCreate(&spam_task, "spam_task", 2048, NULL, 5, NULL); +} + +void loop(void) { + delay(1000); +} diff --git a/src/sdcard.h b/src/sdcard.h new file mode 100644 index 0000000..497abcb --- /dev/null +++ b/src/sdcard.h @@ -0,0 +1,242 @@ +#include "FS.h" +#include "SD_MMC.h" + +void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ + #ifdef debug + Serial.printf("Listing directory: %s\n", dirname); + #endif + + File root = fs.open(dirname); + if(!root){ + #ifdef debug + Serial.println("Failed to open directory"); + #endif + return; + } + if(!root.isDirectory()){ + #ifdef debug + Serial.println("Not a directory"); + #endif + return; + } + + File file = root.openNextFile(); + while(file){ + if(file.isDirectory()){ + #ifdef debug + Serial.print(" DIR : "); + #endif + Serial.println(file.name()); + if(levels){ + listDir(fs, file.name(), levels -1); + } + } else { + #ifdef debug + Serial.print(" FILE: "); + #endif + Serial.print(file.name()); + #ifdef debug + Serial.print(" SIZE: "); + #endif + Serial.println(file.size()); + } + file = root.openNextFile(); + } +} + +void createDir(fs::FS &fs, const char * path){ + #ifdef debug + Serial.printf("Creating Dir: %s\n", path); + #endif + if(fs.mkdir(path)){ + #ifdef debug + Serial.println("Dir created"); + #endif + } else { + #ifdef debug + Serial.println("mkdir failed"); + #endif + } +} + +void removeDir(fs::FS &fs, const char * path){ + #ifdef debug + Serial.printf("Removing Dir: %s\n", path); + #endif + if(fs.rmdir(path)){ + #ifdef debug + Serial.println("Dir removed"); + #endif + } else { + #ifdef debug + Serial.println("rmdir failed"); + #endif + } +} + +void readFile(fs::FS &fs, const char * path){ + #ifdef debug + Serial.printf("Reading file: %s\n", path); +#endif + File file = fs.open(path); + if(!file){ + #ifdef debug + Serial.println("Failed to open file for reading"); + #endif + return; + } +#ifdef debug + Serial.print("Read from file: "); + #endif + while(file.available()){ + Serial.write(file.read()); + } +} + +void writeFile(fs::FS &fs, const char * path, const char * message){ + #ifdef debug + Serial.printf("Writing file: %s\n", path); +#endif + File file = fs.open(path, FILE_WRITE); + if(!file){ + #ifdef debug + Serial.println("Failed to open file for writing"); + #endif + return; + } + if(file.print(message)){ + #ifdef debug + Serial.println("File written"); + #endif + } else { + #ifdef debug + Serial.println("Write failed"); + #endif + } +} + +void appendFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Appending to file: %s\n", path); + + File file = fs.open(path, FILE_APPEND); + if(!file){ + Serial.println("Failed to open file for appending"); + return; + } + if(file.print(message)){ + Serial.println("Message appended"); + } else { + Serial.println("Append failed"); + } +} + +void renameFile(fs::FS &fs, const char * path1, const char * path2){ + Serial.printf("Renaming file %s to %s\n", path1, path2); + if (fs.rename(path1, path2)) { + Serial.println("File renamed"); + } else { + Serial.println("Rename failed"); + } +} + +void deleteFile(fs::FS &fs, const char * path){ + Serial.printf("Deleting file: %s\n", path); + if(fs.remove(path)){ + Serial.println("File deleted"); + } else { + Serial.println("Delete failed"); + } +} + +void testFileIO(fs::FS &fs, const char * path){ + File file = fs.open(path); + static uint8_t buf[512]; + size_t len = 0; + uint32_t start = millis(); + uint32_t end = start; + if(file){ + len = file.size(); + size_t flen = len; + start = millis(); + while(len){ + size_t toRead = len; + if(toRead > 512){ + toRead = 512; + } + file.read(buf, toRead); + len -= toRead; + } + end = millis() - start; + Serial.printf("%u bytes read for %u ms\n", flen, end); + file.close(); + } else { + Serial.println("Failed to open file for reading"); + } + + + file = fs.open(path, FILE_WRITE); + if(!file){ + Serial.println("Failed to open file for writing"); + return; + } + + size_t i; + start = millis(); + for(i=0; i<2048; i++){ + file.write(buf, 512); + } + end = millis() - start; + Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); + file.close(); +} + +void setup_sdcard() { + if(!SD_MMC.begin("/sdcard", true, false)){ + Serial.println("Card Mount Failed"); + return; + } + uint8_t cardType = SD_MMC.cardType(); + + if(cardType == CARD_NONE){ + Serial.println("No SD_MMC card attached"); + return; + } + + Serial.print("SD_MMC Card Type: "); + if(cardType == CARD_MMC){ + Serial.println("MMC"); + } else if(cardType == CARD_SD){ + Serial.println("SDSC"); + } else if(cardType == CARD_SDHC){ + Serial.println("SDHC"); + } else { + Serial.println("UNKNOWN"); + } + + uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024); + Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize); + /* + listDir(SD_MMC, "/", 0); + createDir(SD_MMC, "/mydir"); + listDir(SD_MMC, "/", 0); + removeDir(SD_MMC, "/mydir"); + listDir(SD_MMC, "/", 2); + writeFile(SD_MMC, "/hello.txt", "Hello "); + appendFile(SD_MMC, "/hello.txt", "World!\n"); + readFile(SD_MMC, "/hello.txt"); + deleteFile(SD_MMC, "/foo.txt"); + renameFile(SD_MMC, "/hello.txt", "/foo.txt"); + readFile(SD_MMC, "/foo.txt"); + */ + /* + xTaskCreate( + Task1code, + "Task1", + 10000, + NULL, + 1, + &Task1); + */ + //Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024)); + //Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024)); +} diff --git a/src/sht31.h b/src/sht31.h new file mode 100644 index 0000000..61129af --- /dev/null +++ b/src/sht31.h @@ -0,0 +1,52 @@ +#include + +Adafruit_SHT31 sht31 = Adafruit_SHT31(); + +void setup_sht() { + if (! sht31.begin(0x44)) { // Set to 0x45 for alternate i2c addr + Serial.println("Couldn't find SHT31"); + } + sht31.heater(1); + Serial.print("Heater Enabled State: "); + if (sht31.isHeaterEnabled()) + Serial.println("ENABLED"); + else + Serial.println("DISABLED"); +} + +void loop_sht(JsonObject &root) { + JsonObject sht = root.createNestedObject("sht31"); + float t = sht31.readTemperature(); + float h = sht31.readHumidity(); + sht["temperature"] = t; + sht["humidity"] = h; + +/* + if (! isnan(t)) { // check if 'is not a number' + Serial.print("Temp *C = "); Serial.print(t); Serial.print("\t\t"); + } else { + Serial.println("Failed to read temperature"); + } + + if (! isnan(h)) { // check if 'is not a number' + Serial.print("Hum. % = "); Serial.println(h); + } else { + Serial.println("Failed to read humidity"); + } + + // Toggle heater enabled state every 30 seconds + // An ~3.0 degC temperature increase can be noted when heater is enabled + /* + if (++loopCnt == 30) { + enableHeater = !enableHeater; + sht31.heater(enableHeater); + Serial.print("Heater Enabled State: "); + if (sht31.isHeaterEnabled()) + Serial.println("ENABLED"); + else + Serial.println("DISABLED"); + + loopCnt = 0; + } + */ +} \ No newline at end of file diff --git a/src/wifi_setup.h b/src/wifi_setup.h new file mode 100644 index 0000000..4a9c1bc --- /dev/null +++ b/src/wifi_setup.h @@ -0,0 +1,47 @@ +#include +#include +#include "esp_wifi.h" + +const char * networkName = "Overlord"; +const char * networkPswd = "justmonika"; + +const char * udpAddress = "192.168.2.255"; +const int udpPort = 3333; + +boolean connected = false; + +WiFiUDP udp; + +//wifi event handler +void WiFiEvent(WiFiEvent_t event){ + switch(event) { + case SYSTEM_EVENT_STA_GOT_IP: + //When connected set + Serial.print("WiFi connected! IP address: "); + Serial.println(WiFi.localIP()); + //initializes the UDP state + //This initializes the transfer buffer + udp.begin(WiFi.localIP(),udpPort); + connected = true; + break; + case SYSTEM_EVENT_STA_DISCONNECTED: + Serial.println("WiFi lost connection"); + connected = false; + break; + default: break; + } +} + +void connectToWiFi(const char * ssid, const char * pwd){ + Serial.println("Connecting to WiFi network: " + String(ssid)); + + // delete old config + WiFi.disconnect(true); + //register event handler + WiFi.onEvent(WiFiEvent); + + //Initiate connection + WiFi.begin(ssid, pwd); + + Serial.println("Waiting for WIFI connection..."); +} \ No newline at end of file diff --git a/test/README b/test/README new file mode 100644 index 0000000..b94d089 --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html