秋月で見つけたUSBマイコンがあのトラ技のおまけだった件
秋月電子で発見したUSBマイコン300円

「長期保管品です」「ご質問ご返品ご返金一切不可です」の記述がある。
家に帰って調べてみるとチップにはD78F0730との記述があり、
あの有名なトラ技さんの2008年8月号に付録されたものらしい事とか
今はRENESASさんでサポートされており、後継はRL78/G1Cであることなどが判る。
で、以下のページ
https://www.renesas.com/jp/ja/software/D3017761.html
2010年頃の記事ですが
SUBドライバーはルネサスさんが提供の物が使用可能で、
『64bit版OSで動かすには、ルネサスエレクトロニクスさんの
提供するドライバをダウンロードして解凍し、
中の wlh_amd64 のフォルダを開きます。
そして、necelusbvcom.inf をメモ帳で開き、
3箇所あるUSB¥VID_0409&PID_01CD をUSB¥VID_0409&PID_FFFD に変更します。』
とあるのでRENESASさんにページに行ってドライバーを探します。
・・・・
・・・
・・
・
・
・
・
1時間探して見つからない。
さっきのページに戻ってみて、
記事の続きを読んでいくと
『「ルネサスエレクトロニクスさんが提供しているドライバ」ですが、リンクが切れています。
ルネサスさんのHPで探したのですが見当たりません。提供されなくなったのでしょうか?
合併に伴ってページが移動されて、なくなったようです。』
となっており、
さらに
『トラ技78Kマイコン用の64bit対応版ドライバは、
「J-Writer」のページに64bit版ドライバを用意したので、
それをお使いください。
詳しくは、当ブログの下記の記事をご覧ください。
http://nahitafu.cocolog-nifty.com/nahitafu/2012/02/j-writerwindows.html』
おお神だ。世の中には神様がおられるんですね。
早速ダウンロードして解凍。
拡張子sysとかinfとかのファイルが現れた。

これがドライバー
これをデバイスマネージャーで「不明なデバイス」扱いされていたあいつに認識させればOKなはず。

まず、フォルダーをまるごとc:¥jwriter_trg78k_64にコピー
不明なデバイスで右クリック→ドライバーの更新→コンピューターを参照して・・・検索
c:¥jwriter_trg78k_64を指定。
出たー「このデバイスをソフトウェアをインストールしますか?」
もちろんインストールする

ドライバーが正常に更新されました
出来たー

デバイスドライバーで見るとCOM10で認識。

テラタームで接続。
何も出ないので改行を送ってみるとおおーTragiBIOS Version1.20Aと表示。
トラ技バイオスがちゃんと見えました。

でも、でもでも。
何を入力しても、Unknown Commandって言われちゃう。
それも当然。俺コマンド知らないし。
調べると
load,flash,wr,dump,jmp
の5コマンドが使えるとの事。
適当に入れていくと文字化けするが、これはTeraTermの設定がSJISになっていない為。
SJISにしておけば日本語が表示される。
例えばjmp 100とすれば「ユーザプログラムを実行します」と言って固まる。
つまり、プログラムが何も書かれていないので暴走したのだろう。
固まった場合は一度USBから外してもう一度USBに入れてあげると治る。
loadとやるとDrop a HEX fileとなって、TeraTermに何かファイルをドロップすると
errorとなってしまった。
何がエラーなのかわからないが
色々調べると
loadしてjmp f000してあげると良いっぽい。
じゃ、何をロードすればいいのだろうか?
コンパイル済みの実行ファイルだろうなー。
それはトラ技の本を買うと付いてくるCDの中ですよね。
それと、RENESASとかからコンパイラーが手に入ったとしても
トラ技bios用のライブラリも無いと駄目だし。
結局全部セットで入ってくるあれを注文して本日は終了とします。

届いたら続きやりますね。
オーディオタイマーで換気
最近寒いですよね。
寒いときはあまり考えないんですが、去年
服にカビが生えてしまって着れなくなる事件が発生しまして。
それ以来カビに関して調べていたのですが、
湿気と温度がポイントで、
湿気がたまらなくすることがポイントの様です。
それで、なんですが、
自分が普段部屋にいない平日日中の時間に自動的に喚起してくれるシステムを考えたわけです。
毎日セットするのも面倒なので、
月から金まで選択可能で、ONとOFFにする時間も選択可能なやつ。
それの作成記事です。
昔々音楽を聴くのがカセットだった時代に、
FMラジオとか録音するのにオーディオタイマーっていうのがあって、
ラジオが始まる時間になると自動的に電源が入って録音を開始してくれて、
終わる時間になると電源を切って録音が終了するっていうやつ。
あれみたいなものです。
で、完成の写真とその動画です。

■機能
機能的は以下の5個にしました。
・週間繰り返しモードで指定した曜日の指定した時間だけONにする
・ずっとONにする
・ずっとOFFにする。
・一定期間経過後にONにしてその後はずっとONにする
・一定時間経過後にOFFにしてその後はずっとOFFにする
■操作
黄ボタンがSET
緑ボタンがSELECT
最初電入れた直後は現在時刻を入力
緑で選択して、黄で決定していきます。
年→月→日→時→分 をそれぞれ決定すると
通常の時刻表示に切り替わります。
時刻表示から黄ボタン押すとモード切替に入って
緑ボタンを押すと
WEEKLY→AlWayON→AlWayOFF→AfterON→AfterOFFと切り替わります。
表示の意味は
WEEKLY :
週間繰り返しモードで指定した曜日の指定した時間だけONにする
AlWayON
ずっとONにする
AlWayOFF
ずっとOFFにする。
AfterON
一定期間経過後にONにしてその後はずっとONにする
AfterOFF
一定時間経過後にOFFにしてその後はずっとOFFにする
モードを決めて黄ボタンを押し、画面の表示に従って追加の操作をしていきます。
時計表示の時に緑ボタンを押すと液晶のバックライトを消します。
もう一度何かを押すとバックライトが点灯します。
■回路図

