ArduinoにLCDディスプレイを付けて文字を表示させる
温湿度センサーと一緒にAmazonで購入していたArduino用のディスプレイモジュールが届いたので使ってみました。
上の画像のような商品で、Arduinoに上から被せるようにつけるだけで使用可能です。
実際につけた画像がこちらです。
一つ問題がありまして、今まではピンを刺すだけでArduinoと接続できたものが、このモジュールを使う場合はハンダ付けする必要があります。
今回は、ハンダ付けするのが面倒だったので、ピンを接触させて抜けないように固定しているだけです。
このLCDモジュールで使用するピン番号は以下の通りです。
Analog 0 :キー(select, up, right, down and left)
Digital 4 :DB4
Digital 5 :DB5
Digital 6 :DB6
Digital 7 :DB7
Digital 8 :RS (Data or Signal Display Selection)
Digital 9 :Enable
これ以外のピンがLCDモジュールの穴の開いている部分に割り当てられています。
その穴にジャンパワイヤのオス側を入れて抜けないように固定するか、ハンダ付けすれば使えると思います。
また、文字の出力にはライブラリを使うと簡単に表示することが出来ます。
実際に表示したものがこちらです。
今回使用したDHT11のセンサから取得した値をLCDディスプレイモジュールに出力させるコードを以下に載せます。コメントが英語になっている部分はドキュメントのサンプルコードをそのまま使用しています。
#include <DHT.h> #include <LiquidCrystal.h> #define DHTPIN 2 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); float temp_c = 0; // 摂氏値( ℃ ) int red_led = 12; int blue_led = 13; int sum = 0; float value1 = 0; int data = 0; // LCD panelで使用するピン番号の選択 LiquidCrystal lcd(8, 9, 4, 5, 6, 7); int lcd_key = 0; int adc_key_in = 0; #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 #define BACKLIGHT 10 int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result // For V1.1 us this threshold if (adc_key_in < 50) return btnRIGHT; if (adc_key_in < 250) return btnUP; if (adc_key_in < 450) return btnDOWN; if (adc_key_in < 650) return btnLEFT; if (adc_key_in < 850) return btnSELECT; return btnNONE; // when all others fail, return this... } void setup(){ Serial.begin(9600); // シリアル通信速度 pinMode ( red_led, OUTPUT); pinMode ( blue_led, OUTPUT); dht.begin(); lcd.begin(16, 2); // start the library } void loop(){ lcd_key = read_LCD_buttons(); // read the buttons switch (lcd_key) // depending on which button was pushed, we perform an action { case btnRIGHT: { break; } case btnLEFT: { break; } case btnUP: { analogWrite(BACKLIGHT, 255/4); break; } case btnDOWN: { break; } case btnSELECT: { break; } case btnNONE: { //バックライトを少し暗めに設定 analogWrite(BACKLIGHT, 64/4); break; } } // アナログピンから計測値を取得(0〜203) //センサーの値を50回読み取り平均化する sum = 0; for (int i = 0; i < 50; i++) { data = analogRead(1); sum = sum + data; delay(2); } value1 = sum / 50; // 入力値を摂氏に換算 temp_c = ((5 * value1) / 1024) * 100; Serial.println( temp_c ); //温湿度センサから値を取得 float h = dht.readHumidity(); float t = dht.readTemperature(); if (isnan(h) || isnan(t)) { Serial.println("Failed to read from DHT sensor!"); lcd.setCursor(0,0); lcd.print("Can't get data!"); return; } //文字のクリア lcd.clear(); Serial.println(h); lcd.setCursor(0,1); lcd.print("Humidity:"); lcd.setCursor(9,1); lcd.print(h); lcd.setCursor(13,1); lcd.print("\x25"); lcd.setCursor(0,0); lcd.print("Temp:"); Serial.println(t); lcd.setCursor(5,0); lcd.print(t); lcd.setCursor(9,0); lcd.print("\xdf"); lcd.setCursor(10,0); lcd.print("C"); if(t > 20 && t < 30){ // LEDを点灯 ( HIGHは電圧レベル ) digitalWrite( blue_led, HIGH ); digitalWrite( red_led, LOW ); }else{ digitalWrite( red_led, HIGH ); digitalWrite( blue_led, LOW ); } // 1000ms待機 delay(10000); }
ハリネズミウォッチの完成
前回に引き続きハリネズミ管理システムの温度湿度表示Pebbleアプリを開発していきます。前回まででほぼ完成しているので、時計の表示とアニメーションの追加を行って完成とします。
実際に開発したアプリはこのようになりました。
前回までのと比較して変更した部分は
- 時計のラベルの追加、一秒ごとに時間取得をして表示
- ハリネズミの画像を一秒ごとに切り替えてアニメーションさせる
- iPhoneとのBluetooth通信が切れた際にバイブで知らせる
- ステータスにハートを追加(適温かつBluetooth接続中)
の主に4つです。バッテリー状態も取得していますが、表示部分が微妙だったので使ってません。
Githubにソースファイル一式を公開しますので、CloudPebbleでインポートして変更して使用することが出来ます。
もし参考にしてみたい方がいればどうぞ!
GitHub - hiro0202/HariWatch
- システムの全体像
まずハリネズミのマロンがいます。
この子の温度管理の為のシステム構成が以下の通りです。
PebbleからRaspberry piサーバーへ一定間隔でGetリクエストを送ります。
Pebble→iPhone→家のルーター→Raspberrypiという順番にリクエストが送られていき、温度・湿度の情報はJSON形式でその逆に送られていきます。
温度はArduinoにつながっているセンサーから取得し、Raspberry piとArduinoはUSBのシリアル通信で温度・湿度の情報を受け渡ししています。
部屋に居る時に視覚的にすぐに適温かチェックできるようにArduinoでは適温の際に青いLED、それ以外は赤いLEDを点灯させます。
この様な感じで開発しました。
ソースコードは以下の通りです。
#include "pebble.h" static Window *s_main_window; static TextLayer *s_temperature_layer; static TextLayer *s_city_layer; static TextLayer *s_temperature2_layer; static TextLayer *text1_layer; static TextLayer *text2_layer; static TextLayer *s_time_layer, *s_date_layer; static BitmapLayer *s_icon_layer; static BitmapLayer *onpu_layer; static GBitmap *s_icon_bitmap = NULL; static GBitmap *onpu_bitmap = NULL; int counter = 0; bool isConnect = true; //バッテリー表示レイヤー static TextLayer *s_bat_layer; //Bluetoothステータス static char bt_status[4] = "O"; //過去のBluetoothステータス static int bt_st_old = 1; //バッテリーハンドラ static void battery_handler(BatteryChargeState new_state) { //バッテリー状態取得して表示(ついでにBluetooth接続状態を表示) static char s_battery_buffer[32]; snprintf(s_battery_buffer, sizeof(s_battery_buffer), "%d%%", new_state.charge_percent); //text_layer_set_text(s_bat_layer, s_battery_buffer); } //Bluetoothハンドラ void bt_handler(bool connected) { if (connected) { //接続に変化したらバイブ if(bt_st_old == 0) vibes_short_pulse(); bt_st_old = 1; snprintf(bt_status,4,"O"); isConnect = true; } else { //切断に変化したらバイブ if(bt_st_old == 1) vibes_short_pulse(); bt_st_old = 0; snprintf(bt_status,4,"-"); isConnect = false; } //現在のバッテリーを読む(BT表示) battery_handler(battery_state_service_peek()); } enum WeatherKey { WEATHER_ICON_KEY = 0x0, // TUPLE_INT WEATHER_TEMPERATURE_KEY = 0x1, // TUPLE_CSTRING WEATHER_CITY_KEY = 0x2, // TUPLE_CSTRING WEATHER_HUMID_KEY = 0x3, // TUPLE_CSTRING }; static AppSync s_sync; static uint8_t s_sync_buffer[64]; static void sync_error_callback(DictionaryResult dict_error, AppMessageResult app_message_error, void *context) { APP_LOG(APP_LOG_LEVEL_DEBUG, "App Message Sync Error: %d", app_message_error); } static void sync_tuple_changed_callback(const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) { switch (key) { case WEATHER_TEMPERATURE_KEY: printf("temp\n"); // App Sync keeps new_tuple in s_sync_buffer, so we may use it directly printf("len = %d\n",strlen(new_tuple->value->cstring)); if(strlen(new_tuple->value->cstring) > 2){ text_layer_set_text(s_temperature_layer, new_tuple->value->cstring); } break; case WEATHER_CITY_KEY: printf("temp2\n"); if(strlen(new_tuple->value->cstring) > 2){ text_layer_set_text(s_city_layer, new_tuple->value->cstring); if (onpu_bitmap) { gbitmap_destroy(onpu_bitmap); } if(atoi(new_tuple->value->cstring) > 18){ if(isConnect){ onpu_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_HEART); }else{ onpu_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_ONPU); } }else{ onpu_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_IKARI); } bitmap_layer_set_compositing_mode(onpu_layer,GCompOpSet); bitmap_layer_set_bitmap(onpu_layer, onpu_bitmap); } break; case WEATHER_HUMID_KEY: printf("humid\n"); if(strlen(new_tuple->value->cstring) > 2){ text_layer_set_text(s_temperature2_layer, new_tuple->value->cstring); } break; default : break; } } static void request_weather(void) { DictionaryIterator *iter; app_message_outbox_begin(&iter); if (!iter) { // Error creating outbound message return; } int value = 1; dict_write_int(iter, 1, &value, sizeof(int), true); dict_write_end(iter); app_message_outbox_send(); } //----------------------------------------------------------- static void update_time() { // Get a tm structure time_t temp = time(NULL); struct tm *tick_time = localtime(&temp); // Create a long-lived buffer static char buffer[] = "00:00"; // Write the current hours and minutes into the buffer if(clock_is_24h_style() == true) { // Use 24 hour format strftime(buffer, sizeof("00:00"), "%H:%M", tick_time); } else { // Use 12 hour format strftime(buffer, sizeof("00:00"), "%I:%M", tick_time); } // Display this time on the TextLayer text_layer_set_text(s_time_layer, buffer); // Copy date into buffer from tm structure static char date_buffer[16]; strftime(date_buffer, sizeof(date_buffer), "%a %d %b", tick_time); // Show the date text_layer_set_text(s_date_layer, date_buffer); } static void tick_handler(struct tm *tick_time, TimeUnits units_changed) { update_time(); counter++; if (s_icon_bitmap) { gbitmap_destroy(s_icon_bitmap); } if(counter%2 == 0){ s_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_HEADGEHOG); bitmap_layer_set_compositing_mode(s_icon_layer, GCompOpSet); bitmap_layer_set_bitmap(s_icon_layer, s_icon_bitmap); }else{ s_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_HEADGEHOG2); bitmap_layer_set_compositing_mode(s_icon_layer, GCompOpSet); bitmap_layer_set_bitmap(s_icon_layer, s_icon_bitmap); } if(counter%600 == 0){ request_weather(); counter = 0; } } //----------------------------------------------------------- static void window_load(Window *window) { Layer *window_layer = window_get_root_layer(window); GRect bounds = layer_get_bounds(window_layer); //ハリネズミの画像表示用 s_icon_layer = bitmap_layer_create(GRect(40, 20, 97, 60)); layer_add_child(window_layer, bitmap_layer_get_layer(s_icon_layer)); //ステータス画像表示用 onpu_layer = bitmap_layer_create(GRect(-50, 15, bounds.size.w, 33)); layer_add_child(window_layer, bitmap_layer_get_layer(onpu_layer)); text1_layer = text_layer_create(GRect(0, 80, bounds.size.w, 32)); text_layer_set_text_color(text1_layer, GColorWhite); text_layer_set_background_color(text1_layer, GColorClear); text_layer_set_font(text1_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14)); text_layer_set_text_alignment(text1_layer, GTextAlignmentCenter); text_layer_set_text(text1_layer, "Room Cage"); layer_add_child(window_layer, text_layer_get_layer(text1_layer)); s_temperature_layer = text_layer_create(GRect(-30, 90, bounds.size.w, 32)); text_layer_set_text_color(s_temperature_layer, GColorWhite); text_layer_set_background_color(s_temperature_layer, GColorClear); text_layer_set_font(s_temperature_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); text_layer_set_text_alignment(s_temperature_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(s_temperature_layer)); s_city_layer = text_layer_create(GRect(30, 90, bounds.size.w, 32)); text_layer_set_text_color(s_city_layer, GColorWhite); text_layer_set_background_color(s_city_layer, GColorClear); text_layer_set_font(s_city_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); text_layer_set_text_alignment(s_city_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(s_city_layer)); text2_layer = text_layer_create(GRect(0, 120, bounds.size.w, 32)); text_layer_set_text_color(text2_layer, GColorWhite); text_layer_set_background_color(text2_layer, GColorClear); text_layer_set_font(text2_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14)); text_layer_set_text_alignment(text2_layer, GTextAlignmentCenter); text_layer_set_text(text2_layer, "Humidity"); layer_add_child(window_layer, text_layer_get_layer(text2_layer)); s_temperature2_layer = text_layer_create(GRect(0, 130, bounds.size.w, 32)); text_layer_set_text_color(s_temperature2_layer, GColorWhite); text_layer_set_background_color(s_temperature2_layer, GColorClear); text_layer_set_font(s_temperature2_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); text_layer_set_text_alignment(s_temperature2_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(s_temperature2_layer)); s_bat_layer = text_layer_create(GRect(0, 0, bounds.size.w, 32)); text_layer_set_text_color(s_bat_layer, GColorWhite); text_layer_set_background_color(s_bat_layer, GColorClear); text_layer_set_font(s_bat_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14)); text_layer_set_text_alignment(s_bat_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(s_bat_layer)); //--------------------------------------------------------------- // Create time TextLayer s_time_layer = text_layer_create(GRect(0, 30, bounds.size.w, 32)); text_layer_set_background_color(s_time_layer, GColorClear); text_layer_set_text_color(s_time_layer, GColorBlack); text_layer_set_text(s_time_layer, "00:00"); text_layer_set_font(s_time_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24)); text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter); // Create date TextLayer s_date_layer = text_layer_create(GRect(0, 53, bounds.size.w, 32)); text_layer_set_text_color(s_date_layer, GColorBlack); text_layer_set_background_color(s_date_layer, GColorClear); text_layer_set_text_alignment(s_date_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(s_time_layer)); layer_add_child(window_layer, text_layer_get_layer(s_date_layer)); //--------------------------------------------------------------- Tuplet initial_values[] = { TupletCString(WEATHER_TEMPERATURE_KEY, "-\u00B0C"), TupletCString(WEATHER_CITY_KEY, "-\u00B0C"), TupletCString(WEATHER_HUMID_KEY, "-%"), TupletInteger(WEATHER_ICON_KEY, (uint8_t) 1), }; app_sync_init(&s_sync, s_sync_buffer, sizeof(s_sync_buffer), initial_values, ARRAY_LENGTH(initial_values), sync_tuple_changed_callback, sync_error_callback, NULL); //バッテリー状態サービスに登録する battery_state_service_subscribe(battery_handler); //現在のバッテリーを読む battery_handler(battery_state_service_peek()); //Bluetooth接続状態サービスに登録する connection_service_subscribe((ConnectionHandlers){ .pebble_app_connection_handler = bt_handler }); request_weather(); } static void window_unload(Window *window) { if (s_icon_bitmap) { gbitmap_destroy(s_icon_bitmap); } text_layer_destroy(s_city_layer); text_layer_destroy(s_temperature_layer); text_layer_destroy(s_temperature2_layer); text_layer_destroy(text1_layer); text_layer_destroy(text2_layer); bitmap_layer_destroy(s_icon_layer); bitmap_layer_destroy(onpu_layer); text_layer_destroy(s_time_layer); text_layer_destroy(s_date_layer); // text_layer_destory(s_bat_layer); } static void init(void) { s_main_window = window_create(); window_set_background_color(s_main_window, PBL_IF_COLOR_ELSE(GColorBlack, GColorBlack)); window_set_window_handlers(s_main_window, (WindowHandlers) { .load = window_load, .unload = window_unload }); window_stack_push(s_main_window, true); app_message_open(64, 64); // Register with TickTimerService tick_timer_service_subscribe(SECOND_UNIT, tick_handler); } static void deinit(void) { window_destroy(s_main_window); app_sync_deinit(&s_sync); } int main(void) { init(); app_event_loop(); deinit(); }
Raspberry pi 3をJSONを投げるWEBサーバーとし、スマートウォッチから情報取得を行う
前回は新しい温度センサを使えるようにしました。
室温、ハリネズミのケージ内の温度、湿度が測れるようになったので、この情報を外部から取得できるようにします。
- Raspberry piから情報を取得する
今回実装した流れとしては、スマートウォッチ(Pebble)からRaspberry piサーバーにGETリクエストを送り、サーバーはJSON形式で情報を返します。スマートウォッチではJSONをパースし情報を表示させます。
使用したプログラム言語
Raspberry piサーバー:Node.js
スマートウォッチ:c言語(画面表示部分),JavaScript(GETリクエスト送信部分)
となります。
- スマートウォッチ
前々から持っていたPebble Time Roundを使用します。プログラムがC言語で書けるので気軽に書けます。
ディスプレイはカラー電子ペーパーで他の何とかウォッチと違い常時点灯しています。値段が安いのとiPhoneとAndroidどちらでも使えるのが良い点です。
前回までに構成した温度管理システムはこのような構成になっています。
- 実行結果
先に実行結果を載せます。温度が適温の場合は音符マークが表示され、それ以外は怒っているマークが表示されます。
なかなかいい感じに仕上がりました。ハリネズミの画像はフリー素材の物を使用しました。
以下に実際に書いたコードを載せます。
- サーバー側のプログラム
//httpモジュールをインポート var http = require('http'); var url = require('url'); var qs = require('querystring'); //Webサーバーの設定 http.createServer(function (req, res) { var serialPort = require("serialport") var sp = new serialPort.SerialPort("/dev/ttyACM0", { baudrate: 9600, parser:serialPort.parsers.readline("\n") }); var count = 0; if(req.method=='GET') { res.writeHead(200, { 'Content-Type': 'application/json' }); //getリクエストの時 var url_parts = url.parse(req.url,true); console.log(url_parts.query); var data1, data2, data3; sp.on('data', function(data) { count = count + 1; if(count == 3){ data3 = String(data); res.end(JSON.stringify({ 'temp1' : data1, 'humid' : data2, 'temp2' : data3}))\ ; }else if(count < 3){ if(count == 1) data1 = String(data); if(count == 2) data2 = String(data); } }); }else{ res.writeHead(200, {'Content-Type': 'text/plain'}); sp.on('data', function(data) { count = count + 1; if(count == 3){ res.write('CaseTemp : '); res.end(String(data)); return; }else if(count < 3){ if(count == 1) res.write('RoomTemp : '); if(count == 2) res.write('CaseHumid : '); res.write(String(data)); }else{ //読み込み終了時 } }); } }).listen(8180); //ポート番号はお好みで console.log('Server running at http://localhost:8180/');
開発環境はCloud Pebbleという開発環境を使います。エミュレータもあるのでデバッグがしやすいのが特徴です。
Pebble側のソースコードは以下のようになりました。サンプルの天気取得のやつをベースに作っているので、変数名などが一部そのままです。
#include "pebble.h" static Window *s_main_window; static TextLayer *s_temperature_layer; static TextLayer *s_city_layer; static TextLayer *s_temperature2_layer; static TextLayer *text1_layer; static TextLayer *text2_layer; static BitmapLayer *s_icon_layer; static BitmapLayer *onpu_layer; static GBitmap *s_icon_bitmap = NULL; static GBitmap *onpu_bitmap = NULL; static AppSync s_sync; static uint8_t s_sync_buffer[64]; enum WeatherKey { WEATHER_ICON_KEY = 0x0, // TUPLE_INT WEATHER_TEMPERATURE_KEY = 0x1, // TUPLE_CSTRING WEATHER_CITY_KEY = 0x2, // TUPLE_CSTRING WEATHER_HUMID_KEY = 0x3, // TUPLE_CSTRING }; static const uint32_t WEATHER_ICONS[] = { RESOURCE_ID_IMAGE_SUN, // 0 RESOURCE_ID_IMAGE_CLOUD, // 1 RESOURCE_ID_IMAGE_RAIN, // 2 RESOURCE_ID_IMAGE_SNOW // 3 }; static void sync_error_callback(DictionaryResult dict_error, AppMessageResult app_message_error, void *context) { APP_LOG(APP_LOG_LEVEL_DEBUG, "App Message Sync Error: %d", app_message_error); } static void sync_tuple_changed_callback(const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) { if (s_icon_bitmap) { gbitmap_destroy(s_icon_bitmap); } s_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_HEADGEHOG); bitmap_layer_set_compositing_mode(s_icon_layer, GCompOpSet); bitmap_layer_set_bitmap(s_icon_layer, s_icon_bitmap); switch (key) { case WEATHER_TEMPERATURE_KEY: printf("temp\n"); // App Sync keeps new_tuple in s_sync_buffer, so we may use it directly text_layer_set_text(s_temperature_layer, new_tuple->value->cstring); break; case WEATHER_CITY_KEY: printf("temp2\n"); text_layer_set_text(s_city_layer, new_tuple->value->cstring); if (onpu_bitmap) { gbitmap_destroy(onpu_bitmap); } if(atoi(new_tuple->value->cstring) > 18){ onpu_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_ONPU); }else{ onpu_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_IKARI); } bitmap_layer_set_compositing_mode(onpu_layer,GCompOpAssign); bitmap_layer_set_bitmap(onpu_layer, onpu_bitmap); break; case WEATHER_HUMID_KEY: printf("humid\n"); text_layer_set_text(s_temperature2_layer, new_tuple->value->cstring); break; } } static void request_weather(void) { DictionaryIterator *iter; app_message_outbox_begin(&iter); if (!iter) { // Error creating outbound message return; } int value = 1; dict_write_int(iter, 1, &value, sizeof(int), true); dict_write_end(iter); app_message_outbox_send(); } static void window_load(Window *window) { Layer *window_layer = window_get_root_layer(window); GRect bounds = layer_get_bounds(window_layer); s_icon_layer = bitmap_layer_create(GRect(40, 20, 97, 60)); layer_add_child(window_layer, bitmap_layer_get_layer(s_icon_layer)); onpu_layer = bitmap_layer_create(GRect(-50, 15, bounds.size.w, 33)); layer_add_child(window_layer, bitmap_layer_get_layer(onpu_layer)); text1_layer = text_layer_create(GRect(0, 80, bounds.size.w, 32)); text_layer_set_text_color(text1_layer, GColorWhite); text_layer_set_background_color(text1_layer, GColorClear); text_layer_set_font(text1_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14)); text_layer_set_text_alignment(text1_layer, GTextAlignmentCenter); text_layer_set_text(text1_layer, "Room Cage"); layer_add_child(window_layer, text_layer_get_layer(text1_layer)); s_temperature_layer = text_layer_create(GRect(-30, 90, bounds.size.w, 32)); text_layer_set_text_color(s_temperature_layer, GColorWhite); text_layer_set_background_color(s_temperature_layer, GColorClear); text_layer_set_font(s_temperature_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); text_layer_set_text_alignment(s_temperature_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(s_temperature_layer)); s_city_layer = text_layer_create(GRect(30, 90, bounds.size.w, 32)); text_layer_set_text_color(s_city_layer, GColorWhite); text_layer_set_background_color(s_city_layer, GColorClear); text_layer_set_font(s_city_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); text_layer_set_text_alignment(s_city_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(s_city_layer)); text2_layer = text_layer_create(GRect(0, 120, bounds.size.w, 32)); text_layer_set_text_color(text2_layer, GColorWhite); text_layer_set_background_color(text2_layer, GColorClear); text_layer_set_font(text2_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14)); text_layer_set_text_alignment(text2_layer, GTextAlignmentCenter); text_layer_set_text(text2_layer, "Humidity"); layer_add_child(window_layer, text_layer_get_layer(text2_layer)); s_temperature2_layer = text_layer_create(GRect(0, 130, bounds.size.w, 32)); text_layer_set_text_color(s_temperature2_layer, GColorWhite); text_layer_set_background_color(s_temperature2_layer, GColorClear); text_layer_set_font(s_temperature2_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); text_layer_set_text_alignment(s_temperature2_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(s_temperature2_layer)); Tuplet initial_values[] = { TupletCString(WEATHER_TEMPERATURE_KEY, "室温\u00B0C"), TupletCString(WEATHER_CITY_KEY, "温度\u00B0C"), TupletCString(WEATHER_HUMID_KEY, "湿度%"), TupletInteger(WEATHER_ICON_KEY, (uint8_t) 1), }; app_sync_init(&s_sync, s_sync_buffer, sizeof(s_sync_buffer), initial_values, ARRAY_LENGTH(initial_values), sync_tuple_changed_callback, sync_error_callback, NULL); request_weather(); } static void window_unload(Window *window) { if (s_icon_bitmap) { gbitmap_destroy(s_icon_bitmap); } text_layer_destroy(s_city_layer); text_layer_destroy(s_temperature_layer); text_layer_destroy(s_temperature2_layer); text_layer_destroy(text1_layer); text_layer_destroy(text2_layer); bitmap_layer_destroy(s_icon_layer); bitmap_layer_destroy(onpu_layer); } static void init(void) { s_main_window = window_create(); window_set_background_color(s_main_window, PBL_IF_COLOR_ELSE(GColorBlack, GColorBlack)); window_set_window_handlers(s_main_window, (WindowHandlers) { .load = window_load, .unload = window_unload }); window_stack_push(s_main_window, true); app_message_open(64, 64); } static void deinit(void) { window_destroy(s_main_window); app_sync_deinit(&s_sync); } int main(void) { init(); app_event_loop(); deinit(); }
function iconFromWeatherId(weatherId) { if (weatherId < 600) { return 2; } else if (weatherId < 700) { return 3; } else if (weatherId > 800) { return 1; } else { return 0; } } function fetchWeather() { var req = new XMLHttpRequest(); req.open('GET', 'http://******', true); req.onload = function () { if (req.readyState === 4) { if (req.status === 200) { console.log(req.responseText); var response = JSON.parse(req.responseText); var temp1 = Math.round(response.temp1); var key = iconFromWeatherId(response.humid); var temp2 = Math.round(response.temp2); var humid = Math.round(response.humid); console.log(temp1); console.log(key); console.log(temp2); console.log(humid); Pebble.sendAppMessage({ 'WEATHER_ICON_KEY': key, 'WEATHER_TEMPERATURE_KEY': temp1 + '\xB0C', 'WEATHER_CITY_KEY': temp2 + '\xB0C', 'WEATHER_HUMID_KEY':humid + '%' }); } else { console.log('Error'); } } }; req.send(null); } Pebble.addEventListener('ready', function (e) { console.log('connect!' + e.ready); fetchWeather(); console.log(e.type); }); Pebble.addEventListener('appmessage', function (e) { fetchWeather(); console.log(e.type); console.log(e.payload.temperature); console.log('message!'); }); Pebble.addEventListener('webviewclosed', function (e) { console.log('webview closed'); console.log(e.type); console.log(e.response); });
次はリモートでエアコンONなど温度調節できる方法がないか調べてみます。
温湿度センサをArduinoで使ってみる
Amazonで300円くらいで売られていたDHT11という温湿度センサが届いたので使用してみます。
見た目はこんな感じで、メスメスのワイヤーとセットで送られてきました。
説明書などは無しです。調べてみるとディジタル出力で温度と湿度を取得できるみたいです。
便利なライブラリがあり、簡単に使用できそうでしたので試してみました。
ライブラリは以下のGithubからダウンロードします。
GitHub - adafruit/DHT-sensor-library: Arduino library for DHT11DHT22, etc Temp & Humidity Sensors
ダウンロードしてきたzipファイルは解凍せずにそのままArduinoのIDEからzip形式のライブラリをインストールで先ほどダウンロードしてきたファイルを選択するとOKです。
コードはこちらのサイトを参考にしました。
ArduinoでDHT11デジタル温度センサーをAdafruitライブラリから使う - Qiita
#include <DHT.h> #define DHTPIN 4 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); float a_in; // アナログ入力値(0〜203) float temp_c = 0; // 摂氏値( ℃ ) int red_led = 2; int blue_led = 3; void setup(){ Serial.begin(9600); // シリアル通信速度 pinMode ( red_led, OUTPUT); pinMode ( blue_led, OUTPUT); dht.begin(); } void loop(){ // アナログピンから計測値を取得(0〜203) a_in = analogRead(0); // 入力値を摂氏に換算 temp_c = ((5 * a_in) / 1024) * 100; // 改行しながら出力 Serial.print("Temperature: "); Serial.print( temp_c ); Serial.println(" *C "); float h = dht.readHumidity(); float t = dht.readTemperature(); if (isnan(h) || isnan(t)) { Serial.println("Failed to read from DHT sensor!"); return; } Serial.print("Humidity: "); Serial.print(h); Serial.println(" %\t"); Serial.print("Inside Temperature: "); Serial.print(t); Serial.println(" *C "); if(t > 20 && t < 30){ // LEDを点灯 ( HIGHは電圧レベル ) digitalWrite( blue_led, HIGH ); digitalWrite( red_led, LOW ); }else{ digitalWrite( red_led, HIGH ); digitalWrite( blue_led, LOW ); } // 1000ms待機 delay(10000); }
出力結果
こんな感じですぐにセンサの値が取得できました。
ライブラリが用意してあるので簡単ですね。このセンサはハリネズミのケージ内の温湿度を測るために使おうと思います。
Raspberry piで温度管理システムをバックグラウンドで起動できるようにする
前回の記事でRaspberry piの温度管理システムから、メールが送信できるように変更しました。今回は、前回のPythonのプログラムをバックグラウンドで起動させておく方法を紹介します。
- Daemon(デーモン)
Linuxで何かサービスをバックグラウンドで起動させておくにはデーモンという名前の物を使えばいいみたいです。
Pythonのプログラムをデーモン化するには、python-daemonを使いました。
$sudo apt-get install python-daemon
でインストールしてください。
実際に作成したプログラムを以下に載せます。
#!/usr/bin/env python # -*- coding: utf-8 -*- import serial import time import os.path import datetime import smtplib from email import Encoders from email.Utils import formatdate from email.MIMEBase import MIMEBase from email.MIMEMultipart import MIMEMultipart from email.MIMEText import MIMEText import daemon.runner #Google account ADDRESS = "送信元のアカウント" PASSWARD = "パスワード" #SMTP Server SMTP = "smtp.gmail.com" PORT = 587 def create_message(from_addr, to_addr, subject, body): msg = MIMEMultipart() msg["From"] = from_addr msg["To"] = to_addr msg["Date"] = formatdate() msg["Subject"] = subject body = MIMEText(body) msg.attach(body) return msg def send(from_addr, to_addrs, msg): smtpobj = smtplib.SMTP(SMTP, PORT) smtpobj.ehlo() smtpobj.starttls() smtpobj.ehlo() smtpobj.login(ADDRESS, PASSWARD) smtpobj.sendmail(from_addr, to_addrs, msg.as_string()) smtpobj.close() class TempDaemon: def __init__(self): self.stdin_path = self.stdout_path = self.stderr_path = '/dev/null' self.pidfile_timeout = 10 self.directory = os.path.expanduser('~/.test/') os.mkdir(self.directory) self.pidfile_path = os.path.join(self.directory, 'test.pid') def run(self): count = 0; ser = serial.Serial('/dev/ttyACM0', 9600) time.sleep(3) while True: count = count + 1 time.sleep(1) data = ser.readline() if count > 1: data = float(data) to_addr = "送信先アドレス" if data < 18 or data > 30 : #subject and body subject = "温度警告" body = "現在の温度 " + str(data) + "度です。\n温度調節して下さい。" #create message msg = create_message(ADDRESS, to_addr, subject, body) #send send(ADDRESS, [to_addr], msg) elif count%1440 == 0: #4時間ごとに定期報告 subject = "ハリネズミ管理システム" body = time.ctime()+"\n現在の温度 " + str(data) + "度です。" #create message msg = create_message(ADDRESS, to_addr, subject, body) #send send(ADDRESS, [to_addr], msg) def main(): daemon_runner = daemon.runner.DaemonRunner(TempDaemon()) daemon_runner.do_action() if __name__ == '__main__': main()
このプログラムでは、温度が18度を下回るか、30度より高くなった場合にあらかじめ設定しておいたメールアドレスに警告メールを送ります。
また、警告メール以外に4時間おきに温度をメールで報告してくれます。
上記コードの以下の部分がデーモンの本体です。
class TempDaemon: def __init__(self): self.stdin_path = self.stdout_path = self.stderr_path = '/dev/null' self.pidfile_timeout = 10 self.directory = os.path.expanduser('~/.test/') os.mkdir(self.directory) self.pidfile_path = os.path.join(self.directory, 'test.pid') def run(self): while True: #何か処理を記述
バックグラウンドで起動させるには
./temp.py start
終了させるには
./temp.py stop
を入力すればオッケーです。
これでとりあえずバックグラウンドでの起動が可能となりました。
Raspberry pi 3とArduinoの連携によるハリネズミ温度管理システムの改良Ver1.1
前回はArduinoを用いた温度の取得とLEDによる警告を行うシステムを作りました。
今回はRaspberry piとシリアル通信によりデータを共有し、もう少し改良を行います。
- やる事
前回のArduino側のソースコードを少し変更しました。
↓変更部分
void loop(){ // アナログピンから計測値を取得(0〜203) a_in = analogRead(0); // 入力値を摂氏に換算 temp_c = ((5 * a_in) / 1024) * 100; // 改行しながら出力 Serial.println( temp_c );
シリアル通信でデータを投げる部分は温度だけ投げるように変更しました。
これでRaspberry pi とUSB接続してRaspberry pi 側で取得のプログラムを書けば温度が取得できるはずです。
- Raspberry pi側のソースコード
こちらはPythonを用いてプログラムを書いていきます。
シリアル通信によるデータ取得とメール送信用のプログラムは以下のようになります。
# -*- coding: utf-8 -*- import serial import time import os.path import datetime import smtplib from email import Encoders from email.Utils import formatdate from email.MIMEBase import MIMEBase from email.MIMEMultipart import MIMEMultipart from email.MIMEText import MIMEText #Google account ADDRESS = "送信用のアドレス" PASSWARD = "パスワード" #SMTP Server SMTP = "smtp.gmail.com" PORT = 587 def create_message(from_addr, to_addr, subject, body): msg = MIMEMultipart() msg["From"] = from_addr msg["To"] = to_addr msg["Date"] = formatdate() msg["Subject"] = subject body = MIMEText(body) msg.attach(body) return msg def send(from_addr, to_addrs, msg): smtpobj = smtplib.SMTP(SMTP, PORT) smtpobj.ehlo() smtpobj.starttls() smtpobj.ehlo() smtpobj.login(ADDRESS, PASSWARD) smtpobj.sendmail(from_addr, to_addrs, msg.as_string()) smtpobj.close() if __name__ == '__main__': count = 0; ser = serial.Serial('/dev/ttyACM0', 9600) print(ser.portstr) time.sleep(3) while 1: time.sleep(1) data = ser.readline() if count > 0: data = float(data) print(time.ctime() + ', ' + str(data)) if data < 20 : #adress to_addr = "送信先のアドレス" #subject and body subject = "温度警告" body = "現在の温度 " + str(data) + "度です。\n温度調節して下さい。" #create message msg = create_message(ADDRESS, to_addr, subject, body) #send send(ADDRESS, [to_addr], msg) print('send aleart message.\n') count = count + 1
- 実行結果
計測した温度が20度を下回った際に警告のメールが送信されるようになりました。
実際に送られてきたメール
- まとめ
ArduinoとRaspberry piを連携させたシステムを構築した。
Arduinoではセンサーの管理、Raspberry piではメール送信などの仕事をさせた。
何か他に良いアイディアが思いついたら随時追加していきます。
ハリネズミ管理のためのArduinoを使った温度管理システムVer1.0
前回、ハリネズミを見守るためのRaspberry pi 3を用いたネットワークカメラの構築を行いました。こんな感じでiPhoneからも見れます。
今回は、家で眠っていたArduinoを掘り出してきて、ちょっと賢い温度計を作ります。
- 必要な物
Arduino uno
温度センサ(LM35)
LED(赤と青二つ)
抵抗
ワイヤ
フレットボード
USBケーブルなど
以下のセットで全て手に入ります。
- なぜArduinoを使ったのか
家で眠っていて何か使えないかという事が一点と、LM35という温度センサーをraspberry piで使用するためにはAD変換しないと使用できないのがもう一点。
Arduinoはディジタルもアナログもいける子なのでこいつを使います。
配線はこんな感じで行いました。
LM35の温度センサの配線はこんな感じです。
LEDは青と赤を使用し、一定間隔毎に計測した温度を元に、温度が20度より高く30度より低い時はちょうど良い温度なので青色のLEDを点灯させ、それ以外の時は寒すぎるか暑すぎるときなので、赤色のLEDを点灯させます。
ArduinoはC言語で書いたソースで制御できるので、手軽に使用できます。
今回のソースコードは以下のようになります。
float a_in; // アナログ入力値(0〜203)
float temp_c = 0; // 摂氏値( ℃ )
int red_led = 2;
int blue_led = 3;
void setup(){
Serial.begin(9600); // シリアル通信速度
pinMode ( red_led, OUTPUT);
pinMode ( blue_led, OUTPUT);
}
void loop(){
// アナログピンから計測値を取得(0〜203)
a_in = analogRead(0);
// 入力値を摂氏に換算
temp_c = ((5 * a_in) / 1024) * 100;
// 出力
Serial.print( temp_c );
Serial.println('C');
if(temp_c > 20 && temp_c < 30){
// 青色LEDを点灯 ( HIGHは電圧レベル )
digitalWrite( blue_led, HIGH );
digitalWrite( red_led, LOW );
}else{
// 赤色LEDを点灯 ( HIGHは電圧レベル )
digitalWrite( red_led, HIGH );
digitalWrite( blue_led, LOW );
}
// 10s待機
delay(10000);
}
- 全体図
ArduinoはRaspberry piから給電を行い、動作させます。
後々、部品や環境が整ったらシリアル通信をさせて何かリッチな表示をさせたいと思います。