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ですが、(プログラムのバグ以外では)安定して動作しています。

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

2016年6月9日木曜日

デジタルマイクが動かない(SPI DAC出力編

Board1 - Mic - ADC+D級AMP

■ノイズしか出ねえ■

前回のあらすじ。秋月で300円のデジタルマイクモジュールを試してみたものの、マイコンからパルスを入れてもそれっぽい信号が取れている気がしない。でも、出力をLPF通してアンプにつないだら音が出ていた。

ということはPDMとしての動作はOKであとはデジタルとしてどうやったら信号を取り込めるか、ということになります。

ESP-WROOM-02を160Mhzで動かし、約1mhzでデューティー比50%近いパルスを入れてダウンエッジから約60nSecぐらいのところで取り込んだデータを集計してDACから出力してみたのですが…雑音しか出て来ませんでしたorz

ということで、以下失敗の記録です。なお、失敗記録を読んでもしょうがない、という方は先にこちら(デジタルマイクSPM0405HD4Hがデジタルで動いた!)をどうぞ。

■SPIと競合しちゃう■

最初GPIO12から信号を読み込んで居たのですが、ずっとゼロでした。SPIライブラリの内部で何かに使っているようです。GPIO5からクロック出力、GPIO4からデータ入力…という形にしたところ、読み込みできました。

なお、DACはMCP4922を使い、配線は以下の通りです。正しく動作しているかどうかは例によってノコギリ波で確認しました。

ESP8266SPM0405HD4HMCP4922
GPIO4DAT
GPIO5CLK
3v3Vcc1, 1, 11, 13
GndGnd, L/R12 GND
GPIO135 SDI
GPIO144 SCK
GPIO153 !CS
GPIO168 !LDAC


■最適化されちゃう■

タイミングやデューティー比を調整するために意味のないインクリメントを入れてみたのですが、localで宣言した意味のない演算は最適化で消されちゃうみたいですね。
for (int i = 0; i < n; i++) {
  int a = 0;
  a++;
  a++;
}
a++を増やしても結果変わりません。

void loop()の外で
int a = 0;
を宣言すると1つあたり25nSec程度の遅延が得られます。

digitalWrite(x, HIGH)で225nSec、digitalWrite(x, LOW)で212.5nSec程度の処理時間がかかります。

■試行錯誤しちゃう■

  • 現在のソースではダウンエッジだがアップエッジでも試す。ダウンエッジとアップエッジをLRライン=LOW/HIGH両方で試す。
  • ループ前にcountを0にして入力HIGHなら加算。あるいはLOWなら加算
    ▶ノイズだけ
  • ループ前にcountを毎回初期化せず、HIGHなら加算, LOWなら減算。あるいは逆
    ▶発散しました
  • ループ回数を減らしたり増やしたり
    ▶ノイズの音質が変わりますw
  • パルス出力からサンプリングまでのタイミングを増やしたり減らしたり
    ▶ノイズの音質が変わりますw
浩一はもう疲れました…。


■ソース■

loop内でforループから抜けた後のSerialは、タイミングなどを確認した後にコメントアウトしてます。処理時間がものすごく長いので。それとArduino IDEではツールメニュー>CPU Frequencyで160Mhzを選んでください。80Mhzだとクロック500khzしか出ませんので。


■そして…■

2016年6月5日日曜日

ハンダ付け:拡大鏡からUSB顕微鏡へ

「EDI」に見えるのは「103」天地逆

えー、老眼です。

ハンダ付けにはルーペのついたハンダ付け作業台が必須です。

でも覗き込んで作業するのは首やら腰に負担が大きいので何とかしたいなと、以前USB顕微鏡を購入しました。

が。

  • 附属のLEDランプが明るすぎる
  • ランプが真上から照らすのでハンダの金属光沢が見えづらい
  • 専用アプリでは遅延が大きすぎて作業に支障出まくり

ということで、買ってすぐオクラにしてました。「誰か買ってー(泣)」とfacebookで呼びかけたものの、「こういう欠点ある(きっぱり)」なものが売れるはずもなく。

が。

ちょっと顕微鏡的なことに使いたくて、久しぶりにパッケージを開けてみました。専用ソフトは消してしまったので、「またインストールするのも面倒くさいな」と手っ取り早くPhotoBoothを起動してみたら…表示されました。ははは。

それもほとんど遅延なしで表示されてます。あははははは。

ただPhotoBoothだと鏡像なので、QuickTimeで試してみたら…ちゃんと見えます。問題なく。遅延なくフルスクリーンで表示できて鏡像/正像の表示ができればOKなので何でも良いのですが。さがすの面倒くさいのでAppStoreで240円のアプリを買いました。
で。

作業台に取り付けようとUSBケーブルをたぐっていたら、ケーブルの途中に見慣れないツマミが。ボリュームとスイッチがついてます。ボリュームを動かすと何とLEDランプが暗くなり、消すこともできるじゃありませんか。だははははは。

というわけで、撮影したのが一番上の写真。赤っぽいのは電球色LEDで撮影したせいです。ハンダ付けには倍率大きすぎるんですが、でも冒頭の欠点はすべて解消しました。強いて言えば割と発熱するので使わない時は切っておきたいんですが、アプリ等からオフにできないので使わない時はUSBハブから引き抜かないといけない=使うときに引っぱり出さなければいけない、というのが欠点かしら(ズボラっぷりをカミングアウトしているだけとも言う)。

ということで、微細なハンダ付けをきれいに仕上げたい方にお勧めです。

BeMicro MAX10のPMOD

A, B, C, DのAだから端についているだろと思ったら、甘かったw 写真手前から二段目です。

奥から順にPMOD_C, PMOD_D, PMOD_A, PMOD_Bの順番で並んでいます。


PMODとしてのピン番号は右から1-6、一番左の6番が3v3, 5番がGndなのは共通です。

BeMicroの端子とピンの配置については、bemicrom10_getting_started.pdfって資料に出てます。

PMODはこんな感じ。毎回さがすの面倒くさいから貼っておきます。


デジタルマイクが動かない(ESPとFPGAでクロック生成 )◀アナログ出てました

秋月のディジタルシリコンマイクモジュール(細ピンヘッダ版)を試してみました。

秋月のサイトより

このモジュールは1-3.25MHzのクロックを入れればPDMフォーマットでデジタルデータが取り出せます。PDMなのでローパスフィルタを通せばそのままアナログ信号として使えます。しかも300円。素晴らしい。

…しかし使えなければ意味がありません。結果として動作させることができませんでした。

今週は何をやっても動かないなorz

■追記6/6■

電子工作部の方から「LPFの出力をアンプに繋いでみたら何か信号出てるんじゃない?」というアドバイスをいただき、FPGAで作った1.25Mhzのクロックを入れて、LPF出力をカップリングコンデンサ経由で27dBのアンプにつないでみました。

そしたら!

小さいですが確かに音が出ています!! いえええええ!!! アドバイスありがとうございました。うれしー。小さすぎてオシロでは見えなかったけど、信号出てたのね。

というわけで、今週末はデジタルでの処理に再挑戦します。

それまでは以下のマイコンでの試行錯誤からFPGAでのクロック発生回路でアナログとしての取得に成功、最終的にデジタル信号として取り込みDACから音が出るまでのドタバタ劇をお楽しみいただければ幸いですw

■SPIでデジタル入力(失敗)■

今度はSPIです。SPIのクロックを1Mhzに設定し、SPI modeをいろいろ変えたりしてみたのですが、結果は似たようなもので値が123-127の間をパラパラ上下するだけでした。

#include <SPI.h>
const uint8_t BITS_COUNT_TABLE[256] = {
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};
int numofbits2(uint16_t bits)
{
    return BITS_COUNT_TABLE[((unsigned char*)&bits)[0]]
         + BITS_COUNT_TABLE[((unsigned char*)&bits)[1]];
}
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  SPI.begin();
  SPI.setFrequency(1000000);
  SPI.setDataMode(SPI_MODE0);
  noInterrupts();
}
static int mabiki = 0;
void loop() {
  // put your main code here, to run repeatedly:
  int num = 0;
  for (int i = 0; i < 16; i++) {
    num += numofbits2(SPI.transfer16(0xffff));
  }
  char buf[6];
  sprintf(buf, "%4d", num);
  if (mabiki == 20) {
    Serial.println(buf);
    mabiki = 0;
  } else {
    Serial.print(buf);  
  }
  mabiki++;
}
なお、ビットカウントはこちらの記事から使わせていただきました。

