//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());
}