学習能力のあるカラスは電撃一回で来なくなったようですが、鳩は電撃で対策した柵には寄りつかなくなったものの今度は室外機の上に来るようになりました。
ということで、今度は、光り物と動き物で脅かす作戦にしました。やつら「実害」がないとダメなので、当初は電極を振り回そうかと思ったのですが、製作中に私が「実害」を受ける未来しか浮かばないので保留。
■仕様■
まず使用機材:- 動作状況をモニタできるようにESP8266を使用
- 動きモノはサーボモータSG90
- 光りモノは赤色LEDを直接接続
- 鳩検出はPIR
- 動作状況の記録にはThingSpeakを使用
動作仕様:
- PIRで何かを検出したら時計をセット
- 時計がセットされていたらサーボ腕を振り回しつつLEDを点滅させる。5秒経ってもどかなければ、LEDの点滅を激しくする
- 何かがいなくなったら、サーボとLEDを止めてから何秒とどまっていたかをThingSpeakに送信
- それとは別に1分ごとに気温と湿度をThingSpeakに送信する
■ハードウェア制作■
ESP8266/ESP-WROOM-02の電源を甘く見てはいけません。最終的に毎度お馴染みの「超高効率DC-DCコンバーター(3.3V0.5A) M78AR033-0.5」を使い、アルミ電解コンデンサ470uF + タンタル4.7uF + 積層セラミック1uをブレッドボードの3.3v電源ラインに並べ、ESPモジュールの上、Vcc - GND間に直接積層セラミック0.1uをハンダ付けして、安定動作させることができました。VccとGNDが離れているのでパスコン付けにくいんですよね…。ロットでの差異や個体差もあると思いますが、ここまでやれば起動直後にドーンと電流食われてもだいたい安定します。
サーボモータはIOピン直結、LEDはVfの実測値1.8vだったのでESPのIO定格値12mAを超えないよう150Ωの抵抗を入れて制限します。
HDC1000は秋月のプルアップ抵抗内蔵モジュールを使いますので、電源とI2Cを接続するだけでOKです。
PIRはHC-SR501を使いました。アマゾンでも秋月でも入手できます。このPIRは4.5vから使うことができます。秋月では5-12vと書いてありますが、他サイトでは4.5-12vになっていて、実際に試しても5vで問題ないようです。SE-10のように5vって書いてあるけど5vで使うとしばらくして不安定になる、ということはありません。以下配線表です。
ESP-WROOM-02 | モジュール接続先 | |
1 | Vcc | 3.3vライン |
2 | EN | 10kΩを通して3.3vラインへ(プルアップ) |
3 | IO14 | HDC1000のSCLへ |
4 | IO12 | 150Ωを介してLED1へ |
5 | IO13 | 150Ωを介してLED2へ |
6 | IO15 | N.C.(無接続) |
7 | IO2 | プルアップ |
8 | IO0 | プルアップ スイッチを通してGNDへ |
9 | GND | GNDへ |
10 | IO4 | HDC1000のSDAへ |
11 | RxD | FTDIのTxへ |
12 | TxD | FTDIのRxへ |
13 | GND | GNDへ |
14 | IO5 | N.C. |
15 | RST | プルアップ スイッチを通してGNDへ |
16 | TOUT | N.C. |
17 | IO16 | SG90の信号線(オレンジ色) |
18 | GND | GNDへ |
PIRは感度と出力秒数をボリュームで、ジャンパで出力モードを指定できます。感度ボリュームは検知範囲の指定ですが、人間よりも小さい鳩が対象なので最大(7m程度)にします。出力秒数は動きが検知されなくなった後何秒ぐらい出力HIGHをキープするかを設定します。よくある侵入警告灯などで使う機能ですね。これは居なくなったらできるだけ早く消えて欲しいので最小にします。ジャンパは信号出力中に動きを検知した場合に点灯時間を延長するか最初の検知から指定時間を経過したらその間動きがあってもOFFにするかを切り替えます。内側は動いている間はずっと出力、外側は指定時間でOFF。今回は鳩の滞在時間を調べたいので、内側にします。上記の通り出力が延長されてしまうので正確な記録にはならないのですが…まぁ傾向はわかるかなと。
■ThingSpeakすげえ■
今回、ThingSpeak初めて使ったのですが、こういうWebサービスの中では今までで一番簡単でした。開発中、「そろそろThingSpeakへの書き込みコードを用意しておこう」と思ってコピペしてAPI KEYや変数などを書き換えてそのまま放置していたんですが、画面の隅で何か動いているものがあるなと思ったらThingSpeakのグラフでした。これだけでグラフになっちゃったのは初めてです。いやー、恐ろしい。
なお、ThingSpeakへの書き込みコードはここからコピーしました。ありがとうございます。Particleも良いんだけどね…I2Cが動かないのと何よりも技適がないのでどうにもなりまへん。
■ESPをArduino IDEで使う場合のピン番号■
動かしたヒトならみんなご存じだと思うのですが、探しても明記しているページが出てこなかったので書いておきます。
Arduino IDEでpinmode, digitalOutなどを使い場合に指定するピン番号は、ESPのIO4, IO5などと書いてある数字を使います。物理的なピン番号ではありませんのでお間違いのないように。
■ハマり所■
- 上にも書きましたが、ESP8266の電源はちゃんとインピーダンスを下げないと暴走します。それもArduino IDEが書き込み終わった直後に暴走(電解コンデンサ追加で改善)、動作するけど数分で止まる(タンタル4.7uF追加)、数十分で止まる(パスコンを基板上に直接ハンダ付けで改善)…と対策をすれば良くなりますので、安定して動作しないとお悩みの方はお試しあれ。
- Arduino HDC1000ライブラリはそのままでは動きません。Wire.beginでSDL, SCLのピン番号を指定する必要があります。HDC1000.cppのコンストラクタでWire.begin(); を Wire.begin(4,14); にします。
- 存在しないIO番号、例えば6を指定すると、Arduino IDEではエラーになりませんがpinmodeなどを実行した際にExceptionが出ます(シリアルコンソールにメッセージが出ます)。
- サーボモータを使うのはこれが初めてです。サーボモータは0-180度で動作するとのことなんですが、激安サーボとして有名なSG90は45-135度ぐらいしか動作しませんでした。その範囲外を指定すると唸り始めます。5個買ったうちの2個が同じ状態だったのですが、そういうもの?
- サーボモータをつないだままにしておくと信号が一定でもピクピクもぞもぞ動いていますが、これを止めるにはdetachします。使う時にまたatachします。消費電力の節減にもなります。
■ソース■
より高い威嚇効果を求めて複雑な動きをさせたら腕の動きが激しくなったときにサーボがモゲそうになったので、単純な動きにしていますが…RTOSあたりをつかって、ちゃんと加速度制御を考えた上で複雑な動きをさせたいものです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <ESP8266WiFi.h> | |
#include <WiFiClient.h> | |
#include <Servo.h> | |
#include <Wire.h> | |
#include <HDC1000.h> | |
const char *ssid = "arduino"; | |
const char *password = "1234567890qaz"; | |
WiFiClient client; | |
Servo myservo; | |
const int PIR = 5; | |
const int LED1 = 12; | |
const int LED2 = 13; | |
const int SERVO = 16; | |
HDC1000 hdc1000; | |
long pirDetected = 0; | |
long lastSent = 0; | |
void setup() | |
{ | |
Serial.begin(115200); | |
WiFi.begin ( ssid, password ); | |
// Wait for connection | |
while ( WiFi.status() != WL_CONNECTED ) { | |
delay ( 500 ); | |
Serial.print ( "." ); | |
} | |
Serial.println("Wifi Connected"); | |
pinMode(PIR, INPUT); | |
pinMode(LED1, OUTPUT); | |
pinMode(LED2, OUTPUT); | |
hdc1000.beginOnESP8266(); | |
ESP.wdtEnable(10); | |
Serial.println("Setup complete"); | |
} | |
int lastSec = 0; | |
int lastSecond = 0; | |
void loop() | |
{ | |
ESP.wdtFeed(); | |
int second = millis() / 1000; | |
if (second != lastSecond) { | |
lastSecond = second; | |
if ((second % 60) == 0) { | |
sendTempHumi(); | |
} | |
} | |
if (pirDetected > 0) { | |
long past = millis() - pirDetected; | |
int sec = past / 1000; | |
if (sec != lastSec) { | |
lastSec = sec; | |
myservo.write( (sec % 2) ? 45 : 135 ); | |
Serial.println("Move R/L"); | |
} | |
if (past > 5000) { | |
int onOff = (millis() / 100) % 2; | |
digitalWrite(LED1, onOff); | |
digitalWrite(LED2, 1 - onOff); | |
} else if (past > 0) { | |
int onOff = (millis() / 100) % 10; | |
onOff = (onOff <= 1) ? HIGH : LOW; | |
digitalWrite(LED1, onOff); | |
digitalWrite(LED2, onOff); | |
} | |
if (digitalRead(PIR) == LOW && millis()) { | |
digitalWrite(LED1, LOW); | |
digitalWrite(LED2, LOW); | |
myservo.detach(); | |
if (millis() - lastSent > 15000) { // wait for thing speak update | |
sendDetected(millis() - pirDetected); | |
lastSent = millis(); | |
} | |
pirDetected = 0; | |
} | |
} else { | |
if (digitalRead(PIR)) { | |
pirDetected = millis(); | |
myservo.attach(SERVO); | |
} else { | |
Serial.println("Waiting"); | |
delay(1000); | |
} | |
} | |
} | |
void sendDetected(int inDuration) { | |
String postStr = "&field3=" + String(inDuration); | |
send(postStr); | |
} | |
void sendTempHumi() { | |
String postStr = "&field1=" + String(hdc1000.getTemp()); | |
postStr += "&field2=" + String(hdc1000.getHumi()); | |
send(postStr); | |
} | |
void send(String inPostStr) { | |
String apiKey = "BV156445P6AUS8AU"; | |
Serial.print("Connecting..."); | |
if (client.connect("184.106.153.149", 80)) { // api.thingspeak.com | |
Serial.print("Connected...."); | |
String postStr = apiKey + inPostStr + "\r\n\r\n"; | |
client.print("POST /update HTTP/1.1\n"); | |
client.print("Host: api.thingspeak.com\n"); | |
client.print("Connection: close\n"); | |
client.print("X-THINGSPEAKAPIKEY: " + apiKey + "\n"); | |
client.print("Content-Type: application/x-www-form-urlencoded\n"); | |
client.print("Content-Length: "); | |
client.print(postStr.length()); | |
client.print("\n\n"); | |
client.print(postStr); | |
Serial.println("posted."); | |
} | |
client.stop(); | |
} | |
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。