Skip to content

Instantly share code, notes, and snippets.

@felixerdy
Created January 23, 2020 13:39
Show Gist options
  • Save felixerdy/a42dab711531a8ac587a96b5ab7b24cf to your computer and use it in GitHub Desktop.
Save felixerdy/a42dab711531a8ac587a96b5ab7b24cf to your computer and use it in GitHub Desktop.
/*
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 <SD.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 <Makerblog_TSL45315.h>
#include <VEML6070.h>
#include <SDS011-select-serial.h>
// 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
// Bodenfeuchte 1
#define SMT50_1_CONNECTED
// Bodentemperatur 1
#define SMT50_1_CONNECTED
// Bodenfeuchte 2
#define SMT50_2_CONNECTED
// Bodentemperatur 2
#define SMT50_2_CONNECTED
// Ultraschall
#define ULTRASONIC_CONNECTED
// Display
#define DISPLAY_CONNECTED
// SD
#define SD_CONNECTED
// Number of serial port the SDS011 is connected to. Either Serial1 or Serial2
#ifdef SDS011_CONNECTED
#define SDS_UART_PORT (Serial1)
#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_1_CONNECTED
#define SOILTEMPPIN_1 3
#define SOILMOISPIN_1 4
float soilTemp1 = 0;
float soilMoist1 = 0;
#endif
#ifdef SMT50_2_CONNECTED
#define SOILTEMPPIN_2 5
#define SOILMOISPIN_2 6
float soilTemp2 = 0;
float soilMoist2 = 0;
#endif
#ifdef ULTRASONIC_CONNECTED
int trig = 1; // Trig-Pin des Sensors ist an Pin 1 angeschlossen.
int echo = 2; // Echo-Pin des Sensors ist an Pin 2 angeschlossen.
unsigned int time = 0;
unsigned int distance = 0;
#endif
#ifdef DISPLAY_CONNECTED
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#endif
#ifdef SD_CONNECTED
File myFile;
String fileName = "data.csv";
#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);
}
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);
message.addUint16(lux >> 8);
delay(2000);
#endif
//-----UV intensity-----//
#ifdef VEML6070_CONNECTED
DEBUG(F("UV: "));
uv = VEML.getUV();
DEBUG(uv);
message.addUint8(uv);
message.addUint16(uv >> 8);
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_1_CONNECTED
float voltage_1 = analogRead(SOILTEMPPIN_1) * (3.3 / 1024.0);
soilTemp1 = (voltage_1 - 0.5) * 100;
message.addUint16((soilTemp1 + 18) * 771);
voltage_1 = analogRead(SOILMOISPIN_1) * (3.3 / 1024.0);
soilMoist1 = (voltage_1 * 50) / 3;
message.addHumidity(soilMoist1);
#endif
//-----Soil Temperature & Moisture-----//
#ifdef SMT50_2_CONNECTED
float voltage_2 = analogRead(SOILTEMPPIN_2) * (3.3 / 1024.0);
soilTemp2 = (voltage_2 - 0.5) * 100;
message.addUint16((soilTemp2 + 18) * 771);
voltage_2 = analogRead(SOILMOISPIN_2) * (3.3 / 1024.0);
soilMoist2 = (voltage_2 * 50) / 3;
message.addHumidity(soilMoist2);
#endif
#ifdef ULTRASONIC_CONNECTED
digitalWrite(trig, HIGH);
delayMicroseconds(10);
digitalWrite(trig, LOW);
time = pulseIn(echo, HIGH);
distance = time / 58;
message.addUint8(distance);
message.addUint16(distance >> 8);
#endif
#ifdef SD_CONNECTED
// Datei öffnen mit Schreibzugriff
myFile = SD.open(fileName, FILE_WRITE);
myFile.print(temperature);
myFile.print(",");
myFile.print(humidity);
myFile.print(",");
myFile.print(pressure);
myFile.print(",");
myFile.print(lux);
myFile.print(",");
myFile.print(uv);
myFile.print(",");
myFile.print(pm10);
myFile.print(",");
myFile.print(pm25);
myFile.print(",");
myFile.print(soilTemp1);
myFile.print(",");
myFile.print(soilMoist1);
myFile.print(",");
myFile.print(soilTemp2);
myFile.print(",");
myFile.print(soilMoist2);
myFile.print(",");
myFile.println(distance);
// Nach benutzung wird die Datei wieder geschlossen
myFile.close();
#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 ULTRASONIC_CONNECTED
pinMode(trig, OUTPUT);
pinMode(echo, INPUT);
#endif
#ifdef DISPLAY_CONNECTED
display.begin(SSD1306_SWITCHCAPVCC, 0x3D);
display.display();
delay(100);
display.clearDisplay();
#endif
#ifdef SD_CONNECTED
SD.begin(28);
myFile = SD.open(fileName, FILE_WRITE);
myFile.close();
#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);
}
void loop() {
#ifdef DISPLAY_CONNECTED
printOnDisplay("Temperatur", String(temperature), "*C", "rel. Luftfeuchte", String(humidity), "%");
long start = millis();
while (millis() < start + 8000) {os_runloop_once();}
printOnDisplay("Luftdruck", String(pressure), "hPa", "", "", "");
while (millis() < start + 16000) {os_runloop_once();}
printOnDisplay("Helligkeit", String(lux), "lx", "UV-Intensitaet", String(uv), "uW/cm2");
while (millis() < start + 24000) {os_runloop_once();}
printOnDisplay("PM10", String(pm10), "ug/m3", "PM2.5", String(pm25), "ug/m3");
while (millis() < start + 32000) {os_runloop_once();}
printOnDisplay("Bodenfeuchte", String(soilMoist1), "%", "Bodentemperatur", String(soilTemp1), "*C");
while (millis() < start + 40000) {os_runloop_once();}
printOnDisplay("Bodenfeuchte 2", String(soilMoist2), "%", "Bodentemperatur 2", String(soilTemp2), "*C");
while (millis() < start + 48000) {os_runloop_once();}
printOnDisplay("Distanz", String(distance), "cm", "", "", "");
while (millis() < start + 56000) {os_runloop_once();}
#endif
os_runloop_once();
}
void printOnDisplay(String title1, String measurement1, String unit1, String title2, String measurement2, String unit2) {
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.setTextColor(WHITE, BLACK);
display.println(title1);
display.setCursor(0, 10);
display.setTextSize(2);
display.print(measurement1);
display.print(" ");
display.setTextSize(1);
display.println(unit1);
display.setCursor(0, 30);
display.setTextSize(1);
display.println(title2);
display.setCursor(0, 40);
display.setTextSize(2);
display.print(measurement2);
display.print(" ");
display.setTextSize(1);
display.println(unit2);
display.display();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment