Commit 9fe899db authored by Eric Duminil's avatar Eric Duminil
Browse files

Merge branch 'refactor/src_folder' into develop

parents e48ddf50 79e8d26b
......@@ -6,6 +6,18 @@ 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.
## Features
The *CO<sub>2</sub> Ampel* can:
* Display CO2 concentration on LED ring.
* Allow calibration.
* Get current time over NTP
* Send data over MQTT.
* Send data over LoRaWAN.
* Display measurements and configuration on a small website.
* Log data to a CSV file, directly on the ESP flash memory.
## Hardware Requirements
* [ESP8266](https://en.wikipedia.org/wiki/ESP8266) or [ESP32](https://en.wikipedia.org/wiki/ESP32) microcontroller (this project has been tested with *ESP8266 ESP-12 WIFI* and *TTGO ESP32 SX1276 LoRa*)
......
......@@ -3,7 +3,6 @@
/*****************************************************************
* Libraries *
*****************************************************************/
//TODO: check header dependencies, and simplify if possible.
#include "config.h"
#ifndef MEASUREMENT_TIMESTEP
# error Missing config.h file. Please copy config.public.h to config.h.
......@@ -29,7 +28,6 @@
#include "util.h"
#include "co2_sensor.h"
#include "led_effects.h"
#if defined(ESP8266)
......
......@@ -59,14 +59,14 @@
* Setup *
*****************************************************************/
void setup() {
LedEffects::setupOnBoardLED();
LedEffects::onBoardLEDOff();
led_effects::setupOnBoardLED();
led_effects::onBoardLEDOff();
Serial.begin(BAUDS);
pinMode(0, INPUT); // Flash button (used for forced calibration)
LedEffects::setupRing();
led_effects::setupRing();
sensor::initialize();
......@@ -165,18 +165,18 @@ void loop() {
*/
void checkFlashButton() {
if (!digitalRead(0)) { // Button has been pressed
LedEffects::onBoardLEDOn();
led_effects::onBoardLEDOn();
delay(300);
if (digitalRead(0)) {
Serial.println(F("Flash has been pressed for a short time. Should toggle night mode."));
LedEffects::toggleNightMode();
led_effects::toggleNightMode();
} else {
Serial.println(F("Flash has been pressed for a long time. Keep it pressed for calibration."));
if (LedEffects::countdownToZero() < 0) {
if (led_effects::countdownToZero() < 0) {
sensor::startCalibrationProcess();
}
}
LedEffects::onBoardLEDOff();
led_effects::onBoardLEDOff();
}
}
......
......@@ -44,7 +44,7 @@ namespace sensor {
if (scd30.begin(config::auto_calibrate_sensor) == false) {
Serial.println("Air sensor not detected. Please check wiring. Freezing...");
while (1) {
LedEffects::showWaitingLED(color::red);
led_effects::showWaitingLED(color::red);
}
}
......@@ -113,7 +113,7 @@ namespace sensor {
scd30.setForcedRecalibrationFactor(config::co2_calibration_level);
Serial.println(F(" Done!"));
Serial.println(F("Sensor calibrated."));
resetAmpel();
ESP.restart(); // softer than ESP.reset
}
void logToSerial() {
......@@ -129,7 +129,7 @@ namespace sensor {
void displayCO2OnLedRing() {
if (co2 < 250) {
// Sensor should be calibrated.
LedEffects::showWaitingLED(color::magenta);
led_effects::showWaitingLED(color::magenta);
return;
}
/**
......@@ -137,11 +137,11 @@ namespace sensor {
* Those effects include a short delay.
*/
if (co2 < 2000) {
LedEffects::displayCO2color(co2);
LedEffects::breathe(co2);
led_effects::displayCO2color(co2);
led_effects::breathe(co2);
} else {
// >= 2000: entire ring blinks red
LedEffects::redAlert();
led_effects::redAlert();
}
}
......@@ -162,7 +162,7 @@ namespace sensor {
//NOTE: Data is available, but it's sometimes erroneous: the sensor outputs zero ppm but non-zero temperature and non-zero humidity.
if (co2 <= 0) {
// No measurement yet. Waiting.
LedEffects::showWaitingLED(color::blue);
led_effects::showWaitingLED(color::blue);
return false;
}
......@@ -180,7 +180,7 @@ namespace sensor {
if (stable_measurements == 60) {
calibrateAndRestart();
}
LedEffects::showWaitingLED(waiting_color);
led_effects::showWaitingLED(waiting_color);
return false;
}
......
......@@ -8,9 +8,9 @@
*/
// Comment or remove those lines if you want to disable the corresponding services
# define AMPEL_WIFI // WiFi, and all other dependent services (MQTT, HTTP, NTP, ...)
# define AMPEL_HTTP // Should HTTP web server be started?
# define AMPEL_MQTT // Should data be sent over MQTT?
# define AMPEL_WIFI // 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 // Should data be sent over MQTT? (AMPEL_WIFI should be enabled too)
# define AMPEL_CSV // Should data be logged as CSV, on the ESP flash memory?
// # define AMPEL_LORAWAN // Should data be sent over LoRaWAN? (Requires ESP32 + LoRa modem, and "MCCI LoRaWAN LMIC library")
......@@ -124,7 +124,6 @@
// 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/
// 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?
# 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
......
......@@ -135,7 +135,7 @@ namespace csv_writer {
}
void log(const String &timeStamp, const int16_t &co2, const float &temperature, const float &humidity) {
LedEffects::onBoardLEDOn();
led_effects::onBoardLEDOn();
File csv_file = openOrCreate();
char csv_line[42];
snprintf(csv_line, sizeof(csv_line), "%s;%d;%.1f;%.1f\r\n", timeStamp.c_str(), co2, temperature, humidity);
......@@ -155,7 +155,7 @@ namespace csv_writer {
//NOTE: Can it ever happen that outfile is false?
Serial.println(F("Problem on create file!"));
}
LedEffects::onBoardLEDOff();
led_effects::onBoardLEDOff();
}
void logIfTimeHasCome(const String &timeStamp, const int16_t &co2, const float &temperature, const float &humidity) {
......
......@@ -11,8 +11,10 @@
# error Board should be either ESP8266 or ESP832
#endif
#include "led_effects.h"
#include "config.h"
#include "util.h"
#include "led_effects.h"
namespace config {
extern uint16_t csv_interval; // [s]
}
......
......@@ -33,7 +33,7 @@ const uint16_t CO2_TICKS[NUMPIXELS + 1] = { 0, 500, 600, 700, 800, 900, 1000, 12
const uint16_t LED_HUES[NUMPIXELS] = { 21845, 19114, 16383, 13653, 10922, 8191, 5461, 2730, 0, 0, 0, 0 }; // [hue angle]
Adafruit_NeoPixel pixels(NUMPIXELS, NEOPIXELS_PIN, NEO_GRB + NEO_KHZ800);
namespace LedEffects {
namespace led_effects {
//On-board LED on D4, aka GPIO02
const int ONBOARD_LED_PIN = 2;
......@@ -139,14 +139,14 @@ namespace LedEffects {
pixels.show();
}
void showRainbowWheel(int duration_s, uint16_t hue_increment) {
void showRainbowWheel(int duration_ms, uint16_t hue_increment) {
if (config::night_mode) {
return;
}
static uint16_t wheel_offset = 0;
unsigned long t0 = seconds();
unsigned long t0 = millis();
pixels.setBrightness(config::max_brightness);
while (seconds() < t0 + duration_s) {
while (millis() < t0 + duration_ms) {
for (int i = 0; i < NUMPIXELS; i++) {
pixels.setPixelColor(i, pixels.ColorHSV(i * 65535 / NUMPIXELS + wheel_offset));
wheel_offset += hue_increment;
......
#ifndef LED_EFFECTS_H_INCLUDED
#define LED_EFFECTS_H_INCLUDED
#include <Arduino.h>
#include "util.h"
#include "config.h"
// Adafruit NeoPixel (Arduino library for controlling single-wire-based LED pixels and strip)
......@@ -17,7 +16,7 @@ namespace color {
const uint32_t magenta = 0xFF00FF;
}
namespace LedEffects {
namespace led_effects {
void setupOnBoardLED();
void onBoardLEDOff();
void onBoardLEDOn();
......@@ -30,7 +29,7 @@ namespace LedEffects {
int countdownToZero();
void showWaitingLED(uint32_t color);
void showKITTWheel(uint32_t color, uint16_t duration_s = 2);
void showRainbowWheel(int duration_s = 1, uint16_t hue_increment = 50);
void showRainbowWheel(int duration_ms = 1000, uint16_t hue_increment = 50);
void displayCO2color(uint16_t co2);
}
#endif
......@@ -74,7 +74,7 @@ namespace lorawan {
case EV_JOINED:
waiting_for_confirmation = false;
connected = true;
LedEffects::onBoardLEDOff();
led_effects::onBoardLEDOff();
Serial.println(F("EV_JOINED"));
{
u4_t netid = 0;
......@@ -121,12 +121,12 @@ namespace lorawan {
break;
case EV_TXCANCELED:
waiting_for_confirmation = false;
LedEffects::onBoardLEDOff();
led_effects::onBoardLEDOff();
Serial.println(F("EV_TXCANCELED"));
break;
case EV_JOIN_TXCOMPLETE:
waiting_for_confirmation = false;
LedEffects::onBoardLEDOff();
led_effects::onBoardLEDOff();
Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept."));
Serial.println(F("Other services may resume."));
break;
......@@ -136,7 +136,7 @@ namespace lorawan {
break;
}
if (waiting_for_confirmation) {
LedEffects::onBoardLEDOn();
led_effects::onBoardLEDOn();
Serial.println(F("LoRa - waiting for OTAA confirmation. Freezing every other service!"));
}
}
......
#ifndef AMPEL_LORAWAN_H_
#define AMPEL_LORAWAN_H_
#include "config.h"
#if defined(AMPEL_LORAWAN) && defined(ESP32)
#include <Arduino.h>
// Requires "MCCI LoRaWAN LMIC library", which will be automatically used with PlatformIO but should be added in "Arduino IDE".
......@@ -10,7 +13,6 @@
#include <SPI.h>
#include "led_effects.h"
#include "config.h"
#include "util.h"
......
......@@ -40,7 +40,7 @@ namespace mqtt {
void publish(const String &timestamp, int16_t co2, float temperature, float humidity) {
if (WiFi.status() == WL_CONNECTED && mqttClient.connected()) {
LedEffects::onBoardLEDOn();
led_effects::onBoardLEDOn();
Serial.print(F("Publishing MQTT message ... "));
char payload[75]; // Should be enough for json...
......@@ -52,7 +52,7 @@ namespace mqtt {
} else {
Serial.println(F("Failed."));
}
LedEffects::onBoardLEDOff();
led_effects::onBoardLEDOff();
}
}
......@@ -65,7 +65,7 @@ namespace mqtt {
Serial.println("s.");
sensor::scd30.setMeasurementInterval(messageString.toInt());
config::measurement_timestep = messageString.toInt();
LedEffects::showKITTWheel(color::green, 1);
led_effects::showKITTWheel(color::green, 1);
}
}
......@@ -75,7 +75,7 @@ namespace mqtt {
Serial.print(F("Setting Sending Interval to : "));
Serial.print(config::sending_interval);
Serial.println("s.");
LedEffects::showKITTWheel(color::green, 1);
led_effects::showKITTWheel(color::green, 1);
}
#ifdef AMPEL_CSV
......@@ -85,7 +85,7 @@ namespace mqtt {
Serial.print(F("Setting CSV Interval to : "));
Serial.print(config::csv_interval);
Serial.println("s.");
LedEffects::showKITTWheel(color::green, 1);
led_effects::showKITTWheel(color::green, 1);
}
#endif
......@@ -131,7 +131,7 @@ namespace mqtt {
if (length == 0) {
return;
}
LedEffects::onBoardLEDOn();
led_effects::onBoardLEDOn();
Serial.print(F("Message arrived on topic: "));
Serial.print(sub_topic);
Serial.print(F(". Message: '"));
......@@ -160,20 +160,20 @@ namespace mqtt {
setCSVinterval(messageString);
} else if (messageString == "format_filesystem") {
FS_LIB.format();
LedEffects::showKITTWheel(color::blue, 2);
led_effects::showKITTWheel(color::blue, 2);
#endif
} else if (messageString == "night_mode") {
LedEffects::toggleNightMode();
led_effects::toggleNightMode();
} else if (messageString == "local_ip") {
sendInfoAboutLocalNetwork();
} else if (messageString == "reset") {
resetAmpel();
ESP.restart(); // softer than ESP.reset()
} else {
LedEffects::showKITTWheel(color::red, 1);
led_effects::showKITTWheel(color::red, 1);
Serial.println(F("Message not supported. Doing nothing."));
}
delay(50);
LedEffects::onBoardLEDOff();
led_effects::onBoardLEDOff();
}
void reconnect() {
......@@ -187,10 +187,10 @@ namespace mqtt {
}
Serial.print(F("Attempting MQTT connection..."));
LedEffects::onBoardLEDOn();
led_effects::onBoardLEDOn();
// Wait for connection, at most 15s (default)
mqttClient.connect(publish_topic.c_str(), config::mqtt_user, config::mqtt_password);
LedEffects::onBoardLEDOff();
led_effects::onBoardLEDOff();
connected = mqttClient.connected();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment