人感センサー付きライトを手早く作ってみた、というお話

Reveです。久しぶりにブログ書きます。。。
(Twitter連動すると、ついつい書くのがおろそかになりますね((´・ω・`;)))

少し前にTwitterであげた(元ネタ:Twitter記事)のですが、家に使われていない人感センサーモバイルバッテリーがあったので、
近づいたら自動的に光るライトをちゃちゃっと作ってみました。

完成形はこんな感じです。
motionlight-s.jpg

このライトを作るのに使った主な部品は以下の通り。
・人感センサー(焦電型赤外線センサー)
・LED
・5V以上の電源
・Nch MOSFET(といくつかの電子部品)

まず、人感センサーとは人(というより熱源)の動きを捉えるセンサーです。
家にあったのは、下のような人を検知すると出力される(電圧がHになる)モジュールでした。


主なスペックはこちら。
電源(5V以上)と出力電圧(3.3V)が異なるのは注意点かと思います。
項目詳細
電源5~20V
出力L:0V, H:3.3V
その他検出距離と出力時間を調整可能

電源は簡便さを求めてモバイルバッテリー(cheero Canvas 3200mAh IoT機器対応)にしました。
5V、2.1A出力、容量が3200mAhで、最大の特徴が消費電流が少なくても自動的に切れないという点で、IoT機器の電源として使いやすいバッテリーです。

電源としては乾電池4本でもよかったのですが、交換の手間と、何より余っていたものを使いたかったのが最大の理由ですw

続いて、LEDは実に多くの種類がありますが、手っ取り早く作りたかったので、その辺にあった白色LEDを5個くらい使いました。
今回は秋葉原で数十円くらいで買える高輝度LEDを使いましたが、家の電灯と遜色ないような明るさを求めるのであれば、電力1Wや3Wと書かれたパワーLEDや、LED電球を使うのも考えた方が良いでしょう。ただ、電源も強力なもの(家庭用電源、リチウムイオンバッテリーとか)が必要ですし、とても発熱するので放熱する必要もあります。

そして制御用の回路ですが、簡単に作りたかったのでNch MOSFETでスイッチング回路を構成しました。
MOSFETによるスイッチング回路は、こちらを参考にしています。
https://www.marutsu.co.jp/pc/static/large_order/fet_3
MOSFETもいろいろな種類があります。LEDを十分に明るく光らせられない場合もあるので、ゲート電圧が低くても駆動するものを選びましょう。今回はこちらを使いました。


あとは、スイッチング回路のゲート部分とセンサーの出力ピンを接続し、LEDを電源の+側とドレイン部分の間に挟むようにすれば、センサーの出力に応じてLEDを制御できるようになります。

テーマ : 電子工作
ジャンル : 趣味・実用

ESP-WROOM-02にArduinoでプログラムを書き込む(3)

前回に続き、いよいよプログラミングに入っていきます。

【プログラミング】
早速、ソースプログラムすべてを載せます。
27行目のSSIDと28行目のセキュリティキーは、接続するネットワークのものを入れましょう。

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
extern "C"{
#include "user_interface.h"
}

#define LEDPIN 0

const char INDEX_HTML[] =
"<!DOCTYPE HTML>"
"<html lang=¥"ja¥">"
"<head>"
"<meta charset=¥"UTF-8¥">"
"<title>ESP8266 LED web turn ON-OFF</title>"
"<style></style>"
"</head>"
"<body>"
"<form>"
"<button type=¥"submit¥" name=¥"led¥" value=¥"lighton¥">LED On</button>"
"<button type=¥"submit¥" name=¥"led¥" value=¥"lightoff¥">LED Off</button>"
"</form>"
"</body>"
"</html>";

const char* ssid = "Your SSID";
const char* password = "Your Security Key";

ESP8266WebServer server(80);

/*
* root process for receiving server request successfully
*/
void handleRoot(){
if(server.hasArg("led")){
handleSubmit();
}else{
server.send(200, "text/html", INDEX_HTML);
}
}

void handleSubmit(){
if(!server.hasArg("led")){
return returnFail("BAD ARGS");
}

char temp[400];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;

snprintf(temp, 400, INDEX_HTML, hr, min % 60, sec % 60);

if(server.arg("led") == "lighton"){
digitalWrite(LEDPIN, HIGH);
server.send(200, "text/html", temp);
}else if(server.arg("led") == "lightoff"){
digitalWrite(LEDPIN, LOW);
server.send(200, "text/html", temp);
}else{
returnFail("Bad LED value");
}
}

/*
* Irregular response for server request
*/
void handleNotFound(){
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);
}

void handleLEDon(){
digitalWrite(LEDPIN, HIGH);
returnOK();
}

void handleLEDoff(){
digitalWrite(LEDPIN, LOW);
returnOK();
}

void returnOK(){
server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(200, "text/plain", "OK¥r¥n");
}

void returnFail(String msg){
server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(500, "text/plain", msg + "¥r¥n");
}

void setup() {
pinMode(LEDPIN, OUTPUT);
digitalWrite(LEDPIN, LOW);

Serial.begin(115200);
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 start up
server.on("/", handleRoot); //on access
server.on("/lighton", handleLEDon);
server.on("/lightoff", handleLEDoff);
server.on("/inline", [](){
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound); //not found event

server.begin();
Serial.println("HTTP server started");
Serial.print("Access to http://esp8266WebForm.local or http://");
Serial.println(WiFi.localIP());
}

void loop() {
//int adc = system_adc_read(); //TOUT(pin 16)
server.handleClient();
}

これを、前回で環境構築したESP-WROOM-02に書き込むことで、WebブラウザからESP-WROOM-02にアクセスし、表示された2種類のボタン(On/Off)からLEDを点灯、あるいは消灯させることができます。
IMG_20170914_142946.jpg

それでは、ソースを上から順に追っていきましょう。
まずは必要なライブラリをインクルードしていきます。

#include <ESP8266WiFi.h>
#include <WiFiClient.h> //クライアント処理
#include <ESP8266WebServer.h> //サーバー処理
#include <ESP8266mDNS.h> //DNS設定(独自のドメイン名でアクセスできるようにする)

続いて、INDEX_HTMLという文字列(char配列)が出てきますが、こちらはESP_WROOM_02に格納されたHTMLファイルで、いわばアクセスするサイトの中身ということになります。

const char INDEX_HTML[] =
"<!DOCTYPE HTML>"
"<html lang=¥"ja¥">"
"<head>"
"<meta charset=¥"UTF-8¥">"
"<title>ESP8266 LED web turn ON-OFF</title>"
"<style></style>"
"</head>"
"<body>"
"<form>"
"<button type=¥"submit¥" name=¥"led¥" value=¥"lighton¥">LED On</button>"
"<button type=¥"submit¥" name=¥"led¥" value=¥"lightoff¥">LED Off</button>"
"</form>"
"</body>"
"</html>";

直下の部分は、接続するWi-FiネットワークのSSIDとパスワード、そしてサーバーのインスタンスを生成します。
今回はSSIDとパスワードは決め打ち、サーバーはポート80番で接続するようにしています。

const char* ssid = "Your SSID";
const char* password = "Your Security Key";

ESP8266WebServer server(80);

この後は、クライアントからのアクセスに対するコールバックメソッドを定義しています。
まず、handleRootは最初にアクセスを受けた際のイベント処理を定義し、アドレスにledというパラメータがあればhandleSubmitを呼び出し、それ以外は格納されているHTMLファイルをそのまま開くという処理になっています。

/*
* root process for receiving server request successfully
*/
void handleRoot(){
if(server.hasArg("led")){
handleSubmit();
}else{
server.send(200, "text/html", INDEX_HTML);
}
}

void handleSubmit(){
if(!server.hasArg("led")){
return returnFail("BAD ARGS");
}

char temp[400];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;

snprintf(temp, 400, INDEX_HTML, hr, min % 60, sec % 60);

if(server.arg("led") == "lighton"){
digitalWrite(LEDPIN, HIGH);
server.send(200, "text/html", temp);
}else if(server.arg("led") == "lightoff"){
digitalWrite(LEDPIN, LOW);
server.send(200, "text/html", temp);
}else{
returnFail("Bad LED value");
}
}

続いて、handleNotFoundはサーバー側、つまりESP-WROOM-02にエラーが発生した場合の処理を定義します。

/*
* Irregular response for server request
*/
void handleNotFound(){
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);
}

こちらは、ブラウザのボタンを押した際のイベントを定義します。
(文字通り)handleLEDonが点灯、handleLEDoffが消灯の処理となります。

void handleLEDon(){
digitalWrite(LEDPIN, HIGH);
returnOK();
}

void handleLEDoff(){
digitalWrite(LEDPIN, LOW);
returnOK();
}

そして、クライアントへ送るステータスメッセージの内容を定義します。
イベント処理ではないのですが、前述のイベントメソッドの一部で呼び出されます。returnOKがアクセス成功、returnFailがアクセス失敗をクライアントに通知するメソッドです。

void returnOK(){
server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(200, "text/plain", "OK¥r¥n");
}

void returnFail(String msg){
server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(500, "text/plain", msg + "¥r¥n");
}

一連の準備が完了したところで、いよいよメイン処理に移ります。
まず、setupメソッドで出力ピン設定、シリアル通信とWi-Fi、そしてサーバーを起動します。
ピン設定は、IO0を出力ピンにするため、0(LEDPIN)をデジタル出力と設定します。
続いて、Wi-Fiおよびシリアル通信ですが、まずWiFi.biginメソッドで接続先のSSIDとパスワードを入れてネットワークへの接続を確立します。Wi-Fiへの接続は、WiFi.status()WL_CONNECTEDになるまで待機しています。
ESP-WROOM-02とのシリアル通信は必須ではないのですが、デバッグ処理に便利なため設定しています。ただ、ESP-WROOM-02の初期設定がボーレート115200bpsとなっているため、プログラム側でも同じ速度に設定します。
最後のサーバー設定は、大まかに(1)コールバック設定、(2)サーバー立ち上げに分かれます。
まず、コールバック設定については、前述のコールバックメソッドを各イベントに合わせて設定します。server.onはクライアントのアクセス時、server.onNotFoundはサーバー内部のエラー発生時の処理を与えます。
必要なコールバック処理を追加したら、server.beginでサーバーとして起動します。

void setup() {
//pin configuration (IO 0 as output)
pinMode(LEDPIN, OUTPUT);
digitalWrite(LEDPIN, LOW);

//Serial setting
Serial.begin(115200);
//Wi-Fi initialization (input SSID and password to enter)
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());

//start DNS
if(MDNS.begin("esp8266")){
Serial.println("MDNS responder started");
}

//Server start up
server.on("/", handleRoot); //on access
server.on("/lighton", handleLEDon);
server.on("/lightoff", handleLEDoff);
server.on("/inline", [](){
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound); //not found event

server.begin();
Serial.println("HTTP server started");
Serial.print("Access to http://esp8266WebForm.local or http://");
Serial.println(WiFi.localIP());
}

最後のメインループは、常にクライアントからのアクセスを待機するのみです。

void loop() {
server.handleClient();
}


なお、今回使用したモジュール(部品については初回参照)の注意点として、出力に使えるピン番号が0, 2のみのため、プログラムのピン設定も0、あるいは2のどちらかを設定してください。
ちなみに、アナログ出力(PWM)は可能ですが、入力ピンとしてはどちらも一切使えないため、実質このモジュールは出力ピンが2つだけとなっています。

【テスト】
プログラムを書き込んだら、さっそくテストしてみましょう。
手順は以下の通り。
(1)Webブラウザを開く
(2)IPアドレス(あるいはhttp://esp8266WebForm.local)を入力してアクセス
(3)サイトに出てきたボタン(LED On / LED Off)を押す
(4)回路のLEDが点灯/消灯すれば成功


これですべての実装が終わりました。いかがだったでしょうか。
まだまだいろいろと必要な知識も残っていますが、これがIoT電子工作の入門になれば幸いです。
では(・ω・)ノ))

テーマ : 電子工作
ジャンル : 趣味・実用

ESP-WROOM-02にArduinoでプログラムを書き込む(2)

こんにちは、Reveです。
前回に引き続き、ESP-WROOM-02IoT電子工作っぽいことをする記事です。
今回はいよいよ開発環境の準備に入ります。

【電子回路】
ここでは、ESP-WROOM-02にファームウェアを書き込むための回路を作っていきます。
(前回出てきたESPr Developerを使う場合は不要)

早速ですが、回路図です。
ESP8266_program_circuit_r1.jpg


(青の楕円はコンデンサ、角丸の黒い長方形は抵抗です。)

ちなみに、当方は秋月電子のXBeeインタフェースボードで代用していましたが、ピンの対応は以下の通り。
特にTX、RXピンはデータシートのDIN、DOUTと逆に配線しなければならない事に注意です(FTDI USBシリアル通信ICとXBeeが対に配線されているため)。
3.3V: 1(VCC)
TX: 3(<- XBeeのDINと対)
RX: 2(<- XBeeのDOUTと対)
RTS: 16(RTS)
DTR: 9(DTR)
GND: 10(GND)


配線をつないだ写真がこちら。
ESP8266_circuit_pic1

回路の機能は2つあります。
(1)USBシリアル通信モジュールとの接続
(2)自動プログラム用のモード切替(任意)


ジャンパ切り替えやリセットボタン押下なしの自動プログラム (Auto Program)を実行できるようにしており、NodeMCUのシステムを基に実装しています。
NodeMCUはWi-Fiモジュールの一つでESP-WROOM-02より安いのですが、技適を通っておらず、日本国内では使用できません。ですが、回路や自動プログラムのシステムは流用できるため、これを導入することにしました。

【プログラム開発環境】
続いて、プログラミング環境の構築に移ります。
まずは、Arduino IDEの環境設定に入ります。
esp8266_arduino01.png

Arduinoの開発環境で、「追加するボードマネージャー」に以下のURLを貼り付けます。
http://arduino.esp8266.com/stable/package_esp8266com_index.json
esp8266_arduino02.png

そのあと、ボードマネージャーを開きます。
esp8266_arduino03.png

ESP8266モジュールをインストールします。
esp8266_arduino04.png

ESP8266へプログラムを書き込むための設定として、「Generic ESP8266 Module」を選び、Reset Methodを「NodeMCU」とします。
esp8266_arduino05.png

esp8266_arduino06.png

以上でプログラムの開発環境も構築できました。
ファームウェア制作とテストについては次の記事で書いていく予定です。 → 更新しました。

【参考】
自動プログラムのほうは、こちらの回路を参考にしました。
http://ht-deko.com/arduino/esp-wroom-02.html#13_08

続きを読む

テーマ : 電子工作
ジャンル : 趣味・実用

ArduinoのSDカードからファイル名を一覧で取得する

こんにちは、Reveです。
このところtwitterでのつぶやき連携ばかりだったのですが、今日はちゃんとブログ記事を書こうかと思い、
久々のArduinoネタでつらつらと書いていきます。
(仕事しててちょうどネタが見つかったので)

ArduinoSDカード
このブログをご覧いただいている皆様はおおよそ承知かと思いますが、
Arduinoというのは電子回路の試作開発などに大変便利なマイコンボードで、世界で最も普及しているプロトタイピングツールといっても過言ではないでしょう。

そんなArduinoSDカードの読み書きも標準のライブラリから可能で、まさに至れり尽くせりなツールなのですが、
今日はSDカードの読み込みで、特定のファイルの名前だけを一覧で取り出す方法を記事にしたいと思います。

【ファイル名の一覧を作成】
ArduinoSDカード用ライブラリではファイル名の取得も当然できるのですが、
例えばSDカード内にあるファイルを一定時間ごとにランダムで開きたい時などは、わざわざ毎回SDカードにアクセスして調べると処理が重くなる可能性もあります。

かといって、事前にファイル名をArduinoのプログラム側に書いておくのも実装は楽ですが、頻繁にファイルを入れ替える場合は毎回わざわざ書き換えるのも面倒です。

そこで、SDカードに入っているファイルをいったん調べ、そのファイル名を配列のようにリスト化してしまえば動的に処理できるし、アクセス速度や負荷の心配もまずありません。

【実装手法とサンプル】
早速、サンプルを見てみましょう。
ここではルートフォルダ、つまりSDカードの一番上のフォルダにあるファイルをすべて読み込む処理になっています。

//SDカードライブラリの読み込み
#include <SD.h>
//SDライブラリで、SPI通信のライブラリも使うので、一緒に読み込む
#include <SPI.h>

//ファイル名を記憶しておくリスト変数
char** nameList;
//リスト内に含まれるファイル数
int numList;

void setup(){
//シリアル通信の設定
Serial.begin(9600);
while(!Serial){
//シリアル通信が準備できるまでの待機。Leonardoなどでのみ必須
}

//SDカードの読み込み
if(!SD.begin(4)){ //4番ピンをアクセス制御ピンとして使う
//SDカードの初期読み込みができない場合はエラー
Serial.println("initialization failed!");
return;
}

//内部のファイルにアクセス
File root = SD.open("/"); //SDカードのルートフォルダ
String listTemp = "";
while (true) {
File entry = root.openNextFile();
if (!entry) {
//これ以上ファイルがない場合
break;
}

//ディレクトリ名ではない場合
if (!entry.isDirectory()) {
String fileName = entry.name();
listTemp += String(fileName);
listTemp += ",";

/*
//特定の種類のファイルだけを選び出すことも可能(ここでは音楽ファイルを抽出)
//ファイルの拡張子を取り出す
//一番後ろのピリオドから後の文字を抽出
String ext = fileName.substring(fileName.lastIndexOf('.'));
//拡張子が指定のものだけを入れていく
if (ext.equalsIgnoreCase(".wav") || ext.equalsIgnoreCase(".mp3")) { //大文字か小文字かを無視する
listTemp += String(fileName);
listTemp += ",";
}
*/
}
else{
//ディレクトリ内部を 検索する場合は、再起関数として同じ処理を呼び出す
}
}

if (listTemp.length() > 0) {
//Serial.println(listTemp);

//リストの要素数を数え上げる
for (int i = 0; i < listTemp.length(); i++) {
i = listTemp.indexOf(',', i); //コンマの位置を探す
numList++;
}

//リストの初期化
nameList = new char*[numList];

for (int i = 0; i < numList; i++) {
//カンマの位置を見つけ、
int index = listTemp.indexOf(',');
String temp = String(listTemp.substring(0, index));
nameList[i] = new char[temp.length() + 1];
temp.toCharArray(nameList[i], temp.length() + 1);
Serial.println(nameList[i]);
listTemp.remove(0, index + 1);
}

Serial.println(numList);
}
else {
//ファイルが見つからなかった場合は強制終了
return;
}
}

void loop(){
//リストを利用する
}


今回のポイントは主に2つ。
1) ライブラリSDのFileクラスによるフォルダ検索とファイル名取得
2) ポインタのポインタによる文字列のリストの動的生成


これらを順番に見ていきましょう。
1) ライブラリSDのFileクラスによるフォルダ検索とファイル名取得
SDカードのライブラリでは、Fileクラスというものでファイル情報を管理します。
参照する親元を指定した後は、中のファイル情報を順番にFileクラスで取得していく流れになっています。

順序としては、以下のようになっています。
(a) SDカードの初期化: SD.begin(4);
(b) 参照するフォルダ(ルートフォルダ)を指定: File root = SD.open("/");
(c) フォルダ内のファイル名を順番に見ていく: File entry = root.openNextFile();
(d) ファイルがディレクトリでなければ、ファイル名を取得: if(!entry.isDirectory()){String filename = entry.name();}

なお、SDカードとArduinoのつなぎ方についてはこちらのリンク先を参照。
http://arms22.blog91.fc2.com/blog-entry-502.html

2) ポインタによる文字列のリストの動的生成
Arduinoは基本的にC++ベースですが、動的配列のテンプレートライブラリであるvector等は入っていないため、可変長の配列を実現するにはポインタなどのメモリ操作が必要になってきます。

ここでの処理は以下のようになります。
(a) グローバル変数として配列(ポインタ)を確保: char** nameList;
(b) ファイルの数をもとに配列の初期化: nameList = new char*[numList];
(c) 配列の中にそれぞれ値をコピーしていく

値のコピーについては色々と手法がありますが、今回はファイル名をStringクラスで取得しているため、toCharArrayメソッドで配列の要素へと値を入れています。

ちなみに、今回は文字列配列のため、要素それぞれの長さが別々になっています。
(C#でいうところのジャグ配列に近いイメージ)。
そのため、要素数を配列から直接求めるのは難しいので、要素数は別の変数で記憶しておきましょう。


と、このような形でファイル名のリストが作れるので、メインの処理などでファイルを参照するときにこのリストでファイル名を取得して操作するということが可能になります。
ぜひ試してみてください。

続きを読む

テーマ : 電子工作
ジャンル : 趣味・実用

AVRマイコンでウォッチドッグタイマーを一番楽に動かす方法(6/29修正)

どーもー、Reveです。
今日は久しぶりに電子工作ネタです。

Arduinoを扱っている関係で、AVRマイコンを直接いじることもあるのですが、
今まで触ったことが無かったウォッチドッグタイマーを動かすことができたので、備忘録を兼ねてその手法を記していこうかと。

【AVRマイコン】
Atmel社が出しているマイコンのシリーズで、Arduinoでも(ATMega328P)使用されています。
特に、ATTiny13A-PUというマイコンは秋月電子でなんと50円という超低価格の割に機能も豊富で便利なので、簡単な電子工作ではとても重宝します。

当方もこのATTiny13Aをよく利用するのですが、今回はそのウォッチドッグタイマー機能を簡単に利用する方法を書いていきます。

【ウォッチドッグタイマー】
このウォッチドッグタイマー(以下、WDT)とはシステムの監視を行うためのタイマーで、通常では異常事態が発生した際にリセットをかけてシステムを再スタートさせるのですが、通常のタイマーとしての利用も可能です。

実はマイコンのスリープ機能(休止モード)で、メインクロックが停止したときもWDTは動作するので、スリープからの復帰に使えたりします。あとは他のタイマーをすでに使っているときとか

もちろん、ATTiny13Aにも搭載されているのですが、今日はこの機能をレジスタをいじることなく使用して行きます。

なお、WDTをタイマーとして使う場合、最大で8秒までしか計測できないため、それ以上長い時間は変数を設けるなど工夫する必要があります。

【使い方】
では、どうすればWDTを使えるかというと、開発環境が提供するライブラリを駆使します。
Atmel社が提供している公式の開発環境(Atmel Studio 7)がVisual Studioベースでなかなか使いやすく、ライブラリも豊富(今回使うライブラリも含む)なので、こちらを導入しましょう。
下のリンクからインストーラをダウンロードして実行すればOKです。
http://www.atmel.com/Microsite/atmel-studio/

開発環境を入れたら、コードの書き方に入っていきましょう。
順を追って解説していきます。

1. ライブラリの導入(インクルード)
avr/wdt.hというライブラリをインクルードします。
これにはWDTを使うための関数が3つ用意されています。
・wdt_reset() : WDTの設定をリセットするための関数
・wdt_enable(value) : WDTを有効にする関数。valueに定数を入れて時間を設定する
・wdt_disable() : WDTを無効にする関数


ライブラリと関数については、このリンク先に詳細が載っています。
http://cega.jp/avr-libc-jp/group__avr__watchdog.html

(6/29追記)
関数wdt_enableでWDTを起動した場合、タイマーの設定時間が経過するとリセットがかかるようです。
通常のタイマーのように使う手法は、手順3.に記載しています。

2. WDT発動後に再起動を繰り返す問題の対策
どうやら最近のAVRマイコンは、WDTの発動後もタイマーが継続するので、次回以降は最速のタイミング(15ms)でリセットを繰り返すという動作が起きてしまうそうで、意図しない誤作動を防ぐために入れています。

(参照) 電子牛乳様 AVR がウォッチドッグタイマ発動後に再起動を繰り返す問題
http://milkandlait.blogspot.jp/2014/06/avr_3.html

3. WDTの割り込み処理
WDTの設定時間が経過した際の割り込み処理を記述します。

AVRマイコンでは割り込み処理をISRという名前の関数に実装します。
ISRの引数は、割り込みのイベントを指定するもので、例えばタイマーの時間経過やスイッチ入力などがありますが、今回はWDTなのでWDT_vectを入れます。

あとは関数内に好きな処理を実装するのですが、wdt_disableを入れておくとWDTを一時停止できるので、WDT発動後の意図しないリセットを防ぐことができます。

(6/29追記)
前述のとおり、wdt_enableでは割り込み処理後にリセットがかかります。
通常のタイマー割り込みとして使うためには関係するレジスタ(WDTCR)を設定する必要があります。

WDTCRにタイマー割り込みの設定をするには、レジスタ内の
・WDCEを1にする
・WDTIEを1にして、時間を設定するための定数を入れる
と、通常のタイマーのようにWDTを使うことができます。

なお、設定の直前に割り込みを無効にし、直後に再び割り込みを無効にするのが推奨されているようです。

以上の設定が簡単にできるよう、自作の関数を定義すると良いでしょう。

void WDT_start(int wt)
{
//disable interrupt
cli();
//watchdog timer reset
wdt_reset();
//watchdog timer change enable, use for interruption
WDTCR |= (1 << WDCE) | (1 << WDTIE);
//interruption interval
WDTCR |= wt;
//enable interrupt
sei();
}


4. WDTの実行
以上の準備が終わったら、任意のタイミングでWDTが起動するように関数を記述します。
wdt_enableを必要な場所に記述することでその場所からWDTが起動します。
時間の設定は、引数に指定の定数を入れることで可能になります。

下に書き方をソースで載せました(WDTの実装のみなので、入出力ピンなどの処理は追加してください)。

//1. ライブラリの導入
//他に必要なライブラリは適宜インクルードする
#include <avr/wdt.h>

//2. WDT発動後に再起動を繰り返す問題の対策
//詳しくはこのリンクを参照
//http://milkandlait.blogspot.jp/2014/06/avr_3.html
uint8_t mcusr_mirror __attribute__ ((section (".noinit")));
void get_mcusr(void) __attribute__((naked)) __attribute__((section(".init3")));
void get_mcusr(void)
{
mcusr_mirror = MCUSR;
MCUSR = 0;
wdt_disable();
}

//3. WDTの設定
//WDTの設定と起動
void WDT_start(int wt)
{
//disable interrupt
cli();
//watchdog timer reset
wdt_reset();
//watchdog timer change enable, use for interruption
WDTCR |= (1 << WDCE) | (1 << WDTIE);
//interruption interval
WDTCR |= wt;
//enable interrupt
sei();
}

//WDTのタイマー時間経過時の割り込み処理
ISR(WDT_vect)
{
//処理内容を記述

//WDTの無効
wdt_disable();
}

//4. WDTの起動(メイン処理内)
int main(void)
{
while (1)
{
//WDTの許可
//WDTの時間はライブラリ内の定数で指定(WDTO_1Sで1秒)
WDT_start(WDTO_1S);

//設定した時間が経過した際にリセットをかけたい場合は、この関数を使う
//wdt_enable(WDTO_1S);
}
}


と、こんな感じでライブラリを駆使すればすぐにWDTが使えます。
ぜひやってみようw

続きを読む

プロフィール

Reveちゃん

Author:Reveちゃん
コンビでやってます。
夢担当と技術担当がいます。

大学院卒業 → ロボットベンチャー(漆黒)就職 → 1年で退職 → ベトナムで仕事中(今ここ) → メディアアーティスト(未来☆)

リンクフリーです。

最新記事
最新コメント
月別アーカイブ
カテゴリ
アクセス数
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR