diff --git a/README.md b/README.md index 09bcf3a3850bf2f4ff6606508b5ff05a1c07c805..157f5a33eada1f65e87fc04e695138251c0aa760 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,13 @@ The *CO<sub>2</sub> Ampel* can: * Send data over [LoRaWAN](https://en.wikipedia.org/wiki/LoRa#LoRaWAN). * Display measurements and configuration on a small website. * Log data to a [CSV](https://en.wikipedia.org/wiki/Comma-separated_values) file, directly on the ESP flash memory. +* Accept many interactive commands. ## 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*) * [Sensirion SCD30](https://www.sensirion.com/en/environmental-sensors/carbon-dioxide-sensors/carbon-dioxide-sensors-co2/) "Sensor Module for HVAC and Indoor Air Quality Applications" -* [NeoPixel Ring - 12](https://www.adafruit.com/product/1643) +* [NeoPixel Ring - 12](https://www.adafruit.com/product/1643), or [NeoPixel Ring - 16](https://www.adafruit.com/product/1463) (experimental) See the [documentation](https://transfer.hft-stuttgart.de/gitlab/co2ampel/ampel-documentation) for more info. @@ -67,6 +68,33 @@ make upload board=esp32 && make monitor # For ESP32 * *Upload* * *Tools > Serial Monitor* +## Available commands + +In Arduino IDE *Serial Monitor* or PlatformIO *Monitor*, type `help` + <kbd>Enter</kbd> in order to list the available commands: + +* `auto_calibrate 0/1` (Disables/enables autocalibration). +* `calibrate` (Starts calibration process). +* `calibrate 600` (Starts calibration process, to given ppm). +* `calibrate! 600` (Calibrates right now, to given ppm). +* `co2 1500` (Sets co2 level, for debugging purposes). +* `color 0xFF0015` (Shows color, specified as RGB, for debugging). +* `csv 60` (Sets CSV writing interval, in s). +* `format_filesystem` (Deletes the whole filesystem). +* `free` (Displays available heap space). +* `local_ip` (Displays local IP and current SSID). +* `lora 300` (Sets LoRaWAN sending interval, in s). +* `mqtt 60` (Sets MQTT sending interval, in s). +* `night_mode` (Toggles night mode on/off). +* `reset` (Restarts the sensor). +* `reset_scd` (Resets SCD30). +* `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). +* `show_csv` (Displays the complete CSV file on Serial). +* `timer 30` (Sets measurement interval, in s). +* `wifi_scan` (Scans available WiFi networks). + +The commands can be sent via the Serial interface, from the webpage or via MQTT. + ## Authors * Eric Duminil (HfT Stuttgart) diff --git a/ampel-firmware/co2_sensor.cpp b/ampel-firmware/co2_sensor.cpp index e68b2ea43514e685332229e33eab2f0ea54d4a57..261e5a341f85395701d76f24c31d2f334ee32f58 100644 --- a/ampel-firmware/co2_sensor.cpp +++ b/ampel-firmware/co2_sensor.cpp @@ -113,6 +113,7 @@ namespace sensor { sensor_console::defineIntCommand("calibrate!", calibrateSensorRightNow, F("600 (Calibrates right now, to given ppm)")); sensor_console::defineIntCommand("auto_calibrate", setAutoCalibration, F("0/1 (Disables/enables autocalibration)")); + sensor_console::defineCommand("reset_scd", resetSCD, F("(Resets SCD30)")); } bool hasSensorSettled() { @@ -157,13 +158,14 @@ namespace sensor { switchState(PREPARE_CALIBRATION_UNSTABLE); } - void calibrateAndRestart() { + void calibrate() { Serial.print(F("Calibrating SCD30 now...")); scd30.setAltitudeCompensation(config::altitude_above_sea_level); scd30.setForcedRecalibrationFactor(config::co2_calibration_level); Serial.println(F(" Done!")); Serial.println(F("Sensor calibrated.")); - ESP.restart(); // softer than ESP.reset + //WARNING: Do not reset the ampel or the SCD30! + //At least one measurement needs to happen in order for the calibration to be correctly applied. } void logToSerial() { @@ -209,7 +211,7 @@ namespace sensor { // leave them before calibration is done. bool ready_for_calibration = countStableMeasurements(); if (ready_for_calibration) { - calibrateAndRestart(); + calibrate(); } } else if (co2 < 250) { // Sensor should be calibrated. @@ -325,7 +327,13 @@ namespace sensor { config::co2_calibration_level = calibrationLevel; Serial.print(config::co2_calibration_level); Serial.println(F(" ppm.")); - calibrateAndRestart(); + calibrate(); } } + + void resetSCD() { + Serial.print(F("Resetting SCD30...")); + scd30.reset(); + Serial.println(F("done.")); + } } diff --git a/ampel-firmware/co2_sensor.h b/ampel-firmware/co2_sensor.h index 318b9596ae50ce15476366c3481d45e238895291..d9eff8724f018e885f260060aaed58678c814092 100644 --- a/ampel-firmware/co2_sensor.h +++ b/ampel-firmware/co2_sensor.h @@ -33,5 +33,6 @@ namespace sensor { void calibrateSensorToSpecificPPM(int32_t calibrationLevel); void calibrateSensorRightNow(int32_t calibrationLevel); void setAutoCalibration(int32_t autoCalibration); + void resetSCD(); } #endif diff --git a/ampel-firmware/mqtt.cpp b/ampel-firmware/mqtt.cpp index cb0c5019c00d14094d6389684bb120eb061197f6..8c61696569dbf14dbf520ff4dacd0f462ec86b03 100644 --- a/ampel-firmware/mqtt.cpp +++ b/ampel-firmware/mqtt.cpp @@ -30,10 +30,9 @@ namespace mqtt { void initialize(const char *sensorId) { json_sensor_format = PSTR("{\"time\":\"%s\", \"co2\":%d, \"temp\":%.1f, \"rh\":%.1f}"); snprintf(publish_topic, sizeof(publish_topic), "CO2sensors/%s", sensorId); -#if defined(ESP8266) - espClient.setInsecure(); // Sorry, we don't want to flash the sensors every 3 months. -#endif - // mqttClient.setSocketTimeout(config::mqtt_timeout); //NOTE: somehow doesn't seem to have any effect on connect() + // The sensor doesn't check the fingerprint of the MQTT broker, because otherwise this fingerprint should be updated + // on the sensor every 3 months. The connection can still be encrypted, though: + espClient.setInsecure(); // If not available for ESP32, please update Arduino IDE / PlatformIO mqttClient.setServer(config::mqtt_server, config::mqtt_port); sensor_console::defineIntCommand("mqtt", setMQTTinterval, F("60 (Sets MQTT sending interval, in s)")); diff --git a/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/LICENSE.md b/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/LICENSE.md index e64bd4ef35dab8bab46d8dfe2833a13d2e95b852..37238686da3d06815b45154961d5744d28516153 100644 --- a/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/LICENSE.md +++ b/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/LICENSE.md @@ -34,7 +34,7 @@ Code The MIT License (MIT) -Copyright (c) 2016 SparkFun Electronics +Copyright (c) 2020 SparkFun Electronics Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/README.md b/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/README.md index 2f7c69c4c8967f95ba0ce8eaeafe8dd4e041dbcc..9be1a0eae22818145a733f66955603ef007c49a9 100644 --- a/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/README.md +++ b/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/README.md @@ -24,6 +24,7 @@ Thanks to! * [awatterott](https://github.com/awatterott) for adding [getAltitudeCompensation()](https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library/pull/18) * [jogi-k](https://github.com/jogi-k) for adding [teensy i2clib](https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library/pull/19) support * [paulvha](https://github.com/paulvha) for the suggestions and corrections in [his version of the library](https://github.com/paulvha/scd30) +* [yamamaya](https://github.com/yamamaya) for the [3ms delay](https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library/pull/24) Repository Contents ------------------- @@ -43,8 +44,6 @@ License Information This product is _**open source**_! -Various bits of the code have different licenses applied. Anything SparkFun wrote is beerware; if you see me (or any other SparkFun employee) at the local, and you've found our code helpful, please buy us a round! - Please use, reuse, and modify these files as you see fit. Please maintain attribution to SparkFun Electronics and release anything derivative under the same license. Distributed as-is; no warranty is given. diff --git a/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/library.properties b/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/library.properties index a02d23554f4380fc445fdcbd48bf4c680da0bf9a..1e004493e332a8dcc2af1dce00ee7aab8e3bdc72 100644 --- a/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/library.properties +++ b/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/library.properties @@ -1,5 +1,5 @@ name=SparkFun SCD30 Arduino Library -version=1.0.11 +version=1.0.13 author=SparkFun Electronics maintainer=SparkFun Electronics <sparkfun.com> sentence=Library for the Sensirion SCD30 CO2 Sensor diff --git a/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.cpp b/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.cpp index c2a4230c587db68fdd8cf2fc69877d683cf5ce67..3d4ed4558b8e321a5308c9f195212e2382ac0e63 100644 --- a/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.cpp +++ b/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.cpp @@ -266,6 +266,8 @@ bool SCD30::readMeasurement() if (_i2cPort->endTransmission() != 0) return (0); //Sensor did not ACK + delay(3); + const uint8_t receivedBytes = _i2cPort->requestFrom((uint8_t)SCD30_ADDRESS, (uint8_t)18); bool error = false; if (_i2cPort->available()) @@ -358,6 +360,8 @@ bool SCD30::getSettingValue(uint16_t registerAddress, uint16_t *val) if (_i2cPort->endTransmission() != 0) return (false); //Sensor did not ACK + delay(3); + _i2cPort->requestFrom((uint8_t)SCD30_ADDRESS, (uint8_t)3); // Request data and CRC if (_i2cPort->available()) { @@ -389,6 +393,8 @@ uint16_t SCD30::readRegister(uint16_t registerAddress) if (_i2cPort->endTransmission() != 0) return (0); //Sensor did not ACK + delay(3); + _i2cPort->requestFrom((uint8_t)SCD30_ADDRESS, (uint8_t)2); if (_i2cPort->available()) { diff --git a/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.h b/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.h index 3f39996a2c3800794c533df04b3a542f9a43fe14..0104d092167cac83b806c41fdc8694e504b607d2 100644 --- a/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.h +++ b/ampel-firmware/src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.h @@ -57,9 +57,10 @@ #define COMMAND_STOP_MEAS 0x0104 #define COMMAND_READ_FW_VER 0xD100 -typedef union { - byte array[4]; - float value; +typedef union +{ + byte array[4]; + float value; } ByteToFl; // paulvha class SCD30 @@ -69,9 +70,9 @@ public: bool begin(bool autoCalibrate) { return begin(Wire, autoCalibrate); } #ifdef USE_TEENSY3_I2C_LIB - bool begin(i2c_t3 &wirePort = Wire, bool autoCalibrate=true, bool measBegin=true); //By default use Wire port + bool begin(i2c_t3 &wirePort = Wire, bool autoCalibrate = false, bool measBegin = true); //By default use Wire port #else - bool begin(TwoWire &wirePort = Wire, bool autoCalibrate=true, bool measBegin=true); //By default use Wire port + bool begin(TwoWire &wirePort = Wire, bool autoCalibrate = false, bool measBegin = true); //By default use Wire port #endif void enableDebugging(Stream &debugPort = Serial); //Turn on debug printing. If user doesn't specify then Serial will be used. @@ -82,11 +83,11 @@ public: // based on paulvha bool getSettingValue(uint16_t registerAddress, uint16_t *val); - bool getForcedRecalibration(uint16_t *val) {return(getSettingValue(COMMAND_SET_FORCED_RECALIBRATION_FACTOR, val));} - bool getMeasurementInterval(uint16_t *val) {return(getSettingValue(COMMAND_SET_MEASUREMENT_INTERVAL, val));} - bool getTemperatureOffset(uint16_t *val) {return(getSettingValue(COMMAND_SET_TEMPERATURE_OFFSET, val));} - bool getAltitudeCompensation(uint16_t *val) {return(getSettingValue(COMMAND_SET_ALTITUDE_COMPENSATION, val));} - bool getFirmwareVersion(uint16_t *val) {return(getSettingValue(COMMAND_READ_FW_VER, val));} + bool getForcedRecalibration(uint16_t *val) { return (getSettingValue(COMMAND_SET_FORCED_RECALIBRATION_FACTOR, val)); } + bool getMeasurementInterval(uint16_t *val) { return (getSettingValue(COMMAND_SET_MEASUREMENT_INTERVAL, val)); } + bool getTemperatureOffset(uint16_t *val) { return (getSettingValue(COMMAND_SET_TEMPERATURE_OFFSET, val)); } + bool getAltitudeCompensation(uint16_t *val) { return (getSettingValue(COMMAND_SET_ALTITUDE_COMPENSATION, val)); } + bool getFirmwareVersion(uint16_t *val) { return (getSettingValue(COMMAND_READ_FW_VER, val)); } uint16_t getCO2(void); float getHumidity(void); @@ -115,12 +116,11 @@ public: uint8_t computeCRC8(uint8_t data[], uint8_t len); private: - //Variables #ifdef USE_TEENSY3_I2C_LIB i2c_t3 *_i2cPort; //The generic connection to user's chosen I2C hardware #else - TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware + TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware #endif //Global main datums float co2 = 0; @@ -136,6 +136,5 @@ private: //Debug Stream *_debugPort; //The stream to send debug messages to if enabled. Usually Serial. boolean _printDebug = false; //Flag to print debugging variables - }; #endif diff --git a/ampel-firmware/util.h b/ampel-firmware/util.h index 79fdb9ee2e1cffac84415be6554bbf5f68e3ddba..a64f460c48b9faec111fa72437f568b3c0f9c780 100644 --- a/ampel-firmware/util.h +++ b/ampel-firmware/util.h @@ -38,7 +38,7 @@ class Ampel { private: static void showFreeSpace(); public: - const char *version = "v0.1.1"; // Update manually after significant changes. + const char *version = "v0.2.0"; // Update manually after significant changes. const char *board; const char *sensorId; uint32_t max_loop_duration;