Commit 8efb0a30 authored by Eric Duminil's avatar Eric Duminil
Browse files

Merge branch 'experimental/iotwebconfig' into develop

Current state seems to be stable enough for develop
parents 34496cfe 6ea33a0e
Pipeline #5893 passed with stage
in 2 minutes and 22 seconds
...@@ -6,6 +6,34 @@ It measures the current CO<sub>2</sub> concentration (in ppm), and displays it o ...@@ -6,6 +6,34 @@ It measures the current CO<sub>2</sub> concentration (in ppm), and displays it o
The room should be ventilated as soon as one LED turns red. The room should be ventilated as soon as one LED turns red.
## Ampel web-conf
Ampel-firmware + [IotWebConf](https://github.com/prampec/IotWebConf).
This is beta software, and bugs are expected! There is not much free RAM left on ESP8266, so the ESP might crash randomly. :-/
* You probably need to update `config.h`. There's a new template in `config.public.h`.
* Every parameter can be set in the web-configuration, so you don't have to modify `config.h` once it's been updated.
* Flash
* Start the *Ampel*
* It should blink turquoise, to indicate *Access Point* mode
* Connect with a laptop/smartphone to ESPxxxxxx WiFi.
* Open a browser, it should bring you to the *Ampel* web-page.
* The usual web-page will be shown. You can click on "Configuration".
* You'll have to set an *Ampel* password with at least 8 characters.
* You can set WiFi SSID + Password if you want. It will then try to connect if you disconnect from the *Access Point*.
* If you don't specify SSID + Password, the *Ampel* will simply stay in *Access Point* mode.
* The *Ampel* password will be required if a connection fails, and the *Access Point* starts again.
* The *Ampel* password will be needed (with user 'admin') to connect to the web-page once the *Ampel* is connected to WiFi.
* It might be necessary to reset the *Ampel* after changing important parameters.
* You can rename the *Ampel*. This name will be used instead of ESPxxxxxx for CSV files and the mDNS address. You'll get a new CSV file after renaming, which can be convenient, e.g. to indicate at which location the measurements were made.
* If you forgot the password, you can send `reset_config` as command, and `reset` the ampel.
* If you disabled WiFi, you can enable it again with `wifi 1`.
* It's also possible to specify WiFi SSID and password in the console, with `ssid wifi_ssid_here` and `pwd wifi_password_here` commands.
* You can enable CSV by clicking on "+CSV", or disable it by clicking on "Disable CSV".
* The same applies to MQTT and LoRaWAN.
* If you disable a set, the parameters are still saved, and will appear again once the service is enabled.
## Features ## Features
The *CO<sub>2</sub> Ampel* can: The *CO<sub>2</sub> Ampel* can:
...@@ -72,11 +100,12 @@ make upload board=esp32 && make monitor # For ESP32 ...@@ -72,11 +100,12 @@ make upload board=esp32 && make monitor # For ESP32
In Arduino IDE *Serial Monitor* or PlatformIO *Monitor*, type `help` + <kbd>Enter</kbd> in order to list the available commands: In Arduino IDE *Serial Monitor* or PlatformIO *Monitor*, type `help` + <kbd>Enter</kbd> in order to list the available commands:
* `ap 0/1` (Disables/enables access point).
* `auto_calibrate 0/1` (Disables/enables autocalibration). * `auto_calibrate 0/1` (Disables/enables autocalibration).
* `calibrate` (Starts calibration process).
* `calibrate 600` (Starts calibration process, to given ppm). * `calibrate 600` (Starts calibration process, to given ppm).
* `calibrate` (Starts calibration process).
* `calibrate! 600` (Calibrates right now, to given ppm). * `calibrate! 600` (Calibrates right now, to given ppm).
* `co2 1500` (Sets CO<sub>2</sub> level, for debugging purposes). * `co2 1500` (Sets CO<sub>2</sub> level, for debugging).
* `color 0xFF0015` (Shows color, specified as RGB, for debugging). * `color 0xFF0015` (Shows color, specified as RGB, for debugging).
* `csv 60` (Sets CSV writing interval, in s). * `csv 60` (Sets CSV writing interval, in s).
* `format_filesystem` (Deletes the whole filesystem). * `format_filesystem` (Deletes the whole filesystem).
...@@ -85,12 +114,17 @@ In Arduino IDE *Serial Monitor* or PlatformIO *Monitor*, type `help` + <kbd>Ente ...@@ -85,12 +114,17 @@ In Arduino IDE *Serial Monitor* or PlatformIO *Monitor*, type `help` + <kbd>Ente
* `local_ip` (Displays local IP and current SSID). * `local_ip` (Displays local IP and current SSID).
* `lora 300` (Sets LoRaWAN sending interval, in s). * `lora 300` (Sets LoRaWAN sending interval, in s).
* `mqtt 60` (Sets MQTT sending interval, in s). * `mqtt 60` (Sets MQTT sending interval, in s).
* `pwd abc` (Sets WiFi password to 'abc').
* `reset` (Restarts the ESP). * `reset` (Restarts the ESP).
* `reset_config` (Resets the complete IotWeb config).
* `reset_scd` (Resets SCD30). * `reset_scd` (Resets SCD30).
* `save_config` (Saves the config to EEPROM).
* `send_local_ip` (Sends local IP and SSID via MQTT. Can be useful to find sensor). * `send_local_ip` (Sends local IP and SSID via MQTT. Can be useful to find sensor).
* `set_time 1618829570` (Sets time to the given UNIX time). * `set_time 1618829570` (Sets time to the given UNIX time).
* `show_csv` (Displays the complete CSV file on Serial). * `show_csv` (Displays the complete CSV file on Serial).
* `ssid name` (Sets SSID to 'name').
* `timer 30` (Sets measurement interval, in s). * `timer 30` (Sets measurement interval, in s).
* `wifi 0/1` (Turns Wifi on/off).
* `wifi_scan` (Scans available WiFi networks). * `wifi_scan` (Scans available WiFi networks).
The commands can be sent via the Serial interface, from the webpage or via MQTT. The commands can be sent via the Serial interface, from the webpage or via MQTT.
......
...@@ -3,34 +3,17 @@ ...@@ -3,34 +3,17 @@
/***************************************************************** /*****************************************************************
* Libraries * * Libraries *
*****************************************************************/ *****************************************************************/
#include "config.h"
#ifndef MEASUREMENT_TIMESTEP
# error Missing config.h file. Please copy config.public.h to config.h.
#endif
#ifdef AMPEL_CSV //NOTE: Too many headers. Move them to include/ folder?
# include "csv_writer.h" #include "web_config.h" // Needed for offline config too.
#endif
#ifdef AMPEL_WIFI #include "csv_writer.h"
# include "wifi_util.h"
# ifdef AMPEL_MQTT
# include "mqtt.h"
# endif
# ifdef AMPEL_HTTP
# include "web_server.h"
# endif
# if defined(ESP8266)
//allows sensor to be seen as SENSOR_ID.local, from the local network. For example : espd03cc5.local
# include <ESP8266mDNS.h>
# elif defined(ESP32)
# include <ESPmDNS.h>
# endif
#endif
#ifdef AMPEL_LORAWAN #include "wifi_util.h"
# include "lorawan.h" #include "mqtt.h"
#endif #include "web_server.h"
#include "lorawan.h"
#include "util.h" #include "util.h"
#include "ntp.h" #include "ntp.h"
...@@ -38,4 +21,9 @@ ...@@ -38,4 +21,9 @@
#include "co2_sensor.h" #include "co2_sensor.h"
#include "led_effects.h" #include "led_effects.h"
void wifiConnected();
void wifiFailed();
void keepServicesAlive();
void checkFlashButton();
#endif #endif
...@@ -63,13 +63,21 @@ void setup() { ...@@ -63,13 +63,21 @@ void setup() {
led_effects::setupOnBoardLED(); led_effects::setupOnBoardLED();
led_effects::onBoardLEDOff(); led_effects::onBoardLEDOff();
Serial.begin(BAUDS); Serial.begin(config::bauds);
web_config::initialize();
web_config::setWifiConnectionCallback(wifiConnected);
web_config::setWifiFailCallback(wifiFailed);
pinMode(0, INPUT); // Flash button (used for forced calibration) pinMode(0, INPUT); // Flash button (used for forced calibration)
Serial.println(); Serial.println();
Serial.print(F("Sensor ID: ")); Serial.print(F("Sensor ID: "));
Serial.println(ampel.sensorId); Serial.println(ampel.sensorId);
Serial.print(F("Name : "));
Serial.println(config::ampel_name());
Serial.print(F("MAC : ")); Serial.print(F("MAC : "));
Serial.println(ampel.macAddress); Serial.println(ampel.macAddress);
Serial.print(F("Board : ")); Serial.print(F("Board : "));
...@@ -81,56 +89,36 @@ void setup() { ...@@ -81,56 +89,36 @@ void setup() {
sensor::initialize(); sensor::initialize();
#ifdef AMPEL_CSV csv_writer::initialize(config::ampel_name());
csv_writer::initialize(ampel.sensorId);
#endif
#ifdef AMPEL_WIFI
wifi::connect(ampel.sensorId);
if (wifi::connected()) { ntp::initialize();
# ifdef AMPEL_HTTP
web_server::initialize();
# endif
ntp::initialize(); if (config::is_wifi_on) {
wifi::defineCommands();
if (MDNS.begin(ampel.sensorId)) { // Start the mDNS responder for SENSOR_ID.local web_server::definePages();
MDNS.addService("http", "tcp", 80); wifi::tryConnection();
Serial.println(F("mDNS responder started"));
} else {
Serial.println(F("Error setting up MDNS responder!"));
}
# ifdef AMPEL_MQTT
mqtt::initialize(ampel.sensorId);
# endif
} }
#endif
#if defined(AMPEL_LORAWAN) && defined(ESP32) #if defined(ESP32)
lorawan::initialize(); if (config::is_lorawan_active()) {
lorawan::initialize();
}
#endif #endif
} }
/*****************************************************************
* Helper functions *
*****************************************************************/
void keepServicesAlive();
void checkFlashButton();
void checkSerialInput();
/***************************************************************** /*****************************************************************
* Main loop * * Main loop *
*****************************************************************/ *****************************************************************/
void loop() { void loop() {
#if defined(AMPEL_LORAWAN) && defined(ESP32) #if defined(ESP32)
//LMIC Library seems to be very sensitive to timing issues, so run it first. if (config::is_lorawan_active()) {
lorawan::process(); //LMIC Library seems to be very sensitive to timing issues, so run it first.
lorawan::process();
if (lorawan::waiting_for_confirmation) {
// If node is waiting for join confirmation from Gateway, nothing else should run. if (lorawan::waiting_for_confirmation) {
return; // If node is waiting for join confirmation from Gateway, nothing else should run.
return;
}
} }
#endif #endif
//NOTE: Loop should never take more than 1000ms. Split in smaller methods and logic if needed. //NOTE: Loop should never take more than 1000ms. Split in smaller methods and logic if needed.
...@@ -142,19 +130,21 @@ void loop() { ...@@ -142,19 +130,21 @@ void loop() {
// Short press for night mode, Long press for calibration. // Short press for night mode, Long press for calibration.
checkFlashButton(); checkFlashButton();
checkSerialInput(); sensor_console::checkSerialInput();
if (sensor::processData()) { if (sensor::processData()) {
#ifdef AMPEL_CSV if (config::is_csv_active()) {
csv_writer::logIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity); csv_writer::logIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity);
#endif }
#if defined(AMPEL_WIFI) && defined(AMPEL_MQTT) if (config::is_wifi_on && config::is_mqtt_active()) {
mqtt::publishIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity); mqtt::publishIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity);
#endif }
#if defined(AMPEL_LORAWAN) && defined(ESP32) #if defined(ESP32)
lorawan::preparePayloadIfTimeHasCome(sensor::co2, sensor::temperature, sensor::humidity); if (config::is_lorawan_active()) {
lorawan::preparePayloadIfTimeHasCome(sensor::co2, sensor::temperature, sensor::humidity);
}
#endif #endif
} }
...@@ -167,12 +157,39 @@ void loop() { ...@@ -167,12 +157,39 @@ void loop() {
} }
} }
void checkSerialInput() { /*****************************************************************
while (Serial.available() > 0) { * Callbacks *
sensor_console::processSerialInput(Serial.read()); *****************************************************************/
void wifiConnected() {
led_effects::showKITTWheel(color::green);
Serial.println();
Serial.print(F("WiFi - Connected! IP address: "));
IPAddress address = WiFi.localIP();
snprintf(wifi::local_ip, sizeof(wifi::local_ip), "%d.%d.%d.%d", address[0], address[1], address[2], address[3]);
Serial.println(wifi::local_ip);
ntp::connect();
if (config::is_mqtt_active()) {
mqtt::initialize(ampel.sensorId);
} }
Serial.print(F("You can access this sensor via http://"));
Serial.print(config::ampel_name());
Serial.print(F(".local (might be unstable) or http://"));
Serial.println(WiFi.localIP());
} }
void wifiFailed() {
Serial.print(F("WiFi - Could not connect to "));
Serial.println(config::selected_ssid());
led_effects::showKITTWheel(color::red);
}
/*****************************************************************
* Helper functions *
*****************************************************************/
/** /**
* Checks if flash button has been pressed: * Checks if flash button has been pressed:
* If not, do nothing. * If not, do nothing.
...@@ -186,6 +203,7 @@ void checkFlashButton() { ...@@ -186,6 +203,7 @@ void checkFlashButton() {
if (digitalRead(0)) { if (digitalRead(0)) {
Serial.println(F("Flash has been pressed for a short time. Should toggle night mode.")); Serial.println(F("Flash has been pressed for a short time. Should toggle night mode."));
led_effects::toggleNightMode(); led_effects::toggleNightMode();
//NOTE: Start Access Point instead?
} else { } else {
Serial.println(F("Flash has been pressed for a long time. Keep it pressed for calibration.")); Serial.println(F("Flash has been pressed for a long time. Keep it pressed for calibration."));
if (led_effects::countdownToZero()) { if (led_effects::countdownToZero()) {
...@@ -199,20 +217,13 @@ void checkFlashButton() { ...@@ -199,20 +217,13 @@ void checkFlashButton() {
} }
void keepServicesAlive() { void keepServicesAlive() {
#ifdef AMPEL_WIFI if (config::is_wifi_on) {
if (wifi::connected()) { web_config::update();
# if defined(ESP8266) if (wifi::connected()) {
//NOTE: Sadly, there seems to be a bug in the current MDNS implementation. ntp::update(); // NTP client has its own timer. It will connect to NTP server every 60s.
// It stops working after 2 minutes. And forcing a restart leads to a memory leak. if (config::is_mqtt_active()) {
MDNS.update(); mqtt::keepConnection(); // MQTT client has its own timer. It will keep alive every 15s.
# endif }
ntp::update(); // NTP client has its own timer. It will connect to NTP server every 60s. }
# ifdef AMPEL_HTTP
web_server::update();
# endif
# ifdef AMPEL_MQTT
mqtt::keepConnection(); // MQTT client has its own timer. It will keep alive every 15s.
# endif
} }
#endif
} }
#include "co2_sensor.h" #include "co2_sensor.h"
#include "config.h" #include "web_config.h"
#include "ntp.h" #include "ntp.h"
#include "led_effects.h" #include "led_effects.h"
#include "sensor_console.h" #include "sensor_console.h"
...@@ -11,25 +11,12 @@ ...@@ -11,25 +11,12 @@
#include "src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.h" // From: http://librarymanager/All#SparkFun_SCD30 #include "src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.h" // From: http://librarymanager/All#SparkFun_SCD30
namespace config { namespace config {
// UPPERCASE values should be defined in config.h
uint16_t measurement_timestep = MEASUREMENT_TIMESTEP; // [s] Value between 2 and 1800 (range for SCD30 sensor).
const uint16_t altitude_above_sea_level = ALTITUDE_ABOVE_SEA_LEVEL; // [m]
uint16_t co2_calibration_level = ATMOSPHERIC_CO2_CONCENTRATION; // [ppm]
const uint16_t measurement_timestep_bootup = 5; // [s] Measurement timestep during acclimatization. const uint16_t measurement_timestep_bootup = 5; // [s] Measurement timestep during acclimatization.
const uint8_t max_deviation_during_bootup = 20; // [%] const uint8_t max_deviation_during_bootup = 20; // [%]
const int8_t max_deviation_during_calibration = 30; // [ppm] const int8_t max_deviation_during_calibration = 30; // [ppm]
const int16_t timestep_during_calibration = 10; // [s] WARNING: Measurements can be unreliable for timesteps shorter than 10s. const int16_t timestep_during_calibration = 10; // [s] WARNING: Measurements can be unreliable for timesteps shorter than 10s.
const int8_t stable_measurements_before_calibration = 120 / timestep_during_calibration; // [-] Stable measurements during at least 2 minutes. const int8_t stable_measurements_before_calibration = 120 / timestep_during_calibration; // [-] Stable measurements during at least 2 minutes.
const uint16_t co2_alert_threshold = 2000; // [ppm] Display a flashing led ring, if concentration exceeds this value const uint16_t co2_alert_threshold = 2000; // [ppm] Display a flashing led ring, if concentration exceeds this value
#ifdef TEMPERATURE_OFFSET
// Residual heat from CO2 sensor seems to be high enough to change the temperature reading. How much should it be offset?
// NOTE: Sign isn't relevant. The returned temperature will always be shifted down.
const float temperature_offset = TEMPERATURE_OFFSET; // [K]
#else
const float temperature_offset = -3.0; // [K] Temperature measured by sensor is usually at least 3K too high.
#endif
bool auto_calibrate_sensor = AUTO_CALIBRATE_SENSOR; // [true / false]
const bool debug_sensor_states = false; // If true, log state transitions over serial console const bool debug_sensor_states = false; // If true, log state transitions over serial console
} }
...@@ -116,7 +103,7 @@ namespace sensor { ...@@ -116,7 +103,7 @@ namespace sensor {
Serial.println(F(" s during acclimatization.")); Serial.println(F(" s during acclimatization."));
scd30.setMeasurementInterval(config::measurement_timestep_bootup); // [s] scd30.setMeasurementInterval(config::measurement_timestep_bootup); // [s]
sensor_console::defineIntCommand("co2", setCO2forDebugging, F("1500 (Sets co2 level, for debugging purposes)")); sensor_console::defineIntCommand("co2", setCO2forDebugging, F("1500 (Sets co2 level, for debugging)"));
sensor_console::defineIntCommand("timer", setTimer, F("30 (Sets measurement interval, in s)")); sensor_console::defineIntCommand("timer", setTimer, F("30 (Sets measurement interval, in s)"));
sensor_console::defineCommand("calibrate", startCalibrationProcess, F("(Starts calibration process)")); sensor_console::defineCommand("calibrate", startCalibrationProcess, F("(Starts calibration process)"));
sensor_console::defineIntCommand("calibrate", calibrateSensorToSpecificPPM, sensor_console::defineIntCommand("calibrate", calibrateSensorToSpecificPPM,
...@@ -243,7 +230,7 @@ namespace sensor { ...@@ -243,7 +230,7 @@ namespace sensor {
delay(100); delay(100);
} else { } else {
// Display a flashing led ring, if concentration exceeds a specific value // Display a flashing led ring, if concentration exceeds a specific value
led_effects::redAlert(); led_effects::alert(color::red);
} }
} }
......
...@@ -3,13 +3,6 @@ ...@@ -3,13 +3,6 @@
#include <stdint.h> // For uint16_t #include <stdint.h> // For uint16_t
namespace config {
extern uint16_t measurement_timestep; // [s] Value between 2 and 1800 (range for SCD30 sensor)
extern bool auto_calibrate_sensor; // [true / false]
extern uint16_t co2_calibration_level; // [ppm]
extern const float temperature_offset; // [K] Sign isn't relevant.
}
namespace sensor { namespace sensor {
extern uint16_t co2; extern uint16_t co2;
extern float temperature; extern float temperature;
......
#ifndef CONFIG_H_INCLUDED #ifndef CONFIG_H_INCLUDED
# define CONFIG_H_INCLUDED # define CONFIG_H_INCLUDED
// This file is a config template, and can be copied to config.h. Please don't save any important password in this template. /***
* _ _____ __ _
* /\ | |/ ____| / _(_)
* / \ _ __ ___ _ __ ___| | | ___ _ __ | |_ _ __ _
* / /\ \ | '_ ` _ \| '_ \ / _ \ | | / _ \| '_ \| _| |/ _` |
* / ____ \| | | | | | |_) | __/ | |___| (_) | | | | | | | (_| |
* /_/ \_\_| |_| |_| .__/ \___|_|\_____\___/|_| |_|_| |_|\__, |
* | | __/ |
* |_| |___/
*/
// This file is a config template, and can be copied to config.h.
// Please don't save any important password in this template.
// NOTE: Every parameter can be modified and saved later via the web-config.
// Some of those parameters can also be modified via commands in the Serial monitor :
// e.g. 'wifi 0' to turn WiFi off, or 'csv 60' to log data in csv every minute.
/***
* AMPEL
*/
// You can rename the Ampel if you want.
// This name will be used for CSV files and the mDNS address.
// You'll get a new CSV file after renaming, which can be convenient, e.g. after moving
// the ampel to another room.
// If left empty, the name will be ESPxxxxxx, where xxxxxx represent the last half of the MAC address.
# define AMPEL_NAME ""
/** /**
* SERVICES * SERVICES
*/ */
// Comment or remove those lines if you want to disable the corresponding services // Define the default for corresponding services. They can be enabled/disabled later in the web-config.
# define AMPEL_WIFI // Should ESP connect to WiFi? It allows the Ampel to get time from an NTP server. # define AMPEL_WIFI true // Should ESP connect to WiFi? It allows the Ampel to get time from an NTP server.
# define AMPEL_HTTP // Should HTTP web server be started? (AMPEL_WIFI should be enabled too) # define AMPEL_MQTT true // Should data be sent over MQTT? (AMPEL_WIFI should be enabled too)
# define AMPEL_MQTT // Should data be sent over MQTT? (AMPEL_WIFI should be enabled too) # define AMPEL_CSV true // Should data be logged as CSV, on the ESP flash memory?
# define AMPEL_CSV // Should data be logged as CSV, on the ESP flash memory? # define AMPEL_LORAWAN false // Should data be sent over LoRaWAN? (Requires ESP32 + LoRa modem, and "MCCI LoRaWAN LMIC library")
// # define AMPEL_LORAWAN // Should data be sent over LoRaWAN? (Requires ESP32 + LoRa modem, and "MCCI LoRaWAN LMIC library")
/** /**
* WIFI * WIFI
*/ */
# define WIFI_SSID "MY_SSID" // SSID and PASSWORD need to be defined, but can be empty.
# define WIFI_PASSWORD "P4SSW0RD" # define WIFI_SSID ""
# define WIFI_PASSWORD ""
# define WIFI_TIMEOUT 30 // [s] # define WIFI_TIMEOUT 30 // [s]
/** /**
...@@ -69,13 +93,14 @@ ...@@ -69,13 +93,14 @@
# define LED_COUNT 12 # define LED_COUNT 12
/** /**
* WEB SERVER * AMPEL PASSWORD
* available at http://local_ip, with user HTTP_USER and password HTTP_PASSWORD * will be used for Access Point (without username), and for web-server available at http://local_ip
* with user 'admin', without quotes.
*/ */
// Define empty strings in order to disable authentication, or remove the constants altogether. // If left empty, the password will be set during the first configuration, via access point.
# define HTTP_USER "co2ampel" // In order to be set successfully, it should have at least 8 characters.
# define HTTP_PASSWORD "my_password" # define AMPEL_PASSWORD ""
/** /**
* MQTT * MQTT
...@@ -119,36 +144,35 @@ ...@@ -119,36 +144,35 @@
*/ */
// 1) Requires "MCCI LoRaWAN LMIC library", which will be automatically used with PlatformIO but should be added in "Arduino IDE". // 1) Requires "MCCI LoRaWAN LMIC library", which will be automatically used with PlatformIO but should be added in "Arduino IDE".
// 2) If you need to, region and transceiver type can be specified in lorawan.cpp. Default is "Europe 868" // 2) Region and transceiver type should be specified in:
// * Arduino/libraries/MCCI_LoRaWAN_LMIC_library/project_config/lmic_project_config.h for Arduino IDE
// * platformio.ini for PlatformIO
// See https://github.com/mcci-catena/arduino-lmic#configuration for more information
// 3) It has been tested with "TTGO ESP32 SX1276 LoRa 868" and will only work with an ESP32 + LoRa modem // 3) It has been tested with "TTGO ESP32 SX1276 LoRa 868" and will only work with an ESP32 + LoRa modem
// 4) In order to use LoRaWAN, a gateway should be close to the co2ampel, and an account, an application and a device should be registered, // 4) In order to use LoRaWAN, a gateway should be close to the co2ampel, and an account, an application and a device should be registered,
// e.g. on https://www.thethingsnetwork.org/docs/applications/ // e.g. on https://www.thethingsindustries.com/docs/integrations/
// 5) The corresponding keys should be defined in LORAWAN_DEVICE_EUI, LORAWAN_APPLICATION_EUI and LORAWAN_APPLICATION_KEY // 5) The corresponding keys should be defined in LORAWAN_DEVICE_EUI, LORAWAN_APPLICATION_EUI and LORAWAN_APPLICATION_KEY
// How often should measurements be sent over LoRaWAN? // How often should measurements be sent over LoRaWAN?
# define LORAWAN_SENDING_INTERVAL 300 // [s] This value should not be too low. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle.html#maximum-duty-cycle # define LORAWAN_SENDING_INTERVAL 300 // [s] This value should not be too low. See https://www.thethingsnetwork.org/docs/lorawan/duty-cycle.html#maximum-duty-cycle
// WARNING: If AMPEL_LORAWAN is enabled, you need to modify the 3 following constants! // WARNING: If AMPEL_LORAWAN is true, you need to modify the 3 following constants
// This EUI must be in little-endian format, so least-significant-byte first. // They are written as hexadecimal strings, and will be parsed in the correct order.
// When copying an EUI from ttnctl output, this means to reverse the bytes.
# define LORAWAN_DEVICE_EUI {0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11} //