-
-
Save proffalken/d7ef6f569cbbfaa2374e7aa6f20ae6a9 to your computer and use it in GitHub Desktop.
LoRaWAN from JSON API
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
void LoRaWanClass::join() | |
{ | |
if( overTheAirActivation == true ) | |
{ | |
Serial.println("joining..."); | |
MlmeReq_t mlmeReq; | |
mlmeReq.Type = MLME_JOIN; | |
mlmeReq.Req.Join.DevEui = DevEui; | |
mlmeReq.Req.Join.AppEui = AppEui; | |
mlmeReq.Req.Join.AppKey = AppKey; | |
mlmeReq.Req.Join.NbTrials = 1; | |
/******* FAILING FUNCTION CALL (Line 484) *********/ | |
if( LoRaMacMlmeRequest( &mlmeReq ) == LORAMAC_STATUS_OK ) | |
/******* END FAILING FUNCTION CALL ********/ | |
{ | |
deviceState = DEVICE_STATE_SLEEP; | |
} | |
else | |
{ | |
deviceState = DEVICE_STATE_CYCLE; | |
} | |
} |
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
switch ( mlmeRequest->Type ) { | |
case MLME_JOIN: { | |
if ( ( mlmeRequest->Req.Join.DevEui == NULL ) || | |
( mlmeRequest->Req.Join.AppEui == NULL ) || | |
( mlmeRequest->Req.Join.AppKey == NULL ) ) | |
{ | |
return LORAMAC_STATUS_PARAMETER_INVALID; | |
} | |
// Verify the parameter NbTrials for the join procedure | |
verify.NbJoinTrials = mlmeRequest->Req.Join.NbTrials; | |
if ( RegionVerify( LoRaMacRegion, &verify, PHY_NB_JOIN_TRIALS ) == false ) { | |
// Value not supported, get default | |
getPhy.Attribute = PHY_DEF_NB_JOIN_TRIALS; | |
phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy ); | |
mlmeRequest->Req.Join.NbTrials = ( uint8_t ) phyParam.Value; | |
} | |
LoRaMacFlags.Bits.MlmeReq = 1; | |
queueElement.Request = mlmeRequest->Type; | |
LoRaMacDevEui = mlmeRequest->Req.Join.DevEui; | |
LoRaMacAppEui = mlmeRequest->Req.Join.AppEui; | |
LoRaMacAppKey = mlmeRequest->Req.Join.AppKey; | |
queueElement.Status = LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL; | |
queueElement.RestrictCommonReadyToHandle = false; | |
/********** ERROR RAISED BY NEXT LINE (Line 3181) ******/ | |
LoRaMacConfirmQueueAdd( &queueElement ); | |
/*********** ERROR ENDS **************************/ | |
MaxJoinRequestTrials = mlmeRequest->Req.Join.NbTrials; | |
// Reset variable JoinRequestTrials | |
JoinRequestTrials = 0; | |
// Setup header information | |
macHdr.Value = 0; | |
macHdr.Bits.MType = FRAME_TYPE_JOIN_REQ; | |
ResetMacParameters( ); | |
altDr.NbTrials = JoinRequestTrials + 1; | |
LoRaMacParams.ChannelsDatarate = RegionAlternateDr( LoRaMacRegion, &altDr ); | |
status = Send( &macHdr, 0, NULL, 0 ); | |
break; | |
} |
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
bool LoRaMacConfirmQueueAdd( MlmeConfirmQueue_t* mlmeConfirm ) | |
{ | |
if( MlmeConfirmQueueCnt >= LORA_MAC_MLME_CONFIRM_QUEUE_LEN ) | |
{ | |
// Protect the buffer against overwrites | |
return false; | |
} | |
// Add the element to the ring buffer | |
BufferEnd->Request = mlmeConfirm->Request; // <------- Line 134 | |
BufferEnd->Status = mlmeConfirm->Status; | |
BufferEnd->RestrictCommonReadyToHandle = mlmeConfirm->RestrictCommonReadyToHandle; | |
BufferEnd->ReadyToHandle = false; | |
// Increase counter | |
MlmeConfirmQueueCnt++; | |
// Update end pointer | |
BufferEnd = IncreaseBufferPointer( BufferEnd ); | |
return true; | |
} |
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
#define LORA_PREAMBLE_LENGTH 8 | |
#include <ESP32_LoRaWAN.h> | |
#include <Arduino.h> | |
#include <ArduinoJson.h> | |
#include <HTTPClient.h> | |
#include <Preferences.h> | |
#include <WiFi.h> | |
// Setup the ability to read from the environment | |
#define ST(A) #A | |
#define STR(A) ST(A) | |
// setup the basic counter | |
int counter=0; | |
/*LoraWan channelsmask, default channels 0-7*/ | |
uint16_t userChannelsMask[6]={ 0x00FF,0x0000,0x0000,0x0000,0x0000,0x0000 }; | |
/*LoraWan Class, Class A and Class C are supported*/ | |
DeviceClass_t loraWanClass = CLASS_A; | |
/*OTAA or ABP*/ | |
bool overTheAirActivation = true; | |
/*the application data transmission duty cycle. value in [ms].*/ | |
uint32_t appTxDutyCycle = 15000; | |
/*ADR enable*/ | |
bool loraWanAdr = true; | |
/* Indicates if the node is sending confirmed or unconfirmed messages */ | |
bool isTxConfirmed = true; | |
/* Application port */ | |
uint8_t appPort = 2; | |
/*! | |
* Number of trials to transmit the frame, if the LoRaMAC layer did not | |
* receive an acknowledgment. The MAC performs a datarate adaptation, | |
* according to the LoRaWAN Specification V1.0.2, chapter 18.4, according | |
* to the following table: | |
* | |
* Transmission nb | Data Rate | |
* ----------------|----------- | |
* 1 (first) | DR | |
* 2 | DR | |
* 3 | max(DR-1,0) | |
* 4 | max(DR-1,0) | |
* 5 | max(DR-2,0) | |
* 6 | max(DR-2,0) | |
* 7 | max(DR-3,0) | |
* 8 | max(DR-3,0) | |
* | |
* Note, that if NbTrials is set to 1 or 2, the MAC will not decrease | |
* the datarate, in case the LoRaMAC layer did not receive an acknowledgment | |
*/ | |
uint8_t confirmedNbTrials = 8; | |
/* ABP para - NOT NEEDED BECAUSE OTAA, BUT ALSO NEEDED BECAUSE HELTEC CODE IS CRAP*/ | |
uint8_t NwkSKey[] = { 0x15, 0xb1, 0xd0, 0xef, 0xa4, 0x63, 0xdf, 0xbe, 0x3d, 0x11, 0x18, 0x1e, 0x1e, 0xc7, 0xda,0x85 }; | |
uint8_t AppSKey[] = { 0xd7, 0x2c, 0x78, 0x75, 0x8c, 0xdc, 0xca, 0xbf, 0x55, 0xee, 0x4a, 0x77, 0x8d, 0x16, 0xef,0x67 }; | |
uint32_t DevAddr = ( uint32_t )0x007e6ae1; | |
/*LoraWan region, select in arduino IDE tools*/ | |
LoRaMacRegion_t loraWanRegion = LORAWAN_ACTIVE_REGION; | |
// Initialise (but do not set a value for) the OTAA Keys | |
uint8_t DevEui[8]={}; | |
uint8_t AppEui[8]={}; | |
uint8_t AppKey[16]={}; | |
uint32_t license[4] = {0x0,0x0,0x0,0x0}; | |
// Create our configuration object | |
Preferences config; | |
String wifi_ssid; | |
String wifi_pass; | |
String provisioning_uri; | |
// Heltec need the chip ID for the lorawan license | |
uint64_t chipId = 0; | |
/*LoraWan debug level, select in arduino IDE tools. | |
* None : print basic info. | |
* Freq : print Tx and Rx freq, DR info. | |
* Freq && DIO : print Tx and Rx freq, DR, DIO0 interrupt and DIO1 interrupt info. | |
* Freq && DIO && PW: print Tx and Rx freq, DR, DIO0 interrupt, DIO1 interrupt and MCU deepsleep info. | |
*/ | |
uint8_t debugLevel = LORAWAN_DEBUG_LEVEL; | |
// The following functions are taken from | |
// https://forum.arduino.cc/t/arduino-code-equivalent-for-python-code-binascii-unhexlify-command/674454/3 | |
// and appear to convert the data correctly from the string representation of the TTN API to the uint8_t | |
// expected by the LoRaWAN library. | |
// add the value of an hex digit to res | |
// if the supposed hexDigit is not an hex digit, return false | |
bool addHexDigitToInt(const char hexDigit, int *res) | |
{ | |
if (isdigit(hexDigit)) | |
{ | |
*res += (hexDigit - '0'); | |
return true; | |
} | |
if (!isxdigit(hexDigit)) | |
return false; | |
const char base = isupper(hexDigit) ? 'A' : 'a'; | |
*res += (hexDigit - base) + 10; | |
return true; | |
} | |
// convert two hex digits to an integer | |
// if one (or both) chars are not hex digits, return -1 | |
int twoHexDigitToInt(const char *pHex) | |
{ | |
int res = 0; | |
if (!addHexDigitToInt(*pHex, &res)) | |
return -1; | |
res <<= 4; | |
if (addHexDigitToInt(*(pHex + 1), &res)) | |
return res; | |
return -1; | |
} | |
// convert the hex representation of a byte array back to a byte array | |
// | |
// parameters | |
// | |
// hexStr = [input] hex string, must contain only hex digit (['0'-'9']['a'-'f']['A'-'F']), two hex digit per output byte | |
// binBuf = [output] buffer for binary data | |
// binBufLen = [input] len of binBuf in bytes, must be >= strlen(hexStr)/2 | |
// | |
// return | |
// true if conversion was successfull, false otherwise | |
// | |
bool hexToBin(const char *hexStr, byte *binBuf, const int binBufLen) | |
{ | |
int hexLen = strlen(hexStr); | |
Serial.print("hexToBin called with string: "); | |
Serial.print(hexStr); | |
Serial.print(". Length of string is "); | |
Serial.print(hexLen); | |
Serial.print(" and size was set to "); | |
Serial.println(binBufLen); | |
if (hexLen % 2) { | |
// the input string contains an odd number of hex chars | |
Serial.println("Odd number of hex characters detected"); | |
return false; | |
} | |
if (binBufLen < hexLen/2){ | |
Serial.print("Buffer is too small to store the data, must be at least "); | |
Serial.print(hexLen); | |
Serial.print(" but size was set to "); | |
Serial.println(binBufLen); | |
// the output buffer can't store all the data | |
return false; | |
} | |
const char *pHex = hexStr; | |
byte *pBuf = binBuf; | |
for (int i=0; i < hexLen; i+=2) | |
{ | |
int nextVal = twoHexDigitToInt(pHex+i); | |
if (nextVal < 0) | |
return false; | |
*pBuf = (byte)nextVal; | |
pBuf++; | |
} | |
return true; | |
} | |
void setup() { | |
Serial.begin(115200); | |
delay(300); | |
Serial.begin(115200); | |
while (!Serial); | |
SPI.begin(SCK,MISO,MOSI,SS); | |
// Get the chipID values for Heltec | |
chipId=ESP.getEfuseMac(); | |
Serial.printf("ESP32ChipID=%04X",(uint16_t)(chipId>>32));//print High 2bytes | |
Serial.printf("%08X\r\n",(uint32_t)chipId);//print Low 4bytes. | |
// Hook into our preferences | |
config.begin("wifi", false); | |
// If we're setting a new WiFi configuration, do it now | |
if (STR(WIFI_SSID) != ""){ | |
Serial.print("WiFi SSID was not set, setting to "); | |
Serial.println(STR(WIFI_SSID)); | |
config.putString("wifi_ssid", STR(WIFI_SSID)); | |
config.putString("wifi_pass", STR(WIFI_PASS)); | |
} | |
wifi_ssid = config.getString("wifi_ssid", ""); | |
wifi_pass = config.getString("wifi_pass", ""); | |
// Save our changes to the configuration | |
config.end(); | |
// Connect to the WiFi | |
WiFi.mode(WIFI_STA); | |
WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str()); | |
Serial.print("Connecting to "); | |
Serial.println(wifi_ssid); | |
while (WiFi.status() != WL_CONNECTED) { | |
Serial.print('.'); | |
delay(1000); | |
} | |
Serial.print("Connected to WiFi. IP Address: "); | |
Serial.println(WiFi.localIP()); | |
// Get registration details | |
if ( WiFi.status() == WL_CONNECTED){ | |
Serial.println("Attempting to register with server"); | |
HTTPClient htreg; | |
htreg.setTimeout(1000); | |
provisioning_uri = "https://my.provisioning.server/api/path"; | |
Serial.print("Reg URI: "); | |
Serial.println(provisioning_uri.c_str()); | |
htreg.begin(provisioning_uri.c_str()); | |
// Use HTTP 1.x | |
htreg.useHTTP10(true); | |
// Add the headers, including the auth token | |
htreg.addHeader("Content-Type", "application/json"); | |
// Create the payload JSON doc | |
DynamicJsonDocument reg_doc(128); | |
// Get the MAC Address | |
String mac_addr = WiFi.macAddress(); | |
// Convert it to lower case | |
mac_addr.toLowerCase(); | |
// Remove the colons | |
mac_addr.replace(":", ""); | |
// Echo to the serial port | |
Serial.print("DEVICE ID: "); | |
Serial.println(mac_addr); | |
// Set the JSON Value | |
reg_doc["mac_addr"] = mac_addr; | |
// Create the payload string | |
String reg_payload; | |
// Serialise the payload string into the payload doc | |
serializeJson(reg_doc, reg_payload); | |
// Get the status code | |
int resp_code = htreg.POST(reg_payload); | |
Serial.print("HTTP Response Code was: "); | |
Serial.println(resp_code); | |
// If the status code is 200, then we have a valid template being returned | |
if(resp_code == 200){ | |
// We have a response. | |
// This will look like the following and the values are straight from the TTN API: | |
// | |
// { | |
// "dev_eui": "CC50E381ABC41242", | |
// "app_key": "EFE07F2B338DC55D60CA984E408DE5C3", | |
// "app_eui": "CC50E381ABC41242" | |
// } | |
DynamicJsonDocument ttn_config_response(768); | |
DeserializationError error = deserializeJson(ttn_config_response, htreg.getStream()); | |
if (error) { | |
Serial.print("TTN Response: "); | |
Serial.println(htreg.getString()); | |
Serial.print("deserializeJson() failed: "); | |
Serial.println(error.c_str()); | |
return; | |
} | |
Serial.println("Configuring for TTN."); | |
Serial.print("DevEui JSON: "); | |
Serial.println(ttn_config_response["dev_eui"].as<String>()); | |
Serial.print("AppEui JSON: "); | |
Serial.println(ttn_config_response["app_eui"].as<String>()); | |
Serial.print("AppKey JSON: "); | |
Serial.println(ttn_config_response["app_key"].as<String>()); | |
// Convert our "strings" into bytes that can be passed to the library | |
bool dev_translate = hexToBin(ttn_config_response["dev_eui"], DevEui, 16); | |
bool app_eui_translate = hexToBin(ttn_config_response["app_eui"], AppEui, 16); | |
bool app_key_translate = hexToBin(ttn_config_response["app_key"], AppKey, 32); | |
// Confirm that our translations were successful | |
if (dev_translate != true){ | |
Serial.println("DevEui Translation failed"); | |
} | |
if (app_eui_translate != true){ | |
Serial.println("AppEui Translation failed"); | |
} | |
if (app_key_translate != true){ | |
Serial.println("App Key Translation failed"); | |
} | |
// Print our values to the command line to ensure they still match the JSON values above | |
Serial.printf("\nDevEui="); | |
for(int i = 0;i<sizeof(DevEui);i++) | |
{ | |
Serial.printf("%02X",DevEui[i]); | |
} | |
Serial.printf("\nAppEui="); | |
for(int i = 0;i<sizeof(AppEui);i++) | |
{ | |
Serial.printf("%02X",AppEui[i]); | |
} | |
Serial.printf("\nAppKey="); | |
for(int i = 0;i<sizeof(AppKey);i++) | |
{ | |
Serial.printf("%02X",AppKey[i]); | |
} | |
// Start the MCU Init process | |
if(mcuStarted==0) | |
{ | |
LoRaWAN.displayMcuInit(); | |
} | |
Mcu.init(SS,RST_LoRa,DIO0,DIO1,license); | |
deviceState = DEVICE_STATE_INIT; | |
}else{ | |
Serial.print("Error on sending POST: "); | |
Serial.println(resp_code); | |
} | |
htreg.end(); //Free resources | |
} | |
} | |
void loop(){ | |
switch( deviceState ) | |
{ | |
case DEVICE_STATE_INIT: | |
{ | |
Serial.println("LoRaWAN Initialising"); | |
LoRaWAN.init(loraWanClass,loraWanRegion); | |
break; | |
} | |
case DEVICE_STATE_JOIN: | |
{ | |
Serial.println("Attempting Join"); | |
LoRaWAN.join(); | |
break; | |
} | |
case DEVICE_STATE_SEND: | |
{ | |
Serial.println("Sending Data"); | |
prepareTxFrame( appPort ); | |
LoRaWAN.send(loraWanClass); | |
deviceState = DEVICE_STATE_CYCLE; | |
break; | |
} | |
case DEVICE_STATE_CYCLE: | |
{ | |
Serial.println("Cycling State"); | |
// Schedule next packet transmission | |
txDutyCycleTime = appTxDutyCycle + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND ); | |
LoRaWAN.cycle(txDutyCycleTime); | |
deviceState = DEVICE_STATE_SLEEP; | |
break; | |
} | |
case DEVICE_STATE_SLEEP: | |
{ | |
Serial.println("Sleeping"); | |
LoRaWAN.sleep(loraWanClass,debugLevel); | |
break; | |
} | |
default: | |
{ | |
Serial.println("Default Case Triggered, set to reinit"); | |
deviceState = DEVICE_STATE_INIT; | |
break; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment