Hello,
I am using SX1276 with ESP32 integrated on Heltech Wireless Lite (V2), working at 915MHz with power of 20dB. The application is very simple - it reads one switch, allows delay of 4 cycles, adds header, packet numerator and process time, and transmits it every 4 seconds (this simple processing takes almost 2 seconds). I see the transmiter functionality through LED that is inverted with every transmission.
The two stations are placed 1 meter apart with parallel antenas.
The receiver checks the alarm status and prints the number of the current and previous packets, the difference between them (minus one, to emphasize the loss). It also prints the packet RSSI and accumulated lost packets.
This trivial configuration is loosing most of the packets. I added many delays, especially in the receiver to make it more forgiving for channel problems (although it is so short distance) - nothing helps.
The following is a typical section of the log:
Message= |01-Al 1979 00019692 9|; >>>>> Alert=0; errors count=0; RSSI=-55; current=19692; previous=19683; diff=8; skipped=95
Message= |01-Al 1979 00019694 9|; >>>>> Alert=0; errors count=0; RSSI=-55; current=19694; previous=19692; diff=1; skipped=96
Nothing was received. Error count=1
Nothing was received. Error count=2
Message= |01-Al 1979 00019747 9|; >>>>> Alert=0; errors count=2; RSSI=-59; current=19747; previous=19694; diff=52; skipped=148
The code:
#include <Arduino.h>
#define tractorVerbose // Enable receiver error messages
//#define tractorVerboseTx // Enable transmitter error messages
// Opeational parameters
#define interLoopsInterval 4000 // milliseconds
#define ef 3
#define switchDelayToAlarm 4 // Count of cycle during which the switch is open till alarm
#define allowedMissingPackets 9 // 9 is max lost transmissions without alarms
#define headerLen 5
#define idStart headerLen + 6
#define idLen 8
#define delayLen 4
#define localStationId 0 // Pointer to commHeader vector; limited by the vector’s size
// GPIO Pin
// ==== ===
#define modeSel 34 // 29 Selects between switch side and alarm system side
#define outPort 33 // 30 Buzzer in the switch side, alarm switch in the alarm side
#define alarmSwitch 32 // 31 Sensor in the switch side, not connected in the alarm side
#define buzzer outPort
#define LED 25
// LoRa Pinout
#define LoRa_SCK 5 // 11
#define LoRa_RST 14 // 26
#define LoRa_CS 18 // 7
#define LoRa_MISO 19 // 12
#define LoRa_IRQ 26 // 25
#define LoRa_MOSI 27 // 32
// LoRa parameters
#define txPowerLevelVal 20
#define frequencyVal 915E6
#define LoRaSyncWordVal 0x43
#define LoRaPreambleLengthVal 8
#define LoRaSpreadingFactorVal 11
#define LoRaSignalBandwidthVal 35000
#define LoRaCodingRate4Val 5
const int errorFactor = interLoopsInterval * ef * allowedMissingPackets;
const String commHeader[] = {"01-Al", "01-Al", "01-Al"}; // Alarm sensing station 1
const String idPadding = "00000000";
RTC_DATA_ATTR uint32_t loopCount = 0;
RTC_DATA_ATTR uint32_t alarmDelay;
RTC_DATA_ATTR uint32_t previousMessageId = 0;
RTC_DATA_ATTR int skippedMessages = 0;
RTC_DATA_ATTR uint32_t errCount = 0;
RTC_DATA_ATTR bool alert = false;
RTC_DATA_ATTR uint32_t txTime;
#include "LoRa.h"
/*********************************************************/
void setup()
{
Serial.begin(9600); // initialize serial
while (!Serial);
// Selective GPIOs setting
pinMode(alarmSwitch, INPUT_PULLUP); // Define as input
pinMode(modeSel, INPUT_PULLUP); // Define as input
pinMode(outPort, OUTPUT); // Define as output
pinMode(LED, OUTPUT); // Define as output
// LoRa setup parameters
LoRa.setPins(LoRa_CS, LoRa_RST, LoRa_IRQ);
if (!LoRa.begin(frequencyVal, txPowerLevelVal >= 14))
{
Serial.println("\nLoRa init failed.");
while (true);
}
LoRa.setTxPower(txPowerLevelVal, RF_PACONFIG_PASELECT_PABOOST);
LoRa.setSyncWord(LoRaSyncWordVal);
LoRa.setPreambleLength(LoRaPreambleLengthVal);
LoRa.enableCrc();
LoRa.setSpreadingFactor(LoRaSpreadingFactorVal);
LoRa.setSignalBandwidth(LoRaSignalBandwidthVal);
LoRa.setCodingRate4(LoRaCodingRate4Val);
LoRa.receive();
bool modeF = !digitalRead(modeSel);
digitalWrite(outPort, modeF);
digitalWrite(LED, !digitalRead(LED));
Serial.println("\nInit success. mode - ");
}
/***************** Alarm Sensing side ***************************/
void readSwitches()
{
uint32_t localTxTime = millis();
#ifdef tractorVerboseTx
Serial.println("Switch sensing");
#endif // tractorVerboseTx
// Serial.println("Good morning - switch sensing");
// Read the switches/sensors, prepare the packet and transmit it.
if(digitalRead(alarmSwitch)) // Open alarm contact - alarm on
{
if(alarmDelay > 0)
{
alarmDelay--;
digitalWrite(buzzer, 1);
}
}
else // close alarm contact - normal
{
digitalWrite(buzzer, 0);
alarmDelay = switchDelayToAlarm;
}
if(loopCount++ == 10000000)
loopCount = 0;
String dl = String();
String TxMessage = commHeader[localStationId] + "|";
String tmp = String(txTime);
TxMessage += idPadding.substring(0, delayLen - tmp.length()) + tmp + "|";
tmp = String(loopCount);
TxMessage += idPadding.substring(0, idLen - tmp.length()) + tmp + "|";
TxMessage += String (alarmDelay);
LoRa.beginPacket();
LoRa.print(TxMessage);
LoRa.endPacket(false); // Delay until TX is done
LoRa.receive(); // Set radio work mode
#ifdef tractorVerboseTx
Serial.println("Transmitted:" + TxMessage + "; sleep of " +
String(interLoopsInterval - txTime) + "mS");
Serial.flush();
#endif // tractorVerboseTx
txTime = millis() - localTxTime;
esp_sleep_enable_timer_wakeup((interLoopsInterval - txTime) * 1000); // in microseconds
esp_deep_sleep_start();
}
/***************** Receiver side ***************************/
void receiveAlarms() // Alarm reporting side
{
// Serial.print("Alarm receiver: ");
long currentMillis = millis();
uint8_t packetSize = 0;
while(((millis() - currentMillis) < errorFactor) && (packetSize == 0))
{
packetSize = LoRa.parsePacket();
delay(100);
}
if (packetSize == 0) // No reception - error condition
{
errCount++;
digitalWrite(outPort, false); // Open the transistor to issue an alarm
#ifdef tractorVerbose
Serial.println("Nothing was received. Error count=" + String(errCount));
#endif // tractorVerbose
return;
}
// A package was received
String RxMessage = "";
while (LoRa.available()) // Aggregate message characters
RxMessage += (char)LoRa.read();
LoRa.receive(); // Set work mode
if((RxMessage.length() >= (idStart)) && // minimal message length
(RxMessage.length() == packetSize) && // All characters were collected
(RxMessage.substring(0, headerLen) == commHeader[localStationId])) // Header complies
{
alert = RxMessage[RxMessage.length() - 1] == '0';
digitalWrite(outPort, !alert); // Switch the alarm as needed
#ifdef tractorVerbose // Only for user information
Serial.print("Message= |" + RxMessage);
Serial.print("|; >>>>> Alert=" + String(alert));
Serial.print("; errors count=" + String(errCount));
Serial.print("; RSSI=" + String(LoRa.packetRssi()));
int currentMessageId = atoi(RxMessage.substring(idStart, idStart + idLen).c_str());
if(previousMessageId != 0)
skippedMessages += currentMessageId - (previousMessageId + 1);
Serial.print("; current=" + String(currentMessageId));
Serial.print("; previous=" + String(previousMessageId));
Serial.print("; diff=" + String(currentMessageId - (previousMessageId + 1)));
Serial.print("; skipped=" + String(skippedMessages));
previousMessageId = currentMessageId;
Serial.println();
#endif // tractorVerbose
packetSize = 0; // Clear for the next loop
}
#ifdef tractorVerbose
else // Wrong message - Do nothing
Serial.println("Short message or is not to me: " + RxMessage);
#endif // tractorVerbose
}
/****************************************************************/
void loop()
{
// readSwitches();
receiveAlarms();
Serial.flush();
}