Add command LoRaConfig

This commit is contained in:
Theo Arends 2024-02-26 17:31:24 +01:00
parent 34f7ecf31a
commit d61c96a485
5 changed files with 193 additions and 23 deletions

View File

@ -36,6 +36,7 @@
#define D_JSON_AP "AP" // Access Point
#define D_JSON_APMAC_ADDRESS "APMac"
#define D_JSON_APPENDED "Appended"
#define D_JSON_BANDWIDTH "Bandwidth"
#define D_JSON_BAUDRATE "Baudrate"
#define D_JSON_BLINK "Blink"
#define D_JSON_BLOCKED_LOOP "Blocked Loop"
@ -47,6 +48,7 @@
#define D_JSON_BUILDDATETIME "BuildDateTime"
#define D_JSON_CHANNEL "Channel"
#define D_JSON_CO2 "CarbonDioxide"
#define D_JSON_CODINGRATE4 "CodingRate4"
#define D_JSON_COMMAND "Command"
#define D_JSON_CONFIDENCE "Confidence"
#define D_JSON_CONFIG_HOLDER "CfgHolder"
@ -55,7 +57,9 @@
#define D_JSON_COREVERSION "Core"
#define D_JSON_COUNT "Count"
#define D_JSON_COUNTER "Counter"
#define D_JSON_CRC_BYTES "CrcBytes"
#define D_JSON_CURRENT "Current" // As in Voltage and Current
#define D_JSON_CURRENT_LIMIT "CurrentLimit"
#define D_JSON_CURRENT_NEUTRAL "CurrentNeutral"
#define D_JSON_DARKNESS "Darkness"
#define D_JSON_DATA "Data"
@ -104,6 +108,7 @@
#define D_JSON_HUMIDITY "Humidity"
#define D_JSON_ID "Id"
#define D_JSON_ILLUMINANCE "Illuminance"
#define D_JSON_IMPLICIT_HEADER "ImplicitHeader"
#define D_JSON_IMPORT_ACTIVE "ImportActive"
#define D_JSON_IMPORT_POWER "ImportPower"
#define D_JSON_IMPORT_REACTIVE "ImportReactive"
@ -130,6 +135,7 @@
#define D_JSON_NONE "None"
#define D_JSON_OR "or"
#define D_JSON_ORP "ORP"
#define D_JSON_OUTPUT_POWER "OutputPower"
#define D_JSON_O2 "Oxygen"
#define D_JSON_PERIOD "Period"
#define D_JSON_PH "pH"
@ -141,6 +147,7 @@
#define D_JSON_APPARENT_POWERUSAGE "ApparentPower"
#define D_JSON_REACTIVE_POWERUSAGE "ReactivePower"
#define D_JSON_RANGE "Range"
#define D_JSON_PREAMBLE_LENGTH "PreambleLength"
#define D_JSON_PRESSURE "Pressure"
#define D_JSON_PRESSUREATSEALEVEL "SeaPressure"
#define D_JSON_PRESSURE_UNIT "PressureUnit"
@ -173,6 +180,7 @@
#define D_JSON_SIZE "Size"
#define D_JSON_SPEED "Speed"
#define D_JSON_SPEED_UNIT "SpeedUnit"
#define D_JSON_SPREADING_FACTOR "SpreadingFactor"
#define D_JSON_SSID "SSId"
#define D_JSON_STAGE "Stage"
#define D_JSON_STARTDST "StartDST" // Start Daylight Savings Time
@ -185,6 +193,7 @@
#define D_JSON_SUNSET "Sunset"
#define D_JSON_SWITCH "Switch"
#define D_JSON_SYNC "Sync"
#define D_JSON_SYNCWORD "SyncWord"
#define D_JSON_TEMPERATURE "Temperature"
#define D_JSON_TEMPERATURE_UNIT "TempUnit"
#define D_JSON_TIME "Time"

View File

