Arduinoで遊ぼう - 大容量EEPROMに毎日の温度変化を保存する

Arduinoに搭載されているマイコン(AVR)には電源を切ってもデータが消えないメモリ「EEPROM」が内蔵されている。Duemilanove 168は512バイト、Duemilanove 328は1024バイト、MEGAは4096バイトのEEPROMを内蔵している。
少しだけ情報を保存する場合には内蔵のEEPROMで十分だけど、温度センサの値を数ヶ月に渡って保存したり、インターネットからダウンロードしたファイルを保存するには容量が足りない。そんな時は大容量のEEPROMをArduinoに接続してもりもりデータを保存しよう!今回、I2CインターフェースのEEPROMとCOMS温度センサ「S8100B」を使った簡単な温度ロガーの作り方を紹介するよ。
EEPROMを選ぶ
秋月電子でいろいろなサイズのEEPROMが買える。用途に合わせて最適なEEPROMを選択しよう。今回は512Kbit(64Kbytes)の24LC512を選んだ。
- マイクロチップ 24LC64 64Kbit( 8Kbytes) 60円
- マイクロチップ 24LC256 256Kbit( 32Kbytes) 120円
- マイクロチップ 24LC512 512Kbit( 64Kbytes) 200円
- マイクロチップ 24LC1025 1Mbit(128Kbytes) 450円
温度センサ
温度センサには秋月で買ったS8100Bを使った。S8100Bは-20℃から+80℃までの温度を測定できる温度センサICだ。トランジスタみたいな形をしていて、ラベルを正面にして左からVOUT、GND、VDDとなっている。VDDに5Vを接続してやれば、VOUTから温度に応じた電圧が出力される。1℃温度が上がると約-8mV電圧が下がる、右肩下がりの特性になっている。S8100Bの温度と出力電圧の特性をグラフ化した。

(S8100B 温度-出力電圧 特性)
Arduinoと接続する

(図の555はIC2 EEPROM、NはS8100B)
ArduinoとEEPROMの接続にはアナログ端子の4番と5番を使う。4番にEEPROMのSDAを接続、5番にEEPROMのSCLを接続する。SDA、SCLは1KΩの抵抗でプルアップする。温度センサS8100BのVOUTをアナログ端子の0番に接続する。VOUTは1MΩの抵抗でプルアップする。
ライブラリをダウンロードする
サンプルスケッチを動かすには次の3つのライブラリが必要です。ライブラリをダウンロードしてSKETCHBOOK/librariesフォルダにコピーしてください。
TwEEPROM - I2CEEPROM読み書き用ライブラリ
http://arms22.googlecode.com/files/TwEEPROM001.zip
S8100B - 温度センサ用ライブラリ
http://arms22.googlecode.com/files/S8100B002.zip
Sleep - AVRスリープ用ライブラリ
http://arms22.googlecode.com/files/Sleep003.zip
サンプルスケッチ
スケッチは1秒間隔で温度センサから温度を読み取ってEEPROMに書き込みます。EEPROMに書き込まれた温度データはArduino IDEのシリアルモニタから読み出せます。スケッチは次のコマンドに対応しています。
'd' EEPROMに書き込まれた全温度データを出力します。
'r' EEPROMの内容をクリアします。
'm' モニタ機能をオン・オフします。
'g' Google Chart APIでグラフ表示可能なURLを出力します。

