//WBGT(OLED使用)
//
// WBGT指標とは
// WBGT(湿球黒球温度)とは,人体の熱収支に影響の大きい湿度,輻射熱,気温の3つを取り入れた指標で,
// 乾球温度,湿球温度,黒球温度の値を使って計算する
// ※WBGT(湿球黒球温度)の算出方法
// 屋外:WBGT = 0.7×湿球温度+0.2×黒球温度+0.1×乾球温度
// 屋内:WBGT = 0.7×湿球温度+0.3×黒球温度
//
// 本プログラムではWBGT値を,気温・湿度から推定値で求める
//
#include <Arduino.h>
#include <Wire.h>
#include <avr/pgmspace.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <U8glib.h>
#include <Adafruit_SHT31.h>
//#define DEBUG true
char version[] = "2020.07.23 Ver.1.00";
#define CTRL_BTN PD2 //Control button
#define BTRY_PIN PC0 //battery voltage pin
#define SHT31_ADDRESS 0x44 //Set to 0x45 for alternate i2c addr
U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE);//Just for 0.91 h(128*32) //0x3c
Adafruit_SHT31 sht31 = Adafruit_SHT31();
// NP / no problem
//20以下:1.ほぼ安全 safety
//21~25:2.注意 attention
//25~28:3.警戒 warning
//28~31:4.厳重警戒 danger / dangerous
//31以上:5.危険 critical
// : deadly
const byte wbgt_t[20][17] = {
/* 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 */
/*21*/ { 15, 15, 16, 16, 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24 },
/*22*/ { 15, 16, 17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 24, 24, 25 },
/*23*/ { 16, 17, 17, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26 },
/*24*/ { 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 24, 24, 25, 26, 26, 27 },
/*25*/ { 18, 18, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28 },
/*26*/ { 18, 19, 20, 20, 21, 22, 22, 23, 24, 24, 25, 26, 26, 27, 28, 28, 29 },
/*27*/ { 19, 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 27, 27, 28, 29, 29, 30 },
/*28*/ { 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 27, 28, 28, 29, 30, 30, 31 },
/*29*/ { 21, 21, 22, 23, 24, 24, 25, 26, 26, 27, 28, 29, 29, 30, 31, 31, 32 },
/*30*/ { 21, 22, 23, 24, 24, 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33 },
/*31*/ { 22, 23, 24, 24, 25, 26, 27, 27, 28, 29, 30, 30, 31, 32, 33, 33, 34 },
/*32*/ { 23, 24, 25, 25, 26, 27, 28, 28, 29, 30, 31, 31, 32, 33, 34, 34, 35 },
/*33*/ { 24, 25, 25, 26, 27, 28, 28, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36 },
/*34*/ { 25, 25, 26, 27, 28, 29, 29, 30, 31, 32, 33, 33, 34, 35, 36, 37, 37 },
/*35*/ { 25, 26, 27, 28, 29, 29, 30, 31, 32, 33, 33, 34, 35, 36, 37, 38, 38 },
/*36*/ { 26, 27, 28, 29, 29, 30, 31, 32, 33, 34, 34, 35, 36, 37, 38, 39, 39 },
/*37*/ { 27, 28, 29, 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 41 },
/*38*/ { 28, 28, 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 41, 42 },
/*39*/ { 28, 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 41, 42, 43 },
/*40*/ { 29, 30, 31, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44 }
};
struct history {
short temp; //気温*10
short humi; //湿度*10
};
#define SECPMS 1000 //秒/ms
#define T100MS (SECPMS/10) //100ms
#define T10MS (SECPMS/100) //10ms
#define T60S 60000 //60秒
#define BTN_INT_ON (EIMSK = (1< 0);
}
void loop() {
bool info_mdf = false;
#ifdef DEBUG
Serial.println("loop start");
#endif
u8g.sleepOn();
sleep_enable();
BTN_INT_ON;
for(;;) {
WDT_start();
sleep_mode(); //ここでスリープに入る
WDT_stop();
if(btn_intr) {
btn_intr = false;
sleep_disable();
u8g.sleepOff();
drawOLED(0);
long ms = SECPMS * 5; //モード変更時間をセット
while(!BTN_RELEASE) {
delay(T10MS);
if((ms -= T10MS) <= 0) {
//情報表示モード
infoOLED();
//次にボタンを押下するまでモードは終了しない
//前のボタンリリースまで待ち
while(!BTN_RELEASE) delay(T10MS);
BTN_INT_ON;
do {
infoOLED();
localDelay(SECPMS);
} while(!btn_intr);
btn_intr = false;
//ボタンリリースまで待ち
while(!BTN_RELEASE) delay(T10MS);
info_mdf = true;
break;
}
}
BTN_INT_ON;
if(!info_mdf) {
//5秒間表示(1秒単位で更新)
int log = 0;
for(int tt = 0; tt < 5; tt++) {
if(btn_intr) {
btn_intr = false;
#ifdef DEBUG
Serial.print("press within 5 seconds. log := ");
Serial.println(log);
#endif
while(!BTN_RELEASE) delay(T10MS);
BTN_INT_ON;
if((log += 6) > LOG_COUNT) log -= 6;
tt = 0; //5秒をリセット
} else {
collection(false);
}
drawOLED(log);
localDelay(SECPMS);
}
} else {
info_mdf = false;
}
clearOLED();
u8g.sleepOn();
sleep_enable();
}
if(wdt_count >= 15*5) { //8秒 * 15 * 5 = 10分
//10分間隔でロギング
#ifdef DEBUG
Serial.println("collect data every 10 minutes");
#endif
collection(true);
wdt_count = 0;
}
}
}
// log := * 10 分前
void drawOLED(int log) {
int t, h, wbgt, lv;
char ls[12], ss[8], ts[8], hs[8], ws[8];
#ifdef DEBUG
Serial.println("OLED draw");
// Serial.print(reg_temp);
// Serial.print("(*10)'C, ");
// Serial.print(reg_humi);
// Serial.println("(*10)%");
#endif
//表示データ取得(現在値か,ログか)
// t10 := temp * 10, h10 := humd * 10
int t10 = log? hist_log[log - 1].temp: reg_temp;
int h10 = log? hist_log[log - 1].humi: reg_humi;
//表示データチェック
if(h10 != 0) {
//四捨五入してWBGTにする
t = (t10 + 5) / 10; t = (t > 40)? 40: t; //MAX40(40℃以上の場合は未対応)
h = (h10 + 5) / 10; h = (h > 100)? 100: h; //MAX100
wbgt = wbgt_t[t - 21][(h - 20) / 5];
sprintf(ws, "%d", wbgt);
//WBGT レベル算出
if(wbgt <= 20) lv = 0; //1
else if(wbgt <= 25) lv = 1; //2
else if(wbgt <= 28) lv = 2; //3
else if(wbgt <= 31) lv = 3; //4
else lv = 4; //5
if(lv == 0) {
sprintf(ss, "LOW");
} else if(lv == 4) {
sprintf(ss, "MAX");
} else {
sprintf(ss, "Lv.%d", lv);
}
//WBGT タイトル表示
if(log == 0) {
sprintf(ls, "WBGT");
} else {
//ログ表示
sprintf(ls, "%d hour ago", log/6);
}
} else {
//湿度がの場合はログの記録なしとして現在表示
t10 = reg_temp;
h10 = reg_humi;
lv = 0;
sprintf(ws, "--");
sprintf(ss, "");
sprintf(ls, "NO DATA");
}
sprintf(ts, "%d.%d'C", t10/10, t10%10);
sprintf(hs, "%d.%d%%", h10/10, h10%10);
// picture loop
u8g.firstPage();
do {
//LEVEL Graph Display
for(int n = 1; n <= lv; n++) {
u8g.drawBox(58, 32 - (n * 5 + (n - 1)), 12, 5);
}
//TITLE
u8g.setFont(u8g_font_timB08);
u8g.drawStr(0, 7, ls);
u8g.drawStr(54, 7, ss);
//WBGT
u8g.setFont(u8g_font_ncenB24n);
u8g.drawStr(8, 32, ws);
//u8g.setFont(u8g_font_freedoomr25n);
//u8g.drawStr(8, 34, ws); //u8g.setPrintPos(16, 30); u8g.print(ws);
//Temperature, Humidity
u8g.setFont(u8g_font_helvB14r);
u8g.drawStr(76, 14, ts); //u8g.setPrintPos(64, 15); u8g.print(ts);
u8g.drawStr(76, 32, hs); //u8g.setPrintPos(64, 32); u8g.print(hs);
} while(u8g.nextPage());
}
void clearOLED() {
#ifdef DEBUG
Serial.println("OLED clear");
#endif
u8g.firstPage();
while(u8g.nextPage());
}
//debug用内部情報表示
//128x32なので8x8で16列x4行表示(8, 16, 24, 32)
void infoOLED() {
char lstr[4][32];
//logging count
int logcnt = 0;
for(int n = 0; n < LOG_COUNT; n++) {
if(hist_log[n].humi != 0) logcnt++;
}
//battery voltage
long val = analogRead(BTRY_PIN);
long v10 = val * 11 * 57 / 1024 / 10; //val * 11 / 1024 * 57 / 10
sprintf(lstr[0], "%s", version);
sprintf(lstr[1], "log count := %d", logcnt);
sprintf(lstr[2], "battery := %d.%dV(%d)", (int)v10/10, (int)v10%10, (int)val);
sprintf(lstr[3], "");
u8g.setFont(u8g_font_timB08);
u8g.firstPage();
do {
for(int n = 0; n < 4; n++) {
u8g.drawStr(0, n * 8 + 8, lstr[n]);
}
} while(u8g.nextPage());
}