mirror of
https://github.com/m5stack/M5Stack.git
synced 2026-05-20 10:06:46 -07:00
7af7b6f7de
In C strings are 0-terminated, meaning that after the call to sprintf the char contains { 'a', 'b', '\0' }. This is an array of 3 chars we only reserver memory for 2.
1046 lines
26 KiB
C++
1046 lines
26 KiB
C++
/*
|
|
LoRaWAN.cpp for M5Stack (fork from https://github.com/toddkrein/OTAA-LoRaWAN-Seeed)
|
|
|
|
2013 Copyright (c) Seeed Technology Inc. All right reserved.
|
|
2017 Copyright (c) Todd Krein. All rights reserved.
|
|
|
|
Original Author: Wayne Weng
|
|
Date: 2016-10-17
|
|
|
|
Greatly overhauled 2017 by Todd Krein (todd@krein.org)
|
|
|
|
The MIT License (MIT)
|
|
|
|
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.1 USA
|
|
*/
|
|
|
|
#include "LoRaWan.h"
|
|
|
|
const char *physTypeStr[10] = {"EU434", "EU868", "US915", "US915HYBRID", "AU915", "AS923", "CN470", "KR920", "CN470PREQUEL", "STE920"};
|
|
|
|
LoRaWanClass::LoRaWanClass(void)
|
|
{
|
|
memset(_buffer, 0, 256);
|
|
debug = false;
|
|
}
|
|
|
|
void LoRaWanClass::init(void)
|
|
{
|
|
Serial2.begin(9600, SERIAL_8N1, 16, 17);
|
|
// Serial2.begin(9600, SERIAL_8N1, 2, 5);
|
|
// SerialLoRa.begin(9600);
|
|
}
|
|
|
|
void LoRaWanClass::getVersion(char *buffer, short length, unsigned char timeout)
|
|
{
|
|
if(buffer)
|
|
{
|
|
while(SerialLoRa.available())SerialLoRa.read();
|
|
sendCommand("AT+VER=?\r\n");
|
|
readBuffer(buffer, length, timeout);
|
|
}
|
|
}
|
|
|
|
void LoRaWanClass::getId(char *buffer, short length, unsigned char timeout)
|
|
{
|
|
if(buffer)
|
|
{
|
|
while(SerialLoRa.available())SerialLoRa.read();
|
|
sendCommand("AT+ID=?\r\n");
|
|
readBuffer(buffer, length, timeout);
|
|
}
|
|
}
|
|
|
|
void LoRaWanClass::setId(char *DevAddr, char *DevEUI, char *AppEUI)
|
|
{
|
|
char cmd[64];
|
|
|
|
if(DevAddr)
|
|
{
|
|
memset(cmd, 0, 64);
|
|
sprintf(cmd, "AT+ID=DevAddr,\"%s\"\r\n", DevAddr);
|
|
sendCommand(cmd);
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
if(DevEUI)
|
|
{
|
|
memset(cmd, 0, 64);
|
|
sprintf(cmd, "AT+ID=DevEui,\"%s\"\r\n", DevEUI);
|
|
sendCommand(cmd);
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
if(AppEUI)
|
|
{
|
|
memset(cmd, 0, 64);
|
|
sprintf(cmd, "AT+ID=AppEui,\"%s\"\r\n", AppEUI);
|
|
sendCommand(cmd);
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
}
|
|
|
|
void LoRaWanClass::setKey(char *NwkSKey, char *AppSKey, char *AppKey)
|
|
{
|
|
char cmd[64];
|
|
|
|
if(NwkSKey)
|
|
{
|
|
memset(cmd, 0, 64);
|
|
sprintf(cmd, "AT+KEY=NWKSKEY,\"%s\"\r\n", NwkSKey);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
if(AppSKey)
|
|
{
|
|
memset(cmd, 0, 64);
|
|
sprintf(cmd, "AT+KEY=APPSKEY,\"%s\"\r\n", AppSKey);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
if(AppKey)
|
|
{
|
|
memset(cmd, 0, 64);
|
|
sprintf(cmd, "AT+KEY= APPKEY,\"%s\"\r\n", AppKey);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
}
|
|
|
|
bool LoRaWanClass::setDataRate(_data_rate_t dataRate, _physical_type_t physicalType)
|
|
{
|
|
char cmd[32];
|
|
// const char *str;
|
|
|
|
if ((physicalType <= UNINIT) && (physicalType >= UNDEF)) {
|
|
myType = UNINIT;
|
|
debugPrint("Unknown datarate\n");
|
|
return false;
|
|
}
|
|
|
|
myType = physicalType;
|
|
sendCommand(F("AT+DR="));
|
|
// str = (const char*)(physTypeStr[(int)myType]);
|
|
sendCommand(physTypeStr[myType]);
|
|
// sendCommand(str);
|
|
sendCommand(F("\r\n"));
|
|
// if(physicalType == EU434)sendCommand("AT+DR=EU433\r\n");
|
|
// else if(physicalType == EU868)sendCommand("AT+DR=EU868\r\n");
|
|
// else if(physicalType == US915)sendCommand("AT+DR=US915\r\n");
|
|
// else if(physicalType == AU920)sendCommand("AT+DR=AU920\r\n");
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
|
|
memset(cmd, 0, 32);
|
|
sprintf(cmd, "AT+DR=%d\r\n", dataRate);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
return true;
|
|
}
|
|
|
|
void LoRaWanClass::setPower(short power)
|
|
{
|
|
char cmd[32];
|
|
|
|
memset(cmd, 0, 32);
|
|
sprintf(cmd, "AT+POWER=%d\r\n", power);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setPort(unsigned char port)
|
|
{
|
|
char cmd[32];
|
|
|
|
memset(cmd, 0, 32);
|
|
sprintf(cmd, "AT+PORT=%d\r\n", port);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setAdaptiveDataRate(bool command)
|
|
{
|
|
if(command)sendCommand("AT+ADR=ON\r\n");
|
|
else sendCommand("AT+ADR=OFF\r\n");
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::getChannel(void)
|
|
{
|
|
sendCommand("AT+CH\r\n");
|
|
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setChannel(unsigned char channel, float frequency)
|
|
{
|
|
char cmd[32];
|
|
|
|
// if(channel > 16) channel = 16; // ??? this is wrong for US915
|
|
|
|
memset(cmd, 0, 32);
|
|
if (frequency == 0)
|
|
sprintf(cmd, "AT+CH=%d,0\r\n", channel);
|
|
else
|
|
sprintf(cmd, "AT+CH=%d,%d.%d\r\n", channel, (short)frequency, short(frequency * 10) % 10);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setChannel(unsigned char channel, float frequency, _data_rate_t dataRata)
|
|
{
|
|
char cmd[32];
|
|
|
|
if(channel > 16) channel = 16;
|
|
|
|
memset(cmd, 0, 32);
|
|
sprintf(cmd, "AT+CH=%d,%d.%d,%d\r\n", channel, (short)frequency, short(frequency * 10) % 10, dataRata);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setChannel(unsigned char channel, float frequency, _data_rate_t dataRataMin, _data_rate_t dataRataMax)
|
|
{
|
|
char cmd[32];
|
|
|
|
if(channel > 16) channel = 16;
|
|
|
|
memset(cmd, 0, 32);
|
|
sprintf(cmd, "AT+CH=%d,%d.%d,%d,%d\r\n", channel, (short)frequency, short(frequency * 10) % 10, dataRataMin, dataRataMax);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
bool LoRaWanClass::transferPacket(char *buffer, unsigned char timeout)
|
|
{
|
|
unsigned char length = strlen(buffer);
|
|
int count;
|
|
|
|
while(SerialLoRa.available())SerialLoRa.read();
|
|
|
|
sendCommand("AT+MSG=\"");
|
|
for(int i = 0; i < length; i ++)SerialLoRa.write(buffer[i]);
|
|
sendCommand("\"\r\n");
|
|
|
|
while (true) {
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
count = readLine(_buffer, BEFFER_LENGTH_MAX, timeout);
|
|
if (count == 0)
|
|
continue;
|
|
// handle timout!
|
|
#if _DEBUG_SERIAL_
|
|
SerialUSB.print(_buffer);
|
|
#endif
|
|
if (strstr(_buffer, "+MSG: Done"))
|
|
return true;
|
|
}
|
|
|
|
// memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
// readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout);
|
|
//#if _DEBUG_SERIAL_
|
|
// SerialUSB.print(_buffer);
|
|
//#endif
|
|
// if(strstr(_buffer, "+MSG: Done"))return true;
|
|
// return false;
|
|
}
|
|
|
|
bool LoRaWanClass::transferPacket(unsigned char *buffer, unsigned char length, unsigned char timeout)
|
|
{
|
|
char temp[3] = {0};
|
|
|
|
while(SerialLoRa.available())SerialLoRa.read();
|
|
|
|
sendCommand("AT+MSGHEX=\"");
|
|
for(int i = 0; i < length; i ++)
|
|
{
|
|
sprintf(temp,"%02x", buffer[i]);
|
|
SerialLoRa.write(temp);
|
|
}
|
|
sendCommand("\"\r\n");
|
|
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout);
|
|
#if _DEBUG_SERIAL_
|
|
SerialUSB.print(_buffer);
|
|
#endif
|
|
if(strstr(_buffer, "+MSGHEX: Done"))return true;
|
|
return false;
|
|
}
|
|
|
|
bool LoRaWanClass::transferPacketWithConfirmed(char *buffer, unsigned char timeout)
|
|
{
|
|
unsigned char length = strlen(buffer);
|
|
int i;
|
|
bool sentOK;
|
|
|
|
sentOK = false;
|
|
|
|
while(SerialLoRa.available())
|
|
SerialLoRa.read();
|
|
|
|
sendCommand("AT+CMSG=\"");
|
|
for(int i = 0; i < length; i ++)SerialLoRa.write(buffer[i]);
|
|
sendCommand("\"\r\n");
|
|
|
|
#ifdef deadcode
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
i = readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout);
|
|
_buffer[i] = 0;
|
|
#if _DEBUG_SERIAL_
|
|
SerialUSB.print(_buffer);
|
|
#endif
|
|
if(strstr(_buffer, "+CMSG: ACK Received"))
|
|
return true;
|
|
else
|
|
return false;
|
|
#endif
|
|
|
|
while (true) {
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
i = readLine(_buffer, BEFFER_LENGTH_MAX, timeout);
|
|
if (i == 0)
|
|
continue;
|
|
_buffer[i] = 0;
|
|
|
|
// !!! handle timeout
|
|
#if _DEBUG_SERIAL_
|
|
SerialUSB.print(_buffer);
|
|
#endif
|
|
if (strstr(_buffer, "+CMSG: Start"))
|
|
continue;
|
|
if (strstr(_buffer, "+CMSG: Wait ACK"))
|
|
continue;
|
|
if (strstr(_buffer, "+CMSG: TX"))
|
|
continue;
|
|
if (strstr(_buffer, "+CMSG: RXWIN"))
|
|
continue;
|
|
if (strstr(_buffer, "+CMSG: No free channel"))
|
|
break;
|
|
if (strstr(_buffer, "+CMSG: Done"))
|
|
break;
|
|
|
|
if (strstr(_buffer, "+CMSG: ACK Received")) {
|
|
sentOK = true;
|
|
continue;
|
|
}
|
|
SerialUSB.print("Result didn't match anything I expected.\n");
|
|
}
|
|
|
|
return sentOK;
|
|
}
|
|
|
|
bool LoRaWanClass::transferPacketWithConfirmed(unsigned char *buffer, unsigned char length, unsigned char timeout)
|
|
{
|
|
char temp[3] = {0};
|
|
int i;
|
|
unsigned char *ptr;
|
|
|
|
while(SerialLoRa.available())SerialLoRa.read();
|
|
|
|
sendCommand("AT+CMSGHEX=\"");
|
|
for(int i = 0; i < length; i ++)
|
|
{
|
|
sprintf(temp,"%02x", buffer[i]);
|
|
SerialLoRa.write(temp);
|
|
}
|
|
sendCommand("\"\r\n");
|
|
#if _DEBUG_SERIAL_
|
|
ptr = buffer;
|
|
for (i = 0; i < length; i++)
|
|
SerialUSB.print(*(ptr++));
|
|
|
|
SerialUSB.println("");
|
|
#endif
|
|
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
i = readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout);
|
|
_buffer[i] = 0;
|
|
|
|
SerialUSB.print(_buffer);
|
|
|
|
if(strstr(_buffer, "+CMSGHEX: ACK Received"))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
short LoRaWanClass::receivePacket(char *buffer, short length, short *rssi)
|
|
{
|
|
char *ptr;
|
|
short number = 0;
|
|
|
|
ptr = strstr(_buffer, "RSSI ");
|
|
if(ptr)*rssi = atoi(ptr + 5);
|
|
else *rssi = -255;
|
|
|
|
ptr = strstr(_buffer, "RX: \"");
|
|
if(ptr)
|
|
{
|
|
ptr += 5;
|
|
for(short i = 0; ; i ++)
|
|
{
|
|
char temp[3] = {0, 0};
|
|
unsigned char tmp = '?', result = 0;
|
|
|
|
temp[0] = *(ptr + i * 3);
|
|
temp[1] = *(ptr + i * 3 + 1);
|
|
|
|
for(unsigned char j = 0; j < 2; j ++)
|
|
{
|
|
if((temp[j] >= '0') && (temp[j] <= '9'))
|
|
tmp = temp[j] - '0';
|
|
else if((temp[j] >= 'A') && (temp[j] <= 'F'))
|
|
tmp = temp[j] - 'A' + 10;
|
|
else if((temp[j] >= 'a') && (temp[j] <= 'f'))
|
|
tmp = temp[j] - 'a' + 10;
|
|
|
|
result = result * 16 + tmp;
|
|
}
|
|
|
|
if(i < length)buffer[i] = result;
|
|
|
|
if(*(ptr + i * 3 + 3) == '\"' && *(ptr + i * 3 + 4) == '\r' && *(ptr + i * 3 + 5) == '\n')
|
|
{
|
|
number = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ptr = strstr(_buffer, "MACCMD: \"");
|
|
if(ptr)
|
|
{
|
|
buffer[0] = 'M';
|
|
buffer[1] = 'A';
|
|
buffer[2] = 'C';
|
|
buffer[3] = 'C';
|
|
buffer[4] = 'M';
|
|
buffer[5] = 'D';
|
|
buffer[6] = ':';
|
|
|
|
ptr += 9;
|
|
for(short i = 0; ; i ++)
|
|
{
|
|
char temp[3] = {0};
|
|
unsigned char tmp = '?', result = 0;
|
|
|
|
temp[0] = *(ptr + i * 3);
|
|
temp[1] = *(ptr + i * 3 + 1);
|
|
|
|
for(unsigned char j = 0; j < 2; j ++)
|
|
{
|
|
if((temp[j] >= '0') && (temp[j] <= '9'))
|
|
tmp = temp[j] - '0';
|
|
else if((temp[j] >= 'A') && (temp[j] <= 'F'))
|
|
tmp = temp[j] - 'A' + 10;
|
|
else if((temp[j] >= 'a') && (temp[j] <= 'f'))
|
|
tmp = temp[j] - 'a' + 10;
|
|
|
|
result = result * 16 + tmp;
|
|
}
|
|
|
|
if((i + 7) < length)buffer[i + 7] = result;
|
|
|
|
if(*(ptr + i * 3 + 3) == '\"' && *(ptr + i * 3 + 4) == '\r' && *(ptr + i * 3 + 5) == '\n')
|
|
{
|
|
number = i + 1 + 7;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
|
|
return number;
|
|
}
|
|
|
|
bool LoRaWanClass::transferProprietaryPacket(char *buffer, unsigned char timeout)
|
|
{
|
|
unsigned char length = strlen(buffer);
|
|
|
|
while(SerialLoRa.available())SerialLoRa.read();
|
|
|
|
sendCommand("AT+PMSG=\"");
|
|
for(int i = 0; i < length; i ++)SerialLoRa.write(buffer[i]);
|
|
sendCommand("\"\r\n");
|
|
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout);
|
|
#if _DEBUG_SERIAL_
|
|
SerialUSB.print(_buffer);
|
|
#endif
|
|
if(strstr(_buffer, "+PMSG: Done"))return true;
|
|
return false;
|
|
}
|
|
|
|
bool LoRaWanClass::transferProprietaryPacket(unsigned char *buffer, unsigned char length, unsigned char timeout)
|
|
{
|
|
char temp[3] = {0};
|
|
|
|
while(SerialLoRa.available())SerialLoRa.read();
|
|
|
|
sendCommand("AT+PMSGHEX=\"");
|
|
for(int i = 0; i < length; i ++)
|
|
{
|
|
sprintf(temp,"%02x", buffer[i]);
|
|
SerialLoRa.write(temp);
|
|
}
|
|
sendCommand("\"\r\n");
|
|
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout);
|
|
#if _DEBUG_SERIAL_
|
|
SerialUSB.print(_buffer);
|
|
#endif
|
|
if(strstr(_buffer, "+PMSGHEX: Done"))return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
void LoRaWanClass::setUnconfirmedMessageRepeatTime(unsigned char time)
|
|
{
|
|
char cmd[32];
|
|
|
|
if(time > 15) time = 15;
|
|
else if(time == 0) time = 1;
|
|
|
|
memset(cmd, 0, 32);
|
|
sprintf(cmd, "AT+REPT=%d\r\n", time);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setConfirmedMessageRetryTime(unsigned char time)
|
|
{
|
|
char cmd[32];
|
|
|
|
if(time > 15) time = 15;
|
|
else if(time == 0) time = 1;
|
|
|
|
memset(cmd, 0, 32);
|
|
sprintf(cmd, "AT+RETRY=%d\r\n", time);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::getReceiveWindowFirst(void)
|
|
{
|
|
sendCommand("AT+RXWIN1\r\n");
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setReceiveWindowFirst(bool command)
|
|
{
|
|
if(command)
|
|
sendCommand("AT+RXWIN1=ON\r\n");
|
|
else
|
|
sendCommand("AT+RXWIN1=OFF\r\n");
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
void LoRaWanClass::setReceiveWindowFirst(unsigned char channel, float frequency)
|
|
{
|
|
char cmd[32];
|
|
|
|
// if(channel > 16) channel = 16;
|
|
|
|
memset(cmd, 0, 32);
|
|
if (frequency == 0)
|
|
sprintf(cmd, "AT+RXWIN1=%d,0\r\n", channel);
|
|
else
|
|
sprintf(cmd, "AT+RXWIN1=%d,%d.%d\r\n", channel, (short)frequency, short(frequency * 10) % 10);
|
|
sendCommand(cmd);
|
|
SerialUSB.print(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME * 4); // this can have a lot of data to dump
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setReceiveWindowSecond(float frequency, _data_rate_t dataRate)
|
|
{
|
|
char cmd[32];
|
|
|
|
memset(cmd, 0, 32);
|
|
sprintf(cmd, "AT+RXWIN2=%d.%d,%d\r\n", (short)frequency, short(frequency * 10) % 10, dataRate);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setReceiveWindowSecond(float frequency, _spreading_factor_t spreadingFactor, _band_width_t bandwidth)
|
|
{
|
|
char cmd[32];
|
|
|
|
memset(cmd, 0, 32);
|
|
sprintf(cmd, "AT+RXWIN2=%d.%d,%d,%d\r\n", (short)frequency, short(frequency * 10) % 10, spreadingFactor, bandwidth);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setReceiveWindowDelay(_window_delay_t command, unsigned short _delay)
|
|
{
|
|
char cmd[32];
|
|
|
|
memset(cmd, 0, 32);
|
|
if(command == RECEIVE_DELAY1) sprintf(cmd, "AT+DELAY=RX1,%d\r\n", _delay);
|
|
else if(command == RECEIVE_DELAY2) sprintf(cmd, "AT+DELAY=RX2,%d\r\n", _delay);
|
|
else if(command == JOIN_ACCEPT_DELAY1) sprintf(cmd, "AT+DELAY=JRX1,%d\r\n", _delay);
|
|
else if(command == JOIN_ACCEPT_DELAY2) sprintf(cmd, "AT+DELAY=JRX2,%d\r\n", _delay);
|
|
sendCommand(cmd);
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::setClassType(_class_type_t type)
|
|
{
|
|
if(type == CLASS_A)sendCommand("AT+CLASS=A\r\n");
|
|
else if(type == CLASS_C)sendCommand("AT+CLASS=C\r\n");
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
|
|
//
|
|
// set the JOIN mode to either LWOTAA or LWABP
|
|
// does a half-hearted attempt to check the results
|
|
//
|
|
bool LoRaWanClass::setDeviceMode(_device_mode_t mode)
|
|
{
|
|
char buffer[kLOCAL_BUFF_MAX];
|
|
int timeout = 1;
|
|
|
|
if(mode == LWABP)
|
|
sendCommand("AT+MODE=LWABP\r\n");
|
|
else if(mode == LWOTAA)
|
|
sendCommand("AT+MODE=LWOTAA\r\n");
|
|
else
|
|
return false;
|
|
|
|
memset(buffer, 0, kLOCAL_BUFF_MAX);
|
|
readBuffer(buffer, kLOCAL_BUFF_MAX - 1, timeout);
|
|
|
|
#if _DEBUG_SERIAL_
|
|
SerialUSB.print(buffer);
|
|
// loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
|
|
return strstr(buffer, "+MODE:"); // if it works, response is of form "+MODE: LWOTTA"
|
|
}
|
|
|
|
|
|
//
|
|
// JOIN with the application
|
|
//
|
|
// setDeviceMode should have been called before this.
|
|
bool LoRaWanClass::setOTAAJoin(_otaa_join_cmd_t command, unsigned char timeout)
|
|
{
|
|
// char *ptr;
|
|
short count;
|
|
bool joined = false;
|
|
|
|
if(command == JOIN)
|
|
sendCommand("AT+JOIN\r\n");
|
|
else if(command == FORCE)
|
|
sendCommand("AT+JOIN=FORCE\r\n");
|
|
else {
|
|
SerialUSB.print("Bad command to setOTAAJoin\n");
|
|
return false;
|
|
}
|
|
|
|
#if _DEBUG_SERIAL_
|
|
// loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
// delay(DEFAULT_TIMEWAIT);
|
|
|
|
while (true) {
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
count = readLine(_buffer, BEFFER_LENGTH_MAX, timeout);
|
|
if (count == 0)
|
|
continue;
|
|
|
|
// !!! handle timeout
|
|
#if _DEBUG_SERIAL_
|
|
SerialUSB.print(_buffer);
|
|
#endif
|
|
if (strstr(_buffer, "+JOIN: Join failed"))
|
|
continue;
|
|
if (strstr(_buffer, "+JOIN: LoRaWAN modem is busy"))
|
|
continue;
|
|
if (strstr(_buffer, "+JOIN: NORMAL"))
|
|
continue;
|
|
if (strstr(_buffer, "+JOIN: FORCE"))
|
|
continue;
|
|
if (strstr(_buffer, "+JOIN: Start"))
|
|
continue;
|
|
if (strstr(_buffer, "+JOIN: Done"))
|
|
break;
|
|
if (strstr(_buffer, "+JOIN: No free channel"))
|
|
break;
|
|
if (strstr(_buffer, "+JOIN: Network joined")) {
|
|
joined = true;
|
|
continue;
|
|
}
|
|
if (strstr(_buffer, "+JOIN: NetID")) {
|
|
joined = true;
|
|
continue;
|
|
}
|
|
|
|
SerialUSB.print("Result didn't match anything I expected.\n");
|
|
}
|
|
|
|
SerialUSB.print("Done with Join\n");
|
|
return joined;
|
|
}
|
|
|
|
void LoRaWanClass::setDeviceLowPower(void)
|
|
{
|
|
sendCommand("AT+LOWPOWER\r\n");
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
//
|
|
// Reset the LoRa module. Does not factory reset
|
|
//
|
|
void LoRaWanClass::setDeviceReset(void)
|
|
{
|
|
sendCommand("AT+RESET\r\n");
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
//
|
|
// Factory reset the module.
|
|
//
|
|
void LoRaWanClass::setDeviceDefault(void)
|
|
{
|
|
sendCommand("AT+FDEFAULT=RISINGHF\r\n");
|
|
#if _DEBUG_SERIAL_
|
|
loraDebugPrint(DEFAULT_DEBUGTIME);
|
|
#endif
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::initP2PMode(unsigned short frequency, _spreading_factor_t spreadingFactor, _band_width_t bandwidth,
|
|
unsigned char txPreamble, unsigned char rxPreamble, short power)
|
|
{
|
|
char cmd[64] = {0,};
|
|
sprintf(cmd, "AT+TEST=RFCFG,%d,%d,%d,%d,%d,%d\r\n", frequency, spreadingFactor, bandwidth, txPreamble, rxPreamble, power);
|
|
|
|
sendCommand("AT+MODE=TEST\r\n");
|
|
delay(DEFAULT_TIMEWAIT);
|
|
sendCommand(cmd);
|
|
delay(DEFAULT_TIMEWAIT);
|
|
sendCommand("AT+TEST=RXLRPKT\r\n");
|
|
delay(DEFAULT_TIMEWAIT);
|
|
}
|
|
|
|
void LoRaWanClass::transferPacketP2PMode(char *buffer)
|
|
{
|
|
unsigned char length = strlen(buffer);
|
|
|
|
sendCommand("AT+TEST=TXLRSTR,\"");
|
|
for(int i = 0; i < length; i ++)SerialLoRa.write(buffer[i]);
|
|
sendCommand("\"\r\n");
|
|
}
|
|
|
|
void LoRaWanClass::transferPacketP2PMode(unsigned char *buffer, unsigned char length)
|
|
{
|
|
char temp[3] = {0};
|
|
|
|
sendCommand("AT+TEST=TXLRPKT,\"");
|
|
for(int i = 0; i < length; i ++)
|
|
{
|
|
sprintf(temp,"%02x", buffer[i]);
|
|
SerialLoRa.write(temp);
|
|
}
|
|
sendCommand("\"\r\n");
|
|
}
|
|
|
|
short LoRaWanClass::receivePacketP2PMode(unsigned char *buffer, short length, short *rssi, unsigned char timeout)
|
|
{
|
|
char *ptr;
|
|
short number;
|
|
|
|
while(SerialLoRa.available())SerialLoRa.read();
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
readBuffer(_buffer, BEFFER_LENGTH_MAX, timeout);
|
|
|
|
ptr = strstr(_buffer, "LEN");
|
|
if(ptr)number = atoi(ptr + 4);
|
|
else number = 0;
|
|
|
|
if(number <= 0)return 0;
|
|
|
|
ptr = strstr(_buffer, "RSSI:");
|
|
if(ptr)*rssi = atoi(ptr + 5);
|
|
else *rssi = -255;
|
|
|
|
ptr = strstr(_buffer, "RX \"");
|
|
if(ptr)
|
|
{
|
|
ptr += 4;
|
|
for(short i = 0; i < number; i ++)
|
|
{
|
|
char temp[3] = {0};
|
|
unsigned char tmp='?', result = 0;
|
|
|
|
temp[0] = *(ptr + i * 2);
|
|
temp[1] = *(ptr + i * 2 + 1);
|
|
|
|
for(unsigned char j = 0; j < 2; j ++)
|
|
{
|
|
if((temp[j] >= '0') && (temp[j] <= '9'))
|
|
tmp = temp[j] - '0';
|
|
else if((temp[j] >= 'A') && (temp[j] <= 'F'))
|
|
tmp = temp[j] - 'A' + 10;
|
|
else if((temp[j] >= 'a') && (temp[j] <= 'f'))
|
|
tmp = temp[j] - 'a' + 10;
|
|
|
|
result = result * 16 + tmp;
|
|
}
|
|
|
|
if(i < length)buffer[i] = result;
|
|
}
|
|
}
|
|
|
|
memset(_buffer, 0, BEFFER_LENGTH_MAX);
|
|
|
|
return number;
|
|
}
|
|
|
|
// short LoRaWanClass::getBatteryVoltage(void)
|
|
// {
|
|
// int battery;
|
|
|
|
// pinMode(CHARGE_STATUS_PIN, OUTPUT);
|
|
// digitalWrite(CHARGE_STATUS_PIN, LOW);
|
|
// delay(DEFAULT_TIMEWAIT);
|
|
// battery = (analogRead(BATTERY_POWER_PIN) * 3300 * 11) >> 10;
|
|
// pinMode(CHARGE_STATUS_PIN, INPUT);
|
|
|
|
// return battery;
|
|
// }
|
|
|
|
// ??? I think this essentially connects the serial port to the LoRa module.
|
|
// @@@ typing a "~" will exit
|
|
//
|
|
void LoRaWanClass::loraDebug(void)
|
|
{
|
|
char c;
|
|
|
|
while (true) {
|
|
if(SerialUSB.available()) {
|
|
c = SerialUSB.read();
|
|
if (c == '~')
|
|
return;
|
|
SerialLoRa.write(c);
|
|
}
|
|
if(SerialLoRa.available())SerialUSB.write(SerialLoRa.read());
|
|
}
|
|
}
|
|
|
|
#if _DEBUG_SERIAL_
|
|
//
|
|
// timeout is the total amount of time allowed for collecting data
|
|
// Would it make more sense if it was the time w/o a character?
|
|
void LoRaWanClass::loraDebugPrint(unsigned int timeout)
|
|
{
|
|
unsigned long timerStart, timerEnd;
|
|
char c;
|
|
|
|
timerStart = millis();
|
|
|
|
while(1)
|
|
{
|
|
while(SerialLoRa.available()) {
|
|
SerialUSB.write(c = SerialLoRa.read());
|
|
if (c == '\n')
|
|
return; // !!! This won't work for commands that return multiple lines.
|
|
timerStart = millis();
|
|
}
|
|
|
|
timerEnd = millis();
|
|
// if(timerEnd - timerStart > 1000 * timeout)break;
|
|
if(timerEnd - timerStart > timeout)
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void LoRaWanClass::debugPrint(const char *str) {
|
|
SerialUSB.print(str);
|
|
}
|
|
|
|
void LoRaWanClass::sendCommand(const char *command)
|
|
{
|
|
SerialLoRa.print(command);
|
|
}
|
|
|
|
void LoRaWanClass::sendCommand(const __FlashStringHelper* command) {
|
|
SerialLoRa.print(command);
|
|
}
|
|
|
|
short LoRaWanClass::readBuffer(char *buffer, short length, unsigned char timeout)
|
|
{
|
|
short i = 0;
|
|
unsigned long timerStart, timerEnd;
|
|
|
|
timerStart = millis();
|
|
|
|
while(1)
|
|
{
|
|
if(i < length)
|
|
{
|
|
while(SerialLoRa.available())
|
|
{
|
|
char c = SerialLoRa.read();
|
|
buffer[i ++] = c;
|
|
}
|
|
}
|
|
|
|
timerEnd = millis();
|
|
if(timerEnd - timerStart > 1000 * timeout)break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
short LoRaWanClass::readLine(char *buffer, short length, unsigned char timeout)
|
|
{
|
|
short i = 0;
|
|
unsigned long timerStart, timerEnd;
|
|
char c='\n';
|
|
|
|
timerStart = millis();
|
|
|
|
while(1) {
|
|
if(i < length-1) {
|
|
while(SerialLoRa.available()) {
|
|
c = SerialLoRa.read();
|
|
buffer[i ++] = c;
|
|
if (c == '\n')
|
|
break;
|
|
timerStart = millis();
|
|
}
|
|
}
|
|
if (c == '\n')
|
|
break; // @@@ barf
|
|
timerEnd = millis();
|
|
if(timerEnd - timerStart > 1000 * timeout)break;
|
|
}
|
|
|
|
buffer[i] = 0; // terminate the string
|
|
return i;
|
|
}
|
|
short LoRaWanClass::waitForResponse(char* response, unsigned char timeout)
|
|
{
|
|
short len = strlen(response);
|
|
short sum = 0;
|
|
unsigned long timerStart,timerEnd;
|
|
|
|
timerStart = millis();
|
|
|
|
while(1)
|
|
{
|
|
if(SerialLoRa.available())
|
|
{
|
|
char c = SerialLoRa.read();
|
|
|
|
sum = (c == response[sum]) ? sum + 1 : 0;
|
|
if(sum == len)break;
|
|
}
|
|
|
|
timerEnd = millis();
|
|
if(timerEnd - timerStart > 1000 * timeout)return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
short LoRaWanClass::sendCommandAndWaitForResponse(char* command, char *response, unsigned char timeout)
|
|
{
|
|
sendCommand(command);
|
|
|
|
return waitForResponse(response, timeout);
|
|
}
|
|
|
|
|
|
LoRaWanClass lora;
|