2016年9月27日火曜日

帯に短し襷に長し

無調整で写真を出力。うーん難しい。

Laser加工機が欲しいです。

物欲の対象は現在以下の3機種。アクリル板をカットできることが最低条件。

Podea-01 6W版 159,800円(税別)
半導体レーザーを使い、比較的コンパクトな筐体に収まったタイプ。半導体なので取扱も比較的簡単でハニカムテーブルなども附属しており、完成度が高い。今のところ候補筆頭。アクリル板は3wでも黒なら切れるというレポート有り(非公式)。

Oh Laser HAJIME 30W 400,000円(?)
CO2レーザー、完成度が非常に高い。価格の情報は公開されておらず、上記は比較サイトに掲載されていた価格。

SmartLaser CO2 40W 280,584円(税込)
CO2レーザー、キット。コストパフォーマンスは圧倒的。facebookなどを見ると精度を出すのが大変っぽいし、位置決めキット、水冷キット、光軸調整など他のキットに含まれているものがオプションだけど、それでも安い。SmartLaserは上記三機種の中で唯一Webインタフェースなので、マカーの私にはありがたい。ただWebインタフェースは複雑な形状データを食わせるとハングする、らしい。

で。

予算という概念がなければ、HAJIME一択なのですが、さすがにホイホイ出せる金額ではない。

自転車で15分のところで30Wのプロ用レーザー加工機が45分2000円で使えるので、アクリル板などの切断加工はそっちでやることにしてPodeaの3w(119,800円)で良いかなとも思うけど、でも15分は微妙に遠いし利用料200回分でHAJIME買えるし(まて)。

…ということで一長一短三竦み状態で一歩も動けません。

2016年9月22日木曜日

今更ですがESP-WROOM-02で赤外線リモコン(174円也)

先日、会社でIoT勉強会を実施しました。

勤務先はガチのソフト開発なのですが、やっぱりIoTへの関心は高く、あっという間に定員に達しました。内容的にはESP-WROOM-02+BME280(気温湿度気圧センサー)+PIRセンサー(人感センサー)+空気質センサー+OLEDという有りがちな組み合わせですが、幸いにも好評のうちに勉強会を終えることができました。


勉強会を開催したい!!という方はLinkedIn経由でくらはしまでご連絡ください。材料費は1人前5000円ぐらいです。団地の集会場やコミュニティセンターでやってみたいです。

で、ここからが本題。勉強会参加者の方から、アフターフォローSNSで「エアコンを制御したい」というお題をいただきましたので、さくっと作ってみました。

■部品■

品名Name秋月PN価格購入数量参考バラ単価使用数
赤外線リモコン受信モジュールOSRB38C9AA(2個入)IR_RECEIVERI-046591001501
5mm赤外線LED OSI5FU5111C−40 (5個入)D1, D2I-032611001202
トランジスタ2SC1815GR 60V150mA (20個入)T1I-008812001101
カーボン抵抗(炭素皮膜抵抗) 1/6W 47Ω (100本入)R3, R4R-16470100112
カーボン抵抗(炭素皮膜抵抗) 1/6W 2.2kΩ (100本入)R1, R2R-16222100112
積層セラミックコンデンサ 47μF16V ±10% 5mmピッチ (10個入)C1P-049177001701

秋月で買うと大量に余りますが、特に抵抗とトランジスタは必ず使うので買ってしまってもいいと思います。コンデンサがわりと高いですが、これは無くてもあんまり変わりませんし、同容量の電解コンデンサなら1本10円ぐらいだと思います。

このうち、抵抗とセラミックコンデンサは極性がありません。つまり、どのピンをどっち側につないでもOKです。が、赤外線受信モジュール、LED、トランジスタ、電解コンデンサには極性があります。電解コンデンサは表面に書いてありますし、LEDは長い方の足がプラス電源側になります。トランジスタは仕様書にECBと書いてありますが、それぞれ、エミッター、コレクタ、ベースの略です。2SC1815の場合は、足を下に向け、平らな面(部品番号などが印字されている面)をこちらに向けて、左からECBの順番です。回路図ではトランジスタの左側がB(ベース)、上(矢印のついてないヤツ)はC(コレクタ)、下(矢印)はE(エミッタ)です。