(カップヌードルの上において温度を測定した結果)
#include <Sleep.h>
#include <Wire.h>
#include <TwEEPROM.h>
#include <S8100B.h>
#define LOG_STR_ADDR (0x500000) // device addr + memory addr
#define LOG_MEM_SIZE (0x10000)
#define LOG_ENT_SIZE (2)
#define LOG_SIG_ADDR (LOG_STR_ADDR+LOG_MEM_SIZE-4)
#define LOG_READ_PEROPD (1000)
Temperature myTemp(0);
void setup()
{
Wire.begin();
Serial.begin(115200);
#define LED_PIN 13
pinMode(LED_PIN,OUTPUT);
Logger_init();
}
void loop()
{
static bool moni = false;
static uint32_t last = 0;
uint32_t now;
if(Serial.available()){
int c = Serial.read();
if(c == 'd'){
Logger_printAllTemp();
}
else if(c == 'r'){
Logger_clear();
}
else if(c == 'g'){
Logger_printGoogleChartURL();
}
else if(c == 'm'){
moni = !moni;
if(moni){
Serial.println("moni on");
}
else{
Serial.println("moni off");
}
}
}
Sleep.idle();
now = millis();
if((now - last) > LOG_READ_PEROPD){
digitalWrite(LED_PIN,HIGH);
int temp = myTemp.read() * 10;
Logger_writeTemp(temp);
if(moni){
print_temp(temp);
Serial.println();
}
digitalWrite(LED_PIN,LOW);
last = now;
}
}
void print_temp(int temp)
{
Serial.print(temp/10,DEC);
Serial.print(".");
Serial.print(abs(temp%10),DEC);
}
uint32_t Logger_wtptr;
void Logger_init(void)
{
uint8_t sig[4];
Serial.print("setup...");
sig[0] = TwEEPROM.read(LOG_SIG_ADDR+0);
sig[1] = TwEEPROM.read(LOG_SIG_ADDR+1);
sig[2] = TwEEPROM.read(LOG_SIG_ADDR+2);
sig[3] = TwEEPROM.read(LOG_SIG_ADDR+3);
if(sig[0] == 't' && sig[1] == 'e' && sig[2] == 'm' && sig[3] == 'p' ){
Logger_wtptr = LOG_STR_ADDR;
for(uint32_t rdptr = LOG_STR_ADDR;rdptrint temp;
temp = TwEEPROM.read(rdptr+0) << 8;
temp |= TwEEPROM.read(rdptr+1);
if(temp == 0x7fff){
Logger_wtptr = rdptr;
break;
}
}
}
else{
Logger_clear();
}
Serial.println("done");
}
void Logger_writeTemp(int temp)
{
uint32_t next = Logger_wtptr + LOG_ENT_SIZE;
if(next >= LOG_SIG_ADDR){
next = LOG_STR_ADDR;
}
TwEEPROM.write(next+0, 0x7f);
TwEEPROM.write(next+1, 0xff);
TwEEPROM.write(Logger_wtptr+0, temp >> 8);
TwEEPROM.write(Logger_wtptr+1, temp & 0xff);
Logger_wtptr = next;
}
void Logger_printAllTemp(void)
{
Serial.println("print log begin");
uint32_t rdptr = Logger_wtptr - LOG_ENT_SIZE;
do {
if(rdptr < LOG_STR_ADDR) {
rdptr = LOG_SIG_ADDR - LOG_ENT_SIZE;
}
int temp;
temp = TwEEPROM.read(rdptr+0) << 8;
temp |= TwEEPROM.read(rdptr+1);
if(temp == 0x7fff)
break;
print_temp(temp);
Serial.println();
rdptr -= LOG_ENT_SIZE;
}
while(1);
Serial.println("print log end");
}
void Logger_printGoogleChartURL(void)
{
Serial.println("---- Google Chart URL ----");
Serial.print("http://chart.apis.google.com/chart?"
"cht=lc&"
"chds=-10,40&"
"chs=720x360&"
"chxt=y&"
"chxl=0:|-10|0|10|20|30|40|&"
"chxp=0,-10,0,10,20,30,40&"
"chxr=0,-10,40&"
"chg=5,5,1,5&"
"chd=t:");
#define NUMBER_OF_TEMP 360
#define TEMP_STEP 1
uint32_t rdptr = (Logger_wtptr - LOG_ENT_SIZE) - (NUMBER_OF_TEMP * (LOG_ENT_SIZE * TEMP_STEP));
if(rdptr < LOG_STR_ADDR){
rdptr = LOG_SIG_ADDR - (LOG_STR_ADDR - rdptr);
}
int count = 0;
do{
int temp;
temp = TwEEPROM.read(rdptr+0) << 8;
temp |= TwEEPROM.read(rdptr+1);
print_temp(temp);
rdptr += LOG_ENT_SIZE * TEMP_STEP;
if(rdptr >= LOG_SIG_ADDR)
rdptr = LOG_STR_ADDR + (rdptr - LOG_SIG_ADDR);
if(count < NUMBER_OF_TEMP){
Serial.print(",");
count++;
}
else{
break;
}
}
while(1);
Serial.println();
}
void Logger_clear(void)
{
Serial.print("clear eeprom...");
TwEEPROM.write(LOG_SIG_ADDR+0,'t');
TwEEPROM.write(LOG_SIG_ADDR+1,'e');
TwEEPROM.write(LOG_SIG_ADDR+2,'m');
TwEEPROM.write(LOG_SIG_ADDR+3,'p');
TwEEPROM.write(LOG_STR_ADDR+0,0x7f); // end mark1
TwEEPROM.write(LOG_STR_ADDR+1,0xff); // end mark2
TwEEPROM.write(LOG_SIG_ADDR-2,0x7f); // end mark dammy1
TwEEPROM.write(LOG_SIG_ADDR-1,0xff); // end mark dammy2
Logger_wtptr = LOG_STR_ADDR;
Serial.println("done");
}
Arduinoをはじめよう
posted with amazlet at 09.10.27
Massimo Banzi
オライリージャパン
売り上げランキング: 3641
オライリージャパン
売り上げランキング: 3641
