diff --git a/examples/LoRaWAN/LoRaWAN_ABP/LoRaBoards.cpp b/examples/LoRaWAN/LoRaWAN_ABP/LoRaBoards.cpp new file mode 100644 index 0000000..aaf1e77 --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_ABP/LoRaBoards.cpp @@ -0,0 +1,850 @@ +/** + * @file boards.cpp + * @author Lewis He (lewishe@outlook.com) + * @license MIT + * @copyright Copyright (c) 2024 ShenZhen XinYuan Electronic Technology Co., Ltd + * @date 2024-04-24 + * @last-update 2024-08-07 + * + */ + +#include "LoRaBoards.h" + +#if defined(HAS_SDCARD) +SPIClass SDCardSPI(HSPI); +#endif + + +#if defined(ARDUINO_ARCH_STM32) +HardwareSerial SerialGPS(GPS_RX_PIN, GPS_TX_PIN); +#endif + +#if defined(ARDUINO_ARCH_ESP32) +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0) +#include "hal/gpio_hal.h" +#endif +#include "driver/gpio.h" +#endif //ARDUINO_ARCH_ESP32 + + +DISPLAY_MODEL *u8g2 = NULL; +static DevInfo_t devInfo; + +#ifdef HAS_GPS +static bool find_gps = false; +#endif + + + +#ifdef HAS_PMU +XPowersLibInterface *PMU = NULL; +bool pmuInterrupt; + +static void setPmuFlag() +{ + pmuInterrupt = true; +} +#endif + +bool beginPower() +{ +#ifdef HAS_PMU + if (!PMU) { + PMU = new XPowersAXP2101(PMU_WIRE_PORT); + if (!PMU->init()) { + Serial.println("Warning: Failed to find AXP2101 power management"); + delete PMU; + PMU = NULL; + } else { + Serial.println("AXP2101 PMU init succeeded, using AXP2101 PMU"); + } + } + + if (!PMU) { + PMU = new XPowersAXP192(PMU_WIRE_PORT); + if (!PMU->init()) { + Serial.println("Warning: Failed to find AXP192 power management"); + delete PMU; + PMU = NULL; + } else { + Serial.println("AXP192 PMU init succeeded, using AXP192 PMU"); + } + } + + if (!PMU) { + return false; + } + + PMU->setChargingLedMode(XPOWERS_CHG_LED_CTRL_CHG); + + pinMode(PMU_IRQ, INPUT_PULLUP); + attachInterrupt(PMU_IRQ, setPmuFlag, FALLING); + + if (PMU->getChipModel() == XPOWERS_AXP192) { + + PMU->setProtectedChannel(XPOWERS_DCDC3); + + // lora + PMU->setPowerChannelVoltage(XPOWERS_LDO2, 3300); + // gps + PMU->setPowerChannelVoltage(XPOWERS_LDO3, 3300); + // oled + PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300); + + PMU->enablePowerOutput(XPOWERS_LDO2); + PMU->enablePowerOutput(XPOWERS_LDO3); + + //protected oled power source + PMU->setProtectedChannel(XPOWERS_DCDC1); + //protected esp32 power source + PMU->setProtectedChannel(XPOWERS_DCDC3); + // enable oled power + PMU->enablePowerOutput(XPOWERS_DCDC1); + + //disable not use channel + PMU->disablePowerOutput(XPOWERS_DCDC2); + + PMU->disableIRQ(XPOWERS_AXP192_ALL_IRQ); + + PMU->enableIRQ(XPOWERS_AXP192_VBUS_REMOVE_IRQ | + XPOWERS_AXP192_VBUS_INSERT_IRQ | + XPOWERS_AXP192_BAT_CHG_DONE_IRQ | + XPOWERS_AXP192_BAT_CHG_START_IRQ | + XPOWERS_AXP192_BAT_REMOVE_IRQ | + XPOWERS_AXP192_BAT_INSERT_IRQ | + XPOWERS_AXP192_PKEY_SHORT_IRQ + ); + + } else if (PMU->getChipModel() == XPOWERS_AXP2101) { + +#if defined(CONFIG_IDF_TARGET_ESP32) + //Unuse power channel + PMU->disablePowerOutput(XPOWERS_DCDC2); + PMU->disablePowerOutput(XPOWERS_DCDC3); + PMU->disablePowerOutput(XPOWERS_DCDC4); + PMU->disablePowerOutput(XPOWERS_DCDC5); + PMU->disablePowerOutput(XPOWERS_ALDO1); + PMU->disablePowerOutput(XPOWERS_ALDO4); + PMU->disablePowerOutput(XPOWERS_BLDO1); + PMU->disablePowerOutput(XPOWERS_BLDO2); + PMU->disablePowerOutput(XPOWERS_DLDO1); + PMU->disablePowerOutput(XPOWERS_DLDO2); + + // GNSS RTC PowerVDD 3300mV + PMU->setPowerChannelVoltage(XPOWERS_VBACKUP, 3300); + PMU->enablePowerOutput(XPOWERS_VBACKUP); + + //ESP32 VDD 3300mV + // ! No need to set, automatically open , Don't close it + // PMU->setPowerChannelVoltage(XPOWERS_DCDC1, 3300); + // PMU->setProtectedChannel(XPOWERS_DCDC1); + PMU->setProtectedChannel(XPOWERS_DCDC1); + + // LoRa VDD 3300mV + PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO2); + + //GNSS VDD 3300mV + PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO3); + +#endif /*CONFIG_IDF_TARGET_ESP32*/ + + +#if defined(T_BEAM_S3_SUPREME) + + //t-beam m.2 inface + //gps + PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO4); + + // lora + PMU->setPowerChannelVoltage(XPOWERS_ALDO3, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO3); + + // In order to avoid bus occupation, during initialization, the SD card and QMC sensor are powered off and restarted + if (ESP_SLEEP_WAKEUP_UNDEFINED == esp_sleep_get_wakeup_cause()) { + Serial.println("Power off and restart ALDO BLDO.."); + PMU->disablePowerOutput(XPOWERS_ALDO1); + PMU->disablePowerOutput(XPOWERS_ALDO2); + PMU->disablePowerOutput(XPOWERS_BLDO1); + delay(250); + } + + // Sensor + PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO1); + + PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO2); + + //Sdcard + + PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300); + PMU->enablePowerOutput(XPOWERS_BLDO1); + + PMU->setPowerChannelVoltage(XPOWERS_BLDO2, 3300); + PMU->enablePowerOutput(XPOWERS_BLDO2); + + //face m.2 + PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300); + PMU->enablePowerOutput(XPOWERS_DCDC3); + + PMU->setPowerChannelVoltage(XPOWERS_DCDC4, XPOWERS_AXP2101_DCDC4_VOL2_MAX); + PMU->enablePowerOutput(XPOWERS_DCDC4); + + PMU->setPowerChannelVoltage(XPOWERS_DCDC5, 3300); + PMU->enablePowerOutput(XPOWERS_DCDC5); + + + //not use channel + PMU->disablePowerOutput(XPOWERS_DCDC2); + // PMU->disablePowerOutput(XPOWERS_DCDC4); + // PMU->disablePowerOutput(XPOWERS_DCDC5); + PMU->disablePowerOutput(XPOWERS_DLDO1); + PMU->disablePowerOutput(XPOWERS_DLDO2); + PMU->disablePowerOutput(XPOWERS_VBACKUP); + + +#elif defined(T_BEAM_S3_BPF) + + //gps + PMU->setPowerChannelVoltage(XPOWERS_ALDO4, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO4); + + //Sdcard + PMU->setPowerChannelVoltage(XPOWERS_ALDO2, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO2); + + // Extern Power source + PMU->setPowerChannelVoltage(XPOWERS_DCDC3, 3300); + PMU->enablePowerOutput(XPOWERS_DCDC3); + + PMU->setPowerChannelVoltage(XPOWERS_DCDC5, 3300); + PMU->enablePowerOutput(XPOWERS_DCDC5); + + PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300); + PMU->enablePowerOutput(XPOWERS_ALDO1); + + //not use channel + PMU->disablePowerOutput(XPOWERS_BLDO1); + PMU->disablePowerOutput(XPOWERS_BLDO2); + PMU->disablePowerOutput(XPOWERS_DCDC4); + PMU->disablePowerOutput(XPOWERS_DCDC2); + PMU->disablePowerOutput(XPOWERS_DCDC4); + PMU->disablePowerOutput(XPOWERS_DCDC5); + PMU->disablePowerOutput(XPOWERS_DLDO1); + PMU->disablePowerOutput(XPOWERS_DLDO2); + PMU->disablePowerOutput(XPOWERS_VBACKUP); + + +#endif + + // Set constant current charge current limit + PMU->setChargerConstantCurr(XPOWERS_AXP2101_CHG_CUR_500MA); + + // Set charge cut-off voltage + PMU->setChargeTargetVoltage(XPOWERS_AXP2101_CHG_VOL_4V2); + + // Disable all interrupts + PMU->disableIRQ(XPOWERS_AXP2101_ALL_IRQ); + // Clear all interrupt flags + PMU->clearIrqStatus(); + // Enable the required interrupt function + PMU->enableIRQ( + XPOWERS_AXP2101_BAT_INSERT_IRQ | XPOWERS_AXP2101_BAT_REMOVE_IRQ | //BATTERY + XPOWERS_AXP2101_VBUS_INSERT_IRQ | XPOWERS_AXP2101_VBUS_REMOVE_IRQ | //VBUS + XPOWERS_AXP2101_PKEY_SHORT_IRQ | XPOWERS_AXP2101_PKEY_LONG_IRQ | //POWER KEY + XPOWERS_AXP2101_BAT_CHG_DONE_IRQ | XPOWERS_AXP2101_BAT_CHG_START_IRQ //CHARGE + // XPOWERS_AXP2101_PKEY_NEGATIVE_IRQ | XPOWERS_AXP2101_PKEY_POSITIVE_IRQ | //POWER KEY + ); + + } + + PMU->enableSystemVoltageMeasure(); + PMU->enableVbusVoltageMeasure(); + PMU->enableBattVoltageMeasure(); + + Serial.printf("=========================================\n"); + if (PMU->isChannelAvailable(XPOWERS_DCDC1)) { + Serial.printf("DC1 : %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC1)); + } + if (PMU->isChannelAvailable(XPOWERS_DCDC2)) { + Serial.printf("DC2 : %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC2)); + } + if (PMU->isChannelAvailable(XPOWERS_DCDC3)) { + Serial.printf("DC3 : %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC3)); + } + if (PMU->isChannelAvailable(XPOWERS_DCDC4)) { + Serial.printf("DC4 : %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC4) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC4)); + } + if (PMU->isChannelAvailable(XPOWERS_DCDC5)) { + Serial.printf("DC5 : %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_DCDC5) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_DCDC5)); + } + if (PMU->isChannelAvailable(XPOWERS_LDO2)) { + Serial.printf("LDO2 : %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_LDO2)); + } + if (PMU->isChannelAvailable(XPOWERS_LDO3)) { + Serial.printf("LDO3 : %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_LDO3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_LDO3)); + } + if (PMU->isChannelAvailable(XPOWERS_ALDO1)) { + Serial.printf("ALDO1: %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO1)); + } + if (PMU->isChannelAvailable(XPOWERS_ALDO2)) { + Serial.printf("ALDO2: %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO2)); + } + if (PMU->isChannelAvailable(XPOWERS_ALDO3)) { + Serial.printf("ALDO3: %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO3) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO3)); + } + if (PMU->isChannelAvailable(XPOWERS_ALDO4)) { + Serial.printf("ALDO4: %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_ALDO4) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_ALDO4)); + } + if (PMU->isChannelAvailable(XPOWERS_BLDO1)) { + Serial.printf("BLDO1: %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO1) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO1)); + } + if (PMU->isChannelAvailable(XPOWERS_BLDO2)) { + Serial.printf("BLDO2: %s Voltage: %04u mV \n", PMU->isPowerChannelEnable(XPOWERS_BLDO2) ? "+" : "-", PMU->getPowerChannelVoltage(XPOWERS_BLDO2)); + } + Serial.printf("=========================================\n"); + + + // Set the time of pressing the button to turn off + PMU->setPowerKeyPressOffTime(XPOWERS_POWEROFF_4S); + uint8_t opt = PMU->getPowerKeyPressOffTime(); + Serial.print("PowerKeyPressOffTime:"); + switch (opt) { + case XPOWERS_POWEROFF_4S: Serial.println("4 Second"); + break; + case XPOWERS_POWEROFF_6S: Serial.println("6 Second"); + break; + case XPOWERS_POWEROFF_8S: Serial.println("8 Second"); + break; + case XPOWERS_POWEROFF_10S: Serial.println("10 Second"); + break; + default: + break; + } +#endif + return true; +} + +void disablePeripherals() +{ + +#ifdef HAS_PMU + if (!PMU)return; +#if defined(T_BEAM_S3_BPF) + PMU->disablePowerOutput(XPOWERS_ALDO4); //gps + PMU->disablePowerOutput(XPOWERS_ALDO2); //Sdcard + PMU->disablePowerOutput(XPOWERS_DCDC3); // Extern Power source + PMU->disablePowerOutput(XPOWERS_DCDC5); + PMU->disablePowerOutput(XPOWERS_ALDO1); +#endif +#endif + + +} + +void loopPMU() +{ +#ifdef HAS_PMU + if (!PMU) { + return; + } + if (!pmuInterrupt) { + return; + } + + pmuInterrupt = false; + // Get PMU Interrupt Status Register + uint32_t status = PMU->getIrqStatus(); + Serial.print("STATUS => HEX:"); + Serial.print(status, HEX); + Serial.print(" BIN:"); + Serial.println(status, BIN); + + if (PMU->isVbusInsertIrq()) { + Serial.println("isVbusInsert"); + } + if (PMU->isVbusRemoveIrq()) { + Serial.println("isVbusRemove"); + } + if (PMU->isBatInsertIrq()) { + Serial.println("isBatInsert"); + } + if (PMU->isBatRemoveIrq()) { + Serial.println("isBatRemove"); + } + if (PMU->isPekeyShortPressIrq()) { + Serial.println("isPekeyShortPress"); + } + if (PMU->isPekeyLongPressIrq()) { + Serial.println("isPekeyLongPress"); + } + if (PMU->isBatChagerDoneIrq()) { + Serial.println("isBatChagerDone"); + } + if (PMU->isBatChagerStartIrq()) { + Serial.println("isBatChagerStart"); + } + // Clear PMU Interrupt Status Register + PMU->clearIrqStatus(); +#endif +} + +bool beginDisplay() +{ + Wire.beginTransmission(DISPLAY_ADDR); + if (Wire.endTransmission() == 0) { + Serial.printf("Find Display model at 0x%X address\n", DISPLAY_ADDR); + u8g2 = new DISPLAY_MODEL(U8G2_R0, U8X8_PIN_NONE); + u8g2->begin(); + u8g2->clearBuffer(); + u8g2->setFont(u8g2_font_inb19_mr); + u8g2->drawStr(0, 30, "LilyGo"); + u8g2->drawHLine(2, 35, 47); + u8g2->drawHLine(3, 36, 47); + u8g2->drawVLine(45, 32, 12); + u8g2->drawVLine(46, 33, 12); + u8g2->setFont(u8g2_font_inb19_mf); + u8g2->drawStr(58, 60, "LoRa"); + u8g2->sendBuffer(); + u8g2->setFont(u8g2_font_fur11_tf); + delay(3000); + return true; + } + + Serial.printf("Warning: Failed to find Display at 0x%0X address\n", DISPLAY_ADDR); + return false; +} + + +bool beginSDCard() +{ +#ifdef SDCARD_CS + if (SD.begin(SDCARD_CS, SDCardSPI)) { + uint32_t cardSize = SD.cardSize() / (1024 * 1024); + Serial.print("Sd Card init succeeded, The current available capacity is "); + Serial.print(cardSize / 1024.0); + Serial.println(" GB"); + return true; + } else { + Serial.println("Warning: Failed to init Sd Card"); + } +#endif + return false; +} + +void beginWiFi() +{ + if (!WiFi.softAP(BOARD_VARIANT_NAME)) { + log_e("Soft AP creation failed."); + } + IPAddress myIP = WiFi.softAPIP(); + Serial.print("AP IP address: "); + Serial.println(myIP); +} + + +void printWakeupReason() +{ +#ifdef ESP32 + Serial.print("Reset reason:"); + esp_sleep_wakeup_cause_t wakeup_reason; + wakeup_reason = esp_sleep_get_wakeup_cause(); + switch (wakeup_reason) { + case ESP_SLEEP_WAKEUP_UNDEFINED: + Serial.println(" In case of deep sleep, reset was not caused by exit from deep sleep"); + break; + case ESP_SLEEP_WAKEUP_ALL : + break; + case ESP_SLEEP_WAKEUP_EXT0 : + Serial.println("Wakeup caused by external signal using RTC_IO"); + break; + case ESP_SLEEP_WAKEUP_EXT1 : + Serial.println("Wakeup caused by external signal using RTC_CNTL"); + break; + case ESP_SLEEP_WAKEUP_TIMER : + Serial.println("Wakeup caused by timer"); + break; + case ESP_SLEEP_WAKEUP_TOUCHPAD : + Serial.println("Wakeup caused by touchpad"); + break; + case ESP_SLEEP_WAKEUP_ULP : + Serial.println("Wakeup caused by ULP program"); + break; + default : + Serial.printf("Wakeup was not caused by deep sleep: %d\n", wakeup_reason); + break; + } +#endif +} + + +void getChipInfo() +{ +#if defined(ARDUINO_ARCH_ESP32) + + Serial.println("-----------------------------------"); + + printWakeupReason(); + +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3) + + if (psramFound()) { + uint32_t psram = ESP.getPsramSize(); + devInfo.psramSize = psram / 1024.0 / 1024.0; + Serial.printf("PSRAM is enable! PSRAM: %.2fMB\n", devInfo.psramSize); + } else { + Serial.println("PSRAM is disable!"); + devInfo.psramSize = 0; + } + +#endif + + Serial.print("Flash:"); + devInfo.flashSize = ESP.getFlashChipSize() / 1024.0 / 1024.0; + devInfo.flashSpeed = ESP.getFlashChipSpeed() / 1000 / 1000; + devInfo.chipModel = ESP.getChipModel(); + devInfo.chipModelRev = ESP.getChipRevision(); + devInfo.chipFreq = ESP.getCpuFreqMHz(); + + Serial.print(devInfo.flashSize); + Serial.println(" MB"); + Serial.print("Flash speed:"); + Serial.print(devInfo.flashSpeed); + Serial.println(" M"); + Serial.print("Model:"); + + Serial.println(devInfo.chipModel); + Serial.print("Chip Revision:"); + Serial.println(devInfo.chipModelRev); + Serial.print("Freq:"); + Serial.print(devInfo.chipFreq); + Serial.println(" MHZ"); + Serial.print("SDK Ver:"); + Serial.println(ESP.getSdkVersion()); + Serial.print("DATE:"); + Serial.println(__DATE__); + Serial.print("TIME:"); + Serial.println(__TIME__); + + Serial.print("EFUSE MAC: "); + Serial.print( ESP.getEfuseMac(), HEX); + Serial.println(); + + Serial.println("-----------------------------------"); + +#elif defined(ARDUINO_ARCH_STM32) + uint32_t uid[3]; + + uid[0] = HAL_GetUIDw0(); + uid[1] = HAL_GetUIDw1(); + uid[2] = HAL_GetUIDw2(); + Serial.print("STM UID: 0X"); + Serial.print( uid[0], HEX); + Serial.print( uid[1], HEX); + Serial.print( uid[2], HEX); + Serial.println(); +#endif +} + + + +void setupBoards(bool disable_u8g2 ) +{ + Serial.begin(115200); + + // while (!Serial); + + Serial.println("setupBoards"); + + getChipInfo(); + +#if defined(ARDUINO_ARCH_ESP32) + SPI.begin(RADIO_SCLK_PIN, RADIO_MISO_PIN, RADIO_MOSI_PIN); +#elif defined(ARDUINO_ARCH_STM32) + SPI.setMISO(RADIO_MISO_PIN); + SPI.setMOSI(RADIO_MOSI_PIN); + SPI.setSCLK(RADIO_SCLK_PIN); + SPI.begin(); +#endif + +#ifdef HAS_SDCARD + SDCardSPI.begin(SDCARD_SCLK, SDCARD_MISO, SDCARD_MOSI); +#endif + +#ifdef I2C_SDA + Wire.begin(I2C_SDA, I2C_SCL); + scanDevices(&Wire); +#endif + +#ifdef I2C1_SDA + Wire1.begin(I2C1_SDA, I2C1_SCL); +#endif + +#ifdef HAS_GPS +#if defined(ARDUINO_ARCH_ESP32) + SerialGPS.begin(GPS_BAUD_RATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); +#elif defined(ARDUINO_ARCH_STM32) + SerialGPS.setRx(GPS_RX_PIN); + SerialGPS.setTx(GPS_TX_PIN); + SerialGPS.begin(GPS_BAUD_RATE); +#endif // ARDUINO_ARCH_ +#endif // HAS_GPS + +#if OLED_RST + pinMode(OLED_RST, OUTPUT); + digitalWrite(OLED_RST, HIGH); delay(20); + digitalWrite(OLED_RST, LOW); delay(20); + digitalWrite(OLED_RST, HIGH); delay(20); +#endif + +#ifdef BOARD_LED + /* + * T-Beam LED defaults to low level as turn on, + * so it needs to be forced to pull up + * * * * */ +#if LED_ON == LOW +#if defined(ARDUINO_ARCH_ESP32) + gpio_hold_dis((gpio_num_t)BOARD_LED); +#endif //ARDUINO_ARCH_ESP32 +#endif + + pinMode(BOARD_LED, OUTPUT); + digitalWrite(BOARD_LED, LED_ON); +#endif + +#ifdef GPS_EN_PIN + pinMode(GPS_EN_PIN, OUTPUT); + digitalWrite(GPS_EN_PIN, HIGH); +#endif + +#ifdef GPS_RST_PIN + pinMode(GPS_RST_PIN, OUTPUT); + digitalWrite(GPS_RST_PIN, HIGH); +#endif + + +#if defined(ARDUINO_ARCH_STM32) + SerialGPS.println("@GSR"); delay(300); + SerialGPS.println("@GSR"); delay(300); + SerialGPS.println("@GSR"); delay(300); + SerialGPS.println("@GSR"); delay(300); + SerialGPS.println("@GSR"); delay(300); +#endif + + +#ifdef RADIO_LDO_EN + pinMode(RADIO_LDO_EN, OUTPUT); + digitalWrite(RADIO_LDO_EN, HIGH); +#endif + + beginPower(); + + beginSDCard(); + + if (!disable_u8g2) { + beginDisplay(); + } + + // beginWiFi(); + +#ifdef HAS_GPS +#ifdef T_BEAM_S3_BPF + find_gps = beginGPS(); +#endif +#endif + + Serial.println("init done . "); +} + + +void printResult(bool radio_online) +{ + Serial.print("Radio : "); + Serial.println((radio_online) ? "+" : "-"); + +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3) + + Serial.print("PSRAM : "); + Serial.println((psramFound()) ? "+" : "-"); + + Serial.print("Display : "); + Serial.println(( u8g2) ? "+" : "-"); + +#ifdef HAS_SDCARD + Serial.print("Sd Card : "); + Serial.println((SD.cardSize() != 0) ? "+" : "-"); +#endif + +#ifdef HAS_PMU + Serial.print("Power : "); + Serial.println(( PMU ) ? "+" : "-"); +#endif + +#ifdef HAS_GPS +#ifdef T_BEAM_S3_BPF + Serial.print("GPS : "); + Serial.println(( find_gps ) ? "+" : "-"); +#endif +#endif + + if (u8g2) { + + u8g2->clearBuffer(); + u8g2->setFont(u8g2_font_NokiaLargeBold_tf ); + uint16_t str_w = u8g2->getStrWidth(BOARD_VARIANT_NAME); + u8g2->drawStr((u8g2->getWidth() - str_w) / 2, 16, BOARD_VARIANT_NAME); + u8g2->drawHLine(5, 21, u8g2->getWidth() - 5); + + u8g2->drawStr( 0, 38, "Disp:"); u8g2->drawStr( 45, 38, ( u8g2) ? "+" : "-"); + +#ifdef HAS_SDCARD + u8g2->drawStr( 0, 54, "SD :"); u8g2->drawStr( 45, 54, (SD.cardSize() != 0) ? "+" : "-"); +#endif + + u8g2->drawStr( 62, 38, "Radio:"); u8g2->drawStr( 120, 38, ( radio_online ) ? "+" : "-"); + +#ifdef HAS_PMU + u8g2->drawStr( 62, 54, "Power:"); u8g2->drawStr( 120, 54, ( PMU ) ? "+" : "-"); +#endif + + u8g2->sendBuffer(); + + delay(2000); + } +#endif +} + + + +static uint8_t ledState = LOW; +static const uint32_t debounceDelay = 50; +static uint32_t lastDebounceTime = 0; + +void flashLed() +{ +#ifdef BOARD_LED + if ((millis() - lastDebounceTime) > debounceDelay) { + ledState = !ledState; + if (ledState) { + digitalWrite(BOARD_LED, LED_ON); + } else { + digitalWrite(BOARD_LED, !LED_ON); + } + lastDebounceTime = millis(); + } +#endif +} + + +void scanDevices(TwoWire *w) +{ + uint8_t err, addr; + int nDevices = 0; + uint32_t start = 0; + + Serial.println("I2C Devices scanning"); + for (addr = 1; addr < 127; addr++) { + start = millis(); + w->beginTransmission(addr); delay(2); + err = w->endTransmission(); + if (err == 0) { + nDevices++; + switch (addr) { + case 0x77: + case 0x76: + Serial.println("\tFind BMX280 Sensor!"); + break; + case 0x34: + Serial.println("\tFind AXP192/AXP2101 PMU!"); + break; + case 0x3C: + Serial.println("\tFind SSD1306/SH1106 dispaly!"); + break; + case 0x51: + Serial.println("\tFind PCF8563 RTC!"); + break; + case 0x1C: + Serial.println("\tFind QMC6310 MAG Sensor!"); + break; + default: + Serial.print("\tI2C device found at address 0x"); + if (addr < 16) { + Serial.print("0"); + } + Serial.print(addr, HEX); + Serial.println(" !"); + break; + } + + } else if (err == 4) { + Serial.print("Unknow error at address 0x"); + if (addr < 16) { + Serial.print("0"); + } + Serial.println(addr, HEX); + } + } + if (nDevices == 0) + Serial.println("No I2C devices found\n"); + + Serial.println("Scan devices done."); + Serial.println("\n"); +} + + +#ifdef HAS_GPS +bool beginGPS() +{ + SerialGPS.begin(GPS_BAUD_RATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); + bool result = false; + uint32_t startTimeout ; + for (int i = 0; i < 3; ++i) { + SerialGPS.write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n"); + delay(5); + // Get version information + startTimeout = millis() + 3000; + Serial.print("Try to init L76K . Wait stop ."); + while (SerialGPS.available()) { + Serial.print("."); + SerialGPS.readString(); + if (millis() > startTimeout) { + Serial.println("Wait L76K stop NMEA timeout!"); + return false; + } + }; + Serial.println(); + SerialGPS.flush(); + delay(200); + + SerialGPS.write("$PCAS06,0*1B\r\n"); + startTimeout = millis() + 500; + String ver = ""; + while (!SerialGPS.available()) { + if (millis() > startTimeout) { + Serial.println("Get L76K timeout!"); + return false; + } + } + SerialGPS.setTimeout(10); + ver = SerialGPS.readStringUntil('\n'); + if (ver.startsWith("$GPTXT,01,01,02")) { + Serial.println("L76K GNSS init succeeded, using L76K GNSS Module\n"); + result = true; + break; + } + delay(500); + } + // Initialize the L76K Chip, use GPS + GLONASS + SerialGPS.write("$PCAS04,5*1C\r\n"); + delay(250); + // only ask for RMC and GGA + SerialGPS.write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n"); + delay(250); + // Switch to Vehicle Mode, since SoftRF enables Aviation < 2g + SerialGPS.write("$PCAS11,3*1E\r\n"); + return result; +} +#endif \ No newline at end of file diff --git a/examples/LoRaWAN/LoRaWAN_ABP/LoRaBoards.h b/examples/LoRaWAN/LoRaWAN_ABP/LoRaBoards.h new file mode 100644 index 0000000..1bceb83 --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_ABP/LoRaBoards.h @@ -0,0 +1,102 @@ +/** + * @file boards.h + * @author Lewis He (lewishe@outlook.com) + * @license MIT + * @copyright Copyright (c) 2024 ShenZhen XinYuan Electronic Technology Co., Ltd + * @date 2024-04-25 + * @last-update 2024-08-07 + */ + +#pragma once + + +#include "utilities.h" + +#ifdef HAS_SDCARD +#include +#endif + +#if defined(ARDUINO_ARCH_ESP32) +#include +#include +#endif + +#include +#include +#include +#include +#include + +#ifndef DISPLAY_MODEL +#define DISPLAY_MODEL U8G2_SSD1306_128X64_NONAME_F_HW_I2C +#endif + +#ifndef OLED_WIRE_PORT +#define OLED_WIRE_PORT Wire +#endif + +#ifndef PMU_WIRE_PORT +#define PMU_WIRE_PORT Wire +#endif + +#ifndef DISPLAY_ADDR +#define DISPLAY_ADDR 0x3C +#endif + +#ifndef LORA_FREQ_CONFIG +#define LORA_FREQ_CONFIG 915.0 +#endif + + + + +typedef struct { + String chipModel; + float psramSize; + uint8_t chipModelRev; + uint8_t chipFreq; + uint8_t flashSize; + uint8_t flashSpeed; +} DevInfo_t; + + +void setupBoards(bool disable_u8g2 = false); + +bool beginSDCard(); + +bool beginDisplay(); + +void disablePeripherals(); + +bool beginPower(); + +void printResult(bool radio_online); + +void flashLed(); + +void scanDevices(TwoWire *w); + +bool beginGPS(); + +void loopPMU(); + +#ifdef HAS_PMU +extern XPowersLibInterface *PMU; +extern bool pmuInterrupt; +#endif +extern DISPLAY_MODEL *u8g2; + +#define U8G2_HOR_ALIGN_CENTER(t) ((u8g2->getDisplayWidth() - (u8g2->getUTF8Width(t))) / 2) +#define U8G2_HOR_ALIGN_RIGHT(t) ( u8g2->getDisplayWidth() - u8g2->getUTF8Width(t)) + + +#if defined(ARDUINO_ARCH_ESP32) + +#if defined(HAS_SDCARD) +extern SPIClass SDCardSPI; +#endif + +#define SerialGPS Serial1 +#elif defined(ARDUINO_ARCH_STM32) +extern HardwareSerial SerialGPS; +#endif diff --git a/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino b/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino new file mode 100644 index 0000000..b9242c0 --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino @@ -0,0 +1,96 @@ +/* + RadioLib LoRaWAN ABP Example + + ABP = Activation by Personalisation, an alternative + to OTAA (Over the Air Activation). OTAA is preferable. + + This example will send uplink packets to a LoRaWAN network. + Before you start, you will have to register your device at + https://www.thethingsnetwork.org/ + After your device is registered, you can run this example. + The device will join the network and start uploading data. + + LoRaWAN v1.0.4/v1.1 requires the use of persistent storage. + As this example does not use persistent storage, running this + examples REQUIRES you to check "Resets frame counters" + on your LoRaWAN dashboard. Refer to the notes or the + network's documentation on how to do this. + To comply with LoRaWAN's persistent storage, refer to + https://github.com/radiolib-org/radiolib-persistence + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ + + For LoRaWAN details, see the wiki page + https://github.com/jgromes/RadioLib/wiki/LoRaWAN + +*/ + +#include "configABP.h" + +void setup() +{ + Serial.begin(115200); + + while (!Serial); + + setupBoards(); + +#ifdef RADIO_TCXO_ENABLE + pinMode(RADIO_TCXO_ENABLE, OUTPUT); + digitalWrite(RADIO_TCXO_ENABLE, HIGH); +#endif + + delay(5000); // Give time to switch to the serial monitor + Serial.println(F("\nSetup ... ")); + + Serial.println(F("Initialise the radio")); + int state = radio.begin(); + debug(state != RADIOLIB_ERR_NONE, F("Initialise radio failed"), state, true); + + Serial.println(F("Initialise LoRaWAN Network credentials")); + node.beginABP(devAddr, fNwkSIntKey, sNwkSIntKey, nwkSEncKey, appSKey); + + node.activateABP(); + debug(state != RADIOLIB_ERR_NONE, F("Activate ABP failed"), state, true); + + Serial.println(F("Ready!\n")); +} + +void loop() +{ + Serial.println(F("Sending uplink")); + + // This is the place to gather the sensor inputs + // Instead of reading any real sensor, we just generate some random numbers as example + uint8_t value1 = radio.random(100); + uint16_t value2 = radio.random(2000); + + // Build payload byte array + uint8_t uplinkPayload[3]; + uplinkPayload[0] = value1; + uplinkPayload[1] = highByte(value2); // See notes for high/lowByte functions + uplinkPayload[2] = lowByte(value2); + + // Perform an uplink + int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); + debug(state < RADIOLIB_ERR_NONE, F("Error in sendReceive"), state, false); + + // Check if a downlink was received + // (state 0 = no downlink, state 1/2 = downlink in window Rx1/Rx2) + if (state > 0) { + Serial.println(F("Received a downlink")); + } else { + Serial.println(F("No downlink received")); + } + + Serial.print(F("Next uplink in ")); + Serial.print(uplinkIntervalSeconds); + Serial.println(F(" seconds\n")); + + // Wait until next uplink - observing legal & TTN FUP constraints + delay(uplinkIntervalSeconds * 1000UL); // delay needs milli-seconds +} diff --git a/examples/LoRaWAN/LoRaWAN_ABP/configABP.h b/examples/LoRaWAN/LoRaWAN_ABP/configABP.h new file mode 100644 index 0000000..7de4dd8 --- /dev/null +++ b/examples/LoRaWAN/LoRaWAN_ABP/configABP.h @@ -0,0 +1,155 @@ +#ifndef _RADIOLIB_EX_LORAWAN_CONFIG_H +#define _RADIOLIB_EX_LORAWAN_CONFIG_H + +#include +#include "LoRaBoards.h" + +#if defined(USING_SX1276) +SX1276 radio = new Module(RADIO_CS_PIN, RADIO_DIO0_PIN, RADIO_RST_PIN, RADIO_DIO1_PIN); +#elif defined(USING_SX1262) +SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); +#elif defined(USING_SX1278) +SX1278 radio = new Module(RADIO_CS_PIN, RADIO_DIO0_PIN, RADIO_RST_PIN, RADIO_DIO1_PIN); +#elif defined(USING_LR1121) +LR1121 radio = new Module(RADIO_CS_PIN, RADIO_DIO9_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); +#endif + + +// how often to send an uplink - consider legal & FUP constraints - see notes +const uint32_t uplinkIntervalSeconds = 1UL * 60UL; // minutes x seconds + +// device address - either a development address or one assigned +// to the LoRaWAN Service Provider - TTN will generate one for you +#ifndef RADIOLIB_LORAWAN_DEV_ADDR // Replace with your DevAddr +#define RADIOLIB_LORAWAN_DEV_ADDR 0x260DA9E1 +#endif + +#ifndef RADIOLIB_LORAWAN_FNWKSINT_KEY // Replace with your FNwkSInt Key +#define RADIOLIB_LORAWAN_FNWKSINT_KEY 0xD2, 0xB1, 0x21, 0xCC, 0xDE, 0xBE, 0xE5, 0x29, 0xEC, 0x00, 0xD2, 0xE4, 0xBB, 0x25, 0x9D, 0xFF +#endif +#ifndef RADIOLIB_LORAWAN_SNWKSINT_KEY // Replace with your SNwkSInt Key +#define RADIOLIB_LORAWAN_SNWKSINT_KEY 0x17, 0x4A, 0xAD, 0x42, 0x32, 0x5B, 0x54, 0xAC, 0xA5, 0xB9, 0xE6, 0x45, 0x60, 0x3E, 0x6E, 0x64 +#endif +#ifndef RADIOLIB_LORAWAN_NWKSENC_KEY // Replace with your NwkSEnc Key +#define RADIOLIB_LORAWAN_NWKSENC_KEY 0xA6, 0xFB, 0xE9, 0xDC, 0xB7, 0x16, 0x34, 0x6C, 0xC1, 0x6F, 0xB8, 0x3C, 0x94, 0x0C, 0xA1, 0x63 +#endif +#ifndef RADIOLIB_LORAWAN_APPS_KEY // Replace with your AppS Key +#define RADIOLIB_LORAWAN_APPS_KEY 0xCB, 0xE2, 0x15, 0x39, 0xFB, 0xD4, 0x9A, 0x6E, 0xF5, 0x82, 0xE1, 0x97, 0xEC, 0x2B, 0x0E, 0x3C +#endif + +// for the curious, the #ifndef blocks allow for automated testing &/or you can +// put your EUI & keys in to your platformio.ini - see wiki for more tips + +// regional choices: EU868, US915, AU915, AS923, AS923_2, AS923_3, AS923_4, IN865, KR920, CN500 +const LoRaWANBand_t Region = CN500; +const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 + +// ============================================================================ +// Below is to support the sketch - only make changes if the notes say so ... + +// copy over the keys in to the something that will not compile if incorrectly formatted +uint32_t devAddr = RADIOLIB_LORAWAN_DEV_ADDR; +uint8_t fNwkSIntKey[] = { RADIOLIB_LORAWAN_FNWKSINT_KEY }; +uint8_t sNwkSIntKey[] = { RADIOLIB_LORAWAN_SNWKSINT_KEY }; +uint8_t nwkSEncKey[] = { RADIOLIB_LORAWAN_NWKSENC_KEY }; +uint8_t appSKey[] = { RADIOLIB_LORAWAN_APPS_KEY }; + +// create the LoRaWAN node +LoRaWANNode node(&radio, &Region, subBand); + +// result code to text - these are error codes that can be raised when using LoRaWAN +// however, RadioLib has many more - see https://jgromes.github.io/RadioLib/group__status__codes.html for a complete list +String stateDecode(const int16_t result) { + switch (result) { + case RADIOLIB_ERR_NONE: + return "ERR_NONE"; + case RADIOLIB_ERR_CHIP_NOT_FOUND: + return "ERR_CHIP_NOT_FOUND"; + case RADIOLIB_ERR_PACKET_TOO_LONG: + return "ERR_PACKET_TOO_LONG"; + case RADIOLIB_ERR_RX_TIMEOUT: + return "ERR_RX_TIMEOUT"; + case RADIOLIB_ERR_CRC_MISMATCH: + return "ERR_CRC_MISMATCH"; + case RADIOLIB_ERR_INVALID_BANDWIDTH: + return "ERR_INVALID_BANDWIDTH"; + case RADIOLIB_ERR_INVALID_SPREADING_FACTOR: + return "ERR_INVALID_SPREADING_FACTOR"; + case RADIOLIB_ERR_INVALID_CODING_RATE: + return "ERR_INVALID_CODING_RATE"; + case RADIOLIB_ERR_INVALID_FREQUENCY: + return "ERR_INVALID_FREQUENCY"; + case RADIOLIB_ERR_INVALID_OUTPUT_POWER: + return "ERR_INVALID_OUTPUT_POWER"; + case RADIOLIB_ERR_NETWORK_NOT_JOINED: + return "RADIOLIB_ERR_NETWORK_NOT_JOINED"; + case RADIOLIB_ERR_DOWNLINK_MALFORMED: + return "RADIOLIB_ERR_DOWNLINK_MALFORMED"; + case RADIOLIB_ERR_INVALID_REVISION: + return "RADIOLIB_ERR_INVALID_REVISION"; + case RADIOLIB_ERR_INVALID_PORT: + return "RADIOLIB_ERR_INVALID_PORT"; + case RADIOLIB_ERR_NO_RX_WINDOW: + return "RADIOLIB_ERR_NO_RX_WINDOW"; + case RADIOLIB_ERR_INVALID_CID: + return "RADIOLIB_ERR_INVALID_CID"; + case RADIOLIB_ERR_UPLINK_UNAVAILABLE: + return "RADIOLIB_ERR_UPLINK_UNAVAILABLE"; + case RADIOLIB_ERR_COMMAND_QUEUE_FULL: + return "RADIOLIB_ERR_COMMAND_QUEUE_FULL"; + case RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND: + return "RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND"; + case RADIOLIB_ERR_JOIN_NONCE_INVALID: + return "RADIOLIB_ERR_JOIN_NONCE_INVALID"; + case RADIOLIB_ERR_N_FCNT_DOWN_INVALID: + return "RADIOLIB_ERR_N_FCNT_DOWN_INVALID"; + case RADIOLIB_ERR_A_FCNT_DOWN_INVALID: + return "RADIOLIB_ERR_A_FCNT_DOWN_INVALID"; + case RADIOLIB_ERR_DWELL_TIME_EXCEEDED: + return "RADIOLIB_ERR_DWELL_TIME_EXCEEDED"; + case RADIOLIB_ERR_CHECKSUM_MISMATCH: + return "RADIOLIB_ERR_CHECKSUM_MISMATCH"; + case RADIOLIB_ERR_NO_JOIN_ACCEPT: + return "RADIOLIB_ERR_NO_JOIN_ACCEPT"; + case RADIOLIB_LORAWAN_SESSION_RESTORED: + return "RADIOLIB_LORAWAN_SESSION_RESTORED"; + case RADIOLIB_LORAWAN_NEW_SESSION: + return "RADIOLIB_LORAWAN_NEW_SESSION"; + case RADIOLIB_ERR_NONCES_DISCARDED: + return "RADIOLIB_ERR_NONCES_DISCARDED"; + case RADIOLIB_ERR_SESSION_DISCARDED: + return "RADIOLIB_ERR_SESSION_DISCARDED"; + } + return "See https://jgromes.github.io/RadioLib/group__status__codes.html"; +} + +// helper function to display any issues +void debug(bool failed, const __FlashStringHelper *message, int state, bool halt) +{ + if (failed) { + Serial.print(message); + Serial.print(" - "); + Serial.print(stateDecode(state)); + Serial.print(" ("); + Serial.print(state); + Serial.println(")"); + while (halt) { + delay(1); + } + } +} + +// helper function to display a byte array +void arrayDump(uint8_t *buffer, uint16_t len) +{ + for (uint16_t c = 0; c < len; c++) { + char b = buffer[c]; + if (b < 0x10) { + Serial.print('0'); + } + Serial.print(b, HEX); + } + Serial.println(); +} + +#endif diff --git a/examples/LoRaWAN/RadioLib_OTAA/LoRaBoards.cpp b/examples/LoRaWAN/RadioLib_OTAA/LoRaBoards.cpp index 98846a3..aaf1e77 100644 --- a/examples/LoRaWAN/RadioLib_OTAA/LoRaBoards.cpp +++ b/examples/LoRaWAN/RadioLib_OTAA/LoRaBoards.cpp @@ -648,7 +648,7 @@ void setupBoards(bool disable_u8g2 ) beginDisplay(); } - beginWiFi(); + // beginWiFi(); #ifdef HAS_GPS #ifdef T_BEAM_S3_BPF diff --git a/examples/LoRaWAN/RadioLib_OTAA/RadioLib_OTAA.ino b/examples/LoRaWAN/RadioLib_OTAA/RadioLib_OTAA.ino index cacb864..8ab334f 100644 --- a/examples/LoRaWAN/RadioLib_OTAA/RadioLib_OTAA.ino +++ b/examples/LoRaWAN/RadioLib_OTAA/RadioLib_OTAA.ino @@ -25,16 +25,6 @@ #include #include "LoRaBoards.h" -// !!!!! APP EUI -#define RADIOLIB_LORAWAN_JOIN_EUI 0xAABB112233445566 -// !!!!! DEV EUI -#define RADIOLIB_LORAWAN_DEV_EUI 0x70B3D57ED0066FDA -// !!!!! APP EUI -#define RADIOLIB_LORAWAN_APP_KEY 0xAA, 0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 -// !!! APP KEY -#define RADIOLIB_LORAWAN_NWK_KEY 0xF2, 0x84, 0x31, 0x0F, 0x82, 0x65, 0x99, 0x4F, 0x70, 0xCB, 0x88, 0x74, 0xFA, 0xD6, 0x71, 0xA2 - - #if defined(USING_SX1276) SX1276 radio = new Module(RADIO_CS_PIN, RADIO_DIO0_PIN, RADIO_RST_PIN, RADIO_DIO1_PIN); #elif defined(USING_SX1262) @@ -45,6 +35,21 @@ SX1278 radio = new Module(RADIO_CS_PIN, RADIO_DIO0_PIN, RADIO_RST_PIN, RADIO_DIO LR1121 radio = new Module(RADIO_CS_PIN, RADIO_DIO9_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN); #endif +// joinEUI - previous versions of LoRaWAN called this AppEUI +// for development purposes you can use all zeros - see wiki for details +#define RADIOLIB_LORAWAN_JOIN_EUI 0x0000000000000000 + + +// the Device EUI & two keys can be generated on the TTN console +#ifndef RADIOLIB_LORAWAN_DEV_EUI // Replace with your Device EUI +#define RADIOLIB_LORAWAN_DEV_EUI 0x--------------- +#endif +#ifndef RADIOLIB_LORAWAN_APP_KEY // Replace with your App Key +#define RADIOLIB_LORAWAN_APP_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#endif +#ifndef RADIOLIB_LORAWAN_NWK_KEY // Put your Nwk Key here +#define RADIOLIB_LORAWAN_NWK_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#endif // how often to send an uplink - consider legal & FUP constraints - see notes const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds @@ -69,15 +74,87 @@ uint8_t nwkKey[] = { RADIOLIB_LORAWAN_NWK_KEY }; // create the LoRaWAN node LoRaWANNode node(&radio, &Region, subBand); + +// result code to text - these are error codes that can be raised when using LoRaWAN +// however, RadioLib has many more - see https://jgromes.github.io/RadioLib/group__status__codes.html for a complete list +String stateDecode(const int16_t result) +{ + switch (result) { + case RADIOLIB_ERR_NONE: + return "ERR_NONE"; + case RADIOLIB_ERR_CHIP_NOT_FOUND: + return "ERR_CHIP_NOT_FOUND"; + case RADIOLIB_ERR_PACKET_TOO_LONG: + return "ERR_PACKET_TOO_LONG"; + case RADIOLIB_ERR_RX_TIMEOUT: + return "ERR_RX_TIMEOUT"; + case RADIOLIB_ERR_CRC_MISMATCH: + return "ERR_CRC_MISMATCH"; + case RADIOLIB_ERR_INVALID_BANDWIDTH: + return "ERR_INVALID_BANDWIDTH"; + case RADIOLIB_ERR_INVALID_SPREADING_FACTOR: + return "ERR_INVALID_SPREADING_FACTOR"; + case RADIOLIB_ERR_INVALID_CODING_RATE: + return "ERR_INVALID_CODING_RATE"; + case RADIOLIB_ERR_INVALID_FREQUENCY: + return "ERR_INVALID_FREQUENCY"; + case RADIOLIB_ERR_INVALID_OUTPUT_POWER: + return "ERR_INVALID_OUTPUT_POWER"; + case RADIOLIB_ERR_NETWORK_NOT_JOINED: + return "RADIOLIB_ERR_NETWORK_NOT_JOINED"; + case RADIOLIB_ERR_DOWNLINK_MALFORMED: + return "RADIOLIB_ERR_DOWNLINK_MALFORMED"; + case RADIOLIB_ERR_INVALID_REVISION: + return "RADIOLIB_ERR_INVALID_REVISION"; + case RADIOLIB_ERR_INVALID_PORT: + return "RADIOLIB_ERR_INVALID_PORT"; + case RADIOLIB_ERR_NO_RX_WINDOW: + return "RADIOLIB_ERR_NO_RX_WINDOW"; + case RADIOLIB_ERR_INVALID_CID: + return "RADIOLIB_ERR_INVALID_CID"; + case RADIOLIB_ERR_UPLINK_UNAVAILABLE: + return "RADIOLIB_ERR_UPLINK_UNAVAILABLE"; + case RADIOLIB_ERR_COMMAND_QUEUE_FULL: + return "RADIOLIB_ERR_COMMAND_QUEUE_FULL"; + case RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND: + return "RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND"; + case RADIOLIB_ERR_JOIN_NONCE_INVALID: + return "RADIOLIB_ERR_JOIN_NONCE_INVALID"; + case RADIOLIB_ERR_N_FCNT_DOWN_INVALID: + return "RADIOLIB_ERR_N_FCNT_DOWN_INVALID"; + case RADIOLIB_ERR_A_FCNT_DOWN_INVALID: + return "RADIOLIB_ERR_A_FCNT_DOWN_INVALID"; + case RADIOLIB_ERR_DWELL_TIME_EXCEEDED: + return "RADIOLIB_ERR_DWELL_TIME_EXCEEDED"; + case RADIOLIB_ERR_CHECKSUM_MISMATCH: + return "RADIOLIB_ERR_CHECKSUM_MISMATCH"; + case RADIOLIB_ERR_NO_JOIN_ACCEPT: + return "RADIOLIB_ERR_NO_JOIN_ACCEPT"; + case RADIOLIB_LORAWAN_SESSION_RESTORED: + return "RADIOLIB_LORAWAN_SESSION_RESTORED"; + case RADIOLIB_LORAWAN_NEW_SESSION: + return "RADIOLIB_LORAWAN_NEW_SESSION"; + case RADIOLIB_ERR_NONCES_DISCARDED: + return "RADIOLIB_ERR_NONCES_DISCARDED"; + case RADIOLIB_ERR_SESSION_DISCARDED: + return "RADIOLIB_ERR_SESSION_DISCARDED"; + } + return "See https://jgromes.github.io/RadioLib/group__status__codes.html"; +} + // helper function to display any issues -void debug(bool isFail, const __FlashStringHelper *message, int state, bool Freeze) +void debug(bool failed, const __FlashStringHelper *message, int state, bool halt) { - if (isFail) { + if (failed) { Serial.print(message); - Serial.print("("); + Serial.print(" - "); + Serial.print(stateDecode(state)); + Serial.print(" ("); Serial.print(state); Serial.println(")"); - while (Freeze); + while (halt) { + delay(1); + } } } @@ -94,8 +171,6 @@ void arrayDump(uint8_t *buffer, uint16_t len) Serial.println(); } -static uint32_t txCounter = 0; - void setup() { @@ -116,10 +191,10 @@ void setup() Serial.println(F("Initialise the radio")); - int state = radio.begin(); + int16_t state = 0; // return value for calls to RadioLib + Serial.println(F("Initialise the radio")); + state = radio.begin(); debug(state != RADIOLIB_ERR_NONE, F("Initialise radio failed"), state, true); - printResult(state == RADIOLIB_ERR_NONE); - delay(2000); #ifdef USING_DIO2_AS_RF_SWITCH #ifdef USING_SX1262 @@ -174,38 +249,60 @@ void setup() radio.setTCXO(3.0); #endif - int retry = 0; - while (1) { - if (u8g2) { - u8g2->clearBuffer(); - u8g2->setFont(u8g2_font_NokiaLargeBold_tf ); - uint16_t str_w = u8g2->getStrWidth(BOARD_VARIANT_NAME); - u8g2->drawStr((u8g2->getWidth() - str_w) / 2, 16, BOARD_VARIANT_NAME); - u8g2->drawHLine(5, 21, u8g2->getWidth() - 5); - - u8g2->setCursor(0, 38); - u8g2->print("Join LoRaWAN :"); - u8g2->println(String(retry++)); - u8g2->sendBuffer(); - } - Serial.println(F("Join ('login') to the LoRaWAN Network")); - node.beginOTAA(joinEUI, devEUI, nwkKey, appKey); - state = node.activateOTAA(); - debug(state < RADIOLIB_ERR_NONE, F("Join failed"), state, false); - if (state == RADIOLIB_ERR_NONE || state == RADIOLIB_LORAWAN_NEW_SESSION) { - break; - } - delay(10000); - Serial.print("Retry "); - } + // Override the default join rate + uint8_t joinDR = 4; + + // Setup the OTAA session information + node.beginOTAA(joinEUI, devEUI, nwkKey, appKey); + + Serial.println(F("Join ('login') the LoRaWAN Network")); + state = node.activateOTAA(joinDR); + debug(state != RADIOLIB_LORAWAN_NEW_SESSION, F("Join failed"), state, true); + + // Print the DevAddr + Serial.print("[LoRaWAN] DevAddr: "); + Serial.println((unsigned long)node.getDevAddr(), HEX); + + // Enable the ADR algorithm (on by default which is preferable) + node.setADR(true); + + // Set a datarate to start off with + node.setDatarate(5); + + // Manages uplink intervals to the TTN Fair Use Policy + node.setDutyCycle(true, 1250); + + // Enable the dwell time limits - 400ms is the limit for the US + node.setDwellTime(true, 400); + + Serial.println(F("Ready!\n")); + + if (u8g2) { + u8g2->clearBuffer(); + u8g2->setFont(u8g2_font_NokiaLargeBold_tf ); + uint16_t str_w = u8g2->getStrWidth(BOARD_VARIANT_NAME); + u8g2->drawStr((u8g2->getWidth() - str_w) / 2, 16, BOARD_VARIANT_NAME); + u8g2->drawHLine(5, 21, u8g2->getWidth() - 5); - Serial.println(F("Joined!\n")); + u8g2->setCursor(0, 38); + u8g2->print("Join LoRaWAN Ready!"); + u8g2->sendBuffer(); + } } void loop() { - Serial.println(F("Sending uplink")); + int16_t state = RADIOLIB_ERR_NONE; + + // set battery fill level - the LoRaWAN network server + // may periodically request this information + // 0 = external power source + // 1 = lowest (empty battery) + // 254 = highest (full battery) + // 255 = unable to measure + uint8_t battLevel = 146; + node.setDeviceStatus(battLevel); // This is the place to gather the sensor inputs // Instead of reading any real sensor, we just generate some random numbers as example @@ -218,37 +315,105 @@ void loop() uplinkPayload[1] = highByte(value2); // See notes for high/lowByte functions uplinkPayload[2] = lowByte(value2); - // Perform an uplink - int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); - debug((state != RADIOLIB_LORAWAN_NO_DOWNLINK) && (state != RADIOLIB_ERR_NONE), F("Error in sendReceive"), state, false); + uint8_t downlinkPayload[10]; // Make sure this fits your plans! + size_t downlinkSize; // To hold the actual payload size received - if (u8g2) { - u8g2->clearBuffer(); - u8g2->setFont(u8g2_font_NokiaLargeBold_tf ); - uint16_t str_w = u8g2->getStrWidth(BOARD_VARIANT_NAME); - u8g2->drawStr((u8g2->getWidth() - str_w) / 2, 16, BOARD_VARIANT_NAME); - u8g2->drawHLine(5, 21, u8g2->getWidth() - 5); - u8g2->setCursor(0, 38); - u8g2->print(node.isActivated() ? "Joined." : "NoJoin"); - u8g2->print("\tTx:"); u8g2->println(++txCounter); -#ifdef ARDUINO_ARCH_ESP32 - u8g2->setCursor(0, 54); - u8g2->println("MAC:"); u8g2->println(ESP.getEfuseMac(), HEX); -#endif - u8g2->sendBuffer(); + // you can also retrieve additional information about an uplink or + // downlink by passing a reference to LoRaWANEvent_t structure + LoRaWANEvent_t uplinkDetails; + LoRaWANEvent_t downlinkDetails; + + uint8_t fPort = 10; + + // Retrieve the last uplink frame counter + uint32_t fCntUp = node.getFCntUp(); + + // Send a confirmed uplink on the second uplink + // and also request the LinkCheck and DeviceTime MAC commands + Serial.println(F("Sending uplink")); + if (fCntUp == 1) { + Serial.println(F("and requesting LinkCheck and DeviceTime")); + node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_LINK_CHECK); + node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_DEVICE_TIME); + state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload), fPort, downlinkPayload, &downlinkSize, true, &uplinkDetails, &downlinkDetails); + } else { + state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload), fPort, downlinkPayload, &downlinkSize, false, &uplinkDetails, &downlinkDetails); } + debug(state < RADIOLIB_ERR_NONE, F("Error in sendReceive"), state, false); + + // Check if a downlink was received + // (state 0 = no downlink, state 1/2 = downlink in window Rx1/Rx2) + if (state > 0) { + Serial.println(F("Received a downlink")); + // Did we get a downlink with data for us + if (downlinkSize > 0) { + Serial.println(F("Downlink data: ")); + arrayDump(downlinkPayload, downlinkSize); + } else { + Serial.println(F("")); + } + // print RSSI (Received Signal Strength Indicator) + Serial.print(F("[LoRaWAN] RSSI:\t\t")); + Serial.print(radio.getRSSI()); + Serial.println(F(" dBm")); + + // print SNR (Signal-to-Noise Ratio) + Serial.print(F("[LoRaWAN] SNR:\t\t")); + Serial.print(radio.getSNR()); + Serial.println(F(" dB")); + + // print extra information about the event + Serial.println(F("[LoRaWAN] Event information:")); + Serial.print(F("[LoRaWAN] Confirmed:\t")); + Serial.println(downlinkDetails.confirmed); + Serial.print(F("[LoRaWAN] Confirming:\t")); + Serial.println(downlinkDetails.confirming); + Serial.print(F("[LoRaWAN] Datarate:\t")); + Serial.println(downlinkDetails.datarate); + Serial.print(F("[LoRaWAN] Frequency:\t")); + Serial.print(downlinkDetails.freq, 3); + Serial.println(F(" MHz")); + Serial.print(F("[LoRaWAN] Frame count:\t")); + Serial.println(downlinkDetails.fCnt); + Serial.print(F("[LoRaWAN] Port:\t\t")); + Serial.println(downlinkDetails.fPort); + Serial.print(F("[LoRaWAN] Time-on-air: \t")); + Serial.print(node.getLastToA()); + Serial.println(F(" ms")); + Serial.print(F("[LoRaWAN] Rx window: \t")); + Serial.println(state); + + uint8_t margin = 0; + uint8_t gwCnt = 0; + if (node.getMacLinkCheckAns(&margin, &gwCnt) == RADIOLIB_ERR_NONE) { + Serial.print(F("[LoRaWAN] LinkCheck margin:\t")); + Serial.println(margin); + Serial.print(F("[LoRaWAN] LinkCheck count:\t")); + Serial.println(gwCnt); + } - Serial.print(F("Uplink complete, next in ")); - Serial.print(uplinkIntervalSeconds); - Serial.println(F(" seconds")); + uint32_t networkTime = 0; + uint8_t fracSecond = 0; + if (node.getMacDeviceTimeAns(&networkTime, &fracSecond, true) == RADIOLIB_ERR_NONE) { + Serial.print(F("[LoRaWAN] DeviceTime Unix:\t")); + Serial.println(networkTime); + Serial.print(F("[LoRaWAN] DeviceTime second:\t1/")); + Serial.println(fracSecond); + } -#ifdef BOARD_LED - digitalWrite(BOARD_LED, LED_ON); - delay(200); - digitalWrite(BOARD_LED, !LED_ON); -#endif + } else { + Serial.println(F("[LoRaWAN] No downlink received")); + } + + // wait before sending another packet + uint32_t minimumDelay = uplinkIntervalSeconds * 1000UL; + uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per FUP & law!) + uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows + + Serial.print(F("[LoRaWAN] Next uplink in ")); + Serial.print(delayMs / 1000); + Serial.println(F(" seconds\n")); - // Wait until next uplink - observing legal & TTN FUP constraints - delay(uplinkIntervalSeconds * 1000UL); // delay needs milli-seconds + delay(delayMs); }