■プログラム
バグっていたので
こちらの記事を参考にしてね。→https://ameblo.jp/fc2miha/entry-12833772963.html
以下はバグっています。
#include <stdio.h>
#include <time.h>
#include <LiquidCrystal.h>
//LCD arduino
// 4(rs) D12
// 5(rw) D11
// 6(enable) D10
// 11(db4) D5
// 12(db5) D4
// 13(db6) D3
// 14(db7) D2
LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
//
//D8をSSRへ接続
//
struct BUTTON
{
int value;
int flag;
int input_pin;
};
struct BUTTON bt_select;
struct BUTTON bt_enter;
int lcd_back_light = 1;
void button_initialize()
{
memset(&bt_select, NULL, sizeof(bt_select));
bt_select.input_pin = 6;
memset(&bt_enter, NULL, sizeof(bt_enter));
bt_enter.input_pin = 7;
}
void tick_button(struct BUTTON *bt)
{
int value = digitalRead(bt->input_pin);
if(value!=bt->value){
bt->value = value;
bt->flag = 1;
// if(value==HIGH){
// digitalWrite(13, HIGH);
// lcd_back_light = 1;
// }
delay(10);
}
}
int get_button(int *status, struct BUTTON *bt)
{
if(bt->flag == 1){
*status = bt->value;
bt->flag = 0;
return 1;
}
else{
return 0;
}
}
void setup()
{
Serial.begin(9600);
delay(620);
Serial.print("AcPowerControler version0.1 ¥n");
//
button_initialize();
alarm_initialise();
//lcd back light
digitalWrite(13, HIGH);
//SSR
pinMode(8, OUTPUT);
digitalWrite(8, LOW);
//Input
pinMode(6, INPUT); //enter button
pinMode(7, INPUT); //select button
//back light
pinMode(13, OUTPUT); //LCD back light
//LCD
lcd.begin(2,16);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("AcPowerControler");
delay(500);
//
setup_time_input();
}
void setup_time_input()
{
int YYYY = input("input year?", 4, 2020, 2020, 2100);
int MM = input("input month?", 2, 1, 1, 12);
int DD = input("input day?", 2, 1, 1, 31);
int hh = input("input hour?", 2, 0, 0, 23);
int mm = input("input minits?", 2, 0, 0, 59);
initDateTime(YYYY, MM, DD, hh, mm);
lcd.clear();
}
int input(char *str, int keta, int def, int mMin, int nMax)
{
lcd.clear();
lcd.print(str);
int i = 0;
char buf[17];
char ch[2];
//
lcd.blink();
//
int value = def;
int flag = 1;
int status;
while(1){
tick_button(&bt_select);
tick_button(&bt_enter);
if(flag==1){
//
if(keta==4){
sprintf(buf, "%04d", value);
}
if(keta==2){
sprintf(buf, "%02d", value);
}
if(keta==1){
sprintf(buf, "%01d", value);
}
lcd.setCursor(0, 1);
lcd.print(buf);
lcd.setCursor(keta-1, 1);
flag = 0;
}
if(get_button(&status, &bt_enter)){
if(status==HIGH){
return value;
}
}
if(get_button(&status, &bt_select)){
if(status==HIGH){
flag = 1;
value++;
if(value>nMax){
value = mMin;
}
}
}
}
}
void lcd_print(int x, int y, char *str)
{
lcd.setCursor(x, y);
lcd.print(str);
}
struct DateTime {
time_t base_time;
unsigned long BaseMillis;
unsigned long base_startup_keika_sec;
};
struct DateTime date_time;
void initDateTime(int YYYY, int MM, int DD, int hh, int mm)
{
struct tm now;
now.tm_year = YYYY-1900; /* 2001年 */
now.tm_mon = MM-1; /* 5月 */
now.tm_mday = DD; /* 20日 */
now.tm_wday = 0; /* 日曜日 */
now.tm_hour = hh; /* 14時 */
now.tm_min = mm; /* 20分 */
now.tm_sec = 0; /* 00秒 */
now.tm_isdst= -1; /* 夏時間無効 */
date_time.base_time = mktime(&now);
if (date_time.base_time == (time_t) - 1) { //error
date_time.base_time = 0;
}
date_time.BaseMillis = millis();
date_time.base_startup_keika_sec = date_time.BaseMillis / 1000;
}
struct HHMM{
int HH;
int MM;
int YOUBI;
};
struct HHMM hhmm;
unsigned long getSec()
{
//現在時刻累積秒を求める
unsigned long now_startup_keika_sec = millis() / 1000;
return date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
}
void getDateTime(struct HHMM *hhmm)
{
//現在時刻累積秒を求める
unsigned long now_startup_keika_sec = millis() / 1000;
time_t now_time = date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
//localtimeにする
//now_time = now_time + 60 * 60 * 9;
struct tm *time_inf;
time_inf = localtime(&now_time);
hhmm->HH = time_inf->tm_hour;
hhmm->MM = time_inf->tm_min;
hhmm->YOUBI = time_inf->tm_wday;
}
void getDateTimeStr(char *buf1, char *buf2)
{
//現在時刻累積秒を求める
unsigned long now_startup_keika_sec = millis() / 1000;
time_t now_time = date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.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;
char *wday[] = {"SUN","MON","TUE","WED","TUE","FRI","SAT"};
sprintf(buf1, "%04d/%02d/%02d %s", year, month, day, wday[time_inf->tm_wday]);
sprintf(buf2, "%02d:%02d:%02d", hour, minute, second);
}
void tick_time()
{
unsigned long now = millis();
if(now < date_time.BaseMillis){
date_time.base_startup_keika_sec = ((4294967295 - date_time.BaseMillis) + now)/1000;
date_time.BaseMillis = now;
}
}
struct ALARM
{
int mode; // 0=week, 1=alway on, 2=alway off, 3=after on, 4=after off
int SUN;
int MON;
int TUE;
int WED;
int THU;
int FRI;
int SAT;
int start_hh;
int start_mm;
int end_hh;
int end_mm;
int after_on_hh;
int after_off_hh;
unsigned long setSec;
};
struct ALARM alarm;
int flag = 0;
unsigned long nCount = 0;
void loop()
{
nCount++;
//lcd.noCursor();
tick_time();
tick_button(&bt_select);
tick_button(&bt_enter);
//LCD back light
if(lcd_back_light==1){
digitalWrite(13, HIGH);
}
else{
digitalWrite(13, LOW);
}
//DEBUG
//int kakeru = 60; //DEBUG
int kakeru = 3600; //本番向け
//
unsigned long nowSec = getSec();
//表示
if(alarm.mode==0 || alarm.mode==1 || alarm.mode==2){ //week alway
char date[20];
char time[20];
getDateTimeStr(date, time);
lcd_print(0, 0, date);
lcd_print(0, 1, time);
if(alarm.mode==0){
lcd_print(8, 1, " weekly ");
}
if(alarm.mode==1){
lcd_print(8, 1, " all ON ");
}
if(alarm.mode==2){
lcd_print(8, 1, " all OFF ");
}
}else
if(alarm.mode==3 || alarm.mode==4){
int sec;
if(alarm.mode==3){
sec = alarm.setSec+(alarm.after_on_hh * kakeru) - nowSec;
}
else{
sec = alarm.setSec+(alarm.after_off_hh * kakeru) - nowSec;
}
// Serial.print("sec=");
// Serial.println(sec);
if(sec<0){
sec=0;
}
int hh = sec / 3600;
int mm = (sec - (hh * 3600))/60;
int ss = sec % 60;
char buf[17];
sprintf(buf, "%02d:%02d:%02d", hh, mm, ss);
lcd_print(0, 0, buf);
if(alarm.mode==3){
lcd_print(8, 1, " afterON ");
}
if(alarm.mode==4){
lcd_print(8, 1, "afterOFF");
}
}
lcd.setCursor(1, 0);
//現在時刻取得
struct HHMM hhmm;
getDateTime(&hhmm);
//日付跨ぎの補正
int alarmStartHHMM = (alarm.start_hh * 100 + alarm.start_mm) ;
int alarmEndHHMM = (alarm.end_hh * 100 + alarm.end_mm);
int nowHHMM = (hhmm.HH * 100 + hhmm.MM);
if(alarmStartHHMM>alarmEndHHMM){
alarmEndHHMM = alarmEndHHMM + 2400;
}
if(alarmStartHHMM>nowHHMM){
nowHHMM = nowHHMM + 2400;
}
//
if(alarm.mode==0){ //week
//
if((hhmm.YOUBI==0 && alarm.SUN==1)
||(hhmm.YOUBI==1 && alarm.MON==1)
||(hhmm.YOUBI==2 && alarm.TUE==1)
||(hhmm.YOUBI==3 && alarm.WED==1)
||(hhmm.YOUBI==4 && alarm.THU==1)
||(hhmm.YOUBI==5 && alarm.FRI==1)
||(hhmm.YOUBI==6 && alarm.SAT==1)){
/*
* if( (alarm.start_hh * 100 + alarm.start_mm) <= (hhmm.HH * 100 + hhmm.MM)
&& (hhmm.HH * 100 + hhmm.MM) < (alarm.end_hh * 100 + alarm.end_mm)){
*/
if( alarmStartHHMM <= nowHHMM && nowHHMM < alarmEndHHMM){
digitalWrite(8, HIGH);
}
else{
digitalWrite(8, LOW);
}
}
else{
digitalWrite(8, LOW);
}
}else
if(alarm.mode==1){ //alway on
digitalWrite(8, HIGH);
}else
if(alarm.mode==2){ //alway off
digitalWrite(8, LOW);
}else
if(alarm.mode==3){ //after on
if(alarm.setSec+(alarm.after_on_hh * kakeru) >= nowSec){
digitalWrite(8, LOW);
}
else{
digitalWrite(8, HIGH);
}
}else
if(alarm.mode==4){ //after off
if(alarm.setSec+(alarm.after_off_hh * kakeru) <= nowSec){
digitalWrite(8, LOW);
}
else{
digitalWrite(8, HIGH);
}
}
//
int status;
if(get_button(&status, &bt_select)){
if(status==HIGH){
//alarm_input();
if(lcd_back_light==0){
lcd_back_light = 1;
}
else{
lcd_back_light = 0;
}
}
}
if(get_button(&status, &bt_enter)){
if(status==HIGH){
alarm_input();
}
}
delay(10);
}
void alarm_initialise()
{
memset(&alarm, NULL, sizeof(struct ALARM));
alarm.SUN = 1;
alarm.MON = 1;
alarm.TUE = 1;
alarm.WED = 1;
alarm.THU = 1;
alarm.FRI = 1;
alarm.SAT = 1;
alarm.after_on_hh = 1;
alarm.after_off_hh = 1;
}
void alarm_input()
{
//lcd back light
digitalWrite(13, HIGH);
//
alarm.mode = select_input5("mode :", "weekly ", "always on ", "always off", "after on ", "after off ", alarm.mode);
if(alarm.mode==0){
//weekly
alarm.SUN = select_input("[SUN] : ", " ", "ON", alarm.SUN);
alarm.MON = select_input("[MON] : ", " ", "ON", alarm.MON);
alarm.TUE = select_input("[TUE] : ", " ", "ON", alarm.TUE);
alarm.WED = select_input("[WED] : ", " ", "ON", alarm.WED);
alarm.THU = select_input("[THU] : ", " ", "ON", alarm.THU);
alarm.FRI = select_input("[FRI] : ", " ", "ON", alarm.FRI);
alarm.SAT = select_input("[SAT] : ", " ", "ON", alarm.SAT);
alarm.start_hh = input("start hour", 2, alarm.start_hh, 0, 23);
alarm.start_mm = input("start minits", 2, alarm.start_mm, 0, 59);
alarm.end_hh = input("end hour", 2, alarm.end_hh, 0, 23);
alarm.end_mm = input("end minits", 2, alarm.end_mm, 0, 59);
}
if(alarm.mode==3){
//after on
alarm.after_on_hh = input("after hour", 1, alarm.after_on_hh, 1, 9);
alarm.setSec = getSec();
Serial.print("alarm.setSec=");
Serial.println(alarm.setSec);
}
if(alarm.mode==4){
//after off
alarm.after_off_hh = input("after hour", 1, alarm.after_off_hh, 1, 9);
alarm.setSec = getSec();
}
lcd.clear();
lcd_back_light = 1;
}
int select_input(char *str, char *select1, char *select2, int def)
{
lcd.clear();
lcd.print(str);
int value = def;
char buf[17];
char select[17];
//
lcd.blink();
//
strcpy(buf, "");
strcpy(select, select1);
int flag = 1;
int status;
while(1){
tick_button(&bt_select);
tick_button(&bt_enter);
if(flag==1){
if(value==0){
sprintf(buf, "%s", select1);
}
else{
sprintf(buf, "%s", select2);
}
lcd.setCursor(0, 1);
lcd.print(buf);
lcd.setCursor(strlen(buf)-1, 1);
flag = 0;
}
if(get_button(&status, &bt_enter)){
if(status==HIGH){
return value;
}
}
if(get_button(&status, &bt_select)){
if(status==HIGH){
flag = 1;
if(value==0){
value = 1;
}
else{
value = 0;
}
}
}
}
return 0;
}
int select_input5(char *str, char *select1, char *select2, char *select3, char *select4, char *select5, int def)
{
lcd.clear();
lcd.print(str);
int value = def;
char buf[17];
char select[17];
//
lcd.blink();
//
int flag = 1;
int status;
while(1){
tick_button(&bt_select);
tick_button(&bt_enter);
if(flag==1){
if(value==0){
sprintf(buf, "%s", select1);
}else
if(value==1){
sprintf(buf, "%s", select2);
}else
if(value==2){
sprintf(buf, "%s", select3);
}else
if(value==3){
sprintf(buf, "%s", select4);
}
else{
sprintf(buf, "%s", select5);
}
lcd.setCursor(0, 1);
lcd.print(buf);
lcd.setCursor(strlen(buf)-1, 1);
flag = 0;
}
if(get_button(&status, &bt_enter)){
if(status==HIGH){
return value;
}
}
if(get_button(&status, &bt_select)){
if(status==HIGH){
flag = 1;
if(value==0){
value = 1;
}else
if(value==1){
value = 2;
}else
if(value==2){
value = 3;
}else
if(value==3){
value = 4;
}
else{
value = 0;
}
}
}
}
return 0;
}
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()
{
}
WioLTE-analogA4でdigitalReadすると何ボルトで変化するのかテスト。
前の記事ではWioLTEのアナログポートで電圧を測定してみました。
https://ameblo.jp/fc2miha/entry-12833772941.html
今度は同じ回路でソフトだけ書き替えて何ボルトが境界なのかについて探ってみたいと思います。
■回路図

