LoRaWAN IoT-Wetterstation, Erklärung des Programmablaufs

Hi @david,

habe Dein schönes Projekt “LoRaWAN IoT-Wetterstation” nachgebaut und funktioniert prima. Nun habe ich versucht einige Ergänzungen vorzunehmen, ein Display auf dem die einzelnen Messwerte anzuzeigen sowie eine CO2-Messung, die in anderen Programmen sauber funktioniert.
Allerdings habe ich Schwierigkeiten die Anzeigeroutine unterzubringen. Meistens belibt dann das Programm einfach hängen.
Zum Programmverständnis:
die Loop Funktion besteht nur aus einer Anweisung:

Blockquote
os_runloop_once();

Blockquote

was ich übersetzen würde mit: Betriebssystemschleife einmal durchlaufen

Im Setup-Teil gibt es den Aufruf: do_send(&sendjob);
der zu dem eigentlichen Job führt in dem Messwerte gesammelt werden und in der loramessage gebündelt werden.
Wenn ich hier die Anzeigeroutine einfüge ,bleibt das Programm in einer Endlosschleife hängen genauso, wenn ich die Anzeigeroutine ind dem Loop-Teil unterbringe.
Das Ganze scheint mir Eventgesteuert zu sein, aber durch welches Event?

Vielleicht kannst Du mir einige Erklärungen zum Programmablauf geben.

Vielen Dank im Vorraus.

Hi @altprog,

leider kann ich sowas nicht so gut. Ich tagge mal @Felix der hat da mehr Skills :wink:

Freut mich aber mal wieder von dir zu hören :slight_smile:

Liebe Grüße

David

Kannst du mal den Programmcode hier rein posten? Hast du das mit Blockly gemacht oder mit der Arduino IDE? Ich hatte mir es schon mal sowohl in der loop() als auch im do_send() laufen. Eigentlich sollte das kein Problem sein. Allerdings könnte ein delay() in der loop() Probleme bereiten.

Noch eleganter könntest du es lösen indem du einen zweiten Job startest, ähnlich wie der do_send Job.
Dazu musst du oben im Programmcode die entsprechenden Libraries fürs Display einbinden:

#include <SenseBoxMCU.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

und neben dem sendjob einen weiteren Job deklarieren (z.B. displayjob):

static osjob_t sendjob;
static osjob_t displayjob;

Unter dem TX_INTERVAL habe ich ein neues Intervall erstellt, in welchem das Display aktualisiert werden soll:

const unsigned TX_INTERVAL = 60;
const unsigned DISPLAY_INTERVAL = 1; // update display each second

Nach der do_send() Funktion habe ich eine weitere Funktion erstellt, welche den Inhalt des Displays aktualisiert. os_setTimedCallback ruft die Funktion erneut nach DISPLAY_INTERVAL Sekunden auf.
Den counter habe ich ganz am Anfang ebenfalls als Nummer deklariert.

void update_display(osjob_t* t) {
  counter++; // just a simple counter
  display.clearDisplay();
  display.setCursor(0, 0);
  display.setTextSize(2);
  display.setTextColor(WHITE, BLACK);
  display.print("COUNTER: ");
  display.print(String(counter, DEC));
  display.display();
  os_setTimedCallback(&displayjob, os_getTime() + sec2osticks(DISPLAY_INTERVAL), update_display);
}

Innerhalb der setup Methode starte ich den displayjob nach dem sendjob:

do_send(&sendjob);
update_display(&displayjob);

Somit habe ich die beiden Prozesse quasi parallel laufen.

Einen komplette Demo ist hier zu finden: https://gist.github.com/felixerdy/ceafc6033ae849f4426ba4c1b112d4fa

Hi @Felix,

danke für die beiden Antworten. Hier der Code, den ich benutzt habe, natürlich kommen dort delays vor, was das Ganze etwas erklären würde:

/*   tigerlora sensebox mit Lora Feinstaub und Display
   version 0.3.2 vom 07.09.2020
   mit Schalter für Feinstaubsensor
  senseBox:home - Citizen Sensingplatform
  Version: lorav2.0.0
  Date: 2018-09-11
  Homepage: https://www.sensebox.de https://www.opensensemap.org
  Author: Reedu GmbH & Co. KG
  Note: Sketch for senseBox:home LoRa MCU Edition
  Model: homeV2lora
  Email: support@sensebox.de
  Code is in the public domain.
  https://github.com/sensebox/node-sketch-templater
*/
#include <LoraMessage.h>
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include <senseBoxIO.h>

#include <Adafruit_Sensor.h>
#include <Adafruit_HDC1000.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_BME680.h>
#include <Makerblog_TSL45315.h>
#include <VEML6070.h>
#include <SDS011-select-serial.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

// Uncomment the next line to get debugging messages printed on the Serial port
// Do not leave this enabled for long time use
//#define ENABLE_DEBUG

#ifdef ENABLE_DEBUG
#define DEBUG(str) Serial.println(str)
#else
#define DEBUG(str)
#endif

// Schaltermapping
#define sdsSchalter 1   // an pin 1 ist der Schalter für SDS-Sensor angeschlosse
bool status;
bool schalterStatus;
unsigned long int i=0;          //  laufender Zähler
unsigned long int j=0;          //             "
unsigned long t_anzeige=6000;   // 6 Sekunden Anzeigezeit
unsigned long T_display;
float T;  // variable für Temperatur
float H;  //   "    Feuchte
float P;  //   "    Luftdruck
float Lux;//  "     Helligkeit
float UV; //  "     UV
float CO2; //  "    CO2
const unsigned char cmd_get_sensor[] = {0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79 };
unsigned char dataReviced[9];
int temperature_co2;
int CO2PPM;


