Roboter mit J2ME-Handy steuern
Hallo
Ich bin inzwischen stolzer Besitzer eines Samsung GT-S5620 (Monte) und versuche jetzt, das Teil "sinnvoll" zu nutzen. Für einen guten Ansatz halte ich dabei den Versuch von wolfgangI das Display des Handys als Schnittstelle zu verwenden:
https://www.roboternetz.de/phpBB2/ze...ag.php?t=52064
(Displaysensor: https://www.roboternetz.de/phpBB2/ze...g.php?p=468332)
Erstaunlich, was die ScriptKids alles können. Ich kenne Java leider überhaupt nicht. Nach zähem googlen bin ich auf einen anderen handygesteuerten Roboter gestossen:
http://www.robohobby.com/index_de.jsp
Alles gut erklärt und dokumentiert, aber ich scheitere schon bei der Installation der Java-Umgebung. Irgendwelche Referenzen stimmen nicht, das kann ja heiter werden ;)
Das sollte das Monte auch können. Mit zwei Kameras, WiFi und MIDP 2.0 scheint es mir eine taugliche Plattform zu sein. Allerdings ist der Einstieg ziemlich zäh. Ziel ist ein Websever auf dem Handy, der über eine Browseroberfläche gesteuert wird und das Bild streamen soll. Der Weg dahin ist aber sehr weit und steinig...
Ein erster kleiner Erfolg ist eine Webcamfunktion, die ich hier gefunden habe: http://www.mobile-mir.com/en/MobileWebCam.php
Das Monte sendet über WiFi in einstellbaren Intervallen einen Schnappschuss. Empfänger ist ein Serverscript auf einem Webserver:
Bild hier
Vielleicht habt ihr ein paar Tipps oder Anregungen?
Gruß
mic
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo
Weil das Projekt grad etwas durchhängt schildere ich mal, was bisher geht. Natürlich war der Grundgedanke das Stereosignal zu nutzen um beide Servo getrennt anzusteuern. Blöderweise habe ich aber kein (kostenloses) Programm gefunden mit dem man die Signalform brauchbar in Stereo generieren kann. Ich habe deshalb die Monosignale mit Audacity bearbeitet und zusammengeschnitten. Für die Funktionen vor, zurück und stop habe ich für beide Servos zusammen jeweils kurze Waves mit den wichtigsten Kombinationen erzeugt (Anhang). Testweise kann ich mit diesen Tondateien das Fahrwerk auch direkt am Kopfhörerausgang meines PCs betreiben;)
Als Java-Laie habe ich mir fürs Monte aus verschiedenen Codeschnippseln ein kleines Programm zusammengeklickt. Ausgangsbasis war das kleine PatchyMIDlet. Es zeigt, wie man quick&dirty eine Socketverbindung aufbaut und ankommende Browseranfragen auswertet und beantwortet. Im Orginalzustand "überliest" das PatchyMIDlet aber die Daten die der Browser sendet und antwortet nur mit der Empfangsquittung und einem zufällig ausgewählten String. In der Praxis gibt man die IP des Monte als URL im Browser des PCs ein und sieht dann den Satz oben im Browserfenster. Prima.
Nun zu meinen Erweiterung des PatchMIDlet. Ein Browser sendet bei einer Anfrage eine Menge Daten zum Server, unter anderem auch die aufgerufene URL. Diese lese ich aus dem Datenstrom und suche alles, was hinter http://handy-ip/ eingegeben wurde. Diesen "Anhang" an die URL (normalerweise der Pfad zur angeforderten Datei auf einem Webserver) interpretiere ich als Kommando und ordne einen Dateinamen meiner Servo-WAVs zu. Dann sende ich dem Browser eine kleine HTML-Seite mit einer kleinen Link-Tabelle. Die Links sind dabei die Handy-IP mit passendem Anhang, also z.B. handy-ip/1, handy-ip/2... Der Einfachheit halber im Moment nur einziffrige Befehle:
Code:
<html><head><title>Robotersteuerung mit J2ME-Handy</title></head><body>Kommando:
<table><tr><td>Test2</td><td>Test4</td><td>Test6</td></tr><tr><td>Test8</td><td>Test0</td><td></td></tr></table>
</body></html>
Zusätzlich habe ich einen kleinen Audioplayer ergänzt. Die mit dem Kommando ausgewählte Wav-Datei wird dann am Kopfhöhrerausgang ausgegeben und das Fahrwerk bewegt sich dem Mausklick entsprechend. Leider sind die WAVs etwas zu kurz, deshalb, und weil der Adapter noch nicht gut funktioniert, ist das Video langweilig. Ich zeige es nur, weil es eben existiert:
Bild hier
http://www.youtube.com/watch?v=kkArTA0t6cY
Die WAVs werden übrigens direkt in das Java-Programm eingebunden. Die Java-Syntax ist der von C sehr ähnlich. Das Programm dazu sieht so aus:
Code:
/*
Wireless Java 2nd edition
Jonathan Knudsen
Publisher: Apress
ISBN: 1590590775
*/
import java.io.*;
import javax.microedition.io.*;
//import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
//import javax.microedition.pki.*;
import javax.microedition.io.SocketConnection;
import javax.microedition.io.ServerSocketConnection;
//import javax.microedition.io.UDPDatagramConnection;
//import java.io.InputStream;
//import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
//import javax.microedition.lcdui.List;
import javax.microedition.media.Manager;
import javax.microedition.media.Player;
import javax.microedition.midlet.MIDlet;
public class PatchyMIDlet extends MIDlet implements CommandListener, Runnable {
private Display mDisplay;
private Form mForm;
private ServerSocketConnection mServerSocketConnection;
private boolean mTrucking = true;
private String line;
public void startApp() {
mDisplay = Display.getDisplay(this);
if (mForm == null) {
mForm = new Form("PatchyMIDlet");
mForm.addCommand(new Command("Exit", Command.EXIT, 0));
mForm.setCommandListener(this);
}
Thread t = new Thread(this);
t.start();
mDisplay.setCurrent(mForm);
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) { shutdown(); }
private void log(String text) { log(null, text); }
private void log(String label, String text) {
StringItem si = new StringItem(label, text);
//si.setLayout(Item.LAYOUT_NEWLINE_AFTER);
mForm.append(si);
}
private void shutdown() {
mTrucking = false;
try { mServerSocketConnection.close(); }
catch (IOException ioe) {}
}
public void commandAction(Command c, Displayable s) {
if (c.getCommandType() == Command.EXIT) {
shutdown();
notifyDestroyed();
}
}
public void run() {
try {
mServerSocketConnection = (ServerSocketConnection)
Connector.open("socket://:80");
log("Startup complete.");
SocketConnection sc = null;
while (mTrucking) {
sc = (SocketConnection)
mServerSocketConnection.acceptAndOpen();
log("client: ", sc.getAddress());
//log("Test: ", sc.getLocalAddress());
// Strictly speaking, each client connection
// should be handled in its own thread. For
// simplicity, this implementation handles
// client connections inline.
Reader in = new InputStreamReader(
sc.openInputStream());
String dummy_line;
String erste_line="";
String Kommando;
while ((dummy_line = readLine(in)) != null) if(erste_line.equals("")) erste_line=dummy_line;
// Ignoring the request, send a response.
Kommando=erste_line.substring(5,6);
PrintStream out = new PrintStream(sc.openOutputStream());
out.print("HTTP/1.1 200 OK\r\n\r\n");
//out.print(getMessage());
out.print("<html><head><title>Robotersteuerung mit J2ME-Handy</title>");
out.print("</head><body>");
out.print("Kommando: ");
out.print(Kommando);
log(Kommando);
out.print("
<table><tr><td><a href=1>Test1</a></td>");
out.print("<td><a href=2>Test2</a></td>");
out.print("<td><a href=3>Test3</a></td></tr>");
out.print("<tr><td><a href=4>Test4</a></td>");
out.print("<td><a href=5>Test5</a></td>");
out.print("<td><a href=6>Test6</a></td></tr>");
out.print("<tr><td><a href=7>Test7</a></td>");
out.print("<td><a href=8>Test8</a></td>");
out.print("<td><a href=9>Test9</a></td></tr>");
out.print("<tr><td></td><td><a href=0>Test0</a></td><td></td>");
out.print("</tr></table>
");
//out.print(erste_line);
out.print("");
out.print("");
out.print("");
out.print("");
out.print("</body></html>");
out.close();
in.close();
sc.close();
//playFromResource("/"+Kommando+".wav");
if(Kommando.equals("1")) playFromResource("/servo-4ms-r.wav");
if(Kommando.equals("2")) playFromResource("/servo-4ms.wav");
if(Kommando.equals("3")) playFromResource("/servo-4ms-l.wav");
//if(Kommando.equals("4")) playFromResource("/4.wav");
//if(Kommando.equals("5")) playFromResource("/5.wav");
//if(Kommando.equals("6")) playFromResource("/6.wav");
//if(Kommando.equals("7")) playFromResource("/7.wav");
if(Kommando.equals("8")) playFromResource("/servo-10ms.wav");
//if(Kommando.equals("9")) playFromResource("/9.wav");
if(Kommando.equals("0")) playFromResource("/1ms_lang.wav");
}
}
catch (Exception e) {
log("exception: ", e.toString());
}
}
private String readLine(Reader in) throws IOException {
// This is not efficient.
StringBuffer temp_line = new StringBuffer();
int i;
while ((i = in.read()) != -1) {
char c = (char)i;
if (c == '\n') break;
if (c == '\r') ;
else temp_line.append(c);
}
if (temp_line.length() == 0) return null;
//log(line.toString());
return temp_line.toString();
}
private java.util.Random mRandom = new java.util.Random();
private String getMessage() {
int i = Math.abs(mRandom.nextInt()) % 5;
String s = null;
switch (i) {
case 0: s = "Above all the others we'll fly"; break;
case 1: s = "There is no reason to hide"; break;
case 2: s = "I dreamed about Ray Charles last night"; break;
case 3: s = "Someone keeps moving my chair"; break;
case 4: s = "Joseph's face was black as night"; break;
default: break;
}
return s;
}
private void playFromResource(String wavedatei) {
try {
InputStream in = getClass().getResourceAsStream(wavedatei);
Player player = Manager.createPlayer(in, "audio/x-wav");
player.start();
} catch (Exception e) {
log("exception: ", e.toString());
return;
}
}
}
/*
public void paint(Graphics g) {
((Graphics2D)g).setRenderingHint
(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.green);
g.drawLine(20, 20, 40, 140);
g.setColor(Color.blue);
g.fillOval(50, 110, 120, 60);
g.setColor(Color.red);
g.setFont(new Font("Serif", Font.ITALIC, 36));
g.drawString("Cellini", 40, 80);
}
*/
Gruß
mic
Liste der Anhänge anzeigen (Anzahl: 1)
Ab einer gewissen Zoomstufe (erkennbar an den vielen kleinen Punkten in der Darstellung) kann man den Kurvenverlauf direkt mit der Maus zusammenklicken. Zuerst wird eine einzelne 20ms-Periode mit der gewünschten Impulsdauer erstellt und diese dann vervielfältigt.