Category: 无线传感器网络

无线传感器网络的应用范围问题

By , 2009年7月29日 3:43 下午

前些时间在几个地方做了几次关于Sun SPOT的讲座。经常遇到的一个问题就是无线通讯是否可靠的问题,根据我个人的理解,尝试着解答一下吧。

不管是有线通讯还是无线通讯,其基本原理都是一样的。原始信息在发射机端经过调制之后通过某种信号传递介质发送给接收机,接收机端通过解调得到原始信息。无线通讯和有线通讯的根本区别,就在于其信号传递介质。在有线通讯中,这个信号传递介质通常是某种电传导介质(或者是光传导介质),在这种介质上所传递的信号是电信号(或者是光信号)。在无线通讯中,这个信号传递介质通常是大气,在这种介质上所传递的信号是电磁波。从通讯理论上来讲,只要这两种信号传递介质都是导通的,那么其可靠度水平是相当的。说有线通讯比无线通讯更加可靠,其实是觉得看得见摸得着的线缆比看不见摸不着的空气更加实在。再往远处扯一扯,则是“虚”和“实”的本质问题,是可以做一篇哲学论文的。

任何通讯介质,都有其固有的弱点。有线介质不但怕虫吃鼠咬,更怕民工拿锄头乱挖;无线介质虽然怕障碍物阻挡,但是至少不会被虫子咬断。 所以,无线和有线,是各自有其应用环境的。在合适的应用环境里,就能够充分体现出其优点来。在不合适的应用环境里,理论上说得再好也没用。

无线传感器网络之所以会在未来有广阔的前景,在于它很好地解决了最后一公里,最后一百米,最后十米,或者是最后一米的问题。举个例子说,我国最近几年建设的高速公路,基本上都有光缆覆盖,其带宽足以支撑实时的视频监控应用。但是,高速公路沿途的各种摄像头和传感器是不能够直接接入光缆的,因为每在光缆上接入一个设备,就需要接入一对昂贵的光栅机。通常的做法,是将光缆作为骨干网,每隔一定的距离部署一对光栅机作为主节点,主节点附近的各种设备通过其他方式组成局部子网进行通讯,局部子网上的各种设备将主节点作为数据池(Data Sink),数据池上的数据通过主节点并入骨干网,并最终传输到远程数据采集、分析、控制终端。如上所述之最后N 米问题,用有线的解决方案往往是不太方便的,譬如说在已经通车的高速公路周边部署新的传感器,就不能够频繁地考虑将高速公路挖开铺设新的线缆这种可能性。

无线传感器网络的另外一个应用范围,是不便搭设有线通讯设备的环境。譬如说隧道施工现场和地下矿井矿山,施工现场的复杂性,以及传感器的数量级,使得搭设可靠的有线通讯环境非常困难。尽管无线通讯确实会由于施工现场中的种种障碍受到干扰,但是由于我们能够轻易地将无线传感器节点部署到施工现场的各个角落,从而构建起一个全面覆盖的无线通讯网络。在施工现场发生变化的时候,我们还能够轻易地通过调整无线传感器节点的物理位置来适应施工现场所发生的各种变化。这样的灵活性,是有线网络所远远不能够相比拟的。

无线传感器网络开发教程(基于Sun SPOT)

By , 2009年7月15日 4:56 下午

这些教程是我在过去两年中逐步整理出来的。在这里跟大家分享一下,希望能够对也在作无线传感器网络的朋友有用。其中参考了很多其他老师和研究人员的胶片,就不一一致谢了。

由于水平有限,肯定有很多问题和错误,希望各位不要见怪。

下载到的文件是OpenOffice.Org格式的演示文件,建议使用OpenOffice.Org打开浏览(不过据说比较新版本的Microsoft Office也支持OpenOffice.Org的文件格式了 )。

01 无线传感器网络技术介绍

02 Sun SPOT介绍

03 Sun SPOT开发环境

04 Sun SPOT开发初步

05 Sun SPOT数据处理

06 Sun SPOT无线通讯

07 Sun SPOT基站应用

08 Sun SPOT数据存储

09 Sun SPOT外部接口

利用Sun SPOT驱动直流步进电机

By , 2009年6月27日 8:23 下午

这两天在做一个无线传感器网络方面的培训,顺便贴一下如何利用Sun SPOT来驱动一个简单的直流步进电机。我所使用的步进电机型号为HS-85MG,市面上很容易买到的。接线图如上所示,关键是电机电源的负极要和Sun SPOT的GND相连接,不然的话会有一些小小的问题。

程序也很简单,稍微解说一下:

// 获得传感器板的实例
EDemoBoard db = EDemoBoard.getInstance();
// 获得传感器板上的各个引针
IOutputPin[] pin = db.getOutputPins();
// 创建一个伺服电机驱动,指定H0为脉冲信号输出
Servo myServo = new Servo(pin[EDemoBoard.H0]);
// HS-85MG的脉冲长度为1500微秒
myServo.setValue(1500);
// 驱动电机转动到指定位置,这个位置是一个0 到1 之间的浮点数。
// 0 表示电机转动的起始点,1 表示电机能够转动的最大范围
float position = (float) 0.5
myServo.setPosition(position);

下载源代码ServoDemo.zip

利用Sun SPOT驱动一个简单的直流马达

By , 2008年11月12日 4:36 下午

这是一个最简单的直流马达,从废旧电脑上拆下来的,原来是处理器上的散热风扇。 只有两条引线,红的是电源正极,黑的是电源负极,正常工作电压为12伏。在这个简单的示范程序中,我们将使用Sun SPOT上面的5 伏电源为它供电。这虽然有点勉强,但是还可以工作。

接线说明:

(1)将Sun SPOT上面的5 伏引脚连接到Sun SPOT上面的VH引脚
(2)将Sun SPOT上面的地线引脚与马达的电源负极相连接
(3)将Sun SPOT上面的H0引脚与马达的电源正极相连接

程序片段:

IOutputPin[] pin = EDemoBoard.getInstance().getOutputPins();
ITriColorLED [] leds = EDemoBoard.getInstance().getLEDs();
leds[0].setRGB(100,0,0);                // set color to moderate red

boolean running = true;
while (running) {
    leds[0].setOn();                    // Blink LED
    pin[EDemoBoard.H0].setLow();
    Utils.sleep(5000);                   // wait 5 seconds
    leds[0].setOff();
    pin[EDemoBoard.H0].setHigh();
    Utils.sleep(5000);                  // wait 5 seconds
}