テーブルを使う方法は思いついたのですが、そのあとに記載されているアルゴリズムは本当にびっくりです。1960年代のコンピュータサイエンスは凄まじいですね。

■GPIOでデジタル入力(失敗)■

GPIOでクロックを生成し、ポートを読み込んで積算する方法を試してみました。帰ってきた値は見事にデタラメでグラフ化しても波形になりませんでした。
#include <Arduino.h>
void setup() {
  // put your setup code here, to run once:
  Serial.begin(230400);

  pinMode(14, OUTPUT);
  pinMode(12, INPUT);
}
void loop() {
  // put your main code here, to run repeatedly:
  int count = 0;
  for (long l = 0; l < 1000; l++) {
    digitalWrite(14, HIGH);
    if (digitalRead(12) == HIGH) {
      count ++;
    }
//    else {
//      count --;
//    }
    digitalWrite(14, LOW);
  }
  Serial.println(count);
}

なお、コメントアウトの部分はLOWならデクリメント、というサンプルを見たので試してみたのですが…結果がマイナスに振れただけで同じでしたたたたた。

■マイコン発振 / アナログ出力(失敗)■

1MHz強のクロックを入れ、出力に約20khzのローパスフィルタ(R=1kΩ,1=10nF)を通してマイク前から信号を入れてみました。ローパスフィルタは出力端子>抵抗>コンデンサ>Gndとつないで、抵抗とコンデンサがつながっているところから出力取ります。1MHz強のクロックはESPを160MHzクロックで動かして、以下のプログラムで生成します。デューティー比50%にならないけど、気にしない。デューティー比を考えれば少しでも演算の早いshortの方が良いのですが、shortだと約32mSecごとにシステム?に戻ってしまうのでオシロで波形見るときに邪魔かなと。
#include <Arduino.h>
void setup() {
  // put your setup code here, to run once:
  pinMode(14, OUTPUT);
}
void loop() {
  // put your main code here, to run repeatedly:
  int count = 0;
  for (long l = 0; l < 1000000; l++) {
    digitalWrite(14, HIGH);
    digitalWrite(14, LOW);
  }
}

