Raspberry Pi + OpenECHOでLチカ
ここ数日インフルエンザでぶっ倒れておりましたが、リハビリを兼ねて電子工作開始。
さて、自分のとりあえずの目標はRaspberry PiでOpenECHOを使えるようにすることです。その先にもう一個やりたいことがあるのだけれど、とりあえずはここまで。
前回、無事にRaspberry PiでのProcessing起動と、JavaでのGPIO制御ができるようになったので、あとはOpenECHO for Processingのライブラリを突っ込んでやるだけ。ライブラリはここからmasterのzipをとってきて、解凍した中のOpenECHO/OpenECHO for Processing/libraries/の中にある、controlP5ディレクトリとOpenECHOディレクトリを、前回のpi4jと同じように、各ユーザのsketchbook/librariesディレクトリにコピーする。今のところ、pi4jがrootになっていないとGPIO制御できないので、/root/sketchbook/libraries以下にコピーする。これで、/root/sketchbook/libraries以下には、pi4j, OpenECHO, controlP5の3つのライブラリ用ディレクトリが存在しているハズ。
続いて、Raspberry PiをOpenECHOのノードとしてECHONETに参加させるための、Processingのソースの作成。といっても、基本的に組み合わせるだけ。先ほど落としてきたOpenECHOのライブラリに含まれている、/OpenECHO/OpenECHO for Processing/libraries/OpenECHO/examples/Tutorial4_LightEmulator/Tutorial4_LightEmulator.pdeに、pi4jのLチカサンプルコードを組み合わせるだけです。
import java.io.IOException;
import processing.net.*;
import controlP5.*;
import com.sonycsl.echo.Echo;
import com.sonycsl.echo.node.EchoNode;
import com.sonycsl.echo.eoj.EchoObject;
import com.sonycsl.echo.EchoProperty;
import com.sonycsl.echo.eoj.profile.NodeProfile;
import com.sonycsl.echo.eoj.device.DeviceObject;
import com.sonycsl.echo.eoj.device.housingfacilities.GeneralLighting;
import com.sonycsl.echo.processing.defaults.DefaultNodeProfile;
import com.sonycsl.echo.processing.defaults.DefaultController;
import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;
color backgroundLightOnColor = color(255, 204, 0);
color backgroundLightOffColor = color(0, 0, 0);
color backgroundNow = backgroundLightOffColor;
GpioController gpio;
GpioPinDigitalOutput lightPin;
public class LightEmulator extends GeneralLighting{
byte[] mStatus = {0x31};
byte[] mLocation = {(byte) 0x80};
byte[] mFaultStatus = {0x42};
byte[] mManufactureCode = {0x00, 0x00, 0x00};
protected byte[] getOperationStatus() {
return mStatus;
}
protected boolean setOperationStatus(byte[] edt) {
mStatus[0] = edt[0];
if(mStatus[0] == 0x30){
backgroundNow = backgroundLightOnColor;
lightPin.setState(PinState.HIGH);
}else{
backgroundNow = backgroundLightOffColor;
lightPin.setState(PinState.LOW);
}
try{
inform().reqInformOperationStatus().send();
}catch(IOException e){
e.printStackTrace();
}
return true;
}
protected boolean setInstallationLocation(byte[] edt) {
mLocation[0] = edt[0];
try{
inform().reqInformInstallationLocation().send();
}catch(IOException e){
e.printStackTrace();
}
return true;
}
protected byte[] getInstallationLocation() {
return mLocation;
}
protected byte[] getFaultStatus() {
return mFaultStatus;
}
protected byte[] getManufacturerCode() {
return mManufactureCode;
}
}
// Use to create GUI
ControlP5 cp5;
LightEmulator lightEmu;
void setup(){
// Set display window size
size(400,400);
frameRate(30);
// Set OpenEcho
try{
lightEmu = new LightEmulator();
Echo.start(new DefaultNodeProfile(), new DeviceObject[]{lightEmu});
}catch(IOException e){
e.printStackTrace();
}
Echo.addEventListener(new Echo.Logger(System.out));
// Set pi4j
gpio = GpioFactory.getInstance();
lightPin = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_01, "MyLED", PinState.LOW);
lightPin.low();
} // End of setup()
void draw(){
background(backgroundNow);
};
ブレッドボードとLEDの配線は、pi4jのLチカサンプルコードに記載のとおり。
最後に、照明(GeneralLighting)としてECHONETに参加したRaspberry Piをコントロールするために、PCをコントローラとしてECHONETに参加させるためのProcessingのソースを作成します。これも、ひょっとしたら/OpenECHO/OpenECHO for Processing/libraries/OpenECHO/examples/Tutorial4_LightEmulator/Tutorial4_LightEmulator.pdeがそのまま使えたのかもしれませんが、前に別に作っていたものを流用して作成。
import controlP5.*;
import com.sonycsl.echo.Echo;
import com.sonycsl.echo.node.EchoNode;
import com.sonycsl.echo.eoj.EchoObject;
import com.sonycsl.echo.EchoProperty;
import com.sonycsl.echo.eoj.profile.NodeProfile;
import com.sonycsl.echo.eoj.device.DeviceObject;
import com.sonycsl.echo.eoj.device.housingfacilities.GeneralLighting;
import com.sonycsl.echo.processing.defaults.DefaultNodeProfile;
import com.sonycsl.echo.processing.defaults.DefaultController;
// Use to create GUI
ControlP5 cp5;
// Set Appliace IP Address
final String targetIp = "192.168.24.210";
GeneralLighting myLight;
static final int GENERAL_LIGHTING = 0;
String[] applianceName = {
"GeneralLighting"
};
int applianceNum = applianceName.length;
boolean applianceFound[] = {false};
String[] btFnName = {
"searchAppliances",
"lightOn",
"lightOff"
};
String[] btLabel = {
"Search Appliances",
"GeneralLighting: ON",
"GeneralLighting: OFF"
};
void setup(){
// Set display window size
size(400,400);
frameRate(30);
myDraw();
// Set GUI
textSize(12);
cp5 = new ControlP5(this);
int btFnNum = btFnName.length;
for(int i=0;i<btFnNum;i++){
/* // Processing 2.1.1
Button bt = cp5.addButton(btFnName[i])
.setCaptionLabel(btLabel[i])
.setSize(200,20)
.setPosition(0,120+30*i);
*/
// Processing 2.0.3
ControlP5 bt = new ControlP5(this);
bt.addBang(btFnName[i], 0, 120+40*i, 100, 20);
bt.controller(btFnName[i]).setLabel(btLabel[i]);
}
// Set OpenEcho
try{
Echo.start(new DefaultNodeProfile(), new DeviceObject[]{new DefaultController()});
}catch(IOException e){
e.printStackTrace();
}
Echo.addEventListener(new Echo.Logger(System.out));
Echo.addEventListener(new Echo.EventListener(){
public void onNewGeneralLighting(GeneralLighting device){
super.onNewGeneralLighting(device);
if(!device.getNode().getAddress().getHostAddress().equals(targetIp)){
println("A GeneralLighting has found, but this is NOT yours.");
return;
}
println("Your GneralLighting has Found!");
applianceFound[GENERAL_LIGHTING] = true;
myDraw();
myLight = (GeneralLighting)device;
device.setReceiver(new GeneralLighting.Receiver(){
// Set Callback to get return values
protected void onSetOperationStatus(EchoObject eoj, short tid, byte esv, EchoProperty property, boolean success){
super.onSetOperationStatus(eoj, tid, esv, property, success);
if(!success){
println("error in call reqSetOperationStatus");
println("setOperationStatus Param=" + toHexStr(property.edt));
return;
}
showMessage("Success: onSetOperationStatus");
}
protected void onGetOperationStatus(EchoObject eoj, short tid, byte esv, EchoProperty property, boolean success){
super.onGetOperationStatus(eoj, tid, esv, property, success);
if(!success){
showMessage("error in call reqGetOperationStatus");
return;
}
showMessage("OperationStatus=" + toHexStr(property.edt));
}
}); // End of setReceiver
} // End of onNewGeneralLighting
}); // End of addEventListener
// Search controllable appliances
searchAppliances();
} // End of setup()
void draw(){
};
void myDraw(){
background(0,0,0);
for(int i=0;i<applianceNum;i++){
if(applianceFound[i]){
fill(0,255,0); // Green
text(applianceName[i] + " [FOUND!]", 0, 20+20*i);
}else{
fill(255,255,255); // white
text(applianceName[i] + " [NOT FOUND]", 0, 20+20*i);
}
}
}
public void searchAppliances(){
println("----");
try{
// find other devices
NodeProfile.getG().reqGetSelfNodeInstanceListS().send();
println("Wait 3 seconds ...");
try{
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
println("----");
EchoNode[] nodes = Echo.getNodes();
for(int i=0;i<nodes.length;i++){
EchoNode en = nodes[i];
println("node id=" + en.getAddress().getHostAddress());
println("node profile=" + en.getNodeProfile());
DeviceObject[] dos = en.getDevices();
println("There are " + dos.length + " devices in this node");
for(int j=0;j<dos.length;j++){
DeviceObject d = dos[j];
String typeName = d.getClass().getSuperclass().getSimpleName();
println("device type = " + typeName);
}
println("----");
}
}catch(IOException e){
e.printStackTrace();
}
}
public void lightOn(){
println("--lightOn--");
if(applianceFound[GENERAL_LIGHTING]){
try{
myLight.set()
.reqSetOperationStatus(new byte[]{0x30}) // Light ON
.send();
}catch(IOException e){
e.printStackTrace();
}
}else{
showMessage("There are NO your controllable GeneralLightings.");
}
}
public void lightOff(){
println("--lightOff--");
if(applianceFound[GENERAL_LIGHTING]){
try{
myLight.set()
.reqSetOperationStatus(new byte[]{0x31}) // Light OFF
.send();
}catch(IOException e){
e.printStackTrace();
}
}else{
showMessage("There are NO your controllable GeneralLightings.");
}
}
String toHexStr(byte[] arg){
String ret = "" ;
for(int i=0;i<arg.length;++i){
ret += Integer.toHexString(arg[i] & 0xff) + " ";
}
return ret;
}
void showMessage(String message){
myDraw();
fill(255,255,255); // white
text(message, 0, 350);
println(message);
}
targetIpの部分は、Raspberry Piの接続環境に応じて変更してください。
さて、これで準備完了。まず、Raspberry Piの方でGeneralLightingSample.pdeを(スーパーユーザ)で実行。続いて、同じローカルネット内のPCで、LightControlSample.pdeを実行。ProcessingのGUI上に”GeneralLighting [FOUND!]”と緑色で表示されていれば、Raspberry PiをECHONETのノードとして発見しており、制御可能な状態です。白色で”GeneralLighting [NOT FOUND”と表示されている場合は、”SEARCH APPLIANCES”ボタンを押してみてください。立ち上げ時にたまたま発見できなかった場合は、これで何度か手動で探しに行くことで見つかるハズです。それぞれ無事プログラムを起動できているにも関わらず、何度やっても見つからない場合は、PCとRaspberry Piの参加しているローカルネットが異なっている可能性があります。
さて、この状態になれば、後はPCのProcessing-GUI上のON/OFFボタンで、LEDのON/OFFができるようになっている…ハズ。自分の場合は、こんな感じになりました。
わかりにくくてすみませんが、マウスでGUIのON/OFF操作をしています。
さてさて、できたものはなんだかショボく見えるかもですが、ここまでできれば、あとはやる気次第でどうとでも拡張できるハズ。

ディスカッション
ピンバック & トラックバック一覧
[…] ということで、以前一度やっていますが、改めてRaspberry Pi B+をECHONET Lite機器として動作させてみます。前回はLチカで終わってしまったので、今回はほんのちょっとだけ進化させて、電気 […]