// Connected sensors
// Temperatur
#define HDC1080_CONNECTED
// rel. Luftfeuchte
#define HDC1080_CONNECTED
// Luftdruck
#define BMP280_CONNECTED
// Beleuchtungsstärke
#define TSL45315_CONNECTED
// UV-Intensität
#define VEML6070_CONNECTED
// PM10
#define SDS011_CONNECTED
// PM2.5
#define SDS011_CONNECTED
// CO2
#define MH-Z16_CONNECTED



// Number of serial port the SDS011 is connected to. Either Serial1 or Serial2
#ifdef SDS011_CONNECTED
#define SDS_UART_PORT (Serial2)
#endif

//Load sensors / instances
#ifdef HDC1080_CONNECTED
  Adafruit_HDC1000 HDC = Adafruit_HDC1000();
  float temperature = 0;
  float humidity = 0;
#endif
#ifdef BMP280_CONNECTED
  Adafruit_BMP280 BMP;
  double pressure;
#endif
#ifdef TSL45315_CONNECTED
  uint32_t lux;
  Makerblog_TSL45315 TSL = Makerblog_TSL45315(TSL45315_TIME_M4);
#endif
#ifdef VEML6070_CONNECTED
  VEML6070 VEML;
  uint16_t uv;
#endif
#ifdef SDS011_CONNECTED
  SDS011 SDS(SDS_UART_PORT);
  float pm10 = 0;
  float pm25 = 0;
#endif
#ifdef SMT50_CONNECTED
  #define SOILTEMPPIN 1
  #define SOILMOISPIN 2
#endif
#ifdef SOUNDLEVELMETER_CONNECTED
  #define SOUNDMETERPIN 3
#endif
#ifdef BME680_CONNECTED
  Adafruit_BME680 BME;
#endif
#ifdef WINDSPEED_CONNECTED
  #define WINDSPEEDPIN 5
#endif



// This EUI must be in little-endian format, so least-significant-byte (lsb)
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes.
static const u1_t PROGMEM DEVEUI[8]= {  };
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}

// This EUI must be in little-endian format, so least-significant-byte (lsb)
// 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]= {   };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}

// This key should be in big endian format (msb) (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.
// The key shown here is the semtech default key.
static const u1_t PROGMEM APPKEY[16] = {   };
void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}

//----------------------- Unterprogramme ----------------------

void zeit() {
  for(i=0;i<=t_anzeige;i++) {
    for(j=0;j<=t_anzeige;j++) {
      
    }
  }
}

void anzeige()  {
  
      // temperatur anzeigen
      display.setCursor(0,0);
    display.setTextSize(2);
    display.setTextColor(WHITE,BLACK);
    T_display=int(T);
    display.println((("Temperatur")+String(temperature)));  //Ausgabe auf Display
    display.display();
    delay(300);
    zeit();      


   

        // Feuchte anzeigen
        display.setCursor(0,0);
    display.setTextSize(2);
    display.setTextColor(WHITE,BLACK);
    display.println((("Feuchte:  ")+String(humidity)));  //Ausgabe auf Display
    display.display();
    delay(300);
    zeit();



      //Helligkeit anzeigen
      display.setCursor(0,0);
    display.setTextSize(2);
    display.setTextColor(WHITE,BLACK);
    display.println((("Helligk.:  ")+String(lux)));  //Ausgabe auf Display
    display.display();
    delay(300);
    zeit();


      // UV anzeigen
      display.setCursor(0,0);
    display.setTextSize(2);
    display.setTextColor(WHITE,BLACK);
    display.println((("UV:       ")+String(uv)));  //Ausgabe auf Display
    display.display();
    delay(300);
    Serial.print("UV: "); Serial.println(UV);
    zeit();


      // CO2 anzeigen
      display.setCursor(0,0);
    display.setTextSize(2);
    display.setTextColor(WHITE,BLACK);
    CO2=CO2PPM;
    display.println((("CO2:       ")+String(CO2)));  //Ausgabe auf Display
    display.display();
    delay(300);
    zeit();


 

      // PM10 anzeigen
      display.setCursor(0,0);
    display.setTextSize(2);
    display.setTextColor(WHITE,BLACK);
    display.println((("Temperatur:")+String(pm10)));  //Ausgabe auf Display
    display.display();
    delay(100);


      // PM25 anzeigen
      display.setCursor(0,0);
    display.setTextSize(2);
    display.setTextColor(WHITE,BLACK);
    display.println((("Temperatur:")+String(pm25)));  //Ausgabe auf Display
    display.display();
    delay(100);


 } 



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 = PIN_XB1_CS,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = LMIC_UNUSED_PIN,
  .dio = {PIN_XB1_INT, PIN_XB1_INT, LMIC_UNUSED_PIN},
};