如上程序片断控制LED 0和直流马达交替运转5 秒钟。由于电压比较低的原因,马达的运转有点慢,有些时候需要晃动一下才能够转起来,不过程序是可以正确运行的。由此可见,利用Sun SPOT去控制一个简单的直流马达是多么的简单。

如果要解决马达工作不畅的问题,可以考虑采用一个工作电压为5 伏左右的直流马达,接线和程序都不需要做任何改动。也可以考虑用一个12伏的外部直流电源, 程序不需要做任何改动,但是接线需要修改如下:

(1)将直流电源的正极与Sun SPOT上面的VH引脚相连接
(2)将直流电源的负极与马达的电源负极相连接
(3)将Sun SPOT上面的H0引脚与马达的电源正极相连接

过几天再写一个稍微复杂一点的马达的驱动。

在OpenSolaris 2008.05上安装SunSPOT开发工具

By , 2008年10月7日 11:05 上午

在OpenSolaris 2008.05上安装SunSPOT SDK的关键是要使用JDK 5.0,而不要使用最新版本的JDK例如6.0甚至是7.0。我按照下面的步骤进行安装,经过测试可行。

1 下载JDK 5.0。下载地址是http://java.sun.com/javase/downloads/index_jdk5.jsp, 选择JDK 5.0 Update 16的Solaris x86版本下载,得到的文件是jdk-1_5_0_16-solaris-i586.sh(假设文件下载在您的home目录)。

2 安装JDK 5.0。

# chmod 0777 jdk-1_5_0_16-solaris-i586.sh
# ./jdk-1_5_0_16-solaris-i586.sh
# export PATH=~/jdk1.5.0_16/bin:$PATH

3 安装ant并将其放入PATH中。

4 安装Sun SPOT SDK,只需要安装SDK部分和Demo部分即可。NetBeans及其插件可以选择安装或者不安装。JDK无需再次安装。

就这么简单。

[实验] 在Sun SPOT上存取数据

By , 2008年6月28日 8:04 下午

一、预备知识

在一个典型的Sun SPOT应用中,目标端(移动端)Sun SPOT通常并不是实时地将所有采集到的传感器通过无线数据链发送给基站。以一个大地磁场监测的无线传感器网络应用为例,传感器采集到的数据可以分成如下几类:

1 与平时的监控数据没有什么差异,不值得保存或者是汇报的,当场丢弃处理;

2 与平时的监控数据有一些细微的差别,值得进一步研究,但是并不需要紧急汇报和响应,则存储到该节点自身的存储器上,等到数据存储到一定的量级后再一次性传输到基站上。所有数据都转移到基站上之后,将节点上的数据删除以释放存储空间。

3 与平时的监控书具有较大的差别,需要紧急汇报和响应,则通过无线数据链马上向基站报告异常情况,并且提高数据采集和汇报的频率。

Sun SPOT采用了Java ME中常见的Record Management Store (RMS)框架来管理应用程序的数据。RMS是一个简单的基于纪录的持久层框架,简单地讲,可以将一个Record Store想象成一个文件,将一条记录看成是一个文件里面的一行。RMS通过记录号(可以想象为行号)来定位和存取相关的纪录。在同一个MIDlet应用 程序中,可以创建和访问多个Record Store。

二、示范程序

在一个Sun SPOT应用中,可以通过如下方法来创建和访问Record Store。

导入相关类库:

import javax.microedition.rms.RecordStore;

import javax.microedition.rms.RecordStoreException;

创建一个RecordStore的实例rms,打开一个名为TEST的Record Store。如果该Record Store尚未存在,则创建这个Record Store。

RecordStore rms = RecordStore.openRecordStore(“TEST”, true);

创建一个数组inputData,这个数组是需要写入Record Store的数据:

byte[] inputData = new byte[]{12,13,14,15,16};

将这个数组作为一个记录写入Record Store,同时返回与这个数组相关的记录号recordId:

int recordId = rms.addRecord(inputData, 0, inputData.length);

创建一个数组outputData,将与指定记录号相对应的记录读入该数组:

byte[] outputData = rms.getRecord(recordId);

关闭该RecordStore:

rms.closeRecordStore();

下载示范程序RecordStoreDemo.zip

三、编程练习

编写一个Sun SPOT应用。目标端Sun SPOT以10 Hz的频率采集加速度传感器的信息,并且保存在Sun SPOT上。数据采集的时间为1 分钟,数据采集结束之后通过无线数据链将存储的数据发送给基站端,并且删除保存在目标端Sun SPOT的数据。

四、参考资料

RecordStore

[实验] 开发Sun SPOT基站应用

By , 2008年6月26日 5:35 上午

一、预备知识

在一个典型的Sun SPOT应用中,基站的作用是与远程的Sun SPOT进行通讯,接收远程Sun SPOT发送回来的信息,或者是向远程Sun SPOT发送指令以控制其行为。如下图所示,Host是一台通过USB数据线与一台作为基站(base station)的Sun SPOT相连接的计算机,基站又通过无线电与作为通讯目标(target)的Sun SPOT进行通讯。在基站与目标之间采用的通讯协议为IEEE 802.15.4。

任意一台Sun SPOT设备都可以作为基站使用。通常来说,大部分的Sun SPOT都被设置为非基站模式,因此您需要使用如下命令将其设置为基站模式:

ant selectbasestation

非常重要:当您执行了ant selectbasestation命令之后,您需要按一下该Sun SPOT上的控制按钮(在两个状态指示LED之间)。Sun SPOT在重新启动之后就会进入基站模式。

一 个完整的Sun SPOT应用通常包括两个部分,一个目标端(移动端)应用,另外一个是基站端(主机端)应用。目标端应用是MIDlet应用程序,它运行在作为通讯目标的 Sun SPOT上。基站端应用是一个Java SE应用程序,它运行在主机上(而不是作为基站的Sun SPOT上)。但是,基站端的Java SE应用程序可以通过Sun SPOT SDK中提供的类库访问作为基站的Sun SPOT,因此能够利用基站的无线数据链与目标端进行通讯。

用如下命令编译一个基站端应用:

ant host-compile

用如下命令运行一个基站端应用:

ant host-run

二、基站应用的基本框架

下面我们用一个简单的基站应用来介绍其基本框架:

