I noticed with my board from DIY drones a strange behavior when I use my custom firmware.
Here is an example function which is called in my firmware running on an Arduino board:
void send_attitude(float roll, float pitch, float yaw) {
hal.console->printf("{\"type\":\"sens_attitude\",\"roll\":%.4f,\"pitch\":%.4f,\"yaw\":%.4f}\n",
roll, pitch, yaw);
}
As you can see, the code just writing a message in the serial port set in setup (hal.uartA).
I call this function every 0.5s:
inline void medium_loop() {
static int timer = 0;
int time = hal.scheduler->millis() - timer;
// send every 0.5 s
if(time > 500) {
send_attitude(OUT_PIT, OUT_ROL, OUT_YAW);
timer = hal.scheduler->millis();
}
}
Now to the strange thing. If I use the serial monitor or read the board with another program or script everything is fine. Every 0.5s the proper LED is blinking and message is shown. But if I don't read it out, after appr. 10s the LED is flushing up continuously and no connection/communication is possible anymore. I have to unplug the board then. The same behavior is observed the other way round. If I send to my board over serial port (in my case USB) and don't flush the input buffer, the LED is flushing up continuously and I get a timeout. The following code works:
def send_data(line):
# calc checksum
chk = chksum(line)
# concatenate msg and chksum
output = "%s*%x\r\n" % (line, chk)
try:
bytes = ser.write(output)
except serial.SerialTimeoutException as e:
logging.error("Write timeout on serial port '{}': {}".format(com_port, e))
# Flush input buffer, if there is still some unprocessed data left
# Otherwise the APM 2.5 control boards stucks after some command
ser.flush() # Try to send old message
ser.flushInput() # Delete what is still inside the buffer
If I comment out this line:
ser.flushInput() # Delete what is still inside the buffer
I don't use more settings then this.
I get (depending on the message interval) a timeout sooner or later. In my case I send every 20ms a signal which results in a timeout after ~10s. Also dependent on the length of message. Bigger messages cause it faster than smaller ones.
My settings are shown in the following snippets. Client side python code:
com_port = '/dev/ttyACM0'
baud_rate = '115200'
try:
ser = serial.Serial(com_port, baud_rate, timeout=0.1, writeTimeout=0.1, rtscts=1)
The if these timeouts happen, then I also get one if I set the timeout to something like 2s. In my case I need a very low latency, which is indeed possible if I keep reading and flushing. Firmware code from my Arduino:
void setup() {
// Set baud rate when connected to RPi
hal.uartA->begin(115200);
hal.console->printf("Setup device ..\n");
// Followed by motor, compass, barometer initialization
My questions are:
What exactly happens with my board?
Why it is not reacting anymore if I just write in my serial port without reading or flushing the buffer?
Is it really a buffer or driver problem associated with this strange behavior and is this problem related to all Arduino boards or maybe just mine APM 2.5 from DIY drones?
Last but not least: I was finding no functions in the library which are targeting such problems. Are there maybe any I don't know?
The complete source code is #google code: https://code.google.com/p/rpicopter/source/browse/
What board are you using and what processor does it have? My guess would be that your board is based on the ATmega32U4, or some other microcontroller that has a built-in USB module. If so, I have seen similar behavior before here is what I think is happening:
There is a buffer on your microcontroller to hold serial data going to the computer. There is a buffer in the computer's USB serial driver to hold serial received from the chip. Since you are not reading bytes from the COM port, the buffer on the computer will fill up. Once the buffer on the computer fills up, it stops requesting data from the microcontroller. Therefore, the buffer on the microcontroller will eventually fill up.
Once the microcontroller's buffer is full, how do you expect printf command to behave? For simplicity, the printf you are using is probably designed to just wait in a blocking loop until buffer space is available and then send the next character, until the message is done. Since buffer space will never be available, your program gets stuck in an infinite loop.
A better strategy would be to check to see if enough buffer space is available before calling printf. The code might look something like this:
if(console_buffer_space() > 80)
{
hal.console->printf(...);
}
I don't know if this is possible in the DIY drones firmware, and I don't know if the max buffer space can actually ever reach 80, so you will have to research this a bit.
I don't understand the use of:
ser.flush() # Try to send old message
ser.flushInput() # Delete what is still inside the buffer
Lets say your device is connected to PC and the python code is writing the (line, chk):
ser.flush() - why are you using it?
ser.flushInput() - will "delete" the Serial input buffer at the PC
It looks like other people have the same problem. And thanks to the Mod-Braniac who deleted my minimal example. My bet is, that's a problem with Arduino USB controller chip or the firmware on it.
Related
I have a device which works on serial communication. I am writing python code which will send some commands to get the data from the device.
There are three commands.
1.COMMAND - sop
Device does its internal calculation and sends below data
Response - "b'SOP,0,921,34,40,207,0,x9A\r\n'"
2.COMMAND - time
This gives a date time values which normally do not change untill the device is restarted
3.START - "\r\r" or (<cr><cr>)
This command puts the device in responsive mode after which it responds to above commands. This command is basically entering <enter> twice & only have to do once at the start.
Now the problem which I am facing is that, frequency of data received from sop command is not fixed and hence the data is received anytime. This command can also not be stopped once started, so if I run another command like time, and read the data, I do not receive time values and they are merged with the sop data sometime. Below is the code, I am using:
port = serial.Serial('/dev/ttyS0',115200) #Init serial port
port.write(("\r\r".encode())) #Sending the start command
bytesToRead = port.in_waiting #Checking data bytesize
res = port.read(bytesToRead) #Reading the data which is normally a welcome msg
port.reset_input_buffer() #Clearing the input serial buffer
port.reset_output_buffer() #Clearing the output serial buffer
port.write(("sop\r".encode())) #Sending the command sop
while True:
time.sleep(5)
bytesToRead = port.in_waiting
print(bytesToRead)
res = port.read(bytesToRead)
print(res)
port.reset_input_buffer()
port.write(("time\r".encode()))
res = port.readline()
print(res)
Using the above command I sometimes do not receive the value of time after executing its command or sometimes it is merged with the sop command. Also with the sop command, I received a lot of data during the sleep(5) out of which I need to get the latest data. If I do not include sleep(5), I miss the sop data and it is then received after executing the time command.
I was hoping if anyone can point me to right direction of how to design it in a better way. Also, I think this can easily be done using interrupt handler but I didn't found any code about pyserial interrupts. Can anyone please suggest some good code for using interrupts in pyserial.
Thanks
Instead of using time.sleep(), its preferred to use serialport.in_waiting which help to check the number of bytes available in rcv buffer.
So once there is some data is rcv buffer then only read the data using read function.
so following code sequence can be followed without having any delay
while True:
bytesToRead = port.in_waiting
print(bytesToRead)
if(bytestoRead > 0):
res = port.read(bytesToRead)
print(res)
port.reset_input_buffer()
# put some check or filter then write data on serial port
port.write(("time\r".encode()))
res = port.readline()
print(res)
I am taking a stab here: Your time.sleep(5) might be too long. Have you tried making the sleep really short, for example time.sleep(.300)? If the time data gets written back between the sop returns you will catch it before it gets merged with sop, but I am making an assumption here that it will send time data back, else there is anyway nothing more you can do on the server side (the python) code. I do believe it won't hurt to make the sleep less, it is anyway just sitting there waiting (polling) for communication.
Not having the having the same environment on my side, makes it difficult to answer, because I can't test my answer, so I hope this might help.
What I want to do is actually query Arduino to get data. When I send a command from Python to Arduino, Arduino will send back some data depending on what the query keyword is. I am able to do this but with a very low speed, I wish to have a more efficient way to do this, for example, query at least 100 times per second. Any idea? Thanks!
The code I use:
Arduino:
int temp=1;
char s[2];
int speedIn=0;
void setup() {
Serial.begin(115200);
}
void loop() {
if(Serial.available()>0){
Serial.readBytes(s,1);
speedIn=int(s[0]);
if(speedIn==49){
Serial.println(">Data1Send<");
speedIn=0;
}
else if(speedIn==50){
Serial.print(">");
Serial.print(temp);
Serial.println("<");
speedIn=0;
}
else{
Serial.println(">Wrong Data<");
speedIn=0;
}
}
if(temp<300){
temp=temp+1;
}
//delay(100);
}
Python:
import serial
import time
if __name__=="__main__":
sc = serial.Serial('/dev/tty.usbmodem1421',timeout=None,baudrate=115200)
sc.flush()
while True:
#s=raw_input()
s=str(50)
if len(s) >0:
if s.isdigit():
sc.write(chr(int(s)))
time.sleep(1.0)
if sc.inWaiting()>0:
print(sc.read(sc.inWaiting()))
If I change the time delay in Python from 1.0sec to 0.1sec, it will not work any more (no data can be received and just in waiting state).
It looks like your python code does this:
Send a command
Sleep for some time (presumably to give the arduino time to reply)
Read the reply
If no reply, repeat from step 1
You mentioned that it doesn't work if the sleep period is too short. I wonder if the following could be happening. Imagine there were no sleep period. The python code will immediately check for a reply, which may not exist because the arduino hasn't had time to respond. In that case, it will immediately try to send another command. This could keep happening repeatedly--the python code could essentially flood the line with requests that the arduino can't keep up with. You prevent this from happening using the sleep period, but the sleep period probably takes longer than the arduino actually needs, which would reduce your data rate.
If that's the case, it might work to do this in the python code instead:
Send a command
Wait for the arduino to reply (but not using sleep). Ideally, use a read command that blocks until input is received. A less preferable alternative is to busywait--enter a loop that does nothing but check whether a reply has been received and terminates when input is available.
Read the reply
Repeat from step 1
I've been trying to find a way to send serial commands from my PC (Windows 7) to an Arduino Uno R3. I've been working on this simple program, where you're supposed to send a simple "1" through the USB cable, in order to turn on the onboard LED. My Arduino code should be working (what I know of, but I'll make sure to upload it).
I've been trying to send this command using Python and pySerial, but I can't seem to get pySerial to work. Also I've tried using CMD, but it seems like CMD freezes when i type in my command (ECHO 1 > COM3 BAUD:9600).
I'm the adminstrator of my PC.
This is my arduino coding
int var = 0;
int LEDPin = 13;
int val = 0;
void setup() {
Serial.begin(9600);
pinMode(LEDPin, OUTPUT);
}
void loop() {
if (Serial.available()>0){
val = Serial.read();
}
if (val == 1){
digitalWrite(LEDPin, HIGH);
}
digitalRead(LEDPin);
if (LEDPin==HIGH){
var ++;
delay (1000);
if(var==10){
digitalWrite(LEDPin,LOW);
var = 0;
val = 0;
}
}
}
And the short Python program I've got :)
import serial
ser = serial.Serial('COM3', 9600, timeout=0)
while 1:
var = 1
ser.write(var)
delay(12000)
With Kind Regards
Michael Vedel.
just to be sure - is Arduino really connected to COM3?
In order to check serial connection manually you can use for example Putty. With help of it you can connect to the needed serial port and send command manually. And in order to check whether Arduino received it, you can add Serial.println(val); just after val = Serial.read();, with this Arduino should sends back to serial whatever characters it receives.
Does the code work when you type a '1' into the Serial Monitor in the Arduino IDE? If so, check the COM port. On my PC (also Windows 7), COM3 is never the Arduino port, as it is used internally for something else.
By the way everything after the digitalRead() statement will do nothing, since you're comparing LEDpin (which is declared as 13) to HIGH (which is defined as 1). I think you mean to see if the digitalRead output is HIGH, but I'm not sure that will work, either, since the pin is declared as an OUTPUT. You'd be much better off putting that logic in the (val == 1) section.
just save this code as .bat and make sure that you are using com 6.
This code lets you enter and sends your commands to serial port at 9600bpm
mode com6:9600,N,8,1
#echo off
:start
cls
Set /p commands="enter command:"
echo|set /p= %commands% >com6
goto start
Now you have to read serial using this in Arduino
Serial.read();
I think the root problem is in the Python code in the call to write. The argument has to be bytes (or something compatible). You're passing an integer value. You need to explicitly convert that value to a bytes object. I'm not a Python expert, but I think one way to do that is to replace:
var = 1
with
var = b'\x01'
Oh, and you might need to call flush as well.
BTW, your experiment with ECHO doesn't do what you intended. It sends the character value for '1', which is 49. Since 49 != 1, your Arduino code won't respond to it.
I find the easiest way to test serial commands is to use the Arduino Serial Monitor, which will have all the right defaults as long as you match the baud rate. This will enable you to ensure the Arduino code is right before debugging the Python code. (Then again, it may be difficult to send control characters with the Serial Monitor, so consider using printable text in your protocol. For example 'A' for on and 'B' for off. Printable text can be easy to work with among all these tools (Python, C, Serial Monitor, ECHO, etc.).)
do this it works better for me, just change the com4 for the port you have connected to
#echo off
:start
cls
set /p commands="enter command:"
mode com4 baud=115200 parity=n data=8 stop=1 to=on xon=off odsr=off octs=off dtr=off rts=off idsr=off && echo %commands% > com4
cls
goto start
I think Arduino is stable and easy enough to handle the data from serial, the cause of that as I used same library with Pyserial in python 3 was that event of reading the serial get fired once which is way faster than Arduino board and to solve that you should use a thread method that runs in the background and make it always try to read the incoming data from serial.
I've made this function that recieved the opened serial :
def read_from_port(ser):
while True:
try:
reading = ser.read().decode()
handle_data(reading)
except EXCEPTION:
print("error while decoding" + str(EXCEPTION))
after I have got the serial from the list and assigned it with the desired baud rate and send it to the Thread :
serial_me = serial.Serial(serial_port1, 115200, timeout=3)
try:
thread_x = threading.Thread(target=read_from_port, args= (serial_me,))
thread_x.daemon = True
thread_x.start()
except EXCEPTION:
print('couldn\'t start thread')
print(Exception)
I've been working on a simple XBee/Arduino/Python transfer system. Here's how it works: A hex command is sent from python over xbee, which is received by the arduino. This triggers it to take a picture using an Adafruit TTL Serial Camera. The image is then held in memory and sent over the xbee to the computer, 32 bytes at a time. The Python script then adds the bytes to a .jpg, to be viewed when it finishes.
As of now, both run fine, albeit a tad slow (~25 sec round-trip) for my needs. The problem is, switching from 57600 baud to 115200 in the xbee firmware and programs causes them to fail. They will sometimes give a .jpg roughly half the normal size, or not transmit at all, in both cases being unreadable from a viewer. I've tried changing the timeout in the python side, and modifying the xbee's interfacing options, all to no avail.
Here is the Arduino sketch (adapted largely from the Snapshot example sketch in the Adafruit VC0706 examples library):
#include <Adafruit_VC0706.h>
#include <SoftwareSerial.h>
#define chipSelect 10
SoftwareSerial cameraConnection = SoftwareSerial(2,3);
Adafruit_VC0706 cam = Adafruit_VC0706(&cameraConnection);
void setup()
{
Serial.begin(57600);
pinMode(8, OUTPUT);
if (cam.begin()){}
else { return; } //Abort the transfer if camera does not initialize
cam.setImageSize(VC0706_640x480);
}
void loop()
{
if (Serial.read() == 0x01) //Wait for send command
{
snapAndSend();
cam.reset();
}
}
void snapAndSend()
{
cam.takePicture();
uint16_t jpgLen = cam.frameLength();
while (jpgLen > 0)
{ //Send off 32 bytes of data at a time
uint8_t *buffer;
uint8_t bytesToRead = min(32, jpgLen);
buffer = cam.readPicture(bytesToRead);
Serial.write(buffer, bytesToRead);
jpgLen -= bytesToRead;
}
}
And the Python script:
import serial
import time
link = serial.Serial('COM12', 57600)
print("Engage!")
while True:
answer = input("Press enter to take a picture (type ' to exit): ")
if answer == "'":
break
file = open('imageTest.jpg', 'wb')
link.write(b"\x01")
time1 = time.time()
while True:
if link.inWaiting() > 0:
file.write(link.read())
time1 = time.time()
else:
time2 = time.time()
if (time2 - time1) > .5:
break
print ("Complete! Closing file now...")
file.close()
I'm still a bit new to serial communication and xbees, so I may be overlooking something here. Anyone more experienced have any thoughts on why a switch in baudrate breaks it?
I'm fairly certain that this is going to be related to flow control.
The XBee module can't keep up with a steady stream of data at 115200, so you need to configure it to use hardware flow control. In your case, you need to watch the CTS (clear to send) line from the XBee module, and have your program hold off on sending more data whenever it's not asserted (held low).
You can also configure the XBee to use one of its inputs for an RTS (request to send) signal -- the line the host uses to tell the XBee module when it's ready to receive data.
You may need to modify your Arduino code to receive the entire image from the camera into a buffer, and then slowly send it back out to the XBee module. If you were to check the CTS line before sending each byte, you could hold off whenever the XBee module's buffers were full. You'd need to call a function in your main loop to check the status of your buffered data, and the status of the pin to see if it was possible to send another byte.
I got a problem. I recently bought an Arduino Uno board. I tried to make something funny like controlling an input from my computer. I used Python with pySerial and the program is the following:
arduino = serial.Serial(portaCOM, 9600, timeout = 1)
... in loop ->
arduino.write(value)
def sliderUpdate(self, event):
pos = self.slider.GetValue()
arduino.write(pos)
time.sleep(.1)
print arduino.readline()
try:
arduino = serial.Serial(portaCOM, 9600, timeout = 1)
except:
print "Errore di connessione alla porta seriale"
The write value should send the value to my board though USB.
The program loaded on board is:
const int ledPin = 11;
byte brightness;
void setup(){
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
}
void loop(){
while(Serial.available()){
brightness = Serial.read();
Serial.print(brightness);
analogWrite(ledPin, brightness); //LED doesn't refresh the brightness
delay(10);
}
}
My LED is working properly. I tried with the Fade example provided by Arduino and it's working..
I checked if the program is sending properly the data. Yes, it is. It returns the same thing I sent before.
It should retrieve the value sent and set analaogWriter(pin, VALUE), but something is wrong or not working.
How can I fix this problem?
Solution
The Arduino code
const int ledPin = 11;
byte valoreLed;
void setup(){
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
}
void loop(){
while(Serial.available()){
valoreLed = Serial.read();
analogWrite(ledPin, valoreLed);
delay(20);
}
}
Python script code:
pos = self.slider.GetValue()
arduino.write(chr(pos))
Thank you to everybody!! :)
First of all, make sure your LED is properly connected. Anode (longer pin) to PWM 11 port and cathode (shorter pin) to ground, also you may need to add a resistor between cathode and ground depending on LED.
Make sure you're writing to the right port from python (that FTDI cable is associated with in your OS).
If you're not using FTDI cable with USB connector, make sure that all of the pins are connected to the right inputs.
What is the value of value in your example? Try arduino.write(chr(0xFF)), does LED stay lit?
I suspect that arduino.readline() waits for a newline, and you Arduino code never sends a newline. Hence the Python code blocks indefinitely after sending the first value.
I see in your comments that the LED works with the first character, but you don't see a change with the different characters. Definitely make sure you're sending ASCII characters that are significantly different from the previous characters, e.g. send an ASCII 0 followed by an ASCII 255.
Edit:
Since you say the FadeLED example works, can you just take that example and add serial port functionality to it? For example, only fade after a serial character is received. This will at least get you confident that the serial port and leds can coexist in code you write. Also, I assume you're using the FadeLED example from here. If so, make sure you've set the correct pin number, as the reference code uses pin 9, but your sample code uses pin 11.
Another Edit:
You say you receive the characters back properly from the Arduino, but you're using the pySerial ReadLine function, which should block until it sees a newline, and I don't see anything in the Arduino code that would generate a newline that wasn't sent to it first. To eliminate the number of variables in the question, try using HyperTerminal or a similar COM port program to send bytes to the Arduino, rather than your Python program. For example, you can send space characters (ASCII 32) and '}' characters (ASCII 125), which should be different enough to create a visible difference in the LED.
I would begin by establishing which side of the serial port harbours the problem.
First of all, can you drive the LED using a hard-coded loop on the Arduino. Does the LED change its brightness as expected?
Once that's out of the way, can you make the Arduino do something visibly correct when sending stuff manually from a terminal app (e.g. the one built into the Arduino IDE). This may require changes to the Arduino code.
Once that's working, I would then focus on the Python code, making sure you're talking to the right COM port etc.
"the write value should send the value to my board through USB"
If this is not a typo, then you can not use PySerial to access USB ports. There is pyUSB instead, and probably others.