前と同じ。手抜きではない。
■映像でどうぞ。
■境界値について。
上から下に行くときは1.4Vぐらいで変化しています。
下から上に行くときは1.7Vぐらいで変化しています。
あるていど現在の値を保持するシュミットトリガ的な回路が入っているようですね。
境界の所でHighとLowが行ったり来たりすると結構面倒なんで有難いと言えば有難いです。
■プログラム
#include
WioLTE Wio;
void setup() {
delay(620);
SerialUSB.println("start");
SerialUSB.println("Wio.Init()");
Wio.Init();
SerialUSB.println("Wio.PowerSupplyGrove(true)");
Wio.PowerSupplyGrove(true);
delay(500);
SerialUSB.println("pinMode()");
pinMode(WIOLTE_A4, INPUT);
}
void loop()
{
int value = digitalRead(WIOLTE_A4);
if(value==HIGH){
SerialUSB.println("HIGH");
}
else{
SerialUSB.println("LOW");
}
delay(100);
}
■風景

■ちなみに、デジタルのd38でanalogReadやってみた件。
こちらはさすがにデジタル専用のピンになっているようでして。0しか出てきませんでした。
WioLTEで電圧を測定するって記事です。
WioLTEにはseeedさんのwikiにあるようにアナログの読み込みが4つあります。
https://seeedjp.github.io/Wiki/Wio_LTE_for_Arduino/Hardware-ja.html
A4の所に2チャンネルとA6の所に2チャンネルです。
今回はA4のチャンネルに可変抵抗を接続して電圧を表示してみたいと思います。
arduinoもそうなんですが、
pinModeを使用してアナログ入力に設定してanalogReadで値を読み出します。
この時に0~1023までの数値を返してきます。
0ボルトが0で3.3ボルトが1023です。
ですので、以下のような簡単な回路を用意して
可変抵抗をまわして電圧を変化させつつ読み取った値を画面に表示してみます。
注意点としては3.3V以上を測定する場合は分圧させる必要があります。
例えば12Vを測定する場合は
例として
抵抗器を2本使って
1MΩと250KΩとして以下の接続にして
12V電源(+) ---- 1M抵抗 ---- 測定点 ---- 250KΩ ---- GND(-)
測定点を測定。
今のプログラムの計算結果を5倍すれば正しい値になるはずです。
こうする事でバッテリ残量を測定して警告を上げる装置が出来ますね。
■回路図

