はじめに
MAX7219を使用したLEDモジュールを使ってベッドサイド用時計を作りました。余ったプログラム領域を利用して温度気圧センサーBME280にて測定したデータを24時間推移表示してみました。気圧はローソク表示にしています。
時計表示
日付と時刻を1桁離したかったため、月表示だけが16進表示になっています。
最低輝度でも夜中は明るすぎるため、黒いフィルムを貼っています。
気温気圧表示
24時間の推移表示をします。時計の時刻合せもこちらで行います。
こちらもフィルムを貼っています。
箱の内部
時刻合せタクトスイッチとI2C用のコネクタはProto Shield上の実装しましたが、その他はジャンパーケーブルでお手軽配線しています。RTCのDS1302も実装していますが使用していません。
これでも1年半問題無く動作しています。
用意するもの
Arduino Uno
Proto Sheild
DS3231
TFT
ST7735を使用したSPI通信で動作する160x128TFTモジュールです。
LED
MAX7219を使用したSPI通信を行う8桁のLEDモジュールです。
温度、気圧センサ
BoschのBMP280を使用した温度気圧センサーです。I2Cで通信しています。
結線
///////////////////////////////////////////////////////////////////////
// TFT Module 160x128
// 信号インターフェース:シリアル、内蔵コントローラチップST7735R
//
// 1 VCC -- 5V
// 2 GND -- GND
// 3 GND
// 4 NC
// 5 NC
// 6 NC
// 7 CLK -- Pin 13
// 8 SDA -- Pin 11
// 9 RS -- Pin 09
// 10 RST -- Pin 08
// 11 CS -- Pin 10
//
///////////////////////////////////////////////////////////////////////
// RTC Module DS3231
//
// 1 32K
// 2 SQW
// 3 SCL -- Pin A5
// 4 SDA -- Pin A4
// 5 VCC
// 6 GND
///////////////////////////////////////////////////////////////////////
// LED Module MAX7219
//
// 1 VCC
// 2 GND
// 3 DIN -- Pin 7
// 4 LOAD -- Pin 6
// 5 CLK -- Pin 5
///////////////////////////////////////////////////////////////////////
// SWITCH
//
// SET -- Pin 0
// +SW -- Pin 1
// -SW -- Pin 2
///////////////////////////////////////////////////////
// BMP280 - I2C
//
// VCC : 3.3V
// GND : GND
// SDA : A4
// SCL : A5
//
プログラム
# include <stdint.h>
# include "Wire.h"
# include <LiquidCrystal.h>
# include <Time.h> //http://www.arduino.cc/playground/Code/Time
# include <TimeLib.h>
# include <DS3232RTC.h> //http://github.com/JChristensen/DS3232RTC
// 20170609 時計合わせ時の消去処理を修正
///////////////////////////////////////////////////////////////////////
// TFT Module 160x128
// 信号インターフェース:シリアル、内蔵コントローラチップST7735R
//
// 1 VCC -- 5V
// 2 GND -- GND
// 3 GND
// 4 NC
// 5 NC
// 6 NC
// 7 CLK -- Pin 13
// 8 SDA -- Pin 11
// 9 RS -- Pin 09
// 10 RST -- Pin 08
// 11 CS -- Pin 10
# include <TFT.h>
# include <SPI.h>
# define cs 10
# define dc 9
# define rst 8
//13 scl
//11 to sda
TFT TFTscreen = TFT(cs, dc, rst);
// initialize the library with the numbers of the interface pins
//LiquidCrystal (RS, EN, D4, D5, D6, D7);
//LiquidCrystal lcd(11, 10, 9, 8, 7, 6);
///////////////////////////////////////////////////////
// BMP280 - I2C
//
// VCC : 3.3V
// GND : GND
// SDA : A4
// SCL : A5
//
///////////////////////////////////////////////////////////////////////
// LED Module MAX7219
//
// 1 VCC
// 2 GND
// 3 DIN -- Pin 7
// 4 LOAD -- Pin 6
// 5 CLK -- Pin 5
///////////////////////////////////////////////////////////////////////
# include "LedControl.h" // 8 digit
LedControl lc = LedControl(7, 5, 6, 1);
///////////////////////////////////////////////////////////////////////
// SWITCH
//
// SET -- Pin 0
// +SW -- Pin 1
// -SW -- Pin 2
///////////////////////////////////////////////////////////////////////
# include "MsTimer2.h" // For switch reading
volatile boolean b_sw0_stat = HIGH;
volatile boolean b_sw1_stat = HIGH;
volatile boolean b_sw2_stat = HIGH;
int setup_stat = 0; // 0: Normal mode, 1: hour 2: min, 3, year, 4, mon, 5, day
int setup_stat_old = 0; // 0: Normal mode, 1: hour 2: min, 3, year, 4, mon, 5, day
char old_timetext[6];
char old_daytext[20];
int old_min = -1;
int old_hor = -1;
int old_day = -1;
int old_min_led = -1;
int old_hor_led = -1;
int old_day_led = -1;
volatile int selsts = 0; // SEL key status 0:normal 1:hour 2:min 3:Day 4 Mon, 5 Year
int old_PrehPa = -1;
int now_PrehPa = -1;
int PrehPa[24]; // Pressure strage
int PrehPaST[24]; // Pressure strage
int PrehPaED[24]; // Pressure strage
int PrehPaH[24]; // Pressure strage
int PrehPaL[24]; // Pressure strage
//// Dummy data
//int PrehPa[24] = {1020, 1021, 1013, 1004, 1002, 1009, 1018, 1021, 1016, 1006, 1002, 1006, 1016, 1021, 1018, 1009, 1002, 1004, 1013, 1021, 1020, 1011, 1003, 1002 };
//int PrehPaST[24] = { 1020, 1021, 1015, 1009, 1008, 1013, 1019, 1021, 1017, 1011, 1008, 1011, 1017, 1021, 1019, 1012, 1008, 1009, 1016, 1021, 1020, 1014, 1009, 1008 };
//int PrehPaED[24] = { 1019, 1020, 1010, 999, 996, 1005, 1017, 1021, 1014, 1001, 996, 1002, 1014, 1021, 1017, 1005, 996, 999, 1010, 1020, 1019, 1008, 997, 997 };
//int PrehPaH[24] = { 1021, 1021, 1017, 1013, 1012, 1015, 1020, 1021, 1019, 1014, 1012, 1014, 1019, 1021, 1020, 1015, 1012, 1013, 1017, 1021, 1021, 1016, 1012, 1012 };
//int PrehPaL[24] = { 1019, 1020, 1009, 995, 992, 1002, 1016, 1021, 1013, 998, 992, 998, 1013, 1021, 1016, 1002, 992, 995, 1009, 1020, 1019, 1006, 994, 993 };
int now_TempTrend = -1; // Pressure strage
int old_TempTrend = -1; // Pressure strage
int TempTrend[24]; // Pressure strage
int TempTrendST[24]; // Pressure strage
int TempTrendED[24]; // Pressure strage
int TempTrendH[24]; // Pressure strage
int TempTrendL[24]; // Pressure strage
// Dummy data
//int TempTrend[24] = {332, 335, 297, 252, 242, 276, 322, 339, 310, 262, 240, 263, 311, 339, 322, 275, 241, 252, 297, 335, 331, 289, 247, 244 };
//int TempTrendST[24] = {335, 337, 314, 287, 281, 301, 329, 339, 322, 293, 280, 293, 322, 339, 329, 301, 281, 287, 314, 337, 335, 309, 284, 282 };
//int TempTrendED[24] = {328, 333, 279, 217, 202, 250, 315, 339, 298, 231, 200, 232, 299, 339, 315, 249, 202, 217, 280, 333, 328, 269, 210, 206 };
//int TempTrendH[24] = {336, 338, 322, 304, 300, 314, 333, 339, 328, 309, 300, 309, 328, 339, 333, 314, 300, 304, 322, 338, 336, 319, 303, 301 };
//int TempTrendL[24] = {327, 332, 271, 199, 183, 237, 312, 339, 292, 216, 180, 217, 293, 339, 312, 236, 183, 199, 271, 333, 326, 259, 192, 187 };
class i2c_support
{
private:
byte i2c_address;
public:
i2c_support(byte address) {
i2c_address = address;
Wire.begin();
}
// read 8bit data from pointer.
uint8_t i2c_read8(byte pointer) {
Wire.beginTransmission(i2c_address);
Wire.write(pointer);
Wire.endTransmission();
Wire.requestFrom(i2c_address, (byte)1);
return (uint8_t)Wire.read();
}
// read 16bit data from addr/pointer. 1st MSB, 2nd LSB.
uint16_t i2c_read16(byte pointer) {
Wire.beginTransmission(i2c_address);
Wire.write(pointer);
Wire.endTransmission();
Wire.requestFrom(i2c_address, (byte)2);
unsigned short data = Wire.read();
data <<= 8;
data |= Wire.read();
return data;
}
// read 16bit data from addr/pointer. 1st LSB, 2ns MSB.
uint16_t i2c_read16_swab(byte pointer) {
Wire.beginTransmission(i2c_address);
Wire.write(pointer);
Wire.endTransmission();
Wire.requestFrom(i2c_address, (byte)2);
uint16_t lsb = (uint16_t)Wire.read();
uint16_t msb = (uint16_t)(Wire.read() << 8);
return msb | lsb;
}
// for HDC1000
uint32_t i2c_read32(byte pointer, int delay_ms = 0) {
Wire.beginTransmission(i2c_address);
Wire.write(pointer); // このタイミングでA/D変換が開始する。
Wire.endTransmission();
delay(delay_ms);
Wire.requestFrom(i2c_address, (byte)4);
unsigned int data = Wire.read();
data <<= 8;
data |= Wire.read();
data <<= 8;
data |= Wire.read();
data <<= 8;
data |= Wire.read();
return data;
}
void i2c_read_burst(byte pointer, int byte_count, byte* p) {
Wire.beginTransmission(i2c_address);
Wire.write(pointer);
Wire.endTransmission();
Wire.requestFrom(i2c_address, (byte)byte_count);
while (Wire.available() && byte_count > 0) {
*p++ = (byte)Wire.read();
byte_count--;
}
}
void i2c_write_reg(byte reg, byte data) {
Wire.beginTransmission(i2c_address);
Wire.write(reg);
Wire.write(data);
Wire.endTransmission();
}
};
class hdc1000 : public i2c_support
{
public:
hdc1000() : i2c_support(0x40) {
temp = 0.0;
humi = 0.0;
}
enum { reg_measure = 0, reg_config = 2, reg_id = 0xff };
bool init() {
i2c_write_reg(reg_config, 0x90); // 10000001B = RESET, 32bit MODE(temp << 16 + humi)
return (i2c_read16(reg_id) == 0x1000); // check device id.
}
void measure() {
uint32_t data = i2c_read32(reg_measure, 15);
temp = (float)((data >> 16) / 65536.0) * 165.0 - 40.0;
humi = (float)((data & 0xffff) / 65536.0) * 100.0;
}
float temp;
float humi;
};
# ifndef BME280_S32_t
# define BME280_S32_t int32_t
# endif
# ifndef BME280_U32_t
# define BME280_U32_t uint32_t
# endif
# ifndef BME280_S64_t
# define BME280_S64_t int64_t
# endif
class bme280 : public i2c_support
{
bool bme280_flag; // true : bme280, false: bmp280
public:
bme280(int address) : i2c_support(address) {
temp = 0.0;
humi = 0.0;
press = 0.0;
}
// results.
float temp;
float humi;
float press;
enum { Tmeasure = 10 }; // oversamplinkg, x1 x1 x1
/// registers.
enum { reg_id = 0xd0, reg_reset = 0xe0, reg_ctrl_hum = 0xf2, reg_ctrl_meas = 0xf4, reg_config = 0xf5 };
enum { reg_p_result = 0xf7, reg_t_result = 0xfa, reg_h_result = 0xfd };
// IIR Filter disable.
bool init() {
uint8_t id = i2c_read8(reg_id); // check device-id.
if (id == 0x60)
bme280_flag = true;
else if (id == 0x58)
bme280_flag = false;
else
return false;
load_bme280_compensation_params();
i2c_write_reg(reg_config, 0); // no stand-by, no IIR filter, no SPI.
return true;
}
// TEMP resolution = 16 + (osrs_t ? 1) bit, HUM resolution = 16 + (osrs_h ? 1) bit,
// select forced mode.
void measure() {
if (bme280_flag)
i2c_write_reg(reg_ctrl_hum, 1); // humi measurement control register.00000011 (osrs_h = 1);
i2c_write_reg(reg_ctrl_meas, 0x25); // measurement control register. 00100101 (osrs_p = 1, osrs_t = 1, Forced mode);
delay(Tmeasure);
byte buffer[10];
memset(buffer, 0, sizeof(buffer));
i2c_read_burst(reg_p_result, 8, buffer);
uint32_t raw_press = ((uint32_t)buffer[0] << 12) | ((uint32_t)buffer[1] << 4) | (((uint32_t)buffer[2] >> 4) & 0xf);
uint32_t raw_temp = ((uint32_t)buffer[3] << 12) | ((uint32_t)buffer[4] << 4) | (((uint32_t)buffer[5] >> 4) & 0xf);
uint16_t raw_humi = ((uint16_t)buffer[6] << 8) | (uint16_t)buffer[7];
// uint16_t raw_humi = i2c_read16(reg_h_result);
# if 0
Serial.print("raw_press = ");
Serial.println(raw_press, HEX);
Serial.print("raw_temp = ");
Serial.println(raw_temp, HEX);
Serial.print("raw_humi = ");
Serial.println(raw_humi, HEX);
# endif
temp = BME280_compensate_T_int32(raw_temp) / 100.0;
press = BME280_compensate_P_int64(raw_press) / 25600.0;
if (bme280_flag)
humi = bme280_compensate_H_int32(raw_humi) / 1024.0;
// reset.
// i2c_write_reg(reg_reset, 0xb6);
}
// from BST-BME280_DS001-10.pdf Chapter4.2.3 (rev.1.1)
private:
BME280_S32_t t_fine;
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
// t_fine carries fine temperature as global value
BME280_S32_t BME280_compensate_T_int32(BME280_S32_t adc_T) {
BME280_S32_t var1, var2, T;
var1 = ((((adc_T >> 3) - ((BME280_S32_t)dig_T1 << 1))) * ((BME280_S32_t)dig_T2)) >> 11;
var2 = (((((adc_T >> 4) - ((BME280_S32_t)dig_T1)) * ((adc_T >> 4) - ((BME280_S32_t)dig_T1))) >> 12) * ((BME280_S32_t)dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}
// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
BME280_U32_t BME280_compensate_P_int64(BME280_S32_t adc_P) {
BME280_S64_t var1, var2, p;
var1 = ((BME280_S64_t)t_fine) - 128000;
var2 = var1 * var1 * (BME280_S64_t)dig_P6;
var2 = var2 + ((var1 * (BME280_S64_t)dig_P5) << 17);
var2 = var2 + (((BME280_S64_t)dig_P4) << 35);
var1 = ((var1 * var1 * (BME280_S64_t)dig_P3) >> 8) + ((var1 * (BME280_S64_t)dig_P2) << 12);
var1 = (((((BME280_S64_t)1) << 47) + var1)) * ((BME280_S64_t)dig_P1) >> 33;
if (var1 == 0) {
return 0; // avoid exception caused by division by zero
}
p = 1048576 - adc_P;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((BME280_S64_t)dig_P9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((BME280_S64_t)dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((BME280_S64_t)dig_P7) << 4);
return (BME280_U32_t)p;
}
// Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits).
// Output value of “47445” represents 47445/1024 = 46.333 %RH
BME280_U32_t bme280_compensate_H_int32(BME280_S32_t adc_H) {
BME280_S32_t v_x1_u32r;
v_x1_u32r = (t_fine - ((BME280_S32_t)76800));
v_x1_u32r = (((((adc_H << 14) - (((BME280_S32_t)dig_H4) << 20) - (((BME280_S32_t)dig_H5) * v_x1_u32r)) +
((BME280_S32_t)16384)) >> 15) * (((((((v_x1_u32r * ((BME280_S32_t)dig_H6)) >> 10) * (((v_x1_u32r *
((BME280_S32_t)dig_H3)) >> 11) + ((BME280_S32_t)32768))) >> 10) + ((BME280_S32_t)2097152)) *
((BME280_S32_t)dig_H2) + 8192) >> 14));
v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((BME280_S32_t)dig_H1)) >> 4));
v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
return (BME280_U32_t)(v_x1_u32r >> 12);
}
// Chapter4.2.2. Trimming parameters.
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
uint8_t dig_H1;
int16_t dig_H2;
uint8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
uint8_t dig_H6;
void load_bme280_compensation_params() {
dig_T1 = i2c_read16_swab(0x88);
dig_T2 = (int16_t)i2c_read16_swab( 0x8a);
dig_T3 = (int16_t)i2c_read16_swab(0x8c);
dig_P1 = i2c_read16_swab(0x8e);
dig_P2 = (int16_t)i2c_read16_swab(0x90);
dig_P3 = (int16_t)i2c_read16_swab(0x92);
dig_P4 = (int16_t)i2c_read16_swab(0x94);
dig_P5 = (int16_t)i2c_read16_swab(0x96);
dig_P6 = (int16_t)i2c_read16_swab(0x98);
dig_P7 = (int16_t)i2c_read16_swab(0x9a);
dig_P8 = (int16_t)i2c_read16_swab(0x9c);
dig_P9 = (int16_t)i2c_read16_swab(0x9e);
if (bme280_flag) {
dig_H1 = i2c_read8(0xa1);
dig_H2 = (int16_t)i2c_read16_swab(0xe1);
dig_H3 = i2c_read8(0xe3);
uint16_t _e4 = (uint16_t)i2c_read8(0xe4);
uint16_t _e5 = (uint16_t)i2c_read8(0xe5);
uint16_t _e6 = (uint16_t)i2c_read8(0xe6);
dig_H4 = (int16_t)((_e4 << 4) + (_e5 & 0xf));
dig_H5 = (int16_t)((_e6 << 4) + ((_e5 >> 4) & 0xf));
dig_H6 = (int16_t)i2c_read8(0xe7);
}
}
};
# define START_MSG "\n" + String(__FILE__) + " start."
# define LED1 15
# define MEASURE_INTERVAL_SECONDS 300
bme280 bmp280(0x76);
void die(int msec, const char* cp) {
digitalWrite(LED1, 1);
delay(1000);
digitalWrite(LED1, 0);
delay(1000);
}
void setup() {
Serial.begin(115200);
Serial.println(START_MSG);
delay(50);
// RTC
setSyncProvider(RTC.get); // the function to get the time from the RTC
if (timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
// Time set TEST
//setTime(hr, min, sec, day, month, yr);
// setTime(12, 46, 00, 27, 05, 2017);
// RTC.set(now());
// set up the LCD's number of columns and rows:
// lcd.begin(16, 2);
// // Print a message to the LCD.
// lcd.print("hello, world!");
pinMode(0, INPUT_PULLUP); // SETUP
pinMode(1, INPUT_PULLUP); // + SW
pinMode(2, INPUT_PULLUP); // - SW
pinMode(LED1, OUTPUT);
digitalWrite(LED1, 1);
if (!bmp280.init())
die(500, "FATAL: BMP280 init failed.");
TFTscreen.begin();
TFTscreen.background(0, 0, 0);
// // 変数初期化
for (int i = 0; i < 24 ; i++)
{
PrehPa[i] = -1; // Pressure storage (Current)
PrehPaST[i] = -1; // Pressure storage (Start point)
PrehPaED[i] = -1; // Pressure storage (End point)
PrehPaH[i] = -1; // Pressure storage (Highest value)
PrehPaL[i] = 9999; // Pressure storage (Lowest value)
TempTrend[i] = -1; // Temperature storage (Current)
TempTrendST[i] = -1; // Temperature storage (Start point)
TempTrendED[i] = -1; // Temperature storage (End point)
TempTrendH[i] = -1; // Temperature storage (Highest value)
TempTrendL[i] = 9999; // Temperature storage (Lowest value)
}
/*
The MAX72XX is in power-saving mode on startup,
we have to do a wakeup call
*/
lc.shutdown(0, false);
/* Set the brightness to a medium values */
lc.setIntensity(0, 0);
/* and clear the display */
lc.clearDisplay(0);
MsTimer2::set(100, SwitchCheck); // 500ms毎にSwitchCheck( )割込み関数を呼び出す様に設定
MsTimer2::start(); // タイマー割り込み開始
}
void loop() {
String disp;
static int looptime = 0;
// bmp280.measure();
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
// lcd.setCursor(0, 0);
// disp = "Prs: ";
// disp += String(bmp280.press);
// disp += " hPa";
// lcd.print(disp);
// lcd.setCursor(0, 1);
// disp = "Tmp: ";
// disp += String(bmp280.temp);
// disp += " ";
// disp += char(0xDF); // degC
// disp += "C";
// lcd.print(disp);
looptime++; // delay(1000);
if( looptime > 20 && !setup_stat ) // same as delay(1000) && 時計調整中じゃない
{
bmp280.measure();
Serial.println("\nPa = " + String(bmp280.press) + ", Temp = " + String(bmp280.temp) );
debug_output();
// storeData();
UpdateLedDisp(false); // LED 8digit
UpdateTimeDateTftDisp(); // 時間表時
UpdatePressChart(); // 気圧チャート
UpdateTempChart(); // 気温チャート
storeData();
looptime = 0;
}
// SETUP
if ( LOW == b_sw0_stat ) // SETUPがACTIVEなら
{
setup_stat_old = setup_stat ;
setup_stat ++;
if ( setup_stat >= 6 )
setup_stat = 0;
UpdateTimeDateTftDisp(); // 時間表時
}
// Adjust
if ( setup_stat )
{
int inc = 0;
if ( LOW == b_sw1_stat && LOW == b_sw2_stat ) //同時おし
; // なにもしない
else if ( LOW == b_sw1_stat )
inc = 1;
else if ( LOW == b_sw2_stat )
inc = -1;
// int setup_stat = 0; // 0: Normal mode, 1: hour 2: min, 3, year, 4, mon, 5, day,
int yr, mn, dy, hr, mi, se;
yr = year();
mn = month();
dy = day();
hr = hour();
mi = minute();
se = second();
switch ( setup_stat )
{
case 1: // Hour
hr += inc ;
if( hr >= 24 ) hr = 0;
if( hr < 0 ) hr = 23;
break;
case 2: // min
mi += inc ;
if( inc )se = 0; // 秒をリセット 2018/02/04
if( mi >= 60 ) mi = 0;
if( mi < 0 ) mi = 59;
break;
case 3: // year
yr += inc ;
if( yr >= 2070 ) yr = 1971;
if( yr < 1971 ) yr = 2070;
break;
case 4: // mon
mn += inc ;
if( mn >= 12 ) mn = 0;
if( mn < 0 ) mn = 11; break;
case 5: // day
dy += inc ;
if( dy >= 32 ) dy = 0;
if( dy < 0 ) dy = 31; break;
break;
default:
break;
}
//Time set
//setTime(hr, min, sec, day, month, yr);
setTime(hr, mi, se, dy, mn, yr);
RTC.set(now());
UpdateLedDisp(false); // LED 8digit
UpdateTimeDateTftDisp(); // 時間表時
// rtc.time(g_t);
// //UpdateOledDisp();
// UpdateLedDisp(true); // LED for 8 digt
}
delay(50);
}
void debug_output()
{
# if 0
int i;
Serial.println("old_PrehPa:" + String(old_PrehPa)
+ "now_PrehPa = " + String(now_PrehPa) );
for(i = 0; i < 24 ; i++)
Serial.print( String(PrehPa[i]) + ", ");
Serial.println("");
for(i = 0; i < 24 ; i++)
Serial.print( String(PrehPaST[i]) + ", ");
Serial.println("");
for(i = 0; i < 24 ; i++)
Serial.print( String(PrehPaED[i]) + ", ");
Serial.println("");
for(i = 0; i < 24 ; i++)
Serial.print( String(PrehPaH[i]) + ", ");
Serial.println("");
for(i = 0; i < 24 ; i++)
Serial.print( String(PrehPaL[i]) + ", ");
Serial.println("");
Serial.println("old_TempTrend:" + String(old_TempTrend)
+ "now_TempTrend = " + String(now_TempTrend) );
for(i = 0; i < 24 ; i++)
Serial.print( String(TempTrend[i]) + ", ");
Serial.println("");
for(i = 0; i < 24 ; i++)
Serial.print( String(TempTrendST[i]) + ", ");
Serial.println("");
for(i = 0; i < 24 ; i++)
Serial.print( String(TempTrendED[i]) + ", ");
Serial.println("");
for(i = 0; i < 24 ; i++)
Serial.print( String(TempTrendH[i]) + ", ");
Serial.println("");
for(i = 0; i < 24 ; i++)
Serial.print( String(TempTrendL[i]) + ", ");
Serial.println("");
# endif
}
void storeData(void)
{
int i = hour();
PrehPa[i] = (int)bmp280.press; // Pressure strage
if ( PrehPaH[i] < PrehPa[i] ) PrehPaH[i] = PrehPa[i]; // High
if ( PrehPaL[i] > PrehPa[i] ) PrehPaL[i] = PrehPa[i]; // Low
TempTrend[i] = (int)(bmp280.temp*10.); // Pressure strage
if ( TempTrendH[i] < TempTrend[i] ) TempTrendH[i] = TempTrend[i]; // High
if ( TempTrendL[i] > TempTrend[i] ) TempTrendL[i] = TempTrend[i]; // Low
}
void UpdateTimeDateTftDisp(void)
{
char text[20];
if ( old_hor != hour() ) // 時間開始、終了値保存
{
int hournow = hour();
PrehPaST[hournow] = (int)bmp280.press;
TempTrendST[hournow] = (int)(bmp280.temp * 10.);
if ( hournow == 0 ) hournow = 24;
PrehPaED[hournow - 1] = (int)bmp280.press;
TempTrendED[hournow - 1] = (int)(bmp280.temp * 10.);
old_hor = hour();
old_min -= 1; // force update
old_day -= 1; // force update
old_PrehPa -= 1; // force update
old_TempTrend -= 1; // force update
clearDisp();
}
// Hour: Min
if (old_min != minute())
{
TFTscreen.setTextSize(1);
// delete
TFTscreen.stroke(0, 0, 0);
TFTscreen.text(old_timetext, 3, 3);
// display
sprintf(text, "%02d:%02d", hour(), minute());
TFTscreen.stroke(0, 128, 0);
TFTscreen.text(text, 3, 3);
strcpy(old_timetext, text);
old_min = minute(); // update the time
}
// Date, year
if ( old_day != day())
{
// delete
TFTscreen.stroke(0, 0, 0);
TFTscreen.text(old_daytext, 64, 3);
// display
TFTscreen.setTextSize(1);
char str_mon[4];
char str_day[4];
strcpy( str_mon, monthShortStr(month()));
strcpy( str_day, dayShortStr( weekday()));
sprintf(text, "%02d %s %04d %s", day(), str_mon, year(), str_day );
TFTscreen.stroke(0, 128, 0);
TFTscreen.text(text, 64, 3);
strcpy(old_daytext, text);
old_day = day(); // update the time
}
// 時刻合わせ
if ( 0 != setup_stat)
{
TFTscreen.stroke(128, 0, 0);
const char* setupstring[] = {"HOUR", "MIN.", "YEAR", "MON.", "DAY ", "WEEK"};
sprintf(text, "SET: %s (%02d)", setupstring[ setup_stat - 1 ],second()); // 秒を表示 2018/02/04
TFTscreen.text(text, 32, 32);
TFTscreen.fillRect( 32, 32, 108, 10, 0 );
}
else if( setup_stat == 0 && setup_stat_old != 0 )
{
setup_stat_old = 0;
TFTscreen.fillRect( 32, 32, 108, 10, 0 );
}
}
//////////////////////////////////////////////////////////////////////////////
// 気圧表示
/////////////////////////////////////////////////////////////////////////////
void UpdatePressChart(void)
{
drawPressChartFrame(); // DrawChart Frame
//drawPrassChart(); // Draw graph
drawPrassChartRosoku(); // Rosoku Draw graph
drawPresentPress(); // Draw Circle to show present
}
# define gr_TOP 70 // 15
# define gr_BTM 120
# define gr_LFT 25
# define gr_RGT 150
void drawPresentPress()
{
now_PrehPa = (int)bmp280.press;
if ( old_PrehPa != now_PrehPa )
{
// Clear Old plot
uint16_t col = TFTscreen.newColor(0, 0, 0);
TFTscreen.drawCircle( GetXpos(hour()), GetYpos(old_PrehPa), 3, col);
char text[10];
sprintf(text, "%d", old_PrehPa);
TFTscreen.stroke(0, 0, 0);
TFTscreen.text(text, GetXpos(hour()), GetYpos(old_PrehPa) );
//DIsp new
col = TFTscreen.newColor(255, 0, 0);
TFTscreen.drawCircle( GetXpos(hour()), GetYpos(now_PrehPa), 3, col);
sprintf(text, "%d", now_PrehPa);
TFTscreen.stroke(128, 128, 255);
TFTscreen.text(text, GetXpos(hour()), GetYpos(now_PrehPa) );
old_PrehPa = now_PrehPa;
}
}
void drawPrassChartRosoku()
{
int i;
for (i = 1; i < 24; i++)
{
if ( PrehPa[i] == -1 || PrehPaST[i] == -1 || PrehPaED[i] == -1 || PrehPaH[i] == -1 || PrehPaH[i] == 9999 ) // no data
continue;
drawPrassChartRosukuSub( i, GetYpos(PrehPaST[i]), GetYpos(PrehPaED[i]), GetYpos(PrehPaH[i]), GetYpos(PrehPaL[i]), ( PrehPaED[i - 1] > PrehPa[i]) ? 1 : 0);
}
}
void drawPrassChart()
{
int i;
for (i = 1; i < 24; i++)
{
if ( PrehPa[i] == -1 || PrehPa[i - 1] == -1 ) // no data
continue;
if ( i == hour() + 1 )
continue;
TFTscreen.drawLine( GetXpos(i - 1), GetYpos(PrehPa[i - 1])
, GetXpos(i), GetYpos(PrehPa[i])
, TFTscreen.newColor(128, 128, 128)
);
}
}
void drawPressChartFrame()
{
// 枠をカク X:0-159, Y:0-127
uint16_t col = TFTscreen.newColor(128, 128, 128);
TFTscreen.drawLine( gr_LFT, gr_TOP, gr_RGT, gr_TOP, col); // 上
TFTscreen.drawLine( gr_RGT, gr_TOP, gr_RGT, gr_BTM, col); // 右
TFTscreen.drawLine( gr_LFT, gr_BTM, gr_RGT, gr_BTM, col); // 下
TFTscreen.drawLine( gr_LFT, gr_TOP, gr_LFT, gr_BTM, col); // 左
// 縦軸表示 995 - 1020hPa
TFTscreen.setTextSize(1);
TFTscreen.stroke(128, 128, 128);
// TFTscreen.text(" 995", 0, GetYpos(995) );
TFTscreen.text("1000", 0, GetYpos(1000) );
// TFTscreen.text("1005", 0, GetYpos(1005) );
TFTscreen.text("1010", 0, GetYpos(1010) );
// TFTscreen.text("1015", 0, GetYpos(1015) );
TFTscreen.text("1020", 0, GetYpos(1020) );
// 横軸表示
int Ypos = GetYpos(995) + 1;
TFTscreen.text("0", GetXpos(0), Ypos );
TFTscreen.text("3", GetXpos(3), Ypos );
TFTscreen.text("6", GetXpos(6), Ypos );
TFTscreen.text("9", GetXpos(9), Ypos );
TFTscreen.text("12", GetXpos(12), Ypos );
TFTscreen.text("15", GetXpos(15), Ypos );
TFTscreen.text("18", GetXpos(18), Ypos );
TFTscreen.text("21", GetXpos(21), Ypos );
}
int GetYpos(int Press)
{
return gr_BTM - ( ( Press - 995 ) * (gr_BTM - gr_TOP)) / (1020 - 995) ;
}
int GetXpos(int Hour)
{
return gr_LFT + ( ( Hour ) * 52) / 10 ; // 5.2 = (gr_RGT-gr_LFT)/24hour
}
//////////////////////////////////////////////////////////////////////////////
// 温度表示
/////////////////////////////////////////////////////////////////////////////
# define gr_TOP_T 15
# define gr_BTM_T 65
# define gr_LFT_T 25
# define gr_RGT_T 150
void UpdateTempChart(void)
{
drawTempChartFrame(); // DrawChart Frame
drawTempChart(); // Draw graph
drawPresentTemp(); // Draw Circle to show present
}
void drawPresentTemp()
{
now_TempTrend = (int)(bmp280.temp * 10.);
if ( old_TempTrend != now_TempTrend )
{
// Clear Old plot
uint16_t col = TFTscreen.newColor(0, 0, 0);
TFTscreen.drawCircle( GetXpos(hour()), GetYpos_t(old_TempTrend), 3, col);
char text[10];
sprintf(text, "%d.%d", (old_TempTrend - (old_TempTrend % 10)) / 10, old_TempTrend % 10 );
TFTscreen.stroke(0, 0, 0);
TFTscreen.text(text, GetXpos(hour()), GetYpos_t(old_TempTrend) );
//DIsp new
col = TFTscreen.newColor(255, 0, 0);
TFTscreen.drawCircle( GetXpos(hour()), GetYpos_t(now_TempTrend), 3, col);
sprintf(text, "%d.%d", (now_TempTrend - (now_TempTrend % 10)) / 10, now_TempTrend % 10 );
TFTscreen.stroke(128, 128, 255);
TFTscreen.text(text, GetXpos(hour()), GetYpos_t(now_TempTrend) );
old_TempTrend = now_TempTrend ;
}
}
void drawTempChart()
{
int i;
for (i = 1; i < 24; i++)
{
if ( TempTrend[i] == -1 || TempTrend[i - 1] == -1 || TempTrend[i + 1] == -1 ) // no data
continue;
if ( i == hour() + 1 )
continue;
TFTscreen.drawLine( GetXpos(i - 1), GetYpos_t(TempTrend[i - 1])
, GetXpos(i), GetYpos_t(TempTrend[i])
, TFTscreen.newColor(128, 128, 128)
);
}
}
void drawTempChartFrame()
{
// 枠をカク X:0-159, Y:0-127
uint16_t col = TFTscreen.newColor(128, 128, 128);
TFTscreen.drawLine( gr_LFT_T, gr_TOP_T, gr_RGT_T, gr_TOP_T, col); // 上
TFTscreen.drawLine( gr_RGT_T, gr_TOP_T, gr_RGT_T, gr_BTM_T, col); // 右
TFTscreen.drawLine( gr_LFT_T, gr_BTM_T, gr_RGT_T, gr_BTM_T, col); // 下
TFTscreen.drawLine( gr_LFT_T, gr_TOP_T, gr_LFT_T, gr_BTM_T, col); // 左
// 縦軸表示 20 - 35degC
TFTscreen.setTextSize(1);
TFTscreen.stroke(128, 128, 128);
// TFTscreen.text(" 20", 0, GetYpos_t(995) );
TFTscreen.text(" 23", 0, GetYpos_t(230) );
// TFTscreen.text("26", 0, GetYpos_t(1005) );
TFTscreen.text(" 29", 0, GetYpos_t(290) );
// TFTscreen.text("32", 0, GetYpos_t(1015) );
TFTscreen.text(" 35", 0, GetYpos_t(350) );
// // 横軸表示
// int Ypos = GetYpos_t(20) + 1;
// TFTscreen.text("0", GetXpos(0), Ypos );
// TFTscreen.text("3", GetXpos(3), Ypos );
// TFTscreen.text("6", GetXpos(6), Ypos );
// TFTscreen.text("9", GetXpos(9), Ypos );
// TFTscreen.text("12", GetXpos(12), Ypos );
// TFTscreen.text("15", GetXpos(15), Ypos );
// TFTscreen.text("18", GetXpos(18), Ypos );
// TFTscreen.text("21", GetXpos(21), Ypos );
}
int GetYpos_t(int Temp)
{
return gr_BTM_T - ( ( Temp - 200 ) * (gr_BTM_T - gr_TOP_T)) / (350 - 200) ;
}
/////////////////////////////////////////////////////////////////////
// ROSUKU chart drawer
/////////////////////////////////////////////////////////////////////
void drawPrassChartRosukuSub(int hour, int st, int ed, int high, int low, int up )
{
int xPosOrig = GetXpos( hour );
uint16_t col = (up) ? TFTscreen.newColor(0, 255, 0) : TFTscreen.newColor(0, 0, 255);
for (int i = 1; i < 24; i++)
{
if ( PrehPa[i] == -1 ) // no data
continue;
drawBox( xPosOrig - 2, st, xPosOrig + 2, ed, col );
TFTscreen.drawLine( xPosOrig, st, xPosOrig, high, col ); // 上
TFTscreen.drawLine( xPosOrig, ed, xPosOrig, low, col ); // 上
}
}
void drawBox(int left, int top, int right, int btm, uint16_t col)
{
TFTscreen.drawLine( left, top, right, top, col ); // 上
TFTscreen.drawLine( right, top, right, btm, col ); // 右
TFTscreen.drawLine( left, top, left, btm, col ); // 左
TFTscreen.drawLine( left, btm, right, btm, col ); // 下
}
void clearDisp() {
uint16_t col = TFTscreen.newColor(0, 0, 0);
TFTscreen.fillScreen( col ); // 上
}
void UpdateLedDisp(boolean force)
{
# if 0 // mm.ddhh.mm表示
// Mon
if ( old_day_led != day() || force )
{
old_day_led = day();
lc.setDigit(0, 7, (month() / 10), false);
lc.setDigit(0, 6, (month() % 10), true);
// Day
lc.setDigit(0, 5, (day() / 10), false);
lc.setDigit(0, 4, (day() % 10), false);
}
# else // m.dd_hh.mm表示
if ( old_day_led != day() || force)
{
old_day_led = day();
if ( month() < 9)
lc.setDigit(0, 7, (month() % 10), true);
else
{
const char month_char[] = "0123456789ond";
lc.setChar(0, 7, month_char[month()], true);
}
// Day
lc.setDigit(0, 6, (day() / 10), false);
lc.setDigit(0, 5, (day() % 10), false);
}
# endif
// hour
if (old_min_led != minute() || force)
{
old_min_led = minute();
lc.setDigit(0, 3, (hour() / 10), false);
lc.setDigit(0, 2, (hour() % 10), true);
// minute
lc.setDigit(0, 1, (minute() / 10), false);
lc.setDigit(0, 0, (minute() % 10), false);
}
}
// 割り込み時に処理される関数
void SwitchCheck() {
static boolean sw0 = HIGH;
static boolean sw1 = HIGH;
static boolean sw2 = HIGH;
b_sw0_stat = digitalRead( 0 );
if ( sw0 == b_sw0_stat ) b_sw0_stat = sw0;
sw0 = b_sw0_stat;
b_sw1_stat = digitalRead( 1 );
if ( sw1 == b_sw1_stat ) b_sw1_stat = sw1;
sw1 = b_sw1_stat;
b_sw2_stat = digitalRead( 2 );
if ( sw2 == b_sw2_stat ) b_sw2_stat = sw2;
sw2 = b_sw2_stat;
}
ケース
100均で購入してきたバルサ材の箱におさめました。カッターで加工できるので便利です。
おわりに
縦軸は23-35℃、1000-1020hPaで固定しています。お住いの地域に合せて変更してください。(ハードコーディングしていてごめんなさい)
気温、気圧データは電源OFFでリセットされます。
2018/08/26 Ikeda