Dear Professionals,
Current status:
I have had a device running in the TTN EU868 network for 2 years without any problems. It sends various sensor data. The device hardware is built with an ESP8266 NodeMCU-12E and an SX1276.
Should be:
I wanted to expand the circuit to include the analog A0 pin for measuring the photovoltaic battery voltage.
In order not to disrupt “production”, I rebuilt the circuit (see circuit diagram 2023). This time with an ESP8266 NodeMCU-12F and again the SX1276.
See sketch from 2023 (version 4.1.1 lmic.h) which leads to following
Problem:
The following error message occurs after calling “os_init()”:
16:54:57.336 -> ets Jan 8 2013,rst cause:4, boot mode:(3,6)
16:54:57.336 ->
16:54:57.336 -> wdt reset
16:54:57.336 -> load 0x4010f000, len 3424, room 16
16:54:57.336 -> tail 0
16:54:57.336 -> chksum 0x2e
16:54:57.336 -> load 0x3fff20b8, len 40, room 8
16:54:57.409 -> tail 0
16:54:57.409 -> chksum 0x2b
16:54:57.409 -> csum 0x2b
16:54:57.409 -> v00048c20
16:54:57.409 -> ~ld
16:54:57.430 -> �n� r��n|�l�l` b b r l�nb�n l` � r l�l ��bis daher hats noch funktioniert?
16:54:57.552 -> FAILURE
16:54:57.552 -> oslmic.c:53
Pin mapping:
const lmic_pinmap lmic_pins = {
.nss = 16,
.rxtx = LMIC_UNUSED_PIN,
.rst = LMIC_UNUSED_PIN, //5
.dio = {5, 4, LMIC_UNUSED_PIN},
};
I carried out the following tests to isolate the error:
• Old device (ESP8266 NodeMCU-12E) on new board: error-free, sends data to TTN, so the new board is ok.
• Sketch from 2021 compiled on a new device (ESP8266 NodeMCU-12F) leads to the same error again, so there should be no error in the new Sketch 2023.
• ESP8266 NodeMCU-12F tested with other sketches without LMIC works perfectly, so NodeMCU-12F has no HW error.
• Pin mapping ESP8266 / SX1276 / LMIC checked several times.
• LMIC Library Version 3.3.0 ex 2021 installed and recompiled, same error again.
• Some hours in the relevant forums.
• Test sketch compiled on ESP8266 NodeMCU-12F results in this error:
���Starting
FAILURE
oslmic.c:53
The hardware seems fine. Have been anything changed significantly in the Libraries between 2021 and 2023? Now I don’t know what to do and I’m hoping for help from the professionals!
Thank you, Peter
Sketch 2021:
/***************************************************/
/* NODEMCU 1.0 Gartenhaus */
/* Version 02 / 06.03.2021: DHT22 Structure */
/* Version 03 / 07.03.2021: LoRa Sender */
/* MQ2 Structure */
/* Version 04 / 09.03.2021: BME680 Structure */
/* Version 05 / 11.03.2021: Fenster/Tür */
/* Version 06 / 29.03.2021: DHT22 Sensor eingebaut */
/* Version 07 / 10.05.2021: LoRaWAN payload */
/***************************************************/
// Includes für WiFi und ESP-NOW
#include <ESP8266WiFi.h>
#include <espnow.h>
// Includes und Definitionen für DHT22
#include <Adafruit_Sensor.h>
#include <DHT.h>
// Digital pin GPIO0 = D3
#define DHTPIN D3
// Verwendeter Sensortyp DHT22 (AM2302):
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// current temperature & humidity, updated in loop()
float t = 0.0;
float h = 0.0;
// Includes für Wire und LoRa
#include <Wire.h>
#include <lmic.h>
#include <hal/hal.h>
// Definition der ESP-NOW Eingabestruktur
uint8_t mydata[20];
uint16_t LoRaByte2;
#define RUNDEN0(x) ((unsigned) ((x) + .5))
typedef struct struct_message {
float DHT_Temp;
float DHT_Feucht;
float MQ2_Gaswert;
char MQ2_Gasalarm;
float BME_Temp;
float BME_Feucht;
float BME_Iaq;
float BME_Druck;
float BME_CO2;
char Fenster_Status;
char Tuer_Status;
} struct_message;
// Create a struct_message Sensoren_Daten
struct_message Sensoren_Daten;
// Callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
memcpy(&Sensoren_Daten, incomingData, sizeof(Sensoren_Daten));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Temperatur DHT: ");
Serial.println(Sensoren_Daten.DHT_Temp);
Serial.print("Luftfeuchte DHT: ");
Serial.println(Sensoren_Daten.DHT_Feucht);
Serial.print("Gaskonzentration: ");
Serial.println(Sensoren_Daten.MQ2_Gaswert);
Serial.print("Gasalarm Status: ");
Serial.println(Sensoren_Daten.MQ2_Gasalarm);
Serial.print("Temperatur BME: ");
Serial.println(Sensoren_Daten.BME_Temp);
Serial.print("Luftfeuchte BME: ");
Serial.println(Sensoren_Daten.BME_Feucht);
Serial.print("IAQ BME: ");
Serial.println(Sensoren_Daten.BME_Iaq);
Serial.print("Luftdruck BME: ");
Serial.println(Sensoren_Daten.BME_Druck);
Serial.print("CO2 BME: ");
Serial.println(Sensoren_Daten.BME_CO2);
Serial.print("Fenster offen: ");
Serial.println(Sensoren_Daten.Fenster_Status);
Serial.print("Tür offen: ");
Serial.println(Sensoren_Daten.Tuer_Status);
Serial.print("Temper. Aussen: ");
Serial.println(t);
Serial.print("Feuchte Aussen: ");
Serial.println(h);
Serial.println();
}
/*********************************************/
/* LoRaWAN */
/*********************************************/
// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, 0x70.
static const u1_t PROGMEM APPEUI[8]={ x };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]={ x };
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
static const u1_t PROGMEM APPKEY[16] = { x };
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
// Pin mapping
const lmic_pinmap lmic_pins = {
.nss = 16,
.rxtx = LMIC_UNUSED_PIN,
.rst = LMIC_UNUSED_PIN, //5
.dio = {5, 4, LMIC_UNUSED_PIN},
};
unsigned long lastTime = 0;
unsigned long timerDelay = 300000; // LoRa send alle 5 Min.
String output;
unsigned long lastDHT = 0;
unsigned long DHTDelay = 10000; // Sensor alle 10 Sec. auslesen
void onEvent (ev_t ev) {
Serial.print(os_getTime());
Serial.print(": ");
switch (ev) {
case EV_SCAN_TIMEOUT:
Serial.println(F("EV_SCAN_TIMEOUT"));
break;
case EV_BEACON_FOUND:
Serial.println(F("EV_BEACON_FOUND"));
break;
case EV_BEACON_MISSED:
Serial.println(F("EV_BEACON_MISSED"));
break;
case EV_BEACON_TRACKED:
Serial.println(F("EV_BEACON_TRACKED"));
break;
case EV_JOINING:
Serial.println(F("EV_JOINING"));
break;
case EV_JOINED:
Serial.println(F("EV_JOINED"));
break;
case EV_RFU1:
Serial.println(F("EV_RFU1"));
break;
case EV_JOIN_FAILED:
Serial.println(F("EV_JOIN_FAILED"));
break;
case EV_REJOIN_FAILED:
Serial.println(F("EV_REJOIN_FAILED"));
break;
case EV_TXCOMPLETE:
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
if (LMIC.txrxFlags & TXRX_ACK)
Serial.println(F("Received ack"));
if (LMIC.dataLen) {
Serial.println(F("Received "));
Serial.println(LMIC.dataLen);
Serial.println(F(" bytes of payload"));
}
break;
case EV_LOST_TSYNC:
Serial.println(F("EV_LOST_TSYNC"));
break;
case EV_RESET:
Serial.println(F("EV_RESET"));
break;
case EV_RXCOMPLETE:
// data received in ping slot
Serial.println(F("EV_RXCOMPLETE"));
break;
case EV_LINK_DEAD:
Serial.println(F("EV_LINK_DEAD"));
break;
case EV_LINK_ALIVE:
Serial.println(F("EV_LINK_ALIVE"));
break;
case EV_TXSTART:
Serial.println(F("EV_TXSTART"));
break;
case EV_JOIN_TXCOMPLETE:
Serial.println(F("EV_JOIN_TXCOMPLETE"));
break;
default:
output = "Unknown event: " + String(ev);
Serial.println(output);
break;
}
}
void setup() {
// Initialize Serial Monitor
Serial.begin(115200);
// Initialize DHT22 Sensor
dht.begin();
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
esp_now_register_recv_cb(OnDataRecv);
// LMIC init
os_init();
// Reset the MAC state. Session and pending data transfers will be discarded.
LMIC_reset();
LMIC_setAdrMode(1);
LMIC_setLinkCheckMode(1);
LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);
}
void loop() {
os_runloop_once();
if ((millis() - lastDHT) > DHTDelay) {
// Read temperature as Celsius (the default)
float newT = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
//float newT = dht.readTemperature(true);
// if temperature read failed, don't change t value
if (isnan(newT)) {
Serial.println("Failed to read Temperatur from DHT sensor!");
}
else {
t = newT;
Serial.println(t);
}
// Read Humidity
float newH = dht.readHumidity();
// if humidity read failed, don't change h value
if (isnan(newH)) {
Serial.println("Failed to read Humidity from DHT sensor!");
}
else {
h = newH;
Serial.println(h);
}
lastDHT = millis();
}
if ((millis() - lastTime) > timerDelay) {
// LoRaWAN payload aufbereiten
//* 1 Temperatur Gewächshaus ex DHT22
Sensoren_Daten.DHT_Temp = (Sensoren_Daten.DHT_Temp + 50) * 10; // ev. Minus-Grade wegaddieren, ohne Komma
LoRaByte2 = Sensoren_Daten.DHT_Temp;
mydata[0] = LoRaByte2;
mydata[1] = LoRaByte2 >> 8;
//* 2 Feuchtigkeit Gewächshaus ex DHT22
mydata[2] = RUNDEN0(Sensoren_Daten.DHT_Feucht); // Feuchigkeit wird nur ganzzahlig benötigt
//* 3 Gaswert Gewächshaus ex MQ2
LoRaByte2 = Sensoren_Daten.MQ2_Gaswert;
mydata[3] = LoRaByte2;
mydata[4] = LoRaByte2 >> 8;
//* 4 Gasalarm Gewächshaus ex MQ2
mydata[5] = Sensoren_Daten.MQ2_Gasalarm;
//* 5 Temperatur Gewächshaus ex BME680
Sensoren_Daten.BME_Temp = (Sensoren_Daten.BME_Temp + 50) * 100; // ev. Minus-Grade wegaddieren, ohne Komma
LoRaByte2 = Sensoren_Daten.BME_Temp;
mydata[6] = LoRaByte2;
mydata[7] = LoRaByte2 >> 8;
//* 6 Feuchigkeit Gewächshaus ex BME680
mydata[8] = RUNDEN0(Sensoren_Daten.BME_Feucht);
//* 7 IAQ Gewächshaus ex BME680
Sensoren_Daten.BME_Iaq = Sensoren_Daten.BME_Iaq * 100; // ohne Komma
LoRaByte2 = Sensoren_Daten.BME_Iaq;
mydata[9] = LoRaByte2;
mydata[10] = LoRaByte2 >> 8;
//* 8 Luftdruck Gewächshaus ex BME680
Sensoren_Daten.BME_Druck = RUNDEN0(Sensoren_Daten.BME_Druck / 100); // 300 bis 1100
LoRaByte2 = Sensoren_Daten.BME_Druck;
mydata[11] = LoRaByte2;
mydata[12] = LoRaByte2 >> 8;
//* 9 CO2 Equivalten Gewächshaus ex BME680
Sensoren_Daten.BME_CO2 = RUNDEN0(Sensoren_Daten.BME_CO2);
LoRaByte2 = Sensoren_Daten.BME_CO2;
mydata[13] = LoRaByte2;
mydata[14] = LoRaByte2 >> 8;
//* 10 Dachfenster Gewächshaus
mydata[15] = Sensoren_Daten.Fenster_Status;
//* 11 Schiebetür Gewächshaus
mydata[16] = Sensoren_Daten.Tuer_Status;
//* 12 Temperatur Gartenhaus-Außen ex DHT22
t = (t + 50) * 10; // ev. Minus-Grade wegaddieren, ohne Komma
LoRaByte2 = t;
mydata[17] = LoRaByte2;
mydata[18] = LoRaByte2 >> 8;
//* 13 Feuchtigkeit Gartenhaus-Außen ex DHT22
mydata[19] = RUNDEN0(h); // Feuchigkeit wird nur ganzzahlig benötigt
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND) {
Serial.println(F("OP_TXRXPEND, not sending"));
} else {
// Prepare upstream data transmission at the next possible time.
LMIC_setTxData2(1, mydata, sizeof(mydata), 0);
Serial.println(F("Packet queued"));
}
// save the last time you updated the DHT values
lastTime = millis();
}
}
Sketch 2023:
/****************************************************/
/* NODEMCU 1.0 Gartenhaus */
/* Version 02 / 06.03.2021: DHT22 Structure */
/* Version 03 / 07.03.2021: LoRa Sender */
/* MQ2 Structure */
/* Version 04 / 09.03.2021: BME680 Structure */
/* Version 05 / 11.03.2021: Fenster/Tür */
/* Version 06 / 29.03.2021: DHT22 Sensor eingebaut */
/* Version 07 / 10.05.2021: LoRaWAN payload */
/* Version 08 / 10.12.2023: Spannungsmessung mit A0 */
/****************************************************/
// Includes für WiFi und ESP-NOW
#include <ESP8266WiFi.h>
#include <espnow.h>
// Includes und Definitionen für DHT22
#include <Adafruit_Sensor.h>
#include <DHT.h>
// Digital pin GPIO0 = D3
#define DHTPIN D3
// Verwendeter Sensortyp DHT22 (AM2302):
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// current temperature & humidity, updated in loop()
float t = 0.0;
float h = 0.0;
// Includes für Wire und LoRa
#include <Wire.h>
#include <lmic.h>
#include <hal/hal.h>
// Definition der LoRa Ausgabestruktur, ab V08 [22] Bytes statt [20]
uint8_t mydata[22];
uint16_t LoRaByte2;
#define RUNDEN0(x) ((unsigned) ((x) + .5))
// Definition der ESP-NOW Eingabestruktur
typedef struct struct_message {
float DHT_Temp;
float DHT_Feucht;
float MQ2_Gaswert;
char MQ2_Gasalarm;
float BME_Temp;
float BME_Feucht;
float BME_Iaq;
float BME_Druck;
float BME_CO2;
char Fenster_Status;
char Tuer_Status;
} struct_message;
// Create a struct_message Sensoren_Daten
struct_message Sensoren_Daten;
// Callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
memcpy(&Sensoren_Daten, incomingData, sizeof(Sensoren_Daten));
Serial.print("Bytes received: ");
Serial.println(len);
Serial.print("Temperatur DHT: ");
Serial.println(Sensoren_Daten.DHT_Temp);
Serial.print("Luftfeuchte DHT: ");
Serial.println(Sensoren_Daten.DHT_Feucht);
Serial.print("Gaskonzentration: ");
Serial.println(Sensoren_Daten.MQ2_Gaswert);
Serial.print("Gasalarm Status: ");
Serial.println(Sensoren_Daten.MQ2_Gasalarm);
Serial.print("Temperatur BME: ");
Serial.println(Sensoren_Daten.BME_Temp);
Serial.print("Luftfeuchte BME: ");
Serial.println(Sensoren_Daten.BME_Feucht);
Serial.print("IAQ BME: ");
Serial.println(Sensoren_Daten.BME_Iaq);
Serial.print("Luftdruck BME: ");
Serial.println(Sensoren_Daten.BME_Druck);
Serial.print("CO2 BME: ");
Serial.println(Sensoren_Daten.BME_CO2);
Serial.print("Fenster offen: ");
Serial.println(Sensoren_Daten.Fenster_Status);
Serial.print("Tür offen: ");
Serial.println(Sensoren_Daten.Tuer_Status);
Serial.print("Temper. Aussen: ");
Serial.println(t);
Serial.print("Feuchte Aussen: ");
Serial.println(h);
Serial.println();
}
/*********************************************/
/* LoRaWAN */
/*********************************************/
// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3, 0x70.
static const u1_t PROGMEM APPEUI[8]={ x };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]={ x };
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
static const u1_t PROGMEM APPKEY[16] = { x };
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
// Pin mapping
const lmic_pinmap lmic_pins = {
.nss = 16,
.rxtx = LMIC_UNUSED_PIN,
.rst = LMIC_UNUSED_PIN, //5
.dio = {5, 4, LMIC_UNUSED_PIN},
};
unsigned long lastTime = 0;
unsigned long timerDelay = 300000; // LoRa send alle 5 Min.
String output;
void onEvent (ev_t ev) {
Serial.print(os_getTime());
Serial.print(": ");
switch (ev) {
case EV_SCAN_TIMEOUT:
Serial.println(F("EV_SCAN_TIMEOUT"));
break;
case EV_BEACON_FOUND:
Serial.println(F("EV_BEACON_FOUND"));
break;
case EV_BEACON_MISSED:
Serial.println(F("EV_BEACON_MISSED"));
break;
case EV_BEACON_TRACKED:
Serial.println(F("EV_BEACON_TRACKED"));
break;
case EV_JOINING:
Serial.println(F("EV_JOINING"));
break;
case EV_JOINED:
Serial.println(F("EV_JOINED"));
break;
case EV_RFU1:
Serial.println(F("EV_RFU1"));
break;
case EV_JOIN_FAILED:
Serial.println(F("EV_JOIN_FAILED"));
break;
case EV_REJOIN_FAILED:
Serial.println(F("EV_REJOIN_FAILED"));
break;
case EV_TXCOMPLETE:
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
if (LMIC.txrxFlags & TXRX_ACK)
Serial.println(F("Received ack"));
if (LMIC.dataLen) {
Serial.println(F("Received "));
Serial.println(LMIC.dataLen);
Serial.println(F(" bytes of payload"));
}
break;
case EV_LOST_TSYNC:
Serial.println(F("EV_LOST_TSYNC"));
break;
case EV_RESET:
Serial.println(F("EV_RESET"));
break;
case EV_RXCOMPLETE:
// data received in ping slot
Serial.println(F("EV_RXCOMPLETE"));
break;
case EV_LINK_DEAD:
Serial.println(F("EV_LINK_DEAD"));
break;
case EV_LINK_ALIVE:
Serial.println(F("EV_LINK_ALIVE"));
break;
case EV_TXSTART:
Serial.println(F("EV_TXSTART"));
break;
case EV_JOIN_TXCOMPLETE:
Serial.println(F("EV_JOIN_TXCOMPLETE"));
break;
default:
output = "Unknown event: " + String(ev);
Serial.println(output);
break;
}
}
// Messung der Batteriespannung
int BAT= A0; //Analog channel A0 as used to measure battery voltage
float RatioFactor=3.147; //Resistors Ration Factor, siehe Excel
void setup() {
// Initialize Serial Monitor
Serial.begin(115200);
// Initialize DHT22 Sensor
dht.begin();
// Set device as a Wi-Fi Station
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}
// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
esp_now_register_recv_cb(OnDataRecv);
Serial.println("bis daher hats noch funktioniert?");
// LMIC init
os_init();
Serial.println("nach os_init nicht mehr");
// Reset the MAC state. Session and pending data transfers will be discarded.
LMIC_reset();
LMIC_setAdrMode(1);
LMIC_setLinkCheckMode(1);
LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100);
}
void loop() {
os_runloop_once();
// wieder 5 Minuten um, daher senden
if ((millis() - lastTime) > timerDelay) {
// Spannungsmessung und für LoRa aufbereiten
float Tvoltage=0.0;
float Vvalue=0.0,Rvalue=0.0;
for(unsigned int i=0;i<10;i++){
Vvalue=Vvalue+analogRead(BAT); //Read analog Voltage
delay(5); //ADC stable
}
Vvalue=(float)Vvalue/10.0; //Find average of 10 values
Rvalue=(float)(Vvalue/1024.0)*5; //Convert Voltage in 5v factor
Tvoltage=Rvalue*RatioFactor; //Find original voltage by multiplying with factor
Serial.print("Durchschnitt =\t");
Serial.println(Vvalue);
Serial.print("Korrigiert =\t");
Serial.println(Rvalue);
Serial.print("Battery Voltage =\t");
Serial.println(Tvoltage);
// Read temperature as Celsius (the default)
float newT = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
//float newT = dht.readTemperature(true);
// if temperature read failed, don't change t value
if (isnan(newT)) {
Serial.println("Failed to read Temperatur from DHT sensor!");
}
else {
t = newT;
Serial.println(t);
}
// Read Humidity
float newH = dht.readHumidity();
// if humidity read failed, don't change h value
if (isnan(newH)) {
Serial.println("Failed to read Humidity from DHT sensor!");
}
else {
h = newH;
Serial.println(h);
}
// LoRaWAN payload aufbereiten
//* 1 Temperatur Gewächshaus ex DHT22
Sensoren_Daten.DHT_Temp = (Sensoren_Daten.DHT_Temp + 50) * 10; // ev. Minus-Grade wegaddieren, ohne Komma
LoRaByte2 = Sensoren_Daten.DHT_Temp;
mydata[0] = LoRaByte2;
mydata[1] = LoRaByte2 >> 8;
//* 2 Feuchtigkeit Gewächshaus ex DHT22
mydata[2] = RUNDEN0(Sensoren_Daten.DHT_Feucht); // Feuchigkeit wird nur ganzzahlig benötigt
//* 3 Gaswert Gewächshaus ex MQ2
LoRaByte2 = Sensoren_Daten.MQ2_Gaswert;
mydata[3] = LoRaByte2;
mydata[4] = LoRaByte2 >> 8;
//* 4 Gasalarm Gewächshaus ex MQ2
mydata[5] = Sensoren_Daten.MQ2_Gasalarm;
//* 5 Temperatur Gewächshaus ex BME680
Sensoren_Daten.BME_Temp = (Sensoren_Daten.BME_Temp + 50) * 100; // ev. Minus-Grade wegaddieren, ohne Komma
LoRaByte2 = Sensoren_Daten.BME_Temp;
mydata[6] = LoRaByte2;
mydata[7] = LoRaByte2 >> 8;
//* 6 Feuchigkeit Gewächshaus ex BME680
mydata[8] = RUNDEN0(Sensoren_Daten.BME_Feucht);
//* 7 IAQ Gewächshaus ex BME680
Sensoren_Daten.BME_Iaq = Sensoren_Daten.BME_Iaq * 100; // ohne Komma
LoRaByte2 = Sensoren_Daten.BME_Iaq;
mydata[9] = LoRaByte2;
mydata[10] = LoRaByte2 >> 8;
//* 8 Luftdruck Gewächshaus ex BME680
Sensoren_Daten.BME_Druck = RUNDEN0(Sensoren_Daten.BME_Druck / 100); // 300 bis 1100
LoRaByte2 = Sensoren_Daten.BME_Druck;
mydata[11] = LoRaByte2;
mydata[12] = LoRaByte2 >> 8;
//* 9 CO2 Equivalten Gewächshaus ex BME680
Sensoren_Daten.BME_CO2 = RUNDEN0(Sensoren_Daten.BME_CO2);
LoRaByte2 = Sensoren_Daten.BME_CO2;
mydata[13] = LoRaByte2;
mydata[14] = LoRaByte2 >> 8;
//* 10 Dachfenster Gewächshaus
mydata[15] = Sensoren_Daten.Fenster_Status;
//* 11 Schiebetür Gewächshaus
mydata[16] = Sensoren_Daten.Tuer_Status;
//* 12 Temperatur Gartenhaus-Außen ex DHT22
t = (t + 50) * 10; // ev. Minus-Grade wegaddieren, ohne Komma
LoRaByte2 = t;
mydata[17] = LoRaByte2;
mydata[18] = LoRaByte2 >> 8;
//* 13 Feuchtigkeit Gartenhaus-Außen ex DHT22
mydata[19] = RUNDEN0(h); // Feuchigkeit wird nur ganzzahlig benötigt
//* neu in Version V08:
//* 14 Batteriespannung ex A0 Analogeingang
Tvoltage = Tvoltage * 100; // ohne Komma
LoRaByte2 = Tvoltage;
mydata[20] = LoRaByte2;
mydata[21] = LoRaByte2 >> 8;
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND) {
Serial.println(F("OP_TXRXPEND, not sending"));
} else {
// Prepare upstream data transmission at the next possible time.
LMIC_setTxData2(1, mydata, sizeof(mydata), 0);
Serial.println(F("Packet queued"));
}
// zu diesem Timestamp wurde zuletzt gesendet
lastTime = millis();
}
}
Test Sketch:
/*******************************************************************************
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
* Copyright (c) 2018 Terry Moore, MCCI
*
*******************************************************************************/
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
//
// For normal use, we require that you edit the sketch to replace FILLMEIN
// with values assigned by the TTN console. However, for regression tests,
// we want to be able to compile these scripts. The regression tests define
// COMPILE_REGRESSION_TEST, and in that case we define FILLMEIN to a non-
// working but innocuous value.
//
#ifdef COMPILE_REGRESSION_TEST
# define FILLMEIN 0
#else
# warning "You must replace the values marked FILLMEIN with real values from the TTN control panel!"
# define FILLMEIN (#dont edit this, edit the lines that use FILLMEIN)
#endif
// This EUI must be in little-endian format, so least-significant-byte
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes. For TTN issued EUIs the last bytes should be 0xD5, 0xB3,
// 0x70.
static const u1_t PROGMEM APPEUI[8]={ x };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
// This should also be in little endian format, see above.
static const u1_t PROGMEM DEVEUI[8]={ x };
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
// This key should be in big endian format (or, since it is not really a
// number but a block of memory, endianness does not really apply). In
// practice, a key taken from ttnctl can be copied as-is.
static const u1_t PROGMEM APPKEY[16] = { x };
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
static uint8_t mydata[] = "Hello, world!";
static osjob_t sendjob;
// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 60;
// Pin mapping
const lmic_pinmap lmic_pins = {
.nss = 16,
.rxtx = LMIC_UNUSED_PIN,
.rst = LMIC_UNUSED_PIN, //5
.dio = {5, 4, LMIC_UNUSED_PIN},
};
void printHex2(unsigned v) {
v &= 0xff;
if (v < 16)
Serial.print('0');
Serial.print(v, HEX);
}
void onEvent (ev_t ev) {
Serial.print(os_getTime());
Serial.print(": ");
switch(ev) {
case EV_SCAN_TIMEOUT:
Serial.println(F("EV_SCAN_TIMEOUT"));
break;
case EV_BEACON_FOUND:
Serial.println(F("EV_BEACON_FOUND"));
break;
case EV_BEACON_MISSED:
Serial.println(F("EV_BEACON_MISSED"));
break;
case EV_BEACON_TRACKED:
Serial.println(F("EV_BEACON_TRACKED"));
break;
case EV_JOINING:
Serial.println(F("EV_JOINING"));
break;
case EV_JOINED:
Serial.println(F("EV_JOINED"));
{
u4_t netid = 0;
devaddr_t devaddr = 0;
u1_t nwkKey[16];
u1_t artKey[16];
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
Serial.print("netid: ");
Serial.println(netid, DEC);
Serial.print("devaddr: ");
Serial.println(devaddr, HEX);
Serial.print("AppSKey: ");
for (size_t i=0; i<sizeof(artKey); ++i) {
if (i != 0)
Serial.print("-");
printHex2(artKey[i]);
}
Serial.println("");
Serial.print("NwkSKey: ");
for (size_t i=0; i<sizeof(nwkKey); ++i) {
if (i != 0)
Serial.print("-");
printHex2(nwkKey[i]);
}
Serial.println();
}
// Disable link check validation (automatically enabled
// during join, but because slow data rates change max TX
// size, we don't use it in this example.
LMIC_setLinkCheckMode(0);
break;
/*
|| This event is defined but not used in the code. No
|| point in wasting codespace on it.
||
|| case EV_RFU1:
|| Serial.println(F("EV_RFU1"));
|| break;
*/
case EV_JOIN_FAILED:
Serial.println(F("EV_JOIN_FAILED"));
break;
case EV_REJOIN_FAILED:
Serial.println(F("EV_REJOIN_FAILED"));
break;
case EV_TXCOMPLETE:
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
if (LMIC.txrxFlags & TXRX_ACK)
Serial.println(F("Received ack"));
if (LMIC.dataLen) {
Serial.print(F("Received "));
Serial.print(LMIC.dataLen);
Serial.println(F(" bytes of payload"));
}
// Schedule next transmission
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
break;
case EV_LOST_TSYNC:
Serial.println(F("EV_LOST_TSYNC"));
break;
case EV_RESET:
Serial.println(F("EV_RESET"));
break;
case EV_RXCOMPLETE:
// data received in ping slot
Serial.println(F("EV_RXCOMPLETE"));
break;
case EV_LINK_DEAD:
Serial.println(F("EV_LINK_DEAD"));
break;
case EV_LINK_ALIVE:
Serial.println(F("EV_LINK_ALIVE"));
break;
/*
|| This event is defined but not used in the code. No
|| point in wasting codespace on it.
||
|| case EV_SCAN_FOUND:
|| Serial.println(F("EV_SCAN_FOUND"));
|| break;
*/
case EV_TXSTART:
Serial.println(F("EV_TXSTART"));
break;
case EV_TXCANCELED:
Serial.println(F("EV_TXCANCELED"));
break;
case EV_RXSTART:
/* do not print anything -- it wrecks timing */
break;
case EV_JOIN_TXCOMPLETE:
Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
break;
default:
Serial.print(F("Unknown event: "));
Serial.println((unsigned) ev);
break;
}
}
void do_send(osjob_t* j){
// Check if there is not a current TX/RX job running
if (LMIC.opmode & OP_TXRXPEND) {
Serial.println(F("OP_TXRXPEND, not sending"));
} else {
// Prepare upstream data transmission at the next possible time.
LMIC_setTxData2(1, mydata, sizeof(mydata)-1, 0);
Serial.println(F("Packet queued"));
}
// Next TX is scheduled after TX_COMPLETE event.
}
void setup() {
Serial.begin(9600);
Serial.println(F("Starting"));
#ifdef VCC_ENABLE
// For Pinoccio Scout boards
pinMode(VCC_ENABLE, OUTPUT);
digitalWrite(VCC_ENABLE, HIGH);
delay(1000);
#endif
// LMIC init
os_init();
// Reset the MAC state. Session and pending data transfers will be discarded.
LMIC_reset();
// Start job (sending automatically starts OTAA too)
do_send(&sendjob);
}
void loop() {
os_runloop_once();
}
Circuit Diagram 2023:
Gartenhaus_V2.pdf (38.9 KB)