■映像でどうぞ。
■真ん中の青いのが可変抵抗ね。

■プログラム
#include
WioLTE Wio;
void setup() {
delay(620);
SerialUSB.println("start");
SerialUSB.println("Wio.Init()");
Wio.Init();
SerialUSB.println("Wio.PowerSupplyGrove(true)");
Wio.PowerSupplyGrove(true);
delay(500);
SerialUSB.println("pinMode()");
pinMode(WIOLTE_A4, INPUT_ANALOG);
pinMode(WIOLTE_A5, INPUT_ANALOG);
}
void loop()
{
int value1 = analogRead(WIOLTE_A4);
float volt1 = value1 * 3.3 / 1024;
SerialUSB.print("volt1=");
SerialUSB.println(volt1);
int value2 = analogRead(WIOLTE_A5);
float volt2 = value2 * 3.3 / 1024;
SerialUSB.print("volt2=");
SerialUSB.println(volt2);
delay(1000);
}
■風景

WioLTEでSMS通信します。
前にWio-LTEでいろいろ関数を試してみた。
https://ameblo.jp/fc2miha/entry-12833772938.html
の中でSMS使えるSIMが無かったので
出来なかったSMSの送受信でしたがSMS機能を追加したものを入手しましたのでお試しです。
■スケッチ書く
ファイルのスケッチ例からWioLTE for arduino→smsの所
ここから持ってきて適当に組み合わせて出来ました。
使用するのは以下の関数です。
Wio.Init(); // 初期化
Wio.PowerSupplyLTE(true); // LTEの電源ON
Wio.TurnOnOrReset() // LTEモジュールを起動
Wio.SendSMS(PHONE_NUMBER, "first message") //SMS送信
Wio.ReceiveSMS(buf, sizeof(buf), number, sizeof(number)) //SMS受信
Wio.DeleteReceivedSMS() //SMSのメッセージを一つ削除
■DMMのSIM追加のプラン料金は以下の感じです。
□変更後プランの内容
シェアコース データSIM 1GBプラン
- 基本料金:528円
□SIMカード追加内容
・データSIM / nanoサイズ
- SMSオプション:あり(165円/月)
□手数料
・SIMカード追加手数料:3300円
・SIMカード準備料:433円×1
□追加SIM利用料
・SIM1枚分: 330円/月
えっとつまり、
追加の手数料で3300円+433円で3733円
月々はSMSの165円とSIM1枚分330円
合計495円 SMS無しなら330円です。
SMSM付きで495円ならすごく安いと感じました。
■プログラム
#include <WioLTEforArduino.h>
#define PHONE_NUMBER "0x0xxxxxxxx"
WioLTE Wio;
void setup() {
char buf[4096];
delay(620);
SerialUSB.println("strat");
SerialUSB.println("Wio.Init()");
Wio.Init();
SerialUSB.println("Wio.PowerSupplyLTE(true)");
Wio.PowerSupplyLTE(true);
delay(500);
SerialUSB.println("Wio.TurnOnOrReset()");
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("error");
return;
}
delay(3000);
SerialUSB.println("Wio.SendSMS");
if (!Wio.SendSMS(PHONE_NUMBER, "first message")) {
SerialUSB.println("error");
return;
}
char number[25];
while (true) {
SerialUSB.println("Wio.ReceiveSMS()");
int strLen = Wio.ReceiveSMS(buf, sizeof(buf), number, sizeof(number));
if (strLen < 0) {
SerialUSB.println("error");
}else
if (strLen > 0){
SerialUSB.println(buf);
SerialUSB.println(number);
SerialUSB.println("Wio.DeleteReceivedSMS()");
if (!Wio.DeleteReceivedSMS()) {
SerialUSB.println("error");
}
}
//
SerialUSB.println("sleep 10 sec");
delay(10000);
}
SerialUSB.println("end");
}
void loop() {
}
金属の棒を使用した水検知センサー2 に保護回路を付けてみた。
前に作った
水にくっついたのを検知するセンサーの記事。
https://ameblo.jp/fc2miha/entry-12833772920.htm
こちら、室内で使う場合は問題ないんですが、
外に出して田んぼの水が有るか無いかを検知する場合に
何らかの原因でセンサーに電圧がかかった場合に
ATMEGAちゃんがおそらく死ぬであろう事が予想されます。
よく見ると
ATMEGAのピンをそのまま外だししてセンサーにしている恐ろしい回路ですよね。
今回は『ATMEGAが死にません様に』と願いを込めて
雷にはとても敵わなくても静電気ぐらいは防御したいので
保護回路を追加してみました。
■回路図

