試作 家庭菜園で使う温湿度ロガー
作った目的
家庭菜園で野菜を栽培しており、温湿度などを測定してトレンドを掴もうと思いましたが通信環境を整えるのに費用かかるため リアルタイムではなくても情報が取れれば良いので、なるべく省電力で測定できてデータを見ればどこで測定したのかわかれば良い(緯度経度があるので)と考えたロガーです。
試作と書いた理由
EEPROMからデータを読み出すのにイチゴジャムPCのLOADとLISTが使えればわざわざ読み出しプログムを作る必要ないと考えプログラムをつくっておりましたがどうしてもLISTコマンドで読み出す事が出来ずコンテストに間に合わなかったのですべてASCIIコードとして保存するバージョンになっております。
使い方
電池を接続するとすぐ起動します。
LED0が点灯している間は緯度経度情報取得中で取得完了すると消灯します
温湿度情報は3分毎に取得しI2CEEPROMに記録します。
外部から動作している事を確認する為30秒LED4点灯後2分30秒消灯を繰り返します。
EEPROMからデータの取り出しとASCIIコード化するプログラムは未作成でした。
保存データフォーマット
1 | 2 | 3 | 4 | 5 | 6 | 7 | |||
---|---|---|---|---|---|---|---|---|---|
一行目 | 行番号 | 文字数 | 年 | 月 | 日 | 緯度 | 経度 | 偶数調整 | 行の終了 |
一行目出力例 | 10 | 18 | 2025 | 1 | 22 | XX.XX | YYY.YY | 0 | \n |
二行目以降繰り返し | 行番号 | 文字数 | 時 | 分 | 温度 | 湿度 | 気圧 | 地中温度 | 行の終了 |
二行目出力例 | 11 | 19 | 7 | 34 | 23.8 | 67 | 1004.2 | 10.5 | \n |
※すべてASCIIコードデータとして保存して、1行当たりのバイト数が偶数になるよう調整しています。
※行の終了にLFをつけました
出力例
HEXコード
31 30 2C 32 33 2C 32 30 32 35 2C 31 2C 32 34 2C 58 58 2E 58 58 2C 59 59 59 2C 59 59 30 0A 0A 31 31 2C 32 34 3A 38 3A 39 2C 31 39 2E 31 2C 35 36 2C 31 30 31 36 2E 32 2C 31 37 2E 31 0A
ASCII変換後
10,23,2025,1,24,XX.XX,YYY,YY0
11,24:8:9,19.1,56,1016.2,17.1
DS18B20について
arduinoide付属のOneWireではスプレッセンス側の対応が一部無いらしく正常動作しません URL:https://note.com/regnant_saya/n/n2560adf75690 に記述ございました。
この記事にあったライブラリを使わせていただいております。
尚このライブラリはオリジナルのOneire.hと入れ替えて使うようになっていますのでおりますのでオリジナルも必要な場合はあらかじめコピーとるとか別な名前で使えるようにするとかの工夫必要です
使用したプログラムとライブラリ
URL:https://www.arduino.cc/en/software ArduinoIDE
URL:https://github.com/finitespace/BME280 BME280(ボードマネージャーからインストール可能)
URL:https://github.com/saya-mitsuwa/spresense_onewire DS18B20 spresense_onewire(説明文読んで使うイメージです)
URL:https://github.com/RobTillaart/I2C_24LC1025 I2CEEPROM(ボードマネージャーからインストール可能)
プログラム
ヘッダーファイルと本体にわけております.
GNSS_DS18B20_BME280.h
#include <RTC.h>
#include <GNSS.h>
#define MY_TIMEZONE_IN_SECONDS (9 * 60 * 60) // JST
SpGnss Gnss;
float latitude1(NAN),longitude1(NAN);
int test_lati(0),check_data(0),year_check(0);
String time1(""),time2("");
#include <BME280I2C.h>
BME280I2C bme;
float temp(NAN), hum(NAN), pres(NAN);
String temp2(""); //温度・湿度・気圧
#include <OneWire2.h>
int DS18S20_Pin(22); //ピンNo変えるときはOneWire2のライブラリも書き換える
OneWire ds(DS18S20_Pin); // on digital pin 22
#include <Wire.h>//BME280 and I2CEEPROM
#include "I2C_24LC1025.h"
I2C_24LC1025 ee(0x50);
int offset_w = 0; //EEPROMに書き込むとき用のオフセット
int offset1(0),count1(10),wordlength1(0);
String data1(""),datatotal1("");
int offset2(0),count2(0),wordlength2(0);
String data2(""),datatotal2("");
#define SERIAL_BAUD 9600
program_main.ino
#include "GNSS_DS18B20_BME280.h"
//GNSS
void printClock(RtcTime &rtc){
time1 = String(rtc.year())+","+String(rtc.month())+","+String(rtc.day());
time2 = String(rtc.hour())+":"+String(rtc.minute());
year_check = rtc.year();
}
void updateClock(){
static RtcTime old;
RtcTime now = RTC.getTime();
if (now != old){
printClock(now);
old = now;
}
}
void printBME280Data(Stream* client){
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
BME280::PresUnit presUnit(BME280::PresUnit_Pa);
bme.read(pres, temp, hum, tempUnit, presUnit);
temp2 = String(temp,1)+","+String(hum,0)+","+String(pres/100,1);
delay(10);
}
float getTemp(){
delay(50);
//returns the temperature from one DS18S20 in DEG Celsius
byte data[12];
byte addr[8];
if ( !ds.search(addr)){
//no more sensors on chain, reset search
ds.reset_search();
return -1000;
}
if ( OneWire::crc8( addr, 7) != addr[7]){
Serial.println("CRC is not valid!");
return -1000;
}
if ( addr[0] != 0x10 && addr[0] != 0x28){
Serial.print("Device is not recognized");
return -1000;
}
delay(50);
ds.reset();
ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at the end
byte present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
for (int i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
}
ds.reset_search();
byte MSB = data[1];
byte LSB = data[0];
float tempRead = ((MSB << 8) | LSB); //using two's compliment
float TemperatureSum = tempRead / 16;
return TemperatureSum;
}
void setup(){
pinMode(LED0, OUTPUT);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(LED3, OUTPUT);
Serial.begin(SERIAL_BAUD);
while (!Serial);
Wire.begin();
if (! ee.begin())
{
Serial.println("EEPROM not found...");
while (1);
}
RTC.begin();
int ret;
ret = Gnss.begin();
assert(ret == 0);
ret = Gnss.start();
assert(ret == 0);
while(!bme.begin())
{
Serial.println("Could not find BME280 sensor!");
delay(1000);
}
if (Gnss.waitUpdate()){
SpNavData NavData;
// Get the UTC time
Gnss.getNavData(&NavData);
SpGnssTime *time = &NavData.time;
// Check if the acquired UTC time is accurate
if (time->year >= 2000) {
RtcTime now = RTC.getTime();
// Convert SpGnssTime to RtcTime
RtcTime gps(time->year, time->month, time->day,
time->hour, time->minute, time->sec, time->usec * 1000);
#ifdef MY_TIMEZONE_IN_SECONDS
// Set the time difference
gps += MY_TIMEZONE_IN_SECONDS;
#endif
int diff = now - gps;
if (abs(diff) >= 1) {
RTC.setTime(gps);
}
}
}
SpNavData NavData;
while(check_data == 0){
if (Gnss.waitUpdate()) {
SpNavData NavData;
Gnss.getNavData(&NavData);
SpGnssTime *time = &NavData.time;
if (time->year >= 2000) {
RtcTime now = RTC.getTime();
RtcTime gps(time->year, time->month, time->day,
time->hour, time->minute, time->sec, time->usec * 1000);
#ifdef MY_TIMEZONE_IN_SECONDS
gps += MY_TIMEZONE_IN_SECONDS;
#endif
int diff = now - gps;
if (abs(diff) >= 1) {
RTC.setTime(gps);
}
}
}
updateClock();
Gnss.waitUpdate();
delay(10);
Gnss.getNavData(&NavData);
digitalWrite(LED0,HIGH);
delay(10);
latitude1 = (NavData.latitude);
longitude1 = (NavData.longitude);
delay(10);
test_lati = (NavData.latitude);
if(test_lati >0){
check_data = 1;
}
}
updateClock();
data1 = String(time1)+","+String(latitude1)+","+String(longitude1);
wordlength1 = data1.length();
if(wordlength1 %2 == 0){
datatotal1 = String(count1)+","+String(wordlength1)+","+String(data1)+String(0)+String("\n");
Serial.println(String(datatotal1)+" EVEN "+String(datatotal1.length()));
byte buffer_1[datatotal1.length() + 1];
datatotal1.getBytes(buffer_1, datatotal1.length() + 1);
offset_w = offset1 ;
delay(10);
ee.writeBlock(offset_w, (uint8_t*) buffer_1, datatotal1.length());
delay(10);
}else{
datatotal1 = String(count1)+","+String(wordlength1)+","+String(data1)+String("\n");
Serial.println(String(datatotal1)+" ODD "+String(datatotal1.length()));
byte buffer_1[datatotal1.length() + 1];
datatotal1.getBytes(buffer_1, datatotal1.length() + 1);
offset_w = offset1 ;
delay(10);
ee.writeBlock(offset_w, (uint8_t*) buffer_1, datatotal1.length());
delay(10);
}
digitalWrite(LED0,LOW);
getTemp();
}
void loop(){
float temperature = getTemp();
printBME280Data(&Serial);
delay(10);
if(offset2 == 0){offset2 = datatotal1.length();}else{offset2 = offset2;}
delay(5);
if(count2 == 0){count2 = count1+1;}else{ count2 = count2;}
delay(5);
updateClock();
data2 = String(time2)+","+String(temp2)+","+String(temperature,1);
wordlength2 = data2.length();
if(wordlength2 %2 == 0){
datatotal2 = String(count2)+","+String(wordlength2)+","+String(data2)+String(0)+String("\n");
byte buffer_2[datatotal2.length() + 1];
datatotal2.getBytes(buffer_2, datatotal2.length()+1);
offset_w = offset2 ;
ee.writeBlock(offset_w, (uint8_t*) buffer_2, datatotal2.length());
delay(10);
Serial.println(String(datatotal2)+" EVEN "+datatotal2.length());
count2++;
offset2 = offset2 + datatotal2.length();
}else{
datatotal2 = String(count2)+","+String(wordlength2)+","+String(data2)+String("\n");
byte buffer_2[datatotal2.length() + 1];
datatotal2.getBytes(buffer_2, datatotal2.length()+1);
offset_w = offset2 ;
ee.writeBlock(offset_w, (uint8_t*) buffer_2, datatotal2.length());
delay(10);
Serial.println(String(datatotal2)+" ODD "+datatotal2.length());
count2++;
offset2 = offset2 + datatotal2.length();
}
delay(10);
digitalWrite(LED3,HIGH);
delay(30000);
digitalWrite(LED3,LOW);
delay(150000);
}
部品表
品名 | 概要 | |
---|---|---|
1 | BME280 | 3.3V用を購入したが1.8Vで動作しました |
2 | DS18B20 | 3V動作の為レベルコンバータ使いました |
3 | いちごジャムPC用EEPROM | お値段手ごろの為 |
4 | ピンヘッダーや配線材料 | ぁユニバーサル基板も使いました |
5 | レベルコンバータ | DS18B20用です |
※レベルコンバーターは4回路用のうち一回路を使っています。
回路図
スプレセンスメインボードから 1.8V,3.3V,DS18B20用D22,SDL,SCR,GNDを使い、配線しています。
センサーやEEPROMはプルアップ抵抗付きの部品を使ったため特に考慮しておりません。
ケース
温度湿度は容器に入れると温室効果で実際よりかなり高い値を示しますがグラフ化してトレンドを掴むには丁度よかったのです。
100均のタッパーを使い開けた穴はボンドで塞いであります。
投稿者の人気記事
-
Makato-kan
さんが
2025/01/29
に
編集
をしました。
(メッセージ: 初版)
-
Makato-kan
さんが
2025/01/31
に
編集
をしました。
(メッセージ: 1バイト書き込み忘れを直しました)
-
Makato-kan
さんが
2025/01/31
に
編集
をしました。
(メッセージ: 行末をLFにするよう変更しました)
-
Makato-kan
さんが
2025/01/31
に
編集
をしました。
(メッセージ: これで多分大丈夫かと思う)
-
Makato-kan
さんが
2025/01/31
に
編集
をしました。
(メッセージ: ダメ直し)
ログインしてコメントを投稿する