赤外線受信モジュールIR RECEIVERはモノによって違いますが、今回使ったものは足を下に向け、受光窓をこちらに向けた左から1 OUT、2 GND、3 VCCです。違うモジュールを使った場合には仕様書で確認してみてください。

抵抗値は、2.2kΩ(回路図では2k2と表記)は赤赤赤金、47Ωは黄紫黒金です。最後の金は精度を表すもので金は±5%、銀は±10%です。今回のような用途では5%でも10%でも問題ありません。カラーコードについては、ここのページが見やすいかと思います(特に一番下の表)。

■回路■


会社のブログでは私のホワイトボードへろへろ回路図が晒されてしまったので、汚名返上を願ってEagleで書いてみた。

…ツール使ってもヘロヘロだったorz

あ、4u7じゃなくて47uだった。受信プログラムを走らせていると、リモコン操作していなくてもインバータやLEDランプのノイズを出鱈目な信号として誤認識してしまうので、ノイズ対策としてつけてみたんですが、あんまり違いありませんでした。誤読の場合はメーカー識別コードなどが出鱈目になるので対象となる製品のコードでフィルタリングすれば実用上は問題ないかと思います。

47μFの積層セラミックコンデンサは結構高いので、なしでもええんやで、H田さん。

受信機はIO15、LEDはIO16につないでいますが、これは空いていればどこでもOKですが、ソースのIOポート番号をそれに合わせて変更する必要があります。

同じ回路をArduinoで使う場合には、3v3ではなく5vにつなぎ、R3とR4を75Ωに取り替えてください。

■ライブラリ■

Arduinoの定番IRRemoteをESPで使うとAVR.hでひっかかってしまうので、IRRemoteESP8266を使います。原作者の方、ESP8266へポートしてくださった方に感謝致します。

githubからzip落としてArduino IDEに読み込ませます。

IRRemote/IRRemoteESP8266とも、そのままではバッファが小さくてエアコンのデータを扱うことができません。適当なエディタでIRRemoteESP8266.hを開き、RAWBUFの値を100から400に変更255に変更して保存します。

追記(2017年2月6日):400では動かない、ここは8bitなので255が最大値とのご指摘をいただきました。大変申し訳ありません。ご教示いただきありがとうございました。

…ところで何故私のところでは動いていたのでしょう・・・。

■受信プログラム■

コードを解析してバイナリコードで送るのがスマートなんですけど、面倒くさいのでrawコード(信号のon/off時間をそのままベタに記録したフォーマット。とても長い)を受信します。

なお、エアコンの場合、テレビと違ってリモコン信号が正常に送られているかどうか判断するのが難しいです。設定温度を本体に表示してくれるエアコンは別ですが。なので、私はタイマーのon / offで確認しました。タイマーをセットするとエアコン本体の予約ランプが点灯するので、セット / 解除の信号を送って、ランプの点滅で動作確認しようという寸法です。

何その巨大Lチカ。

他にはフィンの向きを使う(左右に切り替える)のも良いかと思います。なおエアコンの頻繁なon/offは電力消費にもエアコン本体の寿命にも悪影響を与えるので、ご注意を。

仕事部屋は古いFujitsuエアコン、IRRemoteのサンプルを改造して、以下のような出力を得ました。Panasonicとか48bitsとか出てますが気にしないように。

26度、風量自動、1時間タイマーON:
8083F
Decoded PANASONIC - Address: 28C6 Value: 8083F (48 bits)

Raw (244): 0xCE4,0x672,0x1C2,0x190,0x1C2,0x190,0x1C2,0x4E2,0x1C2,0x190,0x1C2,0x4E2,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1F4,0x190,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x190,0x4E2,0x1C2,0x190,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x190,0x4E2,0x1C2,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x190,0x190,0x1F4,0x190,0x190,0x1C2,0x4E2,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x4E2,0x1C2,0x1C2,0x190,0x4E2,0x1C2,0x4E2,0x1C2,0x190,0x190,0x1F4,0x190,0x1C2,0x1C2,0x4E2,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x190,0x1C2,0x1C2,0x1C2,0x190,0x190,0x190,0x4E2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x190,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x190,0x1C2,0x4E2,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x190,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x190,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x1C2,0x190,0x4E2,0x1C2,0x4E2,0x1C2,0x190,0x190,0x4E2,0x1C2,0x4E2,0x190,