青い所が今回追加の回路です。
突入電流防止用に「NTCサーミスタ 突入電流抑制用 Φ22mm 1Ω」を使います。
http://akizukidenshi.com/catalog/g/gP-12543/
大きな電圧がかかった時様にバリスタ「サージ吸収素子 Z2018」
http://akizukidenshi.com/catalog/g/gI-09427/
きちんと勉強したわけではないのでよく判らなくて書いております。
いちおうサーミスタは回路に直列に入れて使うものであり、
バリスタは回路に並列に入れるものであります。
サーミスタは電源ONの時に突発的に電流が流れるのを防ぐと解説にあるので
それを信じます。
それからバリスタですが、
電圧が多くかかった場合に抵抗値が下がって
電流が急激に流れるという代物でありまして
つまり、大電流が流れるとATEGAまで電気が来ない様に出来るという仕掛けにしたい。
さらにATMEGAに流れる前に抵抗を入れることで分圧されてATMEGAの故障を防ぎたいと考えております。
■試験
試しに、前回記事と同様に動くことが確認できましたので
今回の目的の保護回路としてはどうか?
試験の方法としてはSENSOR1とSENSOR2の間に電池をつないでみようと。
電池をつないでみて、さらにその時のATMEGAのD7の所の電圧を図ってみた。
その結果。
SENSOR1-2間の電圧 → D7の電圧
0V(直接短絡した場合)→ 3.27V
1.5V(電池1本) → 3.98V
3.0V(電池2本) → 4.02V
6.0V(電池4本) → 4.06V
9.0V(006P電池) → 4.08V
18V(006P2本) → 4.11V
30V(006P2本+PC電源12V) → 3.1V
うーん、わかりにくいけど30V入れたときに下がっております。
バリスタの効果がこのへんから効いて来ている見たいですね。
もっともっと電圧を上げてみたいけどー、
30V入れたときにバチッって火花が出たので実験はここまでとしました。
■プログラム
const int LED = 13;
const int SENSOR = 7;
void setup() {
pinMode(LED, OUTPUT);
pinMode(SENSOR, INPUT);
}
void loop() {
int val = digitalRead(SENSOR);
if( val == HIGH ){
digitalWrite(LED, HIGH);
}
else{
digitalWrite(LED, LOW);
}
}
Wio-LTEでいろいろ関数を試してみた。
WioLTEのリファレンスマニュアルを見つけたので片っ端から使ってみる。
https://seeedjp.github.io/Wiki/Wio_LTE_for_Arduino/Reference-ja.html
■準備
SDのライブラリが必要になります。
https://github.com/SeeedJP/SDforWioLTE
からダウンロードして
ArduinoIDEでスケッチ→ライブラリをインクルード→ZIP形式のライブラリをインストール
としてファイルを指定する。
■自己評価と今回判った事
WioLTEなかなかよくできています。
手持ちの0SimのSimはSMSも通話も出来ないタイプなので
SendSMSとかSMS関連が出来ませんでした。これは残念な点です。
たぶんその為だと思いますが電話番号も表示されませんでした。
日本版のWioLTEはGPS関連機能が使用できません。
こちらも残念な点です。
ヘッドホンジャックが搭載されているのでこれを使えば通話ができると思い
リファレンスマニュアルを隅から隅まで見たんですがそれらしき関数は見つかりません。
つっこんでググってみたんですが、seeedさんのblogに関連記事見つけました。
https://www.seeed.co.jp/blog/201712-wio-lte-user-event/
日本版のWioLTEはLTEしか搭載していなくてしかも音声通話をLTEでやるにはVoLTEという技術が必要なんだけど
そのライセンスが別売りなので出来ないって解説でした。
GetLocationの解説が「接続している基地局の位置(緯度、経度)を取得します」となっているので
基地局の座標が取れるのは割と面白いと思ったんですがerrorになってしまいました。
現在時刻が取得できたことと電波の強さが表示されるのが面白いかなと思います。
SDの読み書きも普通にできましたし総合では結構使える印象です。
以下今回判った事
IMEI:国際移動体装置識別番号( International Mobile Equipment Identifier)
IMSI:全ての携帯電話ユーザーに割り当てられている一意な識別番号(International Mobile Subscriber Identity)
ICCID:SIM(USIM、UIM)カードに付与された固有の番号(Integrated Circuit Card ID)
CSネットワーク:回線交換方式(CS:Circuit Switched)音声通信は回線交換ネットワーク
PSネットワーク:パケット交換方式(PS:Packed Switched)データ通信はパケットネットワーク
■実行結果
start
Wio.Init()
Wio.PowerSupplyLed(true)
Wio.PowerSupplyGrove(true)
Wio.PowerSupplySD(true)
Wio.LedSetRGB(1, 1, 1)
Wio.PowerSupplyLTE(true)
Wio.TurnOnOrReset()
Wio.Sleep()
Wio.Wakeup()
Wio.GetIMEI(buf, sizeof(buf))
866522040036762
Wio.GetIMSI(buf, sizeof(buf))
440103165133274
Wio.GetICCID(buf, sizeof(buf))
8981100024630310060
Wio.GetPhoneNumber(buf, sizeof(buf))
Wio.GetReceivedSignalStrength()
-59dBm
Wio.GetTime(&now)
2020/01/12 11:13:01
Wio.WaitForCSRegistration()
Wio.WaitForPSRegistration()
Wio.Activate(APN, USERNAME, PASSWORD)
HttpGet
60
SD.begin()
SD.remove(filename)
SD.open()
test write 1
test write 2
test write 3
setup() end
■プログラム
#include <WioLTEforArduino.h>
#include <stdio.h>
#if defined ARDUINO_ARCH_STM32F4
#include <SD.h> // https://github.com/Seeed-Studio/SD
#elif defined ARDUINO_ARCH_STM32
#include <SDforWioLTE.h> // https://github.com/SeeedJP/SDforWioLTE
#endif
WioLTE Wio;
#define URL "http://xxxxx.japaneast.cloudapp.azure.com:8000/ondo/WIO-LTE/25.5/35.5/1000/"
void setup() {
delay(620);
SerialUSB.println("start 1");
//Wio LTEを初期化する
//初期化直後は、LTEモジュール電源・Groveコネクター電源がオフになる
SerialUSB.println("Wio.Init()");
Wio.Init();
//フルカラーLEDへの電源供給をオン/オフする
SerialUSB.println("Wio.PowerSupplyLed(true)");
Wio.PowerSupplyLed(true);
//Groveコネクター D20/A4/A6/I2C/UARTの電源供給をオン/オフする
SerialUSB.println("Wio.PowerSupplyGrove(true)");
Wio.PowerSupplyGrove(true);
//マイクロSDの電源供給をオン/オフする
SerialUSB.println("Wio.PowerSupplySD(true)");
Wio.PowerSupplySD(true);
//Wio LTE上のフルカラーLEDを点灯する 消灯したいときは、red/green/blue全てに0を指定する
SerialUSB.println("Wio.LedSetRGB(1, 1, 1)");
Wio.LedSetRGB(1, 1, 1);
//Wio LTE上のLTEモジュールの電源供給をオン/オフする
//LTEモジュールは電源供給オンの後に、
//起動操作(TurnOnOrReset)する事で利用可能になる
//本関数を実行した後の、LTEモジュールの操作は0.5秒以上待つこと
SerialUSB.println("Wio.PowerSupplyLTE(true)");
Wio.PowerSupplyLTE(true);
delay(500);
//Wio LTE上のLTEモジュールを起動する
//LTEモジュールが電源オンしていないときは電源オン
//電源オンしているときはリセットする
//本関数の実行前に、LTEモジュールへ電源供給(PowerSupplyLTE)が必要
SerialUSB.println("Wio.TurnOnOrReset()");
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("error");
return;
}
delay(500);
//
//SerialUSB.println("Wio.PowerSupplyLTE(false)");
//Wio.PowerSupplyLTE(false);
//delay(500);
//Wio LTE上のLTEモジュールを停止する
//SerialUSB.println("Wio.TurnOff()");
//if (!Wio.TurnOff()){
// SerialUSB.println("error");
// return;
//}
//LTEモジュールを省電力モードにする
//本関数を実行した後に、LTEモジュールを使うときは
//省電力モードから復帰(Wakeup)する必要がある
SerialUSB.println("Wio.Sleep()");
Wio.Sleep();
delay(5000);
//LTEモジュールを省電力モードから通常モードに復帰する
SerialUSB.println("Wio.Wakeup()");
if (!Wio.Wakeup()) {
SerialUSB.println("error");
return;
}
//Wio LTEのIMEIを取得する
char buf[4096];
memset(buf, '¥0', sizeof(buf));
SerialUSB.println("Wio.GetIMEI(buf, sizeof(buf))");
int n = Wio.GetIMEI(buf, sizeof(buf));
if(n>=0){
SerialUSB.println(buf);
}
//Wio LTEに取り付けられたSIMのIMSIを取得する
memset(buf, '¥0', sizeof(buf));
SerialUSB.println("Wio.GetIMSI(buf, sizeof(buf))");
n = Wio.GetIMSI(buf, sizeof(buf));
if(n>=0){
SerialUSB.println(buf);
}
//Wio LTEに取り付けられたSIMのICCIDを取得する
memset(buf, '¥0', sizeof(buf));
SerialUSB.println("Wio.GetICCID(buf, sizeof(buf))");
n = Wio.GetICCID(buf, sizeof(buf));
if(n>=0){
SerialUSB.println(buf);
}
//Wio LTEに取り付けられたSIMの電話番号を取得する
memset(buf, '¥0', sizeof(buf));
SerialUSB.println("Wio.GetPhoneNumber(buf, sizeof(buf))");
n = Wio.GetPhoneNumber(buf, sizeof(buf));
if(n>=0){
SerialUSB.println(buf);
}
//LTEモジュールが受信している信号強度[dBm]を取得する
SerialUSB.println("Wio.GetReceivedSignalStrength()");
n= Wio.GetReceivedSignalStrength();
sprintf(buf, "%ddBm", n);
SerialUSB.println(buf);
//LTEモジュールが保持している日時を取得する
SerialUSB.println("Wio.GetTime(&now)");
struct tm now;
if (!Wio.GetTime(&now)){
SerialUSB.println("error");
return;
}
int year = now.tm_year + 1900;
byte month = now.tm_mon + 1;
byte day = now.tm_mday;
byte hour = now.tm_hour;
byte minute = now.tm_min;
byte second = now.tm_sec;
//UTC-->JST
hour += 9;
if (hour > 23) {
day += 1;
hour -= 24;
}
sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
SerialUSB.println(buf);
//CSネットワークに登録完了するまで待つ
//回線交換方式(CS:Circuit Switched)音声通信は回線交換ネットワーク
SerialUSB.println("Wio.WaitForCSRegistration()");
if (!Wio.WaitForCSRegistration()){
SerialUSB.println("error");
return;
}
//PSネットワークに登録完了するまで待つ
//パケット交換方式(PS:Packed Switched)データ通信はパケットネットワーク
SerialUSB.println("Wio.WaitForPSRegistration()");
if (!Wio.WaitForPSRegistration()){
SerialUSB.println("error");
return;
}
//指定したAPN、ユーザー名、パスワードを使って、データ通信を有効にする
SerialUSB.println("Wio.Activate(APN, USERNAME, PASSWORD)");
if (!Wio.Activate("so-net.jp", "nuro", "nuro")) {
SerialUSB.println("error");
return;
}
//指定したURLから、HTTP通信でGETする
SerialUSB.println("HttpGet");
int nRc = Wio.HttpGet(URL, buf, sizeof(buf), 3000);
if (nRc<0) {
SerialUSB.println("error");
}
else{
SerialUSB.println(buf);
}
//データ通信を無効にする
//SerialUSB.println("Wio.Deactivate()");
//if (!Wio.Deactivate()){
// SerialUSB.println("error");
// return;
//}
//接続している基地局の位置(緯度、経度)を取得する
//double longitude;
//double latitude;
//SerialUSB.println("Wio.GetLocation(&longitude, &latitude)");
//if (!Wio.GetLocation(&longitude, &latitude)){
// SerialUSB.println("error");
// return;
//}
//sprintf(buf, "longitude=%lf, latitude=%lf", longitude, latitude);
//SerialUSB.println(buf);
//CPUをリセットする
//SerialUSB.println("Wio.SystemReset()");
//Wio.SystemReset();
//SD初期化
SerialUSB.println("SD.begin()");
if (!SD.begin()) {
SerialUSB.println("error");
return;
}
//ファイル削除
char *filename="test.txt";
SerialUSB.println("SD.remove(filename)");
SD.remove(filename);
//ファイル書き込み
File f;
SerialUSB.println("SD.open()");
f = SD.open(filename, FILE_WRITE);
if (!f) {
SerialUSB.println("error");
return;
}
f.println("test write 1");
f.println("test write 2");
f.println("test write 3");
f.close();
//ファイル読み込み
f = SD.open(filename, FILE_READ);
if (!f) {
SerialUSB.println("error");
return;
}
while (f.available()) {
char c = f.read();
SerialUSB.write(c);
}
f.close();
SerialUSB.println("setup() end");
}
void loop() {
// put your main code here, to run repeatedly:
}
Wio-LTEでUART通信。
WioLTEのUARTポートを使用して通信する方法の記事です。
WioLTEを使うのは初めてなんで。
とりあえず、Groveのコネクタが6個ついているので
手始めにUART接続して実験しました。
■基礎知識
WioLTEのUARTコネクタの並びですが、
上から
RX
TX
3.3-B
GND
となります。
初期状態では3.3-Bの電源はOFFだそうです。
■回路
PC----USBUART変換(※1)----WioLTE----PC
※1
USBUART変換はSWITCHSCIENCEのUSB-Serial-FTDIです。
WioLTEwoPCの間はWioLTE付属のケーブルでOKです。

