今回レビューするのは、インテルのマイコンキット、「Edison Kit for Arduino」です。
「Edison Kit for Arduino」はIntel Edison モジュール(本体)とIntel Edison Board for Aduino(Arduino互換拡張ボード)がセットになったものです。
インテル® Edison モジュール ― 小さい 1 つのプラットフォームに無限の可能性
私は、この手のものを一切触ったことがない、紛うことなき初心者なので、まずはスケッチサンプルを実際に試しながら学習して、オリジナル試作機の開発を目指します。
※パッケージと内容物(製品の写真等)とGetting Started(組み立てや初期設定等)は文末にあります。
2015/11/09「7-4. Eclipse + mraa_pwmで7-2と同じ機能のプログラムを書く」を追記
2015/11/11「7-4. Eclipse + mraa_pwmで7-2と同じ機能のプログラムを書く」を更新
2015/11/12「8. 焦電型赤外線センサの使い方を学習して、焦電型赤外線センサを使った試作機を開発する」を追記
2015/11/12「8-1. 焦電型赤外線センサを使った警報器的な試作機」を追記
EdisonとArduino、Arduino拡張ボードについて
まずは基礎の確認です。
Edison(Intel Edison - Wikipedia)はインテルのIoT向けの組み込み用超小型開発プラットフォーム。
Arduino(Arduino - Wikipedia)は、AVRマイコン、入出力ポートを備えた基板、C++風のArduino言語とそれの統合開発環境から構成されるシステム。
Arduino拡張ボードは、EdisonをArduino互換にする拡張ボード。
という事で、Edisonのレビューですが、内容の多くがArduinoな感じになりそうです。
英語
日本語
EdisonでのArduinoについて
一般的なArduinoではOSを使っていませんが、EdisonはYoctoというLinuxベースの組み込みOSを使っています。EdisonでのArudinoスケッチは、このOSのプロセスとして動作していて、Arduino IDEとのやり取りやスケッチの管理等を行うサービスが実行されているようです。
Arduino IDEからアップロードしたスケッチは/sketch/sketch.elfという実行ファイルになります。また、一般的なArduinoと違いEdisonでは、内容的に競合しないようなら、複数のスケッチを同時に実行する事ができます。
アップロードしたスケッチをシェルで実行したい時は
/sketch/sketch.elf /dev/pts/0
で実行できます。※CTRL + Cで強制終了
複数実行したい場合等、コピーする時は
cp /sketch/sketch.elf /sketch/****.elf
でコピーを作る事ができます。
基本的にEdisonのシェルや仕組みはYocto、Linux等のOSと同じなので、それらの解説が参考になります。
1. 3つのスケッチで学習して、オリジナル試作機を開発する
今回のレビューテーマは「スケッチサンプルを活用してオリジナル試作機を開発しよう!」という事なので、Arduino IDEを使って3つのスケッチのコードや仕組みを学習して、それらを活用(応用)する事で、オリジナル試作機を開発します。
学習に使用するスケッチは以下の3つです。
Communication 通信
04. Virtual Color Mixer | 複数のアナログ入力読み込み
3つのポテンシオメーターの値を読み取り、それらをRGB値に変換してシリアルモニタ上の画面背景色として表示。
Liquid Crystal Display (LCD) 液晶画面
07. Serial Display | ユーザ入力で文字列を表示
シリアルモニタから入力した文字を、液晶画面に表示する。
Wi-Fi 無線ネットワーク
02. Web Server | アナログ入力値を表示するウェブサーバ
入力デバイスにポテンシオメーターを使い、その値を同じネットワーク上のHTMLブラウザに表示する。
部品はスイッチサイエンスに「Make it with Edison」トライアルキット by Edison Labという、Edison Labの「スケッチとコードのサンプル集」で使われる部品を集めたキットがあるので、こちらを使います。
1-1. スケッチ1: Virtual Color Mixer
Edison Labに「3つのポテンシオメーターの値を読み取り、それらをRGB値に変換してシリアルモニタ上の画面背景色として表示。」と書いてありますが、Arduino IDEを使った場合は、Edison Labにも最後に書いてありますが「シリアルモニタでポテンシオメータの値を観察することができる。」です。画面背景色として表示させる場合は、Processing(Processing - Wikipedia)を使う必要があります。コード途中にある「Processing code for this example」から下の部分がProccessing用のコードです。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- ブレッドボード
- トリマポテンシオメータ 3個
- ジャンプワイヤ
ブレッドボード(ブレッドボード - Wikipedia)は、電子回路の試作、実験用の基板です。日本でブレッドボードというと、はんだ付けが不要な「ソルダーレス・ブレッドボード」の場合が多いようです。数字、+、-がそれぞれ中で一直線に繋がっています。製品によって形状、大きさ、穴の数などが違うし、繋がり方を同じにすればいいだけなので、Edison Labの手順の画像と同じ配置にする必要はありません。
ここでは、トリマポテンシオメータ(ポテンショメータ - Wikipedia)を使って、抵抗値を変化させることで、アナログ入力の値を変化させています。
回路を組み終わったら、次はスケッチ(プログラム)です。
Arduinoでは、Arduinoボードにアップロードして実行するひとまとまりのコードをスケッチと呼びます。プログラム言語はC、C++をベースにしています。
スケッチは、setup()とloop()という関数を必ず使います。
setup()は開始時に一度呼び出される関数で初期化などを行います。
loop()はsetupの後に呼び出される関数で、その名の通り繰り返されます。
それではコードが何をしているのか上から順番に確認していきます。
/*から*/までの間と//の後ろはコメントで、Arduinoから無視されます。
const int redPin = A0; // sensor to control red color
const int greenPin = A1; // sensor to control green color
const int bluePin = A2; // sensor to control blue color
redPinをanalog input 0(14)、greenPinをanalog input 1(15)、bluePinをanalog input 2(16)で宣言します。
void setup() {
Serial.begin(9600);
}
9600bpsでシリアル通信を開始します。
void loop() {
Serial.print(analogRead(redPin));
Serial.print(",");
Serial.print(analogRead(greenPin));
Serial.print(",");
Serial.println(analogRead(bluePin));
}
赤、緑、青の順で間に","を入れてシリアルモニタに書き込みます。loopの中なのでシリアルモニタには「赤の値,緑の値,青の値」が連続で表示されます。トリマポテンシオメータの値を変化させると、シリアルモニタに表示される値が変化します。
コード全文
/*
This example reads three analog sensors (potentiometers are easiest)
and sends their values serially. The Processing and Max/MSP programs at the bottom
take those three values and use them to change the background color of the screen.
The circuit:
* potentiometers attached to analog inputs 0, 1, and 2
http://www.arduino.cc/en/Tutorial/VirtualColorMixer
created 2 Dec 2006
by David A. Mellis
modified 30 Aug 2011
by Tom Igoe and Scott Fitzgerald
This example code is in the public domain.
*/
const int redPin = A0; // sensor to control red color
const int greenPin = A1; // sensor to control green color
const int bluePin = A2; // sensor to control blue color
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.print(analogRead(redPin));
Serial.print(",");
Serial.print(analogRead(greenPin));
Serial.print(",");
Serial.println(analogRead(bluePin));
}
/* Processing code for this example
// This example code is in the public domain.
import processing.serial.*;
float redValue = 0; // red value
float greenValue = 0; // green value
float blueValue = 0; // blue value
Serial myPort;
void setup() {
size(200, 200);
// List all the available serial ports
// if using Processing 2.1 or later, use Serial.printArray()
println(Serial.list());
// I know that the first port in the serial list on my mac
// is always my Arduino, so I open Serial.list()[0].
// Open whatever port is the one you're using.
myPort = new Serial(this, Serial.list()[0], 9600);
// don't generate a serialEvent() unless you get a newline character:
myPort.bufferUntil('\n');
}
void draw() {
// set the background color with the color values:
background(redValue, greenValue, blueValue);
}
void serialEvent(Serial myPort) {
// get the ASCII string:
String inString = myPort.readStringUntil('\n');
if (inString != null) {
// trim off any whitespace:
inString = trim(inString);
// split the string on the commas and convert the
// resulting substrings into an integer array:
float[] colors = float(split(inString, ","));
// if the array has at least three elements, you know
// you got the whole thing. Put the numbers in the
// color variables:
if (colors.length >=3) {
// map them to the range 0-255:
redValue = map(colors[0], 0, 1023, 0, 255);
greenValue = map(colors[1], 0, 1023, 0, 255);
blueValue = map(colors[2], 0, 1023, 0, 255);
}
}
}
*/
/* Max/MSP patch for this example
----------begin_max5_patcher----------
1512.3oc4Z00aaaCE8YmeED9ktB35xOjrj1aAsXX4g8xZQeYoXfVh1gqRjdT
TsIsn+2K+PJUovVVJ1VMdCAvxThV7bO7b48dIyWtXxzkxaYkSA+J3u.Sl7kK
lLwcK6MlT2dxzB5so4zRW2lJXeRt7elNy+HM6Vs61uDDzbOYkNmo02sg4euS
4BSede8S2P0o2vEq+aEKU66PPP7b3LPHDauPvyCmAvv4v6+M7L2XXF2WfCaF
lURgVPKbCxzKUbZdySDUEbgABN.ia08R9mccGYGn66qGutNir27qWbg8iY+7
HDRx.Hjf+OPHCQgPdpQHoxhBlwB+QF4cbkthlCRk4REnfeKScs3ZwaugWBbj
.PS+.qDPAkZkgPlY5oPS4By2A5aTLFv9pounjsgpnZVF3x27pqtBrRpJnZaa
C3WxTkfUJYA.BzR.BhIy.ehquw7dSoJCsrlATLckR.nhLPNWvVwL+Vp1LHL.
SjMG.tRaG7OxT5R2c8Hx9B8.wLCxVaGI6qnpj45Ug84kL+6YIM8CqUxJyycF
7bqsBRULGvwfWyRMyovElat7NvqoejaLm4f+fkmyKuVTHy3q3ldhB.WtQY6Z
x0BSOeSpTqA+FW+Yy3SyybH3sFy8p0RVCmaMpTyX6HdDZ2JsPbfSogbBMueH
JLd6RMBdfRMzPjZvimuWIK2XgFA.ZmtfKoh0Sm88qc6OF4bDQ3P6kEtF6xej
.OkjD4H5OllyS+.3FlhY0so4xRlWqyrXErQpt+2rsnXgQNZHZgmMVzEofW7T
S4zORQtgIdDbRHrObRzSMNofUVZVcbKbhQZrSOo934TqRHIN2ncr7BF8TKR1
tHDqL.PejLRRPKMR.pKFAkbtDa+UOvsYsIFH0DYsTCjqZ66T1CmGeDILLpSm
myk0SdkOKh5LUr4GbWwRYdW7fm.BvDmzHnSdH3biGpSbxxDNJoGDAD1ChH7L
I0DaloOTBLvkO7zPs5HJnKNoGAXbol5eytUhfyiSfnjE1uAq+Fp0a+wygGwR
q3ZI8.psJpkpJnyPzwmXBj7Sh.+bNvVZxlcKAm0OYHIxcIjzEKdRChgO5UMf
LkMPNN0MfiS7Ev6TYQct.F5IWcCZ4504rGsiVswGWWSYyma01QcZgmL+f+sf
oU18Hn6o6dXkMkFF14TL9rIAWE+6wvGV.p.TPqz3HK5L+VxYxl4UmBKEjr.B
6zinuKI3C+D2Y7azIM6N7QL6t+jQyZxymK1ToAKqVsxjlGyjz2c1kTK3180h
kJEYkacWpv6lyp2VJTjWK47wHA6fyBOWxH9pUf6jUtZkLpNKW.9EeUBH3ymY
XSQlaqGrkQMGzp20adYSmIOGjIABo1xZyAWJtCX9tg6+HMuhMCPyx76ao+Us
UxmzUE79H8d2ZB1m1ztbnOa1mGeAq0awyK8a9UqBUc6pZolpzurTK232e5gp
aInVw8QIIcpaiNSJfY4Z+92Cs+Mc+mgg2cEsvGlLY6V+1kMuioxnB5VM+fsY
9vSu4WI1PMBGXye6KXvNuzmZTh7U9h5j6vvASdngPdgOFxycNL6ia1axUMmT
JIzebXcQCn3SKMf+4QCMmOZung+6xBCPLfwO8ngcEI52YJ1y7mx3CN9xKUYU
bg7Y1yXjlKW6SrZnguQdsSfOSSDItqv2jwJFjavc1vO7OigyBr2+gDYorRk1
HXZpVFfu2FxXkZtfp4RQqNkX5y2sya3YYL2iavWAOaizH+pw.Ibg8f1I9h3Z
2B79sNeOHvBOtfEalWsvyu0KMf015.AaROvZ7vv5AhnndfHLbTgjcCK1KlHv
gOk5B26OqrXjcJ005.QqCHn8fVTxnxfj93SfQiJlv8YV0VT9fVUwOOhSV3uD
eeqCUClbBPa.j3vWDoMZssNTzRNEnE6gYPXazZaMF921syaLWyAeBXvCESA8
ASi6Zyw8.RQi65J8ZsNx3ho93OhGWENtWpowepae4YhCFeLErOLENtXJrOSc
iadi39rf4hwc8xdhHz3gn3dBI7iDRlFe8huAfIZhq
-----------end_max5_patcher-----------
*/
1-2. スケッチ2: Serial Display
シリアルモニタから入力した文字を、液晶画面に表示する。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- Adafruit LCD 16x2
- ブレッドボード
- トリマポテンシオメータ
- ジャンプワイヤ
Adafruit LCD 16x2は、入力した文字を表示するLCD(liquid crystal display、液晶ディスプレイ)です。下の画像はEdison LabのLCDと信号の配置が違うLCDを使っているので、配線が違います。
回路を組み終わったら、次はスケッチ(プログラム)です。
今回はそのままだと動かないので、setupの最初に
lcd.init(1, 12, 255, 11, 5, 4, 3, 2, 0, 0, 0, 0);
を追加します。Edison(Galileo)は、初期化がうまくいかないみたいで、これを追加する必要があるようです。setupが以下のようになります。
void setup() {
lcd.init(1, 12, 255, 11, 5, 4, 3, 2, 0, 0, 0, 0);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// initialize the serial communications:
Serial.begin(9600);
}
それではコードが何をしているのか上から順番に確認していきます。
// include the library code:
#include <LiquidCrystal.h>
LiquidCrystalのライブラリをインクルードします。
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LCDの初期設定を行います。
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
16x2でLCDを開始します。
// initialize the serial communications:
Serial.begin(9600);
9600bpsでシリアル通信を開始します。
// when characters arrive over the serial port...
if (Serial.available()) {
Serialに文字があったら中に入ります。
シリアルモニタで入力を行うとSerialに文字がとどきます。
// wait a bit for the entire message to arrive
delay(100);
100ミリ秒待ちます。
// clear the screen
lcd.clear();
LCDに書かれてる文字を消します。
while (Serial.available() > 0) {
// display each character to the LCD
lcd.write(Serial.read());
}
Serialに文字がなくなるまで、Serialから1文字読み込んで、LCDに書き込みます。Serial.read()は1文字(1バイト)づつ処理する関数なので、文字がなくなるまで繰り返し行います。
トリマポテンシオメータで明るさを調節、シリアルモニタで入力すると、LCDに表示されます。
コード全文
/*
LiquidCrystal Library - Serial Input
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there, and you
can usually tell them by the 16-pin interface.
This sketch displays text sent over the serial port
(e.g. from the Serial Monitor) on an attached LCD.
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystalSerial
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// initialize the serial communications:
Serial.begin(9600);
}
void loop() {
// when characters arrive over the serial port...
if (Serial.available()) {
// wait a bit for the entire message to arrive
delay(100);
// clear the screen
lcd.clear();
// read all the available characters
while (Serial.available() > 0) {
// display each character to the LCD
lcd.write(Serial.read());
}
}
}
1-3. スケッチ3: Wifi Web Server
入力デバイスにポテンシオメーターを使い、その値を同じネットワーク上のHTMLブラウザに表示する。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- ブレッドボード
- ポテンシオメータ
- ジャンプワイヤ
回路はポテンシオメータを1つ付けるだけなのでシンプルです。
回路を組み終わったら、次はスケッチ(プログラム)です。
このスケッチはコードが長めですが、半分以上がシリアルモニタにサーバーのログを送るコードなので、上記の機能を実装する部分だけだとそれほど長くはないです。
日付は同じですが、Arduino IDE1.6.5のスケッチとWebページのスケッチに違いがありました。Arduino IDE1.6.5のスケッチには、WiFiのファームウェアのバージョンが1.1.0以外だった場合はシリアルモニタに「Please upgrade the firmware」と表示する以下のコードが追加されています。
String fv = WiFi.firmwareVersion();
if( fv != "1.1.0" )
Serial.println("Please upgrade the firmware");
それではコードが何をしているのか上から順番に確認していきます。
#include <SPI.h>
#include <WiFi.h>
Serial Peripheral Interfaceのライブラリをインクルード。
WiFiのライブラリをインクルード。
char ssid[] = "yourNetwork"; // your network SSID (name)
char pass[] = "secretPassword"; // your network password
接続する無線LAN親機のSSIDとパスワードです。yourNetworkとsecretPasswordを接続する無線LAN親機のSSIDとパスワードに書き換えます。PC等で接続する時と同じものです。
int status = WL_IDLE_STATUS;
statusにWL_IDLE_STATUSを代入する。
WiFiServer server(80);
サーバーのポート番号です。通常のWeb Server(http)は80で、edisonの場合はDevice Informationが使っています。今回は8055に書き換えました。ブラウザ等で接続する場合はアドレスの後ろに:8055を追加します。edisonのIPアドレスが192.168.1.24の場合は192.168.1.24:8055と書きます。
//Initialize serial and wait for port to open:
Serial.begin(9600);
9600bpsでシリアル通信を開始します。
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
Serialが利用可能になるまでループさせる。
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while(true);
}
WiFi.statusがWL_NO_SHIELDだったらシリアルモニタに「WiFi shield not present」と表示させる。WiFi機能の確認のようです。
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
statusがWL_CONNECTED以外だったらシリアルモニタに「Attempting to connect to SSID: ssid」と表示させてからWiFi接続を行います。10秒毎にstatusを確認して、WL_CONNECTEDになるまで(WiFiが接続できるまで)繰り返します。
server.begin();
サーバーを開始します。
// you're connected now, so print out the status:
printWifiStatus();
printWifiStatus()はコードの一番後ろにあります。WiFiに接続して、サーバーを開始したらシリアルモニタにWifiの情報を表示させます。
// listen for incoming clients
WiFiClient client = server.available();
サーバーに接続しているクライアントを取得します。
if (client) {
クライアントの接続が確立するとtrueになります。
Serial.println("new client");
シリアルモニタに「new client」と表示する。
// an http request ends with a blank line
boolean currentLineIsBlank = true;
currentLineIsBlankにtrueを代入する。
while (client.connected()) {
クライアントの接続が切れるまで繰り返す。
if (client.available()) {
クライアントが読み取り可能なら中に入る。
char c = client.read();
クライアントから1文字読み込んでcに代入する。
Serial.write(c);
シリアルモニタにcを書き込む。
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
cが\n(改行)でcurrentLineIsBlankがtrueなら中に入る。空白行(2回連続で\n(改行))の場合に中に入ります。
以下のコードでクライアントに書き込みを行います。ブラウザ等が受信するデータです。
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
5秒後に自動でリフレッシュ(ブラウザの更新)を行うようにする。
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
// output the value of each analog input pin
for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
今回の回路はポテンシオメータ1つですが、0から5まで6つのアナログ情報を表示しています。
int sensorReading = analogRead(analogChannel);
analogChannelのアナログの値をsensorReadingに代入します。
client.print("analog input ");
client.print(analogChannel);
クライアントにアナログチャンネル番号を書き込む。
client.print(" is ");
client.print(sensorReading);
クライアントにアナログの値を書き込む。
client.println("<br />");
client.println("</html>");
アナログ0の値が0、1の値が100、2の値が200、3の値が300、4の値が400、5の値が500だった場合、ブラウザ等のクライアントに
analog input 0 is 0
analog input 1 is 100
analog input 2 is 200
analog input 3 is 300
analog input 4 is 400
analog input 5 is 500
と表示されます。
break;
「while (client.connected()) {」を抜けます。
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
cが\n(改行)だった場合はcurrentLineIsBlankをtrueにする。cが\r以外だった場合はcurrentLineIsBlankをfalseにする。\nでtrue、\r以外でfalseなので、\rは無視されます。
// give the web browser time to receive the data
delay(1);
クライアントを切断する前に1ミリ秒待つ。
// close the connection:
client.stop();
Serial.println("client disonnected");
クライアントを切断して、シリアルモニタに「client disonnected」と表示させる。
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
シリアルモニタにSSID、IPアドレス、RSSIを表示させる。
Wi-Fiに接続して、クライアント(ブラウザ)からの接続を待ちます。ブラウザが接続したらアナログインプットの値をクライアントに送信して切断します。
コード全文
/*
WiFi Web Server
A simple web server that shows the value of the analog input pins.
using a WiFi shield.
This example is written for a network using WPA encryption. For
WEP or WPA, change the Wifi.begin() call accordingly.
Circuit:
* WiFi shield attached
* Analog inputs attached to pins A0 through A5 (optional)
created 13 July 2010
by dlf (Metodo2 srl)
modified 31 May 2012
by Tom Igoe
*/
#include <SPI.h>
#include <WiFi.h>
char ssid[] = "yourNetwork"; // your network SSID (name)
char pass[] = "secretPassword"; // your network password
int keyIndex = 0; // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;
WiFiServer server(80);
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while(true);
}
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
server.begin();
// you're connected now, so print out the status:
printWifiStatus();
}
void loop() {
// listen for incoming clients
WiFiClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
// output the value of each analog input pin
for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
int sensorReading = analogRead(analogChannel);
client.print("analog input ");
client.print(analogChannel);
client.print(" is ");
client.print(sensorReading);
client.println("<br />");
}
client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disonnected");
}
}
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
1-4. オリジナル試作機: Color String Web Server
今まで学習した3つのスケッチサンプルを活用(応用)して
3つのポテンシオメーターの値を読み取り、それらをRGB値に変換した色で、シリアルモニタから入力した文字を、ネットワーク上のHTMLブラウザに表示する。
Color String Web Serverというオリジナルの試作機を開発します。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- ブレッドボード
- トリマポテンシオメータ 3個
- ジャンプワイヤ
回路はVirtual Color Mixerと同じです。
コードはWifi Web ServerをベースにVirtual Color MixerとSerial Displayの要素を追加した感じです。
#include <SPI.h>
#include <WiFi.h>
char ssid[] = "yourNetwork"; // 接続する無線LAN親機のSSID
char pass[] = "secretPassword"; // 接続する無線LAN親機のパスワード
int keyIndex = 0; // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;
WiFiServer server(8055);
String input; // シリアルモニタで入力した文字列
const int redPin = A0; // sensor to control red color
const int greenPin = A1; // sensor to control green color
const int bluePin = A2; // sensor to control blue color
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while(true);
}
String fv = WiFi.firmwareVersion();
// check for the presence of the shield:
if ( fv != "1.1.0")
Serial.println("Please upgrade the firmware");
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
server.begin();
// you're connected now, so print out the status:
printWifiStatus();
}
void loop() {
// inputにシリアルモニタからの入力を代入。
if (Serial.available()) {
// wait a bit for the entire message to arrive
delay(100);
input = Serial.readString();
}
// listen for incoming clients
WiFiClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println("Refresh: 1"); // refreshを5秒から1秒に変更しました。
client.println();
client.println("<!DOCTYPE HTML>");
client.print("<html>");
// アナログ入力の10bit(0-1023)を8bit(0-255)に変換します。
int redValue = map(analogRead(redPin), 0, 1023, 0, 255);
int greenValue = map(analogRead(greenPin), 0, 1023, 0, 255);
int blueValue = map(analogRead(bluePin), 0, 1023, 0, 255);
// フォントのサイズを60px、カラーをアナログ入力の値に設定します。
client.print("<body style=\"font-size:60px;color:rgb(");
client.print(redValue);
client.print(",");
client.print(greenValue);
client.print(",");
client.print(blueValue);
client.print(");\">");
client.print(input);
client.print("</body>");
client.println("</html>");
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
// close the connection:
client.stop();
Serial.println("client disonnected");
}
}
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
Wi-Fiに接続すると、シリアルモニタの入力またはクライアント(ブラウザ)の接続を待ちます。シリアルモニタに入力があった場合、文字列inputに入力した文字列を代入します。クライアントから接続があった場合は、3つのアナログ入力(トリマポテンシオメータ)の値をフォントカラーにして、文字列inputをクライアントに表示します。
2. センサーの使い方を学習して、気象観測器的な試作機を開発する
今回はセンサーの使い方を学習しつつ、気象観測器的な試作機の開発を目指します。
2-1. 温度センサーを使った試作機: TempSensor
まずは、アナログ入力で簡単に温度を測ることができる温度センサー、LM35DZを使って温度を測ります。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- ブレッドボード
- ジャンプワイヤ
- LM35DZ(温度センサー)
回路はLM35DZの3本のピンを5V、A0、GNDに接続するだけです。
アナログの出力は0から1023なので
float voltage = sensorValue * (5.0 / 1023.0);
でボルトに換算する事ができます。
LM35DZは0℃で0V、1℃増えると10mVづつ出力が上がっていきます。0℃が0Vで、0.01Vで1℃増えるという事は、単純にボルトを100倍にすれば摂氏温度という事になるので
float tempc = (sensorValue * (5.0 / 1023.0)) * 100
で摂氏温度に換算できます。
コード全文
//1秒毎に温度センサーの値を習得してシリアルモニタに摂氏温度を表示させる
void setup() {
//シリアル通信を開始
Serial.begin(9600);
}
void loop() {
//アナログ0から計測値を取得する
int sensorValue = analogRead(A0);
//センサーの計測値を摂氏温度に換算する
float tempc = (sensorValue * (5.0 / 1023.0)) * 100
//換算した摂氏温度をシリアルモニタに表示
Serial.println(tempc);
//1秒待機
delay(1000);
}
2-2. 温湿度・気圧センサモジュールでI2Cを使った試作機: MeteorologicalSensor
次は温度、湿度、気圧が同時に測れるBME280を搭載したセンサモジュールキット(AE-BME280)でI2C(I2C - Wikipedia)を使ってみます。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- ブレッドボード
- ジャンプワイヤ
- AE-BME280(温湿度・気圧センサモジュールキット)
まずは、ブレッドボードで使いやすいように、AE-BME280にピンヘッダを実装します。
10ピンのヘッダが付属していたので、余分な4ピンをニッパーでカットして、はんだ付けしました。I2Cで使うのでJ3をジャンパしてあります。
回路は画像左のピンから、VDDを3.3Vに接続、GNDをGNDに接続、CSBは未使用(J3のジャンパでVDDと繋がっています)、SDIはSDAに接続、SDOはGNDに接続、SCKはSCKに接続します。
コードはスイッチサイエンスのサンプルスケッチを使いました。長いので解説は後日行う予定です。
コード全文
#include <Wire.h>
#define BME280_ADDRESS 0x76
unsigned long int hum_raw,temp_raw,pres_raw;
signed long int t_fine;
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
int8_t dig_H1;
int16_t dig_H2;
int8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;
void setup()
{
uint8_t osrs_t = 1; //Temperature oversampling x 1
uint8_t osrs_p = 1; //Pressure oversampling x 1
uint8_t osrs_h = 1; //Humidity oversampling x 1
uint8_t mode = 3; //Normal mode
uint8_t t_sb = 5; //Tstandby 1000ms
uint8_t filter = 0; //Filter off
uint8_t spi3w_en = 0; //3-wire SPI Disable
uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode;
uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en;
uint8_t ctrl_hum_reg = osrs_h;
Serial.begin(9600);
Wire.begin();
writeReg(0xF2,ctrl_hum_reg);
writeReg(0xF4,ctrl_meas_reg);
writeReg(0xF5,config_reg);
readTrim(); //
}
void loop()
{
double temp_act = 0.0, press_act = 0.0,hum_act=0.0;
signed long int temp_cal;
unsigned long int press_cal,hum_cal;
readData();
temp_cal = calibration_T(temp_raw);
press_cal = calibration_P(pres_raw);
hum_cal = calibration_H(hum_raw);
temp_act = (double)temp_cal / 100.0;
press_act = (double)press_cal / 100.0;
hum_act = (double)hum_cal / 1024.0;
Serial.print("TEMP : ");
Serial.print(temp_act);
Serial.print(" DegC PRESS : ");
Serial.print(press_act);
Serial.print(" hPa HUM : ");
Serial.print(hum_act);
Serial.println(" %");
delay(1000);
}
void readTrim()
{
uint8_t data[32],i=0; // Fix 2014/04/06
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(0x88);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS,24); // Fix 2014/04/06
while(Wire.available()){
data[i] = Wire.read();
i++;
}
Wire.beginTransmission(BME280_ADDRESS); // Add 2014/04/06
Wire.write(0xA1); // Add 2014/04/06
Wire.endTransmission(); // Add 2014/04/06
Wire.requestFrom(BME280_ADDRESS,1); // Add 2014/04/06
data[i] = Wire.read(); // Add 2014/04/06
i++; // Add 2014/04/06
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(0xE1);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS,7); // Fix 2014/04/06
while(Wire.available()){
data[i] = Wire.read();
i++;
}
dig_T1 = (data[1] << 8) | data[0];
dig_T2 = (data[3] << 8) | data[2];
dig_T3 = (data[5] << 8) | data[4];
dig_P1 = (data[7] << 8) | data[6];
dig_P2 = (data[9] << 8) | data[8];
dig_P3 = (data[11]<< 8) | data[10];
dig_P4 = (data[13]<< 8) | data[12];
dig_P5 = (data[15]<< 8) | data[14];
dig_P6 = (data[17]<< 8) | data[16];
dig_P7 = (data[19]<< 8) | data[18];
dig_P8 = (data[21]<< 8) | data[20];
dig_P9 = (data[23]<< 8) | data[22];
dig_H1 = data[24];
dig_H2 = (data[26]<< 8) | data[25];
dig_H3 = data[27];
dig_H4 = (data[28]<< 4) | (0x0F & data[29]);
dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F); // Fix 2014/04/06
dig_H6 = data[31]; // Fix 2014/04/06
}
void writeReg(uint8_t reg_address, uint8_t data)
{
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(reg_address);
Wire.write(data);
Wire.endTransmission();
}
void readData()
{
int i = 0;
uint32_t data[8];
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(0xF7);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS,8);
while(Wire.available()){
data[i] = Wire.read();
i++;
}
pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
hum_raw = (data[6] << 8) | data[7];
}
signed long int calibration_T(signed long int adc_T)
{
signed long int var1, var2, T;
var1 = ((((adc_T >> 3) - ((signed long int)dig_T1<<1))) * ((signed long int)dig_T2)) >> 11;
var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T>>4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}
unsigned long int calibration_P(signed long int adc_P)
{
signed long int var1, var2;
unsigned long int P;
var1 = (((signed long int)t_fine)>>1) - (signed long int)64000;
var2 = (((var1>>2) * (var1>>2)) >> 11) * ((signed long int)dig_P6);
var2 = var2 + ((var1*((signed long int)dig_P5))<<1);
var2 = (var2>>2)+(((signed long int)dig_P4)<<16);
var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((signed long int)dig_P2) * var1)>>1))>>18;
var1 = ((((32768+var1))*((signed long int)dig_P1))>>15);
if (var1 == 0)
{
return 0;
}
P = (((unsigned long int)(((signed long int)1048576)-adc_P)-(var2>>12)))*3125;
if(P<0x80000000)
{
P = (P << 1) / ((unsigned long int) var1);
}
else
{
P = (P / (unsigned long int)var1) * 2;
}
var1 = (((signed long int)dig_P9) * ((signed long int)(((P>>3) * (P>>3))>>13)))>>12;
var2 = (((signed long int)(P>>2)) * ((signed long int)dig_P8))>>13;
P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4));
return P;
}
unsigned long int calibration_H(signed long int adc_H)
{
signed long int v_x1;
v_x1 = (t_fine - ((signed long int)76800));
v_x1 = (((((adc_H << 14) -(((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) +
((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) *
(((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int) 32768))) >> 10) + (( signed long int)2097152)) *
((signed long int) dig_H2) + 8192) >> 14));
v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4));
v_x1 = (v_x1 < 0 ? 0 : v_x1);
v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1);
return (unsigned long int)(v_x1 >> 12);
}
正確に動いているのかわかりませんが、温度、湿度、気圧がシリアルモニタに表示されます。
2-3. 取得した値をタイムスタンプ付きでSDカードに保存する試作機: MeteorologicalLogger
引き続きBME280を搭載したセンサモジュールキット(AE-BME280)を使います。
前回は一定時間毎に取得した値をシリアルモニタに表示するものでしたが、今回は取得した値をタイムスタンプ付きでSDカード(東芝 microSDHCメモリカード8GB CLASS4 (SD-ME008GS))に保存します。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- ブレッドボード
- ジャンプワイヤ
- AE-BME280(温湿度・気圧センサモジュールキット)
- SDカード(microSDカード)
まずはEdisonにmicroSDカードを挿します。
/dev/mmcblk1p1 7.2G 32.0K 7.2G 0% /media/sdcard
こちらが追加されました。オートでマウントされ、認識したようです。
回路は前回と同じです。まずはArduino - DataloggerとArduino - UdpNTPClientを参考にするので、こちらの2つが正常に動くか試しに実行してみました。
Dataloggerはアナログ0から2のデータをdatalog.txtにひたすら追加していくものです。待ち時間がないので、すごい勢いで追加していきます。実行後/media/sdcard/を確認したところ、datalog.txtが作られていて、アナログの値が大量に書き込まれていました。SDカードは正常に使えているようです。
UdpNTPClientはNTP server(Network Time Protocol - Wikipedia)のtime.nist.gov(米国政府が運営しているNTP Server)にUDPで時間を送ってもらい、シリアルモニタに時、分、秒を表示するものです。ちなみにNTPサーバーが送ってくるのは、1900年1月1日0時0分0秒から経過した時間なので、年、月、日、時、分、秒などの表示を行う場合は計算する必要があります。こちらも正常に動作しているようです。
それでは、前回のコードを修正して、取得した値をタイムスタンプ付きでSDカードに保存する機能を追加します。
まずはWiFiを使うためのコード、NTPサーバーに時間を送ってもらうためのコードを追加します。
必要なライブラリをインクルード、変数を追加します。yourNetworkとsecretPasswordは接続する無線LAN親機のSSIDとパスワードです。接続する無線LAN親機のSSIDとパスワードに書き換えます。また頻繁にアクセスする場合、NTP serverはもっと身近なところに変更した方がよさそうです。
#include <WiFi.h>
#include <WiFiUdp.h>
int status = WL_IDLE_STATUS;
char ssid[] = "mynetwork"; // your network SSID (name)
char pass[] = "mypassword"; // your network password
int keyIndex = 0; // your network key Index number (needed only for WEP)
unsigned int localPort = 2390; // local port to listen for UDP packets
IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
WiFiUDP Udp;
setup()にWiFiの初期化処理を追加します。
//WiFiの初期化
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while(true);
}
String fv = WiFi.firmwareVersion();
if( fv != "1.1.0" )
Serial.println("Please upgrade the firmware");
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
Serial.println("Connected to wifi");
printWifiStatus();
Serial.println("\nStarting connection to server...");
Udp.begin(localPort);
loop()に時間を受信して、秒数を代入する処理を追加します。
//時間を受信して、秒数を変数に代入します
sendNTPpacket(timeServer); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
Serial.println( Udp.parsePacket() );
if ( Udp.parsePacket() ) {
Serial.println("packet received");
// We've received a packet, read the data from it
Udp.read(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print("Seconds since Jan 1 1900 = " );
Serial.println(secsSince1900);
}
WiFiとNTPで使う関数を追加します。
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address) {
//Serial.println("1");
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
//Serial.println("2");
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
//Serial.println("3");
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
//Serial.println("4");
Udp.write(packetBuffer,NTP_PACKET_SIZE);
//Serial.println("5");
Udp.endPacket();
//Serial.println("6");
}
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
次はSDカードを使う為のコード、値を保存するコードを追加します。
必要なライブラリをインクルード、変数を追加します。
#include <SD.h>
const int chipSelect = 4;
setup()にSDカードの初期化を追加します。
//SDカードの初期化
Serial.print("Initializing SD card...");
// make sure that the default chip select pin is set to
// output, even if you don't use it:
pinMode(10, OUTPUT);
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;
}
Serial.println("card initialized.");
loop()の最初でファイルを開いて、各所でデータを書き込み、最後に閉じます。
//SDカードのファイルを開く
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("mlog.txt", FILE_WRITE);
//ファイルに時間を書き込む
// if the file is available, write to it:
if (dataFile) {
dataFile.print(secsSince1900);
dataFile.print(",");
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening mlog.txt");
}
//ファイルにセンサーの値を書き込む
// if the file is available, write to it:
if (dataFile) {
dataFile.print(temp_act);
dataFile.print(",");
dataFile.print(press_act);
dataFile.print(",");
dataFile.println(hum_act);
dataFile.close();
}
これで完成です。
ファイルに秒数,温度,気圧,湿度が保存されました。
保存したデータを使ってブラウザ等でグラフにしようかと思っていましたが、長期間のデータを貯めないといけないし(適当に捏造すればいいのかもしれませんが)、いろいろと手間がかかりそうなので、これは一旦終了して、次の試作機を開発しようと思います。
コード全文
#include <Wire.h>
#include <SPI.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#include <SD.h>
#define BME280_ADDRESS 0x76
unsigned long int hum_raw,temp_raw,pres_raw;
signed long int t_fine;
uint16_t dig_T1;
int16_t dig_T2;
int16_t dig_T3;
uint16_t dig_P1;
int16_t dig_P2;
int16_t dig_P3;
int16_t dig_P4;
int16_t dig_P5;
int16_t dig_P6;
int16_t dig_P7;
int16_t dig_P8;
int16_t dig_P9;
int8_t dig_H1;
int16_t dig_H2;
int8_t dig_H3;
int16_t dig_H4;
int16_t dig_H5;
int8_t dig_H6;
//WiFiで使う変数
int status = WL_IDLE_STATUS;
char ssid[] = "mynetwork"; // your network SSID (name)
char pass[] = "mypassword"; // your network password
int keyIndex = 0; // your network key Index number (needed only for WEP)
unsigned int localPort = 2390; // local port to listen for UDP packets
IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
WiFiUDP Udp;
//SDカードで使う変数
const int chipSelect = 4;
void setup() {
uint8_t osrs_t = 1; //Temperature oversampling x 1
uint8_t osrs_p = 1; //Pressure oversampling x 1
uint8_t osrs_h = 1; //Humidity oversampling x 1
uint8_t mode = 3; //Normal mode
uint8_t t_sb = 5; //Tstandby 1000ms
uint8_t filter = 0; //Filter off
uint8_t spi3w_en = 0; //3-wire SPI Disable
uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode;
uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3w_en;
uint8_t ctrl_hum_reg = osrs_h;
Serial.begin(9600);
Wire.begin();
writeReg(0xF2,ctrl_hum_reg);
writeReg(0xF4,ctrl_meas_reg);
writeReg(0xF5,config_reg);
readTrim(); //
//WiFiの初期化
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while(true);
}
String fv = WiFi.firmwareVersion();
if( fv != "1.1.0" )
Serial.println("Please upgrade the firmware");
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
Serial.println("Connected to wifi");
printWifiStatus();
Serial.println("\nStarting connection to server...");
Udp.begin(localPort);
//SDカードの初期化
Serial.print("Initializing SD card...");
// make sure that the default chip select pin is set to
// output, even if you don't use it:
pinMode(10, OUTPUT);
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
return;
}
Serial.println("card initialized.");
}
void loop() {
//SDカードのファイルを開く
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("mlog.txt", FILE_WRITE);
//時間を受信して、秒数を変数に代入します
sendNTPpacket(timeServer); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
Serial.println( Udp.parsePacket() );
if ( Udp.parsePacket() ) {
Serial.println("packet received");
// We've received a packet, read the data from it
Udp.read(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print("Seconds since Jan 1 1900 = " );
Serial.println(secsSince1900);
//ファイルに時間を書き込む
// if the file is available, write to it:
if (dataFile) {
dataFile.print(secsSince1900);
dataFile.print(",");
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening mlog.txt");
}
}
double temp_act = 0.0, press_act = 0.0,hum_act=0.0;
signed long int temp_cal;
unsigned long int press_cal,hum_cal;
readData();
temp_cal = calibration_T(temp_raw);
press_cal = calibration_P(pres_raw);
hum_cal = calibration_H(hum_raw);
temp_act = (double)temp_cal / 100.0;
press_act = (double)press_cal / 100.0;
hum_act = (double)hum_cal / 1024.0;
Serial.print("TEMP : ");
Serial.print(temp_act);
Serial.print(" DegC PRESS : ");
Serial.print(press_act);
Serial.print(" hPa HUM : ");
Serial.print(hum_act);
Serial.println(" %");
delay(1000);
//ファイルにセンサーの値を書き込む
// if the file is available, write to it:
if (dataFile) {
dataFile.print(temp_act);
dataFile.print(",");
dataFile.print(press_act);
dataFile.print(",");
dataFile.println(hum_act);
dataFile.close();
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening mlog.txt");
}
delay(8000);
}
void readTrim() {
uint8_t data[32],i=0; // Fix 2014/04/06
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(0x88);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS,24); // Fix 2014/04/06
while(Wire.available()){
data[i] = Wire.read();
i++;
}
Wire.beginTransmission(BME280_ADDRESS); // Add 2014/04/06
Wire.write(0xA1); // Add 2014/04/06
Wire.endTransmission(); // Add 2014/04/06
Wire.requestFrom(BME280_ADDRESS,1); // Add 2014/04/06
data[i] = Wire.read(); // Add 2014/04/06
i++; // Add 2014/04/06
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(0xE1);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS,7); // Fix 2014/04/06
while(Wire.available()) {
data[i] = Wire.read();
i++;
}
dig_T1 = (data[1] << 8) | data[0];
dig_T2 = (data[3] << 8) | data[2];
dig_T3 = (data[5] << 8) | data[4];
dig_P1 = (data[7] << 8) | data[6];
dig_P2 = (data[9] << 8) | data[8];
dig_P3 = (data[11]<< 8) | data[10];
dig_P4 = (data[13]<< 8) | data[12];
dig_P5 = (data[15]<< 8) | data[14];
dig_P6 = (data[17]<< 8) | data[16];
dig_P7 = (data[19]<< 8) | data[18];
dig_P8 = (data[21]<< 8) | data[20];
dig_P9 = (data[23]<< 8) | data[22];
dig_H1 = data[24];
dig_H2 = (data[26]<< 8) | data[25];
dig_H3 = data[27];
dig_H4 = (data[28]<< 4) | (0x0F & data[29]);
dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F); // Fix 2014/04/06
dig_H6 = data[31]; // Fix 2014/04/06
}
void writeReg(uint8_t reg_address, uint8_t data) {
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(reg_address);
Wire.write(data);
Wire.endTransmission();
}
void readData() {
int i = 0;
uint32_t data[8];
Wire.beginTransmission(BME280_ADDRESS);
Wire.write(0xF7);
Wire.endTransmission();
Wire.requestFrom(BME280_ADDRESS,8);
while(Wire.available()){
data[i] = Wire.read();
i++;
}
pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
hum_raw = (data[6] << 8) | data[7];
}
signed long int calibration_T(signed long int adc_T) {
signed long int var1, var2, T;
var1 = ((((adc_T >> 3) - ((signed long int)dig_T1<<1))) * ((signed long int)dig_T2)) >> 11;
var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T>>4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14;
t_fine = var1 + var2;
T = (t_fine * 5 + 128) >> 8;
return T;
}
unsigned long int calibration_P(signed long int adc_P) {
signed long int var1, var2;
unsigned long int P;
var1 = (((signed long int)t_fine)>>1) - (signed long int)64000;
var2 = (((var1>>2) * (var1>>2)) >> 11) * ((signed long int)dig_P6);
var2 = var2 + ((var1*((signed long int)dig_P5))<<1);
var2 = (var2>>2)+(((signed long int)dig_P4)<<16);
var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((signed long int)dig_P2) * var1)>>1))>>18;
var1 = ((((32768+var1))*((signed long int)dig_P1))>>15);
if (var1 == 0) {
return 0;
}
P = (((unsigned long int)(((signed long int)1048576)-adc_P)-(var2>>12)))*3125;
if(P<0x80000000) {
P = (P << 1) / ((unsigned long int) var1);
} else {
P = (P / (unsigned long int)var1) * 2;
}
var1 = (((signed long int)dig_P9) * ((signed long int)(((P>>3) * (P>>3))>>13)))>>12;
var2 = (((signed long int)(P>>2)) * ((signed long int)dig_P8))>>13;
P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4));
return P;
}
unsigned long int calibration_H(signed long int adc_H) {
signed long int v_x1;
v_x1 = (t_fine - ((signed long int)76800));
v_x1 = (((((adc_H << 14) -(((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) + ((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) * (((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int) 32768))) >> 10) + (( signed long int)2097152)) * ((signed long int) dig_H2) + 8192) >> 14));
v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4));
v_x1 = (v_x1 < 0 ? 0 : v_x1);
v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1);
return (unsigned long int)(v_x1 >> 12);
}
//WiFi、NTPで使う関数
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address) {
//Serial.println("1");
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
//Serial.println("2");
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
//Serial.println("3");
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
//Serial.println("4");
Udp.write(packetBuffer,NTP_PACKET_SIZE);
//Serial.println("5");
Udp.endPacket();
//Serial.println("6");
}
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
3. USB関係の機能を学習して、USB機器を使った試作機を開発する
ここではいろいろなUSB機器をEdisonに接続してみて、USB関係の機能や使い方を学習しつつ、USB機器を使った試作機を開発を目指します。
Arduino拡張ボードのUSBについて
まずはArduino拡張ボードのUSBについて確認します。
Arduino拡張ボードには2つのUSB Micro-B端子、1つのUSB A端子が付いています。外側のUSB Micro-B端子(J3)はFTDIのUSBからシリアルUARTインターフェースへの変換チップに接続しているので、シリアル通信専用になります。通常のUSB機器を接続する事はできませんが、接続するPC等にFTDIのドライバをインストールするとUSBをシリアルとして利用できるようになります。またAndroidには専用のアプリ(FTDI UART Terminal)があるので、こちらを使えば、EdisonのシェルをUSBで接続したAndroid端末で使えるようになります。
内側のUSB Micro-B端子(J16)およびUSB A端子(J6)は通常のUSBです。USB Micro-B端子(J16)がスレーブでUSB A端子がホストですが、同時に使用する事はできないので、USB Micro-B端子(J16)とUSB A端子の間にあるスイッチ(SW1)で選択します。
PC等のホストに接続する場合は、スイッチ(SW1)をUSB Micro-B端子(J16)側にしてUSB Micro-B端子(J16)をスレーブとして使用します。EdisonにUSB機器を接続する場合は、スイッチ(SW1)をUSB A端子(J6)側にしてUSB A端子(J6)をホストとして使用します。また、USB A端子(J6)をホストとして使っている時は、USB Micro-B端子(J16)を使ったArduino IDEからEdisonへの書き込み、USB Micro-B端子(J16)からの給電は行えなくなります。
DCジャック(J1)から給電する場合についてIntelのガイドでは
A direct current (DC) power supply. Your power supply should be rated as follows:
- 7-15V DC
- At least 1500mA
- The center/inner pin should be the positive pole of the power supply
と書かれているので、7-15VのDC、最低1500mA(1.5A)、センタープラスという事です。またジャックは内径2.1mm、外径5.5mmのようです。
どのくらいがベターなのかわかりませんが、私は家庭用のコンセントからEdisonに給電するために、こちら(スイッチングACアダプター12V 1.5A 100V-240V GF18-US1215T)を購入しました。
3-1. USBヘッドセットを接続して、サウンドを再生、録音する
今回はこちら(LOGICOOL サラウンドサウンドヘッドセット G-35)を使いました。またUSBをホストとして使うので、USBからの給電ができないので、こちら(スイッチングACアダプター12V 1.5A 100V-240V GF18-US1215T)を使いました。
ファームウェアはIntel Edison® Board Firmware Software Release 2.1で行っています。※不具合の修正なのか、リリース2.1のままで更新されているようなので、ファイル名が変わっていたら更新した方がいいかもしれません。
スイッチ(SW1)をUSB A(J6)側にして、ACアダプターからの給電で起動します。※スイッチ(SW1)は起動中でも普通に機能しますが、何かしらの問題があるかもしれません。
まずは再生するサウンドファイルを用意します。今回はWindowsのmediaフォルダにある「tada.wav」をmicroSDカード(東芝 microSDHCメモリカード8GB CLASS4 (SD-ME008GS))にコピーしました。microSDカードをEdisonに挿すとデフォルトだと/media/sdcardに自動でマウントされるので、カレントディレクトリをこちらにしておきます。
root@edison:~# cd /media/sdcard
まずはUSBデバイスを表示するコマンドを入力します。
root@edison:/media/sdcard# lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
次はUSBヘッドセットを接続してからコマンドを入力します。
root@edison:/media/sdcard# lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 046d:0a15 Logitech, Inc.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
ヘッドセットが追加されました。
AlsaMixerでサウンドの設定を行います。
root@edison:/media/sdcard# alsamixer
「F6」を押してサウンドデバイスを選択します。
設定を行ったら「ESC」で閉じます。
まずは再生です。aplay -Lでデバイスのリストを表示して確認します。
root@edison:/media/sdcard# aplay -L
null
Discard all samples (playback) or generate zero samples (capture)
default:CARD=Loopback
Loopback, Loopback PCM
Default Audio Device
sysdefault:CARD=Loopback
Loopback, Loopback PCM
Default Audio Device
front:CARD=Loopback,DEV=0
Loopback, Loopback PCM
Front speakers
surround21:CARD=Loopback,DEV=0
Loopback, Loopback PCM
2.1 Surround output to Front and Subwoofer speakers
surround40:CARD=Loopback,DEV=0
Loopback, Loopback PCM
4.0 Surround output to Front and Rear speakers
surround41:CARD=Loopback,DEV=0
Loopback, Loopback PCM
4.1 Surround output to Front, Rear and Subwoofer speakers
surround50:CARD=Loopback,DEV=0
Loopback, Loopback PCM
5.0 Surround output to Front, Center and Rear speakers
surround51:CARD=Loopback,DEV=0
Loopback, Loopback PCM
5.1 Surround output to Front, Center, Rear and Subwoofer speakers
surround71:CARD=Loopback,DEV=0
Loopback, Loopback PCM
7.1 Surround output to Front, Center, Side, Rear and Woofer speakers
default:CARD=dummyaudio
dummy-audio,
Default Audio Device
sysdefault:CARD=dummyaudio
dummy-audio,
Default Audio Device
default:CARD=Headset
Logicool G35 Headset, USB Audio
Default Audio Device
sysdefault:CARD=Headset
Logicool G35 Headset, USB Audio
Default Audio Device
front:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
Front speakers
surround21:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
2.1 Surround output to Front and Subwoofer speakers
surround40:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
4.0 Surround output to Front and Rear speakers
surround41:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
4.1 Surround output to Front, Rear and Subwoofer speakers
surround50:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
5.0 Surround output to Front, Center and Rear speakers
surround51:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
5.1 Surround output to Front, Center, Rear and Subwoofer speakers
surround71:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
7.1 Surround output to Front, Center, Side, Rear and Woofer speakers
iec958:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
IEC958 (S/PDIF) Digital Audio Output
今回は「iec958」を使います。
root@edison:/media/sdcard# aplay -D iec958:CARD=Headset tada.wav
Playing WAVE 'tada.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
お馴染みの音が再生されました。
次は録音です。こちらもarecord -Lでデバイスのリストを表示して確認しましたが、同じみたいです。
root@edison:/media/sdcard# arecord -L
null
Discard all samples (playback) or generate zero samples (capture)
default:CARD=Loopback
Loopback, Loopback PCM
Default Audio Device
sysdefault:CARD=Loopback
Loopback, Loopback PCM
Default Audio Device
front:CARD=Loopback,DEV=0
Loopback, Loopback PCM
Front speakers
surround21:CARD=Loopback,DEV=0
Loopback, Loopback PCM
2.1 Surround output to Front and Subwoofer speakers
surround40:CARD=Loopback,DEV=0
Loopback, Loopback PCM
4.0 Surround output to Front and Rear speakers
surround41:CARD=Loopback,DEV=0
Loopback, Loopback PCM
4.1 Surround output to Front, Rear and Subwoofer speakers
surround50:CARD=Loopback,DEV=0
Loopback, Loopback PCM
5.0 Surround output to Front, Center and Rear speakers
surround51:CARD=Loopback,DEV=0
Loopback, Loopback PCM
5.1 Surround output to Front, Center, Rear and Subwoofer speakers
surround71:CARD=Loopback,DEV=0
Loopback, Loopback PCM
7.1 Surround output to Front, Center, Side, Rear and Woofer speakers
default:CARD=dummyaudio
dummy-audio,
Default Audio Device
sysdefault:CARD=dummyaudio
dummy-audio,
Default Audio Device
default:CARD=Headset
Logicool G35 Headset, USB Audio
Default Audio Device
sysdefault:CARD=Headset
Logicool G35 Headset, USB Audio
Default Audio Device
front:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
Front speakers
surround21:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
2.1 Surround output to Front and Subwoofer speakers
surround40:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
4.0 Surround output to Front and Rear speakers
surround41:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
4.1 Surround output to Front, Rear and Subwoofer speakers
surround50:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
5.0 Surround output to Front, Center and Rear speakers
surround51:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
5.1 Surround output to Front, Center, Rear and Subwoofer speakers
surround71:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
7.1 Surround output to Front, Center, Side, Rear and Woofer speakers
iec958:CARD=Headset,DEV=0
Logicool G35 Headset, USB Audio
IEC958 (S/PDIF) Digital Audio Output
今回はiec958:CARD=Headsetを使います。
root@edison:/media/sdcard# arecord -D iec958:CARD=Headset -f S16_LE test.wav
Recording WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Mono
^CAborted by signal Interrupt...
適当に喋ってから、「CTRL+C」で強制終了しました。
録音で作った「test.wav」を再生します。
root@edison:/media/sdcard# aplay -D iec958:CARD=Headset test.wav
Playing WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 8000 Hz, Mono
しっかりと私の声が録音されていました。
基本的にLinuxベースのOSなので、設定等はLinuxと同じような感じです。またUSBのサウンドデバイス(スピーカー、ヘッドホン、マイク、ヘッドセット)は同じように設定等が行えます。
3-2. USBキーボードを接続して、入力を確認する
今回はこちら(USBキーボード)を使いました。またUSBをホストとして使うので、USBからの給電ができないので、こちら(スイッチングACアダプター12V 1.5A 100V-240V GF18-US1215T)を使いました。
ファームウェアはIntel Edison® Board Firmware Software Release 2.1で行っています。※不具合の修正なのか、リリース2.1のままで更新されているようなので、ファイル名が変わっていたら更新した方がいいかもしれません。
スイッチ(SW1)をUSB A(J6)側にして、ACアダプターからの給電で起動します。※スイッチ(SW1)は起動中でも普通に機能しますが、何かしらの問題があるかもしれません。
まずは私も詳しくはありませんが、LinuxのInput subsystemについて、少し説明します。
Input Subsystemはカーネル、コマンドライン、グラフィカルユーザインタフェースとユーザーが使用する入力デバイス(キーボード、マウス、ジョイスティック等)が対話するための仕組みで、やりとりはファイルの読み書きで行われ、ファイルは/dev/input/以下に置かれます。
ls /dev/input/でファイルを確認してみましょう。
root@edison:/# ls /dev/input/
by-path event0 event1
event0とevent1はEdisonに実装されているボタンのファイルのようです。ボタンを押したら書き込みが行われました。
by-pathディレクトリのファイルにも同じように読み書きが行われているようです。こちらはファイル名が固定されているようなので、プログラムで指定する時は、こちらの方が使いやすいかもしれません。
root@edison:~# ls /dev/input/by-path
platform-gpio-keys-event platform-mid_powerbtn-event
hexdumpを使えばイベントが起きたのが確認できます。
hexdump /dev/input/event*
またevtestというプログラムを使えば、キーボードの何を押したとか、マウスを操作したとか、表示してくれます。
lsusbでUSBを表示します。
root@edison:/# lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
USB A(J6)にUSBキーボードを接続して、lsusbでUSBのリストを表示します。
root@edison:~# lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 04f3:0103 Elan Microelectronics Corp. ActiveJet K-2024 Multimedia Keyboard
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
USBキーボードが追加されました。
ls /dev/input/でファイルを表示します。
root@edison:/# ls /dev/input/
by-id by-path event0 event1 event2 event3
/dev/input/にevent2とevent3、by-idディレクトリが追加されました。
ls /dev/input/by-path/でファイルを表示します。
root@edison:~# ls /dev/input/by-path/
pci-0000:00:11.0-platform-dwc3-host.2-usb-0:1:1.0-event-kbd
pci-0000:00:11.0-platform-dwc3-host.2-usb-0:1:1.1-event
platform-gpio-keys-event
platform-mid_powerbtn-event
こちらにもファイルが2つ追加されました。「pci-0000:00:11.0-platform-dwc3-host.2-usb-0:1:1.0-event-kbd」と「pci-0000:00:11.0-platform-dwc3-host.2-usb-0:1:1.1-event」は、event2とevent3と同じように読み書きが行われているようです。
ls /dev/input/by-id/でファイルを表示します。
root@edison:~# ls /dev/input/by-id
usb-04f3_0103-event-if01 usb-04f3_0103-event-kbd
こちらも同じように2つ追加されevent2とevent3と同じように読み書きが行われているようです。
このキーボードでは、ファイルが2つ(idとpathを別に計算すると6つ)作られ、通常のキーは「*event-kbd」に書き込まれ、特殊なキー[Wake Up][Sleep][Power]の3つは、もう片方に書き込まれるようです。
3-3. USBでPS3のコントローラを接続して、入力を確認する
今回はPlayStation3のコントローラ(DUALSHOCK 3)をUSBで接続して、入力を確認します。
Edisonの最新(2015/10/27)のファームウェア(Intel Edison® Board Firmware Software Release 2.1)は、そのままでは「DUALSHOCK 3」が使えません。モジュールをインストールするだけでも使えるようになりますが、テストの為に頻繁にフラッシュするので、今回はジョイスティックとPS3コントローラのモジュールを追加したイメージを作りました。
イメージの作り方については「カスタムビルドに挑戦する」を参照してください。
今回はモジュールで2つ追加しました。
Device Drivers > Input device support > Joystick interface
Device Drivers > HID support > Special HID drivers > Sony PS3 controller
「Joystick interface」と「Sony PS3 controller」は別のものなので、片方だけでも動作します。
Sony PS3 controllerの方が機能や性能が良いようですが、Joystick interfaceの方が、サンプルが多い感じだったので、両方入れてみました。
新しく作ったイメージでフラッシュして、スイッチ(SW1)をUSB A(J6)側にして、ACアダプターからの給電で起動します。※スイッチ(SW1)は起動中でも普通に機能しますが、何かしらの問題があるかもしれません。
あとは接続するだけで「DUALSHOCK 3」が使えるようになります。※最初は開始するのにPSボタン(真ん中の丸いボタン)を押さないいけないかもしれません
root@edison:~# ls -R /dev/input/
/dev/input/:
by-id by-path event0 event1 event2 js0
/dev/input/by-id:
usb-Sony_PLAYSTATION_R_3_Controller-event-joystick
usb-Sony_PLAYSTATION_R_3_Controller-joystick
/dev/input/by-path:
pci-0000:00:11.0-platform-dwc3-host.2-usb-0:1:1.0-event-joystick
pci-0000:00:11.0-platform-dwc3-host.2-usb-0:1:1.0-joystick
platform-gpio-keys-event
platform-mid_powerbtn-event
event2、usb-Sony_PLAYSTATION_R_3_Controller-event-joystick、pci-0000:00:11.0-platform-dwc3-host.2-usb-0:1:1.0-event-joystickが「Sony PS3 controller」で追加されたもの、js0、usb-Sony_PLAYSTATION_R_3_Controller-joystick、pci-0000:00:11.0-platform-dwc3-host.2-usb-0:1:1.0-joystickが「Joystick interface」で追加されたものです。
※event0、event1、platform-gpio-keys-event、platform-mid_powerbtn-eventの4つははじめからあるファイルです。
「hexdump /dev/input/js0」や「cat /dev/input/event2」で入力を確認できますが、モーションセンサーが付いているので、ものすごい速さで流れるので、今回は入力を確認するための簡単なプログラムを書きました。※下記プログラムは15行目「js.number < 4」でモーションセンサーを含む、AXIS4以上を無視するようにしてあります
コード全文
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/joystick.h>
#define DEV_INPUT_JS "/dev/input/js0"
using namespace std;
int main() {
int jsf(-1);
jsf = open(DEV_INPUT_JS, O_RDONLY);
while(true) {
js_event js;
read(jsf, &js, sizeof(js_event));
switch(js.type & ~JS_EVENT_INIT) {
case JS_EVENT_AXIS:
if (js.number < 4) {
printf("AXIS[%d]: %d \n", js.number, js.value );
}
break;
case JS_EVENT_BUTTON:
printf("BUTTON[%d]: %d \n", js.number, js.value );
break;
}
}
close(jsf);
return 0;
}
上記を適当なファイル名(説明はps3ctest.cppで行います)でEdisonに保存します。
PuTTYでEdisonに接続している場合は ブラウザでコード全文をコピー>vi ps3ctest.cpp>i>PuTTYを右クリックでペースト>ESC>:wq で保存できます。
コンパイルする。
root@edison:~# g++ ps3ctest.cpp -o ps3ctest
実行する。
root@edison:~# ./ps3ctest
正常に動作しているようです。
4. Bluetooth関係の機能を学習して、Bluetooth機器を使った試作機を開発する
ここではいろいろなBluetooth機器をEdisonに接続してみて、Bluetooth関係の機能や使い方を学習しつつ、Bluetooth機器を使った試作機を開発を目指します。
4-1. Androidタブレットの音声出力をEdison経由でBluetoothスピーカーから出力する
今回はEdisonにBluetoothで、Bluetoothスピーカー(TT-SK03(B))と、Anrdoidタブレット(TM105A)を接続して、Androidタブレットの音声出力を、Edisonに接続したBluetoothスピーカーから出力します。AndroidタブレットとBluetoothスピーカーを接続した方が早いとは思いますが、テストなので気にしないでください。
ファームウェアはIntel Edison® Board Firmware Software Release 2.1で行っています。※不具合の修正なのか、リリース2.1のままで更新されているようなので、ファイル名が変わっていたら更新した方がいいかもしれません。
まずは「rfkill list」でBluetoothの状態を確認してみましょう。
root@edison:~# rfkill list
0: phy0: wlan
Soft blocked: no
Hard blocked: no
1: brcmfmac-wifi: wlan
Soft blocked: no
Hard blocked: no
2: bcm43xx Bluetooth: bluetooth
Soft blocked: yes
Hard blocked: no
EdisonはデフォルトだとBluetoothがブロック(無効)されているので、「rfkill unblock bluetooth」でアンブロック(有効)にします。
root@edison:~# rfkill unblock bluetooth
root@edison:~# rfkill list
0: phy0: wlan
Soft blocked: no
Hard blocked: no
1: brcmfmac-wifi: wlan
Soft blocked: no
Hard blocked: no
2: bcm43xx Bluetooth: bluetooth
Soft blocked: no
Hard blocked: no
3: hci0: bluetooth
Soft blocked: no
Hard blocked: no
これでBluetoothが使えるようになりました。
Bluetoothの設定は、bluetoothctlを使います。
root@edison:~# bluetoothctl
[NEW] Controller 00:00:00:00:00:00 edison [default]
[bluetooth]#
スピーカーとAndroidタブレットをスキャン、ペアリングできる状態にします。※各デバイスのマニュアルを参照してください
scan onでスキャンを開始します。※scan offでスキャンを停止
[bluetooth]# scan on
Discovery started
[CHG] Controller 00:00:00:00:00:00 Discovering: yes
[NEW] Device 10:10:10:10:10:10 TM105A
[NEW] Device 20:20:20:20:20:20 TaoTronics TT-SK03
Androidタブレットの設定を行います。
通常は、まずAgentの設定を行い、ペアリングやデバイス機能使用の可否をどう行うかを設定しますが、他のデバイスをEdisonからtrustすると、他のデバイスがEdisonに接続する時、Edisonがすべて可にするので、Agentを使わなくても使えるようなので、Agentの説明は省きます。
「pair BDアドレス」でペアリングを行います。
[bluetooth]# pair 10:10:10:10:10:10
Attempting to pair with 10:10:10:10:10:10
[CHG] Device 10:10:10:10:10:10 Connected: yes
[CHG] Device 10:10:10:10:10:10 Modalias: usb:v000Ap0000d0000
[CHG] Device 10:10:10:10:10:10 UUIDs:
00000000-0000-0000-0000-000000000000
00000000-0000-0000-0000-000000000000
[CHG] Device 10:10:10:10:10:10 Paired: yes
Pairing successful
[CHG] Device 10:10:10:10:10:10 Connected: no
自動で接続できるように「trust BDアドレス」します。
[bluetooth]# trust 10:10:10:10:10:10
[CHG] Device 10:10:10:10:10:10 Trusted: yes
Changing 10:10:10:10:10:10 trust succeeded
「connect BDアドレス」でデバイスと接続します。
[bluetooth]# connect 10:10:10:10:10:10
Attempting to connect to 10:10:10:10:10:10
[CHG] Device 10:10:10:10:10:10 Connected: yes
Connection successful
これでAndroidタブレットと接続できました。
また「help」でヘルプを表示、「info BDアドレス」でデバイス情報が確認できます。
Bluetoothスピーカーも同じように設定します。
設定が終わったら「quit」または「exit」でbluetoothctlを終了します。
[bluetooth]# quit
[DEL] Controller 00:00:00:00:00:00 edison [default]
Androidタブレットで設定>Bluetooth>Edisonの設定で「メディアの音声」をチェックして接続して、Androidの音声出力がEdisonに行くようにします。Edisonはデフォルトのままならそのままで問題なく、Bluetoothスピーカーはそのままでスピーカーとして機能してるようなので、これでAndroidタブレットの音声出力がEdisonに接続したBluetoothスピーカーから出力されるようになります。
ちなみにAndroidタブレットの音声出力を入力して、Bluetoothスピーカーに出力しているのは、PulseAudio(PulseAudio - Wikipedia)で、「pactl info」と入力すると入出力を確認できます。
root@edison:~# pactl info
Server String: /var/run/pulse/native
Library Protocol Version: 30
Server Protocol Version: 30
Is Local: yes
Client Index: 0
Tile Size: 65496
User Name: pulse
Host Name: edison
Server Name: pulseaudio
Server Version: 6.0
Default Sample Specification: s16le 2ch 44100Hz
Default Channel Map: front-left,front-right
Default Sink: bluez_sink.スピーカーのBDアドレス
Default Source: bluez_source.タブレットのBDアドレス
Cookie: 0000:0000
また「pactl help」でヘルプが表示されます。
4-2. Bluetoothキーボードを接続して入力を行う
今回はこちら(iBUFFALO Bluetooth 3.0対応 コンパクトキーボード BSKBB22BK)を使いました。
ファームウェアはIntel Edison® Board Firmware Software Release 2.1で行っています。※不具合の修正なのか、リリース2.1のままで更新されているようなので、ファイル名が変わっていたら更新した方がいいかもしれません。
「4-1」と同じようにBluetoothキーボードの設定を行います。
rfkill unblock bluetooth>bluetoothctl>キーボードをペアリング可能な状態にする>scan on>pair BDアドレス>trust BDアドレス>connect BDアドレス>quit
Agentを使って接続しないと、そのままではキーボードとして機能しないようなので、今回はキーボードの電源を入れなおしてキーボードから再接続を行いました。
USBキーボードと同じように、/dev/input/にeventファイルが追加されるので、hexdumpで入力を確認してみましょう。他に何も接続していない場合はevent2になるので「hexdump /dev/input/event2」で確認できます。
4-3. BluetoothでPS3のコントローラを接続して、入力を確認する
今回はPlayStation3のコントローラ(DUALSHOCK 3)をBluetoothで接続して、入力を確認します。
「3-3. USBでPS3のコントローラを接続して、入力を確認する」からの続きになります。
USBに「DUALSHOCK 3」を接続した状態でPSボタン(真ん中の丸いボタン)を押すと「DUALSHOCK 3」にEdisonが登録され、接続してくるようになるらしいです。Linuxの解説等では、これで自動で使えるようになるらしいですが、なにか足りないのか、やり方が悪いのか、接続はしていますが、Edisonが拒否しているようで、手動でtrustしないと使える状態にはなりませんでした。bluetoothctlでtrustすれば、その後は自動で使えるようになるので、特に問題はないので気にしない事にします。
それでは「3-3. USBでPS3のコントローラを接続して、入力を確認する」と同じように「ps3ctest」を使って入力を確認します。
USBの時と同じように動作するようです。
5. サーボの使い方を学習して、サーボを使った試作機を開発する
サーボの使い方を学習しつつ、サーボを使った試作機の開発を目指します。
5-1. ポテンショメータを使ってサーボを操作する
まずは動作確認を兼ねて、Arduinoのスケッチでサーボを動かします。
Tutorials > Examples from Libraries > Servo > Knob
ポテンショメータの値でサーボを操作するスケッチです。今回はこちら(マイクロサーボ SG92R)とこちら(小型ボリューム 10KΩB)を使います。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- サーボ
- ポテンショメータ
- 接続用のワイヤ、ブレッドボード等
サーボをGND、5V、D9(PWM)に接続します。ポテンショメータを5V、A0、GNDに接続します。配線等は製品によって違うので、マニュアル等を参照してください。
回路を組み終わったら、次はスケッチ(プログラム)です。
サーボによって仕様が違うので、大きく違う場合はうまく動かない事があるようですが、上記のサーボは、サンプルスケッチに一切手を加えていませんが、それなりに動作しました。うまく動かない場合は「servo.attach(pin, min, max);」と「servo.writeMicroseconds(uS);」でサーボの仕様に合わせてminとmax、uSを指定すると良いようです。
5-2. PS3のコントローラを使ってサーボを操作する
PlayStation3のコントローラ(DUALSHOCK 3)の左側のアナログスティックの横(X軸)と縦(Y軸)でサーボ2つを操作します。
今回はこちら(マイクロサーボ SG92R)を2つ使います。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- サーボ x2
- DUALSHOCK 3または代替品
- 接続用のワイヤ、ブレッドボード等
DUALSHOCK 3をUSBまたはBluetoothで接続して使える状態にします。X軸用のサーボをGND、5V、D9(PWM)に接続します。Y軸用のサーボをGND、5V、D6(PWM)に接続します。配線等は製品によって違うので、マニュアル等を参照してください。
回路を組み終わったら、次はプログラムです。今回はEclipse + Intel(R) IoT Developer Kitで開発しました。
コード全文
#include "mraa.h"
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/joystick.h>
#define DEV_INPUT_JS "/dev/input/js0"
using namespace std;
int main() {
int cycle=20000;
float min=0.030;
float range=0.080;
mraa_init();
mraa_pwm_context pwmx;
mraa_pwm_context pwmy;
pwmx = mraa_pwm_init(9);
pwmy = mraa_pwm_init(6);
mraa_pwm_period_us(pwmx, cycle);
mraa_pwm_period_us(pwmy, cycle);
mraa_pwm_enable(pwmx, 1);
mraa_pwm_enable(pwmy, 1);
int jsf(-1);
jsf = open(DEV_INPUT_JS, O_RDONLY);
while(true) {
js_event js;
read(jsf, &js, sizeof(js_event));
switch(js.type & ~JS_EVENT_INIT) {
case JS_EVENT_AXIS:
if (js.number == 0) {
mraa_pwm_write(pwmx, min + (range * (js.value + 32767) / 65534));
printf("AXIS X: %d \n", js.value );
} else if (js.number == 1) {
mraa_pwm_write(pwmy, min + (range * (js.value + 32767) / 65534));
printf("AXIS Y: %d \n", js.value );
}
break;
}
}
close(jsf);
return 0;
}
6. 圧電スピーカーの使い方を学習して、圧電スピーカーを使った試作機を開発する
圧電スピーカーの使い方を学習しつつ、圧電スピーカーを使った試作機の開発を目指します。
6-1. tone関数を使って、メロディーを奏でる
まずはArduinoのスケッチで短いメロディーを奏でてみましょう。
Tutorials > Built-In Examples > 02.Digital > toneMelody
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- 圧電スピーカー
圧電スピーカーには、向きとかはないので、DIGITAL8とGNDを繋ぐだけです。
回路を組み終わったら、次はスケッチ(プログラム)です。
Arduinoには、toneという圧電スピーカーで簡単に音を出せる関数があるので、こちらのスケッチではそれを使って音を出しています。
tone関数は「tone(出力するピン,周波数,出力する時間);」で音を鳴らしてます。また出力する時間を省略すると、noTone()で止めるまで鳴り続けます。
このスケッチはmelody[]で指定された音(周波数)をnoteDurations[]で指定された長さで鳴らす事で短いメロディーを奏でています。
コード全文 toneMelody
/*
Melody
Plays a melody
circuit:
* 8-ohm speaker on digital pin 8
created 21 Jan 2010
modified 30 Aug 2011
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/Tone
*/
#include "pitches.h"
// notes in the melody:
int melody[] = {
NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
};
// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
4, 8, 8, 4, 4, 4, 4, 4
};
void setup() {
// iterate over the notes of the melody:
for (int thisNote = 0; thisNote < 8; thisNote++) {
// to calculate the note duration, take one second
// divided by the note type.
//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
int noteDuration = 1000 / noteDurations[thisNote];
tone(8, melody[thisNote], noteDuration);
// to distinguish the notes, set a minimum time between them.
// the note's duration + 30% seems to work well:
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
// stop the tone playing:
noTone(8);
}
}
void loop() {
// no need to repeat the melody.
}
コード全文 pitches.h
/*************************************************
* Public Constants
*************************************************/
#define NOTE_B0 31
#define NOTE_C1 33
#define NOTE_CS1 35
#define NOTE_D1 37
#define NOTE_DS1 39
#define NOTE_E1 41
#define NOTE_F1 44
#define NOTE_FS1 46
#define NOTE_G1 49
#define NOTE_GS1 52
#define NOTE_A1 55
#define NOTE_AS1 58
#define NOTE_B1 62
#define NOTE_C2 65
#define NOTE_CS2 69
#define NOTE_D2 73
#define NOTE_DS2 78
#define NOTE_E2 82
#define NOTE_F2 87
#define NOTE_FS2 93
#define NOTE_G2 98
#define NOTE_GS2 104
#define NOTE_A2 110
#define NOTE_AS2 117
#define NOTE_B2 123
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_FS3 185
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247
#define NOTE_C4 262
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
#define NOTE_D5 587
#define NOTE_DS5 622
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_FS5 740
#define NOTE_G5 784
#define NOTE_GS5 831
#define NOTE_A5 880
#define NOTE_AS5 932
#define NOTE_B5 988
#define NOTE_C6 1047
#define NOTE_CS6 1109
#define NOTE_D6 1175
#define NOTE_DS6 1245
#define NOTE_E6 1319
#define NOTE_F6 1397
#define NOTE_FS6 1480
#define NOTE_G6 1568
#define NOTE_GS6 1661
#define NOTE_A6 1760
#define NOTE_AS6 1865
#define NOTE_B6 1976
#define NOTE_C7 2093
#define NOTE_CS7 2217
#define NOTE_D7 2349
#define NOTE_DS7 2489
#define NOTE_E7 2637
#define NOTE_F7 2794
#define NOTE_FS7 2960
#define NOTE_G7 3136
#define NOTE_GS7 3322
#define NOTE_A7 3520
#define NOTE_AS7 3729
#define NOTE_B7 3951
#define NOTE_C8 4186
#define NOTE_CS8 4435
#define NOTE_D8 4699
#define NOTE_DS8 4978
6-2. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする
USBキーボードのキーで、こちら(圧電スピーカー PKM13EPYH4000-A0)から音が出るようにして、USBキーボードを鍵盤楽器にします。
USBをホストとして使うため、USBからの給電ができないので、こちら(スイッチングACアダプター12V 1.5A 100V-240V GF18-US1215T)を使いました。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- USBキーボード
- 圧電スピーカー
動画では、ブレッドボードを使っていますが、向きとかはなくDIGITALとGNDを繋ぐだけなので、無くても問題はないです。また今回のレビューではdigital pin 8を使っています。
はじめはArduinoで作ろうと思ってやっていましたが、EdisonではUSBHostがうまく動かないので、ArduinoでUDPサーバー、EclipseでUDPクライアントを作ってみました。2つ作るので手間はかかりますが、シンプルなUDPサーバーで音を鳴らしているので、他にも使えそうです。
まずはArduinoでUDPサーバー「BeepUdpServer」を作ります。Arduinoには、toneという圧電スピーカーで簡単に音を出せる関数があるので、今回はこちらを使って作ります。
tone(出力するピン,周波数,出力する時間)で音を鳴らしてくれます。また時間を省略すると、noTone()で止めるまで鳴り続けます。
Arduinoの例では、piches.hを使っていますが、簡単に書けるように配列にしてみました。またWiFiUdpを使うと、Wi-Fiの再接続をしないといけないみたいなので、EthernetUdpを使ってみました。
プログラムは1byte読んで、その数値で音を鳴らすだけのシンプルなものです。0がB0、37がC4(ピアノで中央辺りのド)、88がDS8、99でnoToneします。
コード全文
#include <EthernetUdp.h>
//0=B0,37=C4,88=DS8,99=noTone
int pitches [] = {
31, 33, 35, 37, 39, 41, 44, 46, 49, 52,
55, 58, 62, 65, 69, 73, 78, 82, 87, 93,
98, 104, 110, 117, 123, 131, 139, 147, 156, 165,
175, 185, 196, 208, 220, 233, 247, 262, 277, 294,
311, 330, 349, 370, 392, 415, 440, 466, 494, 523,
554, 587, 622, 659, 698, 740, 784, 831, 880, 932,
988, 1047,1109,1175,1245,1319,1397,1480,1568,1661,
1760,1865,1976,2093,2217,2349,2489,2637,2794,2960,
3136,3322,3520,3729,3951,4186,4435,4699,4978
};
unsigned int localPort = 8055;
EthernetUDP Udp;
void setup() {
pinMode(8,OUTPUT);
Udp.begin(localPort);
}
void loop() {
if (Udp.available()) {
byte c = Udp.read();
if (c == 99) {
Serial.println("Stop");
noTone(8);
} else if (c >= 0 && c <= 88) {
tone(8, pitches[c]);
}
}
}
次はEclipseでUDPクライアント「beepudpclient」を作ります。Create C/C++ IoT ProjectのIntel(R) IoT cloud analyticsが、UdpClient.hppという、都合の良さそうなヘッダを使っていたので、こちらを使って作りました。
コード全文
#include <linux/input.h>
#include "UdpClient.hpp"
#define NODE "localhost"
#define SERVICE "8055"
char p[1];
int main() {
UdpClient c;
c.connectUdp(NODE, SERVICE);
for (;;) {
struct input_event e;
if (read(0, &e, sizeof(e)) != sizeof(e)) {
exit(EXIT_FAILURE);
}
switch(e.type) {
case EV_KEY:
switch(e.code) {
case KEY_Z: if (e.value == 1){ p[0] = 37; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_S:if (e.value == 1){ p[0] = 38; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_X:if (e.value == 1){ p[0] = 39; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_D:if (e.value == 1){ p[0] = 40; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_C:if (e.value == 1){ p[0] = 41; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_V:if (e.value == 1){ p[0] = 42; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_G:if (e.value == 1){ p[0] = 43; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_B:if (e.value == 1){ p[0] = 44; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_H:if (e.value == 1){ p[0] = 45; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_N:if (e.value == 1){ p[0] = 46; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_J:if (e.value == 1){ p[0] = 47; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_M:if (e.value == 1){ p[0] = 48; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_COMMA:if (e.value == 1){ p[0] = 49; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_L:if (e.value == 1){ p[0] = 50; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_DOT:if (e.value == 1){ p[0] = 51; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_SEMICOLON:if (e.value == 1){ p[0] = 52; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_SLASH:if (e.value == 1){ p[0] = 53; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_Q:if (e.value == 1){ p[0] = 54; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_2:if (e.value == 1){ p[0] = 55; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_W:if (e.value == 1){ p[0] = 56; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_3:if (e.value == 1){ p[0] = 57; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_E:if (e.value == 1){ p[0] = 58; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_4:if (e.value == 1){ p[0] = 59; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_R:if (e.value == 1){ p[0] = 60; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_T:if (e.value == 1){ p[0] = 61; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_6:if (e.value == 1){ p[0] = 62; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_Y:if (e.value == 1){ p[0] = 63; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_7:if (e.value == 1){ p[0] = 64; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_U:if (e.value == 1){ p[0] = 65; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_I:if (e.value == 1){ p[0] = 66; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_9:if (e.value == 1){ p[0] = 67; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_O:if (e.value == 1){ p[0] = 68; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_0:if (e.value == 1){ p[0] = 69; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_P:if (e.value == 1){ p[0] = 70; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_MINUS:if (e.value == 1){ p[0] = 71; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_LEFTBRACE:if (e.value == 1){ p[0] = 72; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
case KEY_RIGHTBRACE: if (e.value == 1){ p[0] = 73; c.writeData(p); } else if (e.value == 0) { p[0] = 99; c.writeData(p); } break;
}
break;
default:
break;
}
}
}
標準入力をUSBキーボードのイベントファイルにして実行します。
beepudpclient < /dev/input/event2
Eclipseで実行する場合は、「Run Configurations」の「Arguments」に書きます。
6-3. mraaのPWMでtone関数と同じような関数を作る
プログラムが2つになってるのは、いろいろと手間がかかるので、今回はArduinoのtone関数と同じような機能を持った関数をmraaのPWMを使って作り、1つのプログラムで済むようにします。
プログラムはEclipse + Intel(R) IoT Developer Kitで開発しました。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- 圧電スピーカー
回路は今までとほぼ同じですが、PWMを使うのでDIGITAL9(PWM)とGNDに繋ぎます。
次はプログラムです。Arduinoのtone関数と同じように使えるtoneとnoToneを作りました。このプログラムはテストとして、1秒間隔でドレミと鳴らして終了します。
コード全文
#include "mraa.h"
using namespace std;
#define NOTE_C5 523
#define NOTE_D5 587
#define NOTE_E5 659
void tone(int pin, unsigned int freq) {
mraa_pwm_context p = mraa_pwm_init(pin);
mraa_pwm_period_us(p, 1000000/freq);
mraa_pwm_enable(p, 1);
mraa_pwm_write(p, 0.5);
}
void tone(int pin, unsigned int freq, unsigned long dur) {
mraa_pwm_context p = mraa_pwm_init(pin);
mraa_pwm_period_us(p, 1000000/freq);
mraa_pwm_enable(p, 1);
mraa_pwm_write(p, 0.5);
usleep(1000*dur);
mraa_pwm_enable(p, 0);
}
void noTone(int pin) {
mraa_pwm_context p = mraa_pwm_init(pin);
mraa_pwm_enable(p, 0);
}
int main() {
mraa_init();
int pin = 9;
tone(pin, NOTE_C5, 1000);
tone(pin, NOTE_D5, 1000);
tone(pin, NOTE_E5);
sleep(1);
noTone(pin);
return 0;
}
6-4. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする その2
音が鳴らせるようになったので、1つのプログラムでできるようにしてみました。
USBをホストとして使うため、USBからの給電ができないので、こちら(スイッチングACアダプター12V 1.5A 100V-240V GF18-US1215T)を使いました。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- USBキーボード
- 圧電スピーカー
回路は「6-3. mraaのPWMでtone関数と同じような関数を作る」と同じです。
次はプログラムです。今回は周波数を計算(MIDI Tuning Standard - Wikipedia, the free encyclopedia)で出しているので、より正確な音になるようにtone関数のfreqをdoubleにしてみました。
コード全文
#include "mraa.h"
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>
#include <math.h>
#define DEV_INPUT "/dev/input/event2"
using namespace std;
void tone(int pin, double freq) {
mraa_pwm_context p = mraa_pwm_init(pin);
mraa_pwm_period_us(p, 1000000/freq);
mraa_pwm_enable(p, 1);
mraa_pwm_write(p, 0.5);
}
void tone(int pin, double freq, unsigned long dur) {
mraa_pwm_context p = mraa_pwm_init(pin);
mraa_pwm_period_us(p, 1000000/freq);
mraa_pwm_enable(p, 1);
mraa_pwm_write(p, 0.5);
usleep(1000*dur);
mraa_pwm_enable(p, 0);
}
void noTone(int pin) {
mraa_pwm_context p = mraa_pwm_init(pin);
mraa_pwm_enable(p, 0);
}
double mts(double mnn){
return(pow(2,((mnn-69)/12))*440);
}
int main() {
int f(-1);
f = open(DEV_INPUT, O_RDONLY);
struct input_event e;
mraa_init();
int pin = 9;
while(true) {
if (read(f, &e, sizeof(e)) != sizeof(e)) {
exit(EXIT_FAILURE);
}
switch(e.type) {
case EV_KEY:
switch(e.code) {
case KEY_Z:if (e.value == 1){ tone(pin, mts(60)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_S:if (e.value == 1){ tone(pin, mts(61)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_X:if (e.value == 1){ tone(pin, mts(62)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_D:if (e.value == 1){ tone(pin, mts(63)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_C:if (e.value == 1){ tone(pin, mts(64)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_V:if (e.value == 1){ tone(pin, mts(65)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_G:if (e.value == 1){ tone(pin, mts(66)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_B:if (e.value == 1){ tone(pin, mts(67)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_H:if (e.value == 1){ tone(pin, mts(68)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_N:if (e.value == 1){ tone(pin, mts(69)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_J:if (e.value == 1){ tone(pin, mts(70)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_M:if (e.value == 1){ tone(pin, mts(71)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_COMMA:if (e.value == 1){ tone(pin, mts(72)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_L:if (e.value == 1){ tone(pin, mts(73)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_DOT:if (e.value == 1){ tone(pin, mts(74)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_SEMICOLON:if (e.value == 1){ tone(pin, mts(75)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_SLASH:if (e.value == 1){ tone(pin, mts(76)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_Q:if (e.value == 1){ tone(pin, mts(77)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_2:if (e.value == 1){ tone(pin, mts(78)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_W:if (e.value == 1){ tone(pin, mts(79)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_3:if (e.value == 1){ tone(pin, mts(80)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_E:if (e.value == 1){ tone(pin, mts(81)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_4:if (e.value == 1){ tone(pin, mts(82)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_R:if (e.value == 1){ tone(pin, mts(83)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_T:if (e.value == 1){ tone(pin, mts(84)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_6:if (e.value == 1){ tone(pin, mts(85)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_Y:if (e.value == 1){ tone(pin, mts(86)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_7:if (e.value == 1){ tone(pin, mts(87)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_U:if (e.value == 1){ tone(pin, mts(88)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_I:if (e.value == 1){ tone(pin, mts(89)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_9:if (e.value == 1){ tone(pin, mts(90)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_O:if (e.value == 1){ tone(pin, mts(91)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_0:if (e.value == 1){ tone(pin, mts(92)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_P:if (e.value == 1){ tone(pin, mts(93)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_MINUS:if (e.value == 1){ tone(pin, mts(94)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_LEFTBRACE:if (e.value == 1){ tone(pin, mts(95)); } else if (e.value == 0) { noTone(pin); } break;
case KEY_RIGHTBRACE: if (e.value == 1){ tone(pin, mts(96)); } else if (e.value == 0) { noTone(pin); } break;
}
break;
default:
break;
}
}
return 0;
}
7. 赤外線通信を学習して、赤外線通信を使った試作機を開発する
ここでは赤外線LEDや赤外線受光モジュール、赤外線通信関係の使い方や仕組み、機能を学習しつつ、赤外線通信を使った試作機の開発を目指します。
7-1. 赤外線LEDを使って、TVのチャンネルを変える
今回は赤外線LEDを使ってTV(Panasonic VIERA TH-L47DT5)のチャンネルアップする試作機を作り、家電等の赤外線通信を学習します。
もっと簡単にできるものかと思っていましたが、Edison特有の問題もあり、何かと問題があって、なかなかうまくいきませんでした。まずは家電で多く使われている赤外線リモコンの赤外線通信について軽く説明します。日本の家電で使われている主要な赤外線通信のフォーマットは、下記の3種類です。※公式な資料ではないので、情報の正確性は不明です。
NEC
キャリア: 赤外線(λp = 940nm)
サブキャリア: 38kHz duty1/3
T = 562μs
Data bitは0が1T(ON)1T(OFF)、1が1T(ON)3T(OFF)
Frameは16T 8Tのリーダーの後に
32bitの固定長フレームで
16bitのカスタマーコード
8bitのデータ
8bitの反転データ
終端
AEHA
キャリア: 赤外線(λp = 940nm)
サブキャリア: 33-40kHz duty1/3
T = 350~500μs
Data bitは0が1T(ON)1T(OFF)、1が1T(ON)3T(OFF)
Frameは8T(ON) 4T(OFF)のリーダーの後に
16bitのカスタマーコード
4bitのパリティ
可変長のデータ(28bit)
終端
SONY
キャリア: 赤外線(λp = 940nm)
サブキャリア: 40kHz duty1/3
T = 600μs
Data bitは0が1T(OFF)1T(ON)、1が1T(OFF)2T(ON)
Frameは4T(ON)のリーダーの後に
7bitのデータ
5/8/13bitのアドレス
キャリアは赤外線LEDの波長で、家電の一般的なリモコンについている赤外線LEDの多くが、これもしくはこれに近いものらしいので、気にしなくても問題がない場合が多いようです。
サブキャリアの周波数は、LEDが1秒間に点滅する回数で、各フォーマットで少しづつ違いますが、ある程度幅があったりするので、違っていても動くことがあるようです。
送信するデータはフォーマットで大きく違うし、メーカーや製品でも違うので、細かく合わせる必要があります。今回はパナソニックなのでAEHAになります。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- 赤外線LED
- 50Ω程度の抵抗
赤外線LEDは向きがあるので+と-には注意する必要があります。大半の赤外線LEDを5Vで使う場合は、50Ω程度の抵抗を付ける事になるようですが、付けると出力が落ちるからなのか、反応する範囲が狭くなったりするようなので、適度に調節するのが良いようです。今回の赤外線LEDはこちらですが、いらなくなったリモコンから抜いてきたものなので、スペックは不明です。
間に抵抗を入れて赤外線LEDの+とDigital 8を接続します。赤外線LEDの-をGNDに接続します。
次はプログラムです。やり方や開発環境がいろいろありますが、今回はArduinoで開発しました。
まずEdisonのArduinoで開発する時にありがちな問題ですが、delayMicrosecondsが遅いという問題があります。
例えば35kHz duty1/3を普通に書く場合、下記のような処理が1秒で行えないといけないわけですが
for(int i = 0; i < 35000; i++) {
digitalWrite(IR, HIGH);
delayMicroseconds(d);
digitalWrite(IR, LOW);
delayMicroseconds(d*2);
}
delayMicrosecondsが遅すぎて、dを1にしても7.3秒ほどかかるので話になりません。
そこで今回は下記のような関数をdelayMicrosecondsの代わりに使って、下記のようにしました。
void waitMicroseconds(long wait) {
long start = micros();
while (micros() - start < wait) {}
}
for(int i = 0; i < 35000; i++) {
digitalWrite(IR, HIGH);
waitMicroseconds(d);
digitalWrite(IR, LOW);
waitMicroseconds(d*2);
}
こちらで行うとdが10で1.074秒、9で0.966秒ほどでした。
今回は待ち時間(w)を増減させて1秒に近くなるように調整する処理をsetupで行うようにしてみました。
コード全文
#define IR 8
int w;
byte b[6] = {0x40, 0x04, 0x01, 0x00, 0x2C, 0x2D};
void waitMicroseconds(long wait) {
long start = micros();
while (micros() - start < wait) {}
}
void setup() {
int h = 0;
w = 10;
pinMode(IR, OUTPUT) ;
Serial.begin(115200);
while (true) {
long t = micros();
for(int i = 0; i < 35000; i++) {
digitalWrite(IR, HIGH);
waitMicroseconds(w);
digitalWrite(IR, LOW);
waitMicroseconds(w*2);
}
t = micros() - t;
Serial.print(w);
Serial.print(": ");
Serial.println(t);
if (t > 1000000) {
if (h == 2) {
break;
}
h = 1;
w--;
}
else {
if (h == 1) {
break;
}
h = 2;
w++;
}
}
}
void IRON(long ontime) {
long start = micros();
while (micros() - start < ontime) {
digitalWrite(IR, HIGH);
waitMicroseconds(w);
digitalWrite(IR, LOW);
waitMicroseconds(w*2);
}
}
void loop() {
IRON(3200);
waitMicroseconds(1600);
for (int i = 0; i < 6; i++) {
for (int j = 7; j > -1; j--) {
IRON(400);
if (bitRead(b[i], j) == 1) {
waitMicroseconds(1200);
} else {
waitMicroseconds(400);
}
}
}
IRON(400);
delay(5000);
}
7-2. 赤外線受光モジュールと赤外線LEDでEdisonを学習リモコンにする
今回は赤外線受光モジュールのテストとして、赤外線受光モジュールで受信したデータを、そのまま赤外線LEDから送信する。学習リモコンのような試作機を作ります。赤外線受光モジュールはこちら(リモコン受光モジュール RPM6938)を使いました。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- 赤外線受光モジュール
- 赤外線LED
- 50Ω程度の抵抗
- 接続用のワイヤ、ブレッドボード等
前回(7-1)の回路に赤外線受光モジュールを追加します。モジュールによって形やピンの配置が違うので、詳細は各製品のマニュアル等を参照してください。今回使用する「リモコン受光モジュール RPM6938」は、丸い出っ張りがある方から見て、左をDigital 2、中央をGND、右を5Vに接続しました。
次はプログラムです。今回もArduinoで開発しました。
今回使用した「リモコン受光モジュール RPM6938」は「USBから給電では正常に動かない」「通常時はHIGHですが、ノイズのような感じでLOWになる」という2つの問題(詳細不明)があるので、その対策として、給電はアダプターで行い、ノイズ対策のコードを少し追加してあります。
コード全文
/*
* 赤外線受光モジュールのテスト用スケッチ
* リモコンから受信したデータを記録して赤外線LEDから送信する
* シリアルモニタで「r」を入力してからリモコンのボタンを押して記録する
* シリアルモニタで「s」を入力すると記録したデータを赤外線LEDから送信する
* テストでは リモコン受光モジュール RPM6938 を使用しました
*/
#define IRR 2//赤外線受光モジュールのPIN
#define IR 8//赤外線LEDのPIN
#define FREQ 38000//周波数
#define DUTY 2//duty 1/2=1 1/3=2
unsigned long p[1024];//リモコンから受信したデータを格納する変数、1024に意味はない
int w = 10;//赤外線LEDの周波数(点滅の速さ)を調整する為の数値
int mode = 0;//UIで使う変数 0=入力、1=受信(recv)、2=送信(send)
void waitMicroseconds(long wait) {
long start = micros();
while (micros() - start < wait) {}
}
void IRON(long ontime) {
long start = micros();
while ( micros() - start < ontime) {
digitalWrite(IR, HIGH);
waitMicroseconds(w);
digitalWrite(IR, LOW);
waitMicroseconds(w * DUTY);
}
}
void setup(){
int h = 0;
Serial.begin(115200);
pinMode(IRR, INPUT);
pinMode(IR, OUTPUT);
while (true) {
long t = micros();
for(int i = 0; i < FREQ; i++) {
digitalWrite(IR, HIGH);
waitMicroseconds(w);
digitalWrite(IR, LOW);
waitMicroseconds(w * DUTY);
}
t = micros() - t;
Serial.print(w);
Serial.print(": ");
Serial.println(t);
if (t > 1000000) {
if (h == 2) {
break;
}
h = 1;
w--;
} else {
if (h == 1) {
break;
}
h = 2;
w++;
}
}
}
void loop() {
if (mode == 0) {
if (Serial.available()) {
delay(100);
char m = Serial.read();
if (m == 'r') {
mode = 1;
} else if (m == 's') {
mode = 2;
}
}
} else if (mode == 1) {
Serial.println("recv start");
long now, lastchange;
int c = 0;
int lastrecv = 0;
while (true) {
if (digitalRead(IRR) == LOW && lastrecv == 0) {
if (c == 0) {
lastrecv = 1;
lastchange = micros();
c++;
} else {
lastrecv = 1;
now = micros();
p[c - 1] = now - lastchange;
Serial.print((now - lastchange));
Serial.print(",");
lastchange = now;
c++;
}
} else if (digitalRead(IRR) == HIGH && lastrecv == 1) {
lastrecv = 0;
now = micros();
//noise対策として間隔が250us以下(一般的なリモコンは300以上)はcを0にしてやり直す
if (now - lastchange < 250) {
c = 0;
} else {
p[c - 1] = now - lastchange;
Serial.print((now - lastchange));
Serial.print(",");
lastchange = now;
c++;
}
} else {
//recv開始後で0.01秒間変化がなければ終端として0を入れて終了する
if (lastchange + 10000 < micros() && c > 0) {
p[c - 1] = 0;
Serial.print("\nrecv end\n");
mode = 0;
break;
}
}
//配列の上限対策としてcが1024だった場合は最後に終端として0を入れて終了する
if (c == 1024) {
p[c - 1] = 0;
Serial.print("\nrecv end\n");
mode = 0;
break;
}
}
} else if (mode == 2) {
Serial.println("send");
mode = 0;
for (int i = 0; i < 1024; i++) {
//0(終端)だった場合は終了する
if (p[i] == 0) {
break;
} else {
//偶数と奇数(偶数以外)で分岐してON OFFを交互に行う
if (i % 2 == 0) {
IRON(p[i]);
} else {
waitMicroseconds(p[i]);
}
}
}
}
}
7-3. Eclipse + mraa_pwmで7-1と同じ機能のプログラムを書く
今回は「7-1. 赤外線LEDを使って、TVのチャンネルを変える」でArduinoで書いたプログラムと同じ機能を持ったプログラムをEclipse + Intel(R) IoT Developer Kitのmraa_pwmを使って書きます。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- 赤外線LED
- 50Ω程度の抵抗
回路は「7-1」とほぼ同じですが、PWMを使うのでピンがDigital 8からDigital 9になっています。
Arduinoで書いたプログラムを置き換える感じで書いてみましたが、反応しない事が多い感じでした。LinuxのC/C++では、delayMicroseconds(μs)と同じ感じの関数で、usleep(μs)という関数を使うようなので、これを使いましたが、delayMicroseconds(μs)の時と同じように遅すぎるのが問題のようです。
下記のプログラムを実行したところ、100μs前後でたまに500μs以上といった感じでした。
コード全文
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
using namespace std;
int main() {
while(true) {
struct timeval start;
struct timeval now;
gettimeofday(&start, NULL);
usleep(1);
gettimeofday(&now, NULL);
int t = now.tv_usec - start.tv_usec;
printf("%d\n", t);
}
return 0;
}
-100μsするだけでも、たまに失敗する程度で動きそうですが、Arduinoの時と同じ感じで、下記の関数をusleepの代わりに使ってみました。
void waitMicroseconds(long wait) {
struct timeval start;
struct timeval now;
gettimeofday(&start, NULL);
while (true) {
gettimeofday(&now, NULL);
if (now.tv_sec == start.tv_sec) {
if (now.tv_usec - start.tv_usec >= wait) {
break;
}
} else {
if (now.tv_usec + 1000000 - start.tv_usec >= wait) {
break;
}
}
}
}
Arduinoの時よりだいぶ長くなってしまいました。999999の次が0みたいなので、秒が違っていたら1000000足すようにしました。
コード全文
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
using namespace std;
void waitMicroseconds(long wait) {
struct timeval start;
struct timeval now;
gettimeofday(&start, NULL);
while (true) {
gettimeofday(&now, NULL);
if (now.tv_sec == start.tv_sec) {
if (now.tv_usec - start.tv_usec >= wait) {
break;
}
} else {
if (now.tv_usec + 1000000 - start.tv_usec >= wait) {
break;
}
}
}
}
int main() {
while(true) {
struct timeval start;
struct timeval now;
gettimeofday(&start, NULL);
waitMicroseconds(1);
gettimeofday(&now, NULL);
int t = now.tv_usec - start.tv_usec;
printf("%d\n", t);
}
return 0;
}
7μs前後で安定しているので、これなら失敗が少なくなりそうです。
コード全文
#include "mraa.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
using namespace std;
void waitMicroseconds(long wait) {
struct timeval start;
struct timeval now;
gettimeofday(&start, NULL);
while (true) {
gettimeofday(&now, NULL);
if (now.tv_sec == start.tv_sec) {
if (now.tv_usec - start.tv_usec >= wait) {
break;
}
} else {
if (now.tv_usec + 1000000 - start.tv_usec >= wait) {
break;
}
}
}
}
int main() {
unsigned char f[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
unsigned char p[6] = {0x40, 0x04, 0x01, 0x00, 0x2C, 0x2D};
mraa_init();
mraa_pwm_context pwm;
pwm = mraa_pwm_init(9);
mraa_pwm_period_us(pwm, 1000000/35000);
mraa_pwm_enable(pwm, 1);
while (true) {
mraa_pwm_write(pwm, 0.333);
waitMicroseconds(3200);
mraa_pwm_write(pwm, 0);
waitMicroseconds(1600);
for (int i = 0; i < 6; i++) {
for (int j = 7; j > -1; j--) {
mraa_pwm_write(pwm, 0.333);
waitMicroseconds(400);
mraa_pwm_write(pwm, 0);
if (p[i] & f[j]) {
waitMicroseconds(1200);
} else {
waitMicroseconds(400);
}
}
}
mraa_pwm_write(pwm, 0.333);
waitMicroseconds(400);
mraa_pwm_write(pwm, 0);
sleep(5);
}
return 0;
}
ほぼ失敗なしで、チャンネルアップするようになりました。
7-4. Eclipse + mraa_pwmで7-2と同じ機能のプログラムを書く
前回に引き続き、今回は「7-2. 赤外線受光モジュールと赤外線LEDでEdisonを学習リモコンにする」でArduinoで書いたプログラムと同じ機能を持ったプログラムをEclipse + Intel(R) IoT Developer Kitのmraa_pwmを使って書きます。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- 赤外線受光モジュール
- 赤外線LED
- 50Ω程度の抵抗
- 接続用のワイヤ、ブレッドボード等
回路は「7-2」とほぼ同じですが、こちらもPWMを使うので赤外線LEDのピンがDigital 8からDigital 9になっています。
Arduinoで書いたプログラムを置き換える感じで書いてみましたが、USBからの給電で動かしてる時と同じ感じで、赤外線受光モジュールが正常に動かずにLOWになりました。いろいろ試したところ、PWMを使っている時(enableにした時)は、赤外線受光モジュールが正常に動作しないようです。そこで使う時だけenableするようにしたところ正常に動作するようになりました。またprintfで時間がかかって、受信の精度が下がる雰囲気だったので、終了時にまとめて行うようにしました。
コード全文
#include "mraa.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
using namespace std;
#define IRR 2//赤外線受光モジュールのPIN
#define IR 9//赤外線LEDのPIN
#define FREQ 38000//周波数
#define DUTY 0.333//duty
unsigned int p[1024];//リモコンから受信したデータを格納する変数、1024に意味はない
int mode = 0;//UIで使う変数 0=入力、1=受信(recv)、2=送信(send)
void waitMicroseconds(long wait) {
struct timeval start;
struct timeval now;
gettimeofday(&start, NULL);
while (true) {
gettimeofday(&now, NULL);
if (now.tv_sec == start.tv_sec) {
if (now.tv_usec - start.tv_usec >= wait) {
break;
}
} else {
if (now.tv_usec + 1000000 - start.tv_usec >= wait) {
break;
}
}
}
}
int main() {
mraa_init();
mraa_pwm_context ir;
ir = mraa_pwm_init(IR);
mraa_pwm_period_us(ir, 1000000/FREQ);
mraa_gpio_context irr;
irr = mraa_gpio_init(IRR);
mraa_gpio_dir(irr, MRAA_GPIO_IN);
while (true) {
if (mode == 0) {
char m = getchar();
if (m == 'r') {
mode = 1;
} else if (m == 's') {
mode = 2;
}
} else if (mode == 1) {
printf("recv start\n");
struct timeval lastchange;
struct timeval now;
int c = 0;
int lastrecv = 0;
while (true) {
if (mraa_gpio_read(irr) == 0 && lastrecv == 0) {
if (c == 0) {
lastrecv = 1;
gettimeofday(&lastchange, NULL);
c++;
} else {
lastrecv = 1;
gettimeofday(&now, NULL);
if (now.tv_sec == lastchange.tv_sec) {
p[c - 1] = now.tv_usec - lastchange.tv_usec;
} else {
p[c - 1] = now.tv_usec + 1000000 - lastchange.tv_usec;
}
lastchange = now;
c++;
}
} else if (mraa_gpio_read(irr) == 1 && lastrecv == 1) {
lastrecv = 0;
gettimeofday(&now, NULL);
//noise対策として間隔が250us以下(一般的なリモコンは300以上)はcを0にしてやり直す
if (now.tv_sec == lastchange.tv_sec && now.tv_usec - lastchange.tv_usec < 250) {
c = 0;
} else if (now.tv_sec != lastchange.tv_sec && now.tv_usec + 1000000 - lastchange.tv_usec < 250){
c = 0;
} else {
if (now.tv_sec == lastchange.tv_sec) {
p[c - 1] = now.tv_usec - lastchange.tv_usec;
} else {
p[c - 1] = now.tv_usec + 1000000 - lastchange.tv_usec;
}
lastchange = now;
c++;
}
} else {
//recv開始後で0.01秒間変化がなければ終端として0を入れて終了する
gettimeofday(&now, NULL);
if (now.tv_sec == lastchange.tv_sec && lastchange.tv_usec + 10000 < now.tv_usec && c > 0) {
p[c - 1] = 0;
for (int i = 0; i < c - 1; i++) {
printf("%d,", p[i]);
}
printf("\nrecv end\n");
mode = 0;
break;
} else if (now.tv_sec != lastchange.tv_sec && lastchange.tv_usec + 10000 < now.tv_usec + 1000000 && c > 0) {
p[c - 1] = 0;
for (int i = 0; i < c - 1; i++) {
printf("%d,", p[i]);
}
printf("\nrecv end\n");
mode = 0;
break;
}
}
//配列の上限になったら最後に終端として0を入れて終了する
if (c == 1024) {
p[c - 1] = 0;
for (int i = 0; i < c - 1; i++) {
printf("%d,", p[i]);
}
printf("\nrecv end\n");
mode = 0;
break;
}
}
} else if (mode == 2) {
printf("send\n");
mraa_pwm_enable(ir, 1);
mode = 0;
for (int i = 0; i < 1024; i++) {
//0(終端)だった場合は終了する
if (p[i] == 0) {
mraa_pwm_enable(ir, 0);
break;
} else {
//偶数と奇数(偶数以外)で分岐してON OFFを交互に行う
if (i % 2 == 0) {
mraa_pwm_write(ir, DUTY);
waitMicroseconds(p[i]);
} else {
mraa_pwm_write(ir, 0);
waitMicroseconds(p[i]);
}
}
}
}
}
return 0;
}
8. 焦電型赤外線センサの使い方を学習して、焦電型赤外線センサを使った試作機を開発する
ここでは焦電型赤外線センサの使い方や仕組み、機能を学習しつつ、焦電型赤外線センサを使った試作機の開発を目指します。
焦電型赤外線センサは、焦電効果を利用した赤外線センサで、電子レンジ、エアコンのコントロール、警報器、自動ドア、照明機器等、いろいろな機器に使われています。
8-1. 焦電型赤外線センサを使った警報器的な試作機
今回使う焦電型赤外線センサはこちら(焦電型赤外線センサ AKE-1(RE-210))です。秋月電子で100円だったので、何となく買っておいたものです。
必要なハードウェア
- インテル® Edison モジュール
- Arduino拡張ボード
- 焦電型赤外線センサ
- 抵抗 10kΩ
- 圧電スピーカー
- 接続用のワイヤ、ブレッドボード等
上手に使うのは、難しそうですが、アバウトな感じで使うなら、シンプルな回路とプログラムでも行けるようなので、とりあえず回路を組んで、出力を確認してみました。
コード全文
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println(analogRead(A0));
delay(100);
}
変化が少ない状態の出力は135から141くらいのようなので、Digital12に圧電スピーカーを追加して下記のスケッチを実行してみました。
コード全文
void setup() {
Serial.begin(9600);
}
void loop() {
if (analogRead(Pin) > 141) {
tone(12, 800);
} else {
noTone(12);
}
Serial.println(analogRead(A0));
delay(100);
}
警報器的な感じにはなりましたが、反応する距離が短く、安定しないので、実用的ではないですね。
Intel® XDK IoT Editionを試す
Edisonのレビューで、Arduino IDEだけなのもどうかと思ったので、IoT(IoT - Wikipedia)開発向けの開発環境、Intel® XDK IoT Editionを試してみました。
Intel® Edison Board InstallersのオプションまたはIntel® XDK IoT Editionをダウンロードしてインストールします。
XDK IoT Editionを起動するとサインインを求められるので、アカウントがない場合は作ります。
またBonjourがインストールされていない場合は、インストールを促されます。
回路が「Color String Web Server」のままで、アナログにトリマポテンシオメータが付いていたので、TemplatesのAnalog Readで新規プロジェクトを作ってみました。
このTemplatesはアナログ0の値をConsoleに書くものでした。
そのままで動くようなので、次はアップロードするために、IoTデバイスを選択します。
Edisonを選択してUsernameとPasswordを入力して接続します。
接続できたらアップロードします。
Runを押すと実行されます。
ファイルは.node_app_slotにアップロードされます。SSHでログインして実行してみました。
Eclipse + Intel(R) IoT Developer Kitを試す
Eclipse(Eclipse - Wikipedia)は、IBMによって開発された高機能な統合開発環境で、オープンソース化され、現在はEclipse Foundationが開発を行っています。
Wi-Fiを使って手軽にEdison上でテストできるので、とても便利です。何も考えずに使っていましたが、C/C++で開発してるので、クロスコンパイルしてるという事ですね。
ArduinoとXDKはどちらも特殊な感じの開発環境なので、C/C++ならEdisonでセルフコンパイルする事もできますが、3つの中ではこれが一番普通な感じがします。
ログに表示されるので、すぐにわかる事ですが、コンパイルしたファイルはEdisonの/tmp/にアップロードして実行しているようです。
カーネルビルドに挑戦する
Edisonのカーネルビルドを行います。今回はカーネルビルド専用に1台組みました。何となくリモートで何でもできるようにvProで組んでみました。
CPU: Intel Core i7-3770
マザーボード: Intel BOXDQ77MK
メモリー: CFD W3U1600HQ-8G 16GB
HDD: WesternDigital WD10EFRX 1TB
電源: HuntKey 絢風 AYAKAZE300 300W GOLD
まずはUbuntu 14.04 32bitと去年のソースファイル(edison-src-rel1-maint-rel1-ww42-14.tgz)で挑戦しました。
Ubuntu 14.04 32bitと「edison-src-rel1-maint-rel1-ww42-14.tgz」の組み合わせでは、エラーを1つ修正しただけでビルドに成功しました。エラーはcapivara3さんと同じところだったので、すぐに解決できました。
操作の流れ
gitの設定
git config --global user.email "email"
git config --global user.name "name"
設定しておかないと失敗するみたい
ビルドに必要なパッケージをインストール
sudo apt-get install build-essential git diffstat gawk chrpath texinfo libtool gcc-multilib
ソースファイルをダウンロード
wget http://downloadmirror.intel.com/24389/eng/edison-src-rel1-maint-rel1-ww42-14.tgz
ソースファイルを解凍する。
tar xvf edison-src-rel1-maint-rel1-ww42-14.tgz
解凍で作られたディレクトリに移動
cd edison-src
ビルド環境のセットアップを行う
./device-software/setup.sh
シェル環境の設定を行う
source poky/oe-init-build-env
ビルド開始
bitbake edison-image
エラー修正
vi ../device-software/meta-edison-distro/recipes-connectivity/libwebsockets/libwebsockets_1.23.bb
22行目を
export OPENSSL_CONF=${TMPDIR}/sysroots/i686-linux/usr/lib/ssl/openssl.cnf
に修正して保存する。
ビルド開始
bitbake edison-image
フラッシュに必要なファイルをtoFlashにまとめる。
../device-software/utils/flash/postBuild.sh
toFlashに移動する
cd toFlash
Edisonにイメージを書き込む
flashall.sh
メッセージがでたら、USBでPCとEdisonを接続する。
さらにUbuntu 14.04 32bitと最新(2015/10/20)の「edison-src-ww25.5-15.tgz」の組み合わせでも挑戦しました。
こちらはエラーなしで成功しましたが、「setup.sh」で「--build_dir=BUILDDIR」を指定しなかったのがまずかったようで、「postBuild.sh」でコピーに失敗しました。今回の場合は、何も指定しないとbuildディレクトリが「/edison-src/build/」ではなく「/edison-src/out/linux32/build/」になりますが、「postBuild.sh」は「/edison-src/build/」からファイルをコピーしようとするので、そこにはファイルがないので失敗するようです。※追記2015/10/26 「--build_dir=BUILDDIR」を指定していても、「/edison-src/build/」以外(/edison-src/build/を指定する場合は「--build_dir=/home/user/path/edison-src/」です)だと「postBuild.sh」でコピーに失敗するようです。「/edison-src/build/」以外を使う場合は、「postBuild.sh」でbuildディレクトリを指定しないとだめそうです
同じように失敗する場合は、「postBuild.sh」を実行する時に場所を指定すると成功します。
./meta-intel-edison/utils/flash/postBuild.sh /home/user/path/edison-src/out/linux32/build
ディレクトリ名が「device-software」から「meta-intel-edison」に変わっていたりしますが、流れは「edison-src-rel1-maint-rel1-ww42-14.tgz」とほぼ同じです。
カスタムビルドに挑戦する
今回はUbuntu 14.04 32bitと最新(2015/10/26)のedison-src-ww25.5-15.tgzで行います。
まずはgitの設定とビルドに必要なパッケージのインストールをしますが、既に行っている場合は必要ありません。
gitの設定をします。
git config --global user.email "email"
git config --global user.name "name"
設定しておかないと失敗するみたい
ビルドに必要なパッケージをインストールします。
sudo apt-get install build-essential git diffstat gawk chrpath texinfo libtool gcc-multilib
ソースファイルをダウンロードします。
wget http://downloadmirror.intel.com/25028/eng/edison-src-ww25.5-15.tgz
※前回ビルドしたものは一切使わずに、ダウンロードから再度行っています。
ソースファイルを解凍する。
tar xvf edison-src-ww25.5-15.tgz
解凍で作られたディレクトリに移動します。
cd edison-src
ビルド環境のセットアップを行います。
./meta-intel-edison/setup.sh
※今回もオプションは省略しました
buildディレクトリに移動します。
cd /out/linux32/
シェル環境の設定を行います。
source poky/oe-init-build-env
一度ビルドします
bitbake edison-image
カーネルの設定ファイルは「edison-src/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/files/defconfig」です。直接編集するか、「menuconfig」で作ったファイルで置き換えます。
今回は「menuconfig」を使って編集します。
bitbake virtual/kernel -c menuconfig
※Xが必要です
編集してsaveします。
menuconfigで作った設定ファイルをコピーします。
cp /home/user/path/edison-src/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/files/defconfig /home/user/path/edison-src/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/files/defconfig.orig
cp /home/user/path/edison-src/out/linux32/build/tmp/work/edison-poky-linux/linux-yocto/3.10.17-r0/linux-edison-standard-build/.config /home/user/path/edison-src/meta-intel-edison/meta-intel-edison-bsp/recipes-kernel/linux/files/defconfig
ビルドします。
bitbake virtual/kernel -c configure -f -v
bitbake edison-image
フラッシュに必要なファイルをtoFlashにまとめる。
/home/user/path/edison-src/meta-intel-edison/utils/flash/postBuild.sh /home/user/path/edison-src/out/linux32/build
Edisonをバッテリーで起動する
Edisonをこちら(Li-Poバッテリー (3.7V 380mAh) G024H [日本正規品])で起動してみました。
正常に起動しました。ピンの位置は丁度いいけど、強く挿しこんだら抜くのに苦労しそうです。このままだとプラスとマイナスを間違えやすそうだし、バッテリー接続用のソケットを付けた方がよさそうです。光っているオンボードのLEDは電源が入ってる時に点灯するLED(DS1)です。
バッテリーを接続した状態で、ACアダプターを接続しました。LEDがもう一つ点灯しましたが、こちらは充電中に光るLED(DS3)です。ACアダプターからの給電で、Li-Poバッテリーを充電できるらしいです。ちなみにLチカで使われるオンボードのLED(DS2)はバッテリー用のピン(J2)のすぐ右にあります。1時間ほどそのまま繋いでおいたら、充電が終わったみたいで、いつのまにか充電中に光るLED(DS3)が消えていました。
Edisonのサービスを追加する
EdisonのOSは、systemctlというコマンドでサービスの管理を行えます。今回は例として「6-2. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする」で作ったUDPサーバー「BeepUdpServer」を追加するための、必要最小限の説明を行います。
まずは「/sketch/sketch.elf」のままだと、問題があるので、コピーして、「BeepUdpServer.elf」という名前にします。
root@edison:~# cp /sketch/sketch.elf /sketch/BeepUdpServer.elf
次は設定ファイルを作ります。
root@edison:~# vi /etc/systemd/system/BeepUdpServer.service
[Unit]
Description=BeepUdpServerService
[Service]
ExecStart=/sketch/BeepUdpServer.elf /dev/pts/0
[Install]
WantedBy=multi-user.target
「status」でステータスを確認します。
root@edison:~# systemctl status BeepUdpServer
● BeepUdpServer.service - BeepUdpServerService
Loaded: loaded (/etc/systemd/system/BeepUdpServer.service; disabled)
Active: inactive (dead)
ロードされたので、次は「enable」してOS起動時に自動で起動するようにします。
root@edison:~# systemctl enable BeepUdpServer
ln -s '/etc/systemd/system/BeepUdpServer.service' '/etc/systemd/system/multi-user.target.wants/BeepUdpServer.service'
※自動起動を止める時はsystemctl disable BeepUdpServer
OSの再起動または「start」で起動させます。
root@edison:~# systemctl start BeepUdpServer
※止める時はsystemctl stop BeepUdpServer
「status」でステータスを確認します。
root@edison:~# systemctl status BeepUdpServer
● BeepUdpServer.service - BeepUdpServerService
Loaded: loaded (/etc/systemd/system/BeepUdpServer.service; enabled)
Active: active (running) since Thu 2015-10-22 09:16:32 UTC; 1min 34s ago
Main PID: 355 (BeepUdpServer.e)
CGroup: /system.slice/BeepUdpServer.service
└─355 /sketch/BeepUdpServer.elf /dev/pts/0
Oct 22 09:16:32 edison systemd[1]: Started BeepUdpServerService.
これでサービスを追加できました。またプロセスが増えると負荷が増えて行くので、必要なくなったサービスはdisableまたはdisableしてから設定ファイルを削除します。
What will you make?
2015/09/12
「3つのスケッチサンプルを活用(応用)したオリジナル試作機の開発」に成功しました。初心者なので、ちゃんとできるのか不安でしたが、思いのほかすんなりと進み、楽しく開発できました。現在はいろいろ研究しつつ、次の試作機を開発中です。
2015/10/12
「気象観測器的な試作機の開発」に成功しました。さらに機能を追加したいところですが、他にも作りたいものがたくさんあるので、これは一旦終了して、次の試作機を開発します。
パッケージと内容物
Edison キット for Arduinoのパッケージと内容物です。
バルク品のような箱に帯が付いています。簡易パッケージというより、こういうデザインなのかな。
開けるとプラスチックのケースにはいったEdisonモジュールが固定されています。
内容物一覧
Edisonモジュール、とても小さいです。
Arduino 拡張ボード、Edisonモジュールと比べると大きいけど、普通に小さいです。
Getting Started
WindowsのPC(VAIO T14)で開発を行うので「インテル® Edison キット For Arduinoをはじめよう(windows) Edison Lab (エジソン ラボ)」を見ながら組み立てや初期設定を行いました。
まずは組み立てです。EdisonモジュールをArduino拡張ボードに固定します。4つのスペーサー(足)を固定します。
拡張ボードのマイクロスイッチがmicro USBポート側にあることを確認してから、Micro USBケーブル2本でPCと接続します。
接続してしばらくすると、WindowsがEdisonを認識してデバイスとドライブに「Edison」が追加されます。
次はソフトウェアのインストールです。
インストーラーが更新されていて、上記のガイドとは違うところがありましたが、問題なく行えました。※更新で上記のガイドより簡単になりました。
インストールが終わったので、「Step 4 – Blinkサンプルを使ったLED点滅テスト」を行おうと思って、Arduino IDEを起動しましたがポートの認識に問題があるようで、ポートの設定ができなかったので、USBを接続しなおしました。接続しなおしたらポートの設定ができるようになったので、問題なく「Blink」が実行できました。
次は「Step 5 – EdisonをWi-Fiにつなぐ」を行ってみました。ガイドとは設定の順番が違いましたが、指示にあわせて設定を行いました。
ブラウザでEdisonのページを表示できました。
すべて問題なく終わりました。スケッチが正常に動作、WiFiに接続できたので、初期不良とかはなさそうです。
インテルのインストーラーでインストールしたArduino IDEは1.6.4でしたが、スイッチサイエンスの「Make it with Edison」トライアルキット by Edison Labの冊子の「Arduino IDEについて」という項目で最新版の事が書かれていたので、Arduino IDEはhttps://www.arduino.cc/en/Main/Softwareで最新の1.6.5をダウンロードして、インストールしました。
更新履歴
2015/09/16「Intel® XDK IoT Editionを試す」を追記
2015/10/09「1-2. スケッチ2: Serial Display」を更新
2015/10/09「1-3. スケッチ3: Wifi Web Server」を更新
2015/10/09「1-4. オリジナル試作機: Color String Web Server」を更新
2015/10/10「2. センサーの使い方を学習して、気象観測器的な試作機を開発する」を追記
2015/10/10「2-1. 温度センサーを使った試作機: TempSensor」を追記
2015/10/10「2-1. 温度センサーを使った試作機: TempSensor」を更新
2015/10/10「2-2. 温湿度・気圧センサモジュールでI2Cを使った試作機: MeteorologicalSensor」を追記
2015/10/11「2-2. 温湿度・気圧センサモジュールでI2Cを使った試作機: MeteorologicalSensor」を更新
2015/10/11「2-2. 温湿度・気圧センサモジュールでI2Cを使った試作機: MeteorologicalSensor」を更新
2015/10/12「2-3. 取得した値をタイムスタンプ付きでSDカードに保存する試作機: MeteorologicalLogger」を追記
2015/10/12「2-3. 取得した値をタイムスタンプ付きでSDカードに保存する試作機: MeteorologicalLogger」を更新
2015/10/12「2-3. 取得した値をタイムスタンプ付きでSDカードに保存する試作機: MeteorologicalLogger」を更新
2015/10/16「EdisonでのArduinoについて」を追記
2015/10/16「3. USB関係の機能を学習して、USB機器を使った試作機を開発する」を追記
2015/10/16「3-1. USBヘッドセットを接続して、サウンドを再生、録音する」を追記
2015/10/16「Arduino拡張ボードのUSBについて」を追記
2015/10/16「3-1. USBヘッドセットを接続して、サウンドを再生、録音する」を更新
2015/10/17「Arduino拡張ボードのUSBについて」を更新
2015/10/17「3-1. USBヘッドセットを接続して、サウンドを再生、録音する」を更新
2015/10/18「Arduino拡張ボードのUSBについて」を更新
2015/10/20「カーネルビルドに挑戦する」を追記
2015/10/20「3-2. USBキーボードを接続して、入力を確認する」を追記
2015/10/21「Edisonをバッテリーで起動する」を追記
2015/10/22「3-2-2. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする」を追記
2015/10/22「3-2-2. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする」を更新
2015/10/23「3-2-2. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする」を更新
2015/10/23「Eclipse + Intel(R) IoT Developer Kitを試す」を追記
2015/10/23「Edisonをバッテリーで起動する」を更新
2015/10/23「3-2-2. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする」を更新
2015/10/23「Edisonのサービスを追加する」を追記
2015/10/23「4. Bluetooth関係の機能を学習して、Bluetooth機器を使った試作機を開発する」を追記
2015/10/23「4-1. EdisonにBluetoothでスピーカーとAndroidを接続して、Androidの音声出力をBluetoothスピーカーから出力する」を追記
2015/10/24「4-2. Bluetoothキーボードを接続して入力を行う」を追記
2015/10/24「4-1. Androidタブレットの音声出力をEdison経由でBluetoothスピーカーから出力する」を更新
2015/10/24「4-2. Bluetoothキーボードを接続して入力を行う」を更新
2015/10/24「Edisonのサービスを追加する」を更新
2015/10/26「カーネルビルドに挑戦する」を更新
2015/10/26「カスタムビルドに挑戦する」を追記
2015/10/27「3-3. USBでPS3のコントローラを接続して、入力を確認する」を追記
2015/10/27「4-3. BluetoothでPS3のコントローラを接続して、入力を確認する」を追記
2015/10/27「3-3. USBでPS3のコントローラを接続して、入力を確認する」を更新
2015/10/27「4-3. BluetoothでPS3のコントローラを接続して、入力を確認する」を更新
2015/10/28「3-3. USBでPS3のコントローラを接続して、入力を確認する」を更新
2015/10/28「4-3. BluetoothでPS3のコントローラを接続して、入力を確認する」を更新
2015/10/29「5. サーボの使い方を学習して、サーボを使った試作機を開発する」を追記
2015/10/29「5-1. ポテンショメータを使ってサーボを操作する」を追記
2015/10/29「5-2. PS3のコントローラを使ってサーボを操作する」を追記
2015/10/29「3-1. USBヘッドセットを接続して、サウンドを再生、録音する」を更新
2015/10/30「5-1. ポテンショメータを使ってサーボを操作する」を更新
2015/10/30「5-2. PS3のコントローラを使ってサーボを操作する」を更新
2015/10/30「5-1. ポテンショメータを使ってサーボを操作する」を更新
2015/11/02「3-2-2. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする」を移動
2015/11/02「6. 圧電スピーカーの使い方を学習して、圧電スピーカーを使った試作機を開発する」を追記
2015/11/02「6-1. tone関数を使って、メロディーを奏でる」を追記
2015/11/02「6-2. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする」を更新
2015/11/02「6-3. mraaのPWMでtone関数と同じような関数を作る」を追記
2015/11/02「6-4. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする その2」を追記
2015/11/05「7. 赤外線通信を学習して、赤外線通信を使った試作機を開発する」を追記
2015/11/05「7-1. 赤外線LEDを使って、TVのチャンネルを変える」を追記
2015/11/05「6-2. 圧電スピーカーを使って、USBキーボードを鍵盤楽器にする」を更新
2015/11/06「7-2. 赤外線受光モジュールと赤外線LEDでEdisonを学習リモコンにする」を追記
2015/11/06「7-2. 赤外線受光モジュールと赤外線LEDでEdisonを学習リモコンにする」を更新
2015/11/08「7-3. Eclipse + mraa_pwmで7-1と同じ機能のプログラムを書く」を追記
jakeさん
2015/10/12
シールドがいるのかと思ってました。
ソースコードのSSIDとパスワードは削除した方がいいかもしれません。
万が一がありますんで。
n-eさん
2015/10/12
全文のところを修正するのにArduino IDEから全文コピペして、SSIDとパスワードを元に戻すのを忘れていました。