Go Down

Topic: Controlling Automower (Read 41299 times) previous topic - next topic

Dawiinci

Thanks. So with readBytes I can use two arguments?
I think with available >= 5 it should limit it to 5 bytes already, right?

I removed the delay. But is there a timeout if the response isn't immediate?
Should I move the reading to the loop?
BTW. There won't be any data except after sending 5 bytes.

pylon

Quote
So with readBytes I can use two arguments?


Yes.

Quote
I think with available >= 5 it should limit it to 5 bytes already, right?


You don't need that because readBytes() already waits until either the number of bytes were received or a configurable timeout was reached.

Quote
But is there a timeout if the response isn't immediate?


If you use readBytes() there may be one. See http://arduino.cc/en/Reference/Stream

Quote
Should I move the reading to the loop?


Please explain in more detail.

Quote
There won't be any data except after sending 5 bytes.


What does that mean?

Dawiinci

Thanks this is really helpful.

So I have a standard Timeout of 1000 ms which I can even extend by using altSerial.setTimeout(2000). The idea with the loop was to get data even after a timeout but because I can change it this became obsolete.

To clarify the last cryptic sentence:
I have to send exactly 5 bytes to get exactly 5 bytes back - no control bytes or something similar
If I don't send these 5 bytes there is nothing to read.
This means after timeout the received bytes are reset. This is helpful if only four bytes would be read. In this case the next status report will be a fresh 5 byte package.

But maybe I'm thinking too complex with this one. ;)

Again thanks a lot, there might be some questions when I can use the hardware to test it.

Dawiinci

I finally tested it on the real hardware. It works to send commands, but I don't get the right feedback.

The status codes should look like this:
   6, 12, 18, 1000, etc.
Unfortunately I don't know how the hex code looks like and of course my script doesn't match these and gives unknown status.
If I emulate the response 0xFF 0xFF returns me -1.

Also the return of the command which should be the same as the command doesn't match.
Code: [Select]
if(memcmp(statusAutomower, commandAutomower, 5) == 0)
{
  client.print("done");
}
else
{
  client.print("failed");
}


I think there is something wrong with my conversion of the response to a number.

Code: [Select]
int statusInt = statusAutomower[3] << 8 | statusAutomower[4];
       client.print(statusInt);

Dawiinci

This might help (from the Automower project):

Code: [Select]
#Ermitteln, ob es sich um einen Read oder Write Befehl handelt
if {([string range $hexin 2 2] == "8" || [string range $hexin 2 2] == "9" ||[string range $hexin 2 2] == "A" ||[string range $hexin 2 2] == "B" ||[string range $hexin 2 2] == "C" ||[string range $hexin 2 2] == "D" ||[string range $hexin 2 2] == "E" ||[string range $hexin 2 2] == "F")} {
#Write: Prüfen, ob Byte 1-5 (Byte 4 und 5 vertauscht) der Rückgabe vom Automower mit den Input-Bytes übereinstimmen
if {([string range $hexout 0 5] == [string range $hexin 0 5]) && ([string range $hexout 8 9] == [string range $hexin 6 7]) && ([string range $hexout 6 7] == [string range $hexin 8 9])} {
#Byte 4 und 4 tauschen und als Dezimalwert zurückgeben
set final_result [expr 0x[concat [string range $hexout 8 9][string range $hexout 6 7]]]
} else {
set result_string "#[clock format [clock sec] -format %H:%M:%S]:Write-Error /$command"
if {$debug_mode != 0} {Write_Logfile "[clock format [clock sec] -format %H:%M:%S]: Write-Error (IN= $hexin / OUT= $hexout)" "a"}
}
} else {
#Read: Prüfen, ob Byte 1-3 der Rückgabe vom Automower mit den Input-Bytes übereinstimmen
if {[string range $hexout 0 5] == [string range $hexin 0 5]} {
#Byte 4 und 4 tauschen und als Dezimalwert zurückgeben
set final_result [expr 0x[concat [string range $hexout 8 9][string range $hexout 6 7]]]
} else {
set result_string "#[clock format [clock sec] -format %H:%M:%S]:Read-Error /$command"
if {$debug_mode != 0} {Write_Logfile "[clock format [clock sec] -format %H:%M:%S]: Read-Error (IN=$hexin / OUT=$hexout)" "a"}
}
}

I just need to do this with Arduino.

pylon

I would start by returning the hex codes of the mower's answer to the Yun client:

Code: [Select]
for (uint8_t e = 0; e < 5; e++) {
  client.print(statusAutomower[e], HEX);
  client.print(" ");
}


Post that output.

The basic code you posted seems to do something different than your code, so at least that very short excerpt doesn't help a lot.

Dawiinci

Thanks for the hints. I managed to get the status correctly.

However after some time (typically hours) I get weird messages:

