Skip to content

Commit

Permalink
[LORA] Added WiPhone LoRa support (#1458)
Browse files Browse the repository at this point in the history
* Added WiPhone LoRa support

* Centralised raw-hex functions
  • Loading branch information
xopr authored Mar 12, 2023
1 parent c754704 commit 504463c
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 36 deletions.
150 changes: 142 additions & 8 deletions main/ZgatewayLORA.ino
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,96 @@
# include <SPI.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() {
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_SS);
LoRa.setPins(LORA_SS, LORA_RST, LORA_DI0);
Expand Down Expand Up @@ -69,16 +159,38 @@ void LORAtoMQTT() {
taskMessage = taskMessage + xPortGetCoreID();
//trc(taskMessage);
# endif
String packet;
packet = "";
// Create packet and reserve null terminator space
byte packet[packetSize + 1];
boolean binary = false;
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["snr"] = (float)LoRa.packetSnr();
LORAdata["pferror"] = (float)LoRa.packetFrequencyError();
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);
if (repeatLORAwMQTT) {
Log.trace(F("Pub LORA for rpt" CR));
Expand All @@ -92,26 +204,48 @@ void MQTTtoLORA(char* topicOri, JsonObject& LORAdata) { // json object decoding
if (cmpToMainTopic(topicOri, subjectMQTTtoLORA)) {
Log.trace(F("MQTTtoLORA json" CR));
const char* message = LORAdata["message"];
const char* hex = LORAdata["hex"];
int txPower = LORAdata["txpower"] | LORA_TX_POWER;
int spreadingFactor = LORAdata["spreadingfactor"] | LORA_SPREADING_FACTOR;
long int frequency = LORAdata["frequency "] | LORA_BAND;
long int signalBandwidth = LORAdata["signalbandwidth"] | LORA_SIGNAL_BANDWIDTH;
int codingRateDenominator = LORAdata["codingrate"] | LORA_CODING_RATE;
int preambleLength = LORAdata["preamblelength"] | LORA_PREAMBLE_LENGTH;
byte syncWord = LORAdata["syncword"] | LORA_SYNC_WORD;
bool Crc = LORAdata["enablecrc"] | DEFAULT_CRC;
if (message) {
bool crc = LORAdata["enablecrc"] | DEFAULT_CRC;
bool invertIQ = LORAdata["invertiq"] | INVERT_IQ;
if (message || hex) {
LoRa.setTxPower(txPower);
LoRa.setFrequency(frequency);
LoRa.setSpreadingFactor(spreadingFactor);
LoRa.setSignalBandwidth(signalBandwidth);
LoRa.setCodingRate4(codingRateDenominator);
LoRa.setPreambleLength(preambleLength);
LoRa.setSyncWord(syncWord);
if (Crc)
if (crc)
LoRa.enableCrc();
else
LoRa.disableCrc();

if (invertIQ)
LoRa.enableInvertIQ();
else
LoRa.disableInvertIQ();

LoRa.beginPacket();
LoRa.print(message);
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.endPacket();
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
Expand Down
32 changes: 4 additions & 28 deletions main/ZgatewaySRFB.ino
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void _rfbSend(byte* message) {

void _rfbSend(byte* message, int times) {
char buffer[RF_MESSAGE_SIZE];
_rfbToChar(message, buffer);
_rawToHex(message, buffer, RF_MESSAGE_SIZE);
Log.notice(F("[RFBRIDGE] Sending MESSAGE" CR));

for (int i = 0; i < times; i++) {
Expand Down Expand Up @@ -99,7 +99,7 @@ void _rfbDecode() {
char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};

if (action == RF_CODE_RFIN) {
_rfbToChar(&_uartbuf[1], buffer);
_rawToHex(&_uartbuf[1], buffer, RF_MESSAGE_SIZE);

Log.trace(F("Creating SRFB buffer" CR));
StaticJsonDocument<JSON_MSG_BUFFER> jsonBuffer;
Expand Down Expand Up @@ -142,30 +142,6 @@ void _rfbAck() {
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
void MQTTtoSRFB(char* topicOri, char* datacallback) {
// RF DATA ANALYSIS
Expand Down Expand Up @@ -255,7 +231,7 @@ void MQTTtoSRFB(char* topicOri, char* datacallback) {
valueRPT = 1;

byte message_b[RF_MESSAGE_SIZE];
_rfbToArray(datacallback, message_b);
_hexToRaw(datacallback, message_b, RF_MESSAGE_SIZE);
_rfbSend(message_b, valueRPT);
// 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
Expand All @@ -272,7 +248,7 @@ void MQTTtoSRFB(char* topicOri, JsonObject& SRFBdata) {
if (raw) { // send raw in priority when defined in the json
Log.trace(F("MQTTtoSRFB raw ok" CR));
byte message_b[RF_MESSAGE_SIZE];
_rfbToArray(raw, message_b);
_hexToRaw(raw, message_b, RF_MESSAGE_SIZE);
_rfbSend(message_b, valueRPT);
} else {
unsigned long data = SRFBdata["value"];
Expand Down
1 change: 1 addition & 0 deletions main/config_LORA.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ extern void MQTTtoLORA(char* topicOri, JsonObject& RFdata);
#define LORA_PREAMBLE_LENGTH 8
#define LORA_SYNC_WORD 0x12
#define DEFAULT_CRC true
#define INVERT_IQ false

#define repeatLORAwMQTT false // do we repeat a received signal by using MQTT with LORA gateway

Expand Down
24 changes: 24 additions & 0 deletions main/main.ino
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,30 @@ long value_from_hex_data(const char* service_data, int offset, int data_length,
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
static char a[16];
sprintf(a, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
Expand Down

0 comments on commit 504463c

Please sign in to comment.