26度、風量自動、タイマー解除:
8083F
Decoded PANASONIC - Address: 28C6 Value: 8083F (48 bits)
Raw (244): 0xCE4,0x672,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x4E2,0x1C2,0x1C2,0x1C2,0x4E2,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x4E2,0x1C2,0x4E2,0x1C2,0x190,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x4E2,0x1C2,0x1C2,0x190,0x190,0x1C2,0x190,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x4E2,0x1C2,0x1C2,0x190,0x190,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x190,0x1C2,0x4B0,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x4B0,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x4E2,0x1C2,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x190,0x1C2,0x1C2,0x4E2,0x1C2,0x190,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x190,0x190,0x190,0x1C2,0x1C2,0x190,0x1C2,0x190,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x190,0x1C2,0x1C2,0x190,0x190,0x1C2,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x1C2,0x190,0x1C2,0x190,0x1C2,0x190,0x190,0x1C2,0x1C2,0x190,0x190,0x1C2,0x1C2,0x190,0x1C2,0x190,0x1C2,0x1C2,0x1C2,0x190,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x4E2,0x1C2,0x190,0x190,0x4E2,0x1C2,0x190,0x1C2,0x1C2,0x190,

長いっすねー。ソースは短いっす。

// ESP_IR_Remote_Receive_Dump by koichi kurahashi 2016/09/22
//
//
// This source code is from below sample code. Thanks.
//
// IRremoteESP8266: IRrecvDump - dump details of IR codes with IRrecv
// An IR detector/demodulator must be connected to the input RECV_PIN.
// Version 0.1 Sept, 2015
// Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, Copyright 2009 Ken Shirriff, http://arcfn.com
// JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post)
// LG added by Darryl Smith (based on the JVC protocol)
#include <IRremoteESP8266.h>
int RECV_PIN = 15; // IO15と赤外線受信機の1ピンを接続
// 受信機ピンは正面左からOUTPUT, GND, VCC。VCCは3.3Vに接続
IRrecv irrecv(RECV_PIN);
decode_results results;
void setup()
{
Serial.begin(115200);
irrecv.enableIRIn(); // Start the receiver
}
void dump(decode_results *results) {
// Dumps out the decode_results structure.
// Call this after IRrecv::decode()
int count = results->rawlen;
if (results->decode_type == UNKNOWN) {
Serial.print("Unknown encoding: ");
}
else if (results->decode_type == NEC) {
Serial.print("Decoded NEC: ");
}
else if (results->decode_type == SONY) {
Serial.print("Decoded SONY: ");
}
else if (results->decode_type == RC5) {
Serial.print("Decoded RC5: ");
}
else if (results->decode_type == RC6) {
Serial.print("Decoded RC6: ");
}
else if (results->decode_type == PANASONIC) {
Serial.print("Decoded PANASONIC - Address: ");
Serial.print(results->panasonicAddress, HEX);
Serial.print(" Value: ");
}
else if (results->decode_type == LG) {
Serial.print("Decoded LG: ");
}
else if (results->decode_type == JVC) {
Serial.print("Decoded JVC: ");
}
else if (results->decode_type == AIWA_RC_T501) {
Serial.print("Decoded AIWA RC T501: ");
}
else if (results->decode_type == WHYNTER) {
Serial.print("Decoded Whynter: ");
}
Serial.print(results->value, HEX);
Serial.print(" (");
Serial.print(results->bits, DEC);
Serial.println(" bits)");
Serial.print("Raw (");
Serial.print(count, DEC);
Serial.print("): ");
for (int i = 1; i < count; i++) { // 変更箇所はこのループの中。コピペしやすいようHEXに変更。
Serial.print("0x");
Serial.print(results->rawbuf[i]*USECPERTICK, HEX);
Serial.print(",");
}
Serial.println();
}
void loop() {
if (irrecv.decode(&results)) {
Serial.println(results.value, HEX);
dump(&results);
irrecv.resume(); // Receive the next value
}
}

