2016年6月27日月曜日

ArduinoでJSON


javaでJacksonなんかをいじっていると、 "{\"name\" …なんて書いていくのは面倒くさいというか、どうせミスするのがオチなんだよ俺なんかー(血の叫び

と、つい人は不良化してしまいます。

というわけで、また楽な道を探しましたところ、良いライブラリ見つけました。

これ使いやすいですよ。ちょっとバッファを食うので、nano / unoあたりだと大きなデータは作りにくいかもしれないですが、例えばAmbient(thingspeak.comのようなクラウドサービス)に送るデータをserializeする場合なんか、これだけで書けてしまいます。dictionaryもarrayもかなり直感的に書くことができてとてもラクです。

//
//  send to Ambient
//
const int kMaxJsonBuffer = 2048;
StaticJsonBuffer<kMaxJsonBuffer> jsonBuffer;
char jsonPrintBuffer[kMaxJsonBuffer];
bool sendStorageToAmbient() {
  int n = storage.uni.datas.counter;
  JsonObject& root = jsonBuffer.createObject();
  root["writeKey"] = "*****************";    // ambientのapi write key
  JsonArray& dataArray = root.createNestedArray("data");
  for (int i = 0; i < n; i++) {
    OneData_t *pSrc = &storage.uni.datas.blocks[i];
    JsonObject& one = jsonBuffer.createObject();
    one["created"] = pSrc->created;
    one["d1"] = pSrc->temperature;
    one["d2"] = pSrc->humidity;
    one["d3"] = pSrc->pressure;
    one["d4"] = pSrc->airQuality;
    dataArray.add(one);
  }
  root["data"] = dataArray;
  Serial.print("text size = ");
  Serial.println(root.measureLength());
  root.printTo(jsonPrintBuffer, sizeof(jsonPrintBuffer));
  Serial.println(jsonPrintBuffer);
  int sent = ambient.bulk_send(jsonPrintBuffer);
  return true;
}

お勧め。

上の例ではバッファサイズを2048バイトにしているのでArduinoだと一発アウトですが、一度に送るレコード数を制限する等でカバーしてみてください。

なお、写真は特にこの記事とは関係ありませんが、次期モデルのプロトタイプ。LEDの色をちょっくら変えてみました。緑色LEDが明るすぎるので少し抵抗を大きくしないといけないの図。

2016年6月21日火曜日

450円でESPでもArduinoでも使える高精度RTC

電子工作でRTCっていうとDS1307 / 1302あたりが定番だと思うんですが、秋月で売ってるRX-8025NBって、なかなか良いと思うんです。TXCO使ったものだともっと精度の高いものもあるんですが(NXPのPCF2129など)、450円で月差13秒程度ってのはなかなかのバランスだと思います。

秋月電子のサイトから

ずっと以前に買ってあったものの使う機会がなくて部品沼に沈んでましたが、在庫リストを見ていたら出てきたので使ってみました。

配線は、SDA - SDA, SCL - SCL, Vcc - Vcc, Gnd - Gndで何のひねりもありません。動作電圧範囲が広いのでmbedでもArduino / ESP8266/ ESP-WROOM-02どれでもそのまま使えます。

欠点としては、バッテリバックアップのラインが別になっていないので、電池やスーパーキャパシタをつなぐときにはSBRダイオードなどで一手間かかってしまいます。

この点、VddとVbatが分かれているDS1307+とかPCF2129は便利なんですよね。

余談ですが、こないだPCF2129で基板を作ったんですが動作しなくて泣いています。その辺の顛末はまたいつかブログに書きます。

■ソース■

こちらの記事を参考にさせていただきました。ありがとうございます。

RTCに多いんですが、年月日時分秒すべて60進数のBCDです。通常の10進をそのままセットするとまともな値が返ってこないので、セット前 / 読み出し後に変換してやる必要があります。fromClockFormatはBCD(60進)から10進、toClockFormatは10進からBCDへの変換を行ってます。

time_t(unix時間:1970年1月1日 00:00:00.000からの経過ミリ秒)とグレゴリアン歴との変換は標準関数で済ませてしまいました。テヘペロ。