void onEvent (ev_t ev) {
  senseBoxIO.statusGreen();
  DEBUG(os_getTime());
  switch(ev) {
    case EV_SCAN_TIMEOUT:
      DEBUG(F("EV_SCAN_TIMEOUT"));
      break;
    case EV_BEACON_FOUND:
      DEBUG(F("EV_BEACON_FOUND"));
      break;
    case EV_BEACON_MISSED:
      DEBUG(F("EV_BEACON_MISSED"));
      break;
    case EV_BEACON_TRACKED:
      DEBUG(F("EV_BEACON_TRACKED"));
      break;
    case EV_JOINING:
      DEBUG(F("EV_JOINING"));
      break;
    case EV_JOINED:
      DEBUG(F("EV_JOINED"));

      // Disable link check validation (automatically enabled
      // during join, but not supported by TTN at this time).
      LMIC_setLinkCheckMode(0);
      break;
    case EV_RFU1:
      DEBUG(F("EV_RFU1"));
      break;
    case EV_JOIN_FAILED:
      DEBUG(F("EV_JOIN_FAILED"));
      break;
    case EV_REJOIN_FAILED:
      DEBUG(F("EV_REJOIN_FAILED"));
      break;
    case EV_TXCOMPLETE:
      DEBUG(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
      if (LMIC.txrxFlags & TXRX_ACK)
        DEBUG(F("Received ack"));
      if (LMIC.dataLen) {
        DEBUG(F("Received "));
        DEBUG(LMIC.dataLen);
        DEBUG(F(" bytes of payload"));
      }
      // Schedule next transmission
      os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
      break;
    case EV_LOST_TSYNC:
      DEBUG(F("EV_LOST_TSYNC"));
      break;
    case EV_RESET:
      DEBUG(F("EV_RESET"));
      break;
    case EV_RXCOMPLETE:
      // data received in ping slot
      DEBUG(F("EV_RXCOMPLETE"));
      break;
    case EV_LINK_DEAD:
      DEBUG(F("EV_LINK_DEAD"));
      break;
    case EV_LINK_ALIVE:
      DEBUG(F("EV_LINK_ALIVE"));
      break;
    default:
      DEBUG(F("Unknown event"));
      break;
  }
}

void do_send(osjob_t* j){
  // Check if there is not a current TX/RX job running
  if (LMIC.opmode & OP_TXRXPEND) {
    DEBUG(F("OP_TXRXPEND, not sending"));
  } else {
    LoraMessage message;

    //-----Temperature-----//
    //-----Humidity-----//
    #ifdef HDC1080_CONNECTED
      DEBUG(F("Temperature: "));
      temperature = HDC.readTemperature();
      DEBUG(temperature);
      message.addUint16((temperature + 18) * 771);
      delay(2000);

      DEBUG(F("Humidity: "));
      humidity = HDC.readHumidity();
      DEBUG(humidity);
      message.addHumidity(humidity);
      delay(2000);
    #endif

    //-----Pressure-----//
    #ifdef BMP280_CONNECTED
      float altitude;
      pressure = BMP.readPressure()/100;
      altitude = BMP.readAltitude(1013.25); //1013.25 = sea level pressure
      DEBUG(F("Pressure: "));
      DEBUG(pressure);
      message.addUint16((pressure - 300) * 81.9187);
      delay(2000);
    #endif

    //-----Lux-----//
    #ifdef TSL45315_CONNECTED
      DEBUG(F("Illuminance: "));
      lux = TSL.readLux();
      DEBUG(lux);
      message.addUint8(lux % 255);
      message.addUint16(lux / 255);
      delay(2000);
    #endif

    //-----UV intensity-----//
    #ifdef VEML6070_CONNECTED
      DEBUG(F("UV: "));
      uv = VEML.getUV();
      DEBUG(uv);
      message.addUint8(uv % 255);
      message.addUint16(uv / 255);
      delay(2000);
    #endif

    //-----PM-----//
    #ifdef SDS011_CONNECTED
      uint8_t attempt = 0;
      while (attempt < 5) {
        bool error = SDS.read(&pm25, &pm10);
        if (!error) {
          DEBUG(F("PM10: "));
          DEBUG(pm10);
          message.addUint16(pm10 * 10);
          DEBUG(F("PM2.5: "));
          DEBUG(pm25);
          message.addUint16(pm25 * 10);
          break;
        }
        attempt++;
      }
    #endif

    //-----Soil Temperature & Moisture-----//
    #ifdef SMT50_CONNECTED
      float voltage = analogRead(SOILTEMPPIN) * (3.3 / 1024.0);
      float soilTemperature = (voltage - 0.5) * 100;
      message.addUint16((soilTemperature + 18) * 771);
      voltage = analogRead(SOILMOISPIN) * (3.3 / 1024.0);
      float soilMoisture = (voltage * 50) / 3;
      message.addHumidity(soilMoisture);
    #endif

    //-----dB(A) Sound Level-----//
    #ifdef SOUNDLEVELMETER_CONNECTED
      float v = analogRead(SOUNDMETERPIN) * (3.3 / 1024.0);
      float decibel = v * 50;
      message.addUint16(decibel * 10);
    #endif

    //-----BME680-----//
    #ifdef BME680_CONNECTED
      BME.setGasHeater(0, 0);
      if( BME.performReading()) {
        message.addUint16((BME.temperature-1 + 18) * 771);
        message.addHumidity(BME.humidity);
        message.addUint16((BME.pressure/100 - 300) * 81.9187);
      }
      delay(100);
      BME.setGasHeater(320, 150); // 320*C for 150 ms
      if( BME.performReading()) {
        uint16_t gasResistance = BME.gas_resistance / 1000.0;
        message.addUint8(gasResistance % 255);
        message.addUint16(gasResistance / 255);
      }
    #endif

    //-----Wind speed-----//
    #ifdef WINDSPEED_CONNECTED
      float voltageWind = analogRead(WINDSPEEDPIN) * (3.3 / 1024.0);
      float windspeed = 0.0;
      if (voltageWind >= 0.018){
        float poly1 = pow(voltageWind, 3);
        poly1 = 17.0359801998299 * poly1;
        float poly2 = pow(voltageWind, 2);
        poly2 = 47.9908168343362 * poly2;
        float poly3 = 122.899677524413 * voltageWind;
        float poly4 = 0.657504127272728;
        windspeed = poly1 - poly2 + poly3 - poly4;
        windspeed = windspeed * 0.2777777777777778; //conversion in m/s
      }
      message.addUint16(windspeed * 10);
    #endif

    // Prepare upstream data transmission at the next possible time.
    LMIC_setTxData2(1, message.getBytes(), message.getLength(), 0);
    DEBUG(F("Packet queued"));
  }

  //anzeige();    //Aufruf der Anzeigeroutine
  
  // Next TX is scheduled after TX_COMPLETE event.
}

void setup() {
  #ifdef ENABLE_DEBUG
    Serial.begin(9600);
  #endif
  delay(3000);
  schalterStatus=1;     //SDS ein für den  Anfang

  // RFM9X (LoRa-Bee) in XBEE1 Socket
  senseBoxIO.powerXB1(false); // power off to reset RFM9X
  delay(250);
  senseBoxIO.powerXB1(true);  // power on

  // Sensor initialization
  DEBUG(F("Initializing sensors..."));
  #ifdef VEML6070_CONNECTED
    VEML.begin();
    delay(500);
  #endif
  #ifdef HDC1080_CONNECTED
    HDC.begin();
  #endif
  #ifdef BMP280_CONNECTED
    BMP.begin(0x76);
  #endif
  #ifdef TSL45315_CONNECTED
    TSL.begin();
  #endif
  #ifdef SDS011_CONNECTED
    SDS_UART_PORT.begin(9600);
  #endif
  #ifdef BME680_CONNECTED
    BME.begin(0x76);
    BME.setTemperatureOversampling(BME680_OS_8X);
    BME.setHumidityOversampling(BME680_OS_2X);
    BME.setPressureOversampling(BME680_OS_4X);
    BME.setIIRFilterSize(BME680_FILTER_SIZE_3);
  #endif

  DEBUG(F("Sensor initializing done!"));
  DEBUG(F("Starting loop in 3 seconds."));
  
  display.begin(SSD1306_SWITCHCAPVCC,0x3D);
  display.setCursor(0,0);
  display.setTextSize(2);
  display.setTextColor(WHITE,BLACK);
  display.clearDisplay();
  delay(1000);
  display.println("   Tiger");
  display.println("   lora");
  display.display();
  delay(3000);
  display.clearDisplay();
  //display.display();

  // 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() {
 /* schalterStatus=digitalRead(sdsSchalter);
 if (status==1) {           //ein?
  if (schalterStatus==0) {     // ausschalten
    SDS.sleep();            // SDS aus
  }
 }else {
                           // nix
 }
 if (status==0) {           //aus?
  if (schalterStatus==1) {    // ein
    SDS.wakeup();
    delay(10000);         // warten 
    
  }
 }
  else {
                            //nix
  }

 DEBUG(F("Schalterstellung: "));
 DEBUG(schalterStatus);
*/
 //anzeige();
  os_runloop_once();
}

Interessant ist Dein Vorschlag einen zweiten Job einzurichten. Das muss ich erstmals studieren, da es ganz neu ist.
Wo kann man mehr über die Parallelprogrammierung mit mehreren Jobs erfahren?

Den Code habe ich von der Einrichtung der senseBox auf der openSensemap in die Arduino IDE übernommen und mit meinen Erweiterungen ergänzt.

Hi @Felix

habe Deine Vorschläge versucht in mein Programm einzu bauen. Hier die Änderungen:
Die zusätzlichen includes für das Display habe ich schon am Anfang eingebaut:

die Job-Vereinbarungen:

static osjob_t sendjob;
static osjob_t displayjob;

Die Intervallvereinbarungen:

// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 60;
const unsigned DISPLAY_INTERVAL=1; //update Display each secnd

Die neue Display-Routine :

void update_display(osjob_t* ) {
  counter++;
  display.clearDisplay;
  display.setCursor(0, 0);
  display.setTextSize(2);
  display.setTextColor(WHITE, BLACK);
  display.print("Counter: ");
  display.print(String(counter,DEC));
  display.display();
  os_setTimedCallback(&displayjob, os_getTime() + sec2osticks(DISPLAY_IN
  
}

Und in der Setuo-Routine:

 // Start job (sending automatically starts OTAA too)
  do_send(&sendjob);
  update_display(&displayjob);

Beim compilieren erhalte ich leider folgende Fehlermeldun:

Arduino: 1.8.10 (Mac OS X), Board: "senseBox MCU, ON as Default"

WARNUNG: Bibliothek SDS011-select-serial behauptet auf esp8266, avr Architektur(en) ausgeführt werden zu können und ist möglicherweise inkompatibel mit Ihrem derzeitigen Board, welches auf samd Architektur(en) ausgeführt wird.
**tigerlora_v14:476:0: error: unterminated argument list invoking macro "sec2osticks"**
Mehrere Bibliotheken wurden für "LoraMessage.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/LoraMessage
Mehrere Bibliotheken wurden für "SPI.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/arduino/hardware/samd/1.8.8/libraries/SPI
Mehrere Bibliotheken wurden für "Adafruit_GFX.h" gefunden
 Benutzt: /Users/rolf/Documents/Arduino/libraries/Adafruit_GFX_Library
 Nicht benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/Adafruit-GFX-Library
Mehrere Bibliotheken wurden für "Adafruit_BMP280.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/Adafruit_BMP280
 Nicht benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/Adafruit_BMP280_Library
Mehrere Bibliotheken wurden für "Adafruit_BME680.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/Adafruit_BME680
 Nicht benutzt: /Users/rolf/Documents/Arduino/libraries/Adafruit_BME680_Library
Mehrere Bibliotheken wurden für "Makerblog_TSL45315.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/Makerblog_TSL45315
Mehrere Bibliotheken wurden für "Adafruit_I2CDevice.h" gefunden
 Benutzt: /Users/rolf/Documents/Arduino/libraries/Adafruit_BusIO
Mehrere Bibliotheken wurden für "SenseBoxMCU.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/SenseBox-MCU
Mehrere Bibliotheken wurden für "Wire.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/arduino/hardware/samd/1.8.8/libraries/Wire
Mehrere Bibliotheken wurden für "WiFi101.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/WiFi101
Mehrere Bibliotheken wurden für "DallasTemperature.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/Arduino-Temperature-Control-Library
Mehrere Bibliotheken wurden für "Adafruit_SSD1306.h" gefunden
 Benutzt: /Users/rolf/Documents/Arduino/libraries/Adafruit_SSD1306
 Nicht benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/Adafruit_SSD1306
Mehrere Bibliotheken wurden für "Adafruit_Sensor.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/Adafruit_Sensor
 Nicht benutzt: /Users/rolf/Documents/Arduino/libraries/Adafruit_Unified_Sensor
Mehrere Bibliotheken wurden für "lmic.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/LMIC
 Nicht benutzt: /Users/rolf/Documents/Arduino/libraries/IBM_LMIC_framework
Mehrere Bibliotheken wurden für "TinyGPS++.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/TinyGPSPlus
Mehrere Bibliotheken wurden für "OneWire.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/OneWire
Mehrere Bibliotheken wurden für "senseBoxIO.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/senseBoxIO
Mehrere Bibliotheken wurden für "SD.h" gefunden
 Benutzt: /Users/rolf/Documents/Arduino/libraries/SD
 Nicht benutzt: /Applications/Arduino.app/Contents/Java/libraries/SD
Mehrere Bibliotheken wurden für "Adafruit_HDC1000.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/Adafruit_HDC1000
Mehrere Bibliotheken wurden für "VEML6070.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/VEML6070
Mehrere Bibliotheken wurden für "SDS011-select-serial.h" gefunden
 Benutzt: /Users/rolf/Library/Arduino15/packages/sensebox/hardware/samd/1.3.2/libraries/SDS011-select-serial
exit status 1
**unterminated argument list invoking macro "sec2osticks"**

Dieser Bericht wäre detaillierter, wenn die Option
“Ausführliche Ausgabe während der Kompilierung”
in Datei -> Voreinstellungen aktiviert wäre.

Die Fehlermeldung taucht zwar in der Loop-Routine auf muss aber die Ursache weiter vorher liegen?

Hier ist noch ein Syntaxfehler. Es fehlt noch ein bisschen bei os_setTimedCallback

os_setTimedCallback(&displayjob, os_getTime() + sec2osticks(DISPLAY_INTERVAL), update_display);

Das steht auch unten in der Fehlermeldung:
**unterminated argument list invoking macro "sec2osticks"**

Hallo @Felix ,
habe Deine Änderungen eingebaut. Leider kommt immer noch eine Fehlermeldung:
"
exit status 1
‘update_display’ was not declared in this scope"

und zwar in folgender Zeile:

os_setTimedCallback(&displayjob, os_getTime() + sec2osticks(DISPLAY_INTERVAL), update_display);

Es fehlt wohl die update_display Funktion. Kannst du einmal den Programmcode hier rein kopieren?

Hi @Felix

Hier das Listing:

/*
  tigerlora_v1_4 senseBox mit Lora und CO2
  und einem zweiten Displayjob frei nach Felix
  Version 1.4 vom 05.09.2020 
  senseBox:home - Citizen Sensingplatform
  Version: lorav2.0.0
  Date: 2018-09-11
  
*/
#include <LoraMessage.h>
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
//#include <senseBoxIO.h>
#include <SenseBoxMCU.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


#include <Adafruit_Sensor.h>
#include <Adafruit_HDC1000.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_BME680.h>
#include <Makerblog_TSL45315.h>
#include <VEML6070.h>
#include <SDS011-select-serial.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);


// Uncomment the next line to get debugging messages printed on the Serial port
// Do not leave this enabled for long time use
#define ENABLE_DEBUG

#ifdef ENABLE_DEBUG
#define DEBUG(str) Serial.println(str)
#else
#define DEBUG(str)
#endif

// Connected sensors
// Temperatur
#define HDC1080_CONNECTED
// rel. Luftfeuchte
#define HDC1080_CONNECTED
// Luftdruck
#define BMP280_CONNECTED
// Beleuchtungsstärke
#define TSL45315_CONNECTED
// UV-Intensität
#define VEML6070_CONNECTED
// PM10
#define SDS011_CONNECTED
// PM2.5
#define SDS011_CONNECTED
// CO2
#define MH-Z16_CONNECTED
//CO2 alternativ
#define GROVE_CO2 (Serial1)

// Number of serial port the SDS011 is connected to. Either Serial1 or Serial2
#ifdef SDS011_CONNECTED
#define SDS_UART_PORT (Serial2)
#endif

//Load sensors / instances
#ifdef HDC1080_CONNECTED
  Adafruit_HDC1000 HDC = Adafruit_HDC1000();
  float temperature = 0;
  float humidity = 0;
#endif
#ifdef BMP280_CONNECTED
  Adafruit_BMP280 BMP;
  double pressure;
#endif
#ifdef TSL45315_CONNECTED
  uint32_t lux;
  Makerblog_TSL45315 TSL = Makerblog_TSL45315(TSL45315_TIME_M4);
#endif
#ifdef VEML6070_CONNECTED
  VEML6070 VEML;
  uint16_t uv;
#endif
#ifdef SDS011_CONNECTED
  SDS011 SDS(SDS_UART_PORT);
  float pm10 = 0;
  float pm25 = 0;
#endif
#ifdef GROVE_CO2_CONNECTED
   GROVE_CO2 GROVECO2;
  float CO2;
 #endif  
#ifdef SMT50_CONNECTED
  #define SOILTEMPPIN 0
  #define SOILMOISPIN 1
#endif
#ifdef SOUNDLEVELMETER_CONNECTED
  #define SOUNDMETERPIN 0
#endif
#ifdef BME680_CONNECTED
  Adafruit_BME680 BME;
#endif
#ifdef WINDSPEED_CONNECTED
  #define WINDSPEEDPIN 0
#endif
 
// This EUI must be in little-endian format, so least-significant-byte (lsb)
// first. When copying an EUI from ttnctl output, this means to reverse
// the bytes.
static const u1_t PROGMEM DEVEUI[8]= { 0x64, 0x0C, 0x5A, 0xC3, 0x2F, 0x52, 0x93, 0x00  };
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}

// This EUI must be in little-endian format, so least-significant-byte (lsb)
// 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]= { 0xAB, 0x3F, 0x03, 0xD0, 0x7E, 0xD5, 0xB3, 0x70  };
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}

// This key should be in big endian format (msb) (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.
// The key shown here is the semtech default key.
static const u1_t PROGMEM APPKEY[16] = { 0xDB, 0x57, 0x1E, 0x7F, 0xEE, 0x80, 0xC9, 0xAA, 0x9D, 0x2A, 0xD2, 0x59, 0xD5, 0xDA, 0x1D, 0x51  };
void os_getDevKey (u1_t* buf) {  memcpy_P(buf, APPKEY, 16);}


const unsigned char cmd_get_sensor[] = {0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79 }; // Code zum Auslesen des CO2 Sensors
unsigned char dataReviced[9];
int temperature_co2;
int CO2PPM;
uint8_t attempt = 0;   //Versuchszählrt für PM und CO2
float CO2; //  "    CO2
int counter=0;

// Helper function for co2 sensor
int dataReceive(void)
{
 byte data[9];
 int i = 0;
 //transmit command data
 for(i=0; i<sizeof(cmd_get_sensor); i++)
 {
     GROVE_CO2.write(cmd_get_sensor[i]);
 }
 delay(10);
 //begin receiving data
 if(GROVE_CO2.available())
 {
    while(GROVE_CO2.available())
    {
        for(int i=0;i<9; i++)
        {
            data[i] = GROVE_CO2.read();
        }
    }
}
delay(100);
if((i != 9) || (1 + (0xFF ^ (byte)(data[1] + data[2] + data[3] + data[4] + data[5] + data[6] + data[7]))) !=   data[8])
{
    return false;
}
CO2PPM = (int)data[2] * 256 + (int)data[3];
temperature_co2 = (int)data[4] - 40;
return CO2PPM;
}

static osjob_t sendjob;
static osjob_t displayjob;


// Schedule TX every this many seconds (might become longer due to duty
// cycle limitations).
const unsigned TX_INTERVAL = 60;
const unsigned DISPLAY_INTERVAL=1; //update Display each secnd


// Pin mapping
const lmic_pinmap lmic_pins = {
  .nss = PIN_XB1_CS,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = LMIC_UNUSED_PIN,
  .dio = {PIN_XB1_INT, PIN_XB1_INT, LMIC_UNUSED_PIN},
};

void do_display(osjob_t* ) {
  counter++;
  //display.clearDisplay;
  display.setCursor(0, 0);
  display.setTextSize(2);
  display.setTextColor(WHITE, BLACK);
  display.print("Counter: ");
  display.print(String(counter,DEC));
  display.display();
  //os_setTimedCallback(&displayjob, os_getTime() + sec2osticks(DISPLAY_IN
  os_setTimedCallback(&displayjob, os_getTime() + sec2osticks(DISPLAY_INTERVAL), update_display);
  
}

void onEvent (ev_t ev) {
  senseBoxIO.statusGreen();
  DEBUG(os_getTime());
  switch(ev) {
    case EV_SCAN_TIMEOUT:
      DEBUG(F("EV_SCAN_TIMEOUT"));
      break;
    case EV_BEACON_FOUND:
      DEBUG(F("EV_BEACON_FOUND"));
      break;
    case EV_BEACON_MISSED:
      DEBUG(F("EV_BEACON_MISSED"));
      break;
    case EV_BEACON_TRACKED:
      DEBUG(F("EV_BEACON_TRACKED"));
      break;
    case EV_JOINING:
      DEBUG(F("EV_JOINING"));
      break;
    case EV_JOINED:
      DEBUG(F("EV_JOINED"));

      // Disable link check validation (automatically enabled
      // during join, but not supported by TTN at this time).
      LMIC_setLinkCheckMode(0);
      break;
    case EV_RFU1:
      DEBUG(F("EV_RFU1"));
      break;
    case EV_JOIN_FAILED:
      DEBUG(F("EV_JOIN_FAILED"));
      break;
    case EV_REJOIN_FAILED:
      DEBUG(F("EV_REJOIN_FAILED"));
      break;
    case EV_TXCOMPLETE:
      DEBUG(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
      if (LMIC.txrxFlags & TXRX_ACK)
        DEBUG(F("Received ack"));
      if (LMIC.dataLen) {
        DEBUG(F("Received "));
        DEBUG(LMIC.dataLen);
        DEBUG(F(" bytes of payload"));
      }
      // Schedule next transmission
      os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
      os_setTimedCallback(&displayjob, os_getTime()+sec2osticks(TX_INTERVAL), do_display);
      break;
    case EV_LOST_TSYNC:
      DEBUG(F("EV_LOST_TSYNC"));
      break;
    case EV_RESET:
      DEBUG(F("EV_RESET"));
      break;
    case EV_RXCOMPLETE:
      // data received in ping slot
      DEBUG(F("EV_RXCOMPLETE"));
      break;
    case EV_LINK_DEAD:
      DEBUG(F("EV_LINK_DEAD"));
      break;
    case EV_LINK_ALIVE:
      DEBUG(F("EV_LINK_ALIVE"));
      break;
    default:
      DEBUG(F("Unknown event"));
      break;
  }
}

void do_send(osjob_t* j){
  // Check if there is not a current TX/RX job running
  if (LMIC.opmode & OP_TXRXPEND) {
    DEBUG(F("OP_TXRXPEND, not sending"));
  } else {
    LoraMessage message;

    //-----Temperature-----//
    //-----Humidity-----//
    #ifdef HDC1080_CONNECTED
      DEBUG(F("Temperature: "));
      temperature = HDC.readTemperature();
      DEBUG(temperature);
      message.addUint16((temperature + 18) * 771);
      delay(2000);

      DEBUG(F("Humidity: "));
      humidity = HDC.readHumidity();
      DEBUG(humidity);
      message.addHumidity(humidity);
      delay(2000);
    #endif

    //-----Pressure-----//
    #ifdef BMP280_CONNECTED
      float altitude;
      pressure = BMP.readPressure()/100;
      altitude = BMP.readAltitude(1013.25); //1013.25 = sea level pressure
      DEBUG(F("Pressure: "));
      DEBUG(pressure);
      message.addUint16((pressure - 300) * 81.9187);
      delay(2000);
    #endif

    //-----Lux-----//
    #ifdef TSL45315_CONNECTED
      DEBUG(F("Illuminance: "));
      lux = TSL.readLux();
      DEBUG(lux);
      message.addUint8(lux % 255);
      message.addUint16(lux / 255);
      delay(2000);
    #endif

    //-----UV intensity-----//
    #ifdef VEML6070_CONNECTED
      DEBUG(F("UV: "));
      //uv = VEML.getUV();
      DEBUG(uv);
      message.addUint8(uv % 255);
      message.addUint16(uv / 255);
      delay(2000);
    #endif

    //-----PM-----//
    #ifdef SDS011_CONNECTED
      //uint8_t attempt = 0;
      while (attempt < 5) {
        //bool error = SDS.read(&pm25, &pm10);  //wieder rückgängig
        //if (!error) {
          DEBUG(F("PM10: "));
          DEBUG(pm10);
          message.addUint16(pm10 * 10);
          DEBUG(F("PM2.5: "));
          DEBUG(pm25);
          message.addUint16(pm25 * 10);
          break;
        }
        attempt++;
      }
    #endif


 //---------CO2--------//
    while (attempt < 5) {
    bool error_co2 = dataReceive();
    if (error_co2 != 0) {
      CO2PPM=dataReceive();
      DEBUG(F("CO2 PPM: "));
      DEBUG(CO2PPM);
      Serial.print("CO2: ");
      Serial.println(CO2PPM);
      break;
    }
    CO2PPM=dataReceive();
    Serial.print("CO2: ");
    Serial.println(CO2PPM);
    attempt++;
  }
  CO2=float (CO2PPM);
  message.addUint16(CO2);
  Serial.print(" CO2: ");Serial.println(CO2);   //Kontrolle ob das Programm bis hirherkommt

  
    //-----Soil Temperature & Moisture-----//
    #ifdef SMT50_CONNECTED
      float voltage = analogRead(SOILTEMPPIN) * (3.3 / 1024.0);
      float soilTemperature = (voltage - 0.5) * 100;
      message.addUint16((soilTemperature + 18) * 771);
      voltage = analogRead(SOILMOISPIN) * (3.3 / 1024.0);
      float soilMoisture = (voltage * 50) / 3;
      message.addHumidity(soilMoisture);
    #endif

    //-----dB(A) Sound Level-----//
    #ifdef SOUNDLEVELMETER_CONNECTED
      float v = analogRead(SOUNDMETERPIN) * (3.3 / 1024.0);
      float decibel = v * 50;
      message.addUint16(decibel * 10);
    #endif

    //-----BME680-----//
    #ifdef BME680_CONNECTED
      BME.setGasHeater(0, 0);
      if( BME.performReading()) {
        message.addUint16((BME.temperature-1 + 18) * 771);
        message.addHumidity(BME.humidity);
        message.addUint16((BME.pressure/100 - 300) * 81.9187);
      }
      delay(100);
      BME.setGasHeater(320, 150); // 320*C for 150 ms
      if( BME.performReading()) {
        uint16_t gasResistance = BME.gas_resistance / 1000.0;
        message.addUint8(gasResistance % 255);
        message.addUint16(gasResistance / 255);
      }
    #endif

    //-----Wind speed-----//
    #ifdef WINDSPEED_CONNECTED
      float voltageWind = analogRead(WINDSPEEDPIN) * (3.3 / 1024.0);
      float windspeed = 0.0;
      if (voltageWind >= 0.018){
        float poly1 = pow(voltageWind, 3);
        poly1 = 17.0359801998299 * poly1;
        float poly2 = pow(voltageWind, 2);
        poly2 = 47.9908168343362 * poly2;
        float poly3 = 122.899677524413 * voltageWind;
        float poly4 = 0.657504127272728;
        windspeed = poly1 - poly2 + poly3 - poly4;
        windspeed = windspeed * 0.2777777777777778; //conversion in m/s
      }
      message.addUint16(windspeed * 10);
    #endif

    // Prepare upstream data transmission at the next possible time.
    LMIC_setTxData2(1, message.getBytes(), message.getLength(), 0);
    DEBUG(F("Packet queued"));
  }
  // Next TX is scheduled after TX_COMPLETE event.
}



void setup() {
  #ifdef ENABLE_DEBUG
    Serial.begin(9600);
  #endif
  delay(3000);

  // RFM9X (LoRa-Bee) in XBEE1 Socket
  senseBoxIO.powerXB1(false); // power off to reset RFM9X
  delay(250);
  senseBoxIO.powerXB1(true);  // power on

  // Sensor initialization
  DEBUG(F("Initializing sensors..."));
  #ifdef VEML6070_CONNECTED
    VEML.begin();
    delay(500);
  #endif
  #ifdef HDC1080_CONNECTED
    HDC.begin();
  #endif
  #ifdef BMP280_CONNECTED
    BMP.begin(0x76);
  #endif
  #ifdef TSL45315_CONNECTED
    TSL.begin();
  #endif
  #ifdef SDS011_CONNECTED
    SDS_UART_PORT.begin(9600);
  #endif
  #ifdef BME680_CONNECTED
    BME.begin(0x76);
    BME.setTemperatureOversampling(BME680_OS_8X);
    BME.setHumidityOversampling(BME680_OS_2X);
    BME.setPressureOversampling(BME680_OS_4X);
    BME.setIIRFilterSize(BME680_FILTER_SIZE_3);
  #endif

  DEBUG(F("Sensor initializing done!"));
  DEBUG(F("Starting loop in 3 seconds."));
  delay(3000);

  // 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);
  update_display(&displayjob);
}

void loop() {
  os_runloop_once();
}

Ah auf den ersten Blick sieht es so aus als hättest du die update_display Funktion in do_display umbenannt. Du müsstest also entweder

  • Überall im Code wo update_display in do_display umbenennen
  • Oder überall do_display in update_display umbennennen

Hi @Felix

Heureka, es funktioniert!:smiling_face_with_three_hearts:
Habe es so eingestellt , dass alle 3 Sekunden die Anzeige wechselt. Dass, wenn zwei Werte untereinander stehen sollen , die Anzeige nicht ausreicht (siehe meinen Beitrag " OLED-Display Angaben korrekt?) wird sich auch noch lösen lassen.
Jetzt folgt noch die Feinarbeit.
Zur ursprünglichen Frage: Gibt es irgendwo nähere Informationen über die Möglichkeit mehrere Jobs quasi gleichzeitig laufen zu lassen?

Super, freut mich dass es jetzt funktioniert! Eine wirklich ausführliche Anleitung zu den Jobs habe ich bisher noch nicht wirklich gefunden. Ich kann dir aber die Dokumentation zum LMIC Framework (worauf unser LoRa Code basiert) anbieten: https://github.com/matthijskooijman/arduino-lmic/blob/master/doc/LMiC-v1.5.pdf. Da gibt es im Kapitel 2.1.1. ein paar Informationen zu Jobs, aber auch nicht besonders viel. Ich weiß z.B. auch gar nicht wie viele Jobs mit der senseBox MCU möglich wären.

Das Problem mit dem Display werde ich mir nochmal anschauen.

Hi @Felix,
inzwischen ist mir noch ein Gedanke gekommwn: kann man einen dritten Job erzeugen , MESSEN, der die Sensoren abfragt und die Anzeige beliefert, damit die Anzeige stets aktuell ist und nicht alle 5 bis 6 Minuten nur aktualisiert wird?

HI

ich versuche verzweifelt die Feinstaubmessung in meine LoRa-Cayenne-Station unterzubringen, aber jedesmal verleidet mir eine Fehlermeldung das Ganze:

“int SDS011::read(float*, float*)’ is private within this context”
und zwar in folgender Zeile:

while (attempt < 5) {
bool error = SDS.read(&pm25, &pm10);

Die ganze Routine lief in anderen Programmen einwandfrei
der Feinstaubsensor war folgendermaßen definiert:

SDS011 SDS(Serial2);

ich bin ratlos

@Felix, @david

Geschafft, meine Umweltmessstation ist fertig. Das Problem mit der langsamen Aktalisierung der Daten (alle 6 Minuten habe ich gelöst indem ich die Messroutinen in die Display-update-Routine gepackt habe. Ich brauche also keine extra Messroutine.