■送信プログラム■

受信プログラムからシリアルに吐き出されたraw列をコピペして、5秒ごとに送信受信を繰り返すプログラムです。これも元は附属のサンプルを書き換えました。

動きます。動きますが、赤外線LEDの指向性がかなり強いので、向きをちゃんと合わせないと反応してくれません。気をつけてください。

こっちもソース短いです。

// ESP_IR_Remote_Send_Demo by 2016/09/22 koichi kurahashi
//
//
// This source code is from below sample code. Thanks.
// IRremoteESP8266: IRsendGCDemo - demonstrates sending Global Cache-formatted IR codes with IRsend
// An IR LED must be connected to ESP8266 pin 0.
// Version 0.1 30 March, 2016
// Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, Copyright 2009 Ken Shirriff, http://arcfn.com
#include <IRremoteESP8266.h>
unsigned int fujitsu_26_ac_1hour_timer[] = {0xCE4, 0x672, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1F4, 0x190, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x4E2, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x190, 0x1F4, 0x190, 0x190, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x4E2, 0x1C2, 0x1C2, 0x190, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x190, 0x1F4, 0x190, 0x1C2, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x190, 0x190, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x1C2, 0x190, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x190, 0x4E2, 0x1C2, 0x4E2, 0x190};
unsigned int fujitsu_26_ac_timeroff[] = {0xCE4, 0x672, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x4E2, 0x1C2, 0x1C2, 0x1C2, 0x4E2, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x4E2, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x4E2, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x4B0, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x4B0, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x4E2, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x190, 0x190, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x1C2, 0x190, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x4E2, 0x1C2, 0x190, 0x190, 0x4E2, 0x1C2, 0x190, 0x1C2, 0x1C2, 0x190};
IRsend irsend(16); // IO16に赤外線ダイオード(のドライブ回路)を接続
void setup()
{
irsend.begin();
Serial.begin(115200);
}
void loop() {
int len;
Serial.println("timer on");
len = sizeof(fujitsu_26_ac_1hour_timer) / sizeof(unsigned int);
Serial.println(len);
irsend.sendRaw(fujitsu_26_ac_1hour_timer, len, 38);
delay(5000);
Serial.println("timer on");
len = sizeof(fujitsu_26_ac_timeroff) / sizeof(unsigned int);
Serial.println(len);
irsend.sendRaw(fujitsu_26_ac_timeroff, len, 38);
delay(5000);
}

2016年9月5日月曜日

ブラウザからDCモータを制御

というわけで、せっかくのESP-WROOM-02なので、ブラウザから(というかhttpで)モータを制御するようにプログラム書き換えました。

配線は前回のままです。起動するとIPアドレスが表示されますので、そのIPアドレスにパラメータをつけると制御できます。

http://<ip-address>?brake=0 : ブレーキがかかります
http://<ip-address>?r=40:速度指令40でモータを正転させます
http://<ip-address>?r=-40:速度指令40でモータを逆転させます。

ま、rじゃなくてもbrake以外なら何でも良いんですが。

ソース乗っけておきます。


