[实验] Sun SPOT无线通讯专题
| 发表时间:2008-06-11 21:13:14 | 评论:1 |
一、基本概念
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之间的端口为系统保留端口,请不要在您的应用程序当中使用这些端口。目前已经被系统使用的保留端口包括下面这些:
六、编程练习
在 飞行器中通常需要监测其三轴的加速度、机舱温度和亮度等指标。譬如说,加速度超出预定的范围,则表示飞行器的运动状态可能出现了问题(例如失速)。机舱的 温度过高,可能是由于某些部件过热引起。假设我们的Sun SPOT安装在某型号的小型飞行器上,与一组同型号的小型飞行器编队飞行,这些飞行器之间通过Sun SPOT提供的无线数据链进行通讯。在正常情况下,这些飞行器以每五秒一次的频率报告自己的身份以及OK信号。在加速度、温度或者是亮度超过某个范围的情 况下,以每秒一次的频率向其他的飞行器发送SOS信号以及自身的加速度、温度和亮度数据。
七、参考资料
RadiostreamConnection
RadiogramConnection
Datagram
DataInputStream
DataOutputStream
一切为了奥运
| 发表时间:2008-06-11 02:18:41 | 评论:3 |
这只是一个普通的包裹,里面一共有10套Sun SPOT设备。我们打算用这些Sun SPOT来开发一些演示程序。清华大学的许斌老师计划在这个暑假的小学期当中让学生们基于Sun SPOT做一些项目,到时候也会给他提供一些。我在美国的同事5 月28日的时候就从三藩通过联邦快递(FedEx)将包裹寄出,6 月2 日的时候包裹到达北京。我已经四次给联邦快递打电话,两次以传真的方式向联邦快递提供了盖了公司公章的文件说明此批设备的用途。 但是,到目前为止,这个包裹的状态仍然是“向有关国家机构申报过程中”。
今天,我有一些样品需要寄往国外。叫了DHL的过来,被告知不能够邮寄 -- 为了奥运安全的缘故。 如果我一定要邮寄的话,需要向国家某某单位(记不清楚是什么单位了)申请,并且在快递单上附上该单位盖章的许可文件。
一切为了奥运。咱也只好忍了 -- 快递送不出去算啥,有些地方连健身房都给关了呢。
挪威的森林
| 发表时间:2008-06-10 18:39:04 | 评论:2 |
“所以,有时我环顾世人就气不打一处来 -- 这些家伙为什么不知道努力呢?不努力何必还牢骚满腹呢?”
我惊讶地看着永泽的脸:“在我的印象中,世上的人也都在辛辛苦苦拼死拼活地忙个没完,莫不是我看错了?”
“那不是努力,只是劳动。”永泽断然说道,“我所说的努力与这截然不同。所谓努力,指的是主动而有目的的活动。”
“举例说,就是在职业确定之后其他人无不只顾庆幸的时间里开始学习西班牙语 -- 是这样的吧。”
-- 《挪威的森林》 ,村上春树(著),林少华(译)
读到这一段的时候,不由自主地在计算机上敲了下来。等回过头来再看的时候,觉得颇为吃惊。不行,我怎么可以跟永泽这样的人持有同样的立场呢!在《挪威的森林》中,永泽并不是一个讨人喜欢的角色,在作者的潜意识中,也许是打算拿来做反面教材的吧。不过再仔细想想,虽然永泽非常的招读者讨厌,他的这番话还是很有道理的。可见我们经常称为坏人的人,还是有其值得学习的地方。
坦率地说,很多人喜欢《挪威的森林》中细腻的心理描写和性描写。而我则只喜欢上面这几句。
大漠孤烟直,长河落日圆
| 发表时间:2008-06-09 18:05:00 | 评论:1 |
是的,这就是万众瞩目的国家大剧院。
对于一个外行来说,评估一座公共建筑物,最好的方法是考察其进出口和洗手间。下面我们就从这两个方面来看一看国家大剧院。
国家大剧院一共只有南北两个进出口,并且都位于水下。南边的进出口专门留给工作人员和演出单位,平时禁止公众出入,好几千名观众只能够一起去挤唯一可以进出的北门。在大剧院内部,无法找到紧急逃生示意图,也很难找到紧急出口指示灯。如果发生火灾或者是其他事故,无法想象会是什么样的情形。
在北面的进出口观众排队入场的位置,仅在通向地下停车场的方向有一个洗手间。男宾的洗手间只有两个站位,我们傍晚七点到那里的时候,有差不多二十多个人在那里排队。我们顺着楼梯往下走了一层,又发现一个洗手间,竟然是男女共用的,只有一个位置,没有厕纸,卫生状况实在是让人无法恭维。通过安检进入国家大剧院内部,洗手间的数目和可用位置都大大增加,并且也提供了厕纸。问题在于洗手间大都位于比较隐蔽的地方,走廊的外墙上也没有明显的引导标志。譬如说,在歌剧场旁边的洗手间看起来象是一间清洁工堆放工具的储藏室,不过在其内墙上贴着一个通往洗手间的指示牌,并不明显。顺着这个指示牌下一个楼梯,穿过一个大厅,再下另外一个楼梯,终于找到洗手间。男洗手间里面设有十个小便池,看起来数量不少。问题在于,戏剧场中间有一个十分钟的中场休息,这个时候大部分的观众都出去上洗手间 -- 下一场开始十分钟之后,仍然有不少观众陆续从洗手间回来。
作为一个表演和展示人文艺术的场所,国家大剧院所缺少的,恰恰是对人 -- 对生命 -- 的尊重和关怀。
Eee PC第一印象
| 发表时间:2008-06-05 17:38:24 | 评论:1 |
昨天下午,带着Eee PC 900去开会。事先已经完全充电过,开会的时候显示亮度比较高,开着WIFI,启动的应用程序包括FireFox, Thunderbird, OpenOffice,此外就没有什么耗电的了。会议从3 点开始,4 点结束,结果Eee PC同时宣告电力耗尽。也就是说,这个盒子只能够用一个小时的时间,和之前宣传的2.5到3 个小时有较大的差距。
另外一个不尽人意的地方,Eee PC的终端模拟器的中文显示太差,很多命令的提示信息显示成方块了。此外,这个终端模拟器还没有滚动条,但是用鼠标滑轮可以回滚,真是很奇怪的一种设计。
在Eee PC上安装了jdk 1.5.0_07, ant 1.7.0, NetBeans, Sun SPOT SDK,能够正常工作。缺省安装的文本编辑器竟然有pico,正好是我的最爱。不过没有gedit,还是不够方便,因为在pico下面(也就是在终端模拟器里)处理中文比较困难。
过些时间找个HP 2133来看看。
中山公园竹林文化拾遗
| 发表时间:2008-06-01 05:47:29 | 评论:0 |
不是所有竹林都有文化的,因为文化需要人的参与。因此,公园中的竹林,便比深山老林中的竹林多了一点文化的气息。并且,即使是在公园里,文化遗产总是出现在那些离人行道比较近的竹子上,而不是出现在地处幽静的竹子上 -- 尽管人行道旁边的竹子通常有栅栏的层层防护。因为,文化创作的目的之一就是要让更多的人看到。如果没有人来看,创作本身似乎就失去了意义。
今天与咪咪还有清扬婉兮一同去故宫旁边的中山公园,顺便拍摄了一些竹林文化。在此与各位朋友分享。
概览。总的来说,大部分竹林创作主题单一,缺乏创意。到此一游类作品占大多数,然后是歌颂爱情,祝福生日。
歌颂爱情,是竹林文化永恒的主题。然而,由于创作的条件比较困难(一方面在于空间的限制,一方面还要防止管理员的干预),因此创作的质量比较低。
尽管如此,我还是能够找到一些稍微有些创意的作品。
生日快乐。
一生平安。
这是最让我感动的一幅作品。不知道作者的父亲是否能够亲眼看到或者是听到自己儿子/女儿的心声。 虽然毫无根据,但是我仍然暗自想像,这个作品应该是出自一个女孩子之手。
中山公园竹林文化的最佳创意奖,我建议授予上面这个作品 -- 老公股票天天涨。这个作品所处的方位比较高,我得踮起脚尖来才能够将其全部拍下来,可见作者在创作的时候还是花费了一番心思的。
你说神睡觉吗?
| 发表时间:2008-05-27 16:37:47 | 评论:1 |
昨天晚上九点,早早的将清扬婉兮洗白白,放到她的小床上,关了灯又关了房门。没过多久,那边屋子便没了动静,我想她大概是已经睡着了吧,于是开始看《挪威的森林》。大概十点多的时候,小人突然轻声的叫爸爸,我想她可能想要喝水,于是到她的屋子去看看。结果,人家一点睡意都没有,一双眼睛瞪着蚊帐骨碌碌的转。看见我进来,小人轻声的问:“爸爸,你说神睡觉吗?”
这个问题我还真的没有想过,只好据实回答说:“这个我也不知道,可能也睡觉吧。你看宙斯不也要睡觉吗?”
小人还不满足,又问:“那不想长大的神是不是就不睡觉了呢?象Artemis?”
Artemis小时候是否睡觉,我可是一点头绪头没有。不过,Aremis后来肯定是长大了,这一点是毫无疑问的。于是我回答说:“Artemis后来都长大了嘛,我想她可能也睡觉的。”
小人引经据典打算拒绝睡觉的计划被我打败,说:“就是说我也应该睡觉咯?”
“对啊对啊。好好睡觉才能够......”
“够着高高杠子,才能够翻跟头。”
“对。好好睡吧。”
“爸爸,我爱你。我睡了。”
“我们也爱你。好好睡吧。”
我很庆幸小人没有问Eros是否睡觉 -- 这个问题回答起来要更难些。
[实验] 访问Sun SPOT上的传感器
| 发表时间:2008-05-26 17:47:34 | 评论:0 |
一、加速度计
图一、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应用
| 发表时间:2008-05-24 04:58:16 | 评论:0 |
一、预备内容
首先,您需要安装JDK、Apache Ant、NetBeans以及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做什么事情。
[讲义]无线传感器网络技术 -- 介绍
| 发表时间:2008-05-22 17:52:02 | 评论:1 |
这是昨天晚上在北京交通大学软件学院做的一场关于无线传感器网络技术的介绍性讲座。 这个讲座简单介绍无线传感器网络的基本概念以及发展历史,包括传感器、传感器网络、无线传感器网络。通过一些实际的或者是实验性的案例,说明传感器网络和无线传感器 网络目前的应用状况和将来的应用前景。 在讲座的后半部分,将介绍PC-104, UCB Mote和Sun SPOT等现有的无线传感器网络平台,并且利用Sun SPOT做一些演示。讲座的目的,是让学生了解这个新兴技术,培养对这个技术方向的兴趣。愿意在这个方向发展的学生,可以加入我们的无线传感器网络项目小 组,在Sun SPOT上面自己做一些项目。
演讲文稿:
OpenOffice格式:01_WSN_Introduction.odp
Microsoft Office格式:01_WSN_Introduction.ppt
由于原始的演示文稿是利用OpenOffice(其实是Mac OS上的NeoOffice)制作的,所以OpenOffice格式的文稿格式会好一点,建议各位使用这个版本。
演示录像:
无人驾驶飞行器一:气球
无人驾驶飞行器二:直升飞机
| 更晚内容 | 更早内容 |
云与清风常拥有,
冰雪知音世难求。
击节纵歌相对笑,
案上诗书杯中酒。
2020年12月31日
洛杉矶
最新评论
2026-07-23 11:37:04
qyjohn 评论了 《几句翻译》
2026-07-21 20:11:28
wells 评论了 《几句翻译》
2025-11-15 06:29:51
qyjohn 评论了 《青桔》
2025-11-14 01:55:04
wells 评论了 《青桔》
2025-10-24 18:04:06
Ted Wang 评论了 《关于作者(About Me)》
2025-03-27 08:28:09
Max 评论了 《父亲走了》
2025-03-08 04:13:52
W 评论了 《我不想颂扬普京大帝》
2024-07-22 16:02:03
云思 评论了 《无题》
2024-06-27 10:01:54
yiming 评论了 《致访客(Welcome)》
2024-05-06 08:50:06
qyjohn 评论了 《致访客(Welcome)》
2024-05-03 19:49:00
jimmie 评论了 《致访客(Welcome)》
2024-02-19 03:32:57
林小静 评论了 《埋剑渡》
2024-01-05 03:54:28
路过者 评论了 《2023年度盘点》
2024-01-04 10:22:05
qyjohn 评论了 《致访客(Welcome)》
2024-01-04 08:05:13
qyjohn 评论了 《2023年度盘点》