前回はこちらです。配線まで終わったので、ソフトウェアを作って完成させましょう。
Arduinoのスケッチはこの記事の一番下に貼っておきますが、相当長くてコピーするのも大変だと思いますので、Githubに公開リポジトリを作っておきました。
せっかく10個もLEDがあるので、1時間周期でLEDが順番に変わっていくようにしました。10時間前からの変化がわかると夕立や台風の時の温度変化もわかるかなと思ったのです。
Arduinoで時間を計るといえばまず思いつくのがdelayを使う方法ですが、これだと待っている間何もできないのが困ったところです。数秒待つならともかく、1時間待つのにはあまり適した方法ではないと言えるでしょう。
Arduinoには、起動してからの時間をミリ秒単位で返してくるmillis()という関数があります。50日程度で1周するのでその時に動作がおかしくなることが予想されますが、そこまで長く動作させることもないでしょうからこれでよしとしました。気になる方は直してみてください。
LEDの色変化はtempColor_R, _G, _BとbaroColor_R, _G, _Bで定義しています。アドレス設定の回でも書きましたがこのLED、全力だと暖かくなるほど光ってしまうので、半分程度の明るさで抑えるように設定しました。温度はサーモグラフィー的な感じ、気圧は天気図の低気圧〜高気圧をイメージして色を設定しました。いかがでしょうか。
温度の変化範囲はtempTableとbaroTableで定義しています。温度が℃で気圧がヘクトパスカルです。東京で通年で使うなら温度は-5〜35℃くらいにしたほうがいいと思いますが、あまり変化がないと面白みがないのでそこらへんは適当に変えてみてください。
LEDそのままでもよいのですが、直接LEDを見るとまぶしいのとイマイチ風情がないのでコピー用紙をちょいと切って行灯(あんどん)のようにしてみました。数cm離した位置に紙を立てるとぼんやりとしてちょっとだけインテリアっぽくなりました。
配線をちゃんとしてケースか何かに入れると、ひと夏くらいは使える物になるのかなあ、とも思いました。
光センサを付けて暗くなったらLEDも暗くするとか、LEDスティックをもう一本つないで湿度も分かるようにするとか、いろいろ工夫の余地はありそうです。
今回の工作で使ったBME280モジュールを使って工作して発表してくださったみなさんにはプレゼントの企画もありますし、ぜひチャレンジしてみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | #include <Wire.h> #include "MsTimer2.h" #include "Ws2822s.h" #define PIXEL_NUM 20 //使用するWS2822Sの数 #define LED_PIN 14 //WS2822SのDAIピンにつなげるArduinoのピン番号 #define BME280_ADDRESS 0x76 // BME280のI2Cアドレス unsigned long int hum_raw, temp_raw, pres_raw; signed long int t_fine; unsigned long runtime_next; unsigned long interval = 60 * 60 * 1000L; // 60分×60秒×1000ms // 変換用変数 uint16_t dig_T1; int16_t dig_T2, dig_T3; uint16_t dig_P1; int16_t dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9; int8_t dig_H1; int16_t dig_H2; int8_t dig_H3; int16_t dig_H4; int16_t dig_H5; int8_t dig_H6; Ws2822s LED(LED_PIN , PIXEL_NUM); int tempColor_R[] = {0, 6, 45, 80, 100, 110, 120, 125, 125, 125, 125}; // 温度 色変化テーブル(赤) int tempColor_G[] = {0, 0, 0, 0, 0, 20, 45, 70, 95, 120, 125}; // 温度 色変化テーブル(緑) int tempColor_B[] = {0, 60, 75, 75, 60, 5, 0, 0, 5, 60, 125}; // 温度 色変化テーブル(青) int baroColor_R[] = {125, 100, 75, 50, 25, 0, 0, 0, 0, 0, 0}; // 気圧 色変化テーブル(赤) int baroColor_G[] = {0, 0, 25, 50, 75, 100, 75, 50, 25, 0, 0}; // 気圧 色変化テーブル(緑) int baroColor_B[] = {0, 0, 0, 0, 0, 0, 25, 50, 75, 100, 125}; // 気圧 色変化テーブル(青) int tempMem_R[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // 温度用メモリ確保(赤) int tempMem_G[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // 温度用メモリ確保(緑) int tempMem_B[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // 温度用メモリ確保(青) int baroMem_R[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // 気圧用メモリ確保(赤) int baroMem_G[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // 気圧用メモリ確保(緑) int baroMem_B[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // 気圧用メモリ確保(青) int tempTable[] = {20, 30}; // 温度範囲の指定 long baroTable[] = {950, 1050}; // 気圧範囲の指定 // LEDに色をセットする関数 void write_color() { LED.send(); } void setup() { uint8_t osrs_t = 1; //Temperature oversampling x 1 uint8_t osrs_p = 1; //Pressure oversampling x 1 uint8_t osrs_h = 1; //Humidity oversampling x 1 uint8_t mode = 3; //Normal mode uint8_t t_sb = 5; //Tstandby 1000ms uint8_t filter = 0; //Filter off uint8_t spi3w_en = 0; //3-wire SPI Disable uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode; uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en; uint8_t ctrl_hum_reg = osrs_h; //色配列の初期化 for (int i = 0; i < PIXEL_NUM; i++) { LED.setColor(i, 0, 0, 0); } //色情報送信用タイマ設定 MsTimer2::set(100, write_color); MsTimer2::start(); Serial.begin(9600); Wire.begin(); writeReg(0xF2, ctrl_hum_reg); writeReg(0xF4, ctrl_meas_reg); writeReg(0xF5, config_reg); readTrim(); // Read trim data from BME280 runtime_next = millis() + interval; // 次にLEDを更新する時間を決める } void loop() { double temp_act = 0.0, press_act = 0.0, hum_act = 0.0; //最終的に表示される値を入れる変数 signed long int temp_cal; unsigned long int press_cal, hum_cal; readData(); temp_cal = calibration_T(temp_raw); press_cal = calibration_P(pres_raw); hum_cal = calibration_H(hum_raw); temp_act = (double)temp_cal / 100.0; press_act = (double)press_cal / 100.0; hum_act = (double)hum_cal / 1024.0; Serial.print("TEMP : "); Serial.print(temp_act); Serial.print(" DegC PRESS : "); Serial.print(press_act); Serial.print(" hPa HUM : "); Serial.print(hum_act); Serial.println(" %"); // -------------------------------------- // 気温をLEDバーの色に変換する // -------------------------------------- // 一つ前のLEDの色データを次のLEDに送る (温度編) for (int i = 1; i <= 9; i++) { tempMem_R[i - 1] = tempMem_R[i]; tempMem_G[i - 1] = tempMem_G[i]; tempMem_B[i - 1] = tempMem_B[i]; } // 設定した最低、最高温度を0-999とした時の、現在の温度を計算する int tt = (temp_act * 100 - tempTable[0] * 100) / (tempTable[1] * 100 - tempTable[0] * 100) * 1000; if (tt < 0) tt = 0; // 低すぎるときは0に if (tt > 999) tt = 999; // 高すぎるときは999に // 表示用メモリにRGB値を設定する // 足し算の前半でおおまかな色を決めて、足し算の後半で滑らかに色が変化するように細かい数字を足す tempMem_R[9] = tempColor_R[(int)tt / 100] + ((tempColor_R[(int)tt / 100 + 1] - tempColor_R[(int)tt / 100]) * (tt % 100)) / 100; tempMem_G[9] = tempColor_G[(int)tt / 100] + ((tempColor_G[(int)tt / 100 + 1] - tempColor_G[(int)tt / 100]) * (tt % 100)) / 100; tempMem_B[9] = tempColor_B[(int)tt / 100] + ((tempColor_B[(int)tt / 100 + 1] - tempColor_B[(int)tt / 100]) * (tt % 100)) / 100; // -------------------------------------- // 気圧をLEDバーの色に変換する // -------------------------------------- // 一つ前のLEDの色データを次のLEDに送る (気圧編) for (int i = 8; i >= 0; i--) { baroMem_R[i + 1] = baroMem_R[i]; baroMem_G[i + 1] = baroMem_G[i]; baroMem_B[i + 1] = baroMem_B[i]; } // 設定した最低、最高気圧を0-999とした時の、現在の気圧を計算する int bt = (press_act * 100 - baroTable[0] * 100) / (baroTable[1] * 100 - baroTable[0] * 100) * 1000; if (bt < 0) bt = 0; // 低すぎるときは0に if (bt > 999) bt = 999; // 高すぎるときは999に // 表示用メモリにRGB値を設定する // 足し算の前半でおおまかな色を決めて、足し算の後半で滑らかに色が変化するように細かい数字を足す baroMem_R[0] = baroColor_R[(int)bt / 100] + ((baroColor_R[(int)bt / 100 + 1] - baroColor_R[(int)bt / 100]) * (bt % 100)) / 100; baroMem_G[0] = baroColor_G[(int)bt / 100] + ((baroColor_G[(int)bt / 100 + 1] - baroColor_G[(int)bt / 100]) * (bt % 100)) / 100; baroMem_B[0] = baroColor_B[(int)bt / 100] + ((baroColor_B[(int)bt / 100 + 1] - baroColor_B[(int)bt / 100]) * (bt % 100)) / 100; // LEDに色を設定する for (int i = 0; i < 10; i++) { LED.setColor(i, baroMem_R[i], baroMem_G[i], baroMem_B[i]); LED.setColor(i + 10, tempMem_R[i], tempMem_G[i], tempMem_B[i]); } while (runtime_next > millis()) { // 指定された時間まで待つ } runtime_next = millis() + interval; // 次にLEDが変化する時間を設定 } // -------------------------------------------------------------- // チップ固有の調整用データを読み出す // -------------------------------------------------------------- void readTrim() { uint8_t data[33], i = 0; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0x88); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS, 24); while (Wire.available()) { data[i] = Wire.read(); i++; } Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xA1); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS, 1); data[i] = Wire.read(); i++; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xE1); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS, 7); while (Wire.available()) { data[i] = Wire.read(); i++; } dig_T1 = (data[1] << 8) | data[0]; dig_T2 = (data[3] << 8) | data[2]; dig_T3 = (data[5] << 8) | data[4]; dig_P1 = (data[7] << 8) | data[6]; dig_P2 = (data[9] << 8) | data[8]; dig_P3 = (data[11] << 8) | data[10]; dig_P4 = (data[13] << 8) | data[12]; dig_P5 = (data[15] << 8) | data[14]; dig_P6 = (data[17] << 8) | data[16]; dig_P7 = (data[19] << 8) | data[18]; dig_P8 = (data[21] << 8) | data[20]; dig_P9 = (data[23] << 8) | data[22]; dig_H1 = data[24]; dig_H2 = (data[26] << 8) | data[25]; dig_H3 = data[27]; dig_H4 = (data[28] << 4) | (0x0F & data[29]); dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F); dig_H6 = data[31]; } // -------------------------------------------------------------- // BME280のレジスタに書き込む // -------------------------------------------------------------- void writeReg(uint8_t reg_address, uint8_t data) { Wire.beginTransmission(BME280_ADDRESS); Wire.write(reg_address); Wire.write(data); Wire.endTransmission(); } // -------------------------------------------------------------- // BME280のデータを読み出す // -------------------------------------------------------------- void readData() { int i = 0; uint32_t data[8]; Wire.beginTransmission(BME280_ADDRESS); Wire.write(0xF7); Wire.endTransmission(); Wire.requestFrom(BME280_ADDRESS, 8); while (Wire.available()) { data[i] = Wire.read(); i++; } pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4); hum_raw = (data[6] << 8) | data[7]; } // -------------------------------------------------------------- // 温度を計算する // -------------------------------------------------------------- signed long int calibration_T(signed long int adc_T) { signed long int var1, var2, T; var1 = ((((adc_T >> 3) - ((signed long int)dig_T1 << 1))) * ((signed long int)dig_T2)) >> 11; var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T >> 4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14; t_fine = var1 + var2; T = (t_fine * 5 + 128) >> 8; return T; } // -------------------------------------------------------------- // 気圧を計算する // -------------------------------------------------------------- unsigned long int calibration_P(signed long int adc_P) { signed long int var1, var2; unsigned long int P; var1 = (((signed long int)t_fine) >> 1) - (signed long int)64000; var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((signed long int)dig_P6); var2 = var2 + ((var1 * ((signed long int)dig_P5)) << 1); var2 = (var2 >> 2) + (((signed long int)dig_P4) << 16); var1 = (((dig_P3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((signed long int)dig_P2) * var1) >> 1)) >> 18; var1 = ((((32768 + var1)) * ((signed long int)dig_P1)) >> 15); if (var1 == 0) { return 0; } P = (((unsigned long int)(((signed long int)1048576) - adc_P) - (var2 >> 12))) * 3125; if (P < 0x80000000) { P = (P << 1) / ((unsigned long int) var1); } else { P = (P / (unsigned long int)var1) * 2; } var1 = (((signed long int)dig_P9) * ((signed long int)(((P >> 3) * (P >> 3)) >> 13))) >> 12; var2 = (((signed long int)(P >> 2)) * ((signed long int)dig_P8)) >> 13; P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4)); return P; } // -------------------------------------------------------------- // 湿度を計算する // -------------------------------------------------------------- unsigned long int calibration_H(signed long int adc_H) { signed long int v_x1; v_x1 = (t_fine - ((signed long int)76800)); v_x1 = (((((adc_H << 14) - (((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) + ((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) * (((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int) 32768))) >> 10) + (( signed long int)2097152)) * ((signed long int) dig_H2) + 8192) >> 14)); v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4)); v_x1 = (v_x1 < 0 ? 0 : v_x1); v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1); return (unsigned long int)(v_x1 >> 12); } |