I'm a beginner in both Arduino and Python, and I have an idea but I can't get it to work. Basically, when in Arduino a button is pressed, it sends "4" through the serial port. What I want in Python is as soon as it reads a 4, it should do something. This is what I got so far:
import serial
ser = serial.Serial('/dev/tty.usbserial-A900frF6', 9600)
var = 1
while var == 1:
if ser.inWaiting() > 0:
ser.readline(1)
print "hello"
But obviously this prints hello no matter what. What I would need is something like this:
import serial
ser = serial.Serial('/dev/tty.usbserial-A900frF6', 9600)
var = 1
while var == 1:
if ser.inWaiting() > 0:
ser.readline(1)
if last.read == "4":
print "hello"
But how can I define last.read?
I don't know a good way of synchronising the comms with readLine since it's not a blocking call. You can use ser.read(numBytes) which is a blocking call. You will need to know how many bytes Arduino is sending though to decode the byte stream correctly. Here is a simple example that reads 8 bytes and unpacks them into 2 unsigned shorts and a long (the <HHL part) in Python
try:
data = [struct.unpack('<HHL', handle.read(8)) for i in range(PACKETS_PER_TRANSMIT)]
except OSError:
self.emit(SIGNAL("connectionLost()"))
self.connected = False
Here's a reference to the struct.unpack()
The Arduino code that goes with that. It reads two analog sensor values and the micro timestamp and sends them over the serial.
unsigned int SensA, SensB;
byte out_buffer[64];
unsigned int buffer_head = 0;
unsigned int buffer_size = 64;
SensA = analogRead(SENSOR_A);
SensB = analogRead(SENSOR_B);
micr = micros();
out_buffer[buffer_head++] = (SensA & 0xFF);
out_buffer[buffer_head++] = (SensA >> 8) & 0xFF;
out_buffer[buffer_head++] = (SensB & 0xFF);
out_buffer[buffer_head++] = (SensB >> 8) & 0xFF;
out_buffer[buffer_head++] = (micr & 0xFF);
out_buffer[buffer_head++] = (micr >> 8) & 0xFF;
out_buffer[buffer_head++] = (micr >> 16) & 0xFF;
out_buffer[buffer_head++] = (micr >> 24) & 0xFF;
Serial.write(out_buffer, buffer_size);
The Arduino playground and Processing Forums are good places to look around for this sort of code as well.
UPDATE
I think I might have misled you with readLine not blocking. Either way, the above code should work. I also found this other thread on SO regarding the same subject.
UPDATE You don't need to use the analog sensors, that's just what the project I did happened to be using, you are of course free to pass what ever values over the serial. So what the Arduino code is doing is it has a buffer of type byte where the output is being stored before being sent. The sensor values and micros are then written to the buffer and the buffer sent over the serial. The (SensA & 0xFF) is a bit mask operator that takes the bit pattern of the SensA value and masks it with the bit pattern of 0xFF or 255 in decimal. Essetianlly this takes the first 8 bits from the 16 bit value of SensA which is an Arduino short. the next line does the same thing but shifts the bits right by 8 positions, thus taking the last 8 bits.
You'll need to understand bit patterns, bit masking and bit shifting for this. Then the buffer is written to the serial.
The Python code in turn does reads the bits from the serial port 8 bits at a time. Have a look at the struct.unpack docs. The for comprehension is just there to allow sending more than one set of values. Because the Arduino board and the Python code are running out of sync I added that to be able to send more than one "lines" per transmit. You can just replace that with struct.unpack('<HHL',handle.read(8)). Remember that the ´handle.read()´ takes a number of bytes where as the Arduino send code is dealing with bits.
I think it might work with this modifications:
import serial
ser = serial.Serial('/dev/tty.usbserial-A900frF6', 9600)
var = 1
while var == 1:
if (ser.inWaiting() > 0):
ser.readline(1)
print "hello"
Related
So I have this problem,I'm trying to send sensor data from arduino to python through serial communication.
But the output is a kind of wierd.
Does anyone have an idea on this?
Arduino code: to send sensor data
void setup(){
Serial.begin(9600);
}
void loop(){
int sensor1 = 20;
int sensor2 = 40;
int sensor3 = 60;
Serial.write(sensor1);
}
python code: to receive sent data from arduino
import serial,time
ser = serial.Serial("/dev/ttyACM1",9600,timeout=1)
while True:
data = ser.read()
time.sleep(1)
print("data:",data)
output :
data: b'\x14'
target :
data: 20
second target : sending multiple sensor data in a single serial.write().
data: 20 40 60
By using Serial.write() on the arduino side, you are sending the sensor integers as single byte value characters, (as mentioned here in the reference) but on the receiving side seem to be expecting whole lines of input (readline()). The value printed, b'\x14' (hexadecimal 14) is decimal 20, so the transmission was actually correct. You can solve your problem by sending actual text integers from the arduino with:
Serial.println(sensor1);
I am using the Sensirion SFM3300 flow sensor and can read the correct values with the Arduino with the following code (I2C):
#include <Wire.h>
void setup() {
// put your setup code here, to run once:
Wire.begin();
Serial.begin(115200);
Wire.beginTransmission(byte(0x40));
Wire.write(byte(0x10));
Wire.write(byte(0x00));
Wire.endTransmission();
}
void loop() {
// put your main code here, to run repeatedly:
delay(100);
Wire.requestFrom(0x40,2);
uint16_t a = Wire.read();
uint8_t b = Wire.read();
a = (a<<8) | b;
float flow = ((float)a - 32768) / 120;
Serial.println(flow);
}
But using the Raspberry Pi I have written the nearly the same code, hoping that it also will works.
This is the code:
from smbus2 import SMBus
import time
import numpy as np
address=0x40
bus = SMBus(1)
def write(value):
bus.write_byte(address,value)
write(0x10)
write(0x00)
while True:
time.sleep(0.1)
a = np.uint16(bus.read_byte(0x40))
b = np.uint8(bus.read_byte(0x40))
a = (a<<8) | b
flow = (float(a)-32768)/120
print(flow)
The code really looks the same, but I only get -273,06666666666 as a return value. Does somebody knows where are the differences between Raspberry Pi and Arduino I2C and can help me to get the right values on the Pi?
You can use read_i2c_block_data(addr, offset, numOfBytes) method to get more than 1 byte of data from i2c. the return data is a list of bytes. So it is very easy to convert into an integer.
Edited based on datasheet and Arduino sketch
Here is the complete code for Python that should matched the Arduino example:
from SMBus2 import SMBus
import time
offset = 32768
scale = 120
addr = 0x40
cmd = [0x10, 0x00]
with SMBus(1) as bus:
bus.write_i2c_block_data(addr, 0, cmd)
time.sleep(0.1)
block = bus.read_i2c_block_data(addr, 0, 3)
reading = block[0] * 256 + block[1]
crc = block[2] # should do some crc check for error
flow = (reading - offset)/scale
print(flow)
I don't think your read process in python is correct. Reading from port 40 two times is different from reading two bytes from port 40.
I suggest to use read_byte_data(0x40, 0, 2) and process that with struct.unpack(">H").
I found a working solution. It would be nice if a I2C-expert could tell me why the following code is working instead of the python code above.
from fcntl import ioctl
from struct import unpack
from smbus import SMBus
address = 0x40
SMBus(1).write_byte_data(address,16,0)
i2c = open("/dev/i2c-1", "rb", buffering=0)
ioctl(i2c,0x0703,address)
i2c.read(3)
d0,d1,c = unpack('BBB', i2c.read(3))
d = d0 << 8 | d1
a = (float(d)-32768.)/120
print(a)
I want to send long values using python to an arduino board which runs c++. The serial communication breaks the 4 byte numbers up and sends them byte by byte. When I try to reassemble them on the back end, I only get a valid number for 2 bytes instead of the four bytes I sent.
Here is the python code sending instructions.
pos1 = int(input("pos1: "))
pos2 = int(input("pos2: "))
data = struct.pack('<ll', pos1, pos2)
ser.write(data)
Here is the arduino code to parse the bytes that it reads.
if(Serial.available()>0){
size_t numbytes = Serial.readBytes(data, 8);
for(int i=0; i<8; i++){
Serial.println(data[i], HEX);
}
pos1 = readfourbytes(data[0], data[1], data[2], data[3]);
pos2 = readfourbytes(data[4], data[5], data[6], data[7]);
Serial.println(pos1);
Serial.println(pos2);
}
long readfourbytes(byte fourthbyte, byte thirdbyte, byte thirdbyte, byte firstbyte){
long result = (firstbyte << 24) + (secondbyte << 16) + (thirdbyte << 8) + fourthbyte;
return result;
}
I guess this means the arduino is little endian? My problem is the second position value that is read is completely off. The python code seems to be the problem however I don't know why. when I send the int values of 100 for both, I get an output of
b'd\x00\x00\x00d\x00\x00\x00'
from the python code as the binary being sent in the data variable. But from the arduino, I recieve:
64
0
0
0
6D
2
0
0
100
621
So there is a disconnect between what I am sending and what I am recieving. The baudrates are the same and there is no other obvious fault that I am aware of.
All the expressions (<any>byte << <bits>) are evaluated as int that seems to be 16 bits on the arduino. Cast <any>byte into long, and you're done.
long readfourbytes(byte fourthbyte, byte thirdbyte, byte thirdbyte, byte firstbyte){
long result = ((long)firstbyte << 24) + ((long)secondbyte << 16) + (thirdbyte << 8) + fourthbyte;
return result;
}
I'm currently struggling with an issue. I have an arduino sending serialdata to my raspberry pi. The raspberry pi reads the data and stores it in a database. What i'm struggling with is to get data in the correct order. If i start the script at the correct time, the values get read properly. If i don't they get mixed up.
I have a headerByte sent from the arduino, this value is 999 and it is the first value to be sent each time. Is there a way in python to make 999 the marker for the beginning of every read? My variables will never exceed 999 so this will not be a problem.
Python code:
import serial
import time
values = []
serialArduino = serial.Serial('/dev/ttyACM0', baudrate=9600, timeout=1)
voltageRead = serialArduino.readline()
currentRead = serialArduino.readline()
while True:
voltageRead = serialArduino.readline()
currentRead = serialArduino.readline()
print"V=", voltageRead, "A=", currentRead
Arduino Code:
void loop() {
float voltageRead = analogRead(A0);
float ampsRead = analogRead(A1);
float calculatedVoltage = voltageRead / 103;
float calculatedCurrent = ampsRead / 1;
int headerByte = 999;
Serial.println(headerByte);
Serial.println(calculatedVoltage);
Serial.println(calculatedCurrent);
delay(1000);
}
Your method isn't particularly efficient; you could send everything as a struct from the Arduino (header + data) and parse it on the RPi side with the struct module though your way does have the advantage of simplicity. But, if 999 is the highest value you expect in your readings, then it makes more sense to use a number greater than 999 like 1000. And 999 isn't really a byte.
That said, if "1000" is your header, you can simply check for the header's presence like this:
HEADER = "1000"
serialArduino = serial.Serial('/dev/ttyACM0', baudrate=9600, timeout=1)
while True:
if serialArduino.readline().rstrip() == HEADER: # check for header
voltageRead = serialArduino.readline() # read two lines
currentRead = serialArduino.readline()
print"V=", voltageRead, "A=", currentRead
I am writing a byte to serial port using Python.
import serial
ser = serial.Serial ("/dev/ttyACM0")
ser.baudrate = 115200
ser.write('\x57')
ser.close()
When I connect TX to RX I have no problem to read that byte (sent from Python code), using GtkTerm. But when I am trying to read this data on micro controller using C, I always read 240. But when I use GtkTerm to send hexadecimal data directly (View -> Send Hexadecimal data), I read (on microcontroller) appropriate value. What could be wrong?
C code:
char byte = getc_();
printf_("1 byte received: i: %i \n",byte);
get_c() function:
char getc_()
{
#ifdef LIB_MUTEX
mutex_lock(&mutex_getc_);
#endif
char res = uart_read();
#ifdef LIB_MUTEX
mutex_unlock(&mutex_getc_);
#endif
return res;
}
Likely wrong communication rate. Change receiver/transmitting baud to maybe 1/4 or 1/6 -- Suggest 19200. (or speed up transmitting/reciver)
240 is 0xF0. With RS- 232, data is sent
Start bit - LS bit - next bit - ... -next bit - MS bit - Stop bit
// or
0 - LS bit - next bit - ... -next bit - MS bit - 1
// or 0xF0
0 - 0 - 0 - 0 - 0 - 1 - 1 - 1 - 1 - 1
If the receiving end is seeing too many 0 bits (look at least significant bit first), and data received is in bit groups of 0's and 1's, it usually means data is sent at a slower baud than what the receiver is using.
Another clue to deciphering mis-match baud is what is the ratio of the count of bytes sent versus received. Given various bit patterns this is not a hard rule, but the side with more data is likely the one at the higher baud.
After some time I find this solution. First byte I always receive 240, but with additional small sleeps I get the correct value which I sent.
import serial
import time
ser = serial.Serial (port = "/dev/ttyACM0", bytesize = 8, stopbits = 1)
ser.baudrate = 115200
sleep_time = 0.05
ser.write('\x41') #240
time.sleep(sleep_time)
ser.write('\x41') #right value A
time.sleep(sleep_time)
ser.write('\x41') #right value A
...
ser.close()