起動するとWiFiに接続してsntpから時間を取得してRTCにセットし、あとはRTCから読み込んでは時間を表示しているだけです。

このRTCにはアラームをセットして割り込みを掛けたりする機能もあるのですが、使っていません。ESP-WROOM-02でRSTにつないでやると、deep sleepの後に指定した時刻に立ち上がる、なんてこともできると思います。



いい感じで動いているからあと何個か買っておこう。

…秋月にアフィリエイトがないのが残念です(笑)。

このグラフの原因は…?

■追記 06/28■

原因わかりました。たぶん自分で書いたftoa関数に致命的な間違いがありました。何かもう「cプログラマでござい」な看板を引きずり降ろさなければいけないレベルの致命的なミス。しかも自分で発見できず人に見つけていただきまして。とほほ…。

■追記 06/23■

センサーからの読み込みルーチンの問題でした。データがジャンプしているのは整数部が繰り下がる境界のところですが、小数部と整数部の変換時間差を考慮していないルーチンのため、小数部が(39.)6度を読み、整数部が40.(0)を読みだした結果、40.6度が値として返っていたのが原因です。

スペックシートを読むとstatusビットがあり、ここでbusy(変換中)かどうかを判断すべきところでした。取り急ぎソースコードを書き換えて公開します。

また電子工作部にお助けいただきました。ありがとうございます。

…と思ったら、直ってませんでした。もうどうしましょ。何も思い付きません。

■元記事■

お風呂に浮かべたWiFi温度計のグラフがこんなことになってます。



  • 20:38, 22:40, 00:48, 03:10…とちょっと規則性があるようなないような。
  • 5分おきに計測しているが、1回だけ上がるわけではない。


という特徴があります。

うーん、何だろ? 各1回ずつならノイズってこともあるかもしれないけど複数回だし規則正しいノイズってのもヘン。計測は電池をもたせるためにDeep Sleepからの復帰で行っているけどセンサーは電池につないだままなので、立ち上がりが遅くて誤差ということはないはず。初期化のガードタイム分は待っているし…。

うーん。

2016年6月19日日曜日

ESP-WROOM-02 : SNTPのAPIで大ハマリ


ESP8266 NON OS SDKには、sntp_get_current_timestamp() というapiが用意されています。その名の通り、ntpサーバにアクセスして時間をもらってくるものです。

ところが。

これが全然値を返してくれません。0ばっかりです。sntp_init()の順番が悪いのだろうか、time zoneを設定しなければいけないのか、upd local portが必要なのだろうか、DNSが動いていないのだろうか…など、2時間ほど試したのですが、ダメでした。

が。

ふと見ると、参考にしていたReferenceはver 1.1と書いてありまして。最新の1.5.4を見ると多少サンプルが違います。1.1と違って、waitが入ってます。

