Version 5.3.1; support for T_Beam ESP32, inclding ensor support for GPS and battery, 433MHz support and several bugfixes

pull/42/head
platenspeler 2018-06-28 23:05:43 +02:00
rodzic e179c0a9d4
commit b59250e29b
22 zmienionych plików z 1180 dodań i 242 usunięć

Wyświetl plik

@ -1,6 +1,6 @@
# Single Channel LoRaWAN Gateway
Version 5.2.0, May 30, 2018
Version 5.3.1, June 30, 2018
Author: M. Westenberg (mw12554@hotmail.com)
Copyright: M. Westenberg (mw12554@hotmail.com)
@ -16,6 +16,23 @@ Maintained by Maarten Westenberg (mw12554@hotmail.com)
# Release Notes
New features in version 5.3.1 (June 30, 2018)
- Included support for T-Beam board including on board GPS sensor (_sensor.ino). #define GATEWAYNODE 1 will
turn the gateway into a node as well. Remember to set the address etc in ESP-sc-gway.h.
- First version to explore possibilities of 433 MHz LoRa frequencies.
Included _LFREQ setting in the ESP-sc-gway.h file
- Changes to the WiFi inplementation. The gateway does now store the SSID and poassword.
-
New features in version 5.3.0 (June 20, 2018)
- Connect to both public and private routers
New features in version 5.2.1 (June 6, 2018)
- Repair the downlink functions
- Repair sersor functions
- Bufgixes
New features in version 5.2.0 (May 30, 2018)
- Enable support for ESP32 from TTGO, several code changes where ESP32 differs from ESP8266.
OLED is supported but NOT tested. Some hardware specific reporting functions of the WebGUI do not work yet.

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1 H
// Date: 2018-06-06
// Version 5.3.1 H
// Date: 2018-06-30
//
// 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.2.1.H; 180606a"
#define VERSION "V.5.3.1.H; 180630a"
// This value of DEBUG determines whether some parts of code get compiled.
// Also this is the initial value of debug parameter.
@ -37,7 +37,11 @@
// This is usually a good idea if the webserver is interrupted halfway a writing
// operation.
// Normally, value 0 is a good default.
#define SPIFF_FORMAT 0
#define _SPIFF_FORMAT 0
// Define the LoRa Frequncy band that is used. TTN Supported values are 925MHz, 868MHz and 433MHz.
// So supported values are: 433 868 915
#define _LFREQ 868
// 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.
@ -78,8 +82,9 @@
// 1: HALLARD
// 2: COMRESULT pin out
// 3: ESP32 Wemos pin out
// 4: ESP32 TTGO pinning
// 5: Other, define your own in loraModem.h
// 4: ESP32 TTGO pinning (should work for 433 and OLED too).
// 5: ESP32 TTGO EU433 MHz with OLED
// 6: Other, define your own in loraModem.h
#define _PIN_OUT 1
// Gather statistics on sensor and Wifi status
@ -112,14 +117,6 @@
// getting WiFi SSID and password using WiFiManager)
#define AP_NAME "YourName"
#define AP_PASSWD "YourPassword"
// Defines whether the gateway will also report sensor/status value on MQTT
// after all, a gateway can be a node to the system as well
// Set its LoRa address and key below in this file
// See spec. para 4.3.2
#define GATEWAYNODE 0
#define _CHECK_MIC 0
// This section defines whether we use the gateway as a repeater
// For his, we use another output channle as the channel (default==0) we are
@ -185,8 +182,8 @@
// Port is UDP port in this program
//
// Default for testing: Switched off
#define _THINGPORT <port> // dash.westenberg.org:8057
#define _THINGSERVER "<dns.server.com>" // Server URL of the LoRa-udp.js handler
//#define _THINGPORT <port> // e.g. 1700
//#define _THINGSERVER "<dns.server.com>" // Server URL of the LoRa-udp.js handler
// Gateway Ident definitions
#define _DESCRIPTION "ESP Gateway" // Name of the gateway
@ -203,11 +200,22 @@
#define SECS_IN_HOUR 3600
#define NTP_INTR 0 // Do NTP processing with interrupts or in loop();
// Defines whether the gateway will also report sensor/status value on MQTT
// after all, a gateway can be a node to the system as well
// Set its LoRa address and key below in this file
// See spec. para 4.3.2
#define GATEWAYNODE 0
#define _CHECK_MIC 0
#if GATEWAYNODE==1
#define _DEVADDR { 0x26, 0x01, 0x01, 0x01 }
#define _APPSKEY { 0x00, 0x00, 0x00, 0x20, 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
// For ESP32 based TTGO boards these two are normally included
#define _GPS 0
#define _BATTERY 0
#endif
// Define the correct radio type that you are using

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
// Author: Maarten Westenberg (mw12554@hotmail.com)
//
// Based on work done by Thomas Telkamp for Raspberry PI 1-ch gateway and many others.
@ -102,12 +102,19 @@ extern "C" {
#include <ArduinoOTA.h>
#endif//OTA
#endif//PIN_OUT>=3
#endif//ESP_ARCH
uint8_t debug=1; // Debug level! 0 is no msgs, 1 normal, 2 extensive
uint8_t pdebug=0xFF; // Allow all atterns (departments)
#if GATEWAYNODE==1
#if _GPS==1
#include <TinyGPS++.h>
TinyGPSPlus gps;
HardwareSerial Serial1(1);
#endif
#endif
// You can switch webserver off if not necessary but probably better to leave it in.
#if A_SERVER==1
#if ESP32_ARCH==1
@ -467,6 +474,7 @@ int readUdp(int packetSize)
uint8_t buff[32]; // General buffer to use for UDP, set to 64
uint8_t buff_down[RX_BUFF_SIZE]; // Buffer for downstream
// if ((WiFi.status() != WL_CONNECTED) &&& (WlanConnect(10) < 0)) {
if (WlanConnect(10) < 0) {
#if DUSB>=1
Serial.print(F("readdUdp: ERROR connecting to WLAN"));
@ -962,12 +970,30 @@ void setup() {
Serial.begin(_BAUDRATE); // As fast as possible for bus
delay(100);
#if _GPS==1
// Pins are define in LoRaModem.h together with other pins
Serial1.begin(9600, SERIAL_8N1, GPS_TX, GPS_RX);// PIN 12-TX 15-RX
#endif
#ifdef ESP32
Serial.println(F("ESP32 defined"));
#if DUSB>=1
Serial.print(F("ESP32 defined, freq="));
#if _LFREQ==433
Serial.print(freqs[0]);
Serial.print(F(" EU433"));
#elif _LFREQ==868
Serial.print(freqs[0]);
Serial.print(F(" EU868"));
#endif
Serial.println();
#endif
#endif
#ifdef ARDUINO_ARCH_ESP32
#if DUSB>=1
Serial.println(F("ARDUINO_ARCH_ESP32 defined"));
#endif
#endif
#if DUSB>=1
Serial.flush();
@ -980,7 +1006,7 @@ void setup() {
else {
}
#endif
#if SPIFF_FORMAT>=1
#if _SPIFF_FORMAT>=1
#if DUSB>=1
if (( debug >= 0 ) && ( pdebug & P_MAIN )) {
Serial.println(F("Format Filesystem ... "));
@ -1016,7 +1042,8 @@ void setup() {
#endif
WiFi.mode(WIFI_STA);
WiFi.begin();
WiFi.setAutoConnect(true);
//WiFi.begin();
WlanReadWpa(); // Read the last Wifi settings from SPIFFS into memory
@ -1032,13 +1059,14 @@ void setup() {
// We start by connecting to a WiFi network, set hostname
char hostname[12];
// Setup WiFi UDP connection. Give it some time and retry 50 times..
while (WlanConnect(50) < 0) {
// Setup WiFi UDP connection. Give it some time and retry x times..
while (WlanConnect(0) <= 0) {
Serial.print(F("Error Wifi network connect "));
Serial.println();
yield();
}
// After there is a WiFi router connection, we can also set the hostname.
#if ESP32_ARCH==1
sprintf(hostname, "%s%02x%02x%02x", "esp32-", MAC_array[3], MAC_array[4], MAC_array[5]);
WiFi.setHostname( hostname );

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
@ -15,8 +15,109 @@
//
// This file contains the LoRa filesystem specific code
// ----------------------------------------------------------------------------
// WLANSTATUS prints the status of the Wlan.
// The status of the Wlan "connection" can change if we have no relation
// with the well known router anymore. Normally this relation is preserved
// but sometimes we have to reconfirm to the router again and we get the same
// address too.
// So, if the router is still in range we can "survive" with the same address
// and we may have to renew the "connection" from time to time.
// But when we loose the SSID connection, we may have to look for another router.
//
// Parameters: <none>
// Return value: Returns 1 when still WL_CONNETED, otherwise returns 0
// ----------------------------------------------------------------------------
int WlanStatus() {
switch (WiFi.status()) {
case WL_CONNECTED:
#if DUSB>=1
if (debug>=0) {
Serial.print(F("WlanCStatus:: CONNECTED to")); // 3
Serial.println(WiFi.SSID());
}
#endif
WiFi.setAutoReconnect(true); // Reconenct to this AP if DISCONNECTED
return(1);
break;
// In case we get disconnected from the AP we loose the IP address.
// The ESP is configured to reconnect to the last router in memory.
case WL_DISCONNECTED:
#if DUSB>=1
if (debug>=0) {
Serial.print(F("WlanStatus:: DISCONNECTED, IP=")); // 6
Serial.println(WiFi.localIP());
}
#endif
//while (! WiFi.isConnected() ) {
// Wait
delay(1);
//}
return(0);
break;
// When still pocessing
case WL_IDLE_STATUS:
#if DUSB>=1
if (debug>=0)
Serial.println(F("WlanStatus:: IDLE")); // 0
#endif
break;
// This code is generated as soonas the AP is out of range
// Whene detected, the program will search for a better AP in range
case WL_NO_SSID_AVAIL:
#if DUSB>=1
if (debug>=0)
Serial.println(F("WlanStatus:: NO SSID")); // 1
#endif
break;
case WL_CONNECT_FAILED:
#if DUSB>=1
if (debug>=0)
Serial.println(F("WlanStatus:: FAILED")); // 4
#endif
break;
// Never seen this code
case WL_SCAN_COMPLETED:
#if DUSB>=1
if (debug>=0)
Serial.println(F("WlanStatus:: SCAN COMPLETE")); // 2
#endif
break;
// Never seen this code
case WL_CONNECTION_LOST:
#if DUSB>=1
if (debug>=0)
Serial.println(F("WlanStatus:: LOST")); // 5
#endif
break;
// This code is generated for example when WiFi.begin() has not been called
// before accessing WiFi functions
case WL_NO_SHIELD:
#if DUSB>=1
if (debug>=0)
Serial.println(F("WlanStatus:: WL_NO_SHIELD")); //
#endif
break;
default:
#if DUSB>=1
if (debug>=0) {
Serial.print(F("WlanStatus:: code="));
Serial.println(WiFi.status()); // 255 means ERROR
}
#endif
break;
}
return(-1);
}
// ----------------------------------------------------------------------------
// config.txt is a text file that contains lines(!) with WPA configuration items
@ -63,13 +164,13 @@ int WlanReadWpa() {
// ----------------------------------------------------------------------------
// Print the WPA data of last WiFiManager to file
// Print the WPA data of last WiFiManager to the config file
// ----------------------------------------------------------------------------
#if WIFIMANAGER==1
int WlanWriteWpa( char* ssid, char *pass) {
#if DUSB>=1
if ( debug >=0 ) && ( pdebug & P_MAIN )) {
if (( debug >=0 ) && ( pdebug & P_MAIN )) {
Serial.print(F("WlanWriteWpa:: ssid="));
Serial.print(ssid);
Serial.print(F(", pass="));
@ -105,18 +206,22 @@ int WlanWriteWpa( char* ssid, char *pass) {
// the reconnect is done first thing. By default the system will reconnect to the
// samen SSID as it was connected to before.
// Parameters:
// int maxTry: Number of reties we do:
// 0: Try forever. Which is normally what we want except for Setup maybe
// int maxTry: Number of retries we do:
// 0: Used during Setup first CONNECT
// 1: Try once and if unsuccessful return(1);
// x: Try x times
//
// Returns:
// On failure: Return -1
// int number of retries necessary
// On connect: return 1
// On Disconnect state: return 0
//
// XXX After a few retries, the ESP8266 should be reset. Note: Switching between
// two SSID's does the trick. Rettrying the same SSID does not.
// Workaround is found below: Let the ESP8266 forget the SSID
// Workaround is found below: Let the ESP8266 forget the SSID
//
// NOTE: The Serial works only on debug setting and not on pdebug. This is
// because WiFi problems would make webserver (which works on WiFI) useless.
// ----------------------------------------------------------------------------
int WlanConnect(int maxTry) {
@ -126,130 +231,98 @@ int WlanConnect(int maxTry) {
unsigned char agains = 0;
unsigned char wpa_index = (WIFIMANAGER >0 ? 0 : 1); // Skip over first record for WiFiManager
// The initial setup() call is done with parameter 0
// We clear the WiFi memory and start with previous AP.
//
if (maxTry==0) {
Serial.println(F("WlanConnect:: Init para 0"));
WiFi.persistent(false);
WiFi.mode(WIFI_OFF); // this is a temporary line, to be removed after SDK update to 1.5.4
if (gwayConfig.ssid.length() >0) {
WiFi.begin(gwayConfig.ssid.c_str(), gwayConfig.pass.c_str());
delay(100);
}
}
// So try to connect to WLAN as long as we are not connected.
// The try parameters tells us how many times we try before giving up
// Value 0 is reserved for setup() first time connect
int i=0;
if (WiFi.status() == WL_CONNECTED) return(1);
// We try 5 times before giving up on connect
while ( (WiFi.status() != WL_CONNECTED) && ( i< maxTry ) )
while ( (WiFi.status() != WL_CONNECTED) && ( i<= maxTry ) )
{
// We try every SSID in wpa array until success
for (int j=wpa_index; (j< (sizeof(wpa)/sizeof(wpa[0]))) && (WiFi.status() != WL_CONNECTED ); j++)
{
// Start with well-known access points in the list
char *ssid = wpa[j].login;
char *password = wpa[j].passw;
// We try every SSID in wpa array until success
for (int j=wpa_index; (j< (sizeof(wpa)/sizeof(wpa[0]))) && (WiFi.status() != WL_CONNECTED ); j++)
{
// Start with well-known access points in the list
char *ssid = wpa[j].login;
char *password = wpa[j].passw;
#if DUSB>=1
Serial.print(i);
Serial.print(':');
Serial.print(j);
Serial.print(F(". WiFi connect SSID="));
Serial.print(ssid);
if (( debug>=1 ) && ( pdebug & P_MAIN )) {
Serial.print(F(", pass="));
Serial.print(password);
}
Serial.println();
if (debug>=0) {
Serial.print(i);
Serial.print(':');
Serial.print(j);
Serial.print(F(". WiFi connect SSID="));
Serial.print(ssid);
if ( debug>=1 ) {
Serial.print(F(", pass="));
Serial.print(password);
}
Serial.println();
}
#endif
// Count the number of times we call WiFi.begin
gwayConfig.wifis++;
// Count the number of times we call WiFi.begin
gwayConfig.wifis++;
//WiFi.disconnect();
delay(1000);
WiFi.persistent(false);
WiFi.mode(WIFI_OFF); // this is a temporary line, to be removed after SDK update to 1.5.4
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
delay(9000);
// We increase the time for connect but try the same SSID
// We try for 10 times
agains=1;
while (((WiFi.status()) != WL_CONNECTED) && (agains < 10)) {
agains++;
delay(agains*500);
#if DUSB>=1
if (( debug>=0 ) && ( pdebug & P_MAIN ))
Serial.print(".");
#endif
}
// Check the connection status again
switch (WiFi.status()) {
case WL_CONNECTED:
#if DUSB>=1
if (( debug>=0 ) && ( pdebug & P_MAIN ))
Serial.println(F("WlanConnect:: CONNECTED")); // 3
#endif
WiFi.mode(WIFI_STA);
delay(1000);
WiFi.begin(ssid, password);
delay(8000);
// Check the connection status again, return values
// 1 = CONNECTED
// 0 = DISCONNECTED (will reconnect)
// -1 = No SSID or other cause
int stat = WlanStatus();
if ( stat == 1) {
writeGwayCfg(CONFIGFILE); // XXX Write cnfiguration to SPIFFS
return(1);
break;
case WL_IDLE_STATUS:
}
// We increase the time for connect but try the same SSID
// We try for 10 times
agains=1;
while (((WiFi.status()) != WL_CONNECTED) && (agains < 10)) {
agains++;
delay(agains*500);
#if DUSB>=1
if (( debug>=0 ) && ( pdebug & P_MAIN ))
Serial.println(F("WlanConnect:: IDLE")); // 0
#endif
break;
case WL_NO_SSID_AVAIL:
#if DUSB>=1
Serial.println(F("WlanConnect:: NO SSID")); // 1
#endif
break;
case WL_CONNECT_FAILED:
#if DUSB>=1
if (( debug>=0 ) && ( pdebug & P_MAIN ))
Serial.println(F("WlanConnect:: FAILED")); // 4
#endif
break;
case WL_DISCONNECTED:
#if DUSB>=1
if (( debug>=0 ) && ( pdebug & P_MAIN ))
Serial.println(F("WlanConnect:: DISCONNECTED")); // 6
#endif
break;
case WL_SCAN_COMPLETED:
#if DUSB>=1
if (( debug>=0 ) && ( pdebug & P_MAIN ))
Serial.println(F("WlanConnect:: SCAN COMPLETE")); // 2
#endif
break;
case WL_CONNECTION_LOST:
#if DUSB>=1
if (( debug>=0 ) && ( pdebug & P_MAIN ))
Serial.println(F("WlanConnect:: LOST")); // 5
#endif
break;
default:
#if DUSB>=1
if (( debug>=0 ) && ( pdebug & P_MAIN )) {
Serial.print(F("WlanConnect:: code="));
Serial.println(WiFi.status());
if ( debug>=0 ) {
Serial.print(".");
}
#endif
break;
}
}
if ( WiFi.status() == WL_DISCONNECTED) return(0); // wait
} //for
i++; // Number of times we try to connect
// Make sure that we can connect to different AP's than 1
// this is a patch. Normally we connect to previous one.
WiFi.persistent(false);
WiFi.mode(WIFI_OFF); // this is a temporary line, to be removed after SDK update to 1.5.4
} //for next WPA defined AP
i++; // Number of times we try to connect
} //while
// It should not be possible to be here while WL_CONNECTed
if (WiFi.status() == WL_CONNECTED) {
#if DUSB>=1
if (( debug>=3 ) && ( pdebug & P_MAIN )) {
Serial.print(F("WLAN connected"));
Serial.println();
}
#endif
writeGwayCfg(CONFIGFILE);
return(1);
}
else {
// If we are not connected to a well known AP
// we can invoike WIFIMANAGER or else return unsuccessful.
if (WiFi.status() != WL_CONNECTED) {
#if WIFIMANAGER==1
#if DUSB>=1
Serial.println(F("Starting Access Point Mode"));
@ -273,7 +346,7 @@ int WlanConnect(int maxTry) {
WlanWriteWpa((char *)sta_conf.ssid, (char *)sta_conf.password);
#else
#if DUSB>=1
if (( debug>=0) && ( pdebug & P_MAIN )) {
if (debug>=0) {
Serial.println(F("WlanConnect:: Not connected after all"));
Serial.print(F("WLAN retry="));
Serial.print(i);
@ -281,13 +354,11 @@ int WlanConnect(int maxTry) {
Serial.print(WiFi.status() ); // Status. 3 is WL_CONNECTED
Serial.println();
}
#endif // DUSB
#endif// DUSB
return(-1);
#endif
}
yield();
return(1);
}

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017, 2018 Maarten Westenberg
// Version 5.2.1
// Date: 2018-06-03
// Version 5.3.1
// Date: 2018-06-30
//
// 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, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
@ -42,7 +42,7 @@ int readConfig(const char *fn, struct espGwayConfig *c) {
int tries = 0;
#if DUSB>=1
Serial.print(F("readConfig:: Starting "));
Serial.println(F("readConfig:: Starting "));
#endif
if (!SPIFFS.exists(fn)) {
#if DUSB>=1
@ -89,10 +89,10 @@ int readConfig(const char *fn, struct espGwayConfig *c) {
id_print(id, val);
(*c).ssid = val; // val contains ssid, we do NO check
}
//else if (id == "PASS") { // WiFi Password
// id_print(id, val);
// (*c).pass = val;
//}
else if (id == "PASS") { // WiFi Password
id_print(id, val);
(*c).pass = val;
}
else if (id == "CH") { // Frequency Channel
id_print(id,val);
(*c).ch = (uint32_t) val.toInt();
@ -195,7 +195,7 @@ int readConfig(const char *fn, struct espGwayConfig *c) {
int writeGwayCfg(const char *fn) {
gwayConfig.ssid = WiFi.SSID();
//gwayConfig.pass = WiFi.PASS(); // XXX We should find a way to store the password too
gwayConfig.pass = WiFi.psk(); // XXX We should find a way to store the password too
gwayConfig.ch = ifreq; // Frequency Index
gwayConfig.sf = (uint8_t) sf; // Spreading Factor
gwayConfig.debug = debug;

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
// 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, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
@ -21,9 +21,19 @@
#if OLED>=1
// ----------------------------------------------------------------
// Initilize the OLED functions.
//
// ----------------------------------------------------------------
void init_oLED()
{
#if defined OLED_RST
pinMode(OLED_RST,OUTPUT);
digitalWrite(OLED_RST, LOW); // low to reset OLED
delay(50);
digitalWrite(OLED_RST, HIGH); // must be high to turn on OLED
delay(50);
#endif
// Initialising the UI will init the display too.
display.init();
display.flipScreenVertically();
@ -33,26 +43,33 @@ void init_oLED()
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");
display.setFont(ArialMT_Plain_16);
display.drawString(0, 16, "READY, SSID=");
display.drawString(0, 32, WiFi.SSID());
#elif OLED==2
display.setFont(ArialMT_Plain_16);
display.drawString(0, 24, "READY");
display.drawString(0, 16, "READY, SSID=");
display.drawString(0, 32, WiFi.SSID());
#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);
@ -64,15 +81,14 @@ void msg_oLED(String tim, String sf) {
yield();
}
// ----------------------------------------------------------------
// Print the OLED address in use
//
// ----------------------------------------------------------------
void addr_oLED()
{
Serial.print(F("OLED_ADDR=0x"));
Serial.println(OLED_ADDR, HEX);
}

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
//
// 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, 2018 Maarten Westenberg
// Verison 5.2.1
// Date: 2018-06-06
// Verison 5.3.1
// Date: 2018-06-30
//
// 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, 2018 Maarten Westenberg
// Verison 5.2.1
// Date: 2018-06-06
// Verison 5.3.1
// Date: 2018-06-30
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the MIT License
@ -16,27 +16,33 @@
// Please specify the DevAddr and the AppSKey below (and on your LoRa backend).
// Also you will have to choose what sensors to forward to your application.
//
// Note: disable sensors not used in ESP-sc-gway.h
// - The GPS is included on TTGO T-Beam ESP32 boards by default.
// - The battery sensor works by connecting the VCC pin to A0 analog port
// ============================================================================
#if GATEWAYNODE==1
#include "LoRaCode.h"
#if _GPS==1
// Only used by GPS sensor code
//
static void smartDelay(unsigned long ms)
{
unsigned long start = millis();
do
{
while (Serial1.available())
gps.encode(Serial1.read());
} while (millis() - start < ms);
}
#endif
unsigned char DevAddr[4] = _DEVADDR ; // see ESP-sc-gway.h
// ----------------------------------------------------------------------------
// XXX Experimental Read Internal Sensors
//
// You can monitor some settings of the RFM95/sx1276 chip. For example the temperature
// which is set in REGTEMP in FSK mode (not in LORA). Or the battery value.
// Find some sensible sensor values for LoRa radio and read them below in separate function
//
// ----------------------------------------------------------------------------
//uint8_t readInternal(uint8_t reg) {
//
// return 0;
//}
// ----------------------------------------------------------------------------
// LoRaSensors() is a function that puts sensor values in the MACPayload and
// sends these values up to the server. For the server it is impossible to know
@ -46,25 +52,79 @@ unsigned char DevAddr[4] = _DEVADDR ; // see ESP-sc-gway.h
// of-course you can add any byte string you wish
//
// Parameters:
// - buf: contains the buffer to put the sensor values in
// - buf: contains the buffer to put the sensor values in (max==xx);
// Returns:
// - The amount of sensor characters put in the buffer
// - The amount of sensor characters put in the buffer
//
// NOTE: The code in LoRaSensors() is provided as an example only.
// The amount of sensor values as well as their message layout may differ
// for each implementation.
// Also, the message format used by this gateway is LoraCode, a message format
// developed by me for sensor values. Each value is uniquely coded with an
// id and a value, and the total message contains its length (less than 64 bytes)
// and a parity value in byte[0] bit 7.
// ----------------------------------------------------------------------------
static int LoRaSensors(uint8_t *buf) {
uint8_t internalSersors;
//internalSersors = readInternal(0x1A);
//if (internalSersors > 0) {
// return (internalSersors);
//}
uint8_t tchars = 1;
buf[0] = 0x86; // 134; User code <lCode + len==3 + Parity
buf[1] = 0x80; // 128; lCode code <battery>
buf[2] = 0x3F; // 63; lCode code <value>
// Parity = buf[0]==1 buf[1]=1 buf[2]=0 ==> even, so last bit of first byte must be 0
return(3); // return the number of bytes added to payload
#if _BATTERY==1
#if DUSB>=1
if (debug>=0)
Serial.println(F("LoRaSensors:: Battery"));
#endif
buf[tchars] = 0x80; // 128; lCode code <battery>
buf[tchars+1] = 0x3F; // 63; lCode code <value>/30
tchars+=2;
#endif
#if _GPS==1
#if DUSB>=1
if (debug>=0)
Serial.println(F("LoRaSensors:: GPS"));
if (debug>=1) {
Serial.print("Latitude : ");
Serial.println(gps.location.lat(), 5);
Serial.print("Longitude : ");
Serial.println(gps.location.lng(), 4);
Serial.print("Satellites: ");
Serial.println(gps.satellites.value());
Serial.print("Altitude : ");
Serial.print(gps.altitude.feet() / 3.2808);
Serial.println("M");
Serial.print("Time : ");
Serial.print(gps.time.hour());
Serial.print(":");
Serial.print(gps.time.minute());
Serial.print(":");
Serial.println(gps.time.second());
}
#endif
smartDelay(1000);
if (millis() > 5000 && gps.charsProcessed() < 10) {
#if DUSB>=1
Serial.println(F("No GPS data received: check wiring"));
#endif
return(0);
}
// Assuming we have a value, put it in the buf
// The layout of thi message is specific to the user,
// so adapt as needed.
tchars += lcode.eGpsL(gps.location.lat(), gps.location.lng(), gps.altitude.value(),
gps.satellites.value(), buf + tchars);
#endif
// If all sensor data is encoded, we encode the buffer
lcode.eMsg(buf, tchars); // Fill byte 0 with bytecount and Parity
return(tchars); // return the number of bytes added to payload
}
@ -395,7 +455,7 @@ static void checkMic(uint8_t *buf, uint8_t len, uint8_t *key) {
// ( MHDR | ( FHDR | FPORT | FRMPAYLOAD ) | MIC )
//
// This function makes the totalpackage and calculates MIC
// Te maximum size of the message is: 12 + ( 9 + 2 + 64 ) + 4
// The maximum size of the message is: 12 + ( 9 + 2 + 64 ) + 4
// So message size should be lass than 128 bytes if Payload is limited to 64 bytes.
//
// return value:
@ -411,7 +471,7 @@ int sensorPacket() {
struct LoraUp LUP;
// Init the other LoraUp fields
LUP.sf = 9;
LUP.sf = 8; // Send with SF8
LUP.prssi = -50;
LUP.rssicorr = 139;
LUP.snr = 0;

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
@ -157,7 +157,6 @@ void stateMachine()
Serial.print(F("DONE :: "));
SerialStat(intr);
}
#endif
eventTime=micros(); // reset the timer on timeout
doneTime=micros(); // reset the timer on timeout

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
// 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, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-31
//
// 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, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
// based on work done by many people and making use of several libraries.
//

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
// 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, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many other contributors.
@ -58,9 +58,14 @@ long txDelay= 0x00; // tx delay time on top of server TMST
// Frequencies
// Set center frequency. If in doubt, choose the first one, comment all others
// Each "real" gateway should support the first 3 frequencies according to LoRa spec.
// NOTE: This means you have to specify at least 3 frequencies here for the single
// channel gateway to work.
#if _LFREQ==868
// This the the EU868 format as used in most of Europe
// It is also the default for most of the single channel gateway work.
int freqs [] = {
868100000, // Channel 0, 868.1 MHz primary
868100000, // Channel 0, 868.1 MHz/125 primary
868300000, // Channel 1, 868.3 MHz mandatory
868500000, // Channel 2, 868.5 MHz mandatory
867100000, // Channel 3, 867.1 MHz Optional
@ -68,10 +73,48 @@ int freqs [] = {
867500000, // Channel 5, 867.5 MHz Optional
867700000, // Channel 6, 867.7 MHz Optional
867900000, // Channel 7, 867.9 MHz Optional
868800000, // Channel 8, 868.9 MHz Optional
869525000 // Channel 9, 869.5 MHz for responses gateway (10%)
868800000, // Channel 8, 868.9 MHz/125 Optional
869525000 // Channel 9, 869.5 MHz/125 for RX2 responses SF9(10%)
// TTN defines an additional channel at 869.525Mhz using SF9 for class B. Not used
};
#elif _LFREQ==433
// The following 3 frequencies should be defined/used in an EU433
// environment.
int freqs [] = {
433175000, // Channel 0, 433.175 MHz/125 primary
433375000, // Channel 1, 433.375 MHz primary
433575000, // Channel 2, 433.575 MHz primary
433775000, // Channel 3, 433.775 MHz primary
433975000, // Channel 4, 433.975 MHz primary
434175000, // Channel 5, 434.175 MHz primary
434375000, // Channel 6, 434.375 MHz primary
434575000, // Channel 7, 434.575 MHz primary
434775000 // Channel 8, 434.775 MHz primary
};
#elif _LFREQ==915
// US902=928
// AU915-928
int freqs [] = {
// Uplink
903900000, // Channel 0, SF7BW125 to SF10BW125 primary
904100000, // Ch 1, SF7BW125 to SF10BW125
904300000, // Ch 2, SF7BW125 to SF10BW125
904500000, // Ch 3, SF7BW125 to SF10BW125
904700000, // Ch 4, SF7BW125 to SF10BW125
904900000, // Ch 5, SF7BW125 to SF10BW125
905100000, // Ch 6, SF7BW125 to SF10BW125
905100000, // Ch 7, SF7BW125 to SF10BW125
904600000 // Ch 8, SF8BW500
// Downlink
// We should specify downlink frequencies here
// SFxxxBW500
};
#else
int freqs [] = {
// Print an Error, Not supported
#error "Sorry, but your frequency plan is not supported"
};
#endif
uint32_t freq = freqs[0];
uint8_t ifreq = 0; // Channel Index
@ -101,7 +144,7 @@ unsigned long detTime=0;
#if _PIN_OUT==1
// ----------------------------------------------------------------------------
// HALLARD Definition of the GPIO pins for Hallard type boards
// 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
@ -116,7 +159,7 @@ struct pins {
#elif _PIN_OUT==2
// ----------------------------------------------------------------------------
// COMRESULT gateway PCB use the following settings
// For ComResult gateway PCB use the following settings
struct pins {
uint8_t dio0=5; // GPIO5 / D1. Dio0 used for one frequency and one SF
uint8_t dio1=4; // GPIO4 / D2. Used for CAD, may or not be shared with DIO0
@ -139,13 +182,13 @@ struct pins {
uint8_t dio1=26; // GPIO26 / Used for CAD, may or not be shared with DIO0
uint8_t dio2=26; // GPI2O6 / Used for frequency hopping, don't care
uint8_t ss=18; // GPIO18 / Dx. Select pin connected to GPIO18
uint8_t rst=14; // GPIO14 / D3. Reset pin not used
uint8_t rst=14; // GPIO0 / D3. Reset pin not used
} pins;
#elif _PIN_OUT==4
// ----------------------------------------------------------------------------
// ESP32/TTGO based board
// For ESP32/TTGO based board.
// SCK == GPIO5/ PIN5
// SS == GPIO18/PIN18 CS
// MISO == GPIO19/ PIN19
@ -153,21 +196,25 @@ struct pins {
// RST == GPIO14/ PIN14
struct pins {
uint8_t dio0=26; // GPIO26 / Dio0 used for one frequency and one SF
uint8_t dio1=33; // GPIO33 / Used for CAD, may or not be shared with DIO0
uint8_t dio2=32; // GPIO32 / Used for frequency hopping, don't care
uint8_t dio1=33; // GPIO26 / Used for CAD, may or not be shared with DIO0
uint8_t dio2=32; // GPIO26 / Used for frequency hopping, don't care
uint8_t ss=18; // GPIO18 / Dx. Select pin connected to GPIO18
uint8_t rst=14; // GPIO14 / D3. Reset pin not used
uint8_t rst=14; // GPIO0 / D3. Reset pin not used
} pins;
#define SCK 5
#define MISO 19
#define MOSI 27
#define RST 14
#define SS 18
#define GPS_RX 15
#define GPS_TX 12
#elif _PIN_OUT==5
// ----------------------------------------------------------------------------
// ESP32/TTGO based board, with onboard battery and GPS(!)
// For ESP32/TTGO based board for EU32 with 0.9" OLED
// NOTE: This board shoudl be same as general type TTGO (nr 4)
// but for the moment we include this as a separate item
//
// SCK == GPIO5/ PIN5
// SS == GPIO18/PIN18 CS
// MISO == GPIO19/ PIN19
@ -175,15 +222,15 @@ struct pins {
// RST == GPIO14/ PIN14
struct pins {
uint8_t dio0=26; // GPIO26 / Dio0 used for one frequency and one SF
uint8_t dio1=33; // GPIO33 / Used for CAD, not be shared with DIO0, NOT CONNECTED BY DEFAULT
uint8_t dio2=32; // GPIO32 / Used for frequency hopping, don't care
uint8_t dio1=33; // GPIO26 / Used for CAD, may or not be shared with DIO0
uint8_t dio2=32; // GPIO26 / Used for frequency hopping, don't care
uint8_t ss=18; // GPIO18 / Dx. Select pin connected to GPIO18
uint8_t rst=14; // GPIO14 / D3. Reset pin not used
uint8_t rst=14; // GPIO0 / D3. Reset pin not used
} pins;
#define SCK 5
#define MISO 19
#define MOSI 27
#define RST 14
#define SCK 5 // Check
#define MISO 19 // Check
#define MOSI 27 // Check
#define RST 14 // Check
#define SS 18
#else

Wyświetl plik

@ -1,7 +1,7 @@
// 1-channel LoRa Gateway for ESP8266
// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
// Version 5.2.1
// Date: 2018-06-06
// Version 5.3.1
// Date: 2018-06-30
//
// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
// and many others.
@ -29,7 +29,7 @@
#if OLED>=1 // If OLED is used
// --------------------------------------------------------
// Define the diffretn PIN's used for SCL/SDA for each arch.
// Define the different PIN's used for SCL/SDA for each arch.
//
#if _PIN_OUT==1 // HALLARD
#define OLED_SCL 5 // GPIO5 / D1
@ -40,8 +40,9 @@
#define OLED_SDA 2 // GPIO2 / D4
#elif _PIN_OUT==4 // TTGO (onboard version used, also for DIY)
#define OLED_SCL 5 // GPIO5 / D1
#define OLED_SDA 4 // GPIO4 / D2
#define OLED_SCL 15 // GPIO15 /
#define OLED_SDA 4 // GPIO4 /
#define OLED_RST 16
#endif

Wyświetl plik

@ -1,6 +1,6 @@
# Single Channel LoRaWAN Gateway
Version 5.2.1, June 06, 2018
Version 5.3.1, June 30, 2018
Author: M. Westenberg (mw12554@hotmail.com)
Copyright: M. Westenberg (mw12554@hotmail.com)
@ -15,18 +15,20 @@ Maintained by Maarten Westenberg (mw12554@hotmail.com)
# Description
First of all: PLEASE READ THIS FILE AND HTTP://THINGS4U.GITHUB.IO it should contain most of the information you need to get going.
First of all: PLEASE READ THIS FILE AND HTTP://THINGS4U.GITHUB.IO it should contain most of the
information you need to get going.
Unfortunately I do not have the time to follow up on all emails, and as most information including pin-outs
etc etc are contained on these pages I hope you have the time to read them before posting any questions.
I do have more than 10 Wemos D1 mini boards running, some I built myself,
some 10+ on Hallard, 3 on ComResult and 2 ESP32 boards. They ALL work without problems.
some 10+ on Hallard, 3 on ComResult and 4 ESP32 boards. They ALL work without problems
on this code.
I did find however that good soldering joints and wiring makes all the difference,
so if you get resets you cannot explain, please have a second look at your wiring.
This repository contains a proof-of-concept implementation of a single channel LoRaWAN gateway for the ESP8266.
Starting version 5.2 also the ESP32 of TTGO (and others) is supported.
The software implements a standard LoRa gateway with the following exceptions on changes:
The software implements a standard LoRa gateway with the following exceptions and changes:
- 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.

Wyświetl plik

@ -0,0 +1,581 @@
// LoRa encoding and decoding functions
// Copyright (c) 2016, 2017 Maarten Westenberg (mw12554@hotmail.com)
// Version 1.2.0
// Date: 2016-10-23
// Version 1.2.0 on April 20, 2017
//
// 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
//
// The protocols used in this code:
// 1. LoRA Specification version V1.0 and V1.1 for Gateway-Node communication
//
// 2. Semtech Basic communication protocol between Lora gateway and server version 3.0.0
// https://github.com/Lora-net/packet_forwarder/blob/master/PROTOCOL.TXT
//
// Notes:
// The lCode specification is documented on a sparate page on github.
//
// Todo:
// The luminescense is read as a 16-bit value in the library and converted to
// a float value in order to get proper scaling etc. In the lCode lib it is
// coded as a 2-byte value over the air, which might be incorrect for lux values
// over 650 lux (which IS posible since bright daylight has more than 1000 lux).
// So XXX we have to add another byte to cover values above 65
// ----------------------------------------------------------------------------------------
#define DEBUG 0
#if defined(__AVR__)
#include <avr/pgmspace.h>
#include <Arduino.h>
#include <Battery.h>
#elif defined(ARDUINO_ARCH_ESP8266) | defined(ESP32)
#include <ESP.h>
#elif defined(__MKL26Z64__)
#include <Arduino.h>
#else
#error Unknown architecture in aes.cpp
#endif
#include "LoRaCode.h"
int ldebug=DEBUG;
// --------------------------------------------------------------------------------
// Encode Temperature.
// We use 2 bytes for temperature, first contains the integer partial_sort.
// We add 100 so we effectively will measure temperatures between -100 and 154 degrees.
// Second byte contains the fractional part in 2 decimals (00-99).
// --------------------------------------------------------------------------------
int LoRaCode::eTemperature(float val, byte *msg) {
int len=0;
byte i= (byte) (val+100); // Integer part
byte f= (byte) ( ((float)(val - (float)i -100)) * 100); // decimal part
msg[len++] = ((byte)O_TEMP << 2) | 0x01; // Last 2 bits are 0x01, two bytes
msg[len++] = i;
msg[len++] = f;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Temperature "));
Serial.print(i-100); // For readibility
Serial.print(".");
Serial.println(f);
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// Code Humidity in the *msg
// The humidity of the sensor is between 0-100%, we encode this value * 2 so that
// byte value is betwene 0 and 199.
// --------------------------------------------------------------------------------
int LoRaCode::eHumidity(float val, byte *msg) {
int len=0;
byte i = (byte) ((float)val *2); // Value times 2
msg[len++] = ((byte)O_HUMI << 2) | 0x00; // Last 2 bits are 0x00, one byte
msg[len++] = i; //
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Humidity "));
Serial.println((float)(i/2)); // For readibility
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// Code the Airpressure.
// The coded value is the measured value minus 900. This gives an airpressure
// range from 850-1104
// --------------------------------------------------------------------------------
int LoRaCode::eAirpressure(float val, byte *msg) {
int len=0;
byte i = (byte) ((float)val -850); // Value times 2
msg[len++] = ((byte)O_AIRP << 2) | 0x00; // Last 2 bits are 0x00, one byte
msg[len++] = i; //
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Airpressure "));
Serial.println((float)(i + 850)); // For readibility
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// GPS lat and lng encoding
// There are several ways to encode GPS data. Eother by mapping on 3 or 4 bytes of
// Floating, or by just multiplying with 1,000,000 for example (gives 6 digits
// precision which is enough for almost all applications)
// --------------------------------------------------------------------------------
int LoRaCode::eGps(double lat, double lng, byte *msg) {
#if DEBUG>0
if (ldebug>=1) {
Serial.print(F(" LAT: ")); Serial.print(lat,6);
Serial.print(F(" LNG: ")); Serial.println(lng,6);
}
#endif
int len=0;
long factor = 1000000;
msg[len++] = ((byte)O_GPS << 2) | 0x00; // Last 2 bits are 0x00, don't care
const long calculatedLat = (long)(lat * factor);
msg[len++] = (calculatedLat >> (8*3)) & 0xFF;
msg[len++] = (calculatedLat >> (8*2)) & 0xFF;
msg[len++] = (calculatedLat >> (8*1)) & 0xFF;
msg[len++] = (calculatedLat >> (8*0)) & 0xFF;
const long calculatedLng = (long)(lng * factor);
msg[len++] = (calculatedLng >> (8*3)) & 0xFF;
msg[len++] = (calculatedLng >> (8*2)) & 0xFF;
msg[len++] = (calculatedLng >> (8*1)) & 0xFF;
msg[len++] = (calculatedLng >> (8*0)) & 0xFF;
return(len);
}
// --------------------------------------------------------------------------------
// Code the extended GPS format
// latitude and longitude are coded just like the short format
// Altitude is a long containing the altitude in cm
// --------------------------------------------------------------------------------
int LoRaCode::eGpsL(double lat, double lng, long alt, int sat,
byte *msg) {
if (ldebug>=1) {
Serial.print(F(" LAT: ")); Serial.print(lat,6);
Serial.print(F(" LNG: ")); Serial.print(lng,6);
Serial.print(F(" ALT: ")); Serial.print(alt/100);
Serial.print(F(" SAT: ")); Serial.println(sat);
}
int len=0;
long factor = 1000000;
msg[len++] = ((byte)O_GPSL << 2) | 0x00; // Last 2 bits are 0x00, don't care
const long calculatedLat = (long)(lat * factor);
msg[len++] = (calculatedLat >> (8*3)) & 0xFF;
msg[len++] = (calculatedLat >> (8*2)) & 0xFF;
msg[len++] = (calculatedLat >> (8*1)) & 0xFF;
msg[len++] = (calculatedLat >> (8*0)) & 0xFF;
const long calculatedLng = (long)(lng * factor);
msg[len++] = (calculatedLng >> (8*3)) & 0xFF;
msg[len++] = (calculatedLng >> (8*2)) & 0xFF;
msg[len++] = (calculatedLng >> (8*1)) & 0xFF;
msg[len++] = (calculatedLng >> (8*0)) & 0xFF;
const long calculatedAlt = (long)(alt); // Altitude is integer specified in cm (converted later to m)
msg[len++] = (calculatedAlt >> (8*3)) & 0xFF;
msg[len++] = (calculatedAlt >> (8*2)) & 0xFF;
msg[len++] = (calculatedAlt >> (8*1)) & 0xFF;
msg[len++] = (calculatedAlt >> (8*0)) & 0xFF;
msg[len++] = sat & 0xFF; // Positive number, assumed always to be less than 255
return(len);
}
// --------------------------------------------------------------------------------
// Encode the 1-bit PIR value
// --------------------------------------------------------------------------------
int LoRaCode::ePir(int val, byte *msg) {
int i = (byte) ( val );
int len=0; //
msg[len++] = (O_PIR << 2) | 0x00; // Last 2 bits are 0x00, one byte
msg[len++] = i;
return(len);
}
// --------------------------------------------------------------------------------
// Encode Airquality Value.
// Airquality through SDS011 sensors is measured with two float values
// and read as a 10-bit value (0-1024). We use a 2 x 2-byte value
// --------------------------------------------------------------------------------
int LoRaCode::eAirquality(int pm25, int pm10, byte *msg) {
int len=0;
uint16_t val = (uint16_t) (pm25);
msg[len++] = (O_AQ << 2) | 0x03; // Last 2 bits are 0x03, so 4 bytes data
msg[len++] = (val >> 8) & 0xFF;
msg[len++] = val & 0xFF;
val = (uint16_t) (pm10);
msg[len++] = (val >> 8) & 0xFF;
msg[len++] = val & 0xFF;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Airquality <"));
Serial.print(len);
Serial.print(F("> "));
Serial.println(val); // For readibility
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// Encode a Multi-Button sensor. This sensor is a concentrator that receives
// several sensor values over 433MHz and retransmits them over LoRa
// The LoRa sensor node contains the main address (LoRa id), the concentrated
// sendors have their own unique address/channel combination.
// --------------------------------------------------------------------------------
int LoRaCode::eMbuttons(byte val, unsigned long address, unsigned short channel, byte *msg) {
int len=0;
msg[len++] = (O_MB << 2) | 0x00; // Several bytes, do not care
msg[len++] = val; // First code the value
msg[len++] = (address >> (8*3)) & 0xFF; // Address
msg[len++] = (address >> (8*2)) & 0xFF;
msg[len++] = (address >> (8*1)) & 0xFF;
msg[len++] = (address >> (8*0)) & 0xFF;
msg[len++] = (channel >> (8*1)) & 0xFF; // Channel
msg[len++] = (channel >> (8*0)) & 0xFF;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Multi-Button "));
Serial.println(val); // For readibility
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// Moisture detection with two metal sensors in fluid or soil
//
// --------------------------------------------------------------------------------
int LoRaCode::eMoist(int val, byte *msg) {
int len=0;
msg[len++] = (O_MOIST << 2) | 0x00; // Last 2 bits are 0x00, 1 byte
msg[len++] = (val / 4 ) & 0xFF;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Moisture "));
Serial.println(val); // For readibility
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// Luminescense detection with two metal sensors in fluid or soil
// The value to be decoded is 2-byte value which gives
// an integer resolution from 0 to 65535
// It is possible to add a third byte for extra resolution (2 decimals).
// --------------------------------------------------------------------------------
int LoRaCode::eLuminescense(float val, byte *msg) {
int len=0;
uint16_t lux = (uint16_t) (val);
// now determine the fraction for a third byte
msg[len++] = (O_LUMI << 2) | 0x01; // Last 2 bits are 0x00, 2 bytes resolution
msg[len++] = (lux >> (8*1)) & 0xFF; // LSB
msg[len++] = (lux >> (8*0)) & 0xFF;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Lumi "));
Serial.println(val); // For readibility
}
#endif
return(len);
}
int LoRaCode::eLuminescenseL(float val, byte *msg) {
int len=0;
uint16_t lux = (uint16_t) (val);
// now determine the fraction for a third byte
uint8_t frac = (uint8_t) ((val-lux) * 100);
msg[len++] = (O_LUMI << 2) | 0x02; // Last 2 bits are 0x00, 3 bytes resolution
msg[len++] = (lux >> (8*1)) & 0xFF; // LSB
msg[len++] = (lux >> (8*0)) & 0xFF;
msg[len++] = (frac) & 0xFF;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Lumi L="));
Serial.print(lux); // For readibility
Serial.print('.');
Serial.println(frac); // For readibility
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// Distance detection
// In this case we use 2 bytes for 0-65535 mm (which is more than we need)
// NOTE: The sensor will report in mm resolution, but the server side
// will decode in cm resolution with one decimal fraction (if available)
// --------------------------------------------------------------------------------
int LoRaCode::eDistance(int val, byte *msg) {
int len=0;
msg[len++] = (O_DIST << 2) | 0x01; // Last 2 bits are 0x00, 2 bytes resolution
msg[len++] = (val >> (8*1)) & 0xFF; // LSB
msg[len++] = (val >> (8*0)) & 0xFF;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Dist "));
Serial.println(val); // For readibility
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// Encode Gas concentration Value.
// Airquality through MQxx or AQxx sensors is measured through the Analog port
// and read as a 10-bit value (0-1024). We use a 2-byte value
// In order to tell the server which sensor is reporting (we can have several)
// --------------------------------------------------------------------------------
int LoRaCode::eGas(int val, byte *msg) {
int len=0;
msg[len++] = (O_GAS << 2) | 0x01; // Last 2 bits are 0x01, 2 bytes
msg[len++] = (val >> 8) & 0xFF;
msg[len++] = val & 0xFF;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Gas "));
Serial.println(val); // For readibility
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// Encode Battery Value.
// Battery Voltage is between 2 and 4 Volts (sometimes 12V)
// We therefore multiply by 20, reported values are between 1 and 255, so
// sensor values between 0,05 and 12.75 Volts
// --------------------------------------------------------------------------------
int LoRaCode::eBattery(float val, byte *msg) {
int len=0;
int i = (byte) ((float)val *20); // Value times 20
msg[len++] = (O_BATT << 2) | 0x00; // Last 2 bits are 0x00, one byte
msg[len++] = i;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Battery "));
Serial.println((float)(i/20)); // For readibility
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// Encode AD Converter value for pin adc0 and adv1
// Battery Voltage is between 2 and 4 Volts (sometimes 12V)
// We therefore multiply by 20, reported values are between 1 and 255, so
// sensor values between 0,05 and 12.75 Volts
// --------------------------------------------------------------------------------
int LoRaCode::eAdc0(int val, byte *msg) {
int len=0;
msg[len++] = (O_ADC0 << 2) | 0x00; // Last 2 bits are 0x00, 1 byte
msg[len++] = (val / 4 ) & 0xFF;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Adc0 "));
Serial.println(val); // For readibility
}
#endif
return(len);
}
int LoRaCode::eAdc1(int val, byte *msg) {
int len=0;
msg[len++] = (O_ADC1 << 2) | 0x00; // Last 2 bits are 0x00, 1 byte
msg[len++] = (val / 4 ) & 0xFF;
#if DEBUG>0
if (ldebug >=1) {
Serial.print(F("lcode:: Add Adc1 "));
Serial.println(val); // For readibility
}
#endif
return(len);
}
// --------------------------------------------------------------------------------
// Function to encode the sensor values to the Payload message
// Input: Opcode and Value
// Output: msg
// return: Number of bytes added to msg
// --------------------------------------------------------------------------------
int LoRaCode::eVal (int opcode, byte *val, byte *msg) {
int len=0;
switch (opcode) {
case O_TEMP: // Temperature
len += eTemperature((float) *val, msg);
break;
case O_HUMI: // Humidity
len += eHumidity((float) *val, msg);
break;
case O_AIRP: // Airpressure
len += eAirpressure((float) *val, msg);
break;
case O_GPS: // GPS short Info
//len += eGps(lat, lng, msg);
break;
case O_PIR: // PIR
len += ePir((int) *val, msg);
break;
case O_MOIST: // Moisture
len += eMoist((int) *val, msg);
break;
case O_LUMI: // Luminescense
len += eLuminescense((int) *val, msg);
break;
case O_BATT: // Battery
len += eBattery((float) *val, msg);
break;
default:
#if DEBUG>0
Serial.println("lCode:: Error opcode not known");
#endif
break;
}
return(len);
}
// --------------------------------------------------------------------------------
// Function to encode the Payload message (final step)
// This function will modify the first byte of the string to encode the length
// and the parity. Payload begins at position 1, byte 0 is reserved
// --------------------------------------------------------------------------------
bool LoRaCode::eMsg (byte * msg, int len) {
unsigned char par = 0;
if (len > 64) {
#if DEBUG>0
Serial.println("lCodeMsg:: Error, string to long");
#endif
return(false);
}
#if DEBUG>0
if (ldebug>=1) {Serial.print(F("LCodeMsg:: <")); Serial.print(len); Serial.print(F("> ")); }
#endif
// else we probably have a good message
msg[0] = ( len << 1 ) | (0x80);
// Now we have to calculate the Parity for the message
for (int i=0; i< len; i++) {
byte cc = msg[i];
par ^= cc;
}
// Now we have par as one byte and need to XOR the bits of that byte.
unsigned char pp = 8; // width of byte in #bits
while (pp > 1) {
par ^= par >> (pp/2);
pp -= pp/2;
}
if (par & 0x01) { // if odd number of 1's in string
#if DEBUG>0
if (ldebug>=1) Serial.print(F(" odd "));
#endif
msg[0] |= 0x01; // Add another 1 to make parity even
}
else {
#if DEBUG>0
if (ldebug>=1) Serial.print(F(" even "));
#endif
}
return(true);
}
// --------------------------------------------------------------------------------
// Print an encoded string
// --------------------------------------------------------------------------------
void LoRaCode::lPrint (byte *msg, int len) {
Serial.print(F("lCode: "));
for (int i=0; i< len; i++) {
if (msg[i]<10) Serial.print('0');
Serial.print(msg[i],HEX);
Serial.print(" ");
}
Serial.println();
}
// --------------------------------------------------------------------------------
// Decode first byte of message containing length
// --------------------------------------------------------------------------------
int LoRaCode::dLen (byte *msg) {
if ( ! (msg[0] & 0x80 )) return(-1); // Error
int len = msg[0] & 0x7F; // Blank out first bit which is always 1 with LoRaCode
return (len >> 1);
}
// --------------------------------------------------------------------------------
// Decode message. One function for all, always returns a float
// We expect that the buffer is large enough and will contain all bytes required
// by that specific encoding
// XXX This function needs finishig (never used at the moment)
// PARAMETERS:
// msg: Contains the message as a byte string
// val: contains the decoded value as a float (xxx)
// mode: Contains the opcode of the decoded command
// RETURN:
// The number of bytes read from the buffer
// --------------------------------------------------------------------------------
int LoRaCode::dMsg (byte *msg, byte *val, byte *mode) {
float res;
byte len = msg[0] & 0x03; // Last 2 bits
*mode = (byte) (msg[0] >> 2);
switch (*mode) {
case O_TEMP:
*val = (float) msg[1]- 100 + ( (float) msg[2] / 100);
return(3);
break;
case O_HUMI:
*val = (float) msg[1] / 2;
return(2);
break;
case O_AIRP:
*val = 0;
return(2);
break;
case O_GPS: // Returning one float does not work for GPS. function never used
*val = 0;
return(9);
break;
case O_GPSL:
*val = 0;
return(14);
break;
case O_PIR:
*val = msg[1];
return(2);
break;
case O_AQ:
*val = 0;
return(3);
break;
case O_BATT:
*val = (float) msg[1] / 20;
return(2);
break;
case O_STAT:
*val = 0;
return(1);
case O_1CH:
*val = msg[1];
return(2);
break;
case O_SF:
*val = msg[1];
return(2);
break;
case O_TIM: // Timing of the wait cyclus
val[0] = msg[1];
val[1] = msg[2];
return(3); // total lengt 2 bytes data + 1 byte opcode
break;
default:
return(0);
}
return (0);
}
// Variable declaration
LoRaCode lcode;

Wyświetl plik

@ -0,0 +1,103 @@
// LoRa encoding and decoding functions
// Copyright (c) 2016 Maarten Westenberg
// Version 1.1.0
// Date: 2016-10-23
//
// 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
//
// The protocols used in this code:
// 1. LoRA Specification version V1.0 and V1.1 for Gateway-Node communication
//
// 2. Semtech Basic communication protocol between Lora gateway and server version 3.0.0
// https://github.com/Lora-net/packet_forwarder/blob/master/PROTOCOL.TXT
//
// Notes:
// The lCode specification is documented on a sparate page on github.
//
// Todo:
// The luminescense is read as a 16-bit value in the library and converted to
// a float value in order to get proper scaling etc. In the lCode lib it is
// coded as a 2-byte value over the air, which might be incorrect for lux values
// over 650 lux (which IS posible since bright daylight has more than 1000 lux).
// So XXX we have to add another byte to cover values above 65
// ----------------------------------------------------------------------------------------
#ifndef LoRaCode_h
#define LoRaCode_h
// Op Codes
#define O_TEMP 0x01 // Temperature is a one-byte code
#define O_HUMI 0x02 // Humidity is a one-byte code
#define O_AIRP 0x03 // Air pressure is a one-byte code
#define O_GPS 0x04 // Short version: ONLY 3 bytes LAT and 3 bytes LONG
#define O_GPSL 0x05 // Long GPS
#define O_PIR 0x06 // Movement, 1 bit (=1 byte)
#define O_AQ 0x07 // Airquality
#define O_RTC 0x08 // Real Time Clock
#define O_COMPASS 0x09 // Compass
#define O_MB 0x0A // Multi Sensors 433
#define O_MOIST 0x0B // Moisture is one-byte
#define O_LUMI 0x0C // Luminescense u16
#define O_DIST 0x0D // Distance is 2-byte
#define O_GAS 0x0E // GAS
// 0x0F
// 0x10 // 16 values
// 0x11
// ..
// 0x1F
#define O_BATT 0x20 // Internal Battery
#define O_ADC0 0x21 // AD converter on pin 0
#define O_ADC1 0x22
// Reserved for LoRa messages (especially downstream)
#define O_STAT 0x30 // Ask for status message from node
#define O_SF 0x31 // Spreading factor change OFF=0, values 7-12
#define O_TIM 0x32 // Timing of the wait cyclus (20 to 7200 seconds)
#define O_1CH 0x33 // Single channel: Channel Value=0-9, OFF==255
#define O_LOC 0x34 // Ask for the location. Responds with GPS (if available)
// ..
// 0x3F
class LoRaCode
{
public:
int eVal(int opcode, byte *val, byte *msg);
int eTemperature(float val, byte *msg);
int eHumidity(float val, byte *msg);
int eAirpressure(float val, byte *msg);
int eGps(double lat, double lng, byte *msg);
int eGpsL(double lat, double lng, long alt, int sat, byte *msg);
int ePir(int val, byte *msg);
int eAirquality(int pm25, int pm10, byte *msg); // value 0 (good) -1024 (gas)
int eMbuttons(byte val, unsigned long address, unsigned short channel, byte *msg); // concentrator for multi-buttons
int eMoist(int val, byte *msg); // 255 is dry, 0 is wet
int eLuminescense(float val, byte *msg); // val contains light intensity
int eLuminescenseL(float val, byte *msg); // long contains light intensity
int eDistance(int val, byte *msg);
int eGas(int val, byte *msg);
// opcodes 0x0F until 0x1F
int eBattery(float val, byte *msg);
int eAdc0(int val, byte *msg); // Pin A0 has 1024 values, we use 256
int eAdc1(int val, byte *msg); // Pin A1 has 1024 values, we use 256
bool eMsg(byte *msg, int len);
void lPrint(byte *msg, int len);
//Decoding (downstream)
int dLen (byte *msg);
int dMsg (byte *msg, byte *val, byte *mode);
};
extern LoRaCode lcode;
#endif

Wyświetl plik

@ -0,0 +1,5 @@
[.ShellClassInfo]
InfoTip=Deze map wordt online gedeeld.
IconFile=C:\Program Files\Google\Drive\googledrivesync.exe
IconIndex=16