/**
* 定义包名
*/

package org.sunspotworld.demo;

/**
* 导入必要的类库
*/

import com.sun.spot.peripheral.*;
import com.sun.spot.peripheral.radio.*;
import com.sun.spot.io.j2me.radio.*;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.IEEEAddress;
import java.io.*;
import javax.microedition.io.*;

/**
* 开始定义一个类,这个类就是一个简单的Sun SPOT基站应用
*/

public class SunSpotHostApplication {

/**
* run() 方法是这个简单的Sun SPOT基站应用需要完成的任务。
* 它获得本机的IEEE扩展Mac地址,并且将其打印出来。
*/
public void run()
{

/**
* 获得本机的IEEE扩展Mac地址
*/
IEEEAddress ourAddr = new IEEEAddress(Spot.getInstance().getRadioPolicyManager().getIEEEAddress());

/**
* 打印本机的IEEE扩展Mac地址
*/
System.out.println(“Our radio address = ” + ourAddr.asDottedHex());

/**
* 退出(执行完毕)
*/
System.exit(0);
}

/**
* main()方法是每一个Java SE应用都必须声明的方法。
* 它是一个Java SE应用的入口。
*/
public static void main(String[] args) throws Exception
{

/**
* 创建一个自身的事例
*/
SunSpotHostApplication app = new SunSpotHostApplication();

/**
* 调用run()方法
*/
app.run();
}

}

三、基站和目标之间的通讯

在上面的示范程序中,我们注意到基站应用可以获得基站的IEEE扩展Mac地址。这就意味着我们可以利用我们在Sun SPOT无线通讯专题中介绍过的几种通讯方法(例如radiostream, radiogram)来与目标Sun SPOT进行通讯。

下面是一个经过改进的Sun SPOT应用,该应用监听100端口接收到的所有数据并且将其打印到屏幕上:

/**
* 定义包名
*/

package org.sunspotworld.demo;

/**
* 导入必要的类库
*/

import com.sun.spot.peripheral.*;
import com.sun.spot.peripheral.radio.*;
import com.sun.spot.io.j2me.radio.*;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.IEEEAddress;
import java.io.*;
import javax.microedition.io.*;

/**
* 开始定义一个类,这个类就是一个简单的Sun SPOT基站应用
*/

