Created
June 13, 2021 10:11
-
-
Save tckb/6b8a37da5f4d7a7feb0b8ed827ec93fb to your computer and use it in GitHub Desktop.
lorawan_node
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "LoRaWan_APP.h" | |
#include "Arduino.h" | |
#include <CayenneLPP.h> | |
#include <Display.h> | |
/*LoRaWan related params */ | |
// Helium OTAA params | |
// uint8_t devEui[] = {_SECRET_STUFF_}; | |
// uint8_t appEui[] = {_SECRET_STUFF_}; | |
// uint8_t appKey[] = {_SECRET_STUFF_}; | |
// TTN OTAA params` | |
uint8_t devEui[] = {_SECRET_STUFF_}; | |
uint8_t appEui[] = {_SECRET_STUFF_}; | |
uint8_t appKey[] = {_SECRET_STUFF_}; | |
// ABP PARAM | |
// NOT USED BUT STILL TO MENTION | |
uint8_t nwkSKey[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | |
uint8_t appSKey[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | |
uint32_t devAddr = (uint32_t)0x00000000; | |
// ABP PARAM | |
uint16_t userChannelsMask[6] = {0x00FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}; | |
// Default LORaWan Config | |
LoRaMacRegion_t loraWanRegion = ACTIVE_REGION; // Set to REGION_EU868 | |
DeviceClass_t loraWanClass = LORAWAN_CLASS; // Set to CLASS_A | |
uint32_t appTxDutyCycle = 15000; //the application data transmission duty cycle in ms | |
bool overTheAirActivation = LORAWAN_NETMODE; // OTAA mode from the menu | |
bool loraWanAdr = LORAWAN_ADR; // Adaptive Bit Rate - normally off | |
bool keepNet = LORAWAN_NET_RESERVE; // normally OFF | |
bool isTxConfirmed = LORAWAN_UPLINKMODE; // if the node is sending confirmed or unconfirmed messages; set to 'Unconfirmed' | |
uint8_t appPort = 2; | |
uint8_t confirmedNbTrials = 4; | |
// other params | |
bool sleepMode = false; | |
uint32_t lastScreenPrint = 0; | |
uint32_t joinStart = 0; | |
// important sensory information | |
float data_bat_per = 100; | |
uint16 data_bat_vol = 0; | |
uint8 data_bat_level = 0; | |
CayenneLPP lpp(LORAWAN_APP_DATA_MAX_SIZE); | |
/* Main code starts here */ | |
void enable_ext_vol_supply(void) | |
{ | |
pinMode(Vext, OUTPUT); | |
digitalWrite(Vext, LOW); | |
delay(100); | |
} | |
void disable_ext_vol_supply(void) | |
{ | |
pinMode(Vext, OUTPUT); | |
digitalWrite(Vext, HIGH); | |
} | |
void cal_battery_level() | |
{ | |
uint32 time = millis(); | |
bool read_sensor = false; | |
// don't calculate the battery as often as it | |
if (lastScreenPrint == 0) | |
{ | |
read_sensor = true; | |
lastScreenPrint = time; | |
} | |
else | |
{ | |
if ((time - lastScreenPrint) >= 15000) | |
{ | |
read_sensor = true; | |
lastScreenPrint = time; | |
} | |
else | |
{ | |
return; | |
} | |
} | |
if (read_sensor) | |
{ | |
data_bat_level = BoardGetBatteryLevel(); | |
data_bat_vol = getBatteryVoltage(); | |
data_bat_per = round(((float_t)data_bat_level - BAT_LEVEL_EMPTY) * 100 / (BAT_LEVEL_FULL - BAT_LEVEL_EMPTY)); | |
set_battery_percent(data_bat_per); | |
set_battery_level(data_bat_level); | |
} | |
#ifdef DEBUG | |
Serial.println(); | |
Serial.print("battery_level:"); | |
Serial.println(battery_level); | |
Serial.print("battery_level_per: "); | |
Serial.print(battery_level_per); | |
#endif | |
} | |
bool is_lora_joined() | |
{ | |
bool joined = false; | |
MibRequestConfirm_t mibReq; | |
LoRaMacStatus_t status; | |
mibReq.Type = MIB_NETWORK_JOINED; | |
status = LoRaMacMibGetRequestConfirm(&mibReq); | |
if (status == LORAMAC_STATUS_OK) | |
{ | |
if (mibReq.Param.IsNetworkJoined == true) | |
{ | |
joined = true; | |
} | |
} | |
return joined; | |
} | |
void goto_sleep(bool wakeupDisplay = true) | |
{ | |
sleepMode = true; | |
if (wakeupDisplay) | |
{ | |
display_wakeup(); | |
} | |
set_main_status("going to sleep in 4s"); | |
delay(4000); | |
display_sleep(); | |
deviceState = DEVICE_STATE_CYCLE; | |
} | |
void wakeup_from_sleep(bool wakeupDisplay = true) | |
{ | |
sleepMode = false; | |
if (wakeupDisplay) | |
{ | |
enable_ext_vol_supply(); | |
display_init(); | |
display_wakeup(); | |
} | |
display_clear(); | |
display_status_bar(); | |
set_main_status("waking up in 4s"); | |
delay(4000); | |
deviceState = DEVICE_STATE_SEND; | |
} | |
/* | |
when user press this button less than 700ms , we either wakeup from sleep | |
or we go back to sleep | |
*/ | |
void user_key_handler(void) | |
{ | |
delay(10); | |
if (digitalRead(P3_3) == 0) | |
{ | |
uint16_t keyDownTime = 0; | |
while (digitalRead(P3_3) == 0) | |
{ | |
delay(1); | |
keyDownTime++; | |
if (keyDownTime >= 700) | |
break; | |
} | |
if (keyDownTime < 700) | |
{ | |
if (sleepMode) | |
{ | |
wakeup_from_sleep(); | |
} | |
else | |
{ | |
goto_sleep(); | |
} | |
} | |
} | |
} | |
static void prepare_lora_tx_frame(uint8_t port) | |
{ | |
lpp.addDigitalOutput(1, data_bat_vol); | |
lpp.addDigitalOutput(2, data_bat_per); | |
appDataSize = lpp.getSize(); | |
lpp.copy(appData); | |
lpp.reset(); | |
} | |
/////////// | |
// Lora Display | |
/////////// | |
void display_lora_init() | |
{ | |
network_status(false); | |
uplink_status(false); | |
downlink_status(false); | |
cal_battery_level(); | |
display_status_bar(); | |
set_main_status("Initializing..."); | |
} | |
void display_lora_joining() | |
{ | |
network_status(false); | |
uplink_status(true); | |
downlink_status(false); | |
ack_status(false); | |
cal_battery_level(); | |
display_status_bar(); | |
set_main_status("Join Requested..."); | |
} | |
void display_lora_sending() | |
{ | |
network_status(true); | |
uplink_status(true); | |
downlink_status(false); | |
ack_status(false); | |
enable_ext_vol_supply(); | |
cal_battery_level(); | |
display_status_bar(); | |
set_main_status("preparing to send..."); | |
if (LoRaWAN.LastDownlinkRssi() != 0) | |
{ | |
String link_status = "Link: " + String(LoRaWAN.LastDownlinkRssi()) + "rssi / " + LoRaWAN.LastDownlinkSNR() + "db"; | |
set_footer_status(link_status); | |
} | |
delay(1000); | |
} | |
void display_lora_acknowledge() | |
{ | |
if (!is_lora_joined()) | |
{ | |
network_status(false); | |
uplink_status(true); | |
downlink_status(false); | |
ack_status(false); | |
cal_battery_level(); | |
display_status_bar(); | |
set_main_status("Waiting to join..."); | |
} | |
else | |
{ | |
if (!sleepMode) | |
{ | |
// if the display is sleeping | |
display_wakeup(); | |
if (LoRaWAN.receivedUplinkAck()) | |
{ | |
LoRaWAN.toggleUplinkAck(); | |
network_status(true); | |
uplink_status(true); | |
downlink_status(true); | |
ack_status(true); | |
display_status_bar(); | |
} | |
else | |
{ | |
uplink_status(true); | |
network_status(true); | |
if (get_ack_status()) | |
{ | |
// keep showing ack if this is already on | |
ack_status(true); | |
} | |
downlink_status(true); | |
display_status_bar(); | |
// nothing to do here | |
set_main_status("Data sent..."); | |
} | |
if (LoRaWAN.LastDownlinkRssi() != 0) | |
{ | |
String link_status = "Link: " + String(LoRaWAN.LastDownlinkRssi()) + "rssi / " + LoRaWAN.LastDownlinkSNR() + "db"; | |
set_footer_status(link_status); | |
} | |
} | |
else | |
{ | |
display_sleep(); | |
disable_ext_vol_supply(); | |
} | |
} | |
} | |
/////////// | |
// setup and loop | |
/////////// | |
void setup() | |
{ | |
boardInitMcu(); | |
Serial.begin(115200); | |
#if (AT_SUPPORT) | |
enableAt(); | |
#endif | |
enable_ext_vol_supply(); | |
display_init(); | |
show_welcome(); | |
deviceState = DEVICE_STATE_INIT; | |
LoRaWAN.ifskipjoin(); | |
cal_battery_level(); | |
//Setup user button - this must be after LoRaWAN.ifskipjoin(), because the button is used there to cancel stored settings load and initiate a new join | |
pinMode(P3_3, INPUT); | |
attachInterrupt(P3_3, user_key_handler, FALLING); | |
} | |
void loop() | |
{ | |
switch (deviceState) | |
{ | |
case DEVICE_STATE_INIT: | |
display_lora_init(); | |
#if (AT_SUPPORT) | |
getDevParam(); | |
#endif | |
printDevParam(); | |
LoRaWAN.init(loraWanClass, loraWanRegion); | |
LoRaWAN.setDataRateForNoADR(0); | |
deviceState = DEVICE_STATE_JOIN; | |
break; | |
case DEVICE_STATE_JOIN: | |
display_lora_joining(); | |
LoRaWAN.join(); | |
break; | |
case DEVICE_STATE_SEND: | |
// User pressed the button while we were waiting for the next send timer | |
if (sleepMode) | |
{ | |
// Send to Cycle so it could setup a sleep timer if not done yet | |
deviceState = DEVICE_STATE_CYCLE; | |
} | |
else | |
{ | |
prepare_lora_tx_frame(appPort); | |
display_lora_sending(); | |
LoRaWAN.send(); | |
// Schedule next send | |
deviceState = DEVICE_STATE_CYCLE; | |
} | |
break; | |
case DEVICE_STATE_CYCLE: | |
txDutyCycleTime = appTxDutyCycle + randr(0, APP_TX_DUTYCYCLE_RND); | |
LoRaWAN.cycle(txDutyCycleTime); | |
deviceState = DEVICE_STATE_SLEEP; | |
break; | |
case DEVICE_STATE_SLEEP: | |
display_lora_acknowledge(); | |
LoRaWAN.sleep(); | |
break; | |
default: | |
deviceState = DEVICE_STATE_INIT; | |
break; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment