UMEHOSHI ITA TOP PAGE COMPUTER SHIEN LAB
ここで紹介したページの作品(EEPROM化作品)は、起動時にHTTPサーバーは、
他のPCなどからブラウザでロボットを制御できる仕様になっています。
ここで使うDHCP、HTTP、TCPサーバーは、「Raspberry PI Zero W」のLinuxで行っています。
起動すると、自身をWifiのアクセスポイントになります。
(アクセスポイント SSID:pizero、パスプレーズ:abcd1234)
そして、DHCPサーバーで、接続してきた相手に
192.168.100.2〜のIPアドレスを配信します。
このロボットのIPアドレスは192.168.100.1です。
PCでロボットのWifiアクセスポイントが見えて接続できるようになるまでには、
約1分20秒を要します。
http://192.168.100.1/quit.jpgの閲覧でWebサーバーが終了すると、
TCP制御サーバーが起動します。
このサーバーへPC側よりでpython umeclient2.py で接続して
'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>の
メニュー操作で制御できます。
このページでは、このPythonコードの代わりになるJavaのソースを紹介しています。
このJavaのソース(UmeClient2.java)では、外部に.umhのファイルを必要とせずに、直接EEPROMの各種制御用エントリーポイントを
メッセージ送信で実行させています。
'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>wwaaaxのメニューに対する文字列を入力した場合、
wwで0.4秒前進した後、aaaで0.6秒左回転し、
その後にxで0.2秒後退させる指示になります
| コマンド文字列 | 意味概要 |
|---|---|
| M | 次に、ロボットに送信するUME専用のメッセージ入力用プロンプトがでます。 そこで例えばR009D020D00003Bの入力でブザーを鳴らせます。 |
| f | 次に、ロボットに送信する.umhのファイル入力用プロンプトがでます。 |
| quit | クライアントプログラム終了 |
| w | 0.2秒だけモータ前進 |
| x | 0.2秒だけモータ後進 |
| a | 0.2秒だけモータ左回転 |
| d | 0.2秒だけモータ右回転 |
| Q | モータ左デューティ幅アップ |
| Z | モータ左デューティ幅ダウン |
| W | モータ右デューティ幅アップ |
| C | モータ右デューティ幅ダウン |
| T | カメラで撮影し、その画像ファイル(serverpicture.jpg)を要求 これで、受信した画像ファイルは起動時で出現するFrameに表示されます。 |
import java.io.*; //入出力(OutputStream,InputStream)用
import java.net.Socket;//ソケット用
import java.awt.*;
import java.awt.event.*;
public class UmeClient2 extends Frame implements Runnable ,WindowListener{
static UmeClient2 instance;// 自身のオブジェクト
Socket socket;
Image img;
String imgPath = null;
public UmeClient2(Socket socket){// コンストラクタ
UmeClient2.instance = this;
addWindowListener(this); // 自分自身をリスナーとして登録
this.socket = socket;
Thread thread = new Thread( this );
thread.start();
this.setSize(720, 480);
//setImageFile("serverpicture.jpg");
this.setVisible(true);// 表示
}
public void windowOpened(WindowEvent e) { }
public void windowClosing(WindowEvent e) {
System.exit(0);//閉じる処理を追加
}
public void windowClosed(WindowEvent e) { }
public void windowIconified(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { }
public void windowActivated(WindowEvent e) { }
public void windowDeactivated(WindowEvent e) { }
public void paint(Graphics g) {// 描画処理のオーバーロード
if(this.img != null) {
g.drawImage(this.img, 0, 0, 720, 480, this);// 画像を描画
}
}
public void setImageFile(String path) {// 外部から画像ファイルを指定するメソッド
if( new File(path).exists() == false ) return;
this.img = Toolkit.getDefaultToolkit().getImage(path);
// Toolkit.getImage(...) は内部的に画像をキャッシュするため、同じパスを指定すると以前の画像が再利用される。
img.flush(); // このキャッシュをクリアして、画像が再利用されないようにする
repaint(); // 再描画を要求
System.out.printf("setImageFile(%s), img:%s\n", path, this.img.toString());
}
public static void sendFile(Socket socket, String filename) throws Exception {
File file = new File(filename); // 送信ファイルをfileで管理
if(file.exists() == false){
System.out.println(filename + "が存在しません。");
return;
}
int filesize = (int) file.length(); // 送信ファイルバイト取得
byte []bin = new byte[filesize]; // 送信に使うbyte配列に、ファイル内容を全て記憶
InputStream is = new FileInputStream(file);
for(int i=0; i < filesize; i++){ // ファイルから1byte読む
bin[i] = (byte)is.read(); // 1byteを配列に記憶する繰り返し
}
is.close();
String s = String.format("f%s %d\r\n", filename, filesize);
OutputStream os = socket.getOutputStream();// 送信用byteストリーム生成
os.write(s.getBytes("utf-8")); // ファイル名とサイズの文字列を送信
os.write(bin); // ファイル内容を全て記憶されるbyte配列を、全て送信
System.out.println( s );
}
static void sendString(Socket socket, String msg) throws Exception {// msg の文字列を送信
OutputStream os = socket.getOutputStream();
os.write(msg.getBytes("utf-8"));
}
public static void recieveFile(Socket socket) throws Exception {// 画像ファイル受信
byte []buff=new byte[128];
int count=0;
InputStream is = socket.getInputStream();
for(;;){
int c = is.read();//1byte入力
// System.out.printf("%X ",c);
if( c == -1 ) return; // 接続が閉じられたか、またはエラー
buff[count++] = (byte)c;
if( buff[count-1] == '\n' ){ // 1行の文字列受信終了?
String s = new String(buff,0,count, "utf-8");
// System.out.println(s);
String []a=s.split(" ");
String filename = a[0].trim();
int filesize = Integer.parseInt(a[1].trim());
System.out.println(filename + " size:" + filesize); // 受信ファイル名とサイズ確定
buff=new byte[filesize];
for(int i = 0;i < filesize; i++){
c = is.read();//1byte入力
buff[i] = (byte)c;
}
OutputStream os = new FileOutputStream( filename );
os.write(buff);// 受信ファイルを保存
os.close();
System.out.printf("recieve filename: %s, size=%d\n", filename, filesize);
UmeClient2.instance.imgPath = filename;
return;
}
}
}
static void receiveData(Socket socket) throws Exception {// 一つの受信処理(文字列のメッセージかバイナリファイルの受信)
InputStream is = socket.getInputStream();
try{
int c = is.read();//1byte入力
if( c == -1 ) {//ストリームの終わりに達した場合は-1
socket.close();
return;
}
if( c == 'M'){// メッセージ受信
byte buff[]= new byte[512]; // 入力
int count = 0;
while ( true ){
c = is.read();//1byte入力
buff[count++] = (byte)c;
if( c== '\n') break;
}
System.out.print(new String(buff,0,count, "utf-8"));//メッセージ表示
} else if( c == 'f'){
recieveFile(socket);
}
}
catch(Exception e){
System.out.print(e.getMessage());
}
}
public void run(){//上記の受信処理を、このrunの別スレッドが動かす。
try {
while (socket.isClosed()==false) {
receiveData(socket);// 文字列のメッセージかバイナリファイルの受信
}
}
catch(Exception e){
e.printStackTrace();
}
}
public static void main(String []a) throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String ip = "192.168.100.1";// br.readLine();
System.out.println(ip+"に59154番で接続します。");
Socket socket = new Socket( ip, 59154);
System.out.println(ip+"に59154番で接続しました。----------------------!");
UmeClient2 umeClient2 = new UmeClient2(socket);// 画像表示用Frame生成
boolean loopFlag=true;
while(loopFlag){
if(umeClient2.imgPath != null){
umeClient2.setImageFile(umeClient2.imgPath);// 受信ファイルの設定表示
umeClient2.imgPath = null;
}
System.out.print("'M'/'f'/'quit'/w/x/a/d/Q/Z/E/C/T>");// メニュー表示
String str = br.readLine();
if(str.equals("M")){
System.out.print("input message>");
String msg = br.readLine();
sendString(socket, "M"+msg+"\r\n");// ロボットへの文字列(UME専用Hexコマンド)送信
}
else if( str.startsWith("f") ) {
System.out.print("input send filename(.umh)>");
String filename = br.readLine();
sendFile(socket, filename );
}
else if( str.startsWith("quit") ) {// 終了?
loopFlag = false;
}
else if( str.startsWith("T") ) {
sendString(socket, "T\r\n");// カメラ撮影と撮影画像ファイル("serverpicture.jpg")要求
Thread.sleep(1000);
}
else while(str.length() > 0){//「UME専用Hexコマンド」の文字列送信
if(str.startsWith("b")) sendString(socket,"MR009D020D00003B\r\n");
else if(str.startsWith("w")) sendString(socket,"MR009D020200004D\r\n");//前進
else if(str.startsWith("a")) sendString(socket,"MR009D020500004A\r\n");//左回転
else if(str.startsWith("d")) sendString(socket,"MR009D020400004B\r\n");//右回転
else if(str.startsWith("x")) sendString(socket,"MR009D020300004C\r\n");//後進
else if(str.startsWith("Q")) sendString(socket,"MR009D0206000049\r\n");//モータ左デューティ幅アップ
else if(str.startsWith("Z")) sendString(socket,"MR009D0207000048\r\n");//モータ左デューティ幅ダウン
else if(str.startsWith("E")) sendString(socket,"MR009D0208000047\r\n");//モータ右デューティ幅アップ
else if(str.startsWith("C")) sendString(socket,"MR009D0209000046\r\n");//モータ右デューティ幅ダウン
else if(str.startsWith("S")) sendString(socket,"MR009D020A00003E\r\n");//モータストップ
str=str.substring(1);// 上記の処理文字を除いた次の入力文字列を取得
}
Thread.sleep(200);
}
socket.close();// 接続を終了
}
}