@ -12,12 +12,24 @@
#define LORA_MAX_PACKET_LENGTH 252 // Max packet length allowed (defined by RadioLib driver)
struct {
bool (* Config)(void);
bool (* Available)(void);
int (* Receive)(char*);
bool (* Send)(char*, uint32_t);
float rssi;
float snr;
int packet_size;
float frequency; // 868.0 MHz
float bandwidth; // 125.0 kHz
int spreading_factor; // 9
int coding_rate; // 7
int sync_word; // 0x12
int output_power; // 10 dBm
long preamble_length; // 8 symbols
float current_limit; // 60.0 mA (Overcurrent Protection (OCP))
int implicit_header; // 0
bool crc_bytes; // 2 bytes
uint8_t gain;
volatile bool receivedFlag; // flag to indicate that a packet was received
volatile bool enableInterrupt; // disable interrupt when it's not needed
bool sendFlag;

View File

@ -74,14 +74,76 @@ int LoraReceiveSx126x(char* data) {
bool LoraSendSx126x(char* data, uint32_t len) {
Lora.sendFlag = true;
int state = LoRaRadio.startTransmit(data, len);
return (RADIOLIB_ERR_NONE == state);
// int state = LoRaRadio.startTransmit(data, len);
// return (RADIOLIB_ERR_NONE == state);
// https://learn.circuit.rocks/battery-powered-lora-sensor-node
uint32_t retry_CAD = 0;
uint32_t retry_send = 0;
bool send_success = false;
while (!send_success) {
// time_t lora_time = millis();
// Check 200ms for an opportunity to send
while (LoRaRadio.scanChannel() != RADIOLIB_CHANNEL_FREE) {
retry_CAD++;
if (retry_CAD == 20) {
// LoRa channel is busy too long, give up
// AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Channel is too busy, give up"));
retry_send++;
break;
}
}
// AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: CAD finished after %ldms tried %d times"), (millis() - loraTime), retryCAD);
if (retry_CAD < 20) {
// Channel is free, start sending
// lora_time = millis();
int status = LoRaRadio.transmit(data, len);
// AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Transmit finished after %ldms with status %d"), (millis() - loraTime), status);
if (status == RADIOLIB_ERR_NONE) {
send_success = true;
}
else {
retry_send++;
}
}
if (retry_send == 3) {
// AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Failed 3 times to send data, giving up"));
send_success = true;
}
}
return send_success;
}
bool LoraConfigSx126x(void) {
LoRaRadio.setFrequency(Lora.frequency);
LoRaRadio.setBandwidth(Lora.bandwidth);
LoRaRadio.setSpreadingFactor(Lora.spreading_factor);
LoRaRadio.setCodingRate(Lora.coding_rate);
LoRaRadio.setSyncWord(Lora.sync_word);
LoRaRadio.setOutputPower(Lora.output_power);
LoRaRadio.setPreambleLength(Lora.preamble_length);
LoRaRadio.setCurrentLimit(Lora.current_limit);
LoRaRadio.setCRC(Lora.crc_bytes);
if (Lora.implicit_header) {
LoRaRadio.implicitHeader(Lora.implicit_header);
} else {
LoRaRadio.explicitHeader();
}
return true;
}
bool LoraInitSx126x(void) {
// LoRa = new Module(Pin(GPIO_LORA_CS), Pin(GPIO_LORA_DI1), Pin(GPIO_LORA_RST), Pin(GPIO_LORA_BUSY));
LoRaRadio = new Module(Pin(GPIO_LORA_CS), 33, Pin(GPIO_LORA_RST), 34);
if (RADIOLIB_ERR_NONE == LoRaRadio.begin(868.0)) {
if (RADIOLIB_ERR_NONE == LoRaRadio.begin(Lora.frequency)) {
LoraConfigSx126x();
LoRaRadio.setDio1Action(LoraOnReceiveSx126x);
if (RADIOLIB_ERR_NONE == LoRaRadio.startReceive()) {
return true;

View File

@ -31,22 +31,33 @@
/*********************************************************************************************/
void LoraOnCadDoneSx127x(boolean signalDetected) {
if (signalDetected) { // detect preamble
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Signal detected"));
LoRa.receive(); // put the radio into continuous receive mode
} else {
LoRa.channelActivityDetection(); // try next activity dectection
}
}
// this function is called when a complete packet is received by the module
void LoraOnReceiveSx127x(int packet_size) {
if (0 == packet_size) { return; } // if there's no packet, return
if (!Lora.enableInterrupt) { return; } // check if the interrupt is enabled
Lora.packet_size = packet_size; // we got a packet, set the flag
if (0 == packet_size) { return; } // if there's no packet, return
if (!Lora.enableInterrupt) { return; } // check if the interrupt is enabled
Lora.packet_size = packet_size; // we got a packet, set the flag
}
bool LoraAvailableSx127x(void) {
return (Lora.packet_size > 0); // check if the flag is set
return (Lora.packet_size > 0); // check if the flag is set
}
int LoraReceiveSx127x(char* data) {
Lora.enableInterrupt = false; // disable the interrupt service routine while processing the data
Lora.enableInterrupt = false; // disable the interrupt service routine while processing the data
int packet_size = 0;
while (LoRa.available()) { // read packet up to LORA_MAX_PACKET_LENGTH
while (LoRa.available()) { // read packet up to LORA_MAX_PACKET_LENGTH
char sdata = LoRa.read();
if (packet_size < LORA_MAX_PACKET_LENGTH -1) {
data[packet_size++] = sdata;
@ -55,29 +66,58 @@ int LoraReceiveSx127x(char* data) {
packet_size = (Lora.sendFlag) ? 0 : +1;
Lora.sendFlag = false;
Lora.packet_size = 0; // reset flag
Lora.enableInterrupt = true; // we're ready to receive more packets, enable interrupt service routine
Lora.packet_size = 0; // reset flag
Lora.enableInterrupt = true; // we're ready to receive more packets, enable interrupt service routine
Lora.rssi = LoRa.packetRssi();
Lora.snr = LoRa.packetSnr();
LoRa.channelActivityDetection(); // put the radio into CAD mode
return packet_size;
}
bool LoraSendSx127x(char* data, uint32_t len) {
Lora.sendFlag = true;
LoRa.beginPacket(); // start packet
LoRa.write((uint8_t*)data, len); // send message
LoRa.endPacket(); // finish packet and send it
LoRa.receive(); // go back into receive mode
LoRa.beginPacket(Lora.implicit_header); // start packet
LoRa.write((uint8_t*)data, len); // send message
LoRa.endPacket(); // finish packet and send it
LoRa.receive(); // go back into receive mode
return true;
}
bool LoraConfigSx127x(void) {
LoRa.setFrequency(Lora.frequency * 1000 * 1000);
LoRa.setSignalBandwidth(Lora.bandwidth * 1000);
LoRa.setSpreadingFactor(Lora.spreading_factor);
LoRa.setCodingRate4(Lora.coding_rate);
LoRa.setSyncWord(Lora.sync_word);
LoRa.setTxPower(Lora.output_power);
LoRa.setPreambleLength(Lora.preamble_length);
LoRa.setOCP(Lora.current_limit);
if (Lora.crc_bytes) {
LoRa.enableCrc();
} else {
LoRa.disableCrc();
}
/*
if (Lora.implicit_header) {
LoRa.implicitHeaderMode();
} else {
LoRa.explicitHeaderMode();
}
*/
return true;
}
bool LoraInitSx127x(void) {
LoRa.setPins(Pin(GPIO_LORA_CS), Pin(GPIO_LORA_RST), Pin(GPIO_LORA_DI0));
if (LoRa.begin(868E6)) {
// LoRa.setSyncWord(0x12);
if (LoRa.begin(Lora.frequency * 1000 * 1000)) {
LoraConfigSx127x();
LoRa.onCadDone(LoraOnCadDoneSx127x); // register the channel activity dectection callback
LoRa.onReceive(LoraOnReceiveSx127x);
LoRa.receive();
// LoRa.receive();
LoRa.channelActivityDetection();
return true;
}
return false;

View File

@ -59,6 +59,19 @@ void LoraInit(void) {
SPI.begin(Pin(GPIO_SPI_CLK), Pin(GPIO_SPI_MISO), Pin(GPIO_SPI_MOSI), -1);
#endif // ESP32
Lora.frequency = 868.0; // MHz
Lora.bandwidth = 125.0; // kHz
Lora.spreading_factor = 9;
Lora.coding_rate = 7;
Lora.sync_word = 0x12;
Lora.output_power = 10; // dBm
Lora.preamble_length = 8; // symbols
Lora.current_limit = 60.0; // mA (Overcurrent Protection (OCP))
Lora.implicit_header = 0; // explicit
Lora.crc_bytes = 2; // bytes
Lora.enableInterrupt = true;
char hardware[20];
if (false) {
}
@ -66,6 +79,7 @@ void LoraInit(void) {
else if (PinUsed(GPIO_LORA_DI0)) {
// SX1276, RFM95W
if (LoraInitSx127x()) {
Lora.Config = &LoraConfigSx127x;
Lora.Available = &LoraAvailableSx127x;
Lora.Receive = &LoraReceiveSx127x;
Lora.Send = &LoraSendSx127x;
@ -77,6 +91,7 @@ void LoraInit(void) {
#ifdef USE_LORA_SX126X
else if (LoraInitSx126x()) {
// SX1262, LilyGoT3S3
Lora.Config = &LoraConfigSx126x;
Lora.Available = &LoraAvailableSx126x;
Lora.Receive = &LoraReceiveSx126x;
Lora.Send = &LoraSendSx126x;
@ -87,9 +102,6 @@ void LoraInit(void) {
else {
strcpy_P(hardware, PSTR("Not"));
}
if (Lora.present) {
Lora.enableInterrupt = true;
}
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: %s initialized"), hardware);
}
}
@ -99,12 +111,13 @@ void LoraInit(void) {
\*********************************************************************************************/
#define D_CMND_LORASEND "Send"
#define D_CMND_LORACONFIG "Config"
const char kLoraCommands[] PROGMEM = "LoRa|" // Prefix
D_CMND_LORASEND;
D_CMND_LORASEND "|" D_CMND_LORACONFIG;
void (* const LoraCommand[])(void) PROGMEM = {
&CmndLoraSend };
&CmndLoraSend, &CmndLoraConfig };
void CmndLoraSend(void) {
// LoRaSend "Hello Tiger" - Send "Hello Tiger\n"
@ -168,6 +181,40 @@ void CmndLoraSend(void) {
}
}
void CmndLoraConfig(void) {
// LoRaConfig - Show all parameters
// LoRaConfig {"Frequency":868.0,"Bandwidth":125.0} - Enter float parameters
// LoRaConfig {"SyncWord":18} - Enter decimal parameter (=0x12)
if (XdrvMailbox.data_len > 0) {
JsonParser parser(XdrvMailbox.data);
JsonParserObject root = parser.getRootObject();
if (root) {
Lora.frequency = root.getFloat(PSTR(D_JSON_FREQUENCY), Lora.frequency);
Lora.bandwidth = root.getFloat(PSTR(D_JSON_BANDWIDTH), Lora.bandwidth);
Lora.spreading_factor = root.getUInt(PSTR(D_JSON_SPREADING_FACTOR), Lora.spreading_factor);
Lora.coding_rate = root.getUInt(PSTR(D_JSON_CODINGRATE4), Lora.coding_rate);
Lora.sync_word = root.getUInt(PSTR(D_JSON_SYNCWORD), Lora.sync_word);
Lora.output_power = root.getUInt(PSTR(D_JSON_OUTPUT_POWER), Lora.output_power);
Lora.preamble_length = root.getUInt(PSTR(D_JSON_PREAMBLE_LENGTH), Lora.preamble_length);
Lora.current_limit = root.getFloat(PSTR(D_JSON_CURRENT_LIMIT), Lora.current_limit);
Lora.implicit_header = root.getUInt(PSTR(D_JSON_IMPLICIT_HEADER), Lora.implicit_header);
Lora.crc_bytes = root.getUInt(PSTR(D_JSON_CRC_BYTES), Lora.crc_bytes);
Lora.Config();
}
}
ResponseCmnd(); // {"LoRaConfig":
ResponseAppend_P(PSTR("{\"" D_JSON_FREQUENCY "\":%1_f"), &Lora.frequency); // xxx.x MHz
ResponseAppend_P(PSTR(",\"" D_JSON_BANDWIDTH "\":%1_f"), &Lora.bandwidth); // xxx.x kHz
ResponseAppend_P(PSTR(",\"" D_JSON_SPREADING_FACTOR "\":%d"), Lora.spreading_factor);
ResponseAppend_P(PSTR(",\"" D_JSON_CODINGRATE4 "\":%d"), Lora.coding_rate);
ResponseAppend_P(PSTR(",\"" D_JSON_SYNCWORD "\":%d"), Lora.sync_word);
ResponseAppend_P(PSTR(",\"" D_JSON_OUTPUT_POWER "\":%d"), Lora.output_power); // dBm
ResponseAppend_P(PSTR(",\"" D_JSON_PREAMBLE_LENGTH "\":%d"), Lora.preamble_length); // symbols
ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT_LIMIT "\":%1_f"), &Lora.current_limit); // xx.x mA (Overcurrent Protection - OCP)
ResponseAppend_P(PSTR(",\"" D_JSON_IMPLICIT_HEADER "\":%d"), Lora.implicit_header); // 0 = explicit
ResponseAppend_P(PSTR(",\"" D_JSON_CRC_BYTES "\":%d}}"), Lora.crc_bytes); // bytes
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/