/*** * ____ ___ ____ _ _ * / ___/ _ \___ \ / \ _ __ ___ _ __ ___| | * | | | | | |__) | / _ \ | '_ ` _ \| '_ \ / _ \ | * | |__| |_| / __/ / ___ \| | | | | | |_) | __/ | * \____\___/_____| /_/__ \_\_| |_| |_| .__/ \___|_| _ * | | | |/ _|_ _| / ___|| |_ _ _| |_| |_ __ _ __ _ _ __| |_ * | |_| | |_ | | \___ \| __| | | | __| __/ _` |/ _` | '__| __| * | _ | _| | | ___) | |_| |_| | |_| || (_| | (_| | | | |_ * |_| |_|_| |_| |____/ \__|\__,_|\__|\__\__, |\__,_|_| \__| * |___/ */ #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 . */ /***************************************************************** * Authors * *****************************************************************/ /* * Eric Duminil * Robert Otto * Myriam Guedey * Tobias Gabriel Erhart * Jonas Stave */ /***************************************************************** * 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() { LedEffects::setupOnBoardLED(); LedEffects::onBoardLEDOff(); Serial.begin(BAUDS); pinMode(0, INPUT); // Flash button (used for forced calibration) LedEffects::setupRing(); sensor::initialize(); Serial.print(F("Sensor ID: ")); Serial.println(SENSOR_ID); Serial.print(F("Board : ")); Serial.println(BOARD); // Try to connect to Wi-Fi WiFiConnect(SENSOR_ID); 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(SENSOR_ID.c_str())) { // 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("CO2sensors/" + SENSOR_ID); #endif } #ifdef CSV_WRITER csv_writer::initialize(); #endif #if defined(LORAWAN) && defined(ESP32) lorawan::initialize(); #endif } /***************************************************************** * Main loop * *****************************************************************/ void loop() { #if defined(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. //TODO: Restart every day or week, in order to not let t0 overflow? uint32_t t0 = millis(); keepServicesAlive(); // Short press for night mode, Long press for calibration. checkFlashButton(); if (sensor::processData()) { #ifdef CSV_WRITER csv_writer::logIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity); #endif #ifdef AMPEL_MQTT mqtt::publishIfTimeHasCome(sensor::timestamp, sensor::co2, sensor::temperature, sensor::humidity); #endif #if defined(LORAWAN) && defined(ESP32) lorawan::preparePayloadIfTimeHasCome(sensor::co2, sensor::temperature, sensor::humidity); #endif } uint32_t duration = millis() - t0; if (duration > max_loop_duration) { max_loop_duration = duration; Serial.print("Max loop duration : "); Serial.print(max_loop_duration); Serial.println(" ms."); } } /** * 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 LedEffects::onBoardLEDOn(); delay(300); if (digitalRead(0)) { Serial.println(F("Flash has been pressed for a short time. Should toggle night mode.")); LedEffects::toggleNightMode(); } else { Serial.println(F("Flash has been pressed for a long time. Keep it pressed for calibration.")); if (LedEffects::countdownToZero() < 0) { sensor::startCalibrationProcess(); } } LedEffects::onBoardLEDOff(); } } void keepServicesAlive() { 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 } }