├── demo.jpg ├── kuhomon.fzz ├── kuhomon_case.stl ├── MH-Z19_CO2 Manual V2.pdf ├── i2c_oled_ssd1306_datasheet.pdf ├── .gitignore ├── platformio.ini ├── README.md ├── lib └── readme.txt ├── CMakeLists.txt ├── .travis.yml ├── CMakeListsPrivate.txt └── src └── main.cpp /demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumekay/kuhomon/HEAD/demo.jpg -------------------------------------------------------------------------------- /kuhomon.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumekay/kuhomon/HEAD/kuhomon.fzz -------------------------------------------------------------------------------- /kuhomon_case.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumekay/kuhomon/HEAD/kuhomon_case.stl -------------------------------------------------------------------------------- /MH-Z19_CO2 Manual V2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumekay/kuhomon/HEAD/MH-Z19_CO2 Manual V2.pdf -------------------------------------------------------------------------------- /i2c_oled_ssd1306_datasheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kumekay/kuhomon/HEAD/i2c_oled_ssd1306_datasheet.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pioenvs 2 | .clang_complete 3 | .gcc-flags.json 4 | .piolibdeps 5 | .pioenvs 6 | .vscode 7 | .vscode/c_cpp_properties.json 8 | .vscode/.browse.c_cpp.db* 9 | .vscode/launch.json 10 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | # 2 | # Project Configuration File 3 | # 4 | # A detailed documentation with the EXAMPLES is located here: 5 | # http://docs.platformio.org/en/latest/projectconf.html 6 | # 7 | 8 | # A sign `#` at the beginning of the line indicates a comment 9 | # Comment lines are ignored. 10 | 11 | # Simple and base environment 12 | # [env:mybaseenv] 13 | # platform = %INSTALLED_PLATFORM_NAME_HERE% 14 | # framework = 15 | # board = 16 | # 17 | # Automatic targets - enable auto-uploading 18 | # targets = upload 19 | 20 | [env:base] 21 | platform = espressif8266 22 | framework = arduino 23 | board = esp12e 24 | lib_deps = 25 | Si7021 26 | Adafruit BME280 Library 27 | ArduinoJson 28 | Blynk 29 | Bounce2 30 | U8g2 31 | SimpleTimer 32 | WIFIManager 33 | Adafruit BMP085 Library 34 | 35 | monitor_baud = 115200 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kumekay Home Monitoring Device 2 | 3 | System for measurement CO2/Humidity/Temperature/Pressure with OLED display and data upload through WiFi 4 | 5 | If you want to use BME280 instead of SI7021+BMP085 take a look on [BME280 branch](https://github.com/pinya/kuhomon/tree/bme280) 6 | 7 | ## Discussion 8 | [![Gitter chat](https://badges.gitter.im/kuhomon.png)](https://gitter.im/kuhomon) 9 | 10 | 11 | ## Components 12 | 13 | - CO2 Sensor MH-Z19 14 | - ESP8266 (NodeMCU ESP12+ based) 15 | - SSD1306 0.96" 128x64 i2c OLED. Library: 16 | - Humidity/Temperature SI7021 i2c. Library: 17 | - Pressure/Temperature BMP085 i2c. Library: 18 | 19 | ## Case 20 | 21 | Model for printing kuhomon_case.stl 22 | 23 | Known issues: 24 | - To fit system to this case you have to use short wires 25 | - Temperature measurements seems to be higher due to heat from ESP8266 26 | 27 | ## TODO 28 | - Add offline support (show data on screen without WiFi) 29 | -------------------------------------------------------------------------------- /lib/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for the project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link to executable file. 4 | 5 | The source code of each library should be placed in separate directory, like 6 | "lib/private_lib/[here are source files]". 7 | 8 | For example, see how can be organized `Foo` and `Bar` libraries: 9 | 10 | |--lib 11 | | |--Bar 12 | | | |--docs 13 | | | |--examples 14 | | | |--src 15 | | | |- Bar.c 16 | | | |- Bar.h 17 | | |--Foo 18 | | | |- Foo.c 19 | | | |- Foo.h 20 | | |- readme.txt --> THIS FILE 21 | |- platformio.ini 22 | |--src 23 | |- main.c 24 | 25 | Then in `src/main.c` you should use: 26 | 27 | #include 28 | #include 29 | 30 | // rest H/C/CPP code 31 | 32 | PlatformIO will find your libraries automatically, configure preprocessor's 33 | include paths and build them. 34 | 35 | See additional options for PlatformIO Library Dependency Finder `lib_*`: 36 | 37 | http://docs.platformio.org/en/latest/projectconf.html#lib-install 38 | 39 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(kuhomon) 3 | 4 | include(CMakeListsPrivate.txt) 5 | 6 | add_definitions(-DF_CPU=80000000L) 7 | add_definitions(-D__ets__) 8 | add_definitions(-DICACHE_FLASH) 9 | add_definitions(-DPLATFORMIO=021001) 10 | add_definitions(-DESP8266) 11 | add_definitions(-DARDUINO_ARCH_ESP8266) 12 | add_definitions(-DARDUINO_ESP8266_ESP12) 13 | add_definitions(-DARDUINO=20200) 14 | 15 | add_custom_target( 16 | PLATFORMIO_BUILD ALL 17 | COMMAND ${PLATFORMIO_CMD} -f -c clion run 18 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 19 | ) 20 | 21 | add_custom_target( 22 | PLATFORMIO_UPLOAD ALL 23 | COMMAND ${PLATFORMIO_CMD} -f -c clion run --target upload 24 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 25 | ) 26 | 27 | add_custom_target( 28 | PLATFORMIO_CLEAN ALL 29 | COMMAND ${PLATFORMIO_CMD} -f -c clion run --target clean 30 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 31 | ) 32 | 33 | add_custom_target( 34 | PLATFORMIO_PROGRAM ALL 35 | COMMAND ${PLATFORMIO_CMD} -f -c clion run --target program 36 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 37 | ) 38 | 39 | add_custom_target( 40 | PLATFORMIO_UPLOADFS ALL 41 | COMMAND ${PLATFORMIO_CMD} -f -c clion run --target uploadfs 42 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 43 | ) 44 | 45 | add_custom_target( 46 | PLATFORMIO_UPDATE_ALL ALL 47 | COMMAND ${PLATFORMIO_CMD} -f -c clion update 48 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 49 | ) 50 | 51 | add_executable(kuhomon 52 | src/main.cpp 53 | ) 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration (CI) is the practice, in software 2 | # engineering, of merging all developer working copies with a shared mainline 3 | # several times a day < http://docs.platformio.org/en/latest/ci/index.html > 4 | # 5 | # Documentation: 6 | # 7 | # * Travis CI Embedded Builds with PlatformIO 8 | # < https://docs.travis-ci.com/user/integration/platformio/ > 9 | # 10 | # * PlatformIO integration with Travis CI 11 | # < http://docs.platformio.org/en/latest/ci/travis.html > 12 | # 13 | # * User Guide for `platformio ci` command 14 | # < http://docs.platformio.org/en/latest/userguide/cmd_ci.html > 15 | # 16 | # 17 | # Please choice one of the following templates (proposed below) and uncomment 18 | # it (remove "# " before each line) or use own configuration according to the 19 | # Travis CI documentation (see above). 20 | # 21 | 22 | 23 | # 24 | # Template #1: General project. Test it using existing `platformio.ini`. 25 | # 26 | 27 | # language: python 28 | # python: 29 | # - "2.7" 30 | # 31 | # sudo: false 32 | # cache: 33 | # directories: 34 | # - "~/.platformio" 35 | # 36 | # install: 37 | # - pip install -U platformio 38 | # 39 | # script: 40 | # - platformio run 41 | 42 | 43 | # 44 | # Template #2: The project is intended to by used as a library with examples 45 | # 46 | 47 | # language: python 48 | # python: 49 | # - "2.7" 50 | # 51 | # sudo: false 52 | # cache: 53 | # directories: 54 | # - "~/.platformio" 55 | # 56 | # env: 57 | # - PLATFORMIO_CI_SRC=path/to/test/file.c 58 | # - PLATFORMIO_CI_SRC=examples/file.ino 59 | # - PLATFORMIO_CI_SRC=path/to/test/directory 60 | # 61 | # install: 62 | # - pip install -U platformio 63 | # 64 | # script: 65 | # - platformio ci --lib="." --board=TYPE_1 --board=TYPE_2 --board=TYPE_N 66 | -------------------------------------------------------------------------------- /CMakeListsPrivate.txt: -------------------------------------------------------------------------------- 1 | set(ENV{PATH} "/home/ku/.local/bin:/home/ku/bin:/home/ku/.rbenv/shims:/home/ku/.rbenv/bin:/home/ku/.local/bin:/home/ku/bin:/home/ku/.rbenv/shims:/home/ku/.rbenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin") 2 | set(PLATFORMIO_CMD "/usr/local/bin/platformio") 3 | 4 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/variants/generic") 5 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/variants/nodemcu") 6 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/cores/esp8266") 7 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/cores/esp8266/libb64") 8 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/cores/esp8266/spiffs") 9 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/cores/esp8266/umm_malloc") 10 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266WiFi/src") 11 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266WiFi/src/include") 12 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/DNSServer/src") 13 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266WebServer/src") 14 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266WebServer/src/detail") 15 | include_directories("$ENV{HOME}/.platformio/lib/WifiManager_ID567") 16 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266HTTPClient/src") 17 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266httpUpdate/src") 18 | include_directories("$ENV{HOME}/.platformio/lib/Blynk_ID415/Adapters") 19 | include_directories("$ENV{HOME}/.platformio/lib/Blynk_ID415/Blynk") 20 | include_directories("$ENV{HOME}/.platformio/lib/Blynk_ID415") 21 | include_directories("$ENV{HOME}/.platformio/lib/Blynk_ID415/utility") 22 | include_directories("$ENV{HOME}/.platformio/lib/Json_ID64") 23 | include_directories("$ENV{HOME}/.platformio/lib/Json_ID64/include") 24 | include_directories("$ENV{HOME}/.platformio/lib/Json_ID64/include/ArduinoJson") 25 | include_directories("$ENV{HOME}/.platformio/lib/Json_ID64/include/ArduinoJson/Internals") 26 | include_directories("$ENV{HOME}/.platformio/lib/Json_ID64/include/ArduinoJson/Polyfills") 27 | include_directories("$ENV{HOME}/.platformio/lib/Json_ID64/include/ArduinoJson/TypeTraits") 28 | include_directories("$ENV{HOME}/.platformio/lib/Json_ID64/src/Internals") 29 | include_directories("$ENV{HOME}/.platformio/lib/Json_ID64/src") 30 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/Wire") 31 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/SPI") 32 | include_directories("$ENV{HOME}/Dropbox/kuhomon/lib/TFT_ILI9163C") 33 | include_directories("$ENV{HOME}/Dropbox/kuhomon/lib/TFT_ILI9163C/_display") 34 | include_directories("$ENV{HOME}/Dropbox/kuhomon/lib/TFT_ILI9163C/_fonts") 35 | include_directories("$ENV{HOME}/Dropbox/kuhomon/lib/TFT_ILI9163C/_includes") 36 | include_directories("$ENV{HOME}/Dropbox/kuhomon/lib/TFT_ILI9163C/_settings") 37 | include_directories("$ENV{HOME}/.platformio/lib/Adafruit_DHT_ID19") 38 | include_directories("$ENV{HOME}/.platformio/lib/SimpleTimer_ID419") 39 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/tools/sdk/include") 40 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/tools/sdk/lwip/include") 41 | include_directories("$ENV{HOME}/Dropbox/kuhomon/src") 42 | include_directories("$ENV{HOME}/.platformio/lib/Adafruit_BMP085_Unified_ID16") 43 | include_directories("$ENV{HOME}/.platformio/lib/Adafruit_GFX_ID13") 44 | include_directories("$ENV{HOME}/.platformio/lib/Adafruit_PCD8544_ID81") 45 | include_directories("$ENV{HOME}/.platformio/lib/Adafruit_Unified_Sensor_ID31") 46 | include_directories("$ENV{HOME}/.platformio/lib/Esp8266Configuration_ID227") 47 | include_directories("$ENV{HOME}/.platformio/lib/Nanopb_ID431") 48 | include_directories("$ENV{HOME}/.platformio/lib/PubSubClient_ID89/src") 49 | include_directories("$ENV{HOME}/.platformio/lib/RTC_ID274/src") 50 | include_directories("$ENV{HOME}/.platformio/lib/Task_ID272/src") 51 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ArduinoOTA") 52 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/EEPROM") 53 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266AVRISP/src") 54 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266HTTPUpdateServer/src") 55 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266SSDP") 56 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266WiFiMesh/src") 57 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/ESP8266mDNS") 58 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/Ethernet/src") 59 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/GDBStub/src") 60 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/Hash/src") 61 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/SD/src") 62 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/Servo/src") 63 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/SoftwareSerial") 64 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/TFT_Touch_Shield_V2") 65 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/Ticker") 66 | include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif/libraries/esp8266/src") 67 | include_directories("$ENV{HOME}/.platformio/packages/toolchain-xtensa/xtensa-lx106-elf/include") 68 | include_directories("$ENV{HOME}/.platformio/packages/toolchain-xtensa/lib/gcc/xtensa-lx106-elf/4.8.2/include") 69 | include_directories("$ENV{HOME}/.platformio/packages/toolchain-xtensa/lib/gcc/xtensa-lx106-elf/4.8.2/include-fixed") 70 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include //https://github.com/esp8266/Arduino 4 | 5 | // Wifi Manager 6 | #include 7 | #include 8 | #include //https://github.com/tzapu/WiFiManager 9 | 10 | // HTTP requests 11 | #include 12 | 13 | // OTA updates 14 | #include 15 | // Blynk 16 | #include 17 | 18 | // Debounce 19 | #include //https://github.com/thomasfredericks/Bounce2 20 | 21 | // JSON 22 | #include //https://github.com/bblanchon/ArduinoJson 23 | 24 | // GPIO Defines 25 | #define I2C_SDA 5 // D1 Orange 26 | #define I2C_SCL 4 // D2 Yellow 27 | #define HW_RESET 12 28 | 29 | // Debounce interval in ms 30 | #define DEBOUNCE_INTERVAL 10 31 | 32 | Bounce hwReset {Bounce()}; 33 | 34 | // Humidity/Temperature SI7021 35 | #include 36 | SI7021 si7021; 37 | 38 | #include 39 | 40 | // Pressure and Temperature 41 | #include 42 | 43 | // Use U8g2 for i2c OLED Lib 44 | #include 45 | #include 46 | U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, I2C_SCL, I2C_SDA, U8X8_PIN_NONE); 47 | byte x {0}; 48 | byte y {0}; 49 | 50 | // Handy timers 51 | #include 52 | 53 | // SW Serial 54 | #include 55 | 56 | SoftwareSerial swSer(13, 15, false, 256); // GPIO15 (TX) and GPIO13 (RX) 57 | 58 | // CO2 SERIAL 59 | #define DEBUG_SERIAL Serial 60 | #define SENSOR_SERIAL swSer 61 | 62 | byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79}; 63 | unsigned char response[7]; 64 | 65 | // Pressure and temperature 66 | Adafruit_BMP085 bme; 67 | 68 | // Blynk token 69 | char blynk_token[33] {"Blynk token"}; 70 | char blynk_server[64] {"blynk-cloud.com"}; 71 | const uint16_t blynk_port {8442}; 72 | 73 | // Device Id 74 | char device_id[17] = "Device ID"; 75 | const char fw_ver[17] = "0.1.0"; 76 | 77 | // Handy timer 78 | SimpleTimer timer; 79 | 80 | // Setup Wifi connection 81 | WiFiManager wifiManager; 82 | 83 | // Network credentials 84 | String ssid { "ku_" + String(ESP.getChipId())}; 85 | String pass {"ku_" + String(ESP.getFlashChipId()) }; 86 | 87 | //flag for saving data 88 | bool shouldSaveConfig = false; 89 | 90 | // Sensors data 91 | int t {-100}; 92 | int p {-1}; 93 | int h {-1}; 94 | int co2 {-1}; 95 | 96 | char loader[4] {'.'}; 97 | 98 | //callback notifying the need to save config 99 | void saveConfigCallback() { 100 | DEBUG_SERIAL.println("Should save config"); 101 | shouldSaveConfig = true; 102 | } 103 | 104 | void factoryReset() { 105 | DEBUG_SERIAL.println("Resetting to factory settings"); 106 | wifiManager.resetSettings(); 107 | SPIFFS.format(); 108 | ESP.reset(); 109 | } 110 | 111 | void printString(String str) { 112 | DEBUG_SERIAL.println(str); 113 | } 114 | 115 | void readCO2() { 116 | // CO2 117 | bool header_found {false}; 118 | 119 | SENSOR_SERIAL.write(cmd, 9); 120 | memset(response, 0, 7); 121 | 122 | // Looking for packet start 123 | while(SENSOR_SERIAL.available() && (!header_found)) { 124 | if(SENSOR_SERIAL.read() == 0xff ) { 125 | if(SENSOR_SERIAL.read() == 0x86 ) header_found = true; 126 | } 127 | } 128 | 129 | if (header_found) { 130 | SENSOR_SERIAL.readBytes(response, 7); 131 | 132 | byte crc = 0x86; 133 | for (char i = 0; i < 6; i++) { 134 | crc+=response[i]; 135 | } 136 | crc = 0xff - crc; 137 | crc++; 138 | 139 | if ( !(response[6] == crc) ) { 140 | DEBUG_SERIAL.println("CO2: CRC error: " + String(crc) + " / "+ String(response[6])); 141 | } else { 142 | unsigned int responseHigh = (unsigned int) response[0]; 143 | unsigned int responseLow = (unsigned int) response[1]; 144 | unsigned int ppm = (256*responseHigh) + responseLow; 145 | co2 = ppm; 146 | DEBUG_SERIAL.println("CO2:" + String(co2)); 147 | } 148 | } else { 149 | DEBUG_SERIAL.println("CO2: Header not found"); 150 | } 151 | 152 | } 153 | 154 | void sendMeasurements() { 155 | // Read data 156 | // Temperature 157 | printString("Getting Temperature from SI7021"); 158 | float tf = si7021.getCelsiusHundredths() / 100.0; 159 | printString("Getting Temperature from BMP085"); 160 | float t2f =bme.readTemperature(); 161 | t = static_cast((tf + t2f) / 2); 162 | 163 | // Humidity 164 | printString("Getting Humidity from SI7021"); 165 | h = si7021.getHumidityPercent(); 166 | 167 | // Pressure (in mmHg) 168 | printString("Getting Pressure from BMP085"); 169 | p = static_cast(bme.readPressure() * 760.0 / 101325); 170 | 171 | // CO2 172 | printString("Getting CO2"); 173 | readCO2(); 174 | 175 | // Send to server 176 | Blynk.virtualWrite(V1, t); 177 | Blynk.virtualWrite(V2, h); 178 | Blynk.virtualWrite(V4, p); 179 | Blynk.virtualWrite(V5, co2); 180 | 181 | // Write to debug console 182 | printString("H: " + String(h) + "%"); 183 | printString("T: " + String(t) + "C"); 184 | printString("P: " + String(p) + "mmHg"); 185 | printString("CO2: " + String(co2) + "ppm"); 186 | } 187 | 188 | 189 | void loading() { 190 | long unsigned int count {(millis() / 500) % 4}; 191 | memset(loader, '.', count); 192 | memset(&loader[count], 0, 1); 193 | } 194 | 195 | void draw() { 196 | u8g2.clearBuffer(); 197 | 198 | // CO2 199 | if (co2 > -1) { 200 | char co2a [5]; 201 | sprintf (co2a, "%i", co2); 202 | 203 | u8g2.setFont(u8g2_font_inb19_mf); 204 | x = (128 - u8g2.getStrWidth(co2a))/2; 205 | y = u8g2.getAscent() - u8g2.getDescent(); 206 | u8g2.drawStr(x, y, co2a); 207 | 208 | const char ppm[] {"ppm CO2"}; 209 | u8g2.setFont(u8g2_font_6x12_mf); 210 | x = (128 - u8g2.getStrWidth(ppm)) / 2; 211 | y = y + 2 + u8g2.getAscent() - u8g2.getDescent(); 212 | u8g2.drawStr(x, y, ppm); 213 | } else { 214 | loading(); 215 | u8g2.setFont(u8g2_font_inb19_mf); 216 | x = (128 - u8g2.getStrWidth(loader)) / 2; 217 | y = u8g2.getAscent() - u8g2.getDescent(); 218 | u8g2.drawStr(x, y, loader); 219 | } 220 | 221 | // Cycle other meauserments 222 | String measurement {"..."}; 223 | const char degree {176}; 224 | 225 | // Switch every 3 seconds 226 | switch((millis() / 3000) % 3) { 227 | case 0: 228 | if (t > -100) { measurement = "T: " + String(t) + degree + "C"; } 229 | break; 230 | case 1: 231 | if (h > -1) { measurement = "H: " + String(h) + "%"; } 232 | break; 233 | default: 234 | if (p > -1) { measurement = "P: " + String(p) + " mmHg"; } 235 | } 236 | 237 | char measurementa [12]; 238 | measurement.toCharArray(measurementa, 12); 239 | 240 | u8g2.setFont(u8g2_font_9x18_mf); 241 | x = (128 - u8g2.getStrWidth(measurementa))/2; 242 | y = 64 + u8g2.getDescent(); 243 | u8g2.drawStr(x, y, measurementa); 244 | 245 | u8g2.sendBuffer(); 246 | } 247 | 248 | void drawBoot(String msg = "Loading...") { 249 | u8g2.clearBuffer(); 250 | u8g2.setFont(u8g2_font_9x18_mf); 251 | x = (128 - u8g2.getStrWidth(msg.c_str())) / 2; 252 | y = 32 + u8g2.getAscent() / 2; 253 | u8g2.drawStr(x, y, msg.c_str()); 254 | u8g2.sendBuffer(); 255 | } 256 | 257 | void drawConnectionDetails(String ssid, String pass, String url) { 258 | String msg {""}; 259 | u8g2.clearBuffer(); 260 | 261 | msg = "Connect to WiFi:"; 262 | u8g2.setFont(u8g2_font_7x13_mf); 263 | x = (128 - u8g2.getStrWidth(msg.c_str())) / 2; 264 | y = u8g2.getAscent() - u8g2.getDescent(); 265 | u8g2.drawStr(x, y, msg.c_str()); 266 | 267 | msg = "net: " + ssid; 268 | x = (128 - u8g2.getStrWidth(msg.c_str())) / 2; 269 | y = y + 1 + u8g2.getAscent() - u8g2.getDescent(); 270 | u8g2.drawStr(x, y, msg.c_str()); 271 | 272 | msg = "pw: "+ pass; 273 | x = (128 - u8g2.getStrWidth(msg.c_str())) / 2; 274 | y = y + 1 + u8g2.getAscent() - u8g2.getDescent(); 275 | u8g2.drawStr(x, y, msg.c_str()); 276 | 277 | msg = "Open browser:"; 278 | x = (128 - u8g2.getStrWidth(msg.c_str())) / 2; 279 | y = y + 1 + u8g2.getAscent() - u8g2.getDescent(); 280 | u8g2.drawStr(x, y, msg.c_str()); 281 | 282 | // URL 283 | // u8g2.setFont(u8g2_font_6x12_mf); 284 | x = (128 - u8g2.getStrWidth(url.c_str())) / 2; 285 | y = y + 1 + u8g2.getAscent() - u8g2.getDescent(); 286 | u8g2.drawStr(x, y, url.c_str()); 287 | 288 | u8g2.sendBuffer(); 289 | } 290 | 291 | bool loadConfig() { 292 | DEBUG_SERIAL.println("Load config..."); 293 | File configFile = SPIFFS.open("/config.json", "r"); 294 | if (!configFile) { 295 | DEBUG_SERIAL.println("Failed to open config file"); 296 | return false; 297 | } 298 | 299 | size_t size = configFile.size(); 300 | if (size > 1024) { 301 | DEBUG_SERIAL.println("Config file size is too large"); 302 | return false; 303 | } 304 | 305 | // Allocate a buffer to store contents of the file. 306 | std::unique_ptr buf(new char[size]); 307 | 308 | // We don't use String here because ArduinoJson library requires the input 309 | // buffer to be mutable. If you don't use ArduinoJson, you may as well 310 | // use configFile.readString instead. 311 | configFile.readBytes(buf.get(), size); 312 | 313 | StaticJsonBuffer<200> jsonBuffer; 314 | JsonObject &json = jsonBuffer.parseObject(buf.get()); 315 | 316 | if (!json.success()) { 317 | DEBUG_SERIAL.println("Failed to parse config file"); 318 | return false; 319 | } 320 | 321 | // Save parameters 322 | strcpy(device_id, json["device_id"]); 323 | strcpy(blynk_server, json["blynk_server"]); 324 | strcpy(blynk_token, json["blynk_token"]); 325 | } 326 | 327 | void configModeCallback (WiFiManager *wifiManager) { 328 | String url {"http://192.168.4.1"}; 329 | printString("Connect to WiFi:"); 330 | printString("net: " + ssid); 331 | printString("pw: "+ pass); 332 | printString("Open browser:"); 333 | printString(url); 334 | printString("to setup device"); 335 | 336 | drawConnectionDetails(ssid, pass, url); 337 | } 338 | 339 | void setupWiFi() { 340 | //set config save notify callback 341 | wifiManager.setSaveConfigCallback(saveConfigCallback); 342 | 343 | // Custom parameters 344 | WiFiManagerParameter custom_device_id("device_id", "Device name", device_id, 16); 345 | WiFiManagerParameter custom_blynk_server("blynk_server", "Blynk server", blynk_server, 64); 346 | WiFiManagerParameter custom_blynk_token("blynk_token", "Blynk token", blynk_token, 34); 347 | wifiManager.addParameter(&custom_blynk_server); 348 | wifiManager.addParameter(&custom_blynk_token); 349 | wifiManager.addParameter(&custom_device_id); 350 | 351 | // wifiManager.setTimeout(180); 352 | wifiManager.setAPCallback(configModeCallback); 353 | 354 | if (!wifiManager.autoConnect(ssid.c_str(), pass.c_str())) { 355 | DEBUG_SERIAL.println("failed to connect and hit timeout"); 356 | } 357 | 358 | //save the custom parameters to FS 359 | if (shouldSaveConfig) { 360 | DEBUG_SERIAL.println("saving config"); 361 | DynamicJsonBuffer jsonBuffer; 362 | JsonObject &json = jsonBuffer.createObject(); 363 | json["device_id"] = custom_device_id.getValue(); 364 | json["blynk_server"] = custom_blynk_server.getValue(); 365 | json["blynk_token"] = custom_blynk_token.getValue(); 366 | 367 | 368 | File configFile = SPIFFS.open("/config.json", "w"); 369 | if (!configFile) { 370 | DEBUG_SERIAL.println("failed to open config file for writing"); 371 | } 372 | 373 | json.printTo(DEBUG_SERIAL); 374 | json.printTo(configFile); 375 | configFile.close(); 376 | //end save 377 | } 378 | 379 | //if you get here you have connected to the WiFi 380 | DEBUG_SERIAL.println("WiFi connected"); 381 | 382 | DEBUG_SERIAL.print("IP address: "); 383 | DEBUG_SERIAL.println(WiFi.localIP()); 384 | } 385 | 386 | // Virtual pin update FW 387 | BLYNK_WRITE(V22) { 388 | if (param.asInt() == 1) { 389 | DEBUG_SERIAL.println("Got a FW update request"); 390 | 391 | char full_version[34] {""}; 392 | strcat(full_version, device_id); 393 | strcat(full_version, "::"); 394 | strcat(full_version, fw_ver); 395 | 396 | t_httpUpdate_return ret = ESPhttpUpdate.update("http://romfrom.space/get", full_version); 397 | switch (ret) { 398 | case HTTP_UPDATE_FAILED: 399 | DEBUG_SERIAL.println("[update] Update failed."); 400 | break; 401 | case HTTP_UPDATE_NO_UPDATES: 402 | DEBUG_SERIAL.println("[update] Update no Update."); 403 | break; 404 | case HTTP_UPDATE_OK: 405 | DEBUG_SERIAL.println("[update] Update ok."); 406 | break; 407 | } 408 | 409 | } 410 | } 411 | 412 | // Virtual pin reset settings 413 | BLYNK_WRITE(V23) { 414 | factoryReset(); 415 | } 416 | 417 | void setup() { 418 | // Init serial ports 419 | DEBUG_SERIAL.begin(115200); 420 | SENSOR_SERIAL.begin(9600); 421 | 422 | // Init I2C interface 423 | Wire.begin(I2C_SDA, I2C_SCL); 424 | 425 | // Setup HW reset 426 | pinMode(HW_RESET,INPUT_PULLUP); 427 | hwReset.interval(DEBOUNCE_INTERVAL); 428 | hwReset.attach(HW_RESET); 429 | 430 | // Init display 431 | u8g2.begin(); 432 | drawBoot(); 433 | 434 | // Init Humidity/Temperature sensor 435 | si7021.begin(I2C_SDA, I2C_SCL); 436 | 437 | // Init Pressure/Temperature sensor 438 | if (!bme.begin()) { 439 | DEBUG_SERIAL.println("Could not find a valid BMP085 sensor, check wiring!"); 440 | } 441 | 442 | // Init filesystem 443 | if (!SPIFFS.begin()) { 444 | DEBUG_SERIAL.println("Failed to mount file system"); 445 | ESP.reset(); 446 | } 447 | 448 | // Setup WiFi 449 | drawBoot("WiFi..."); 450 | setupWiFi(); 451 | 452 | // Load config 453 | drawBoot(); 454 | if (!loadConfig()) { 455 | DEBUG_SERIAL.println("Failed to load config"); 456 | factoryReset(); 457 | } else { 458 | DEBUG_SERIAL.println("Config loaded"); 459 | } 460 | 461 | // Start blynk 462 | 463 | Blynk.config(blynk_token, blynk_server, blynk_port); 464 | DEBUG_SERIAL.print("blynk server: "); 465 | DEBUG_SERIAL.println( blynk_server ); 466 | DEBUG_SERIAL.print("port: " ); 467 | DEBUG_SERIAL.println( blynk_port ); 468 | DEBUG_SERIAL.print("token: " ); 469 | DEBUG_SERIAL.println( blynk_token ); 470 | 471 | drawBoot("Connecting..."); 472 | DEBUG_SERIAL.println("Connecting to blynk..."); 473 | while (Blynk.connect() == false) { 474 | delay(500); 475 | DEBUG_SERIAL.println("Connecting to blynk..."); 476 | } 477 | 478 | // Setup a function to be called every 10 second 479 | timer.setInterval(10000L, sendMeasurements); 480 | 481 | sendMeasurements(); 482 | } 483 | 484 | void loop() { 485 | Blynk.run(); 486 | timer.run(); 487 | draw(); 488 | 489 | hwReset.update(); 490 | if (hwReset.fell()) { 491 | factoryReset(); 492 | } 493 | } 494 | --------------------------------------------------------------------------------