Instead of 56 I get 14392
14392 is 0x3838 which looks a bit like the command itself:
  uint8_t timeAutomower[5] = { 0x0F, 0x00, 0x38, 0x00, 0x00 };

I assume it takes the wrong bytes, maybe the Automower repeated himself or missed to send a byte. Therefore the read 5 bytes doesn't start with an 0x0F anymore. I have no idea just guessing.

How can I implement something that checks if the start bit is correct?

After I reload the sketch over wifi it works again but this is not an optimal way. Can the Arduino can restart itself with a command?

pylon

Quote
However after some time (typically hours) I get weird messages:


Sounds like you're running out of memory slowly. Get rid of the usage of the String class. Although the worst memory leaking bug was fixed in version 1.0.5 of the IDE it still makes excessive use of dynamic memory allocation which fragments the memory and at some point it cannot allocate enough memory. At that point things get weird and unpredictable. Convert the complete string handling to C strings (character arrays).

Also use the F() macro for all constant string calls to print() and println() methods.

Quote
How can I implement something that checks if the start bit is correct?


You can parse the message and react on each byte instead of reading 5 bytes and checking for complete combinations. Although if the mower is only sending data if you sent a command before, this shouldn't be necessary.

Dawiinci

#23
May 28, 2014, 03:04 pm Last Edit: May 28, 2014, 06:56 pm by Dawiinci Reason: 1
Makes sense.

Do you have  examples? I don't understand what you mean.

Code: [Select]
   int messwert = statusAutomower[4] << 8 | statusAutomower[3];

Code: [Select]
  String command = client.readStringUntil('/');
 
Code: [Select]
client.print("Stop");
What do I have to replace with what?

Thanks

pylon

Code: [Select]
client.print("Stop");

gets

Code: [Select]
client.print(F("Stop"));

and

Code: [Select]
String command = client.readStringUntil('/');

maybe written as

Code: [Select]
#define BUFFERSIZE 40
uint8_t buffer[BUFFERSIZE];
uint8_t i = 0;
while (i < BUFFERSIZE) {
  if (client.available()) {
    uint8_t c = client.read();
    if (c == '/') break;
    buffer[i++] = c;
  }
}


you can then use strncmp() to compare the string to a fixed String and there is also a strncmp_P() version to hold the compare string in flash only and save some RAM. This code misses a timeout handling and other error handling (what if buffer overflows?).

Dawiinci

Thanks a lot. I replaced the print with F(). The other command was used in the bridge example and I use it in another sketch as well without issues. Now I have to test it.

Do you expect issues withe the compare functions I use?

Dawiinci

Unfortunately still having the same issue.

Status returns 14392 instead of 56.

Since   
Code: [Select]
String command = client.readStringUntil('/');
is also in other scripts that work, I have no clue what to do now. If I reprogram the Arduino with the same sketch it works normally for some time. Probably a simple restart would fix this issue.

Btw. is there a code way to restart the sketch? (at least this will keep the device working until I found the error)

Dawiinci

Today it happened again, at the same time I didn't have network connection to the device and after this I had this weird feedback. So it might be the cause or a side effect of this. Maybe the cause is even alt.Serial...?

pylon

Quote
The other command was used in the bridge example and I use it in another sketch as well without issues.


That was the case with the print() calls without the F() macro too. Just because it works for some time doesn't mean that it cannot be responsible for the problems after running a while. You're using the String class and that class fragments the available memory after some time. Once there isn't enough memory to fulfill the next allocation request, the program starts to react unpredictably.

Quote
Status returns 14392 instead of 56.


Provide the whole 5 bytes of output (best is to post it in hexadecimal format) not just a single part of it.

Also please post the current code you're working with, I guess you changed some stuff in the meantime.

Quote
Btw. is there a code way to restart the sketch?


Sure there is, but you should get the code to run reliably and not to restart it every other second.

Quote
Do you expect issues withe the compare functions I use?


Do you mean the "memcmp()" function? No, that works very well.

Dawiinci

#29
Jun 11, 2014, 07:56 pm Last Edit: Jun 12, 2014, 06:26 pm by Dawiinci Reason: 1
Thanks a lot, I attached my latest code (except the changes described in this post).
This worked for many days but failed suddenly like before. So I think you are right and I have to change those parts.

I understand to replace this line
Code: [Select]
String command = client.readStringUntil('/');

with this code:
Code: [Select]
#define BUFFERSIZE 40
uint8_t buffer[BUFFERSIZE];
uint8_t i = 0;
while (i < BUFFERSIZE) {
 if (client.available()) {
   uint8_t c = client.read();
   if (c == '/') break;
   buffer[i++] = c;
 }
}


Then I changed this:
Code: [Select]
 if (command == "digital") {

to:
Code: [Select]
if (strcmp (buffer, "digital") == 0)



But this doesn't compile anymore.

Go Up