I have tried and managed to connect my Android phone to Arduino Yun in order to control a toy car.
I use socket communication.
The problem is after I connect to the Arduino with my phone, I have 4 buttons for the direction of the car.
For example, I press to go "left" and it goes left, but when I try to press another button on the app, the serial monitor shows me I still send "left" commands.
Here is my socket part of the Arduino sketch.
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"
#include <Bridge.h>
#include <Console.h>
#include <Wire.h>
#include <YunServer.h>
#include <YunClient.h>
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *frontWheels = AFMS.getMotor(3);
Adafruit_DCMotor *rearWheels = AFMS.getMotor(4);
const int ledPin = 13;
YunServer server(5555);
void setup() {
// Bridge startup
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
Bridge.begin();
AFMS.begin();
frontWheels->setSpeed(0);
frontWheels->run(FORWARD);
frontWheels->run(RELEASE);
rearWheels->setSpeed(0);
rearWheels->run(FORWARD);
rearWheels->run(RELEASE);
server.noListenOnLocalhost();
server.begin();
Serial.begin(9600);
}
void loop() {
YunClient client = server.accept();
if (client) {
client.setTimeout(5);
Serial.println("Client connected!");
while(client.connected()){
process(client);
}
doStop(client);
client.stop();
}
else {
Serial.println("no client connected, retrying");
}
delay(1000);
}
void process(YunClient client) {
String command = client.readStringUntil('/');// Get the first element of the command.
if(command.length() > 0){
int speed = client.parseInt();// Get the second element of the command.
Serial.println((String) speed);
if (command == "forward") {
client.print(F("forward"));
Serial.println("forward");
frontWheels->setSpeed(speed);
frontWheels->run(FORWARD);
}
else if (command == "backward") {
client.print(F("backward"));
Serial.println("backward");
frontWheels->setSpeed(speed);
frontWheels->run(BACKWARD);
In the attachment is Serial monitor which says I send sockets continuously.
What can I improve in my code in order to be able to fully control the car?
Your code is incomplete, please post the whole thing. I suspect that there is something going on in the part that is missing (the braces that terminate your loop and IF statements are gone, so it's impossible to determine the logic.)
Also, what are the commands you send, how are the organized. It looks like you make a single connection, and then send a series of commands. What is the command terminator? How do you handle the end of a command in the code? (Looks like that part may be cut off?)
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"
#include <Bridge.h>
#include <Console.h>
#include <Wire.h>
#include <YunServer.h>
#include <YunClient.h>
/**
* Globals
*/
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *frontWheels = AFMS.getMotor(3);
// You can also make another motor on port M2
Adafruit_DCMotor *rearWheels = AFMS.getMotor(4);
const int ledPin = 13;
// Socket port 5555 to communicate.
YunServer server(5555);
// Internal settings - Can be changed dynamically from an external application using the "settings" action.
const char _settingHonkPin = 8;// Cannot be changed by external application.
int settingHonkNote = 440;
int settingHonkDuration = 250;// Won't be changed from external application actually.
/**
* Entry point of the program.
* Initialize everything. Called when the Arduino is powered.
*/
void setup() {
// Bridge startup
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
Bridge.begin();
AFMS.begin(); // create with the default frequency 1.6KHz
// Set the speed to start, from 70 to 255 (max speed). 0 (off)
frontWheels->setSpeed(0);
frontWheels->run(FORWARD);
frontWheels->run(RELEASE);
//'turning' MOTOR
rearWheels->setSpeed(0);
rearWheels->run(FORWARD);
rearWheels->run(RELEASE);
// Listen the entire network (socket communication)
server.noListenOnLocalhost();
server.begin();
Serial.begin(9600);
}
/**
* Has to act as an -infinite- loop.
* Contains the program logic.
* Wait for a client then process it when he's found.
*/
void loop() {
// Get clients coming from server
YunClient client = server.accept();
// There is a new client
if (client) {
// Change the predifined timeout from 2000 to 5. Avoid impressive timeout.
client.setTimeout(5);
Serial.println("Client connected!");
// When we get a client, go in the loop and exit only when the client disconnect. This will happens when the android application is killed (the socket must be closed by the app). This will automatically happens from the website for each http request.
while(client.connected()){
// Process request
process(client);
}
// Stop the car for security reasons.
doStop(client);
// Close connection and free resources.
client.stop();
}
else {
Serial.println("no client connected, retrying");
}
// Delay for the battery, for the debug too. Doesn't affect the response time of the Arduino. (Check if there is another client each second)
delay(1000);
}
/**
* Will get the command request from the socket/http/etc. entry point.
* Will parse the request and execute it.
*/
void process(YunClient client) {
// Format: COMMAND/SPEED
String command = client.readStringUntil('/');// Get the first element of the command.
// Avoid interferences when there is no request. (Because we are in an infinite loop!)
if(command.length() > 0){
//Serial.println("Query:"+client.readString());
//return;// DEBUG
// Parse the speed.
int speed = client.parseInt();// Get the second element of the command.
Serial.println((String) speed);
if (command == "forward") {
client.print(F("forward"));
Serial.println("forward");
frontWheels->setSpeed(speed);
frontWheels->run(FORWARD);
}
else if (command == "backward") {
client.print(F("backward"));
Serial.println("backward");
frontWheels->setSpeed(speed);
frontWheels->run(BACKWARD);
}
else if (command == "left") {
client.print(F("left"));
Serial.println("left");
rearWheels->setSpeed(speed);// If use speed, doesn't works. (Bad parsing)
rearWheels->run(BACKWARD);
}
else if(command == "right"){
client.print(F("right"));
Serial.println("right");
rearWheels->setSpeed(speed);// If use speed, doesn't works. (Bad parsing)
rearWheels->run(FORWARD);
}
else if(command == "stop"){
doStop(client);
}
else if(command == "stopTurn"){
client.print(F("stopTurn"));
Serial.println("stopTurn");
rearWheels->run(RELEASE);// Stop turn but don't do anything more.
}
else if(command == "photo"){
client.print(F("photo"));
Serial.println("photo");
// TODO Take a photo
}
else if(command == "honk"){
client.print(F("honk"));
Serial.println("honk");
tone(_settingHonkPin, settingHonkNote, settingHonkDuration); //(PinNumber, Note, duration)
}
else if(command == "settings"){
client.print(F("settings"));
Serial.println("settings");
// Load the custom tone.
settingHonkNote = client.parseInt();// Get the third element of the command.
}
}
}
void doStop(YunClient client){
client.print(F("stop"));
Serial.println("stop");
rearWheels->run(RELEASE);
frontWheels->run(RELEASE);
}
And here is the code from Android app which deals with sockets :
/**
* Array of strings that contains all messages to send to the server using sockets.
*/
private ArrayBlockingQueue<String> queriesQueueSocket = new ArrayBlockingQueue<String>(255);
/**
* Atomic boolean shared and accessible between different threads, used to be sure the connection is available.
*/
private AtomicBoolean stopProcessingSocket = new AtomicBoolean(false);
/**
* Contains the values to write in the socket stream.
*/
private OutputStream outputStreamSocket = null;
/**
* Socket connected to the Arduino.
*/
private Socket socket = null;
/**
* Thread which manage socket streams.
*/
private static Thread socketThread = null;
/**
* Runnable running in another thread, responsible to the communication with the car.
*/
private final Runnable networkRunnable = new Runnable() {
@Override
public void run() {
log("starting network thread");
try {
socket = new Socket(serverIpAddress, serverPort);
outputStreamSocket = socket.getOutputStream();
} catch (UnknownHostException e1) {
e1.printStackTrace();
stopProcessingSocket.set(true);
log("UnknownHostException");
} catch(final ConnectException e){// Final because we need it in the runnable.
runOnUiThread(new Runnable(){
public void run(){
// Warn the user because the connection is wrong.
Toast.makeText(context, "Unable to connect to the car, are you sure to be connected on the car network?" + e.getMessage(), Toast.LENGTH_LONG).show();
Toast.makeText(context, "Typically "+Linino.DEFAULT_NETWORK_IP+":"+Linino.DEFAULT_NETWORK_PORT+", you are connected at "+serverIpAddress+":"+serverPort, Toast.LENGTH_LONG).show();
}
});
stopProcessingSocket.set(true);
} catch (IOException e1) {
e1.printStackTrace();
stopProcessingSocket.set(true);
}
queriesQueueSocket.clear(); // we only want new values
// Initialize once the thread is running.
initializeCarSettings();
try {
while(!stopProcessingSocket.get()){
String val = queriesQueueSocket.take();
if(val != "-1"){
log("Sending value "+val);
outputStreamSocket.write((val + "\n").getBytes());
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
try {
stopProcessingSocket.set(true);
if(outputStreamSocket != null) outputStreamSocket.close();
if(socket != null) socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
log("returning from network thread");
socketThread = null;
}
};
Thanks for the updated code. On the surface, the sketch looks correct. I was looking for a situation where the loop might be processing an old command if a new one wasn't received, but I don't see that. But then again, I didn't try running the code, so I could've missed something.
In the Android code you posted, it appears that commands are coming from another module using your queriesSocket. Your code opens the network socket, and then loops to read commands from queriesSocket and send them to the network socket. Your loop has a log statement showing what it's sending: what does the log show? Is it possible that this code is sending the last command over and over? If so, you need to determine if it's a problem in this reading/sending code, or in the code writing to queriesSocket.
To help narrow this down, I would write a stripped down sketch that simply prints out everything received from the Android app. Where you loop where your client is connected, simply dump everything to the serial port, don't try to process it at this point. Something like this:
while(client.connected())
{
if (client.available())
{
char c = client.read();
Serial.print(c, HEX);
Serial.print(" ");
Serial.println(c);
}
}
This will just dump everything received over the client, as both hex and ASCII values, and will let you make sure that you are receiving what you think you should receive.
When writing code that deals with communications between two machines, it's always helpful to use a divide and conquer approach to make sure each side is working properly before trying to get both side working at once.
You can also test your sketch code with a Telnet client, by connecting to your Yun on port 5555. This will let you manually type command strings and see whether the Yun processes them properly.
I have modified the sketch in order to see what I receive from the android app and everything looks fine, but I don't know why It's not working.
Normally, I should send something like this "direction/speed" and direction can be: forward, backward, left, right, stop and speed can be between 25-250.
As you can see in the code, everything looks fine but I am not very sure what is wrong with program.
Any ideas what can I do in order to make it work?
I have also tested the DC Motor example from Adafruit library and it works fine.
66 f
6F o
72 r
77 w
61 a
72 r
64 d
2F /
33 3
30 0
A
62 b
61 a
63 c
6B k
77 w
61 a
72 r
64 d
2F /
32 2
35 5
A
62 b
61 a
63 c
6B k
77 w
61 a
72 r
64 d
2F /
32 2
36 6
A
62 b
61 a
63 c
6B k
77 w
61 a
72 r
64 d
2F /
32 2
37 7
A
62 b
61 a
63 c
6B k
77 w
61 a
72 r
64 d
2F /
32 2
38 8
A
62 b
61 a
63 c
6B k
77 w
61 a
72 r
64 d
2F /
32 2
39 9
A
62 b
61 a
63 c
6B k
77 w
61 a
72 r
64 d
2F /
33 3
30 0
A
62 b
61 a
63 c
6B k
77 w
61 a
72 r
64 d
2F /
33 3
31 1
A
62 b
61 a
63 c
6B k
77 w
61 a
72 r
64 d
2F /
33 3
32 2
A
That was a good test, it shows that the data is being sent properly, and that there isn't an issue with the Android code that causes it to send the last command over and over. So we can concentrate on the sketch side.
Unfortunately, I tried your sketch, and it appears to be working properly for me. I don't have your hardware and motor shield or its libraries, so I deleted anything to do with the actual motors. But I left the rest of your code in place, and didn't touch the way that the communications are handled. When I try it by connecting a telnet client to port 5555 and manually typing in some commands, it all seems to work as expected (or at least the way I expect it based on what you described of your protocol.)
However, there is one difference: I tried two different Telnet clients to send commands, and they all seem to send CarriageReturn/Linefeed combinations, where the hex dump of the data you posted shows that the Android app is just sending LineFeed line terminators. I don't know why that should make a difference, but it is a difference. I tried configuring the Telnet client to just send LineFeed terminators, but I was still getting CR/LF. I tried writing a simple sketch on another Yun to connect to the first one and send data, but it also is using CR/LF terminators.
Just as I hit the "Post" button, I had an idea... I modified my sketch so that instead of using client.println("forward/42") to send a command to the Yun running your sketch, I changed it to client.print("foreward/42\n") to force it to just send a LineFeed line terminator. Printing out the hex dump of the received string proves that it is now sending just the LF terminator.
Now, running your sketch with that data stream, I am also seeing the "forward" command being printed over and over again. Or, more correctly, whatever is the first command is repeated over and over until the socket is closed.
Try modifying your Android code so that it sends an explicit CR/LF for each command, rather than just LF:
I modified the Android app and still doesn't work. The results in serial monitor are the ones below:
A
66 f
6F o
72 r
77 w
61 a
72 r
64 d
2F /
31 1
30 0
34 4
D
A
66 f
6F o
72 r
77 w
61 a
72 r
64 d
2F /
31 1
30 0
35 5
D
A
66 f
6F o
72 r
77 w
61 a
72 r
64 d
2F /
31 1
30 0
36 6
D
A
66 f
6F o
72 r
77 w
61 a
72 r
64 d
2F /
31 1
30 0
37 7
D
A
What could that "D" charater mean after each command I sent via WiFi?
Also, when I upload the sketch in Arduino I see a message "warning - The use of YunClient is deprecated. Use Bridge Client instead !".
Can Yun Client library have an influence on the message I send?
Opeth:
What could that "D" charater mean after each command I sent via WiFi?
That first column of the output is the hexadecimal equivalent of each ASCII character that is received. It's handy for this characters that are not normally visible (those less than 0x20, or greater than 0x7e.)
The print statement isn't printing the "0x" prefix, nor it it printing leading zeros:
D is 0x0d, which is a carriage return character
A is 0x0a, which is a line feed character
So your change was successful, but it doesn't appear to have made a difference in your code? Curious, as it made a difference when I tried it. Just to be clear, you're running the version that prints out the individual characters just as a test? You are removing that test code and putting your original code back to test the actual program logic, right?
Also, when I upload the sketch in Arduino I see a message "warning - The use of YunClient is deprecated. Use Bridge Client instead !".
I've not noticed that message. What version of the IDE are you using? I've never heard of Bridge Client before.
Like I would press the settings button all the time .
I have also tried to change YunClient to Bridge client in the sketch but with no result. I am using Arduino 1.6.7 IDE.
Just a passing thought: is your board made by arduino.cc (this site) or arduino.org (the company that split off and went out on their own)? You can tell by looking at the silkscreen near the Ethernet connector. I'm only familiar with the Arduino.cc version, and I've heard of some software differences between the boards. Perhaps this might be another one of those differences?
Or, I'm still using IDE version 1.6.5 - perhaps it's something that broke in the latest release?
I'm sorry, but I'm out of ideas. When the line terminator is both carriage return and line feed, it works for me. I don't understand why the lime terminator should make a difference, and I really don't understand why it doesn't work for you.
If I come up with any more ideas, I'll be sure to post here. In the meantime, maybe someone else will be along with some ideas.
Finally, I have solved the situation.
I have added in the command field for backward, left, right, stopturn, photo a "\n" as you will observe in the code below. But if I add at forward command, it doesn't work smooth, so I do not add.
I dont' have an explanation for this but I'm glad it works this way.
Thanks for your help guys.
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_MS_PWMServoDriver.h"
#include <Bridge.h>
#include <Console.h>
#include <Wire.h>
#include <YunServer.h>
#include <YunClient.h>
/**
* Globals
*/
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *frontWheels = AFMS.getMotor(3);
// You can also make another motor on port M2
Adafruit_DCMotor *rearWheels = AFMS.getMotor(4);
const int ledPin = 13;
// Socket port 5555 to communicate.
YunServer server(5555);
// Internal settings - Can be changed dynamically from an external application using the "settings" action.
const char _settingHonkPin = 8;// Cannot be changed by external application.
int settingHonkNote = 440;
int settingHonkDuration = 250;// Won't be changed from external application actually.
/**
* Entry point of the program.
* Initialize everything. Called when the Arduino is powered.
*/
void setup() {
// Bridge startup
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
Bridge.begin();
AFMS.begin(); // create with the default frequency 1.6KHz
// Set the speed to start, from 70 to 255 (max speed). 0 (off)
frontWheels->setSpeed(0);
frontWheels->run(FORWARD);
frontWheels->run(RELEASE);
//'turning' MOTOR
rearWheels->setSpeed(0);
rearWheels->run(FORWARD);
rearWheels->run(RELEASE);
// Listen the entire network (socket communication)
server.noListenOnLocalhost();
server.begin();
Serial.begin(9600);
}
/**
* Has to act as an -infinite- loop.
* Contains the program logic.
* Wait for a client then process it when he's found.
*/
void loop() {
// Get clients coming from server
YunClient client = server.accept();
// There is a new client
if (client) {
// Change the predifined timeout from 2000 to 5. Avoid impressive timeout.
client.setTimeout(5);
Serial.println("Client connected!");
// When we get a client, go in the loop and exit only when the client disconnect. This will happens when the android application is killed (the socket must be closed by the app). This will automatically happens from the website for each http request.
while(client.connected()){
// Process request
process(client);
}
// Stop the car for security reasons.
doStop(client);
// Close connection and free resources.
client.stop();
}
else {
Serial.println("no client connected, retrying");
}
// Delay for the battery, for the debug too. Doesn't affect the response time of the Arduino. (Check if there is another client each second)
delay(1000);
}
/**
* Will get the command request from the socket/http/etc. entry point.
* Will parse the request and execute it.
*/
void process(YunClient client) {
// Format: COMMAND/SPEED
String command = client.readStringUntil('/');// Get the first element of the command.
// Avoid interferences when there is no request. (Because we are in an infinite loop!)
if(command.length() > 0){
//Serial.println("Query:"+client.readString());
//return;// DEBUG
// Parse the speed.
int speed = client.parseInt();// Get the second element of the command.
Serial.println((String) speed);
if (command == "forward") {
client.print(F("forward"));
Serial.println("forward");
frontWheels->setSpeed(speed);
frontWheels->run(FORWARD);
}
else if (command == "backward") {
client.print(F("backward\n"));
Serial.println("backward");
frontWheels->setSpeed(speed);
frontWheels->run(BACKWARD);
}
else if (command == "left") {
client.print(F("left\n"));
Serial.println("left");
rearWheels->setSpeed(speed);// If use speed, doesn't works. (Bad parsing)
rearWheels->run(BACKWARD);
}
else if(command == "right"){
client.print(F("right\n"));
Serial.println("right");
rearWheels->setSpeed(speed);// If use speed, doesn't works. (Bad parsing)
rearWheels->run(FORWARD);
}
else if(command == "stop"){
doStop(client);
}
else if(command == "stopTurn"){
client.print(F("stopTurn\n"));
Serial.println("stopTurn");
rearWheels->run(RELEASE);// Stop turn but don't do anything more.
}
else if(command == "photo"){
client.print(F("photo\n"));
Serial.println("photo");
// TODO Take a photo
}
else if(command == "honk"){
client.print(F("honk\n"));
Serial.println("honk");
tone(_settingHonkPin, settingHonkNote, settingHonkDuration); //(PinNumber, Note, duration)
}
else if(command == "settings"){
client.print(F("settings\n"));
Serial.println("settings");
// Load the custom tone.
settingHonkNote = client.parseInt();// Get the third element of the command.
}
}
}
void doStop(YunClient client){
client.print(F("stop\n"));
Serial.println("stop");
rearWheels->run(RELEASE);
frontWheels->run(RELEASE);
}
Now that doesn't make any sense at all! Changing the data you are sending TO the client should in no way change the data you are receiving FROM the client. Very curious...
I had the same problem.
I think I solved partially. Reading the parseInt() documentation found that "Parsing stops when no characters have been read for a configurable time-out value, or a non-digit is read"
I tried to configure timeout with Serial.setTimeout() unsuccessfully.
So what I do know is to send a string between integers and its working.