WioLTEを間欠運転で電池長持ちさせる記事
安価でどからでもセンサーの測定値を送信するシステムを目指してプロトタイプを開発しました。
とりあえず、ATMEGA328をDeepSleepさせながら復帰時にWioLTEの電源をONにして
通信後、WioLTEの電源を切ってATMEGAをDeepSleepさせます。
機能として何も測定しないのではつまらないので温度、電圧を測定して送信します。
■構成
系統1
電池1(3.3V)---ATMEGA
系統2
バッテリ(6V~12V)---WioLTE
電源は2系統持つこととしてひとつは単三電池2本でATMEGAを駆動させる。
もうひとつはバッテリー(バイクとか車用のやつ)でWioLTを駆動させる。
■機能構成
□ATMEGAの機能
一定時間DeepSleepさせて定期的に復帰する。
復帰したときにリレーをONにしてWioLTEを起動する。
2系統ある電源の電圧を測定する。
BME280で温度を取得する。
WioLTEの起動を待ち(UART通信で「start」文字列が到着)、
上記測定したデータをWioLTEにUARTで送信する。
WioLTEの応答を待ち、正常でない場合は1回だけリトライする。
その後DeepSleepして以下繰り返し。
□WioLTEの機能
SDカードに記録されているAPN等の接続情報や送信先のサーバアドレスを読み込む。
起動後、UART通信で「start」文字列を送信する。
ATMEGAからの送信要求(UARTで「SEND」で始まる文字列と温度、電圧の情報を含む)の到着を待つ。
送信指示に従いhttpgetでサーバーへ測定値を送信する。
SDメモリに動作ログを書き込む。
動作ログには温度と2系ある電源電圧を時刻とともに記録する。
■回路図
■写真
■実測1
□ATMEGA側電源の電流を測定。
起動してリレーをONにした状態では約35~40mA
DeepSleep時は0.04mA
□WioLTE側の電流を測定。
6Vのバッテリに接続時電流は220~250mA
□1回の通信にかかる時間は20秒
■映像でどうぞ。
■もくろみ
上記の消費電力にて電池とバッテリーで動作させて場合どのぐらい長持ちになるのか計算してみた。
前提条件:
系統1はアルカリ電池エボルタ 少なくても2000mAHは取り出せると想定。
パナソニックページ参考。
https://jpn.faq.panasonic.com/app/answers/detail/a_id/29060/~/%5B%EF%BD%B1%EF%BE%99%EF%BD%B6%EF%BE%98%EF%BD%A5%EF%BE%8F%EF%BE%9D%EF%BD%B6%EF%BE%9E%EF%BE%9D%5D-%E4%B9%BE%E9%9B%BB%E6%B1%A0%E3%81%AE%E9%9B%BB%E6%B1%A0%E5%AE%B9%E9%87%8F%E3%81%AF%E3%81%A9%E3%82%8C%E4%BD%8D%EF%BC%9F-pz29060
つまり、電流をたくさん流すとトータルの電力量が減るもようでして、
アルカリ乾電池20mAで140時間の場合は 20×140で2800mAH
同様にアルカリ100mAで20時間の場合は 100×20=2000mAH
なので、DeepSleepを考慮しないで最低でも2000mAHは出るかなと
系統2は鉛バッテリ
http://akizukidenshi.com/catalog/g/gB-14075/
これ、結構安い700円を使ってみた場合4500mAHを取り出せると想定。
サイズも小さいので気に入った。
これで、1時間に1回通信させる
計算:
系統1
DeepSleep時 1時間の電流
0.04mA × 3580秒 ÷ 3600秒 = 0.040mAH
リレーON時 1時間の電流
40mA × 20秒 ÷ 3600秒 = 0.2mAH
1時間の電流合計
0.24mAH
アルカリ電池の容量 2000mAHを割ると
2000mAH ÷ 0.24 mAH = 8333時間
って事は24で割って
8333 ÷ 24 = 347日
1年ぐらい持ちますね。
系統2
1時間の電流計算
300mA × 20秒 ÷ 3600秒 = 1.7mAH
バッテリの容量4500mAHを割ると
4500mAH ÷ 1.7mAH = 2647時間
って事は24で割って
2647 ÷ 24 =110日
4か月弱ですねー
サーバーに電圧を測定してを送っているので
様子を見ながら止まる前に充電してもらいましょう。
■測定を開始した。
測定条件:
系統1:アルカリ乾電池エボルタ 単3乾電池2本
系統2:アルカリ乾電池エボルタ 単3乾電池4本
系統1は上の設計通りなのでまあ、良いでしょう。
系統2は4か月待つのもなんかなーな感じなので単3電池で実験開始にした。
送信間隔は5分として1時間に12回ぐらい送信する事にした。
そうすると、上記計算式に当てはめると
系統1
30.8日
ひと月ぐらい。
系統2
9.3日
10日ぐらいですねー
計算間違っていました。
上記のパナソニックページのもう一つの参考
https://jpn.faq.panasonic.com/euf/assets/images/panasonic/answer_images/energy/battery/drycell/29060_a_3.pdf
これで、WioLTEの常時必要とする電流300mAを見ると
2.5時間しか持たないので、2.5時間×300mA=750mAhとなります。
系統2
1.5日ぐらいですね。
測定開始 2/2-11:30
さて、どうなるかみてみよう。
電池の減り具合と停止したときの電圧に注目です。
■結果
測定開始 2/2-11:30
動作停止 2/4-18:32
2.3日動作したことになります。
計算での予測値が1.5日だったので
それよりはいい結果だったって事でですね。
WioLTEno常時消費電力は300mAよりは少ないのかもしれません。
少し前提条件を見直して
1回の通信時間をより正確に計測した17.5秒
WioLTEの常時消費電流を250mAhとして再計算すると
2.14日となりますので、だいたい合っているようですから、
このあたりが適切な数字なのだろうという事にしました。
■電池の減り考察
サーバーのログから電池の持ちのグラフを作ってみました。
縦軸が電圧。
1系がATMEGAの電力
2系がWioLTE動作側
今回は1系のATMEGA側は元気に動いていたのですが、
2系のWioLTE側がサーバーと通信できなくなって停止しておりましたので
サーバと通信時は一瞬大電流がながれるのでしょう。
それに耐えられなくなって動作停止です。
その時の2系の電圧は5Vです。
なので、5.3Vを切ってきたらヤバいよーってメール飛ばすようにしておけば良いかなと思います。
こうやって見ると1系側も結構電圧が下がってきているのが気になりますね。
ATMEGAは328なので、1.8~5.5Vで動くことになっておりまして。
http://akizukidenshi.com/download/ds/microchip/atmega328.pdf
今回のこいつは8MHz動作ですから
上のページの
Speed Grade: ̶ 0 - 4MHz@1.8 - 5.5V, 0 - 10MHz@2.7 - 5.5.V, 0 - 20MHz @ 4.5 - 5.5V
の表記から・・・なんで、8MHz書いてないのよー
と思いましたがま、4Mと10Mの間って事で1.8~2.7の間で適当に按分して2.4Vまで下がっても動けそうです。
問題はリレーの方でして、
Y14H-1C-3DS
http://akizukidenshi.com/catalog/g/gP-01347/
データシート
http://akizukidenshi.com/download/ds/hsinda/y14h.pdf
によると
型番からHighSenceの方で
3V時PickUpVoltage2.4ってなっており、Nominal Current66.7mAってあるので
ま、2.4Vまで行けそうな気がします。
しらんけど。ま、このまま実験続ければ出来るけど。
やってもいいけど、やらんかも。
■値を見直して再計算
系統2
1時間の電流計算
250mA × 17.5秒 ÷ 3600秒 = 1.22mAH
バッテリの容量4500mAHを割ると
4500mAH ÷ 1.22mAH = 3688時間
って事は24で割って
3688 ÷ 24 =153日
5か月っすね。
■ATMEGA側プログラム
#include <SoftwareSerial.h>
#include <Wire.h>
int bme280_error_flag = 0;
#define MIHA_TIMEOUT (-10001)
SoftwareSerial softSerial( 5, 6 );
#define BME280_ADDRESS 0x76
void setup()
{
//アナログ基準電圧を内部にする
analogReference(INTERNAL);
//アナログ基準電圧が安定するまで何度か読みだす
readVoltage(0);readVoltage(0);readVoltage(0);
readVoltage(1);readVoltage(1);readVoltage(1);
//ハードウェアシリアルを準備
Serial.begin(9600);
//一定時間delayしてSerialの準備が整うのを待つ
delay(620);
Serial.print("kanketUunten version0.1 ¥n");
//DEBUG
float nVolt = readVoltage(0);
Serial.print("Volt=");
Serial.println(nVolt);
float nVolt2 = readVoltage(1);
Serial.print("Volt2=");
Serial.println(nVolt2);
// ソフトウェアシリアルの初期化
softSerial.begin(4800);
int rc = init_BME280();
if(rc<0){
bme280_error_flag = 1;
}
}
//エラー時の表示
// 1:start待ちでERRORが発生
// SDセットされていない
// config.inf設定誤り
// 電波の届かないところにいる
// 2:SENDの応答待ちでERRORが発生
// 3:温度の測定エラー
void blink_LED(int n)
{
pinMode( 10, OUTPUT );
for(int i=0;i<5;i++){
digitalWrite(10, HIGH);
delay(500);
digitalWrite(10, LOW);
delay(500);
for(int i=0;i<n;i++){
digitalWrite(10, HIGH);
delay(20);
digitalWrite(10, LOW);
delay(200);
}
delay(500);
}
}
//deepsleep後一定時間経過しないとSerialが安定しない
#define sleep_time 20
int OneTurn(int *sec)
{
//
//
//
int error_flag = 0;
char message[80];
memset(message, '¥0', sizeof(message));
//
//startを待つ
//
Serial.println("wait :");
char buffer[80];
memset(buffer, 0, sizeof(buffer));
int rc = read_wait(buffer, sizeof(buffer), "start", "ERROR");
if(rc==MIHA_TIMEOUT){
Serial.println("timeout read_wait() [start][ERROR]");
return 1;
}
else{
if(memcmp(buffer, "ERROR", 5)==0){
Serial.println("point 2 error");
return 2;
}
}
//arduino側電源電圧測定
float nVolt = readVoltage(0);
// Serial.print("Volt=");
// Serial.println(nVolt);
//WioLTE側電源電圧測定
float nVolt2 = readVoltage(1);
// Serial.print("Volt2=");
// Serial.println(nVolt2);
//
//温度湿度気圧を測定
//
float ondo = 0.0, situdo=0.0, kiatu = 0.0;
if(bme280_error_flag==0){
rc = bme280_get_data(&ondo, &situdo, &kiatu);
//float a , b, c;
//rc = bme280_get_data();
}
else{
rc = -1;
}
if(rc<0){
error_flag = -1;
strcpy(message, "error bme280_get_data()");
}
//
//温度湿度気圧をstringに変換
//デバッグの為 湿度と気圧の代わりにnVoltとnVolt2をサーバーに飛ばす
//
char s_ondo[32];
char s_situdo[32];
char s_kiatu[32];
memset(s_ondo, '¥0', sizeof(s_ondo));
memset(s_situdo, '¥0', sizeof(s_situdo));
memset(s_kiatu, '¥0', sizeof(s_kiatu));
dtostrf(ondo, 5, 1, s_ondo);
dtostrf(nVolt, 5, 1, s_situdo);
dtostrf(nVolt2, 7, 1, s_kiatu);
strip(s_ondo);
strip(s_situdo);
strip(s_kiatu);
//
//データ送信
//
if(error_flag==0){
sprintf(buffer, "SEND %s/%s/%s", s_ondo, s_situdo, s_kiatu);
}
if(error_flag==-1){
sprintf(buffer, "ERROR %s", message);
}
softSerial.print( buffer ); // 送信コマンド
softSerial.print( '¥n' ); // 送信コマンド
Serial.print("write:");
Serial.println(buffer);
//
//読み込み
//
int index = 0;
bool hasData = false;
memset(buffer, 0, sizeof(buffer));
rc = read_wait(buffer, sizeof(buffer), "OK", "ERROR");
if(rc==MIHA_TIMEOUT){
Serial.println("timeout read_wait[OK][ERROR]");
return 3;
}
else{
if(memcmp(buffer, "ERROR", 5)==0){
Serial.println("point 4 error");
return 4;
}
}
//Serial.println(buffer);
*sec = 60;
char *p = strstr(buffer, "OK");
if(p!=0){
*p = NULL;
*sec=atoi(buffer);
if(*sec==0){
*sec = 60;
}
Serial.print("sec=");
Serial.println(*sec);
}
if(error_flag==-1){
return 5;
}
return 0;
}
void loop() {
//
//loop関数開始のログ出力
//
Serial.println("loop start:");
//測定と通信の制御がエラーの場合はもう一度だけリトライする
//
for(int i=0;i<2;i++){
// ソフトウェアシリアルの初期化
softSerial.begin(4800);
//
Serial.print("count:");
Serial.println(i+1);
//
//リレーをON
pinMode( 12, OUTPUT );
digitalWrite(12, HIGH);
int wait_sec = 60;
//計測と通信を行う
int rc = OneTurn(&wait_sec);
//
//リレーをOFF
//
digitalWrite(12, LOW);
//
//エラー判定してLED光らせる
//
if( rc ==2 ){
blink_LED(1);
wait_sec=180;
}
if( rc ==4 ){
blink_LED(2);
wait_sec=180;
}
if( rc ==5 ){
blink_LED(3);
wait_sec=180;
}
if(rc==0){
break;
}
delay(1000);
}
//
//DEEP SLEEP
//
Serial.println("sleep start:");
delay(100);
//ウォッチドッグタイマーでスリープする 8秒×3=24秒
//ここは仮置きで3にしている
//watchDogSleep(1); //DEBUG
//watchDogSleep(sec/8); //サーバーからの値から計算
watchDogSleep((5*60)/8); //とりあえず電池持ちテストのため5分としてみる
//
//DEEP SLEEPから復帰のログ出力
//
delay(sleep_time); //DEEP SLEEP復帰後一定時間待たないとSerialが安定しない
Serial.println("sleep end:");
}
//
//readVoltage()
// 電源電圧を測定
// 事前にanalogReference(INTERNAL)をやる事。
// D12から給電して分圧して14/1をAnalog0に与える
// D12----R(1.3MΩ)---+---R(100KΩ)---GND
// |
// Analog0
//
float readVoltage(int AnalogPortNo)
{
int analogValue = analogRead(AnalogPortNo);
//
//1.1V = 1023
//
float nVolt = analogValue*1100UL;
nVolt = nVolt/1023UL;
nVolt = nVolt*14/1000;
return nVolt;
}
int read_wait(char *buffer, int size, char *waitstring1, char *waitstring2)
{
int count = 0;
while(1){
memset(buffer, '¥0', size);
int n = readline(buffer, size);
if(n!=0){
Serial.print("read_wait:");
Serial.println(buffer);
if(strstr(buffer, waitstring1)!=NULL){
return(0);
}
if(waitstring2 != NULL){
if(strstr(buffer, waitstring2)!=NULL){
return(0);
}
}
}
else{
count++;
if(count>30){
return(MIHA_TIMEOUT);
}
continue;
}
}
}
int readline(char *str, int size)
{
memset(str, '¥0', size);
int i=0;
//Serial.println("readline point 1");
while(1) {
int j;
for(j=0;j<10;j++){
if(softSerial.available()==0){
delay(100);
}
else{
break;
}
}
if(j>=10){
//Serial.println("readline timeout");
break;
}
else{
char c = softSerial.read();
str[i]=c;
//Serial.println(c);
if(c=='¥n'){
//Serial.println("readline break");
break;
}
i++;
if(i>=size-1){
break;
}
}
}
return(i);
}
void strip(char *str)
{
char tmp[256];
memset(tmp, '¥0', sizeof(tmp));
int j = 0;
for(int i=0;i<strlen(str);i++){
if(str[i]!=' '){
tmp[j]=str[i];
j++;
}
}
strcpy(str, tmp);
}
//watchdog interrupt
ISR(WDT_vect){
digitalWrite(13, HIGH);
delay(100);
digitalWrite(13, LOW);
}
void watchDogSleep(int count)
{
//
//おっちドッグタイマーが最大8秒しかできないので
//1秒設定にしてループする
//
for(int i=0;i<count;i++){
// パワーダウンモードを設定 SM1
SMCR |= 0b00000001 | 0b00000100;
// AD変換機能をOFFにする ADEN
ADCSRA &= ~(0b10000000); //
// おっちドッグタイマーをセットする
asm("wdr");
WDTCSR |= 0b00011000; //WDCE WDE set
//WDTCSR = 0b01000000 | 0b000110;// 1 sec
//WDTCSR = 0b01000000 | 0b000111;// 2 sec
//WDTCSR = 0b01000000 | 0b100000;// 4 sec
WDTCSR = 0b01000000 | 0b100001;// 8 sec
// 低電圧検出器を停止するスリープの間有効
MCUCR |= 0b00100000 | 0b01000000; //BODSE BODS
MCUCR = (MCUCR & ~(0b00100000)) | (0b01000000); //BODSE BODS
//スリープ 指定した時間で戻る
asm("sleep");
// おっちドッグタイマーを停止する
asm("wdr");
MCUSR &= ~(0b00001000); //おっちドッグタイマーリセットフラグをOFF WDRF
WDTCSR |= (0b00010000)|(1 << 0b00001000); //おっちドッグタイマー設定変更 WDCE WDE
WDTCSR = 0b00000000; //おっちドッグタイマー停止
// AD変換機能をONにする
ADCSRA |= 0b10000000; //ADEN
}
}
//
//BME280を初期化
//
//BME設定
#define CONFIG 0xF5
#define CTRL_MEAS 0xF4
#define CTRL_HUM 0xF2
//気温補正データ
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
//湿度補正データ
uint8_t dig_H1;
int16_t dig_H2;
uint8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;
//気圧補正データ
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
unsigned char dac[26];
unsigned int i;
int32_t t_fine;
int32_t adc_P, adc_T, adc_H;
int init_BME280()
{
//I2C初期化
Wire.begin();//I2Cを初期化
//BME280動作設定
Wire.beginTransmission(BME280_ADDRESS);//I2Cスレーブ「Arduino Uno」のデータ送信開始
Wire.write(CONFIG);//動作設定
Wire.write(0x00);//「単発測定」、「フィルタなし」、「SPI 4線式」
if(Wire.endTransmission()){ //I2Cスレーブ「Arduino Uno」のデータ送信終了
return -1;
}
//BME280測定条件設定
Wire.beginTransmission(BME280_ADDRESS);//I2Cスレーブ「Arduino Uno」のデータ送信開始
Wire.write(CTRL_MEAS);//測定条件設定
Wire.write(0x24);//「温度・気圧オーバーサンプリングx1」、「スリープモード」
if(Wire.endTransmission()){ //I2Cスレーブ「Arduino Uno」のデータ送信終了
return -1;
}
//BME280温度測定条件設定
Wire.beginTransmission(BME280_ADDRESS);//I2Cスレーブ「Arduino Uno」のデータ送信開始
Wire.write(CTRL_HUM);//湿度測定条件設定
Wire.write(0x01);//「湿度オーバーサンプリングx1」
if(Wire.endTransmission()){ //I2Cスレーブ「Arduino Uno」のデータ送信終了
return -1;
}
//BME280補正データ取得
Wire.beginTransmission(BME280_ADDRESS);//I2Cスレーブ「Arduino Uno」のデータ送信開始
Wire.write(0x88);//出力データバイトを「補正データ」のアドレスに指定
if(Wire.endTransmission()){//I2Cスレーブ「Arduino Uno」のデータ送信終了
return -1;
}
Wire.requestFrom(BME280_ADDRESS, 26);//I2Cデバイス「BME280」に26Byteのデータ要求
for (i=0; i<26; i++){
int count = 0;
while (Wire.available() == 0 ){
if(count>10){
return -1;
}
count++;
delay(10);
}
dac[i] = Wire.read();//dacにI2Cデバイス「BME280」のデータ読み込み
}
dig_T1 = ((uint16_t)((dac[1] << 8) | dac[0]));
dig_T2 = ((int16_t)((dac[3] << 8) | dac[2]));
dig_T3 = ((int16_t)((dac[5] << 8) | dac[4]));
dig_P1 = ((uint16_t)((dac[7] << 8) | dac[6]));
dig_P2 = ((int16_t)((dac[9] << 8) | dac[8]));
dig_P3 = ((int16_t)((dac[11] << 8) | dac[10]));
dig_P4 = ((int16_t)((dac[13] << 8) | dac[12]));
dig_P5 = ((int16_t)((dac[15] << 8) | dac[14]));
dig_P6 = ((int16_t)((dac[17] << 8) | dac[16]));
dig_P7 = ((int16_t)((dac[19] << 8) | dac[18]));
dig_P8 = ((int16_t)((dac[21] << 8) | dac[20]));
dig_P9 = ((int16_t)((dac[23] << 8) | dac[22]));
dig_H1 = ((uint8_t)(dac[25]));
Wire.beginTransmission(BME280_ADDRESS);//I2Cスレーブ「Arduino Uno」のデータ送信開始
Wire.write(0xE1);//出力データバイトを「補正データ」のアドレスに指定
if(Wire.endTransmission()){//I2Cスレーブ「Arduino Uno」のデータ送信終了
return -1;
}
Wire.requestFrom(BME280_ADDRESS, 7);//I2Cデバイス「BME280」に7Byteのデータ要求
for (i=0; i<7; i++){
int count = 0;
while (Wire.available() == 0 ){
if(count>10){
return -1;
}
count++;
delay(10);
}
dac[i] = Wire.read();//dacにI2Cデバイス「BME280」のデータ読み込み
}
dig_H2 = ((int16_t)((dac[1] << 8) | dac[0]));
dig_H3 = ((uint8_t)(dac[2]));
dig_H4 = ((int16_t)((dac[3] << 4) + (dac[4] & 0x0F)));
dig_H5 = ((int16_t)((dac[5] << 4) + ((dac[4] >> 4) & 0x0F)));
dig_H6 = ((int8_t)dac[6]);
return 0;
}
//
//BME280から温度湿度気圧を取得
//
int bme280_get_data(float *ondo, float *situdo, float *kiatu)
{
// int32_t temp_cal;
// uint32_t humi_cal, pres_cal;
long temp_cal;
unsigned long humi_cal, pres_cal;
//BME280測定条件設定(1回測定後、スリープモード)
Wire.beginTransmission(BME280_ADDRESS);//I2Cスレーブ「Arduino Uno」のデータ送信開始
Wire.write(CTRL_MEAS);//測定条件設定
Wire.write(0x25);//「温度・気圧オーバーサンプリングx1」、「1回測定後、スリープモード」
if(Wire.endTransmission()){//I2Cスレーブ「Arduino Uno」のデータ送信終了
return -1;
}
delay(10);//10msec待機
//測定データ取得
Wire.beginTransmission(BME280_ADDRESS);//I2Cスレーブ「Arduino Uno」のデータ送信開始
Wire.write(0xF7);//出力データバイトを「気圧データ」のアドレスに指定
if(Wire.endTransmission()){;//I2Cスレーブ「Arduino Uno」のデータ送信終了
return -1;
}
Wire.requestFrom(BME280_ADDRESS, 8);//I2Cデバイス「BME280」に8Byteのデータ要求
for (i=0; i<8; i++){
int count = 0;
while (Wire.available() == 0 ){
if(count>10){
return -1;
}
count++;
delay(10);
}
dac[i] = Wire.read();//dacにI2Cデバイス「BME280」のデータ読み込み
}
adc_P = ((uint32_t)dac[0] << 12) | ((uint32_t)dac[1] << 4) | ((dac[2] >> 4) & 0x0F);
adc_T = ((uint32_t)dac[3] << 12) | ((uint32_t)dac[4] << 4) | ((dac[5] >> 4) & 0x0F);
adc_H = ((uint32_t)dac[6] << 8) | ((uint32_t)dac[7]);
pres_cal = BME280_compensate_P_int32(adc_P);//気圧データ補正計算
temp_cal = BME280_compensate_T_int32(adc_T);//温度データ補正計算
humi_cal = bme280_compensate_H_int32(adc_H);//湿度データ補正計算
float temp, humi, pres;
pres = (float)pres_cal / 100.0;//気圧データを実際の値に計算
temp = (float)temp_cal / 100.0;//温度データを実際の値に計算
humi = (float)humi_cal / 1024.0;//湿度データを実際の値に計算
*ondo = temp;
*situdo = humi;
*kiatu = pres;
// //return 0; //DEBUG OK
//
Serial.println("DEBUG1 ------ ");
Serial.println(temp);
Serial.println(humi);
Serial.println(pres);
//
// char buffer1[100];
// char buffer2[100];
// char buffer3[100];
//// sprintf(buffer1, "%ld", temp_cal);
//// sprintf(buffer2, "%lu", pres_cal);
//// sprintf(buffer3, "%lu", humi_cal);
// sprintf(buffer1, "%f", temp);
// sprintf(buffer2, "%f", humi);
// sprintf(buffer3, "%f", pres);
// Serial.println("DEBUG2 ------ ");
// Serial.println(buffer1);
// Serial.println(buffer2);
// Serial.println(buffer3);
return 0; //DEBUG
}
//温度補正 関数
int32_t BME280_compensate_T_int32(int32_t adc_T)
{
int32_t var1, var2, T;
var1 = ((((adc_T>>3) - ((int32_t)dig_T1<<1))) * ((int32_t)dig_T2)) >> 11;
var2 = (((((adc_T>>4) - ((int32_t)dig_T1)) * ((adc_T>>4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}
//湿度補正 関数
uint32_t bme280_compensate_H_int32(int32_t adc_H)
{
int32_t v_x1_u32r;
v_x1_u32r = (t_fine - ((int32_t)76800));
v_x1_u32r = (((((adc_H << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) *
((int32_t)dig_H2) + 8192) >> 14));
v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
return (uint32_t)(v_x1_u32r>>12);
}
//気圧補正 関数
uint32_t BME280_compensate_P_int32(int32_t adc_P)
{
int32_t var1, var2;
uint32_t p;
var1 = (((int32_t)t_fine)>>1) - (int32_t)64000;
var2 = (((var1>>2) * (var1>>2)) >> 11 ) * ((int32_t)dig_P6);
var2 = var2 + ((var1*((int32_t)dig_P5))<<1);
var2 = (var2>>2)+(((int32_t)dig_P4)<<16);
var1 = (((dig_P3 * (((var1>>2) * (var1>>2)) >> 13 )) >> 3) + ((((int32_t)dig_P2) * var1)>>1))>>18;
var1 =((((32768+var1))*((int32_t)dig_P1))>>15);
if (var1 == 0)
{
return 0; // avoid exception caused by division by zero
}
p = (((uint32_t)(((int32_t)1048576)-adc_P)-(var2>>12)))*3125;
if (p < 0x80000000)
{
p = (p << 1) / ((uint32_t)var1);
}
else
{
p = (p / (uint32_t)var1) * 2;
}
var1 = (((int32_t)dig_P9) * ((int32_t)(((p>>3) * (p>>3))>>13)))>>12;
var2 = (((int32_t)(p>>2)) * ((int32_t)dig_P8))>>13;
p = (uint32_t)((int32_t)p + ((var1 + var2 + dig_P7) >> 4));
return p;
}
■WioLTE側プログラム
#include <WioLTEforArduino.h>
#include <stdio.h>
#include <SDforWioLTE.h> // https://github.com/SeeedJP/SDforWioLTE
#define MIHA_TIMEOUT (-10001)
//#define URL "http://xxxxx.japaneast.cloudapp.azure.com:8000"
#define PATH "ondo"
#define ERROR "ondolog"
//#define ID "WIO-LTE"
//#define VALUE "25.5/35.5/1000"
WioLTE Wio;
//#define APN "so-net.jp"
//#define USERNAME "nuro"
//#define PASSWORD "nuro"
int readline(char *str, int size)
{
memset(str, '¥0', size);
int i = 0;
while (1) {
int j;
for (j = 0; j < 10; j++) {
if (Serial.available() == 0) {
delay(100);
}
else {
break;
}
}
if (j >= 10) {
//SerialUSB.println("timeout");
break;
}
else {
unsigned char c = Serial.read();
str[i] = c;
if (c == '¥n') {
//SerialUSB.println("break");
break;
}
i++;
if (i >= size - 1) {
break;
}
}
}
if (str[0]) {
logA("readline:", str);
}
return (i);
}
void writeline(char *str)
{
logA("writeline:", str);
for (int i = 0; i < strlen(str); i++) {
Serial.write(str[i]);
}
Serial.write('¥n');
}
int read_wait(char *buffer, int size, char *waitstring1, char *waitstring2)
{
int count = 0;
while (1) {
int n = readline(buffer, size);
if (n != 0) {
if (strstr(buffer, waitstring1) != NULL) {
return (0);
}
if (waitstring2 != NULL) {
if (strstr(buffer, waitstring2) != NULL) {
return (0);
}
}
}
else {
count++;
if (count > 60) {
return (MIHA_TIMEOUT);
}
continue;
}
}
}
void strip(char *str)
{
char tmp[256];
memset(tmp, '¥0', sizeof(tmp));
int j = 0;
for (int i = 0; i < strlen(str); i++) {
if (str[i] != ' ' && str[i] != '¥r' && str[i] != '¥n') {
tmp[j] = str[i];
j++;
}
}
strcpy(str, tmp);
}
struct CONFIG {
char serialno[36];
char apn[101]; //so-net.jp
char username[36]; //nuro
char password[36]; //nuro
char url[256]; //http://xxxxx:8000
};
struct CONFIG config;
void stripRN(char *str)
{
char tmp[512];
memset(tmp, '¥0', sizeof(tmp));
int j = 0;
for (int i = 0; i < strlen(str); i++) {
if (str[i] != ' ' && str[i] != '¥r' && str[i] != '¥n') {
tmp[j] = str[i];
j++;
}
}
strcpy(str, tmp);
}
void sprit(char *str, char *kgr, char *str1, char *str2)
{
int len = strlen(str);
char *p = strstr(str, kgr);
if (p) {
*p = '¥0';
strcpy(str1, str);
if ((len - strlen(str1) - 1) >= 1) {
strcpy(str2, &p[1]);
}
}
}
int file_readLine(File *f, char *buffer, int size)
{
int i = 0;
while (1) {
if (!f->available()) {
return -1;
}
char c = f->read(); //1バイトだけ読み込む
buffer[i] = c;
i++;
if (i >= size - 1) {
buffer[i] = '¥0';
break;
}
if (c == '¥n') {
buffer[i] = '¥0';
break;
}
}
return 0;
}
int sdinit_flag = 0;
void config_read()
{
//
memset(&config, '¥0', sizeof(struct CONFIG));
//WioのSDへの電源供給
Wio.PowerSupplySD(true);
delay(500);
//
if (sdinit_flag == 0) {
sdinit_flag = 1;
//SD初期化
if (!SD.begin()) {
log("error config_read:SD.begin()");
return;
}
}
//
File f;
//
//ファイル読み込み
char line[512];
f = SD.open("config.inf", FILE_READ);
if (f) { //success
while (1) {
memset(line, '¥0', sizeof(line));
int rc = file_readLine(&f, line, sizeof(line));
stripRN(line);
char key[256];
char value[256];
memset(key, '¥0', sizeof(key));
memset(value, '¥0', sizeof(value));
sprit(line, ":", key, value);
if (rc == -1) {
break;
}
if (strcmp(key, "serialno") == 0) {
strcpy(config.serialno, value);
}
if (strcmp(key, "apn") == 0) {
strcpy(config.apn, value);
}
if (strcmp(key, "username") == 0) {
strcpy(config.username, value);
}
if (strcmp(key, "password") == 0) {
strcpy(config.password, value);
}
if (strcmp(key, "url") == 0) {
strcpy(config.url, value);
}
}
f.close();
log("config---");
logA("serialno:",config.serialno);
logA("apn:", config.apn);
logA("username:", config.username);
logA("password:", config.password);
logA("url:", config.url);
log("---------");
}
}
struct DateTime {
time_t base_time;
unsigned long base_startup_keika_sec;
};
struct DateTime dt;
//ログローテーション数
#define ROTATION_COUNT 50000
//ログファイル名
char logFileName[20];
//
//日時取得関数の初期化
// WioLTEのGetTimeから時刻を取得するので
// WioLTEのTurnOnOrReset()を行った後に呼び出す
// WioLTEのTurnOnOrReset()の前は1970年1月1日から開始される
// 時刻の取得はgetDateTimeStrで行う
//
void initDateTime()
{
struct tm now;
if (!Wio.GetTime(&now)) { //error
dt.base_time = 0;
}
else { //success
dt.base_time = mktime(&now);
if (dt.base_time == (time_t) - 1) { //error
dt.base_time = 0;
}
}
dt.base_startup_keika_sec = millis() / 1000;
}
//
//日時取得関数
// 事前にinitDateTime()を行っておくこと
// char *bufにYYYY/MM/DD hh:mm:ss形式で返す
// ex... 2020/01/25 05:50:28
//
void getDateTimeStr(char *buf)
{
//現在時刻累積秒を求める
unsigned long now_startup_keika_sec = millis() / 1000;
time_t now_time = dt.base_time + ((time_t)now_startup_keika_sec - (time_t)dt.base_startup_keika_sec);
//localtimeにする
now_time = now_time + 60 * 60 * 9;
struct tm *time_inf;
time_inf = localtime(&now_time);
int year = time_inf->tm_year + 1900;
int month = time_inf->tm_mon + 1;
int day = time_inf->tm_mday;
int hour = time_inf->tm_hour;
int minute = time_inf->tm_min;
int second = time_inf->tm_sec;
sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
}
void init_log()
{
//WioのSDへの電源供給
Wio.PowerSupplySD(true);
delay(500);
//
if (sdinit_flag == 0) {
sdinit_flag = 1;
//SD初期化
if (!SD.begin()) {
log("error init_log:SD.begin()");
return;
}
}
//
File f;
//
//ファイル読み込み
char strNo[11];
f = SD.open("status.txt", FILE_READ);
if (f) { //success
int i = 0;
memset(strNo, '¥0', sizeof(strNo));
for (int i = 0; i < 10; i++) {
if (!f.available()) {
break;
}
strNo[i] = f.read(); //1バイトだけ読み込む
}
f.close();
}
else { //error
strcpy(strNo, "0");
}
unsigned long No = atol(strNo);
logN("No=", No);
//ローテーション数を超えた過去ログファイルを削除
if (No > ROTATION_COUNT) {
char removeLogFileName[20];
sprintf(removeLogFileName, "wio%lu.log", No - ROTATION_COUNT);
SD.remove(removeLogFileName);
logA("remove:", removeLogFileName);
}
//今回のログファイル名決定
sprintf(logFileName, "wio%lu.log", No);
//次回のログファイルの番号を決定
No = No + 1;
//ファイルの書き込みはなぜか追記しかできないので削除
SD.remove("status.txt");
//次のためにログファイル番号を書いておく
f = SD.open("status.txt", FILE_WRITE);
if (f) {
char buf[20];
sprintf(buf, "%ld", No);
f.print(buf);
f.close();
}
}
void logN(char *str, long n)
{
char *p = (char *)malloc(strlen(str) + 20);
if (p) {
sprintf(p, "%s%ld", str, n);
log(p);
free(p);
}
}
void logA(char *str, char *str2)
{
char *p = (char *)malloc(strlen(str) + strlen(str2) + 1);
if (p) {
sprintf(p, "%s%s", str, str2);
log(p);
free(p);
}
}
void log(char *str)
{
char date_time[100];
//日時取得
getDateTimeStr(date_time);
File f = SD.open(logFileName, FILE_WRITE);
if (f) {
f.print(date_time);
f.print(":");
f.println(str);
f.close();
}
SerialUSB.print(date_time);
SerialUSB.print(":");
SerialUSB.println(str);
}
void setup()
{
delay(620);
Serial.begin(4800);
SerialUSB.println("start:");
SerialUSB.println("Wio.Init()");
Wio.Init();
//日付時刻初期化
initDateTime();
//ログ出力初期化
init_log();
//config読み込む
config_read();
log("Wio.PowerSupplyLed(true)");
Wio.PowerSupplyLed(true);
log("Wio.LedSetRGB()");
Wio.LedSetRGB(1, 0, 0);
log("Wio.PowerSupplyLTE(true)");
Wio.PowerSupplyLTE(true);
delay(500);
log("Wio.TurnOnOrReset()");
if (!Wio.TurnOnOrReset()) {
log("error TurnOnOrReset()");
writeline("");
writeline("");
writeline("ERROR");
return;
}
delay(500);
log("Wio.Activate(APN, USERNAME, PASSWORD)");
if (!Wio.Activate(config.apn, config.username, config.password)) {
log("error Wio.Activate()");
writeline("");
writeline("");
writeline("ERROR");
return;
}
//日付時刻初期化
initDateTime();
log("Wio.LedSetRGB()");
Wio.LedSetRGB(0, 1, 0);
//
//arduinoと通信開始
//
//初めの通信 改行 改行 start改行を送信
writeline("");
writeline("");
writeline("start");
char buffer[4096];
char http_buf[4096];
int nTimeOutCount = 0;
//SEND または ERRORを時受信
while (1) {
int n = read_wait(buffer, sizeof(buffer), "SEND", "ERROR");
//if (n == MIHA_TIMEOUT) {
// continue;
//}
if (buffer[0]=='¥0') {
nTimeOutCount++;
if(nTimeOutCount>2){
strcpy(buffer, "ERROR error : arduino not responding");
}
else{
continue;
}
}
nTimeOutCount = 0;
//
//SENDの場合もERRORの場合もなんか送信
//
if (memcmp(buffer, "SEND ", 5) == 0) {
Wio.LedSetRGB(0, 1, 1);
char rtn_buffer[256];
log("HttpGet");
char value_buffer[256];
strcpy(value_buffer, &buffer[5]);
strip(value_buffer);
char url_buffer[256];
sprintf(url_buffer , "%s/%s/%s/%s/", config.url, PATH, config.serialno, value_buffer);
log(url_buffer);
int nRc = Wio.HttpGet(url_buffer, http_buf, sizeof(http_buf), 3000);
if (nRc < 0) {
Wio.LedSetRGB(1, 0, 0);
log("error Wio.HttpGet()");
strcpy(rtn_buffer, "ERROR");
log("error Wio.HttpGet");
}
else {
Wio.LedSetRGB(0, 0, 1);
log(http_buf);
sprintf(rtn_buffer, "%sOK", http_buf);
}
writeline(rtn_buffer);
}else
if (memcmp(buffer, "ERROR ", 6) == 0) {
Wio.LedSetRGB(0, 1, 1);
char rtn_buffer[256];
log("HttpGet");
char value_buffer[256];
strcpy(value_buffer, &buffer[6]);
strip(value_buffer);
char url_buffer[256];
sprintf(url_buffer , "%s/%s/%s/%s/", config.url, ERROR, config.serialno, value_buffer);
log(url_buffer);
int nRc = Wio.HttpGet(url_buffer, http_buf, sizeof(http_buf), 3000);
if (nRc < 0) {
Wio.LedSetRGB(1, 0, 0);
log("error Wio.HttpGet()");
strcpy(rtn_buffer, "ERROR");
}
else {
Wio.LedSetRGB(0, 0, 1);
log(http_buf);
sprintf(rtn_buffer, "OK", http_buf);
}
writeline(rtn_buffer);
}
else {
writeline("OK");
}
}
while (1)
{}
}
void loop()
{
}