さて出力は…見事に直流です。本当にありがとうございましたorz

■FPGA発振 + アナログ出力■

最後にFPGAでちゃんとした1.25 / 2.5MHzのクロックを作って試してみます。でもまだちゃんと作る自信がないので、「FPGA電子工作スーパーキット」のp112ページからの手順やソースをまず動かして見て、そこにクロック生成を追加します(弱気

なお、同誌附属の基板はまだ組み立てていないので、BeMicro MAX10を使います。
  1. Quartus起動
  2. New Project Wizardでディレクトリ,プロジェクト名ClockGenを入力し、BeMicro搭載デバイスの10M08DAF484C8GESを選び、Finish。
  3. Fileメニュー>New...からVerilog HDL Fileを選びOK
  4. Velilogソースを打ち込む
  5. TasksのAnalysis & Synthesisを右クリックしてStartを選ぶ。エラーが出たら直してもう一度
  6. Assignmentsメニュー>Pin Plannerを選び、ピンを割り当てる(下図1)
  7. TasksのCompile Designを右クリックしてStartを選び、コンパイルし、「…Quartus Prime Full Compilation was successful」が出たらOK
  8. Toolsメニュー>Programmerで、Programmerを起動
  9. USB-Blasterが選択されていることを確認(表示されていなかったらHardware Setup...ボタンから選択ダイアログへ)
  10. Add Fileボタンをクリックし、output_filesの中のClockGen.sofを選ぶ。デバイスと書き込みファイルが表示される(下図2)
  11. Startボタンをクリック、瞬時に書き込みが終わり、LEDがチカチカ。


図1

図2
基本動作が確認できたので、2.5Mhzとその1/2, 1/4, 1/8のクロックをPMOD端子から出力するように書きなおします。
  1. QuartusのEntity欄でClockGenをダブルクリックしてVerilogソースを開きます。
  2. ソースを修正して保存します。
  3. 青の「▶」をクリックすると「変更するか?」と聞いてきますのでYES
  4. Full Compilation was successfulが出たらToolsメニューからProgrammer
  5. Startボタンをクリックしてコンフィグ(書き込み)
と、いうわけで、(少し)きれいな1.25Mhzと2.5Mhzが出ました。で、本題のデジタルマイクですが。同じです。ローパスフィルタを通しても何も出ません。

はい終了。

■FPGAのソース■



■最後に■

デジタルマイクは製品として販売されているからには、動くはず。でも何が悪いのかわかりません。もう涙も枯れ果てました…でもFPGAでクロック出すのはうまく行ったから良しとしよう(問題をすり替えてはいけません)。

続きはこちらをどうぞ。

Verilog:「-1」と「- 1」が違うなんて・・・

ツールに慣れるための素振りとしてFPGAでLチカやったんです。

そしたら、LチカどころかL(数分)チカになってしまいまして。何度見直しても違いがわかりません。原因がこの行なのは間違いないのですが。
assign period_1sec = (counter_1sec == (`CYCLES_1SEC -1);

ただ、defineをやめて
assign period_1sec = (counter_1sec == (48000000);
と書くとちゃんと動きます。
assign period_1sec = (counter_1sec == (`CYCLES_1SEC);
でも大丈夫。

そこで「-1」になっていたところを念のため「-<スペース>1」に変えてみたら…動きました。

うーん、「-<スペース>1」は1減算として動くようです。

となると「-1」って、何?

…調べても出てこないので当面は放置、そのうち文法書でそれについての記述に出会うことだろう(遠い目

いやー…辛いわー(笑顔

2016年6月4日土曜日

ESP8266:I2S MP3 webradio streaming example(give up)


ESP-WROOM-02でI2Sを動かせないかと思ってググっていたら、上記記事がヒットしました。
Web StreamingがESP8266で出来る!

そりゃあやるしかありませんよね。残念ながら「現在は16bitのモノラル」だけど「サンプル・レートは96-320KBit MP3でテストしてうまくいってるっぽい」そうです。スゴいのか凄くないのかよくわかりませんw

なお、記事をよくみるとSPI RAMはSD_D0など見慣れないピンにつながっています。これ32pinのESPじゃないとダメですね。幸いなことにDACもSPI RAMもなしで動く、と書いてありますので、SPI RAMなしで行きます。ES9023も微妙に高かったので、同じように動作するはずのPCM5102ってのを買いました。ebayで1000円ぐらいでした。

上記リンクの通り配線しますが、ES9023ボードはライン出力なので別にアンプを用意する必要があります。
  • PCM5102(ES9023)ボード用電源(とりあえずUSBから取ります)
  • ブレッド‐ボードなど配線材料

数日間チャレンジしたのですが、動かすことができませんでした。最終的な原因はシリアルのコンフリクトだと思います。私は一旦ギブアップしますが、引き継いでくださる方のために記録を残しておきます。

■ダウンロード&ビルド■

このサンプルはfree-rtosで書かれています。ので、まだfree-rtosの環境がなければ、こちらをご参考にどぞ。

各種設定は mp3/user/playconfig.h で行うことができます。上から順番に見ていきます。

接続先のWIFI SSIDとパスワードをAP_NAMEとAP_PASSに設定します。

MP3_DECODERをダウンロードしたままの状態だとローカルのサーバからMP3をダウンロードする設定になっていますが、面倒なのでやめます。リストされているうちでは icecast.omroep.nl ってところだといい感じに接続できました。そのすぐ上の #if 0 を #if 1 に書き換えます。そして #if 1になっている 192.168.33.128 のところを #if 0 に書き換えます。ご自宅にラズパイなどでストリーミングサーバを立てている場合には、それを活かしてみましょう(動作を確認した後で)。

I2Sをつないでいるので DELTA_SIGMA_HACK をコメントアウトします。 PWM_HACK はそのままコメントアウトですね。

上記の通りSPI RAMは使わないのでFAKE_SPI_BUFFの行のコメントを外します。

ビルドはgithubに書いてあるとおり、コマンドラインで当該ディレクトリに移動してからmakeです。たぶん問題ないと思います。ツールが見つからない旨のエラーが出る場合にはパスを設定していあるか確認してください。make対象がないというエラーはディレクトリが違います。
 make COMPILE=gcc BOOT=none APP=0 SPI_SPEED=80 SPI_MODE=QIO SPI_SIZE=1024
エラーがでなければ、ESP-WROOM-02にダウンロードします。なお、元記事にも書いてありますが、コンパイル後に出る eagle.irom0text.bin---->0x40000 は間違っています。下記のように0XA0000から書き込みをしてください。USBシリアルとESP-WROOM-02をつないでPROGとRESETを一緒にGNDに落とし、RESETを先に離してからPROGを離し、ダウンロードモードにしておきます。Board1の場合には白ボタンと赤ボタンを一緒に押して先に赤ボタンを離してから白ボタンを離します。コマンドラインで
cd mp3
esptool.py --port /dev/tty.usbserial-AH016T8K --baud 115200 \
write_flash 0x00000 ../bin/eagle.flash.bin 0xA0000 ../bin/eagle.irom0text.bin

これでConnecting...以下の表示が出ればOKです。
Connecting...
Erasing flash...
Writing at 0x00009c00... (100 %)
Erasing flash...
Writing at 0x000df800... (100 %)
Leaving...
動作はあまり安定しておらず、起動してすぐにexception、数秒走ってはexceptionが出る…を繰り返して、Rxのランプが点灯しっぱなしになったらGPIO15とRxをオシロで見てみます。数MHzのパルスが出ていればひとまず動いてます。

…ここまでplayconfig.hを試行錯誤して数時間かかりました。

ただ、LR切り替え信号が出てません。この信号がDATAのスタート信号になっているので、これがでないとDAC動いてくれません。

ソースを見ると、Tx / RxをI2Sの信号に使っているのにSerialにprintfでメッセージを出しているのですが…これ問題ないのでしょうか? UART_SetBaudrateをコメントアウトして動作に影響しないようにprintfをコメントアウトしてみましたが、ダメでした。

うーん、あと一歩だと思うのですが…ここまで動かしてギブアップするのは残念です。ただ、I2Sの使い方についてはいい勉強になりました。Tx/RxはDMAでバッファを使えるんですね。当面使う見込みないけどもw

以上お目汚し、失礼しました。