// DRV8830 control over http / ESP-WROOM-02
//
// Thanks :
// HelloServer : a Sample program for ESP8266
//
const int LED = 13;
const int kMaxSpeed = 0x3f;
//
// Wifi
//
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include "private_ssid.h"
//const char *ssid = "******************";
//const char *password = "******************";
ESP8266WebServer server(80);
//
// Wire / DRV8830
//
#include <Wire.h>
const int kDrv8830Address = 0x64;
const int kBitClear = 0x80;
const int kBitILimit = 0x10;
const int kBitOTS = 0x08;
const int kBitUVLO = 0x04;
const int kBitOCP = 0x02;
const int kBitFault = 0x01;
//
// Setup
//
void setup() {
Serial.begin(115200);
Wire.begin();
//
// Wifi Server
WiFi.begin(ssid, password);
Serial.println("");
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (MDNS.begin("esp8266")) {
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
}
//
// Loop
//
float r = 0;
void loop() {
//
// http server
server.handleClient();
}
//
// WebServer
//
void handleRoot() {
digitalWrite(LED, HIGH);
for (int i = 0; i < server.args(); i++) {
char *cStr = (char *)server.argName(i).c_str();
Serial.print("received=");
Serial.print((char *)server.argName(i).c_str());
Serial.print("=");
Serial.println(server.arg(i).c_str());
if (strcmp(cStr, "brake") == 0) {
brakeMotor();
} else {
int value = atoi(server.arg(i).c_str());
if (value > kMaxSpeed) value = kMaxSpeed;
if (value < -kMaxSpeed) value = -kMaxSpeed;
Serial.println(value);
runMotor(value);
}
}
int status = readMotorStatus();
char buf[100];
sprintf(buf, "drv8830 result = %02x", status);
if (status != 0) {
resetMotorStatus();
}
server.send(200, "text/plain", buf);
digitalWrite(LED, LOW);
}
void handleNotFound() {
digitalWrite(LED, 1);
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
digitalWrite(LED, 0);
}
//
// DRV8830
//
uint8_t readMotorStatus() {
uint8_t result = 0x00;
Wire.beginTransmission(kDrv8830Address);
Wire.write(0x01); // read register 0x01
Wire.endTransmission();
Wire.requestFrom(kDrv8830Address, 1);
if (Wire.available()) {
result = Wire.read();
} else {
Serial.println("No status data");
}
return result;
}
void resetMotorStatus() {
Wire.beginTransmission(kDrv8830Address);
Wire.write(0x01); // fault
Wire.write(0x80); // clear
Wire.endTransmission(true);
}
void runMotor(int inVector) {
int direction;
int voltage;
if (inVector > 0) {
direction = 0x01;
voltage = inVector;
} else if (inVector == 0) {
direction = 0x00;
voltage = 0;
} else {
direction = 0x02;
voltage = -inVector;
}
writeToDriver(direction, voltage);
}
void brakeMotor() {
writeToDriver(0x03, 0x00);
}
void writeToDriver(byte inDirection, byte inVoltage) {
if (inVoltage <= 0x05) inVoltage = 0x06; // minimum voltage valu is 0x06.
int outData = (inVoltage & 0x3f) << 2 | (inDirection & 0x03);
Wire.beginTransmission(kDrv8830Address);
Wire.write(0x00); // control
Wire.write(outData); //
Wire.endTransmission(true);
delay(12);
}

2016年9月4日日曜日

170円でDCモータを制御(DRV8830):ESP-WROOM-02



Elecrowの4WD模型を買ったのは良いのですが、例によって買っただけで満足してしまってぜんぜん手をつけてません。

これではいけないわ、ということで、とりあえずDCモータを制御するコントローラを試してみました。秋月だけ見ても色々なコントローラが揃っているのですが、ESP-WROOM-02で4つのモータを制御するとなると単純なHブリッジをPWMで制御するのは無理(ピンが足りない)なので、I2Cに9個までぶら下げることのできるDRV8830を使います。

レジスタに値を書けばその通りの電圧で回してくれて、過電流や温度などのステータスも見られるのでラクです。

■必要なもの■


あとはブレッドボードなどの配線資材などが必要です。

■配線■

簡単すぎて表にする意味がなかったですね…。

ESP-WROOM-02DRV8830
GPIO4SDA
GPIO5SCL
VINVcc
GNDGnd

その他に

  • モータの+とOUT1、モータの-とOUT2
  • モータの+と-の間に1nFのセラミックコンデンサ
  • DRV8830のISENSEとGNDの間に0.2Ωの抵抗
  • DRV8830のVCCとGNDの間に0.1μFのセラミックコンデンサ
  • SDAと3.3vの間に10kΩの抵抗
  • SCLと3.3vの間に10kΩの抵抗
  • USBシリアルなどの5v出力とESP-WROOM-02ボードのVIN
  • USBシリアルなどのGNDとESP-WROOM-02ボードのGND
を接続します。

DRV8830の説明には「マイコンの電源とモータの電源は共通に」って書いてあるのですが、ここではDRV8830には5Vをかけてます。オープンドレインなのでI2Cにはプルアップ電圧以上は出てきませんが、FAULTなどには5Vが出てきてしまうのでESP-WROOM-02に接続すると壊れます(ESPが)。マイコンと接続するのは3.3vでプルアップしたI2Cだけにしてください。

■プログラム■

探すとSparkfunさんがサンプルを公開してくれているのですが、AVR用でESPではビルド通りません。なおも探してみると、Arduino版のサンプルが公開されていたので、参考にさせていただきました。ありがとうございます。

モーターが正弦波に乗ってゆっくり加速>減速>反転してゆっくり加速>ゆっくり減速…を繰り返すだけですが、とりあえずソフトで意図した通りの動作ができました。

さて、これでElecrow 4WDを制御しなきゃいけないんですが…普通モーターはトルク≒電流を制御します。DRV8830は出力電圧をPWMで制御するので、実際どのぐらいトルクが出ているのかはわかりません。それに、モータにはロータリーエンコーダなどもついてないので、「モーターがどんな具合いで回っているのかを知る手段がない」という恐ろしい状態でモータを制御しなきゃいけませんw

…どうすんだ、これ…(笑)

// DRV8830 test / ESP-WROOM-02
//
// DRV8830で直流モータをDriveするテスト。速度を正弦波に乗せてゆっくり加減速と反転を繰り返す
//
// Thanks :
// 「Arduino Nano とモータードライバでDCモーターを正転・逆転してみた(1) [Arduino]」
// http://makers-with-myson.blog.so-net.ne.jp/2014-05-15
//
//
// Wire / DRV8830
//
#include <Wire.h>
const int kDrv8830Address = 0x64;
const int kBitClear = 0x80;
const int kBitILimit = 0x10;
const int kBitOTS = 0x08;
const int kBitUVLO = 0x04;
const int kBitOCP = 0x02;
const int kBitFault = 0x01;
//
// Setup
//
void setup() {
Serial.begin(115200);
Wire.begin();
}
//
// Loop
//
float r = 0;
void loop() {
//
// Motor
float s = sin(r) * 64.0;
r += 0.1;
if (r > 6.28) r = 0.0;
int out = (s > 0) ? 0x01 : 0x02;
int speed = s;
runMotor(speed);
int status = readMotorStatus();
if (status & kBitFault) {
Serial.print("Motor Fault : ");
Serial.println(status, HEX);
resetMotorStatus();
}
delay(100);
}
//
// DRV8830 Controll
//
uint8_t readMotorStatus() {
uint8_t result = 0x00;
Wire.beginTransmission(kDrv8830Address);
Wire.write(0x01); // read register 0x01
Wire.endTransmission();
Wire.requestFrom(kDrv8830Address, 1);
if (Wire.available()) {
result = Wire.read();
} else {
Serial.println("No status data");
}
return result;
}
void resetMotorStatus() {
Wire.beginTransmission(kDrv8830Address);
Wire.write(0x01); // fault
Wire.write(0x80); // clear
Wire.endTransmission(true);
}
void runMotor(int inVector) {
int direction;
int voltage;
if (inVector > 0) {
direction = 0x01;
voltage = inVector;
} else if (inVector == 0) {
direction = 0x00;
voltage = 0;
} else {
direction = 0x02;
voltage = -inVector;
}
writeToDriver(direction, voltage);
}
void brakeMotor() {
writeToDriver(0x03, 0x00);
}
void writeToDriver(byte inDirection, byte inVoltage) {
if (inVoltage <= 0x05) inVoltage = 0x06; // minimum voltage valu is 0x06.
int outData = (inVoltage & 0x3f) << 2 | (inDirection & 0x03);
Wire.beginTransmission(kDrv8830Address);
Wire.write(0x00); // control
Wire.write(outData); //
Wire.endTransmission(true);
delay(12);
}


2016年9月3日土曜日

Eagleで角の丸い基板を設計する


スイッチサイエンスさんの基板は四隅が丸くてカッコイイ! 私も真似しよう!!

…というわけではなく、製作したいのはラズパイの上に乗っける基板なので、同じ半径で隅を丸くしないとケースに入らなくなっちゃうんですよね。

しかし方法がわからず、日本語でググってもググっても出て来ませんでした。

が、ふと思い立って「eagle cad round rect outer」でググったら出てきました!

ってことで、半径nミリで角を丸くする手順は以下の通りです。
  1. すでにある外形線はそのまま
  2. Wireを選び、レイヤ:20 Dimension、Wire Bend Style 5、Width:0、Style: Continuousを選びます。
  3. 左下角からnミリ離れたところをクリック、上にマウスポインタを移動します。
  4. 左上角のnミリ下をクリックし、右上角の方にマウスポインタを移動すると、左上角がいい感じに丸くなっていると思います。
  5. 右上角のnミリ左をクリックします。あとは同じような感じで始点まで戻ります。
  6. 四隅の半径を調整したい場合には、Moveアイコンを選んでから曲がり始めの付近をクリックしてマウスポインタを移動すると半径が変わります。
こんだけでした…。簡単すぎるから誰も書いていないのでしょうか…。

ついでに長穴に関しては、いろいろ方法が紹介されているのですが、一番わかりやすくて確実なのは以下の記事だと思います。

これで長円が空くのですが、ベタグラウンドを使ったり基板の真ん中に穴を開けていたりすると平気で配線が穴を横切ったりしますw レジストも銅箔もそのまま塗られてしまうのは気持ちが悪いので、長穴を46 millingで作ったら、
  1. それを4つコピーする
  2. Inspectorで29 tStop、30 bStop、41 tRestrict、42 bRestrictにし、WidthをtStop/bStopは0.2mm大きく、tRestrictとbRestrictは0.4mm大きくします。
  3. millingの上にぴったり重ねます
こうすると、穴が空いてその0.1mm外までレジストが塗られ、さらにその0.1mm外までしか銅箔が来ません。

ってことでelecrowに発注、出来上がってきたのがトップの写真です。ちょっと左上にバリが残ってるしシルクかすれているけど、いい感じです。

シルクがかぶっているのは私のミス…ってか気づけよorz

2016年9月1日木曜日

ケーブル受託製作

WWDC友達のTさんから「急ぎでケーブル作ってもらえませんか??」とメッセージが。

詳細を伺うと私でも出来そうな仕様だったので、勤務終わってから秋葉原に直行して資材を調達。RJ-11の4極4芯はすぐに見つかったもののRJ-12で6芯というのが見つからず、RJ-45を幅を切り替えて使うものを代替品として買う。

…帰宅してAmazonやらヨドバシ検索したらゴロゴロ出てきたけどね。秋葉原にもあるよね、ヨドバシorz

依頼のあったのは「RJ-9 / RJ-12とDSUB9を接続する50cmぐらいのケーブル」。自宅になったRJ用の圧着工具はケーブルの太さなどが合わなくて、挫折。とりあえず、RJ-11で予行演習。

…案の定、RJ-11の4極4芯から出ているワイヤをDSUBに直接ハンダ付けしても、細くてすぐに切れてしまう。

ので、ブレッドボード用ピンを圧着して、その上からダメ押しでハンダを流し込みむ。ハンダ付けすると切れやすくなるけどケーブルの銅線の状態が悪くて何度圧着をやり直しても接触抵抗が600mΩを切れないので苦渋の選択。これで何とかDSUBにハンダ付けできた。経年変化が心配だけど「至急欲しい。耐久性は不要」とのことなのでこれで良し。

で、翌日届いたRJ-12とRJ-9ケーブルを使って、予行演習と同様に配線。Amazonプライムで入手可能なコード付きのRJ-9はカールコードばかり。唯一よじれてないケーブルを注文したが見事なまでに低品質で切断前に測定してみたら直流抵抗が3Ω近い。配線を短く切って圧着とハンダ付け併用で何とか1Ω以下に仕上げる。

先方はほぼ通りがかりにあるので台風の中、郵便受けに投函して納品。

買い物してた時間やら予行演習の時間も含めると時給800円ぐらいだなぁw



なお、途中でRJ11がRJ9に変わっているのは大人の事情♡