Version 5.0.6; Changes to documentation, timing and to the state machine

pull/15/merge
platenspeler 2018-02-11 21:39:04 +01:00
rodzic b5155a8817
commit 100ec650d9
17 zmienionych plików z 1380 dodań i 974 usunięć

Wyświetl plik

@ -1,6 +1,6 @@
# Single Channel LoRaWAN Gateway
Version 4.0.7, July 22, 2017
Version 5.0.2, November 18, 2017
Author: M. Westenberg (mw12554@hotmail.com)
Copyright: M. Westenberg (mw12554@hotmail.com)
@ -16,6 +16,27 @@ Maintained by Maarten Westenberg (mw12554@hotmail.com)
# Release Notes
New Features in version 5.0.5 (Feb 2, 2018)
- Change timer functions to now() and secons instead of millis() as the latter one overflows once
every 50 days.
- Add more debug information
- Simplified and enhanced the State Machine function
New features in version 5.0.4 (January 1, 2018)
- Cleanup of the State machine
- Separate file for oLED work, support for 1.3" SH3006 chips based oLED.
- Still not supported: Multi Frequency works, but with loss of #packages,
and some packages are recognizeg at the wrong frequency (but since they are so close that could happen).
- In-line documenattion cleaned up
New features in version 5.0.1 (November 18, 2017)
- Changed the state machine to run in user space only
- No Watchdog Resets anymore
- For each SF, percentage of such packages received of total packages
- OTAA and downlink work (again) although not always
- Nober of packages per hour displayed in webserver
- All Serial communication only when DUSB==1 is defined at compile time
New features in version 4.0.9 (August 11, 2017)
- This release contains updates for memory leaks in several Gateway files

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.1
// Date: 2017-11-15
// Version 5.0.6
// Date: 2018-02-12
//
// Based on work done by Thomas Telkamp for Raspberry PI 1ch gateway and many others.
// Contibutions of Dorijan Morelj and Andreas Spies for OLED support.
@ -19,7 +19,7 @@
//
// ----------------------------------------------------------------------------------------
#define VERSION "V.5.0.2.H; 171118a nOLED, 15/10"
#define VERSION "V.5.0.6.H; 170212a"
// This value of DEBUG determines whether some parts of code get compiled.
// Also this is the initial value of debug parameter.
@ -33,6 +33,11 @@
// If set to 2 it will also print interrupt messages (not recommended)
#define DUSB 1
// Define whether we should do a formatting of SPIFFS when starting the gateway
// This is usually a good idea if the webserver is interrupted halfway a writing
// operation.
#define SPIFF_FORMAT 0
// The spreading factor is the most important parameter to set for a single channel
// gateway. It specifies the speed/datarate in which the gateway and node communicate.
// As the name says, in principle the single channel gateway listens to one channel/frequency
@ -72,7 +77,8 @@
// If your pin definitions are different, update the loraModem.h file to reflect these settings.
// 1: HALLARD
// 2: COMRESULT pin out
// 3: Other, define your own in loraModem.h
// 3: ESP32 pin out
// 4: Other, define your own in loraModem.h
#define _PIN_OUT 1
// Gather statistics on sensor and Wifi status
@ -102,8 +108,9 @@
// Define the name of the accesspoint if the gateway is in accesspoint mode (is
// getting WiFi SSID and password using WiFiManager)
// NOTE: Change the password for your OWN environment to be secure
#define AP_NAME "ESP8266-Gway-Things4U"
#define AP_PASSWD "MyPw01!"
#define AP_PASSWD "ttnAutoPw"
// Defines whether the gateway will also report sensor/status value on MQTT
@ -120,11 +127,13 @@
// Will we use Mutex or not?
// +SPI is input for SPI, SPO is output for SPI
#define MUTEX 1
#define MUTEX_SPI 0
#define MUTEX_SPO 0
// Protect the interrupt module
#define MUTEX_INT 0
#define MUTEX 0
// Define if OLED Display is connected to i2c
// OLED==1; 0.9 Oled Screen based on SSD1306
// OLED==2; 1"3 Oled screens for Wemos, 128x64 SH1106
#define OLED 2 // Make define 1 on line if you have an OLED display connected
// Define whether we want to manage the gateway over UDP (next to management
// thru webinterface).
@ -146,7 +155,7 @@
#define _LOCUDPPORT 1700 // UDP port of gateway! Often 1700 or 1701 is used for upstream comms
// Timing
#define _MSG_INTERVAL 15
#define _MSG_INTERVAL 15 // Reset timer in seconds
#define _PULL_INTERVAL 55 // PULL_DATA messages to server to get downstream in milliseconds
#define _STAT_INTERVAL 120 // Send a 'stat' message to server
#define _NTP_INTERVAL 3600 // How often do we want time NTP synchronization
@ -163,25 +172,25 @@
// Port is UDP port in this program
//
// Default for testing: Switch off
//#define _THINGPORT 1700 // dash.westenberg.org:8057
//#define _THINGSERVER "yourServer.com" // Server URL of the LoRa-udp.js handler
#define _THINGPORT 57084 // dash.westenberg.org:8057
#define _THINGSERVER "westenberg.org" // Server URL of the LoRa-udp.js handler
// Gateway Ident definitions
#define _DESCRIPTION "My ESP Gateway"
#define _EMAIL "whoami@hotmail.com"
#define _DESCRIPTION "ESP Gateway"
#define _EMAIL "mw12554@hotmail.com"
#define _PLATFORM "ESP8266"
#define _LAT 52.00
#define _LON 5.900
#define _ALT 00
#define _LAT 52
#define _LON 5.9
#define _ALT 1
// ntp
#define NTP_TIMESERVER "nl.pool.ntp.org" // Country and region specific
#define NTP_TIMEZONES 1 // How far is our Timezone from UTC (excl daylight saving/summer time)
#define SECS_PER_HOUR 3600
#define SECS_IN_HOUR 3600
#define NTP_INTR 0 // Do NTP processing with interrupts or in loop();
#if GATEWAYNODE==1
#define _DEVADDR { 0x26, 0x00, 0x00 0x00 }
#define _DEVADDR { 0x26, 0x01, 0x00, 0x00 }
#define _APPSKEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
#define _NWKSKEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
#define _SENSOR_INTERVAL 300
@ -194,12 +203,6 @@
// Serial Port speed
#define _BAUDRATE 115200 // Works for debug messages to serial momitor
// if OLED Display is connected to i2c
#define OLED 0 // Make define 1 on line if you have an OLED display connected
#define OLED_SCL 5 // GPIO5 / D1
#define OLED_SDA 4 // GPIO4 / D2
#define OLED_ADDR 0x3C // Default 0x3C for 0.9", for 1.3" it is 0x78
// Wifi definitions
// WPA is an array with SSID and password records. Set WPA size to number of entries in array
// When using the WiFiManager, we will overwrite the first entry with the
@ -218,8 +221,8 @@ struct wpas {
//
wpas wpa[] = {
{ "" , "" }, // Reserved for WiFi Manager
{ "aap", "aapPasswd" },
{ "ape", "apePasswd" }
{ "aap", "noot" },
{ "mies", "teun" }
};
// For asserting and testing the following defines are used.

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.1
// Date: 2017-11-15
// Version 5.0.6
// Date: 2018-02-12
// Author: Maarten Westenberg (mw12554@hotmail.com)
//
// based on work done by Thomas Telkamp for Raspberry PI 1-ch gateway
@ -62,8 +62,10 @@ extern "C" {
#include <mutex.h> // See lib directory
#endif
// Local include files
#include "loraModem.h"
#include "loraFiles.h"
#include "oLED.h"
#if WIFIMANAGER>0
@ -83,10 +85,7 @@ extern "C" {
#include "AES-128_V10.h"
#endif
#if OLED==1
#include "SSD1306.h"
SSD1306 display(OLED_ADDR, OLED_SDA, OLED_SCL);// i2c ADDR & SDA, SCL on wemos
#endif
int debug=1; // Debug level! 0 is no msgs, 1 normal, 2 extensive
@ -102,14 +101,13 @@ byte currentMode = 0x81;
//char b64[256];
bool sx1272 = true; // Actually we use sx1276/RFM95
uint32_t cp_nb_rx_rcv;
uint32_t cp_nb_rx_ok;
uint32_t cp_nb_rx_bad;
uint32_t cp_nb_rx_nocrc;
uint32_t cp_nb_rx_rcv; // Number of messages received by gateway
uint32_t cp_nb_rx_ok; // Number of messages received OK
uint32_t cp_nb_rx_bad; // Number of messages received bad
uint32_t cp_nb_rx_nocrc; // Number of messages without CRC
uint32_t cp_up_pkt_fwd;
uint8_t MAC_array[6];
//char MAC_char[19];
// ----------------------------------------------------------------------------
//
@ -138,7 +136,12 @@ IPAddress ttnServer; // IP Address of thethingsnetwork server
IPAddress thingServer;
WiFiUDP Udp;
uint32_t stattime = 0; // last time we sent a stat message to server
time_t startTime = 0; // The time in seconds since 1970 that the server started
// be aware that UTP time has to succeed for meaningful values.
// We use this variable since millis() is reset every 50 days...
uint32_t statTime = 0; // last time we sent a stat message to server
uint32_t pulltime = 0; // last time we sent a pull_data request to server
uint32_t lastTmst = 0;
#if A_SERVER==1
@ -322,7 +325,7 @@ int sendNtpRequest(IPAddress timeServerIP) {
if (!sendUdp( (IPAddress) timeServerIP, (int) 123, packetBuffer, NTP_PACKET_SIZE)) {
gwayConfig.ntpErr++;
gwayConfig.ntpErrTime = millis();
gwayConfig.ntpErrTime = now();
return(0);
}
return(1);
@ -364,9 +367,9 @@ time_t getNtpTime()
secs = packetBuffer[40] << 24;
secs |= packetBuffer[41] << 16;
secs |= packetBuffer[42] << 8;
secs |= packetBuffer[43];\
secs |= packetBuffer[43];
// UTC is 1 TimeZone correction when no daylight saving time
return(secs - 2208988800UL + NTP_TIMEZONES * SECS_PER_HOUR);
return(secs - 2208988800UL + NTP_TIMEZONES * SECS_IN_HOUR);
}
Udp.flush();
}
@ -378,8 +381,12 @@ time_t getNtpTime()
// If we are here, we could not read the time from internet
// So increase the counter
gwayConfig.ntpErr++;
gwayConfig.ntpErrTime = millis();
if (debug>0) Serial.println(F("getNtpTime:: read failed"));
gwayConfig.ntpErrTime = now();
#if DUSB>=1
if (debug>0) {
Serial.println(F("getNtpTime:: read failed"));
}
#endif
return(0); // return 0 if unable to get the time
}
@ -477,7 +484,7 @@ int WlanWriteWpa( char* ssid, char *pass) {
// It is a matter of returning to the main loop() asap and make sure in next loop
// the reconnect is done first thing.
// Parameters:
// int maxTtry: Number of reties we do:
// int maxTry: Number of reties we do:
// 0: Try forever. Which is normally what we want except for Setup maybe
// 1: Try once and if unsuccessful return(0);
// x: Try x times
@ -539,9 +546,16 @@ int WlanConnect(int maxTry) {
}
#if DUSB>=1
if (i>0) {
Serial.print(F("WLAN reconnected"));
Serial.println();
if (i==0) {
if (debug>=3) {
Serial.print(F("WLAN connected"));
Serial.println();
}
return(1);
}
else {
Serial.print(F("WLAN retry="));
Serial.println(i);
}
#endif
@ -643,7 +657,7 @@ int readUdp(int packetSize)
}
#endif
gwayConfig.ntpErr++;
gwayConfig.ntpErrTime = millis();
gwayConfig.ntpErrTime = now();
return(0);
}
@ -1093,16 +1107,12 @@ void setup() {
delay(100);
Serial.flush();
delay(500);
#if MUTEX_SPI==1
CreateMutux(&inSPI);
#endif
#if MUTEX_SPO==1
CreateMutux(&inSPO);
#endif
#if MUTEX_INT==1
CreateMutux(&inIntr);
#endif
if (SPIFFS.begin()) Serial.println(F("SPIFFS loaded success"));
#if SPIFF_FORMAT>=1
SPIFFS.format(); // Normally disabled. Enable only when SPIFFS corrupt
#endif
Serial.print(F("Assert="));
#if defined CFG_noassert
@ -1111,14 +1121,8 @@ void setup() {
Serial.println(F("Do Asserts"));
#endif
#if OLED==1
// Initialising the UI will init the display too.
display.init();
display.flipScreenVertically();
display.setFont(ArialMT_Plain_24);
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.drawString(0, 24, "STARTING");
display.display();
#if OLED>=1
init_oLED();
#endif
delay(500);
@ -1227,9 +1231,14 @@ void setup() {
#endif
// Set the NTP Time
// As long as the time has not been set we try to set the time.
#if NTP_INTR==1
setupTime(); // Set NTP time host and interval
#else
// If not using the standard libraries, do a manual setting
// of the time. This meyhod works more reliable than the
// interrupt driven method.
//setTime((time_t)getNtpTime());
while (timeStatus() == timeNotSet) {
Serial.println(F("setupTime:: Time not set (yet)"));
@ -1238,12 +1247,14 @@ void setup() {
newTime = (time_t)getNtpTime();
if (newTime != 0) setTime(newTime);
}
// When we are here we succeeded in getting the time
startTime = now(); // Time in seconds
Serial.print("Time: "); printTime();
Serial.println();
writeGwayCfg(CONFIGFILE );
Serial.println(F("Gateway configuration saved"));
#endif
#endif //NTP_INTR
#if A_SERVER==1
// Setup the webserver
@ -1264,6 +1275,7 @@ void setup() {
_state = S_RX;
rxLoraModem();
}
LoraUp.payLoad[0]= 0;
LoraUp.payLength = 0; // Init the length to 0
// init interrupt handlers, which are shared for GPIO15 / D8,
@ -1283,12 +1295,9 @@ void setup() {
writeConfig( CONFIGFILE, &gwayConfig); // Write config
// activate OLED display
#if OLED==1
// Initialising the UI will init the display too.
display.clear();
display.setFont(ArialMT_Plain_24);
display.drawString(0, 24, "READY");
display.display();
#if OLED>=1
acti_oLED();
addr_oLED();
#endif
Serial.println(F("--------------------------------------"));
@ -1313,32 +1322,36 @@ void setup() {
// ----------------------------------------------------------------------------
void loop ()
{
uint32_t nowSeconds;
uint32_t uSeconds; // micro seconds
int packetSize;
nowTime = micros();
nowSeconds = (uint32_t) millis() /1000;
uint32_t nowSeconds = now();
// check for event value, which means that an interrupt has arrived.
// In this case we handle the interrupt ( e.g. message received)
// in userspace in loop().
//
if (_event != 0x00) {
stateMachine(); // start the state machine
_event = 0; // reset value
return; // Restart loop
while (_event != 0x00) { //
stateMachine(); // start the state machine
}
// After a quiet period, make sure we reinit the modem.
// XXX Still have to measure quiet period in stat[0];
// For the moment we use msgTime
if ( (((nowTime - statr[0].tmst) / 1000000) > _MSG_INTERVAL ) &&
(msgTime < statr[0].tmst)) {
// After a quiet period, make sure we reinit the modem and state machine.
// The interval is in seconds (about 10 seconds) as this re-init
// is a heavy operation.
// SO it will kick in if there are not many messages for the gatway.
// Note: Be carefull that it does not happen too often in normal operation.
//
if ( ((nowSeconds - statr[0].tmst) > _MSG_INTERVAL ) &&
(msgTime < statr[0].tmst) )
{
#if DUSB>=1
Serial.print('r');
if (debug>=1) Serial.println("REINIT");
#endif
initLoraModem();
if (_cad) {
initLoraModem(); // XXX 26/12/2017
if (_hop) {
_state = S_SCAN;
hop();
}
else if (_cad) {
_state = S_SCAN;
cadScanner();
}
@ -1348,7 +1361,7 @@ void loop ()
}
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
writeRegister(REG_IRQ_FLAGS, 0xFF); // Reset all interrupt flags
msgTime = nowTime;
msgTime = nowSeconds;
}
#if A_OTA==1
@ -1373,7 +1386,7 @@ void loop ()
// We will not read Udp in this loop cycle then
if (WlanConnect(1) < 0) {
#if DUSB>=1
Serial.print(F("loop: ERROR reconnect WLAN"));
Serial.println(F("loop: ERROR reconnect WLAN"));
#endif
yield();
return; // Exit loop if no WLAN connected
@ -1382,11 +1395,11 @@ void loop ()
// So if we are connected
// Receive UDP PUSH_ACK messages from server. (*2, par. 3.3)
// This is important since the TTN broker will return confirmation
// messages on UDP for every message sent by the gateway. So we have to consume them..
// messages on UDP for every message sent by the gateway. So we have to consume them.
// As we do not know when the server will respond, we test in every loop.
//
else {
while( (packetSize = Udp.parsePacket()) > 0) { // Length of UDP message waiting
while( (packetSize = Udp.parsePacket()) > 0) {
#if DUSB>=2
Serial.println(F("loop:: readUdp calling"));
#endif
@ -1400,55 +1413,19 @@ void loop ()
}
// Now we know we succesfull received message from host
else {
_event=1; // Could be done double if more messages received
_event=1; // Could be done double if more messages received
}
//yield();
}
//yield(); // XXX 26/12/2017
}
yield();
// The next section is emergency only. If posible we hop() in the state machine.
// If hopping is enabled, and by lack of timer, we hop()
// XXX Experimental, 2.5 ms between hops max
//
if ((_hop) && (((long)(nowTime - hopTime)) > 7500)) {
if ((_state == S_SCAN) && (sf==SF12)) {
#if DUSB>=1
if (debug>=1) Serial.println(F("loop:: hop"));
#endif
hop();
}
// XXX section below does not work without further work. It is the section with the MOST
// influence on the HOP mode of operation (which is somewhat unexpected)
// If we keep staying in another state, reset
else if (((long)(nowTime - hopTime)) > 100000) {
_state= S_RX;
rxLoraModem();
hop();
if (_cad) {
_state= S_SCAN;
cadScanner();
}
}
else if (debug>=3) { Serial.print(F(" state=")); Serial.println(_state); }
inHop = false; // Reset re-entrane protection of HOP
yield();
}
yield(); // XXX 26/12/2017
// stat PUSH_DATA message (*2, par. 4)
//
if ((nowSeconds - stattime) >= _STAT_INTERVAL) { // Wake up every xx seconds
if ((nowSeconds - statTime) >= _STAT_INTERVAL) { // Wake up every xx seconds
#if DUSB>=1
if (debug>=2) {
Serial.print(F("STAT <"));
@ -1481,7 +1458,7 @@ void loop ()
}
}
#endif
stattime = nowSeconds;
statTime = nowSeconds;
}
yield();
@ -1489,7 +1466,7 @@ void loop ()
// send PULL_DATA message (*2, par. 4)
//
nowSeconds = (uint32_t) millis() /1000;
nowSeconds = now();
if ((nowSeconds - pulltime) >= _PULL_INTERVAL) { // Wake up every xx seconds
#if DUSB>=1
if (debug>=1) {
@ -1525,8 +1502,8 @@ void loop ()
#if NTP_INTR==0
// Set the time in a manual way. Do not use setSyncProvider
// as this function may collide with SPI and other interrupts
yield();
nowSeconds = (uint32_t) millis() /1000;
yield(); // 26/12/2017
nowSeconds = now();
if (nowSeconds - ntptimer >= _NTP_INTERVAL) {
yield();
time_t newTime;
@ -1535,4 +1512,6 @@ void loop ()
ntptimer = nowSeconds;
}
#endif
}
}//loop

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg
// Version 5.0.1
// Date: 2017-11-15
// Version 5.0.6
// Date: 2018-02-12
//
// Based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.1
// Date: 2017-11-15
// Version 5.0.6
// Date: 2018-02-12
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
@ -35,13 +35,14 @@
// ----------------------------------------------------------------------------
int readConfig(const char *fn, struct espGwayConfig *c) {
Serial.println(F("readConfig:: Starting"));
Serial.print(F("readConfig:: Starting "));
if (!SPIFFS.exists(fn)) {
Serial.print(F("ERROR:: readConfig, file does not exist "));
Serial.println(fn);
return(-1);
}
File f = SPIFFS.open(fn, "r");
if (!f) {
Serial.println(F("ERROR:: SPIFFS open failed"));
@ -50,6 +51,12 @@ int readConfig(const char *fn, struct espGwayConfig *c) {
while (f.available()) {
#if DUSB>=1
if (debug>=0) {
Serial.print('.');
}
#endif
String id =f.readStringUntil('=');
String val=f.readStringUntil('\n');
@ -127,6 +134,12 @@ int readConfig(const char *fn, struct espGwayConfig *c) {
}
}
f.close();
#if DUSB>=1
if (debug>=0) {
Serial.println('#');
}
#endif
Serial.println();
return(1);
}

Wyświetl plik

@ -0,0 +1,80 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.6
// Date: 2018-02-12
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the MIT License
// which accompanies this distribution, and is available at
// https://opensource.org/licenses/mit-license.php
//
// Author: Maarten Westenberg (mw12554@hotmail.com)
//
// This file contains the state machine code enabling to receive
// and transmit packages/messages.
// ========================================================================================
//
#if OLED>=1
void init_oLED()
{
// Initialising the UI will init the display too.
display.init();
display.flipScreenVertically();
display.setFont(ArialMT_Plain_24);
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.drawString(0, 24, "STARTING");
display.display();
}
// Activate the OLED
//
void acti_oLED()
{
// Initialising the UI will init the display too.
display.clear();
#if OLED==1
display.setFont(ArialMT_Plain_24);
display.drawString(0, 24, "READY");
#elif OLED==2
display.setFont(ArialMT_Plain_16);
display.drawString(0, 24, "READY");
#endif
display.display();
}
// Print a message on the OLED.
// Note: The whole message must fit in the buffer
//
void msg_oLED(String tim, String sf) {
display.clear();
display.setFont(ArialMT_Plain_16);
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.drawString(0, 48, "LEN: " );
// display.drawString(40, 48, String((int)messageLength) );
display.display();
yield();
}
// Print the OLED address in use
//
void addr_oLED()
{
Serial.print(F("OLED_ADDR=0x"));
Serial.println(OLED_ADDR, HEX);
}
#endif

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.1
// Date: 2017-11-15
// Version 5.0.6
// Date: 2018-02-12
//
//
// All rights reserved. This program and the accompanying materials

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg
// Verison 5.0.1
// Date: 2017-11-15
// Verison 5.0.6
// Date: 2018-02-12
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the MIT License

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg
// Verison 5.0.1
// Date: 2017-11-15
// Verison 5.0.6
// Date: 2018-02-12
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the MIT License
@ -471,8 +471,6 @@ int sensorPacket() {
//
if (( frameCount % 10)==0) writeGwayCfg(CONFIGFILE);
//yield(); // XXX Can we remove this here?
if (buff_index > 512) {
if (debug>0) Serial.println(F("sensorPacket:: ERROR buffer size too large"));
return(-1);

Wyświetl plik

@ -0,0 +1,722 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.6
// Date: 2018-02-12
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the MIT License
// which accompanies this distribution, and is available at
// https://opensource.org/licenses/mit-license.php
//
// Author: Maarten Westenberg (mw12554@hotmail.com)
//
// This file contains the state machine code enabling to receive
// and transmit packages/messages.
// ========================================================================================
//
// ----------------------------------------------------------------------------
// stateMachine handler of the state machine.
// We use ONE state machine for all kind of interrupts. This assures that we take
// the correct action upon receiving an interrupt.
//
// _event is the software interrupt: If set this function is executed from loop(),
// the function should itself take care of setting or resetting the variable.
//
// STATE MACHINE
// The program uses the following state machine (in _state), all states
// are done in interrupt routine, only the follow-up of S-RXDONE is done
// in the main loop() program. This is because otherwise the interrupt processing
// would take too long to finish
//
// So _state has one of the following state values:
//
// S-INIT=0, The commands in this state are executed only once
// - Goto S_SCAN
//
// S-SCAN, CadScanner() part
// - Upon CDDECT (int1) goto S_RX,
// - upon CDDONE (int0) goto S_CAD, walk through all SF until CDDETD
// - Else stay in SCAN state
//
// S-CAD,
// - Upon CDDECT (int1) goto S_RX,
// - Upon CDDONE (int0) goto S_SCAN, start with SF7 recognition again
//
// S-RX, Received CDDECT so message detected, RX cycle started.
// - Upon RXDONE (int0) package read. If read ok continue to read message
// - upon RXTOUT (int1) error, goto S_SCAN
//
// S-TX Transmitting a message
// - Upon TXDONE goto S_SCAN
//
// S-TXDONE Transmission complete by loop() now again in interrupt
// - Set the Mask
// - reset the Flags
// - Goto either SCAN or RX
//
// This interrupt routine has been kept as simple and short as possible.
// If we receive an interrupt that does not below to a _state then print error.
//
// NOTE: We may clear the interrupt but leave the flag for the moment.
// The eventHandler should take care of repairing flags between interrupts.
// ----------------------------------------------------------------------------
void stateMachine()
{
// Determine what interrupt flags are set
//
uint8_t flags = readRegister(REG_IRQ_FLAGS);
uint8_t mask = readRegister(REG_IRQ_FLAGS_MASK);
uint8_t intr = flags & ( ~ mask ); // Only react on non masked interrupts
uint8_t rssi;
// If there is NO interrupt and if _hop we wait until this is one
// or the wait time is over.
// That means if hop we will ONLY execute the state machine below
// when having an interrupt value and therefore a _state
//
if (intr == 0x00)
{
// If we hop we have to make sure that we allow enought time to detect
// CDDONE or CDECT. But if we do not receive interrupts, we have to schedule
// another hop after EVENT_WAIT microseconds.
// The process is such that we scan on SF7 (the shortest) preamble and if
// nothing detected within a scan, we switch to another frequency.
//
if (_hop) {
// Reset the IRQ registers. We clear the flag to accept all interrupts
// and we clear all interrupts.
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
while (_event==0)
{
hop(); // next frequency, set sf to SF7
cadScanner(); // Reset to SF7
// Wait for first CDDONE or CDETD interrupt to come in
// This is tricky for hopping as hopping is NOT interrupt driven.
// XXX All such timers are in seconds, or micros is used for real-time
if ( (( micros() - hopTime ) > _STAT_INTERVAL ) ||
(( micros() - hopTime ) > _PULL_INTERVAL ) )
{
_event=0;
return;
}
yield(); // XXX 03/01/2018
delayMicroseconds(300); // Allow CDDETD be noticed after CDDONE. XXX 150
intr = readRegister(REG_IRQ_FLAGS) | intr;
if (intr!=0) _event=1;
}
// We received a real interrupt, so do nothing with either _event
// or intr and let handle by state machine
_state=S_CAD;
#if DUSB>=1
if (debug>=1) {
Serial.print("EVENT=0x");
Serial.print(intr,HEX);
Serial.print(F(", F="));
Serial.print(ifreq);
Serial.print(F(", SF="));
Serial.print(sf);
Serial.print(F(", E="));
Serial.print(_event);
Serial.print(F(", S="));
Serial.print(_state);
Serial.print(F(", t="));
Serial.print( micros() - hopTime );
Serial.println();
}
#endif
_event=0; // If we received an interrupt, do the state machine below.
}// hop
// If not hopping make sure to return without doing anything
// cause we only act on interrupts in this mode ((_event!=0) && (intr!=0))
//
else {
_event=0;
//return; // XXX Does this work as all are Freq 1 message when hopping
}
}// intr==0
// This is the actual state machine of the gateway
// and its next actions are depending on the state we are in.
// For hop situations we do not get interrupts, so we have to
// simulate and generate events ourselves.
//
switch (_state)
{
// --------------------------------------------------------------
// If the state is init, we are starting up.
// The initLoraModem() function is already called in setup();
//
case S_INIT:
#if DUSB>=2
if (debug >= 1) {
Serial.println(F("S_INIT"));
}
#endif
// new state, needed to startup the radio (to S_SCAN)
writeRegister(REG_IRQ_FLAGS, 0xFF ); // Clear ALL interrupts
_event=0;
break;
// --------------------------------------------------------------
// In S_SCAN we measure a high RSSI this means that there (probably) is a message
// coming in at that freq. But not necessarily on the current SF.
// If so find the right SF with CDDETD.
//
case S_SCAN:
//
// Intr==IRQ_LORA_CDDETD_MASK
// We detected a message on this frequency and SF when scanning
// We clear both CDDETD and swich to reading state to read the message
//
if (intr & IRQ_LORA_CDDETD_MASK) {
_state = S_RX; // Set state to receiving
opmode(OPMODE_RX_SINGLE); // set reg 0x01 to 0x06
// Set RXDONE interrupt to dio0, RXTOUT to dio1
writeRegister(REG_DIO_MAPPING_1, (
MAP_DIO0_LORA_RXDONE |
MAP_DIO1_LORA_RXTOUT |
MAP_DIO2_LORA_NOP |
MAP_DIO3_LORA_CRC ));
// Since new state is S_RX, accept no interrupts except RXDONE or RXTOUT
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
IRQ_LORA_RXDONE_MASK |
IRQ_LORA_RXTOUT_MASK |
IRQ_LORA_HEADER_MASK |
IRQ_LORA_CRCERR_MASK));
delayMicroseconds( RSSI_WAIT ); // Wait some microseconds less
// Starting with version 5.0.1 the waittime is dependent on the SF
// So for SF12 we wait longer (2^7 == 128 uSec) and for SF7 4 uSec.
//delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) );
rssi = readRegister(REG_RSSI); // Read the RSSI
_rssi = rssi; // Read the RSSI in the state variable
writeRegister(REG_IRQ_FLAGS, 0xFF ); // reset all interrupt flags
_event = 0; // Make 0, as soon aswe have an interrupt
#if DUSB>=1
if (debug>=2) {
Serial.println(F("SCAN:: CDDETD"));
}
#endif
detTime = micros();
}//if
// CDDONE
// We received a CDDONE int telling us that we received a message on this
// frequency and possibly on one of its SF.
// If so, we switch to CAD state where we will wait for CDDETD event.
//
else if (intr & IRQ_LORA_CDDONE_MASK) {
opmode(OPMODE_CAD);
rssi = readRegister(REG_RSSI); // Read the RSSI
// We choose the generic RSSI as a sorting mechanism for packages/messages
// The pRSSI (package RSSI) is calculated upon successful reception of message
// So we expect that this value makes little sense for the moment with CDDONE.
// Set the rssi as low as the noise floor. Lower values are not recognized then.
// Every cycle starts with ifreq==0 and sf=SF7
//
if ( rssi > RSSI_LIMIT ) // Is set to 35
{
#if DUSB>=1
if (debug>=2) {
Serial.println(F("S_SCAN:: -> CAD"));
}
#endif
_state = S_CAD; // promote next level
_event=0; // next CDDONE by interrupt XXXXX
}
// If the RSSI is not big enough we skip the CDDONE
// and go back to scanning
else {
#if DUSB>=1
if (debug>=2) {
Serial.print("S_SCAN:: rssi=");
Serial.println(rssi);
}
#endif
_state = S_SCAN;
_event=1; // loop() scan until CDDONE
}
// Clear the CADDONE flag
writeRegister(REG_IRQ_FLAGS, 0xFF);
}//SCAN CDDONE
// So if we are here then we are in S_SCAN and the interrupt is not
// CDDECT or CDDONE. it is probably soft interrupt _event==1
// So if _hop we change the frequency and restart the
// interrupt in order to check for CDONE on other frequencies
// if _hop we start at the next frequency, hop () sets the sf to SF7.
// If we are at the end of all frequencies, reset frequencies and sf
// and go to S_SCAN state.
//
// Note:: We should make sure that all frequencies are scanned in a row
// and when we switch to ifreq==0 we should stop for a while
// to allow system processing.
// We should make sure that we enable webserver etc every once in a while.
// We do this by changing _event to 1 in loop() only for _hop and
// use _event=0 for non hop.
//
else if (intr == 0x00)
{
//_state = S_SCAN; // Do this state again but now for other freq.
if (! _hop) _event = 0; // XXX 26/12/2017 !!! NEED
}
// Unkown Interrupt, so we have an error
//
else {
#if DUSB>=1
Serial.print(F("SCAN unknown intr="));
Serial.println(intr,HEX);
#endif
_state=S_SCAN;
writeRegister(REG_IRQ_FLAGS, 0xFF);
}
break; // S_SCAN
// --------------------------------------------------------------
// S_CAD: In CAD mode we scan every SF for high RSSI until we have a DETECT.
// Reason is the we received a CADDONE interrupt so we know a message is received
// on the frequency but may be on another SF.
//
// If message is of the right frequency and SF, IRQ_LORA_CDDETD_MSAK interrupt
// is raised, indicating that we can start beging reading the message from SPI.
//
// DIO0 interrupt IRQ_LORA_CDDONE_MASK in state S_CAD==2 means that we might have
// a lock on the Freq but not the right SF. So we increase the SF
//
case S_CAD:
// Intr=IRQ_LORA_CDDETD_MASK
// We have to set the sf based on a strong RSSI for this channel
//
if (intr & IRQ_LORA_CDDETD_MASK) {
// Set RXDONE interrupt to dio0, RXTOUT to dio1
writeRegister(REG_DIO_MAPPING_1, (
MAP_DIO0_LORA_RXDONE |
MAP_DIO1_LORA_RXTOUT |
MAP_DIO2_LORA_NOP |
MAP_DIO3_LORA_CRC ));
// Accept no interrupts except RXDONE or RXTOUT
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
IRQ_LORA_RXDONE_MASK |
IRQ_LORA_RXTOUT_MASK |
IRQ_LORA_HEADER_MASK |
IRQ_LORA_CRCERR_MASK ));
_state = S_RX; // Set state to start receiving
opmode(OPMODE_RX_SINGLE); // set reg 0x01 to 0x06, initiate READ
delayMicroseconds( RSSI_WAIT ); // Wait some microseconds less
//delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) );
rssi = readRegister(REG_RSSI); // Read the RSSI
_rssi = rssi; // Read the RSSI in the state variable
//writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDETD_MASK | IRQ_LORA_RXDONE_MASK);
writeRegister(REG_IRQ_FLAGS, 0xFF ); // reset all CAD Detect interrupt flags
if (_hop) {
_event=0; // if CDECT, state=S_RX so we wait for intr
#if DUSB>=1
if (debug>=1) {
Serial.print(F("S_CAD:: hop CDECT freq="));
Serial.print(ifreq);
Serial.print(F(", sf="));
Serial.println(sf);
}
#endif
}
else{
_event=1; // XXX was 0;
}
#if DUSB>=1
if (debug>=2) {
Serial.println(F("CAD:: CDDETD"));
}
#endif
detTime=micros();
}// CDDETD
// Intr == CADDONE
// So we scan this SF and if not high enough ... next
//
else if (intr & IRQ_LORA_CDDONE_MASK) {
// If this is not SF12, increment the SF and try again
// We expect on other SF get CDDETD
//
if (((uint8_t)sf) < SF12) {
sf = (sf_t)((uint8_t)sf+1); // Increment sf
setRate(sf, 0x04); // Set SF with CRC==on
opmode(OPMODE_CAD); // Scanning mode
delayMicroseconds(RSSI_WAIT);
rssi = readRegister(REG_RSSI); // Read the RSSI
// reset interrupt flags for CAD Done
writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDONE_MASK | IRQ_LORA_CDDETD_MASK);
//writeRegister(REG_IRQ_FLAGS, 0xFF ); // This will prevent the CDDETD from being read
_event=0; // XXXXX 171215
#if DUSB>=1
if (debug>=2) {
Serial.print(F("S_CAD:: CDONE, SF="));
Serial.println(sf);
}
#endif
}
// If we reach SF12, we should go back to SCAN state
//
else {
_state = S_SCAN; // As soon as we reach SF12 do something
cadScanner(); // Which will reset SF to SF7
//writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDONE_MASK);
writeRegister(REG_IRQ_FLAGS, 0xFF );
_event=1; // reset soft intr, to state machine again
#if DUSB>=1
if (debug>=2) {
Serial.print(F("CAD->SCAN:0x"));
Serial.print(ifreq);
Serial.print(F(":SF"));
Serial.print(sf);
Serial.println();
}
#endif
}
} //CADDONE
// if this interrupt is not CDECT or CDDONE then probably is 0x00
// This means _event was set but there was no real interrupt (yet).
// So we clear _event and wait for next (soft) interrupt.
// We stay in the CAD state because CDDONE means something is
// coming on this frequency so we wait on CDECT.
//
else if (intr == 0x00) {
_event=0; // Stay in CAD _state until real interrupt
}
// else we do not recognize the interrupt. We print an error
// and restart scanning. If hop we even start at ifreq==1
//
else {
#if DUSB>=1
if (debug>=0) {
Serial.print(F("CAD: Unknown interrupt="));
Serial.println(intr);
}
#endif
_state = S_SCAN;
cadScanner(); // Scan and set SF7
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset all interrupts
_event=0;
}
break; //S_CAD
// --------------------------------------------------------------
// If we receive an interrupt on dio0 state==S_RX
// it should be a RxDone interrupt
// So we should handle the received message
//
case S_RX:
if (intr & IRQ_LORA_RXDONE_MASK) {
// We have to check for CRC error which will be visible AFTER RXDONE is set.
// CRC errors might indicate tha the reception is not OK.
// Could be CRC error or message too large.
// CRC error checking requires DIO3
//
if (intr & IRQ_LORA_CRCERR_MASK) {
#if DUSB>=1
if ((debug>=1)&&(intr & IRQ_LORA_CRCERR_MASK)) Serial.println(F("CRC err"));
#endif
if (_cad) {
_state = S_SCAN;
cadScanner();
}
else {
_state = S_RX;
rxLoraModem();
}
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00); // Reset the interrupt mask
// Reset interrupts
writeRegister(REG_IRQ_FLAGS, (uint8_t)(
IRQ_LORA_RXDONE_MASK |
IRQ_LORA_RXTOUT_MASK |
IRQ_LORA_HEADER_MASK |
IRQ_LORA_CRCERR_MASK ));
_event=0;
break;
}
unsigned long ffTime = micros();
// There should not be an error in the message
//
LoraUp.payLoad[0]= 0x00;
if((LoraUp.payLength = receivePkt(LoraUp.payLoad)) <= 0) {
#if DUSB>=1
if (debug>=0) {
Serial.println(F("sMachine:: Error S-RX"));
}
#endif
}
#if DUSB>=1
if (debug>=1) {
Serial.print(F("RXDONE="));
Serial.println(ffTime - detTime);
}
#endif
// Do all register processing in this section
uint8_t value = readRegister(REG_PKT_SNR_VALUE); // 0x19;
if ( value & 0x80 ) { // The SNR sign bit is 1
value = ( ( ~value + 1 ) & 0xFF ) >> 2; // Invert and divide by 4
LoraUp.snr = -value;
}
else {
// Divide by 4
LoraUp.snr = ( value & 0xFF ) >> 2;
}
LoraUp.prssi = readRegister(REG_PKT_RSSI); // read register 0x1A, packet rssi
// Correction of RSSI value based on chip used.
if (sx1272) { // Is it a sx1272 radio?
LoraUp.rssicorr = 139;
} else { // Probably SX1276 or RFM95
LoraUp.rssicorr = 157;
}
LoraUp.sf = readRegister(REG_MODEM_CONFIG2) >> 4;
// If read was successful, read the package from the LoRa bus
//
if (receivePacket() <= 0) { // read is not successful
#if DUSB>=1
Serial.println(F("sMach:: Error receivePacket"));
#endif
}
// Set the modem to receiving BEFORE going back to user space.
//
if (_cad) {
_state = S_SCAN;
cadScanner();
}
else {
_state = S_RX;
rxLoraModem();
}
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset the interrupt mask
_event=0;
}
// RX TIMEOUT: We did receive message receive timeout
//
else if (intr & IRQ_LORA_RXTOUT_MASK) {
// Make sure we reset all interrupts//
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 );
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset all interrupts
// For the modem in cad state we reset to SF7
// If a timeout occurs here we reset the cadscanner
//
if (_cad) { // XXX 01/01/2018
// Set the state to CAD scanning
#if DUSB>=1
if (debug>=2) {
Serial.print(F("RXTOUT:: f="));
Serial.print(ifreq);
Serial.print(F(", sf="));
Serial.print(sf);
Serial.print(F(", tim="));
Serial.println(micros() - detTime);
}
#endif
_state = S_SCAN;
cadScanner(); // Start the scanner after RXTOUT
_event=0;
}
// If not in cad mode we are in single channel single sf mode.
//
else {
_state = S_RX; //
rxLoraModem();
_event=0;
}
}
// The interrupt received is not RXDONE nor RXTOUT
// therefore we restart the scanning sequence (catch all)
// XXX This should not be possible, It is always one of the two...
else {
#if DUSB>=1
if (debug>=1) {
Serial.print(F("S_RX:: no RXDONE or RXTOUT but="));
Serial.println(intr);
}
#endif
//initLoraModem();
//_event=0;
}
break; // S_RX
// --------------------------------------------------------------
// Start te transmissoion of a message in state S-TX
// We use TXDONE as the state to read the message.
//
case S_TX:
if (intr == 0x00) {
#if DUSB>=1
Serial.println(F("TX:00"));
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags
_event=0;
return;
#endif
}
// Initiate the transmission of the buffer (in Interrupt space)
// We react on ALL interrupts if we are in TX state.
txLoraModem(
LoraDown.payLoad,
LoraDown.payLength,
LoraDown.tmst,
LoraDown.sfTx,
LoraDown.powe,
LoraDown.fff,
LoraDown.crc,
LoraDown.iiq
);
#if DUSB>=2
if (debug>=0) {
Serial.println(F("S_TX, "));
}
#endif
_state = S_TXDONE;
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags
_event=1;
break; // S_TX
// ---------------------------------------------------
// AFter the transmission is completed by the hardware,
// the interrupt TXDONE is raised telling us that the tranmission
// was successful.
// If we receive an interrupt on dio0 _state==S_TX it is a TxDone interrupt
// Do nothing with the interrupt, it is just an indication.
// sendPacket switch back to scanner mode after transmission finished OK
//
case S_TXDONE:
if (intr & IRQ_LORA_TXDONE_MASK) {
#if DUSB>=1
Serial.println(F("TXDONE interrupt"));
#endif
// After transmission reset to receiver
if (_cad) {
// Set the state to CAD scanning
_state = S_SCAN;
cadScanner(); // Start the scanner after TX cycle
}
else {
_state = S_RX;
rxLoraModem();
}
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags
#if DUSB>=1
if (debug>=1) {
Serial.println(F("TXDONE handled"));
if (debug>=2) Serial.flush();
}
#endif
_event=0;
}
// If a soft _event==0 interrupt and no transmission finished:
else {
#if DUSB>=1
if (debug>=0) {
Serial.print(F("TXDONE unknown interrupt="));
Serial.println(intr);
if (debug>=2) Serial.flush();
}
#endif
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // reset interrupt flags
_event=0;
}
break; // S_TXDONE
// --------------------------------------------------------------
// If _STATE is in undefined state
// If such a thing happens, we should re-init the interface and
// make sure that we pick up next interrupt
default:
#if DUSB>=1
if (debug >= 0) {
Serial.print("E state=");
Serial.println(_state);
}
#endif
if (_cad) {
_state = S_SCAN;
cadScanner();
}
else
{
_state = S_RX;
rxLoraModem();
}
writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF); // Reset all interrupts
_event=0;
break;
}
return;
}

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.1
// Date: 2017-11-15
// Version 5.0.6
// Date: 2018-02-12
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
@ -148,7 +148,7 @@ int sendPacket(uint8_t *buf, uint8_t length)
LoraDown.payLoad = payLoad;
LoraDown.payLength = payLength;
LoraDown.tmst = tmst;
LoraDown.tmst = tmst; // Downstream in milis
LoraDown.sfTx = sfTx;
LoraDown.powe = powe;
LoraDown.fff = fff;
@ -254,13 +254,26 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
#if STATISTICS >= 1
// Receive statistics
for (int m=( MAX_STAT -1); m>0; m--) statr[m]=statr[m-1];
statr[0].tmst = millis();
statr[0].tmst = now();
statr[0].ch= ifreq;
statr[0].prssi = prssi - rssicorr;
#if RSSI==1
statr[0].rssi = _rssi - rssicorr;
#endif
statr[0].sf = LoraUp.sf;
#if DUSB>=2
if (debug>=0) {
if ((message[4] != 0x26) || (message[1]==0x99)) {
Serial.print(F("addr="));
for (int i=messageLength; i>0; i--) {
if (message[i]<16) Serial.print('0');
Serial.print(message[i],HEX);
Serial.print(' ');
}
Serial.println();
}
}
#endif
statr[0].node = ( message[1]<<24 | message[2]<<16 | message[3]<<8 | message[4] );
#if STATISTICS >= 2
@ -277,7 +290,7 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
#if DUSB>=1
if (debug>=1) {
Serial.print(F("pRSSI: "));
Serial.print(F("buildPacket:: pRSSI="));
Serial.print(prssi-rssicorr);
Serial.print(F(" RSSI: "));
Serial.print(_rssi - rssicorr);
@ -297,31 +310,43 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
#endif
// Show received message status on OLED display
#if OLED==1
display.clear();
display.setFont(ArialMT_Plain_16);
display.setTextAlignment(TEXT_ALIGN_LEFT);
char timBuff[20];
sprintf(timBuff, "%02i:%02i:%02i", hour(), minute(), second());
display.drawString(0, 0, "Time: " );
display.drawString(40, 0, timBuff);
display.drawString(0, 16, "RSSI: " );
display.drawString(40, 16, String(prssi-rssicorr));
display.drawString(70, 16, ",SNR: " );
display.drawString(110, 16, String(SNR) );
#if OLED>=1
char timBuff[20];
sprintf(timBuff, "%02i:%02i:%02i", hour(), minute(), second());
// char addrBuff[20;
// if (message[4] < 0x10) display.drawString( 40, 32, "0"+String(message[4], HEX)); else display.drawString( 40, 32, String(message[4], HEX));
// if (message[3] < 0x10) display.drawString( 61, 32, "0"+String(message[3], HEX)); else display.drawString( 61, 32, String(message[3], HEX));
// if (message[2] < 0x10) display.drawString( 82, 32, "0"+String(message[2], HEX)); else display.drawString( 82, 32, String(message[2], HEX));
// if (message[1] < 0x10) display.drawString(103, 32, "0"+String(message[1], HEX)); else display.drawString(103, 32, String(message[1], HEX));
// sprintf(addrBuff, "%02X,:%02X:%02X:%02X", message[4], message[3], message[2], message[1]);
display.clear();
display.setFont(ArialMT_Plain_16);
display.setTextAlignment(TEXT_ALIGN_LEFT);
// msg_oLED(timBuff, prssi-rssicorr, SNR, message)
display.drawString(0, 0, "Time: " );
display.drawString(40, 0, timBuff);
display.drawString(0, 16, "RSSI: " );
display.drawString(40, 16, String(prssi-rssicorr));
display.drawString(70, 16, ",SNR: " );
display.drawString(110, 16, String(SNR) );
display.drawString(0, 32, "Addr: " );
display.drawString(0, 32, "Addr: " );
if (message[4] < 0x10) display.drawString( 40, 32, "0"+String(message[4], HEX)); else display.drawString( 40, 32, String(message[4], HEX));
if (message[3] < 0x10) display.drawString( 61, 32, "0"+String(message[3], HEX)); else display.drawString( 61, 32, String(message[3], HEX));
if (message[2] < 0x10) display.drawString( 82, 32, "0"+String(message[2], HEX)); else display.drawString( 82, 32, String(message[2], HEX));
if (message[1] < 0x10) display.drawString(103, 32, "0"+String(message[1], HEX)); else display.drawString(103, 32, String(message[1], HEX));
if (message[4] < 0x10) display.drawString( 40, 32, "0"+String(message[4], HEX)); else display.drawString( 40, 32, String(message[4], HEX));
if (message[3] < 0x10) display.drawString( 61, 32, "0"+String(message[3], HEX)); else display.drawString( 61, 32, String(message[3], HEX));
if (message[2] < 0x10) display.drawString( 82, 32, "0"+String(message[2], HEX)); else display.drawString( 82, 32, String(message[2], HEX));
if (message[1] < 0x10) display.drawString(103, 32, "0"+String(message[1], HEX)); else display.drawString(103, 32, String(message[1], HEX));
display.drawString(0, 48, "LEN: " );
display.drawString(40, 48, String((int)messageLength) );
display.display();
yield();
#endif
display.drawString(0, 48, "LEN: " );
display.drawString(40, 48, String((int)messageLength) );
display.display();
yield();
#endif //OLED>=1
int j;
@ -336,11 +361,19 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
}
#endif
base64_encode(b64, (char *) message, messageLength);// max 341
// start composing datagram with the header
uint8_t token_h = (uint8_t)rand(); // random token
uint8_t token_l = (uint8_t)rand(); // random token
// pre-fill the data buffer with fixed fields
buff_up[0] = PROTOCOL_VERSION; // 0x01 still
buff_up[3] = PKT_PUSH_DATA; // 0x00
buff_up[1] = token_h;
buff_up[2] = token_l;
buff_up[3] = PKT_PUSH_DATA; // 0x00
// READ MAC ADDRESS OF ESP8266, and insert 0xFF 0xFF in the middle
buff_up[4] = MAC_array[0];
buff_up[5] = MAC_array[1];
@ -351,11 +384,7 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
buff_up[10] = MAC_array[4];
buff_up[11] = MAC_array[5];
// start composing datagram with the header
uint8_t token_h = (uint8_t)rand(); // random token
uint8_t token_l = (uint8_t)rand(); // random token
buff_up[1] = token_h;
buff_up[2] = token_l;
buff_index = 12; // 12-byte binary (!) header
// start of JSON structure that will make payload
@ -473,7 +502,7 @@ int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool inte
// ----------------------------------------------------------------------------
int receivePacket()
{
uint8_t buff_up[TX_BUFF_SIZE]; // buffer to compose the upstream packet to backend server
uint8_t buff_up[TX_BUFF_SIZE]; // buffer to compose the upstream packet to backend server
long SNR;
uint8_t message[128] = { 0x00 }; // MSG size is 128 bytes for rx
uint8_t messageLength = 0;

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.1
// Date: 2017-11-15
// Version 5.0.6
// Date: 2018-02-12
//
// based on work done by many people and making use of several libraries.
//
@ -69,16 +69,21 @@ static void printHEX(char * hexa, const char sep, String& response)
// ----------------------------------------------------------------------------
// stringTime
// Print the time t into the String reponse. t is of type time_t in seconds.
// Only when RTC is present we print real time values
// t contains number of milli seconds since system started that the event happened.
// t contains number of seconds since system started that the event happened.
// So a value of 100 would mean that the event took place 1 minute and 40 seconds ago
// ----------------------------------------------------------------------------
static void stringTime(unsigned long t, String& response) {
static void stringTime(time_t t, String& response) {
if (t==0) { response += "--"; return; }
// now() gives seconds since 1970
time_t eventTime = now() - ((millis()-t)/1000);
// as millis() does rotate every 50 days
// So we need another timing parameter
time_t eventTime = t;
// Rest is standard
byte _hour = hour(eventTime);
byte _minute = minute(eventTime);
byte _second = second(eventTime);
@ -139,7 +144,8 @@ static void setVariables(const char *cmd, const char *arg) {
if (! _hop) {
ifreq=0;
freq=freqs[0];
rxLoraModem();
rxLoraModem();
cadScanner();
}
writeGwayCfg(CONFIGFILE); // Save configuration to file
}
@ -272,7 +278,7 @@ static void openWebPage()
response +="Version: "; response+=VERSION;
response +="<br>ESP alive since "; // STARTED ON
stringTime(1, response);
stringTime(startTime, response);
response +=", Uptime: "; // UPTIME
uint32_t secs = millis()/1000;
@ -289,7 +295,7 @@ static void openWebPage()
response += String() + _second;
response +="<br>Current time "; // CURRENT TIME
stringTime(millis(), response);
stringTime(now(), response);
response +="<br>";
server.sendContent(response);
@ -529,9 +535,11 @@ static void statisticsData()
response +="<tr><td class=\"cell\">Packages Uplink Total</td>";
response +="<td class=\"cell\">" + String(cp_nb_rx_rcv) + "</td>";
response +="<td class=\"cell\">" + String((cp_nb_rx_rcv*3600)/(millis()/1000)) + "</td></tr>";
response +="<td class=\"cell\">" + String((cp_nb_rx_rcv*3600)/(now() - startTime)) + "</td></tr>";
response +="<tr><td class=\"cell\">Packages Uplink OK </td><td class=\"cell\">";
response +=cp_nb_rx_ok; response+="</tr>";
response +="<tr><td class=\"cell\">Packages Downlink</td><td class=\"cell\">";
response +=cp_up_pkt_fwd; response+="</tr>";
@ -577,8 +585,9 @@ static void sensorData()
response += "<tr>";
response += "<th class=\"thead\">Time</th>";
response += "<th class=\"thead\">Node</th>";
response += "<th class=\"thead\" colspan=\"2\">Channel</th>";
response += "<th class=\"thead\" style=\"width: 50px;\">SF</th>";
response += "<th class=\"thead\" style=\"width: 20px;\">C</th>";
response += "<th class=\"thead\">Freq</th>";
response += "<th class=\"thead\" style=\"width: 40px;\">SF</th>";
response += "<th class=\"thead\" style=\"width: 50px;\">pRSSI</th>";
#if RSSI==1
if (debug > 1) {
@ -594,7 +603,7 @@ static void sensorData()
response = "";
response += String() + "<tr><td class=\"cell\">";
stringTime(statr[i].tmst, response);
stringTime((statr[i].tmst), response); // XXX Change tmst not to be millis() dependent
response += "</td>";
response += String() + "<td class=\"cell\">";
printHEX((char *)(& (statr[i].node)),' ',response);
@ -785,7 +794,8 @@ void setupWWW()
// Reset the statistics
server.on("/RESET", []() {
Serial.println(F("RESET"));
cp_nb_rx_rcv = 0;
startTime= now() - 1; // Reset all timers too
cp_nb_rx_rcv = 0; // Reset package statistics
cp_nb_rx_ok = 0;
cp_up_pkt_fwd = 0;
#if STATISTICS >= 1

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.1
// Date: 2017-11-15
// Version 5.0.6
// Date: 2018-02-12
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
@ -39,7 +39,7 @@ struct espGwayConfig {
uint8_t debug; // range 0 to 4
bool cad; // is CAD enabled?
bool hop; // Is HOP enabled (Note: SHould be disabled)
bool hop; // Is HOP enabled (Note: Should be disabled)
bool isNode; // Is gateway node enabled
bool refresh; // Is WWW browser refresh enabled

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.1
// Date: 2017-11-14
// Version 5.0.6
// Date: 2018-02-12
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many other contributors.
@ -19,10 +19,37 @@
//
// ------------------------------------------------------------------------------------
// Our code should correct the server timing
long txDelay= 0x00; // delay time on top of server TMST
#define SPISPEED 8000000 // was 50000/50KHz < 10MHz
// ----------------------------------------
// Used by REG_PAYLOAD_LENGTH to set receive payload length
#define PAYLOAD_LENGTH 0x40 // 64 bytes
#define MAX_PAYLOAD_LENGTH 0x80 // 128 bytes
// In order to make the CAD behaviour dynamic we set a variable
// when the CAD functions are defined. Value of 3 is minimum frequencies a
// gateway should support to be fully LoRa compliant.
#define NUM_HOPS 2 // 3 should be the minimum
// Do not change these setting for RSSI detection. They are used for CAD
// Given the correction factor of 157, we can get to -122dB with this rating
//
#define RSSI_LIMIT 35 //
// How long to wait in LoRa mode before using the RSSI value.
// This period should be as short as possible, yet sufficient
//
#define RSSI_WAIT 6 // was 25
// How long will it take when hopping before a CDONE or CDETD value
// is measured.
//
#define EVENT_WAIT 600 // was 200
// Our code should correct the server Tramission delay settings
long txDelay= 0x00; // tx delay time on top of server TMST
// SPI setting. 8MHz seems to be the max
#define SPISPEED 8000000 // Set to 8 * 10E6
// Frequencies
// Set center frequency. If in doubt, choose the first one, comment all others
@ -32,7 +59,7 @@ int freqs [] = {
868100000, // Channel 0, 868.1 MHz primary
868300000, // Channel 1, 868.3 MHz mandatory
868500000, // Channel 2, 868.5 MHz mandatory
867100000, // Channel 3, 867.1 MHz
867100000, // Channel 3, 867.1 MHz Optional
867300000,
867500000,
867700000,
@ -60,26 +87,23 @@ volatile uint8_t _event=0;
// so we need to store the current value we like to work with
uint8_t _rssi;
// In order to make the CAD behaviour dynamic we set a variable
// when the CAD functions are defined. Value of 3 is minimum frequencies a
// gateway should support to be fully LoRa compliant.
#define NUM_HOPS 3
bool _cad= (bool) _CAD; // Set to true for Channel Activity Detection, only when dio 1 connected
bool _hop=false; // experimental; frequency hopping. Only use when dio2 connected
bool inHop=false;
unsigned long nowTime=0;
unsigned long hopTime=0;
unsigned long msgTime=0;
unsigned long hopTime=0;
unsigned long detTime=0;
#if _PIN_OUT==1
// Definition of the GPIO pins used by the Gateway for Hallard type boards
//
struct pins {
uint8_t dio0=15; // GPIO15 / D8. For the Hallard board shared between DIO0/DIO1/DIO2
uint8_t dio1=15; // GPIO15 / D8. Used for CAD, may or not be shared with DIO0
uint8_t dio2=15; // GPIO15 / D8. Used for frequency hopping, don't care
uint8_t ss=16; // GPIO16 / D0. Select pin connected to GPIO16 / D0
uint8_t rst=0; // GPIO0 / D3. Reset pin not used
uint8_t rst=0; // GPIO 0 / D3. Reset pin not used
// MISO 12 / D6
// MOSI 13 / D7
// CLK 14 / D5
@ -93,8 +117,22 @@ struct pins {
uint8_t ss=15; // GPIO15 / D8. Select pin connected to GPIO15
uint8_t rst=0; // GPIO0 / D3. Reset pin not used
} pins;
#elif _PIN_OUT==3
// For ESP32 based board
// SCK == GPIO5/ PIN5
// SS == GPIO18/PIN18
// MISO == GPIO19/ PIN19
// MOSI == GPIO27/ PIN27
// RST == GPIO14/ PIN14
struct pins {
uint8_t dio0=26; // GPIO26 / Dio0 used for one frequency and one SF
uint8_t dio1=26; // GPIO26 / Used for CAD, may or not be shared with DIO0
uint8_t dio2=26; // GPI26 / Used for frequency hopping, don't care
uint8_t ss=18; // GPIO18 / Dx. Select pin connected to GPIO18
uint8_t rst=14; // GPIO0 / D3. Reset pin not used
} pins;
#else
// Use your own pin definitions, and uncomment #error line below
// Use your own pin definitions, and comment #error line below
// MISO 12 / D6
// MOSI 13 / D7
// CLK 14 / D5
@ -106,21 +144,18 @@ struct pins {
// Ech time a message is received or sent the statistics are updated.
// In case STATISTICS==1 we define the last MAX_STAT messages as statistics
struct stat_t {
unsigned long tmst; // Time since 1970 in milli seconds
unsigned long tmst; // Time since 1970 in seconds
unsigned long node; // 4-byte DEVaddr (the only one known to gateway)
uint8_t ch; // Channel index to freqs array
uint8_t sf;
#if RSSI==1
int8_t rssi; // XXX Can be < -128
int8_t rssi; // XXX Can be < -128
#endif
int8_t prssi; // XXX Can be < -128
int8_t prssi; // XXX Can be < -128
} stat_t;
#if STATISTICS >= 1
// History of received uplink messages from nodes
struct stat_t statr[MAX_STAT];
// STATC contains the statistic that are gateway related and not per
// message. Example: Number of messages received on SF7 or number of (re) boots
// So where statr contains the statistics gathered per packet the statc
@ -139,55 +174,42 @@ struct stat_c {
} stat_c;
struct stat_c statc;
#endif
// History of received uplink messages from nodes
struct stat_t statr[MAX_STAT];
#else // STATISTICS==0
struct stat_t statr[1]; // Always have at least one element to store in
struct stat_t statr[1]; // Always have at least one element to store in
#endif
// Define the payload structure used to separate interrupt ans SPI
// processing from the loop() part
uint8_t payLoad[128]; // Payload i
uint8_t payLoad[128]; // Payload i
struct LoraBuffer {
uint8_t * payLoad;
uint8_t payLength;
uint32_t tmst;
uint8_t sfTx;
uint8_t powe;
uint32_t fff;
uint8_t crc;
uint8_t iiq;
uint8_t * payLoad;
uint8_t payLength;
uint32_t tmst; // in millis()
uint8_t sfTx;
uint8_t powe;
uint32_t fff;
uint8_t crc;
uint8_t iiq;
} LoraDown;
// Up buffer (from Lora to UDP)
// Up buffer (from Lora sensor to UDP)
//
struct LoraUp {
uint8_t payLoad[128];
uint8_t payLength;
int prssi;
long snr;
int rssicorr;
uint8_t sf;
uint8_t payLoad[128];
uint8_t payLength;
int prssi;
long snr;
int rssicorr;
uint8_t sf;
} LoraUp;
// ----------------------------------------
// Used by REG_PAYLOAD_LENGTH to set receive payload length
#define PAYLOAD_LENGTH 0x40 // 64 bytes
#define MAX_PAYLOAD_LENGTH 0x80 // 128 bytes
// Do not change these setting for RSSI detection. They are used for CAD
// Given the correction factor of 157, we can get to -120dB with this rating
//
#define RSSI_LIMIT 37 // was 39
#define RSSI_LIMIT_DOWN 34 // Was 34
// How long to wait in LoRa mode before using the RSSI value.
// This period should be as short as possible, yet sufficient
//
#define RSSI_WAIT 15 // was 100 works, 50 works, 40 works, 25 works
#define RSSI_WAIT_DOWN 10 // was 75 works, 35 works, 30 works, 20 works
// ============================================================================
// Set all definitions for Gateway
@ -195,7 +217,7 @@ struct LoraUp {
// Register definitions. These are the addresses of the TFM95, SX1276 that we
// need to set in the program.
#define REG_FIFO 0x00
#define REG_FIFO 0x00 // rw FIFO address
#define REG_OPMODE 0x01
// Register 2 to 5 are unused for LoRa
#define REG_FRF_MSB 0x06
@ -204,17 +226,17 @@ struct LoraUp {
#define REG_PAC 0x09
#define REG_PARAMP 0x0A
#define REG_LNA 0x0C
#define REG_FIFO_ADDR_PTR 0x0D
#define REG_FIFO_TX_BASE_AD 0x0E
#define REG_FIFO_RX_BASE_AD 0x0F
#define REG_FIFO_ADDR_PTR 0x0D // rw SPI interface address pointer in FIFO data buffer
#define REG_FIFO_TX_BASE_AD 0x0E // rw write base address in FIFO data buffer for TX modulator
#define REG_FIFO_RX_BASE_AD 0x0F // rw read base address in FIFO data buffer for RX demodulator (0x00)
#define REG_FIFO_RX_CURRENT_ADDR 0x10
#define REG_FIFO_RX_CURRENT_ADDR 0x10 // r Address of last packet received
#define REG_IRQ_FLAGS_MASK 0x11
#define REG_IRQ_FLAGS 0x12
#define REG_RX_NB_BYTES 0x13
#define REG_PKT_SNR_VALUE 0x19
#define REG_PKT_RSSI 0x1A // latest package
#define REG_RSSI 0x1B // Current RSSI, section 6.4, or 5.5.5
#define REG_PKT_RSSI 0x1A // latest package
#define REG_RSSI 0x1B // Current RSSI, section 6.4, or 5.5.5
#define REG_HOP_CHANNEL 0x1C
#define REG_MODEM_CONFIG1 0x1D
#define REG_MODEM_CONFIG2 0x1E
@ -227,7 +249,7 @@ struct LoraUp {
#define REG_RSSI_WIDEBAND 0x2C
#define REG_INVERTIQ 0x33
#define REG_DET_TRESH 0x37 // SF6
#define REG_DET_TRESH 0x37 // SF6
#define REG_SYNC_WORD 0x39
#define REG_TEMP 0x3C
@ -245,7 +267,7 @@ struct LoraUp {
#define SX72_MODE_SLEEP 0x80
#define SX72_MODE_STANDBY 0x81
#define SX72_MODE_FSTX 0x82
#define SX72_MODE_TX 0x83 // 0x80 | 0x03
#define SX72_MODE_TX 0x83 // 0x80 | 0x03
#define SX72_MODE_RX_CONTINUOS 0x85
// ----------------------------------------
@ -266,7 +288,7 @@ struct LoraUp {
// ----------------------------------------
// LOW NOISE AMPLIFIER
#define LNA_MAX_GAIN 0x23
#define LNA_MAX_GAIN 0x23 // Max gain 0x20 | Boost 0x03
#define LNA_OFF_GAIN 0x00
#define LNA_LOW_GAIN 0x20
@ -326,9 +348,13 @@ struct LoraUp {
#define MAP_DIO2_LORA_NOP 0x0C // ----11-- bit 3 and 2
#define MAP_DIO3_LORA_CADDONE 0x00 // ------00 bit 1 and 0
#define MAP_DIO3_LORA_HEADER 0x01 // ------01
#define MAP_DIO3_LORA_CRC 0x02 // ------10
#define MAP_DIO3_LORA_NOP 0x03 // ------11
// FSK specific
#define MAP_DIO0_FSK_READY 0x00 // 00------ (packet sent / payload ready)
#define MAP_DIO1_FSK_NOP 0x30 // --11----
#define MAP_DIO2_FSK_TXNOP 0x04 // ----01--
#define MAP_DIO2_FSK_TIMEOUT 0x08 // ----10--
@ -355,7 +381,7 @@ struct LoraUp {
#define PKT_PULL_ACK 0x04
#define PKT_TX_ACK 0x05
#define MGT_RESET 0x15 // Not a LoRa Gateway Spec message
#define MGT_RESET 0x15 // Not a LoRa Gateway Spec message
#define MGT_SET_SF 0x16
#define MGT_SET_FREQ 0x17

41
ESP-sc-gway/oLED.h 100644
Wyświetl plik

@ -0,0 +1,41 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017 Maarten Westenberg version for ESP8266
// Version 5.0.6
// Date: 2018-02-12
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the MIT License
// which accompanies this distribution, and is available at
// https://opensource.org/licenses/mit-license.php
//
// Author: Maarten Westenberg (mw12554@hotmail.com)
//
// This file contains a number of compile-time settings and definitions for OLED support.
//
// ----------------------------------------------------------------------------------------
#if OLED>=1
#define OLED_SCL 5 // GPIO5 / D1
#define OLED_SDA 4 // GPIO4 / D2
#if OLED==1
#include "SSD1306.h"
#define OLED_ADDR 0x3C // Default 0x3C for 0.9", for 1.3" it is 0x78
SSD1306 display(OLED_ADDR, OLED_SDA, OLED_SCL);// i2c ADDR & SDA, SCL on wemos
#endif
#if OLED==2
#include "SH1106.h"
//#define OLED_ADDR 0x78 // Default for 1.3" it is 0x78
#define OLED_ADDR 0x3C // Default 0x3C for 0.9", for 1.3" it is 0x78
SH1106 display(OLED_ADDR, OLED_SDA, OLED_SCL); // i2c ADDR & SDA, SCL on wemos
#endif
#endif

Wyświetl plik

@ -1,6 +1,6 @@
# Single Channel LoRaWAN Gateway
Version 5.0.1, November, 2017
Version 5.0.3, December 09, 2017
Author: M. Westenberg (mw12554@hotmail.com)
Copyright: M. Westenberg (mw12554@hotmail.com)
@ -20,21 +20,25 @@ The software implements a standard LoRa gateway with the following exceptions on
- This LoRa gateway is not a full gateway but it implements just a one-channel/one frequency gateway.
The minimum amount of frequencies supported by a full gateway is 3, most support 9 or more frequencies.
This software started as a proof-of-concept to prove that a single low-cost RRFM95 chip which was present in almost every
LoRa node in Europe could be used as a cheap alternative to the far more expensive full gateways that were
making use of the SX1301 chip.
This software started as a proof-of-concept to prove that a single low-cost RRFM95 chip which was present
in almost every LoRa node in Europe could be used as a cheap alternative to the far more expensive full
gateways that were making use of the SX1301 chip.
- As the software of this gateway will often be used during the development phase of a project or in demo situations,
the software is flexible and can be easily configured according to environment or customer requirements.
There are two ways of interacting with the software: 1. Modifying the ESP-sc-gway.h file at compile time allows the
administrator to set almost all parameters. 2. Using the webinterface (http://<gateway_IP>) will allow
administrators to set and reset several of the parameters at runtime.
- As the software of this gateway will often be used during the development phase of a project or in
demo situations, the software is flexible and can be easily configured according to environment or
customer requirements. There are two ways of interacting with the software:
1. Modifying the ESP-sc-gway.h file at compile time allows the administrator to set almost all parameters.
2. Using the webinterface (http://<gateway_IP>) will allow administrators to set and reset several of the
parameters at runtime.
Full documentation of the Single Channel Gateway is found at things4u.github.io, please look at the Hardware Guide
under the Gateway chapter.
## testing
The single channel gateway has been tested on a gateway with the Wemos D1 Mini, using a HopeRF RFM95W transceiver.
The LoRa nodes tested are:
The LoRa nodes tested againts this gateway are:
- TeensyLC with HopeRF RFM95 radio
- Arduino Pro-Mini (default Armega328 model, 8MHz 3.3V and 16MHz 3.3V)
@ -72,7 +76,7 @@ coming in on the Serial monitor.
There are two ways of changing the configuration of the single channel gateway:
1. Changing the ESP-sc-gway.h file at compile-time
2. Run the http://<gateway-IP> web interface to change setting at complie time.
2. Run the http://<gateway-IP> web interface to change settings at run time.
## Editing the ESP-sc-gway.h file
@ -89,6 +93,14 @@ The user is advised to turn off functions not used in order to save on memory us
If the heap drops below 18 KBytes some functions may not behave as expected (in extreme case the program may crash).
### Setting USB
The user can determine whether or not the USB console is used for output messages.
When setting DUSB to 0 all output by Serial is disabled
(actually the Serial statements are not included in the code).
\#define DUSB 1
### Debug
The user can set the initial value of the DEBUG parameter.
@ -301,7 +313,7 @@ Please note that they are NOT part of the ESP 1channel gateway and may have thei
However, these libraries are not part of the single-channel Gateway software.
# Connections
# Pin Connections
See http://things4u.github.io in the hardware section for building and connection instructions.
@ -310,7 +322,7 @@ See http://things4u.github.io in the hardware section for building and connectio
The following things are still on my wish list to make to the single channel gateway:
- Receive downstream message with commands form the server. These can be used to configure
- Receive downstream message with commands from the server. These can be used to configure
the gateway through downlink messages (such as setting the SF)
- Support for ESP32 and RFM95 on 915 MHz
- Use the SPIFFS for storing .css files
@ -321,4 +333,5 @@ The following things are still on my wish list to make to the single channel gat
# License
The source files of the gateway sketch in this repository is made available under the MIT
license. The libraries included in this repository are included for convenience only and all have their own license, and are not part of the ESP 1ch gateway code.
license. The libraries included in this repository are included for convenience only and all have their own license,
and are not part of the ESP 1ch gateway code.