public class SunSpotHostApplication {

/**
* run() 方法是这个范例Sun SPOT基站应用需要完成的任务。
* 它监听并打印100端口接收到的所有数据。
*/
public void run() {
/**
* 获得本机的IEEE扩展Mac地址
*/
IEEEAddress ourAddr = new IEEEAddress(Spot.getInstance().getRadioPolicyManager().getIEEEAddress());

/**
* 打印本机的IEEE扩展Mac地址
*/
System.out.println(“Our radio address = ” + ourAddr.asDottedHex());

/**
* 定义一些必要的类
* RadiogramConnection conn,使用RadiogramConnection进行连接
* Datagram dg_receive,发送过来的数据包
* String input_message, 数据包中提取出来数据
*/
RadiogramConnection conn;
Datagram dg_send, dg_receive;
String input_message;

try
{
/**
* 打开100端口,进行监听
*/
conn = (RadiogramConnection) Connector.open(“radiogram://:100”);

/**
* 进入一个无线循环
*/
while (true)
{
/**
* 构造一个新的Datagram对象,用来接收数据
*/
dg_receive = conn.newDatagram(conn.getMaximumLength());
/**
* 等待接收数据包
*/
conn.receive(dg_receive);
/**
* 接收到数据包之后,从中提取出文本信息
*/
input_message = dg_receive.readUTF();
/**
* 将文本信息打印出来,进入下一个循环。
*/
System.out.println(input_message);
}
} catch (IOException e)
{
}

}

/**
* main()方法是每一个Java SE应用都必须声明的方法。
* 它是一个Java SE应用的入口。
*/
public static void main(String[] args) throws Exception
{

/**
* 创建一个自身的事例
*/
SunSpotHostApplication app = new SunSpotHostApplication();

/**
* 调用run()方法
*/
app.run();
}

}

在目标端,我们可以写一个简单的应用,该应用以一定的频率采集XYZ三轴上的加速度以及其倾斜角,并且通过100端口将这些数据广播出去。

package org.sunspotworld;
import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ITemperatureInput;
import com.sun.spot.sensorboard.peripheral.IAccelerometer3D;
import com.sun.spot.sensorboard.peripheral.ITriColorLED;
import com.sun.spot.sensorboard.peripheral.ISwitch;
import com.sun.spot.sensorboard.peripheral.ILightSensor;
import com.sun.spot.sensorboard.peripheral.LEDColor;

import com.sun.spot.peripheral.NoRouteException;
import com.sun.spot.peripheral.radio.IRadioPolicyManager;
import com.sun.spot.io.j2me.radiostream.*;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.*;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet {

/**
* 声明各种传感器,今后会用到
*/
private ISwitch sw1 = EDemoBoard.getInstance().getSwitches()[0];
private ISwitch sw2 = EDemoBoard.getInstance().getSwitches()[1];
private ITriColorLED leds[] = EDemoBoard.getInstance().getLEDs();
private ILightSensor lightSensor = EDemoBoard.getInstance().getLightSensor();
private ITemperatureInput tempSensor = EDemoBoard.getInstance().getADCTemperature();
private IAccelerometer3D accel = EDemoBoard.getInstance().getAccelerometer();

/**
* starApp()方法启动一个Sun SPOT应用程序。
*/
protected void startApp() throws MIDletStateChangeException {

/**
* 启动一个线程,检测USB电缆是否被连接上。
*/
new BootloaderListener().start();

/**
* 获得本机的IEEE扩展Mac地址。
*/
IEEEAddress ourAddr = new IEEEAddress(Spot.getInstance().getRadioPolicyManager().getIEEEAddress());

/**
* 打印本机的IEEE扩展Mac地址。
*/
System.out.println(“Our radio address = ” + ourAddr.asDottedHex());

/**
* 声明程序中用到的对象
* RadiogramConnection — 通讯连接
* dg_send — 发送出去的数据包
* i — 计数器
*/
RadiogramConnection conn;
Datagram dg_send;
int i = 0;

try
{

/**
* 创建广播。
* 100是通讯双方约定的端口号。
*/
conn = (RadiogramConnection) Connector.open(“radiogram://broadcast:100”);

/*
* 声明必要的变量
* accel_X, accel_Y, accel_Z是XYZ三轴上的加速度
* tilt_X, tilt_Y, tilt_Z是XYZ三轴的倾斜角
* message是要发送出去的信息
*/
double accel_X, accel_Y, accel_Z;
double tilt_X, tilt_Y, tilt_Z;
String message;

/**
* 进入一个无限循环。
*/
while (true)
{

/**
* 获得XYZ三轴上的加速度和倾斜角
*/
accel_X = accel.getAccelX();
accel_Y = accel.getAccelY();
accel_Z = accel.getAccelZ();
tilt_X = accel.getTiltX();
tilt_Y = accel.getTiltY();
tilt_Z = accel.getTiltZ();

/**
* 构造将要发送出去的字符串
*/
message = ourAddr.asDottedHex() + “:” + accel_X + “:”+ accel_Y + “:” + accel_Z + “:” + tilt_X + “:” + tilt_Y + “:” + tilt_Z;

/**
* 创建用来发送数据的数据包。
*/
dg_send = conn.newDatagram(conn.getMaximumLength());

/**
* 将需要发送的内容写入需要发送的数据包。
*/
dg_send.writeUTF(message);

/**
* 发送数据包。
*/
conn.send(dg_send);

/**
* 休息三秒钟。进入下一个循环。
*/
Utils.sleep(3000);
}
} catch (IOException e)
{
System.out.println(“No route to the destination Sun SPOT.”);
}

}

protected void pauseApp() {
// This will never be called by the Squawk VM
}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// Only called if startApp throws any exception other than MIDletStateChangeException
}
}

四、示范程序

下载HostDemo_Broadcast.zip ,解压缩之后有两个目录。一个是HostDemo,是基站端应用程序;另外一个是SendData,是目标段应用。

首先将目标端Sun SPOT连接到计算机上,进入SendData目录,编译、部署和运行:

ant jar-app
ant jar-deploy
ant run

将目标端Sun SPOT拔除,接上基站端Sun SPOT,进入HostDemo目录:

ant selectbasestation

按一下控制按钮重新启动Sun SPOT,这时候Sun SPOT就会进入基站模式。我们接下来编译和运行这个基站端应用:

ant host-compile
ant host-run

五、编程作业

编写一个完整的Sun SPOT应用,包括基站端和目标端两个部分。基站Sun SPOT和目标Sun SPOT之间使用radiostream协议来进行通讯,从而可靠地进行数据传输。数据传输的频率为1 Hz,传输的数据为目标Sun SPOT上测量到的温度和亮度。

[实验] Sun SPOT无线通讯专题

By , 2008年6月11日 9:13 下午

一、基本概念

IEEE扩展Mac地址可以形象地比喻为Sun SPOT的IP地址。在一个Sun SPOT网络中可以用IEEE扩展Mac地址来标志一台特定的Sun SPOT,除非发生重大变化不会变成其他的Sun SPOT。

radiostream是 一种类似于套接字(socket)的peer-to-peer通讯协议。当两台Sun SPOT之间需要进行可靠的数据传输时,它们之间通过无线网络建立起一个稳定的、带缓冲的数据数据链。与电话网络相类似,这种数据链是点对点的,通讯的双 方则通过这条数据链来回传输数据。在这条稳定的数据链的基础上,radiostream通讯协议通过信息校验能够保证接收方所接收到的数据和发送方所发送的数据在内容和顺序上是完全一致的,从而实现了数据的可靠传输。

radiogram不是一种基于稳定连接的通讯协议。radiogram通讯协议将独立的数据包从一台Sun SPOT传输到另外一台Sun SPOT,但是并不保证接受方能够接收到该数据包,也不保证接收方所接收到的数据和发送方所发送的数据在内容和顺序上是完全一致的。因此,radiogram通讯协议更类似于普通邮政服务,寄信人不能够保证所寄出去的信能够被收信人及时收到,后发出的信也许会比先发出的信更早到达。

为了让多个应用程序能够共享一个无线通讯模块,一个无线通讯模块可以提供多达255 个通讯端口,不同的应用程序可以使用不同的端口进行通讯。需要注意的事,虽然这个端口号的值可以在0 到255之间,但是0 并不是一个可以正常使用的端口。

二、使用radiostream通讯协议

两台Sun SPOT之间可以通过radiostream通讯协议建立起一个稳定可靠的数据链。具体方法如下:

RadiostreamConnection conn = (RadiostreamConnection)
Connector.open(“radiostream://<destinationAddr>:<portNo>”);

其中,destinationAddr是对方的IEEE扩展Mac地址,portNo则是通讯中使用的端口号。需要注意的是,通讯双方需要使用同样的端口号。

当radiostream连接被建立起来之后,可以使用DataInputStream和DataOutputStream来接收和发送数据:

DataInputStream dis = conn.openDataInputStream();

DataOutputStream dos = conn.openDataOutputStream();

关闭一个RadioStreamConnection对象:

conn.close();

下面的程序片断演示了两台Sun SPOT之间如何通过radiostram通讯协议进行通讯:

package org.sunspotworld;

import com.sun.spot.peripheral.NoRouteException;
import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ITriColorLED;
import com.sun.spot.peripheral.radio.IRadioPolicyManager;
import com.sun.spot.io.j2me.radiostream.*;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.*;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet {

/**
* starApp()方法启动一个Sun SPOT应用程序。
*/

protected void startApp() throws MIDletStateChangeException {

/**
* 启动一个线程,检测USB电缆是否被连接上。
*/

new BootloaderListener().start();

/**
* 获得本机的IEEE扩展Mac地址。
*/

IEEEAddress ourAddr = new IEEEAddress(Spot.getInstance().getRadioPolicyManager().getIEEEAddress());

/**
* 打印本机的IEEE扩展Mac地址。
*/

System.out.println(“Our radio address = ” + ourAddr.asDottedHex());

/**
* 声明程序中用到的对象
* RadiostreamConnection — 通讯连接
* DataInputStream — 数据输出流,接收数据
* DataOutputStream — 数据输出流,发送数据
* input_message — 对方发送过来的数据
* i — 计数器
*/

RadiostreamConnection conn;
DataInputStream dis;
DataOutputStream dos;
String input_message;
int i = 0;

try
{

/**
* 创建网络连接。
* 0014.4F01.0000.081F对方的IEEE扩展Mac地址。
* 100是通讯双方约定的端口号。
*/

conn = (RadiostreamConnection) Connector.open(“radiostream://0014.4F01.0000.081F:100”);

/**
* 获得输入输出流。
*/

dis = conn.openDataInputStream();
dos = conn.openDataOutputStream();

/**
* 进入一个无限循环。
*/

while (true)
{

/**
* 将需要发送的内容写入发送缓冲区。
* 发送的内容为本机的IEEE扩展Mac地址,以及当前消息的编号。
*/

dos.writeUTF(“Sun SPOT ” + ourAddr.asDottedHex() + ” Message number ” + i + “.”);

/**
* 正式发送数据。
*/

dos.flush();

/**
* 接收一条信息。
*/

input_message = dis.readUTF();

/**
* 打印接收到的信息内容。
*/

System.out.println(input_message);

/**
* 休息3 秒钟,计数器加1,进入下一个循环。
*/

Utils.sleep(3000);
i = i + 1;
}
} catch (IOException e)
{
System.out.println(“No route to the destination Sun SPOT.”);
}

}

protected void pauseApp() {
// This will never be called by the Squawk VM
}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// Only called if startApp throws any exception other than MIDletStateChangeException
}
}

从上面的程序片断中,我们注意到如下几个要点:

DataOutputStream.writeUTF(String output_content)方法将打算发送出去的内容output_content写入发送缓冲区。

DataOutputStream.flush ()方法将发送缓冲区中的内容真正发送出去。如果一直没有执行DataOutputSteam.flush()方法,无线通讯模块会在发送缓冲区已经被用 完的时候自动将等待发送的内容发送出去,然后清空发送缓冲区。如果发送缓冲区没有被用完,并且程序没有调用DataOutputStream.flush ()方法,那么数据一直都不会被真正发送出去。

String input_content = DataInputStream.readUTF()方法阻塞当前的进程,等待对方发送的内容。当接收到一个数据包之后,这个数据包的内容被返回到字符串input_content里面。

需要注意的是,如果通讯双方无法找到对方,则会抛出异常NoRouteException。在我们的应用程序中需要捕获和处理这个异常。

三、使用radiogram通讯协议

radiogram是一种基于服务器/客户端的数据包通讯协议。

创建一个RadiogramConnection链接:

RadiogramConnection conn = (RadiogramConnection)
Connector.open(“radiogram://<serverAddr>:<portNo>”);

其中,serverAddr是服务器端的IEEE扩展Mac地址,portNo是通讯中使用的端口号。需要注意的是,通讯双方需要使用同样的端口号。

当通讯双方建立起连接之后,可以通过Datagram进行数据交换。

Datagram dg_send = conn.newDatagram(conn.getMaximumLength());

向一个Datagram对象dg写入数据:

dg_send.writeUTF(“Hello”);

通过RadiogramConnection conn将一个Datagram对象发送出去:

conn.send(dg_send);

通过RadiogramConnection conn接收一个Datagram对象:

Datagram dg_receive = conn.newDatagram(conn.getMaximumLength());
conn.receive(dg_receive);

从一个Datagram对象中读取数据:

String input_content = dg_receive.readUTF();

关闭一个RadiogramConnection对象:

conn.close();

下面的程序片断演示了两台Sun SPOT之间如何通过radiostram通讯协议进行通讯:

package org.sunspotworld;

import com.sun.spot.peripheral.NoRouteException;
import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ITriColorLED;
import com.sun.spot.peripheral.radio.IRadioPolicyManager;
import com.sun.spot.io.j2me.radiostream.*;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.*;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet {

/**
* starApp()方法启动一个Sun SPOT应用程序。
*/

protected void startApp() throws MIDletStateChangeException {

/**
* 启动一个线程,检测USB电缆是否被连接上。
*/

new BootloaderListener().start();

/**
* 获得本机的IEEE扩展Mac地址。
*/

IEEEAddress ourAddr = new IEEEAddress(Spot.getInstance().getRadioPolicyManager().getIEEEAddress());

/**
* 打印本机的IEEE扩展Mac地址。
*/

System.out.println(“Our radio address = ” + ourAddr.asDottedHex());

/**
* 声明程序中用到的对象
* RadiogramConnection — 通讯连接
* dg_send — 发送出去的数据包
* dg_receive — 接收到的数据包
* input_message — 对方发送过来的数据
* i — 计数器
*/

RadiogramConnection conn;
Datagram dg_send, dg_receive;
String input_message;
int i = 0;

try
{

/**
* 创建网络连接。
* 0014.4F01.0000.081F对方的IEEE扩展Mac地址。
* 100是通讯双方约定的端口号。
*/

conn = (RadiogramConnection) Connector.open(“radiogram://0014.4F01.0000.081F:100”);

/**
* 进入一个无限循环。
*/
while (true)
{

/**
* 创建两个空的数据包,一个用来发送数据,一个用来接收数据。
*/

dg_send = conn.newDatagram(conn.getMaximumLength());
dg_receive = conn.newDatagram(conn.getMaximumLength());

/**
* 将需要发送的内容写入需要发送的数据包。
*/
dg_send.writeUTF(“Sun SPOT ” + ourAddr.asDottedHex() + ” Message number ” + i + “.”);

/**
* 发送数据包。
*/
conn.send(dg_send);

/**
* 接收数据包。
*/
conn.receive(dg_receive);

/**
* 从数据包中提取接收到的消息。
*/
input_message = dg_receive.readUTF();

/**
* 打印接收到的消息。
*/
System.out.println(input_message);

/**
* 休息三秒钟。
*/
Utils.sleep(3000);

/**
* 计数器加一,进入下一个循环。
*/

i = i + 1;
}
} catch (IOException e)
{
System.out.println(“No route to the destination Sun SPOT.”);
}

}

protected void pauseApp() {
// This will never be called by the Squawk VM
}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// Only called if startApp throws any exception other than MIDletStateChangeException
}
}
四、使用radiogram进行广播

radiogram还可以用于向所有的Sun SPOT进行广播。为了进行广播,您首先需要在广播服务器端创建一个特殊的DatagramConnection

DatagramConnection conn =
(DatagramConnection) Connector.open(“radiogram://broadcast:<portNo>”);

其中,portNo是需要使用的广播端口。

在接收端,则需要创建一个DatagramConnection,指定其监听相对应的端口。

RadiogramConnection conn = (RadiogramConnection) Connector.open(“radiogram://:<portNo>”);

所有打开同样portNo接收数据的Sun SPOT均可以接收到从广播端发送的数据。

下面的程序片断演示了两台Sun SPOT之间如何通过radiostram通讯协议进行通讯:

数据广播端:

package org.sunspotworld;

import com.sun.spot.peripheral.NoRouteException;
import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ITriColorLED;
import com.sun.spot.peripheral.radio.IRadioPolicyManager;
import com.sun.spot.io.j2me.radiostream.*;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.*;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet {

/**
* starApp()方法启动一个Sun SPOT应用程序。
*/

protected void startApp() throws MIDletStateChangeException {

/**
* 启动一个线程,检测USB电缆是否被连接上。
*/

new BootloaderListener().start();

/**
* 获得本机的IEEE扩展Mac地址。
*/

IEEEAddress ourAddr = new IEEEAddress(Spot.getInstance().getRadioPolicyManager().getIEEEAddress());

/**
* 打印本机的IEEE扩展Mac地址。
*/

System.out.println(“Our radio address = ” + ourAddr.asDottedHex());

/**
* 声明程序中用到的对象
* RadiogramConnection — 通讯连接
* dg_send — 发送出去的数据包
* i — 计数器
*/

RadiogramConnection conn;
Datagram dg_send;
int i = 0;

try
{

/**
* 创建广播。
* 100是通讯双方约定的端口号。
*/

conn = (RadiogramConnection) Connector.open(“radiogram://broadcast:100”);

/**
* 进入一个无限循环。
*/
while (true)
{

/**
* 创建用来发送数据的数据包。
*/
dg_send = conn.newDatagram(conn.getMaximumLength());

/**
* 将需要发送的内容写入需要发送的数据包。
*/
dg_send.writeUTF(“Sun SPOT ” + ourAddr.asDottedHex() + ” Message number ” + i + “.”);

/**
* 发送数据包。
*/
conn.send(dg_send);

/**
* 休息三秒钟。
*/
Utils.sleep(3000);

/**
* 计数器加一,进入下一个循环。
*/

i = i + 1;
}
} catch (IOException e)
{
System.out.println(“No route to the destination Sun SPOT.”);
}

}

protected void pauseApp() {
// This will never be called by the Squawk VM
}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// Only called if startApp throws any exception other than MIDletStateChangeException
}
}

数据接收端:

package org.sunspotworld;

import com.sun.spot.peripheral.NoRouteException;
import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ITriColorLED;
import com.sun.spot.peripheral.radio.IRadioPolicyManager;
import com.sun.spot.io.j2me.radiostream.*;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.*;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet {

/**
* starApp()方法启动一个Sun SPOT应用程序。
*/

protected void startApp() throws MIDletStateChangeException {

/**
* 启动一个线程,检测USB电缆是否被连接上。
*/

new BootloaderListener().start();

/**
* 获得本机的IEEE扩展Mac地址。
*/

IEEEAddress ourAddr = new IEEEAddress(Spot.getInstance().getRadioPolicyManager().getIEEEAddress());

/**
* 打印本机的IEEE扩展Mac地址。
*/

System.out.println(“Our radio address = ” + ourAddr.asDottedHex());

/**
* 声明程序中用到的对象
* RadiogramConnection — 通讯连接
* dg_receive — 接收到的数据包
* input_message — 对方发送过来的数据
*/

RadiogramConnection conn;
Datagram dg_receive;
String input_message;

try
{

/**
* 创建连接。
* 100是通讯双方约定的端口号。
*/

conn = (RadiogramConnection) Connector.open(“radiogram://:100”);

/**
* 进入一个无限循环。
*/
while (true)
{

/**
* 创建用来接收数据的数据包。
*/

dg_receive = conn.newDatagram(conn.getMaximumLength());

/**
* 接收数据包。
*/
conn.receive(dg_receive);

/**
* 从数据包中提取接收到的消息。
*/
input_message = dg_receive.readUTF();

/**
* 打印接收到的消息。
*/
System.out.println(input_message);
}
} catch (IOException e)
{
System.out.println(“No route to the destination Sun SPOT.”);
}

}

protected void pauseApp() {
// This will never be called by the Squawk VM
}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// Only called if startApp throws any exception other than MIDletStateChangeException
}
}

当 一只Sun SPOT打开某个端口进行数据广播时,它还可以打开同一个端口监听来自其他Sun SPOT的广播数据,但是无法接受到来自自己的广播数据。也就是说,假如有一组Sun SPOT需要相互交换信息,那么这些Sun SPOT可以约定使用同样的端口通过广播的方式进行通讯。下面的程序演示了如何通过这个方式进行数据交换:

package org.sunspotworld;

import com.sun.spot.peripheral.NoRouteException;
import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ITriColorLED;
import com.sun.spot.peripheral.radio.IRadioPolicyManager;
import com.sun.spot.io.j2me.radiostream.*;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.*;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class StartApplication extends MIDlet {

/**
* starApp()方法启动一个Sun SPOT应用程序。
*/

protected void startApp() throws MIDletStateChangeException {

/**
* 启动一个线程,检测USB电缆是否被连接上。
*/

new BootloaderListener().start();

/**
* 获得本机的IEEE扩展Mac地址。
*/

IEEEAddress ourAddr = new IEEEAddress(Spot.getInstance().getRadioPolicyManager().getIEEEAddress());

/**
* 打印本机的IEEE扩展Mac地址。
*/

System.out.println(“Our radio address = ” + ourAddr.asDottedHex());

/**
* 声明程序中用到的对象
* RadiogramConnection — 通讯连接
* dg_send — 发送出去的数据包
* i — 计数器
*/

RadiogramConnection conn_send;
Datagram dg_send;
int i = 0;

try
{

/**
* 创建广播。
* 100是通讯双方约定的端口号。
*/

conn_send = (RadiogramConnection) Connector.open(“radiogram://broadcast:100”);

/**
* 启动一个线程来监听其他Sun SPOT发送的数据。
* startListenThread()方法中定义了一个线程,具体参见后面的实现。
*/
startListenThread();

/**
* 进入一个无限循环。
*/
while (true)
{

/**
* 创建用来发送数据的数据包。
*/
dg_send = conn_send.newDatagram(conn_send.getMaximumLength());

/**
* 将需要发送的内容写入需要发送的数据包。
*/
dg_send.writeUTF(“Sun SPOT ” + ourAddr.asDottedHex() + ” Message number ” + i + “.”);

/**
* 发送数据包。
*/
conn_send.send(dg_send);

/**
* 休息三秒钟。
*/
Utils.sleep(3000);

/**
* 计数器加一,进入下一个循环。
*/

i = i + 1;
}
} catch (IOException e)
{
System.out.println(“No route to the destination Sun SPOT.”);
}

/**
* startListenThread()定义并启动一个线程。该线程的功能是监听其他Sun SPOT所发送的信息。
*/
public void startListenThread()
{

/**
* 在Java语言中,可以通过创建一个Runnable对象来创建一个线程。
*/
Runnable r = new Runnable(){

/**
* 每个线程都需要定义一个run()方法。一个线程运行的时候,其实就是运行这个方法。
*/
public void run()
{

/**
* 调试语句。
*/
System.out.println(“Entering listen thread…”);

/**
* 该线程的主要功能,是调用listenForData()方法进行数据监听。
* 该方法在后面有具体的定义。
*/
listenForData();
}
};

/**
* 创建该线程的一个实例,并且通过start()方法启动该线程。
* 在Java语言的线程中,start()方法调用run()方法。
*/
(new Thread(r)).start();
}

/**
* 监听数据广播的方法
*/

public void listenForData()
{

/**
* 定义必要的对象。
*/
RadiogramConnection conn_receive;
Datagram dg_receive;
String input_message;

try
{

/**
* 创建数据监听连接。
*/
conn_receive = (RadiogramConnection) Connector.open(“radiogram://:100”);

/**
* 创建一个Datagram对象用来接收数据。
*/
dg_receive = conn_receive.newDatagram(conn_receive.getMaximumLength());

/**
* 进入一个无限循环。
*/
while (true)
{
/**
* 调试语句。
*/
System.out.println(“Listen for data….”);

/**
* 接收数据。
*/
conn_receive.receive(dg_receive);

/**
* 读取数据。
*/
input_message = dg_receive.readUTF();

/**
* 显示数据并进入下一个循环。
*/
System.out.println(input_message);
}
}catch (IOException e)
{
System.out.println(e.toString());
}

}

}

protected void pauseApp() {
// This will never be called by the Squawk VM
}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// Only called if startApp throws any exception other than MIDletStateChangeException
}
}


五、系统相关

编号为0-31之间的端口为系统保留端口,请不要在您的应用程序当中使用这些端口。目前已经被系统使用的保留端口包括下面这些:

5

六、编程练习

在 飞行器中通常需要监测其三轴的加速度、机舱温度和亮度等指标。譬如说,加速度超出预定的范围,则表示飞行器的运动状态可能出现了问题(例如失速)。机舱的 温度过高,可能是由于某些部件过热引起。假设我们的Sun SPOT安装在某型号的小型飞行器上,与一组同型号的小型飞行器编队飞行,这些飞行器之间通过Sun SPOT提供的无线数据链进行通讯。在正常情况下,这些飞行器以每五秒一次的频率报告自己的身份以及OK信号。在加速度、温度或者是亮度超过某个范围的情 况下,以每秒一次的频率向其他的飞行器发送SOS信号以及自身的加速度、温度和亮度数据。

七、参考资料

RadiostreamConnection
RadiogramConnection
Datagram
DataInputStream
DataOutputStream

[实验] 访问Sun SPOT上的传感器

By , 2008年5月26日 5:47 下午

一、加速度计

图一、Sun SPOT的XYZ坐标图


图二、Sun SPOT的XY坐标方向

加速度是一个矢量。在使用Sun SPOT的加速度计之前,我们需要了解该矢量所使用的坐标系统。上面两个图形介绍了Sun SPOT的坐标系统,当Sun SPOT如图一所示水平放置时,Z 轴的方向为垂直向下,其加速度值为1G。

在应用程序的开头部分,您需要import与加速度计相关的类库:

import com.sun.spot.sensorboard.peripheral.IAccelerometer3D;

在应用程序中,声明一个IAccelerometer3D对象:

private IAccelerometer3D accel = EDemoBoard.getInstance().getAccelerometer();

获得X, Y, Z三轴上的加速度值:

double accelX = accel.getAccelX();
double accelY = accel.getAccelY();
double accelZ = accel.getAccelZ();

获得X, Y, Z三轴上的倾斜度:

double tiltX = accel.getTiltX();
double tiltY = accel.getTiltY();
double tiltZ = accel.getTiltZ();

在Sun SPOT自带的传感器上,自带的加速度计型号为LIS3L02AQ。该加速度计的测量范围可以设定为2G或者是6G,在测量范围不同的时候,其测量精度也 是不同的。如果我们需要在应用程序当中得到关于加速度计的更多信息,建议您直接使用类库LIS3L02AQAccelerometer。同样,在应用程序 的开头部分,您需要import与该加速度及相关的类库:

import com.sun.spot.sensorboard.peripheral.LIS3L02AQAccelerometer;

在应用程序中访问该加速度计:

private LIS3L02AQAccelerometer acc = (LIS3L02AQAccelerometer)EDemoBoard.getInstance().getAccelerometer();

LIS3L02AQAccelerometer是IAccelerometer3D的一个子类,所以上面所介绍的获得三轴加速度和倾斜度的方法仍然可以使用。

获得加速度计的当前测量范围。如果加速度计工作在2G状态,该方法返回SCALE_2G;如果加速度计工作在6G状态,该方法范围SCALE_6G。

public int scale = acc.getCurrentScale();

设定加速度计的测量范围为2G:

acc.setScale(acc.SCALE_2G);

设定加速度计的测量范围为6G:

acc.setScale(acc.SCALE_6G);

二、光照传感器

在应用程序的开头部分,您需要import与光照传感器相关的类库:

import com.sun.spot.sensorboard.peripheral.ILightSensor;

在应用程序中,声明一个ILightSensor对象:

private ILightSensor lightSensor = EDemoBoard.getInstance().getLightSensor();

读取光照强度,其数值范围在0 到740 之间。

int lightIndication = lightSensor.getValue();

三、温度传感器

在应用程序的开头部分,您需要import与温度传感器相关的类库:

import com.sun.spot.sensorboard.io.ITemperatureInput;

在应用程序中,声明一个ITemperatureInput对象:

private ITemperatureInput tempSensor = EDemoBoard.getInstance().getADCTemperature();

读取温度数据,用华氏度表示:

double tempF = tempSensor.getFahrenheit();

读取温度数据,用摄氏度表示:

double tempC = tempSensor.getCelsius();

四、使用控制按钮

在应用程序的开头部分,您需要import与控制按钮相关的类库:

import com.sun.spot.sensorboard.peripheral.ISwitch;

在应用程序中,声明两个ISwitch对象,其中sw1为左边的控制按钮,sw2为右边的控制按钮:

private ISwitch sw1 = EDemoBoard.getInstance().getSwitches()[0];
private ISwitch sw2 = EDemoBoard.getInstance().getSwitches()[1];

控制按钮实际上是一个数字开关,它可以被设置为开启和关闭两种状态。

检查sw1是否为开启状态:

boolean status = sw1.isOpen();

检查sw2是否为关闭状态:

boolean status = sw2.isClose();

您也可以让程序等待某个控制按钮的状态发生改变,例如:

sw1.waitForChange();

waitForChange()是一个阻塞方法。也就是说,它会停止当前线程的执行,一直到该控制按钮的状态发生改变为止。

在 更多的情况下,我们可能需要知道的是某个控制按钮是否曾经被按下(作为改变系统状态的某种信号),而并不关心其当前状态是开启还是关闭。一个简单的办法是 读取该控制按钮的当前状态并且与上一次的状态进行比较,如果结果不一样则判断该按钮的状态曾经被按下(因为其状态发生了改变)。

五、综合编程练习

编 写一个Sun SPOT应用程序,用LED模拟一个在水中作单向运动的小球。该小球可以是一个密度大于水的铁球,也可以是一个密度小于水的气球,使用左边的控制按钮改变 其密度状态。该小球的颜色可以使用温度或者是亮度来控制,当温度(亮度)较高的时候,小球的颜色为红色;当温度(亮度)较低的时候,小球的颜色为绿色;过 渡颜色为蓝色。

在完成这个编程练习的时候,可以参考Demo\CodeSamples这个目录里面的例程。

[实验] 第一个Sun SPOT应用

By , 2008年5月24日 4:58 上午

一、预备内容

首先,您需要安装JDKApache AntNetBeans以及Sun SPOT Manager。JDK 和Apache Ant都是使用Java语言进行应用开发的必备工具。你需要确保JDK和Ant的bin目录都在您的PATH环境变量里面。正确的安装JDK和Ant之后,您打开一个命令行窗口,应该 可以正确运行java、javac、ant命令。

更详细的安装和配置过程,请参考这个英语版的教程:http://www.sunstudentcourses.com/course/view.php?id=12

二、第一个Sun SPOT应用

下载源代码:SunSpotApplicationTemplate.zip 并解压缩。进入解压缩之后的文件夹。

(1) 检查连接到主机上的SPOT情况,可以使用如下命令:

ant info
ant slots

(2) 编译应用程序

ant jar-app

(3) 将应用程序部署到SPOT上

ant jar-deploy

(4) 运行部署到SPOT上的应用程序

ant run

我们看到,启动应用程序之后,左边第一个LED每3 秒左右闪烁一次红光。在主机屏幕上还能够找到“Hello, world!”这个字符串 — 这是SPOT向主机发送的一个信息。

三、理解这个SPOT应用程序

package org.sunspotworld.demo;

import com.sun.spot.peripheral.Spot;
import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ITriColorLED;
import com.sun.spot.peripheral.radio.IRadioPolicyManager;
import com.sun.spot.io.j2me.radiostream.*;
import com.sun.spot.io.j2me.radiogram.*;
import com.sun.spot.util.*;

import java.io.*;
import javax.microedition.io.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class SunSpotApplication extends MIDlet {

/*
* SPOT中的虚拟机通过调用starApp方法来启动一个SPOT应用。
*/
protected void startApp() throws MIDletStateChangeException {

/*
* 标准输出为主机端
*/
System.out.println(“Hello, world”);

/*
* 监控SPOT是否通过USB连接到主机。如果是,获得从主机发送的命令。
*/
new BootloaderListener().start();

/*
* 获得自身的IEEE Mac地址
*/
IEEEAddress ourAddr = new IEEEAddress(Spot.getInstance().getRadioPolicyManager().getIEEEAddress());
System.out.println(“Our radio address = ” + ourAddr.asDottedHex());

/*
* 找到传感器板上的所有LED指示灯,用数组leds来表示
*/
ITriColorLED [] leds = EDemoBoard.getInstance().getLEDs();

/*
* 将第一个LED指示灯设置为亮度为100的红色
* 通常我们利用RGB来表示颜色,每个参数的数值范围在0到255之间。
*/
leds[0].setRGB(100,0,0);

/*
* 进入一个无限循环
*/
while (true) {

/*
* 点亮第一个LED指示灯(之前我们已经将其设置为亮度为100的红色)
*/
leds[0].setOn();

/*
* 等待1/4秒
*/
Utils.sleep(250);

/*
* 熄灭第一个LED指示灯
*/
leds[0].setOff();

/*
* 等待3 秒钟
*/
Utils.sleep(3000);
}
}

protected void pauseApp() {
// This will never be called by the Squawk VM
}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// Only called if startApp throws any exception other than MIDletStateChangeException
}
}

四、改进这个SPOT应用程序

现在,我们对这个应用程序进行一点改进。我们的目的是让第一、第二和第三个LED指示灯轮流点亮1/4秒,其颜色分别是全红,全绿和全蓝。完成一个循环过程之后,三个LED指示灯全部熄灭,等待3 秒钟,重新开始下一个循环。

修改过的应用程序片断如下:

ITriColorLED [] leds = EDemoBoard.getInstance().getLEDs();
leds[0].setRGB(255,0,0);
leds[1].setRGB(0,255,0);
leds[2].setRGB(0,0,255);

while (true) {
leds[0].setOn();
Utils.sleep(250);
leds[0].setOff();
leds[1].setOn();
Utils.sleep(250);
leds[1].setOff();
leds[2].setOn();
Utils.sleep(250);
leds[2].setOff();
Utils.sleep(3000);
}
我们重新编译、部署和运行这个应用程序:

ant jar-app
ant jar-deploy
ant run

你的第一个SPOT应用程序,就这么简单。我建议各位充分发挥想象力,看看能够用8 只LED做什么事情。

Panorama Theme by Themocracy