#include <FS.h>
#include <Arduino.h>
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#include <BlynkSimpleEsp8266.h>
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson
#define I2C_SDA 5
#define I2C_SCL 4
#include <SI7021.h>
SI7021 si7021;
#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <SPI.h>
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, I2C_SCL, I2C_SDA, U8X8_PIN_NONE);
byte x {0};
byte y {0};
#include <SimpleTimer.h>
#define DEBUG_SERIAL Serial1
#define SENSOR_SERIAL Serial
byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
unsigned char response[7];
Adafruit_BMP085 bme;
char blynk_token[33] {"Blynk token"};
char blynk_server[64] {"blynk-cloud.com"};
const uint16_t blynk_port {8442};
char device_id[17] = "Device ID";
const char fw_ver[17] = "0.1.0";
SimpleTimer timer;
WiFiManager wifiManager;
String ssid { "ku_" + String(ESP.getChipId())};
String pass {"ku_" + String(ESP.getFlashChipId()) };
bool shouldSaveConfig = false;
int t {-100};
int p {-1};
int h {-1};
int co2 {-1};
char loader[4] {'.'};
void saveConfigCallback() {
DEBUG_SERIAL.println("Should save config");
shouldSaveConfig = true;
}
void factoryReset() {
wifiManager.resetSettings();
SPIFFS.format();
ESP.reset();
}
void printString(String str) {
DEBUG_SERIAL.println(str);
}
void readCO2() {
bool header_found {false};
char tries {0};
SENSOR_SERIAL.write(cmd, 9);
memset(response, 0, 7);
while(SENSOR_SERIAL.available() && (!header_found)) {
if(SENSOR_SERIAL.read() == 0xff ) {
if(SENSOR_SERIAL.read() == 0x86 ) header_found = true;
}
}
if (header_found) {
SENSOR_SERIAL.readBytes(response, 7);
byte crc = 0x86;
for (char i = 0; i < 6; i++) {
crc+=response[i];
}
crc = 0xff - crc;
crc++;
if ( !(response[6] == crc) ) {
DEBUG_SERIAL.println("CO2: CRC error: " + String(crc) + " / "+ String(response[6]));
} else {
unsigned int responseHigh = (unsigned int) response[0];
unsigned int responseLow = (unsigned int) response[1];
unsigned int ppm = (256*responseHigh) + responseLow;
co2 = ppm;
DEBUG_SERIAL.println("CO2:" + String(co2));
}
} else {
DEBUG_SERIAL.println("CO2: Header not found");
}
}
void sendMeasurements() {
float tf = si7021.getCelsiusHundredths() / 100.0;
float t2f =bme.readTemperature();
t = static_cast<int>((tf + t2f) / 2);
h = si7021.getHumidityPercent();
p = static_cast<int>(bme.readPressure() * 760.0 / 101325);
readCO2();
Blynk.virtualWrite(V1, t);
Blynk.virtualWrite(V2, h);
Blynk.virtualWrite(V4, p);
Blynk.virtualWrite(V5, co2);
printString("H: " + String(h) + "%");
printString("T: " + String(t) + "C");
printString("P: " + String(p) + "mmHg");
printString("CO2: " + String(co2) + "ppm");
}
void loading() {
long unsigned int count {(millis() / 500) % 4};
memset(loader, '.', count);
memset(&loader[count], 0, 1);
}
void draw() {
u8g2.clearBuffer();
if (co2 > -1) {
char co2a [5];
sprintf (co2a, "%i", co2);
u8g2.setFont(u8g2_font_inb19_mf);
x = (128 - u8g2.getStrWidth(co2a))/2;
y = u8g2.getAscent() - u8g2.getDescent();
u8g2.drawStr(x, y, co2a);
const char ppm[] {"ppm CO2"};
u8g2.setFont(u8g2_font_6x12_mf);
x = (128 - u8g2.getStrWidth(ppm)) / 2;
y = y + 2 + u8g2.getAscent() - u8g2.getDescent();
u8g2.drawStr(x, y, ppm);
} else {
loading();
u8g2.setFont(u8g2_font_inb19_mf);
x = (128 - u8g2.getStrWidth(loader)) / 2;
y = u8g2.getAscent() - u8g2.getDescent();
u8g2.drawStr(x, y, loader);
}
String measurement {"..."};
const char degree {176};
switch((millis() / 3000) % 3) {
case 0:
if (t > -100) { measurement = "T: " + String(t) + degree + "C"; }
break;
case 1:
if (h > -1) { measurement = "H: " + String(h) + "%"; }
break;
default:
if (p > -1) { measurement = "P: " + String(p) + " mmHg"; }
}
char measurementa [12];
measurement.toCharArray(measurementa, 12);
u8g2.setFont(u8g2_font_9x18_mf);
x = (128 - u8g2.getStrWidth(measurementa))/2;
y = 64 + u8g2.getDescent();
u8g2.drawStr(x, y, measurementa);
u8g2.sendBuffer();
}
void drawBoot(String msg = "Loading...") {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_9x18_mf);
x = (128 - u8g2.getStrWidth(msg.c_str())) / 2;
y = 32 + u8g2.getAscent() / 2;
u8g2.drawStr(x, y, msg.c_str());
u8g2.sendBuffer();
}
void drawConnectionDetails(String ssid, String pass, String url) {
String msg {""};
u8g2.clearBuffer();
msg = "Connect to WiFi:";
u8g2.setFont(u8g2_font_7x13_mf);
x = (128 - u8g2.getStrWidth(msg.c_str())) / 2;
y = u8g2.getAscent() - u8g2.getDescent();
u8g2.drawStr(x, y, msg.c_str());
msg = "net: " + ssid;
x = (128 - u8g2.getStrWidth(msg.c_str())) / 2;
y = y + 1 + u8g2.getAscent() - u8g2.getDescent();
u8g2.drawStr(x, y, msg.c_str());
msg = "pw: "+ pass;
x = (128 - u8g2.getStrWidth(msg.c_str())) / 2;
y = y + 1 + u8g2.getAscent() - u8g2.getDescent();
u8g2.drawStr(x, y, msg.c_str());
msg = "Open browser:";
x = (128 - u8g2.getStrWidth(msg.c_str())) / 2;
y = y + 1 + u8g2.getAscent() - u8g2.getDescent();
u8g2.drawStr(x, y, msg.c_str());
x = (128 - u8g2.getStrWidth(url.c_str())) / 2;
y = y + 1 + u8g2.getAscent() - u8g2.getDescent();
u8g2.drawStr(x, y, url.c_str());
u8g2.sendBuffer();
}
bool loadConfig() {
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
DEBUG_SERIAL.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 1024) {
DEBUG_SERIAL.println("Config file size is too large");
return false;
}
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
StaticJsonBuffer<200> jsonBuffer;
JsonObject &json = jsonBuffer.parseObject(buf.get());
if (!json.success()) {
DEBUG_SERIAL.println("Failed to parse config file");
return false;
}
strcpy(device_id, json["device_id"]);
strcpy(blynk_token, json["blynk_token"]);
}
void configModeCallback (WiFiManager *wifiManager) {
String url {"http://192.168.4.1"};
printString("Connect to WiFi:");
printString("net: " + ssid);
printString("pw: "+ pass);
printString("Open browser:");
printString(url);
printString("to setup device");
drawConnectionDetails(ssid, pass, url);
}
void setupWiFi() {
wifiManager.setSaveConfigCallback(saveConfigCallback);
WiFiManagerParameter custom_device_id("device_id", "Device name", device_id, 16);
WiFiManagerParameter custom_blynk_server("blynk_server", "Blynk server", blynk_server, 64);
WiFiManagerParameter custom_blynk_token("blynk_token", "Blynk token", blynk_token, 34);
wifiManager.addParameter(&custom_blynk_server);
wifiManager.addParameter(&custom_blynk_token);
wifiManager.addParameter(&custom_device_id);
wifiManager.setAPCallback(configModeCallback);
if (!wifiManager.autoConnect(ssid.c_str(), pass.c_str())) {
DEBUG_SERIAL.println("failed to connect and hit timeout");
}
if (shouldSaveConfig) {
DEBUG_SERIAL.println("saving config");
DynamicJsonBuffer jsonBuffer;
JsonObject &json = jsonBuffer.createObject();
json["device_id"] = custom_device_id.getValue();
json["blynk_server"] = custom_blynk_server.getValue();
json["blynk_token"] = custom_blynk_token.getValue();
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
DEBUG_SERIAL.println("failed to open config file for writing");
}
json.printTo(DEBUG_SERIAL);
json.printTo(configFile);
configFile.close();
}
DEBUG_SERIAL.println("WiFi connected");
DEBUG_SERIAL.print("IP address: ");
DEBUG_SERIAL.println(WiFi.localIP());
}
BLYNK_WRITE(V22) {
if (param.asInt() == 1) {
DEBUG_SERIAL.println("Got a FW update request");
char full_version[34] {""};
strcat(full_version, device_id);
strcat(full_version, "::");
strcat(full_version, fw_ver);
t_httpUpdate_return ret = ESPhttpUpdate.update("http://romfrom.space/get", full_version);
switch (ret) {
case HTTP_UPDATE_FAILED:
DEBUG_SERIAL.println("[update] Update failed.");
break;
case HTTP_UPDATE_NO_UPDATES:
DEBUG_SERIAL.println("[update] Update no Update.");
break;
case HTTP_UPDATE_OK:
DEBUG_SERIAL.println("[update] Update ok.");
break;
}
}
}
BLYNK_WRITE(V23) {
factoryReset();
}
void setup() {
DEBUG_SERIAL.begin(115200);
SENSOR_SERIAL.begin(9600);
SENSOR_SERIAL.swap();
Wire.begin(I2C_SDA, I2C_SCL);
u8g2.begin();
drawBoot();
si7021.begin(I2C_SDA, I2C_SCL);
if (!bme.begin()) {
DEBUG_SERIAL.println("Could not find a valid BMP085 sensor, check wiring!");
}
if (!SPIFFS.begin()) {
DEBUG_SERIAL.println("Failed to mount file system");
ESP.reset();
}
setupWiFi();
drawBoot();
if (!loadConfig()) {
DEBUG_SERIAL.println("Failed to load config");
factoryReset();
} else {
DEBUG_SERIAL.println("Config loaded");
}
Blynk.config(blynk_token, blynk_server, blynk_port);
timer.setInterval(10000L, sendMeasurements);
sendMeasurements();
}
void loop() {
Blynk.run();
timer.run();
draw();
}