Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added WiPhone LoRa support #1458

Merged
merged 3 commits into from
Mar 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -316,6 +316,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