激安マイコン基板で温度・湿度センサーの値を読み取ってみた
ストロベリーリナックスで購入できるMSP430 LaunchPadと中国製らしい温度・湿度センサーDHT22を使ってみた。
- http://strawberry-linux.com/catalog/items?code=18141
- http://strawberry-linux.com/catalog/items?code=18158
DHT22からセンサーの値を読み取るには1本の信号だけを使って行う。この1本線のプロトコルは、モールス信号みたいに信号のHigh期間の長さで0/1を表現するので、カウンタで長さを数えてやればいい。
開発環境のデバッガを走らせてみると、確かにデータを受信できている。(MSP430のスピードが遅いためか、for文で回すとうまく受信できなかったのでベタ書きするようにした)
一度に受信するデータは5バイトで、上から順に
- 湿度のHigh Byte
- 湿度のLow Byte
- 温度のHigh Byte
- 温度のLow Byte
- CheckSum(1〜4を足した値)
となっている。
ちなみに、このときの湿度と温度は、
- 湿度=0x01CE=462=46.2%
- 温度=0x00DE=222=22.2℃
というそれっぽい値だった。
もちろんCheckSumもOK。
//****************************************************************************** // MSP430x2xx Demo - Software Toggle P1.0 // // Description; Toggle P1.0 by xor'ing P1.0 inside of a software loop. // ACLK = n/a, MCLK = SMCLK = default DCO // // MSP430x2xx // ----------------- // /|\| XIN|- // | | | // --|RST XOUT|- // | | // | P1.0|-->LED // // A. Dannenberg // Texas Instruments, Inc // January 2006 // Built with IAR Embedded Workbench Version: 3.40A //****************************************************************************** #include "msp430.h" int main(void) { volatile unsigned char data[5]; volatile unsigned char sum; volatile unsigned int i; char cnt0, cnt1; WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer P1DIR = 0x01; // Set P1.0 to output direction // 0: input, 1: output P1OUT = 0x00; for (;;) { // output start signal to P1.4 P1DIR |= 0x10; // delay i = 1000; do { i--; } while (i != 0); P1DIR &= ~0x10; while ((P1IN & 0x10) != 0); // wait DHT response while ((P1IN & 0x10) == 0); // response while ((P1IN & 0x10) != 0); // sense #define RECV(d) \ cnt0 = 0; \ do { cnt0++; } while ((P1IN & 0x10) == 0); \ cnt1 = 0; \ do { cnt1++; } while ((P1IN & 0x10) != 0); \ data[d] = data[d] << 1; \ if (cnt0 < cnt1) { \ data[d] += 1; \ } // 1st byte RECV(0); RECV(0); RECV(0); RECV(0); RECV(0); RECV(0); RECV(0); RECV(0); // 2nd byte RECV(1); RECV(1); RECV(1); RECV(1); RECV(1); RECV(1); RECV(1); RECV(1); // 3rd byte RECV(2); RECV(2); RECV(2); RECV(2); RECV(2); RECV(2); RECV(2); RECV(2); // 4th byte RECV(3); RECV(3); RECV(3); RECV(3); RECV(3); RECV(3); RECV(3); RECV(3); // 5th byte RECV(4); RECV(4); RECV(4); RECV(4); RECV(4); RECV(4); RECV(4); RECV(4); // checksum sum = 0; sum += data[0]; sum += data[1]; sum += data[2]; sum += data[3]; // compare if (sum == data[4]) P1OUT |= 0x01; // OK else P1OUT &= ~0x00; // NG } }
連番を挿入するEmacs Lispを書いてみた
矩形領域コマンドの要領で領域を指定して連番を挿入できるEmacs Lispを書いてみた。Emacs Lispを書くのは初めてだけどちゃんと動いているっぽい。初期値の設定時にフォーマットを指定できる(たぶん)。便利。
- Start number: 0 を入力したとき → 初期値: 0, フォーマット: %01d
- Start number: 03h を入力したとき → 初期値: 3, フォーマット: %02x
- Start number: A0A0H を入力したとき → 初期値: A0A0, フォーマット: %04X
(defun insert-seq (pstart pend) "Insert sequential numbers to rectangle region." (interactive "r") (save-excursion (let ((cstart (progn (goto-char pstart) (current-column))) (cend (progn (goto-char pend) (current-column))) (lstart (progn (goto-char pstart) (count-lines (point-min) (point)))) (lend (progn (goto-char pend) (count-lines (point-min) (point)))) (fmt (read-from-minibuffer "Start number: " "0"))) (setq fmt_last (string (elt fmt (1- (length fmt))))) (setq radix (if (equal fmt_last "H") "X" (if (equal fmt_last "h") "x" "d"))) (setq len (if (equal radix "d") (length fmt) (1- (length fmt)))) (setq j (string-to-number fmt (if (equal radix "d") 10 16))) (setq i lstart) (while (<= i lend) (let ((s (progn (goto-line i) (move-to-column cstart) (point))) (e (progn (goto-line i) (move-to-column cend) (point)))) (kill-region s e) (goto-char s) ;; (while (< (current-column) cstart) ;; (insert " ")) ;; (insert (format (format "%s0%d%s" "%" len radix) j)) ;; (setq j (1+ j)) (if (= (current-column) cstart) (progn (insert (format (format "%s0%d%s" "%" len radix) j)) (setq j (1+ j)))) (setq i (1+ i)) )))))
LAN内のマシンにホスト名でアクセスする
UbuntuでCaps LockをCtrlにする方法のメモ
~/.xmodmaprcを書き換えなくても簡単に設定できる。
システム→設定→キーボード→レイアウト→オプション→Ctrlキーの位置→Make CapsLock as an additional Ctrl
JapaninoでのADCについて
前の記事でJapaninoと温度センサを使って温度計を作ったが、温度がやけに高めになってしまう問題があったのでちょっと調べてみた。
Arduinoでアナログ信号をADC(アナログデジタル変換)する際に使用できる参照電圧には以下の3つがある。
- DEFAULT: 電源電圧(5V、正確に言うとAVCCピン電圧)
- INTERNAL: 内部で作られる1.1V
- EXTERNAL: AREFピン電圧
ADCの出力はこれらの参照電圧を1024としたときの10bitの値で得られる。USBから供給される5V電源はそれなりの精度で安定しているだろうと考え、上記温度計ではDEFAULTの5Vを用いてADCを行うように設定していた。ところが、Arduinoに供給されている電源の電圧を測ってみると4.75Vしかないことがわかった。つまり、参照電圧が期待していた値よりも低かったため、ADCの結果が高めに出ていたということだ。Japanino基板上の電圧を調べてみたところ、どうやら犯人は電流の逆流防止用と思われるダイオードのようだ。このダイオードの入力と出力で電圧が5.05Vから4.75Vに降下していることがわかった。
対策として、参照電圧が4.75Vであるとして温度を計算するように式を修正した。これにより実際の温度にかなり近い値が表示されるようになった。
void loop() { int num = 20; long sum = 0; for (int i = 0; i < num; i++) { sum += analogRead(analogPin0); delay(5); } int temp = (int) (sum * 4750 / num / 1024 - 600); lcd.clear(); lcd.print("Temp:"); lcd.print(temp/10, DEC); lcd.print("."); lcd.print(temp%10, DEC); if (serial_gets(buffer, 16) > 0) { if (strcmp(buffer, "temp?") == 0) { Serial.println(sum, DEC); } } }
デジタル温度計を作ってみる
温度センサとLCDをArduinoにつないで温度計を作ってみた。
こんなに簡単にLCDを扱えるとは本当に驚きだ。
温度センサにはLM61CIZというICを使った。
ちょっと温度高めになってしまうけどこんなもの?
#include <LiquidCrystal.h> LiquidCrystal lcd = LiquidCrystal(11, 12, 2, 3, 4, 5); // RS,EN,D4,D5,D6,D7 int ledPin = 13; // LED connected to digital pin 13 int analogPin0 = 0; void setup() { analogReference(DEFAULT); pinMode(ledPin, OUTPUT); Serial.begin(9600); } int serial_gets(char *buffer, int size) { if (!Serial.available()) return 0; int i = 0; while (i < size-1) { if (Serial.available()) { buffer[i++] = Serial.read(); if (buffer[i-1] == '\r') { i--; } if (buffer[i-1] == '\n') { i--; break; } } else { delay(1); } } buffer[i] = '\0'; return i; } void loop() { int sum = 0; for (int i = 0; i < 50; i++) { sum += analogRead(analogPin0); delay(4); } int temp = (sum - 6144) * 5 / 32; temp = temp * 20 / 32; lcd.clear(); lcd.print("Temp:"); lcd.print(temp/10, DEC); lcd.print("."); lcd.print(temp%10, DEC); char buffer[16]; if (serial_gets(buffer, 16) > 0) { if (strcmp(buffer, "temp?") == 0) { Serial.println(sum, DEC); } } }
Japaninoとシリアル通信してみる
PCからシリアル経由でJapaninoのLEDを制御してみた。
int ledPin = 13; // LED connected to digital pin 13 void setup() { pinMode(ledPin, OUTPUT); Serial.begin(9600); } int serial_gets(char *buffer, int size) { if (!Serial.available()) return 0; int i = 0; while (i < size-1) { if (Serial.available()) { buffer[i++] = Serial.read(); if (buffer[i-1] == '\r') { i--; } if (buffer[i-1] == '\n') { i--; break; } } else { delay(1); } } buffer[i] = '\0'; return i; } void loop() { char buffer[16]; if (serial_gets(buffer, 16) > 0) { if (strcmp(buffer, "led:on") == 0) { digitalWrite(ledPin, HIGH); } else if (strcmp(buffer, "led:off") == 0) { digitalWrite(ledPin, LOW); } else if (strcmp(buffer, "led?") == 0) { Serial.println(digitalRead(ledPin), DEC); } } }
serial_getsでPCから送られてくる文字列を取得している。取得した文字列をstrcmpで比較して、"led:on"ならばLEDを点灯、"led:off"ならばLEDを消灯、"led?"ならばLEDの出力状態を取得して送信するようにしている。
PC側のプログラムはrubyで書いてみた。シリアル通信にはruby-serialportを使うのでインストールしておく。
sudo gem install ruby-serialport
#!/usr/bin/env ruby require 'rubygems' require 'serialport' sp = SerialPort.new('/dev/ttyUSB0', 9600, 8, 1, SerialPort::NONE) sleep 2 def serial_gets(sp) str = "" while true do c = sp.getc unless c.nil? str << c break if c.chr == "\n" end end str end begin while true sp.puts "led:on" sleep 1 sp.puts "led:off" sleep 1 sp.puts "led?" puts serial_gets(sp) end rescue Interrupt sp.close puts end