■実行風景
■プログラム解説
setupの中のdelayは自分のPCとWioLTEの接続でWioLTEのRSTボタンを押した場合に310ミリ秒までのメッセージが
TeraTermで取得できなかったので余裕をもってその2倍待ってから処理開始しています。
readline
ハードウェアシリアルはSerialと書けばよいらしい。
定義はWioLTEforArduino.hにおまかせみたいです。
whileでまわしてavailableでデータがあった時にreadで読み込んで
str変数にため込み¥nが来たらbreakします。
結果はstr変数と戻り値にバイト数を返します。
JUGEMテーマ:Arduino
writeline
sizeの数だけまわしてあバイトずつSerialにwriteしてあげます
loop
ぐるぐる回して
readlineで読んだものをSerialUSBにprintしながら
writelineでハードウェアシリアルにechoを返しています。
■プログラム
#include <WioLTEforArduino.h>
#include <stdio.h>
void setup()
{
delay(620);
Serial.begin(9600);
SerialUSB.println("start");
}
int readline(char *str, int size)
{
memset(str, '¥0', sizeof(str));
int i=0;
while(Serial.available()) {
char c = Serial.read();
str[i]=c;
if(c=='¥n'){
break;
}
i++;
if(i>=size-1){
break;
}
}
return(i);
}
void writeline(char *str, int size)
{
for(int i=0;i<size;i++){
Serial.write(str[i]);
}
}
void loop()
{
char buffer[4096];
while(1){
int n = readline(buffer, sizeof(buffer));
if(n!=0){
SerialUSB.print(buffer);
writeline(buffer, n);
}
}
}
Wio-LTEでAzure上のクラウドサーバー通信(HttpGet)
Azule上で構築したWEBサーバーに温度と湿度を送信するプログラムの
クライアント側のHttp通信部分だけ作ってみた。
温度の読み取り部分は実装していない。1分ごとに固定値を送信する。
■使用するもの
WioLTEJapanVersion
nanoSim ゼロシム(0sim) (sony nuro)

