[LORA] Added WiPhone LoRa support (#1458)
* Added WiPhone LoRa support * Centralised raw-hex functions
This commit is contained in:
4 files changed
+170
-35
No files matched your search
+141
-7
@@ -33,6 +33,96 @@
|
|||||||
# include <SPI.h>
|
# include <SPI.h>
|
||||||
# include <Wire.h>
|
# include <Wire.h>
|
||||||
|
|
||||||
|
# define WIPHONE_MESSAGE_MAGIC 0x6c6d
|
||||||
|
# define WIPHONE_MESSAGE_MIN_LEN sizeof(wiphone_message) - WIPHONE_MAX_MESSAGE_LEN
|
||||||
|
# define WIPHONE_MAX_MESSAGE_LEN 230
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
// WiPhone uses RadioHead library which has additional (unused) headers
|
||||||
|
uint8_t rh_to;
|
||||||
|
uint8_t rh_from;
|
||||||
|
uint8_t rh_id;
|
||||||
|
uint8_t rh_flags;
|
||||||
|
uint16_t magic;
|
||||||
|
uint32_t to;
|
||||||
|
uint32_t from;
|
||||||
|
char message[WIPHONE_MAX_MESSAGE_LEN];
|
||||||
|
} wiphone_message;
|
||||||
|
|
||||||
|
enum LORA_ID_NUM {
|
||||||
|
UNKNOWN_DEVICE = -1,
|
||||||
|
WIPHONE,
|
||||||
|
};
|
||||||
|
typedef enum LORA_ID_NUM LORA_ID_NUM;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Try and determine device given the payload
|
||||||
|
*/
|
||||||
|
uint8_t _determineDevice(byte* packet, int packetSize) {
|
||||||
|
// Check WiPhone header
|
||||||
|
if (packetSize >= WIPHONE_MESSAGE_MIN_LEN && ((wiphone_message*)packet)->magic == WIPHONE_MESSAGE_MAGIC)
|
||||||
|
return WIPHONE;
|
||||||
|
|
||||||
|
// No matches
|
||||||
|
return UNKNOWN_DEVICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Try and determine device given the JSON type
|
||||||
|
*/
|
||||||
|
uint8_t _determineDevice(JsonObject& LORAdata) {
|
||||||
|
const char* protocol_name = LORAdata["type"];
|
||||||
|
|
||||||
|
if (strcmp(protocol_name, "WiPhone") == 0)
|
||||||
|
return WIPHONE;
|
||||||
|
|
||||||
|
// No matches
|
||||||
|
return UNKNOWN_DEVICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create JSON information from WiPhone packet
|
||||||
|
*/
|
||||||
|
boolean _WiPhoneToMQTT(byte* packet, JsonObject& LORAdata) {
|
||||||
|
// Decode the LoRa packet and send over MQTT
|
||||||
|
wiphone_message* msg = (wiphone_message*)packet;
|
||||||
|
|
||||||
|
// Set the header information
|
||||||
|
char from[9] = {0};
|
||||||
|
char to[9] = {0};
|
||||||
|
snprintf(from, 9, "%06X", msg->from);
|
||||||
|
snprintf(to, 9, "%06X", msg->to);
|
||||||
|
|
||||||
|
// From and To are the last 3 octets from the WiPhone's ESP32 chip ID
|
||||||
|
// Special case is 0x000000: "broadcast"
|
||||||
|
LORAdata["from"] = from;
|
||||||
|
LORAdata["to"] = to;
|
||||||
|
|
||||||
|
LORAdata["message"] = msg->message;
|
||||||
|
LORAdata["type"] = "WiPhone";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create WiPhone packet from JSON
|
||||||
|
*/
|
||||||
|
boolean _MQTTtoWiPhone(JsonObject& LORAdata) {
|
||||||
|
// Prepare a LoRa packet to send to the WiPhone
|
||||||
|
wiphone_message wiphonemsg;
|
||||||
|
wiphonemsg.rh_to = 0xff;
|
||||||
|
wiphonemsg.rh_from = 0xff;
|
||||||
|
wiphonemsg.rh_id = 0x00;
|
||||||
|
wiphonemsg.rh_flags = 0x00;
|
||||||
|
|
||||||
|
wiphonemsg.magic = WIPHONE_MESSAGE_MAGIC;
|
||||||
|
wiphonemsg.from = strtol(LORAdata["from"], NULL, 16);
|
||||||
|
wiphonemsg.to = strtol(LORAdata["to"], NULL, 16);
|
||||||
|
const char* message = LORAdata["message"];
|
||||||
|
strlcpy(wiphonemsg.message, message, WIPHONE_MAX_MESSAGE_LEN);
|
||||||
|
LoRa.write((uint8_t*)&wiphonemsg, strlen(message) + WIPHONE_MESSAGE_MIN_LEN + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void setupLORA() {
|
void setupLORA() {
|
||||||
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_SS);
|
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_SS);
|
||||||
LoRa.setPins(LORA_SS, LORA_RST, LORA_DI0);
|
LoRa.setPins(LORA_SS, LORA_RST, LORA_DI0);
|
||||||
@@ -69,16 +159,38 @@ void LORAtoMQTT() {
|
|||||||
taskMessage = taskMessage + xPortGetCoreID();
|
taskMessage = taskMessage + xPortGetCoreID();
|
||||||
//trc(taskMessage);
|
//trc(taskMessage);
|
||||||
# endif
|
# endif
|
||||||
String packet;
|
// Create packet and reserve null terminator space
|
||||||
packet = "";
|
byte packet[packetSize + 1];
|
||||||
|
boolean binary = false;
|
||||||
for (int i = 0; i < packetSize; i++) {
|
for (int i = 0; i < packetSize; i++) {
|
||||||
packet += (char)LoRa.read();
|
packet[i] = (char)LoRa.read();
|
||||||
|
|
||||||
|
if (packet[i] < 32 || packet[i] > 127)
|
||||||
|
binary = true;
|
||||||
}
|
}
|
||||||
|
// Terminate with a null character in case we have a string
|
||||||
|
packet[packetSize] = 0;
|
||||||
|
|
||||||
LORAdata["rssi"] = (int)LoRa.packetRssi();
|
LORAdata["rssi"] = (int)LoRa.packetRssi();
|
||||||
LORAdata["snr"] = (float)LoRa.packetSnr();
|
LORAdata["snr"] = (float)LoRa.packetSnr();
|
||||||
LORAdata["pferror"] = (float)LoRa.packetFrequencyError();
|
LORAdata["pferror"] = (float)LoRa.packetFrequencyError();
|
||||||
LORAdata["packetSize"] = (int)packetSize;
|
LORAdata["packetSize"] = (int)packetSize;
|
||||||
LORAdata["message"] = (char*)packet.c_str();
|
|
||||||
|
uint8_t deviceId = _determineDevice(packet, packetSize);
|
||||||
|
if (deviceId == WIPHONE) {
|
||||||
|
_WiPhoneToMQTT(packet, LORAdata);
|
||||||
|
} else if (binary) {
|
||||||
|
// We have non-ascii data: create hex string of the data
|
||||||
|
char hex[packetSize * 2 + 1];
|
||||||
|
_rawToHex(packet, hex, packetSize);
|
||||||
|
// Terminate with a null character
|
||||||
|
hex[packetSize * 2] = 0;
|
||||||
|
|
||||||
|
LORAdata["hex"] = hex;
|
||||||
|
} else {
|
||||||
|
// ascii payload
|
||||||
|
LORAdata["message"] = packet;
|
||||||
|
}
|
||||||
pub(subjectLORAtoMQTT, LORAdata);
|
pub(subjectLORAtoMQTT, LORAdata);
|
||||||
if (repeatLORAwMQTT) {
|
if (repeatLORAwMQTT) {
|
||||||
Log.trace(F("Pub LORA for rpt" CR));
|
Log.trace(F("Pub LORA for rpt" CR));
|
||||||
@@ -92,6 +204,7 @@ void MQTTtoLORA(char* topicOri, JsonObject& LORAdata) { // json object decoding
|
|||||||
if (cmpToMainTopic(topicOri, subjectMQTTtoLORA)) {
|
if (cmpToMainTopic(topicOri, subjectMQTTtoLORA)) {
|
||||||
Log.trace(F("MQTTtoLORA json" CR));
|
Log.trace(F("MQTTtoLORA json" CR));
|
||||||
const char* message = LORAdata["message"];
|
const char* message = LORAdata["message"];
|
||||||
|
const char* hex = LORAdata["hex"];
|
||||||
int txPower = LORAdata["txpower"] | LORA_TX_POWER;
|
int txPower = LORAdata["txpower"] | LORA_TX_POWER;
|
||||||
int spreadingFactor = LORAdata["spreadingfactor"] | LORA_SPREADING_FACTOR;
|
int spreadingFactor = LORAdata["spreadingfactor"] | LORA_SPREADING_FACTOR;
|
||||||
long int frequency = LORAdata["frequency "] | LORA_BAND;
|
long int frequency = LORAdata["frequency "] | LORA_BAND;
|
||||||
@@ -99,8 +212,9 @@ void MQTTtoLORA(char* topicOri, JsonObject& LORAdata) { // json object decoding
|
|||||||
int codingRateDenominator = LORAdata["codingrate"] | LORA_CODING_RATE;
|
int codingRateDenominator = LORAdata["codingrate"] | LORA_CODING_RATE;
|
||||||
int preambleLength = LORAdata["preamblelength"] | LORA_PREAMBLE_LENGTH;
|
int preambleLength = LORAdata["preamblelength"] | LORA_PREAMBLE_LENGTH;
|
||||||
byte syncWord = LORAdata["syncword"] | LORA_SYNC_WORD;
|
byte syncWord = LORAdata["syncword"] | LORA_SYNC_WORD;
|
||||||
bool Crc = LORAdata["enablecrc"] | DEFAULT_CRC;
|
bool crc = LORAdata["enablecrc"] | DEFAULT_CRC;
|
||||||
if (message) {
|
bool invertIQ = LORAdata["invertiq"] | INVERT_IQ;
|
||||||
|
if (message || hex) {
|
||||||
LoRa.setTxPower(txPower);
|
LoRa.setTxPower(txPower);
|
||||||
LoRa.setFrequency(frequency);
|
LoRa.setFrequency(frequency);
|
||||||
LoRa.setSpreadingFactor(spreadingFactor);
|
LoRa.setSpreadingFactor(spreadingFactor);
|
||||||
@@ -108,10 +222,30 @@ void MQTTtoLORA(char* topicOri, JsonObject& LORAdata) { // json object decoding
|
|||||||
LoRa.setCodingRate4(codingRateDenominator);
|
LoRa.setCodingRate4(codingRateDenominator);
|
||||||
LoRa.setPreambleLength(preambleLength);
|
LoRa.setPreambleLength(preambleLength);
|
||||||
LoRa.setSyncWord(syncWord);
|
LoRa.setSyncWord(syncWord);
|
||||||
if (Crc)
|
if (crc)
|
||||||
LoRa.enableCrc();
|
LoRa.enableCrc();
|
||||||
|
else
|
||||||
|
LoRa.disableCrc();
|
||||||
|
|
||||||
|
if (invertIQ)
|
||||||
|
LoRa.enableInvertIQ();
|
||||||
|
else
|
||||||
|
LoRa.disableInvertIQ();
|
||||||
|
|
||||||
LoRa.beginPacket();
|
LoRa.beginPacket();
|
||||||
|
uint8_t deviceId = _determineDevice(LORAdata);
|
||||||
|
if (deviceId == WIPHONE) {
|
||||||
|
_MQTTtoWiPhone(LORAdata);
|
||||||
|
} else if (hex) {
|
||||||
|
// We have hex data: create convert to binary
|
||||||
|
byte raw[strlen(hex) / 2];
|
||||||
|
_hexToRaw(hex, raw, sizeof(raw));
|
||||||
|
LoRa.print((char*)raw);
|
||||||
|
} else {
|
||||||
|
// ascii payload
|
||||||
LoRa.print(message);
|
LoRa.print(message);
|
||||||
|
}
|
||||||
|
|
||||||
LoRa.endPacket();
|
LoRa.endPacket();
|
||||||
Log.trace(F("MQTTtoLORA OK" CR));
|
Log.trace(F("MQTTtoLORA OK" CR));
|
||||||
pub(subjectGTWLORAtoMQTT, LORAdata); // we acknowledge the sending by publishing the value to an acknowledgement topic, for the moment even if it is a signal repetition we acknowledge also
|
pub(subjectGTWLORAtoMQTT, LORAdata); // we acknowledge the sending by publishing the value to an acknowledgement topic, for the moment even if it is a signal repetition we acknowledge also
|
||||||
|
|||||||
+4
-28
@@ -54,7 +54,7 @@ void _rfbSend(byte* message) {
|
|||||||
|
|
||||||
void _rfbSend(byte* message, int times) {
|
void _rfbSend(byte* message, int times) {
|
||||||
char buffer[RF_MESSAGE_SIZE];
|
char buffer[RF_MESSAGE_SIZE];
|
||||||
_rfbToChar(message, buffer);
|
_rawToHex(message, buffer, RF_MESSAGE_SIZE);
|
||||||
Log.notice(F("[RFBRIDGE] Sending MESSAGE" CR));
|
Log.notice(F("[RFBRIDGE] Sending MESSAGE" CR));
|
||||||
|
|
||||||
for (int i = 0; i < times; i++) {
|
for (int i = 0; i < times; i++) {
|
||||||
@@ -99,7 +99,7 @@ void _rfbDecode() {
|
|||||||
char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
|
char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
|
||||||
|
|
||||||
if (action == RF_CODE_RFIN) {
|
if (action == RF_CODE_RFIN) {
|
||||||
_rfbToChar(&_uartbuf[1], buffer);
|
_rawToHex(&_uartbuf[1], buffer, RF_MESSAGE_SIZE);
|
||||||
|
|
||||||
Log.trace(F("Creating SRFB buffer" CR));
|
Log.trace(F("Creating SRFB buffer" CR));
|
||||||
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
|
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
|
||||||
@@ -142,30 +142,6 @@ void _rfbAck() {
|
|||||||
Serial.println();
|
Serial.println();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
From an hexa char array ("A220EE...") to a byte array (half the size)
|
|
||||||
*/
|
|
||||||
bool _rfbToArray(const char* in, byte* out) {
|
|
||||||
if (strlen(in) != RF_MESSAGE_SIZE * 2)
|
|
||||||
return false;
|
|
||||||
char tmp[3] = {0};
|
|
||||||
for (unsigned char p = 0; p < RF_MESSAGE_SIZE; p++) {
|
|
||||||
memcpy(tmp, &in[p * 2], 2);
|
|
||||||
out[p] = strtol(tmp, NULL, 16);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
From a byte array to an hexa char array ("A220EE...", double the size)
|
|
||||||
*/
|
|
||||||
bool _rfbToChar(byte* in, char* out) {
|
|
||||||
for (unsigned char p = 0; p < RF_MESSAGE_SIZE; p++) {
|
|
||||||
sprintf_P(&out[p * 2], PSTR("%02X" CR), in[p]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
# if simpleReceiving
|
# if simpleReceiving
|
||||||
void MQTTtoSRFB(char* topicOri, char* datacallback) {
|
void MQTTtoSRFB(char* topicOri, char* datacallback) {
|
||||||
// RF DATA ANALYSIS
|
// RF DATA ANALYSIS
|
||||||
@@ -255,7 +231,7 @@ void MQTTtoSRFB(char* topicOri, char* datacallback) {
|
|||||||
valueRPT = 1;
|
valueRPT = 1;
|
||||||
|
|
||||||
byte message_b[RF_MESSAGE_SIZE];
|
byte message_b[RF_MESSAGE_SIZE];
|
||||||
_rfbToArray(datacallback, message_b);
|
_hexToRaw(datacallback, message_b, RF_MESSAGE_SIZE);
|
||||||
_rfbSend(message_b, valueRPT);
|
_rfbSend(message_b, valueRPT);
|
||||||
// Acknowledgement to the GTWRF topic
|
// Acknowledgement to the GTWRF topic
|
||||||
pub(subjectGTWSRFBtoMQTT, datacallback); // we acknowledge the sending by publishing the value to an acknowledgement topic, for the moment even if it is a signal repetition we acknowledge also
|
pub(subjectGTWSRFBtoMQTT, datacallback); // we acknowledge the sending by publishing the value to an acknowledgement topic, for the moment even if it is a signal repetition we acknowledge also
|
||||||
@@ -272,7 +248,7 @@ void MQTTtoSRFB(char* topicOri, JsonObject& SRFBdata) {
|
|||||||
if (raw) { // send raw in priority when defined in the json
|
if (raw) { // send raw in priority when defined in the json
|
||||||
Log.trace(F("MQTTtoSRFB raw ok" CR));
|
Log.trace(F("MQTTtoSRFB raw ok" CR));
|
||||||
byte message_b[RF_MESSAGE_SIZE];
|
byte message_b[RF_MESSAGE_SIZE];
|
||||||
_rfbToArray(raw, message_b);
|
_hexToRaw(raw, message_b, RF_MESSAGE_SIZE);
|
||||||
_rfbSend(message_b, valueRPT);
|
_rfbSend(message_b, valueRPT);
|
||||||
} else {
|
} else {
|
||||||
unsigned long data = SRFBdata["value"];
|
unsigned long data = SRFBdata["value"];
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ extern void MQTTtoLORA(char* topicOri, JsonObject& RFdata);
|
|||||||
#define LORA_PREAMBLE_LENGTH 8
|
#define LORA_PREAMBLE_LENGTH 8
|
||||||
#define LORA_SYNC_WORD 0x12
|
#define LORA_SYNC_WORD 0x12
|
||||||
#define DEFAULT_CRC true
|
#define DEFAULT_CRC true
|
||||||
|
#define INVERT_IQ false
|
||||||
|
|
||||||
#define repeatLORAwMQTT false // do we repeat a received signal by using MQTT with LORA gateway
|
#define repeatLORAwMQTT false // do we repeat a received signal by using MQTT with LORA gateway
|
||||||
|
|
||||||
|
|||||||
@@ -330,6 +330,30 @@ long value_from_hex_data(const char* service_data, int offset, int data_length,
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
From an hexa char array ("A220EE...") to a byte array (half the size)
|
||||||
|
*/
|
||||||
|
bool _hexToRaw(const char* in, byte* out, int rawSize) {
|
||||||
|
if (strlen(in) != rawSize * 2)
|
||||||
|
return false;
|
||||||
|
char tmp[3] = {0};
|
||||||
|
for (unsigned char p = 0; p < rawSize; p++) {
|
||||||
|
memcpy(tmp, &in[p * 2], 2);
|
||||||
|
out[p] = strtol(tmp, NULL, 16);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
From a byte array to an hexa char array ("A220EE...", double the size)
|
||||||
|
*/
|
||||||
|
bool _rawToHex(byte* in, char* out, int rawSize) {
|
||||||
|
for (unsigned char p = 0; p < rawSize; p++) {
|
||||||
|
sprintf_P(&out[p * 2], PSTR("%02X" CR), in[p]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
char* ip2CharArray(IPAddress ip) { //from Nick Lee https://stackoverflow.com/questions/28119653/arduino-display-ethernet-localip
|
char* ip2CharArray(IPAddress ip) { //from Nick Lee https://stackoverflow.com/questions/28119653/arduino-display-ethernet-localip
|
||||||
static char a[16];
|
static char a[16];
|
||||||
sprintf(a, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
sprintf(a, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
||||||
|
|||||||
Reference in new issue
Block a user