221 lines
7.2 KiB
C++
221 lines
7.2 KiB
C++
/***
|
|
* ____ ___ ____ _ _
|
|
* / ___/ _ \___ \ / \ _ __ ___ _ __ ___| |
|
|
* | | | | | |__) | / _ \ | '_ ` _ \| '_ \ / _ \ |
|
|
* | |__| |_| / __/ / ___ \| | | | | | |_) | __/ |
|
|
* \____\___/_____| /_/__ \_\_| |_| |_| .__/ \___|_| _
|
|
* | | | |/ _|_ _| / ___|| |_ _ _| |_| |_ __ _ __ _ _ __| |_
|
|
* | |_| | |_ | | \___ \| __| | | | __| __/ _` |/ _` | '__| __|
|
|
* | _ | _| | | ___) | |_| |_| | |_| || (_| | (_| | | | |_
|
|
* |_| |_|_| |_| |____/ \__|\__,_|\__|\__\__, |\__,_|_| \__|
|
|
* |___/
|
|
*/
|
|
|
|
#include "ampel-firmware.h"
|
|
|
|
/*****************************************************************
|
|
* GPL License *
|
|
*****************************************************************/
|
|
/*
|
|
* This file is part of the "CO2 Ampel" project ( https://transfer.hft-stuttgart.de/gitlab/co2ampel and
|
|
* https://transfer.hft-stuttgart.de/gitlab/co2ampel/ampel-firmware )
|
|
* Copyright (c) 2020 HfT Stuttgart.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*****************************************************************
|
|
* Authors *
|
|
*****************************************************************/
|
|
/*
|
|
* Eric Duminil
|
|
* Robert Otto
|
|
* Myriam Guedey
|
|
* Tobias Gabriel Erhart
|
|
* Jonas Stave
|
|
* Michael Käppler
|
|
*/
|
|
|
|
/*****************************************************************
|
|
* Configuration *
|
|
*****************************************************************/
|
|
/*
|
|
* Please define settings in 'config.h'.
|
|
* There's an example config file called 'config.example.h'.
|
|
* You can copy 'config.public.h' (stored in Git) to 'config.h' (not stored in Git),
|
|
* and define your credentials and parameters in 'config.h'.
|
|
*/
|
|
|
|
/*****************************************************************
|
|
* Setup *
|
|
*****************************************************************/
|
|
void setup() {
|
|
led_effects::setupOnBoardLED();
|
|
led_effects::onBoardLEDOff();
|
|
|
|
Serial.begin(BAUDS);
|
|
|
|
pinMode(buttonPin, INPUT); // Flash button (used for forced calibration)
|
|
|
|
Serial.println();
|
|
Serial.print(F("Sensor ID: "));
|
|
Serial.println(ampel.sensorId);
|
|
Serial.print(F("MAC : "));
|
|
Serial.println(ampel.macAddress);
|
|
Serial.print(F("Board : "));
|
|
Serial.println(ampel.board);
|
|
Serial.print(F("Firmware : "));
|
|
Serial.println(ampel.version);
|
|
|
|
led_effects::setupRing();
|
|
led_effects::showRainbowWheel(5000);
|
|
sensor::initialize();
|
|
|
|
#ifdef AMPEL_CSV
|
|
csv_writer::initialize(ampel.sensorId);
|
|
#endif
|
|
|
|
#ifdef AMPEL_WIFI
|
|
wifi::connect(ampel.sensorId);
|
|
|
|
Serial.print(F("WiFi - Status: "));
|
|
Serial.println(WiFi.status());
|
|
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
# ifdef AMPEL_HTTP
|
|
web_server::initialize();
|
|
# endif
|
|
|
|
ntp::initialize();
|
|
|
|
if (MDNS.begin(ampel.sensorId)) { // Start the mDNS responder for SENSOR_ID.local
|
|
MDNS.addService("http", "tcp", 80);
|
|
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)
|
|
lorawan::initialize();
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************
|
|
* Helper functions *
|
|
*****************************************************************/
|
|
void keepServicesAlive();
|
|
void checkFlashButton();
|
|
void checkSerialInput();
|
|
|
|
/*****************************************************************
|
|
* Main loop *
|
|
*****************************************************************/
|
|
void loop() {
|
|
#if defined(AMPEL_LORAWAN) && defined(ESP32)
|
|
//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.
|
|
return;
|
|
}
|
|
#endif
|
|
//NOTE: Loop should never take more than 1000ms. Split in smaller methods and logic if needed.
|
|
//NOTE: Only use millis() for duration comparison, not timestamps comparison. Otherwise, problems happen when millis roll over.
|
|
uint32_t t0 = millis();
|
|
|
|
keepServicesAlive();
|
|
|
|
// Short press for night mode, Long press for calibration.
|
|
checkFlashButton();
|
|
|
|
checkSerialInput();
|
|
|
|
if (sensor::processData()) {
|
|
#ifdef AMPEL_CSV
|
|
csv_writer::logIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity);
|
|
#endif
|
|
|
|
#if defined(AMPEL_WIFI) && defined(AMPEL_MQTT)
|
|
mqtt::publishIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity);
|
|
#endif
|
|
|
|
#if defined(AMPEL_LORAWAN) && defined(ESP32)
|
|
lorawan::preparePayloadIfTimeHasCome(sensor::co2, sensor::temperature, sensor::humidity);
|
|
#endif
|
|
}
|
|
|
|
uint32_t duration = millis() - t0;
|
|
if (duration > ampel.max_loop_duration) {
|
|
ampel.max_loop_duration = duration;
|
|
Serial.print(F("Debug - Max loop duration : "));
|
|
Serial.print(ampel.max_loop_duration);
|
|
Serial.println(F(" ms."));
|
|
}
|
|
}
|
|
|
|
void checkSerialInput() {
|
|
while (Serial.available() > 0) {
|
|
sensor_console::processSerialInput(Serial.read());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if flash button has been pressed:
|
|
* If not, do nothing.
|
|
* If short press, toggle LED display.
|
|
* If long press, start calibration process.
|
|
*/
|
|
void checkFlashButton() {
|
|
if (!digitalRead(0)) { // Button has been pressed
|
|
led_effects::onBoardLEDOn();
|
|
delay(300);
|
|
if (digitalRead(0)) {
|
|
Serial.println(F("Flash has been pressed for a short time. Should toggle night mode."));
|
|
led_effects::toggleNightMode();
|
|
} else {
|
|
Serial.println(F("Flash has been pressed for a long time. Keep it pressed for calibration."));
|
|
if (led_effects::countdownToZero()) {
|
|
Serial.println(F("You can now release the button."));
|
|
sensor::startCalibrationProcess();
|
|
led_effects::showKITTWheel(color::red, 2);
|
|
}
|
|
}
|
|
led_effects::onBoardLEDOff();
|
|
}
|
|
}
|
|
|
|
void keepServicesAlive() {
|
|
#ifdef AMPEL_WIFI
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
# if defined(ESP8266)
|
|
//NOTE: Sadly, there seems to be a bug in the current MDNS implementation.
|
|
// It stops working after 2 minutes. And forcing a restart leads to a memory leak.
|
|
MDNS.update();
|
|
# 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
|
|
}
|