■SoracomでなくてもOK
あっちこっちでsoracomのsimを使用した記事が多かったんですが、
WioLTEのソースを見ても特にSoracom縛りはなさそうだったので
手持ちのZeroSimにて試してみました。
■SIMの取り付け。


nanoSIMは写真左の所。に入れます。
写真は型枠の下の所の詰めを引き出して
型枠に下から差し込む感じで入れます。
差し込み方向はsimの端子のある方を写真の下面にして、
字の書いてある方を写真の上面。
角の欠けている方を奥にして入れてください。
■苦労したことハマった事。
今回は電力ではまりました。
いつも通りパソコンに直接機器を接続せず、間にUSBハブを挟んで行っておりました。
これはUSBからの電流が原因でパソコンが故障するのを防ぐためです。
接続図
PC---USBハブ---WioLTE と
何度やってもWio.TurnOnOrReset()の所でエラーになります。
PowerSupplyCellular()とか使ってみたり。
前段でTurnOff()をやってみたり、
GetLastError等いろいろやりながらWebでググってみると
電力が足りない場合にリセットがかかるという記事を見つけました。
でも別にリセットかかっているわけでもないしなーと思いつつも、
USBハブを交換してみたりしてたんですが、最後・・・直接パソコンに接続したところ
あっけなく成功。
USBハブの2段目の電圧では駄目って事なのね。
電流も関係あるっぽいね。
ちなみに、USBの電圧計ではかってみると、
パソコン直が5.03V 電流100mA
USBの二段目が4.45V 電流90mA
でした。
開発でUSBハブを使うならば電力供給が付いたものが良いようです。
私の様に貴重な3時間を失いたくないでしょ。
■ソース
#include <WioLTEforArduino.h>
#include <stdio.h>
#define APN "so-net.jp"
#define USERNAME "nuro"
#define PASSWORD "nuro"
#define URL "http://xxxx.japaneast.cloudapp.azure.com:8000/ondo/WIO-LTE/25.5/35.5/1000/"
#define INTERVAL (60000)
WioLTE Wio;
void setup() {
delay(2000);
SerialUSB.println("start");
SerialUSB.println("Wio.Init()");
Wio.Init();
SerialUSB.println("Wio.PowerSupplyLTE(true)");
Wio.PowerSupplyLTE(true);
delay(500);
SerialUSB.println("Wio.TurnOnOrReset()");
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("error");
return;
}
delay(500);
SerialUSB.println("Wio.Activate(APN, USERNAME, PASSWORD)");
if (!Wio.Activate(APN, USERNAME, PASSWORD)) {
SerialUSB.println("error");
return;
}
SerialUSB.println("Setup end");
}
void loop() {
char buf[4096];
int status;
SerialUSB.println("HttpGet");
int nRc = Wio.HttpGet(URL, buf, sizeof(buf), 3000);
if (nRc<0) {
SerialUSB.println("error");
}
else{
SerialUSB.println(buf);
}
delay(INTERVAL);
}