|
// ESP_ALL_STARS.ino by Koichi Kurahashi 2016-03-15 |
|
// 2016-03-18 Add Dust Counter |
|
// |
|
// thanks: |
|
// OLED Library |
|
// https://github.com/squix78/esp8266-oled-ssd1306 |
|
// |
|
// BME280 Library |
|
// https://learn.sparkfun.com/tutorials/sparkfun-bme280-breakout-hookup-guide |
|
// |
|
// |
|
|
|
// |
|
// ESP / Common |
|
// |
|
#define USE_US_TIMER 1 |
|
extern "C" { |
|
#include "user_interface.h" |
|
#include "osapi.h" |
|
} |
|
|
|
#include <ESP8266WiFi.h> |
|
|
|
#include <Wire.h> |
|
#include <SPI.h> |
|
#include <Time.h> |
|
|
|
|
|
// |
|
// Air Quality and Dust Counter |
|
// |
|
|
|
const int DustCounterP1 = 15; |
|
const int DustCounterP2 = 16; |
|
|
|
ETSTimer Timer; |
|
|
|
int prevSecond = second(); // 秒ごとに表示更新するためのフラグ |
|
|
|
static long sum1, current1; |
|
static long sum2, current2; |
|
|
|
static long store1[60], store2[60]; // data ring buffer |
|
|
|
static float p1, p2; // 直近の計測値(1分移動平均) |
|
static long c1, c2; // 直近の計測値(直近1秒) |
|
|
|
void SetupDustCounter() { |
|
sum1 = sum2 = current1 = current2 = -1; |
|
|
|
for (int i = 0; i < 60; i++) { |
|
store1[i] = store2[i] = -1; |
|
} |
|
|
|
pinMode(DustCounterP1, INPUT); |
|
pinMode(DustCounterP2, INPUT); |
|
os_timer_setfn(&Timer, fetchFunction, NULL); |
|
os_timer_arm_us(&Timer, 1000, true); |
|
} |
|
|
|
// |
|
// Wifi |
|
// |
|
|
|
#include <WiFiClient.h> |
|
#include "../../private_ssid.h" |
|
//const char *ssid = "*************"; |
|
//const char *password = "*************"; |
|
|
|
WiFiClient client; |
|
|
|
void SetupWiFi() { |
|
WiFi.begin ( ssid, password ); |
|
Serial.println("Started"); |
|
} |
|
|
|
|
|
// |
|
// OLED / SSD1306 |
|
// |
|
#include "SSD1306.h" |
|
#include "SSD1306Ui.h" |
|
|
|
// |
|
// MBE280 |
|
// |
|
#include "SparkFunBME280.h" |
|
|
|
BME280 bme; |
|
|
|
|
|
const int MBE_GND = 12; |
|
const int MBE_Vcc = 13; |
|
|
|
void SetupMBE() { |
|
uint8_t chipID; |
|
|
|
pinMode(MBE_GND, OUTPUT); |
|
digitalWrite(MBE_GND, LOW); |
|
pinMode(MBE_Vcc, OUTPUT); |
|
digitalWrite(MBE_Vcc, HIGH); |
|
|
|
|
|
bme.settings.commInterface = I2C_MODE; |
|
bme.settings.I2CAddress = 0x76; |
|
|
|
bme.settings.runMode = 3; //Forced mode |
|
bme.settings.tStandby = 0; // 0.5mS |
|
bme.settings.filter = 0; |
|
bme.settings.tempOverSample = 2; |
|
bme.settings.pressOverSample = 2; |
|
bme.settings.humidOverSample = 2; |
|
|
|
delay(10); |
|
} |
|
|
|
|
|
// |
|
// OLED / SSD1306 |
|
// |
|
SSD1306 display(0x3c, 4, 5); |
|
|
|
void SetupSSD1306() { |
|
display.init(); |
|
display.flipScreenVertically(); |
|
display.displayOn(); |
|
display.clear(); |
|
} |
|
|
|
|
|
|
|
void setup() { |
|
system_timer_reinit(); // to use os_timer_arm_us |
|
|
|
Serial.begin(115200); |
|
Wire.begin(); |
|
|
|
SetupWiFi(); |
|
SetupMBE(); |
|
SetupDustCounter(); |
|
SetupSSD1306(); |
|
} |
|
|
|
|
|
// ThingSpeakに1分ごとに送るためのフラグ |
|
int prevMinute = minute(); |
|
|
|
|
|
// |
|
// loop |
|
// |
|
void loop() { |
|
float temp, humidity, pressure; |
|
char buffer[80]; |
|
|
|
// BME280各データ取り出し |
|
bme.begin(); |
|
delay(10); |
|
temp = bme.readTempC(); |
|
humidity = bme.readFloatHumidity(); |
|
pressure = bme.readFloatPressure() / 100; |
|
|
|
// Air Quality |
|
int air = system_adc_read(); |
|
|
|
// dust counter : p1 and p2 is global |
|
|
|
// OLEDへ表示 |
|
display.clear(); |
|
|
|
display.setFont(ArialMT_Plain_10); |
|
display.setTextAlignment(TEXT_ALIGN_LEFT); |
|
display.drawString( 0, 0, "Temperature"); |
|
display.drawString( 0, 16, "Humidity"); |
|
display.drawString( 0, 32, "Pressure"); |
|
display.drawString( 0, 48, "Air"); |
|
|
|
display.setFont(ArialMT_Plain_16); |
|
display.setTextAlignment(TEXT_ALIGN_RIGHT); |
|
|
|
dtostrf(temp, 7, 2, buffer); |
|
display.drawString(127, 0, buffer); |
|
|
|
dtostrf(humidity, 7, 2, buffer); |
|
display.drawString(127, 16, buffer); |
|
|
|
dtostrf(pressure, 7, 2, buffer); |
|
display.drawString(127, 32, buffer); |
|
|
|
sprintf(buffer, "%4d %4d %4d", air, (int)p1, (int)p2); |
|
display.drawString(127, 48, buffer); |
|
|
|
display.display(); |
|
|
|
// ThingSpeak.comへ送信 |
|
if ( WiFi.status() == WL_CONNECTED && minute() != prevMinute) { |
|
sendTHPA(temp, humidity, pressure, air); |
|
prevMinute = minute(); |
|
} |
|
|
|
delay(80); |
|
} |
|
|
|
|
|
|
|
// |
|
// Dust Counter |
|
// |
|
// |
|
// Lowレベルの比率をダストカウント量に変換 |
|
// y = 1.1x^3 - 3.8x^2 + 520x + 0.62 |
|
// この式は以下のサイトの近似式を流用させていただきました。ありがとうございます。 |
|
// thank you for this site > http://www.howmuchsnow.com/arduino/airquality/grovedust/ |
|
// |
|
float conversion(float inRate) { |
|
return 1.1*pow(inRate, 3.0) - 3.8*pow(inRate, 2.0) + 520.0*inRate + 0.62; |
|
} |
|
|
|
|
|
// |
|
// 100uSごとにP1とP2を監視し、Lowレベルの時間を計測する |
|
// DustCounter check both ports every 100uSec to detect LOW level duration |
|
// |
|
void fetchFunction(void *temp) { |
|
// 1秒ごとにリングバッファの計測スロットをずらしていく |
|
int tempSecond = second(); |
|
if (tempSecond != prevSecond) { |
|
Serial.println(tempSecond); |
|
int n = 0; |
|
long sum1 = sum2 = 0; |
|
for (int i = 0; i < 60; i++) { |
|
if (store1[i] != -1) { |
|
sum1 += store1[i]; |
|
sum2 += store2[i]; |
|
n++; |
|
} |
|
} |
|
|
|
p1 = conversion((float)sum1 / (float)n / 1000.0); // Lowレベル比率をダスト量に換算 |
|
p2 = conversion((float)sum2 / (float)n / 1000.0); |
|
|
|
c1 = store1[prevSecond]; // 直近のカウント率を取得 |
|
c2 = store2[prevSecond]; |
|
|
|
store1[tempSecond] = 0; // 次のカウント記録場所をクリア |
|
store2[tempSecond] = 0; |
|
|
|
prevSecond = tempSecond; // 秒監視フラグをクリア |
|
} |
|
|
|
// P1がlowなら、P1のカウンタをインクリメント |
|
if (store1[tempSecond] != -1 && digitalRead(DustCounterP1) == LOW) { |
|
store1[tempSecond]++; |
|
} |
|
|
|
// P2がlowなら、P2のカウンタをインクリメント |
|
if (store2[tempSecond] != -1 && digitalRead(DustCounterP2) == LOW) { |
|
store2[tempSecond]++; |
|
} |
|
} |
|
|
|
|
|
|
|
// |
|
// ThingSpeakへのパラメータを作って送信 |
|
// |
|
void sendTHPA(float inTemperature, float inHumidity, float inPressure, int inAirQuality) { |
|
char tBuf[10], hBuf[10], pBuf[10], aBuf[10]; |
|
|
|
dtostrf(inTemperature, 7, 2, tBuf); |
|
dtostrf(inHumidity, 7, 2, hBuf); |
|
dtostrf(inPressure, 7, 2, pBuf); |
|
sprintf(aBuf, "%d", inAirQuality); |
|
|
|
String postStr = "&field1=" + String(tBuf) + "&field2=" + String(hBuf) + "&field3=" + String(pBuf) |
|
+ "&field4=" + String(aBuf); |
|
send(postStr); |
|
|
|
Serial.println(postStr); |
|
} |
|
|
|
|
|
// ThingSpeakへ送信 |
|
// |
|
void send(String inPostStr) { |
|
String apiKey = "SA52EN1M2KLY0OXO"; |
|
Serial.print("Connecting..."); |
|
if (client.connect("184.106.153.149", 80)) { // api.thingspeak.com |
|
Serial.print("Connected...."); |
|
String postStr = apiKey + inPostStr + "\r\n\r\n"; |
|
client.print("POST /update HTTP/1.1\n"); |
|
client.print("Host: api.thingspeak.com\n"); |
|
client.print("Connection: close\n"); |
|
client.print("X-THINGSPEAKAPIKEY: " + apiKey + "\n"); |
|
client.print("Content-Type: application/x-www-form-urlencoded\n"); |
|
client.print("Content-Length: "); |
|
client.print(postStr.length()); |
|
client.print("\n\n"); |
|
client.print(postStr); |
|
Serial.println("posted."); |
|
} |
|
client.stop(); |
|
} |
|
|