//---------------------------------------------------------------- // '21.03.14 naka // CO2濃度計 // // CO2 Sensor : MH-Z19C // micro controler : Seeeduino XIAO (UART接続) //---------------------------------------------------------------- #include #include #include // Core graphics library #include // Hardware-specific library #define TFT_CS 4 // TFT select pin #define TFT_DC 0 // TFT data/command pin #define TFT_RST 5 // Or set to -1 and connect TFT RST to Arduino reset pin #define MODE_SW 1 Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST); #define AXIS_ORG_X 26 #define AXIS_ORG_Y 118 #define PPM_BUFF_SIZE 6*60*28 // 10秒に1回で24h + マージン4H (左にスペースが空くので) int16_t ppm_buff[PPM_BUFF_SIZE]; int16_t buff_ptr, buff_num; int16_t ppm,temp; int16_t pre_ppm = -999, pre_temp = -999, pre_maxppm_range = -999; int16_t pre_bgcolor,pre_ringcolor; byte disp_mode; volatile byte sw; // 割り込み関数内で書き換え、外からアクセスするので、 volatile byte trigflg; // レジスタではなくRAMに割り当て指示 int16_t seccnt; //------------------------------------------------------------------- // setup //------------------------------------------------------------------- void setup() { uint16_t color = ST77XX_WHITE; uint16_t bgcolor = ST77XX_GREEN; pinMode(MODE_SW,INPUT); Serial1.begin(9600); // TX(6),RX(7) <---> Co2 sensor MH-Z19C tft.initR(INITR_BLACKTAB); // Initialize screen tft.setRotation(3); buff_ptr = 0; buff_num = 0; // 0 〜 PPM_BUFF_SIZE (バッファが一杯になったら古いものを上書き) disp_mode = 0; sw = 0; trigflg = 0; seccnt = 0; measurement(); disp_digit(1); TimerTc3.initialize(10000); // 10ms割り込み TimerTc3.attachInterrupt(Interrapt); } //------------------------------------------------------------------- // タイマー割り込み関数(スイッチ確認と時間カウント) //------------------------------------------------------------------- void Interrapt() { // タイマー割り込み 10ms毎 if (digitalRead(MODE_SW)==HIGH) sw = HIGH; else sw = LOW; seccnt++; if (seccnt>=1000) { // 10sを数える seccnt = 0; trigflg = 1; } } //------------------------------------------------------------------- // loop //------------------------------------------------------------------- void loop() { if (trigflg==1) { // 10sec経過した trigflg = 0; // トリガークリア measurement(); if (disp_mode==0) disp_digit(0); else if (disp_mode==1) disp_digit_ring(0); else disp_graph(0); } if (sw==HIGH) { disp_mode++; disp_mode %= 7; // modeが0〜6なので if (disp_mode==0) disp_digit(1); else if (disp_mode==1) disp_digit_ring(1); else disp_graph(1); while(sw==HIGH); // LOWになったら抜ける } } //------------------------------------------------------------------- // 測定 //------------------------------------------------------------------- void measurement() { // Start,Reserved,Command, -, -, -, -, -, Checksum byte Command[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79}; struct { byte start; byte command; byte concentration_high; byte concentration_low; byte temp; byte emp[3]; byte checksum; } Return; Serial1.write(Command, sizeof Command); Serial1.readBytes((char *)&Return, 9); if (Return.start!=0xff || Return.command!=0x86 ) { drawtext(5,50,1,"MH-Z19 error",ST77XX_GREEN,ST77XX_WHITE); return; } else { byte checksum = getCheckSum((byte *)&Return); if (Return.checksum!=checksum) { drawtext(5,50,1,"MH-Z19 checksum error",ST77XX_GREEN,ST77XX_WHITE); return; } } ppm = Return.concentration_high*256 + Return.concentration_low; temp = Return.temp - 40; push_ppm(ppm); } byte getCheckSum(byte *packet) { byte i,checksum = 0; for(i=1;i<8;i++) { checksum += packet[i]; } return (0xFF - checksum) + 1; } void push_ppm(int ppm) { ppm_buff[buff_ptr] = ppm; buff_ptr++; if (buff_ptr==PPM_BUFF_SIZE) { buff_ptr = 0; } if (buff_num999 && ppm<1000) { // 前回から表示桁数が減る tft.fillRect(30,50,100,30,bgcolor); } pre_ppm = ppm; // 今回数値の表示 int x; if (ppm<1000) { sprintf(buff,"%3d",ppm); x = 46; } else { sprintf(buff,"%4d",ppm); x = 33; } drawtext(x,50,4,buff,color,bgcolor); } //------------------------------------------------------------------- // Co2濃度の推移グラフ (mode=1 全面書き換え) //------------------------------------------------------------------- void disp_graph(int mode) { uint16_t color = ST77XX_WHITE; uint16_t bgcolor = ST77XX_BLACK; int step; switch(disp_mode) { // 表示モード(表示時間)に応じて取り出す位置のステップを決める case 2: step = 3; break; case 3: step = 6; break; case 4: step = 18; break; case 5: step = 36; break; case 6: step = 72; break; } // カレントから遡って、表示最小単位の平均値で最大ppmを探す int i; int max_ppm = 0; int ptr = buff_ptr - 1; int x = 159; int sum_ppm = 0; int sum_cnt = 0; int ave_ppm; for (i=buff_num;i>0;i--) { sum_ppm += ppm_buff[ptr]; sum_cnt++; if (sum_cnt>=step) { ave_ppm = sum_ppm / step; sum_ppm = 0; sum_cnt = 0; if (max_ppm0;i--) { sum_ppm += ppm_buff[ptr]; sum_cnt++; if (sum_cnt>=step) { ave_ppm = sum_ppm / step; sum_ppm = 0; sum_cnt = 0; int y = (float)(tft.height()-15-10) * ((float)ave_ppm / (float)max_ppm_range) + 0.5; tft.drawLine(x, tft.height()-14, x, tft.height()-y-10, ST77XX_MAGENTA); if (tft.height()-y-15>=12) tft.drawLine(x, tft.height()-y-11, x, 15, ST77XX_BLUE); x--; if (x4) k = 4; else k = max_range; for (int i=0;i<6;i++) { tft.setCursor(0,y_pos[k][i]+12); sprintf(buff,"%4d",y_val[k][i]); tft.print(buff); tft.drawLine(tick_x1, y_pos[k][i]+15, tick_x2, y_pos[k][i]+15, color); if (y_pos[k][i]==0) break; } } //------------------------------------------------------------------- // グラフのX軸表示 //------------------------------------------------------------------- void axis_x(int mode,uint16_t color ) { tft.drawLine(25, tft.height()-13, tft.width()-1, tft.height()-13, color); mode = mode - 1; char buff[8]; int tick_y1 = tft.height()-13; int tick_y2 = tft.height()-13+2; tft.setTextColor(color); tft.setTextSize(1); // 描画時間に合わせた軸を描画 int x_pos[5] = { 159,129,99,69,39}; char *x_val[5][5] = { "0","-15M","-30M","-45M","-1H", "0","-30","-1H","-90M","-2H", "0","","-3H","","-6H", "0","-3H","-6H","-9H","-12H", "0","-6H","-12H","-18H","-24H"}; int k; k = mode -1; int offset; for (int i=0;i<5;i++) { if (i==0) offset = 5; else offset = 12; tft.setCursor(x_pos[i]-offset,tft.height()-8); tft.print(x_val[k][i]); tft.drawLine(x_pos[i], tick_y1, x_pos[i], tick_y2, color); } } //------------------------------------------------------------------- // 描画関数 //------------------------------------------------------------------- // 座標、サイズ、色指定で文字を描く void drawtext(int x,int y,int size, char *text, uint16_t color, uint16_t bgcolor) { tft.setCursor(x, y); tft.setTextColor(color,bgcolor); tft.setTextSize(size); tft.print(text); } // 太い線で円を描く void drawThickCircle(int x,int y, int r, int width, uint16_t color) { int i; for (i=0;i