… sntp_get_current_timestampでは、パケット待ってくれないんですね…。referenceにそんなこた何にも書いてねーぞー(血の叫び

以下のコードで普通に動きました。はい、お疲れ様。

uint32_t getTimestamp() {
  configTime(9 * 3600, 0, "ntp.nict.jp", NULL, NULL);

  uint32_t result = 0;
  int cnt = 0;
  while (result == 0) {
    result = sntp_get_current_timestamp();
    delay(100);
    if (++cnt > 10) {
      break;
    }
  }
  return result;
}
ESP8266のサンプルとしてESP8266WiFi/NTPClientなんてのがあるせいか、スクラッチでUDPのntpパケットを扱っている作例が多いのですが、これ使うと簡単です(ハマらなければね)。

ご活用くださいませ。

2016年6月18日土曜日

Arduino:関数への構造体渡しで大ハマリ

■訂正訂正訂正■

またまた電子工作部で教えていただきました。ダメなのはstruct typedefです。typedefをやめてstructなら問題ありません。

誤った情報を流してしまい申し訳ありませんでした。

出典のブログ主さんにもコメント欄から訂正をお願いしました。

Arduino(4) ブロック崩しを作ってみる(1)

記事より引用:
しかし残念なことに、「構造体を引数とする関数」はスケッチの中では定義できません。
これを行うには、構造体を別ファイルで定義する必要があるようです。

「原典に当たる」という基本を疎かにしたため、誤報を流布してしまいましたことをお詫び申し上げます。


■typedef渡しでエラー■

このコードをコンパイルしてみてくださひ。ESPの人はそのまま、Arduinoなら最初の3行をコメントアウトして。

extern "C" {
#include "user_interface.h"
}

typedef struct {
  float temperature;
  float humidity;
  float pressure;
  float airQuality;
} OneData;

void func(OneData *inData) {

}


void setup() {
  // put your setup code here, to run once:
}
void loop() {
  // put your main code here, to run repeatedly:
}

そうすると…

Arduino:
Arduino_Function_Sample.ino:7:11: error: variable or field 'func' declared void
Arduino_Function_Sample.ino:7:11: error: 'OneData' was not declared in this scope
Arduino_Function_Sample.ino:7:20: error: 'inData' was not declared in this scope
variable or field 'func' declared void

ESP-WROOM-02:
Arduino_Function_Sample.ino:2:11: error: variable or field 'func' declared void
Arduino_Function_Sample.ino:2:11: error: 'OneData' was not declared in this scope
Arduino_Function_Sample.ino:2:20: error: 'inData' was not declared in this scope
variable or field 'func' declared void

こんなエラーが出ます。

で、どちらでもエラーとして表示されていないLine 14-16のvoid funcをコメントアウトするとビルド通ります。

なんじゃそりゃあああ

■答え■

Arduino言語ではスケッチ中で関数の引数として構造体を渡すことはできない

そうです(出典:Arduino(4) ブロック崩しを作ってみる(1))。

今まで知りませんでした…。ヘッダなどで定義してC言語としてコンパイルすれば通りますが、うーん、dependencyが…。

しかし…どーしよう。上のソースは28行しかないけど、久しぶりに気合入れてコーディングしちゃったです。300行ぐらい…構造体渡し使いまくりで…。void *で渡してキャストすればいいけど、事故の元だしなぁ…。でもglobalでベターっと書くよりマシかなぁ…。

どーしましょw

スケッチではstructのtypedefを関数の引数として構造体に渡すことができない

仕様だそうです。上記の定義を以下のように書き換えるとコンパイル通ります。

struct DneData_t {
  float temperature;
  float humidity;
  float pressure;
  float airQuality;
};

void func(struct OneData_t *inData) {

}

良かった良かった。でも、書いてたコードはもうtypedef渡しではなくGlobal仕様に書き換えちゃったんですよね…まぁ動いてるからいいけど…今後は気をつけます。

今週はやることなすことうまくいかない。

ちゃんと勉強してから使えって話なんですけどねorz

2016年6月14日火曜日

風呂水温計 ver 3.1

前回バージョンを少し改良。電池をもたせるために30度以下なら10分ごと、超えたら5分ごとに測定するようにしました。

…こんなグラフになりました。


沸くの早いな、ほんとに。

改良箇所は、void setup()の直前に次の行を追加し
int waitSec;
deepSleepの行を以下に入れ替えました。
waitSec = temperature > 30.0 ? 300 : 600;
ESP.deepSleep((waitSec - second()) * 1000 * 1000, WAKE_RF_DEFAULT);
さて、風呂に入ろうっと。

■追記 6/15■


前記のように「30度を下回ったら」って仕様に変更したんだけど、お湯に入れたままだと30度以下にならない件。これから暑くなるしなぁ。

2016年6月13日月曜日

100円温度センサー+ESP-WROOM-02でWiFi風呂水温計



ってことで、昨日試した100円温度センサーSTTS751をESP-WROOM-02と電池に接続し、100均のジャム瓶?に入れました。フタの発泡ウレタンを少し切り取ってアルミ地金をむき出し、そこに放熱グリースを塗ってセンサーを貼り付けました。

風呂の中でフタを下にして浮かんで欲しいので、重い電池がフタ側に来るようにするのがコツ?です。ただ、フタが下だとどうしても浸水してしまいます。内部の温度が下がるときに熱膨張(の逆)によって水を吸い込みますので、風呂が湧いたらお湯から出すようにするのが一番効果がありました(経験者は語る)。

対策としては膨張した空気の容積差を吸収すればいいので、もっと柔らかいボトルを使うか、ボトル側に穴をあけてビニールシートで空気室を作ってやるのが一番簡単かなと思います。

また、風呂のフタをしめていれば、それほど大きく温度がズレることもないので、電池をボトル側の底に入れても問題ないかと思います。その辺は使い方に応じて選択してください。


ESP-WROOM-02がdeep sleepから立ち上がった時にWiFi経由でThingSpeak.comへデータを送信します。風呂の温度はそれほど急激に変化するものでもありませんから、送信頻度は5分に1回にしました。以前1分間隔で動かした時には単4白エネループ3本で110時間動きました。今回黒エネループで何とか一ヶ月=720時間程度動いてくれると便利なのですが。

なおSTTS751とのやりとりは、こちらを参考にさせていただきました。ありがとうございます。

以下ソースです。初期化処理にbegin transmissionを追加して、データ受信のところを多少書き換えました。上記のサンプルコードには問題があるので後日修正します(6/24)。ThingSpeak.comへの送信は以前と同じというかほぼコピペです。


■追記■

電池のさらなる長持ちを目指してプログラムを改良しました(風呂水温計 ver 3.1)。

2016年6月12日日曜日

100円のデジタル温度センサー+Arduino


初めてこれを見つけた時には、あんまり安いので何かの間違いかと思いました。デジタル式の温度センサーで1個100円(高精度12ビットI2C接続デジタル温度センサ STTS751)。しかも5個買えば1個80円。ってんで、5個買いました。電源3.3vですが、I2Cピンは5vにも対応しているので3.3v電源ならばArduino, mbed, ESP8266, ラズパイなど幅広く対応可能です。とりあえず温度だけ測定したいという用途に向いてます。

秋月のサイトから
基板は以前買ったSOT23オペアンプ用がまだ手元に残っているのでそれ流用します。この基板にはオペアンプのピンに合わせてパスコンのパターンもついているのですが、STTS751とは合いません。普通に「SOT23変換基板 金フラッシュ」を使う方がいいと思います。

秋月のサイトから
で、これをホットプレートでリフロー。ピン間隔狭いので横一列にペーストを塗って乗せただけです。まとめて3個、作っておきました(基板の在庫が3枚しかなかった)。



Arduinoは標準のWireライブラリだと内部でプルアップするのでプルアップ抵抗外付けは不要です。チップは3.3v電源なのですが、SDA/SCLについては5.5vまで対応可能なので、Arduinoでも問題ありません。

仕様書p27から

Arduino nanoパチで試しました。

Arduino nanoSTTS751
A46 SDA
A54 SCL
3v33 Vdd
GND2 Gnd

余談ですが…うちにはつい衝動買いしたArduino nanoパチが大量にあるのですが、Arduino nanoパチは附属のピンヘッダよりもピンソケットの方が使いやすいのではないかと思います。少なくとも複数のArudino nanoパチをお持ちの場合は、1つをピンソケット仕様にしておくと実験の時にムダにブレッドボードを使わなくていいので、お手軽です。というよりも300円のnanoパチに150円のミニブレッドボードを専有されるのはもったいない、という貧乏心なのですが。



で、テスト。手っ取り早く、「arduino stts751」でぐぐったら、コードが出てきたのでさっくり動かします。動きました。

このコードには問題がありました。修正版を後日公開します。とりあえず風呂温度センサーについては修正版を公開しました。

今回これを引っ張りだしたのは、「風呂の水温計測」(ESP-WROOM-02で温度データ収集)に使うためです。以前、ESP-WROOM-02とアナログセンサーで作ったのですが、デジタルセンサーの方が校正などの手間が省けて便利なので。

追記:組み込みました。
100円温度センサー+ESP-WROOM-02でWiFi風呂水温計

追記:ライブラリ書きました。
https://github.com/TareObjects/STTS751

追記:とんでもなくダメなバグをfixしました
グラフがところどころおかしな動きを見せるので、センサーからデータを取り出すところに問題があるのかと大騒ぎしたのですが、浮動小数を文字列に変換する関数に致命的な間違いがありました。余りにもダメなバグなので、晒しておきます。

void ftoa(float f, char *buf) {    // wrong ftoa code
  float a = int(f);
  float b = int((f - a) * 100.0000);
  sprintf(buf, "%d.%d", (int)a, (int)b);
}

以上。codeで首を吊るべきレベルだ…。さすがに自分で書いたものだとは思いたくないけど、そうだとしても気づかない時点で同罪だ…。

なお、浮動小数から文字列への変換にはdtostrfが使いやすいです。dtostrf(浮動小数,整数部最小桁数,小数部桁数,char*)で使えます。整数部最小桁数よりも小さい場合はスペースが入ります。

2016年6月11日土曜日

配線の記録

以前は「ESPの何番とセンサーの何番」って言葉で書いてましたが、それだと自分でもわからないしので、Google Spreadsheetで表を作ってコピペしてます。幸いなことにこのBloggerというサービスは表をそのままコピペできて便利。

最近はそれに加えてソースにもしっかり書くようにしました…ソースいじっている時にいちいちブログ開いて確認するの面倒なもんでw

ただ、作りかけ&再利用待ちの基板が山程あるので、ぱっと見て、どの基板がどのプログラムで動いていたのかわかりませんw 一応CPU1個ごとにSpreadsheetで管理しているのですが、ついに連番が3桁にww

こういう山が他にも3つorz

なのでモノとインターネットを関連付けるためにQRコードでも貼っておきたいのですが…マイコンが小さいとラベルプリンタでも大きすぎます。

A4のラベル用紙にびっしり番号を印刷しといて、番号とブログを対応させるようにしておこうかな…と思ったけどめんどくさい。ICチップも大げさだし。

ジャンプワイヤとミニブレッドボードの在庫がまたしても枯渇しそうなので、そろそろ整理しようかなぁ。

2016年6月10日金曜日

デジタルマイクSPM0405HD4Hがデジタルで動いた!

このまま「またデジタルマイクが動かない」「やっぱりデジタルマイクが動かない」「まだまだデジタルマイクが動かない」と長編化するかと思われたデジタルシリコンマイク、ついに動きました!


■考察■

今までも動いていたけどノイズに埋もれてわからなかった、というのが真相だと思います。

動かなかったバージョンでは100-10000回のカウントを終えるとLoopを抜けていました。これはESP-WROOM-02にはWDTがついていて、約1秒間loop()から抜けないとリセットがかかるのです。

何となくloop()抜けてもすぐに帰ってくるんだろ、ぐらいに考えていたんですが、これが想定以上に大きくて、抜けるたびに処理が止まってノイズとなっていました。loopから抜けるのをやめてESP.wtdFeed()にしたところ、かすかなノイズは入りますがマイクの前の音声がちゃんとADCから出力されるようになりました。

■ソース■

loopの値は100(サンプリング周波数10khz)ぐらいが良いようで、10(100khz)だとDACに出力するためのブランクの比率が著しいノイズとなり1000(1khz)にすると割れて何が何だか、という感じでした。また、countとして++だけでなく--も入れてみたところ、音量がかなり大きくなりました。100の場合で値は-100〜+100まで振れるので、精度は8bit、速度は10khzということになります。





お騒がせしました&ありがとうございました>電子工作部各位殿

もうちょっと高速のCPUが使えれば、DAC出力その他のための中断を回避できるので、もっといい音になると思います。現状で160Mhzクロックの32bit CPUなので、これより上のCPUを用意するよりはFPGAで作るべきものだと思います。約400円で専用チップ売ってるんですけどねw

ちなみにマイクの性能には「14bit相当・10khz」とのこと。うーん、どういう演算処理をすればそういう信号が得られるのでしょう。思いついたらまた試してみます。

■使用部品紹介という名のCM■

今回の実験では、
同社サイトより


同社サイトより

同社サイトより

を使用しました。

マイクモジュールとアンプへの3v3はBoard1搭載の安定化電源から供給しています。何かと電源にシビアなESPですが、(プログラムのバグ以外では)安定して動作しています。

タフな電源にしといてよかった…と設計者は胸をなでおろすのであった(完)。