Python to Arduino UDP communication - python

I'm having troubles sending integers from python to Arduino Leonardo, I have the following scenario:
I have 4 integers, x-y coordinates usually 3 numbers and I want to send them over UDP to the arduino, and at the arduino the input serial data is stored in a buffer which at the end the following function is used to retrieve the data.
if (Serial1.available() > 0) {
// We rotate the Buffer
for (i = (PACKET_SIZE - 1); i > 0; i--)
{
SBuffer[i] = SBuffer[i - 1];
}
SBuffer[0] = Serial1.read();
......
dump = extractParamInt(10);
x= extractParamInt(8);
y= extractParamInt(6);
x1= extractParamInt(4);
y1= extractParamInt(2);
}
uint16_t extractParamInt(uint8_t pos) {
union {
unsigned char Buff[2];
uint16_t d;
}
u;
u.Buff[0] = (unsigned char)SBuffer[pos];
u.Buff[1] = (unsigned char)SBuffer[pos + 1];
return (u.d);
}
Im having difficulties sending raw bytes from python to the buffer, the arduino treats each b'xxx' as a byte and stores it in a buffer index.
So how do I send my 4 values which are often more than 255 to arduino, and make the arduino function combine the sent values into uint16_t correctly?
One thing I know is that the buffer should contain an upper byte and a lower byte so they're combined into uint16_t, but i cannot send 8 bits from python so the arduino proccesses it using serial.read as a one byte.
I have tried struct.pack, string.encode('ascii'), i can't get a solid method to send my values correctly.
Python Code:
# values:
dump = 0
x = 433
y = 223
x1 = 233
y1 = 322
payload = 10
# desire
MESSAGE = 'mm1{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}'.format(dump,int(x),int(y),int(x1),int(y1),payload)
# sock.sendto(MESSAGE.encode('ascii'), (UDP_IP, UDP_PORT))
# testing
sock.sendto(b'\x5f\x00\x00\x00\x01\x00\x07\x01\xcc\x00\x00\x00\x1f\x8b', (UDP_IP, UDP_PORT))

Related

Serial Communication PySerial i get weird Characters (Probably ASCII?) I'm not able to decode it

Im try to communicate serial data between an Arduino and Python. I get this list of Character but im not able to convert them into integers. I didnt got it running through decoding: It works when im sending values from 0 - 255 aka single bytes...
I'm using Python 3.7
y.encode('utf-8').strip()
Python Code
import serial
import numpy as np
barr = [4]
ser = serial.Serial(
port='COM12',\
baudrate=56000,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=5)
print("connected to: " + ser.portstr)
count=0
while True:
y = ser.readline(512)
print(y)
Python Output:
b'\x9a1\x9aX\x9a\x7f\x9a\xa6\x9c\xce\x9a\xf5\x9a\x1c\x9aC\x9cj\x9a\x91\x9a\xb8\x9a\xdf\x9c\x06\x9a-\x9cT\x9a{\x9c\xa2\x9c\xc9\x9a\xf0\x9a\x17\x9c>\x9af\x9a\x8d\x9a\xb4\x9a\xdb\x9a\x02\ and it goes one....
Serial.write part of Arduino Code:
for (unsigned short int nIndexStep = 0; nIndexStep<g_objRF.getConfiguration()->getFreqSpectrumSteps(); nIndexStep++)
{
//Print every step of sweep data onto display
int stepFreq = g_objRF.getSweepData()->getFrequencyKHZ(nIndexStep);
int stepAmp = g_objRF.getSweepData()->getAmplitudeDBM(nIndexStep);
delay(serialdelay);
Serial.write(stepFreq);
delay(serialdelay);
Serial.write(stepAmp);
if (stepAmp <= (TopLevel -90))
{
stepAmp = TopLevel -90;
}
if (stepAmp > TopLevel)
{
stepAmp = TopLevel;
}
//Serial.print(stepAmp);
//Serial.print(", ");
comp2 = stepAmp;
comp1 = max(comp1, comp2);
{
}
}
}
Serial.write("\n");
With Serial.write you can send a byte, a string or a byte array. You cannot send an integer.
If you want to send an integer you'll have to convert it to a byte array first.
There are countless ways to do that. Bit masks, cast pointers, unions... just search the web and pick your favourite.
See https://www.arduino.cc/reference/en/language/functions/communication/serial/write/

Equivalent of this code in Python

I need to send data in a specific format from my Python program. The goal is that I can send data from my Python on my PC to an XBee connected to an Arduino. This has been done from a program (running on an Arduino) but not Python (Python code usually have been the receiver).
// allocate two bytes for to hold a 10-bit analog reading
uint8_t payload[7];
int value1 = 100; // data to be sent
payload[0] = value1 >> 8 & 0xff; // payload[0] = MSB.
payload[1] = value1 & 0xff; // 0xff = 1111 1111, i.e.
And finally sends the data with this command (using XBee library):
Tx16Request tx_5001 = Tx16Request(0x5001, payload, sizeof(payload));
xbee.send(tx_5001);
In Python, using XBee Python library, I want to send this data but it should conform to this format so that I can use the existing code to read the data and convert it to useful value.
In summary, does anyone know what's the equivalent of the following code in Python?
uint8_t payload[7];
int value1 = 100; // data to be sent
payload[0] = value1 >> 8 & 0xff; // payload[0] = MSB.
payload[1] = value1 & 0xff; // 0xff = 1111 1111, i.e.
On the receive side, I have the following to extract the value that can be used:
uint8_t dataHigh = rx.getData(0);
uint8_t dataLow = rx.getData(1);
int value1 = dataLow + (dataHigh * 256);
Edit: I really don't need two bytes for my data, so I did a test by defining a byte in python data=chr(0x5A) and used that in my transmit, and in the received I used int value1 = analogHigh which returns 232 regardless what I define the data!
You probably looking for struct (to convert python int to unit8_t) I have used it to communicate with low level devices.
So
import struct
var = 25 # try 2**17 and see what happens
print(struct.pack('<H', var)) # b'\x00\x19'
print(struct.pack('>H', var)) # b'\x00\x19'
As you can see this > or < changing bytes between Little and Big Endian.
H is for short (uint16_t) and B is for unsigned char (uint8_t).
The b'' is python bytes string that allow you to see bytes data.
So i would to suggest you to start from xbee.send(b'\x45\x15') and see what happens on the other side. If communication works then start to create function that convert python types to structure reperesents.
See also:
BitwiseOperators
There are different ways to handle this, one way is to do a for loop
for (int i = 0; i < rx.getDataLength(); i++)
{
Serial.print( rx.getData(i), HEX );
}
and extract the data from that string which there are so many ways to do it.

Temperature conversion in Python: How to convert int (0 - 255) which is a byte object to degrees Celsius in Python

We are currently working on an Arduino Uno project and getting stuck at the conversion of the integer data to degrees celcius. This code is working however it converts the binary packed data (\xd01) etc. to int (0-255). Our question is: how to convert the integer value to read out a certain degree of Celcius. For example: int 2 = 2 degrees celcius and 255 = 35 degrees Celcius
This is our Python code with the Pyserial module
import serial
import struct
ser = serial.Serial('COM3', 19200, timeout=5)
while True:
tempdata = ser.read(2)
x= struct.unpack('!BB', tempdata)
print(x)
And this is the code of the temperature conversion on our Arduino Uno, it is written in C.
#define F_CPU 16E6
// output on USB = PD1 = board pin 1
// datasheet p.190; F_OSC = 16 MHz & baud rate = 19.200
#define UBBRVAL 51
void uart_init()
{
// set the baud rate
UBRR0H = 0;
UBRR0L = UBBRVAL;
// disable U2X mode
UCSR0A = 0;
// enable transmitter
UCSR0B = _BV(TXEN0);
// set frame format : asynchronous, 8 data bits, 1 stop bit, no parity
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
}
void transmit(uint8_t data)
{
// wait for an empty transmit buffer
// UDRE is set when the transmit buffer is empty
loop_until_bit_is_set(UCSR0A, UDRE0);
// send the data
UDR0 = data;
}
void init_adc()
{
// ref=Vcc, left adjust the result (8 bit resolution),
// select channel 0 (PC0 = input)
ADMUX = (1<<REFS0);
// enable the ADC & prescale = 128
ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}
uint8_t get_adc_value()
{
//ADMUX |= 1
ADCSRA |= (1<<ADSC); // start conversion
loop_until_bit_is_clear(ADCSRA, ADSC);
return ADC; // 8-bit resolution, left adjusted
}
/*
((value / 1024 * 5) - 0. 5) * 100
*/
int main(void) {
init_adc();
uart_init();
//int x;
while(1)
{
int x = get_adc_value();
int temp = ((((float) x / 1024) * 5) - 0.5) * 100;
transmit(temp);
_delay_ms(200);
}
}
The conversion from ADC value to temp will most likely depend on what type of temperature sensor you are using. I recommend looking a the datasheet of your temp sensor.
If you are using a 'TMP36', you can convert using this formula:
Centigrade temperature = [(analog voltage in mV) - 500] / 10
Source: https://learn.adafruit.com/tmp36-temperature-sensor/using-a-temp-sensor
If you a using a thermocouple, you'll need to look at the table of correspondance for the type you are using (e.g. type K: https://www.omega.fr/temperature/Z/pdf/z204-206.pdf)

How to send an array by i2c?

I've been trying for several days now to send a python array by i2c.
data = [x,x,x,x] # `x` is a number from 0 to 127.
bus.write_i2c_block_data(i2c_address, 0, data)
bus.write_i2c_block_data(addr, cmd, array)
In the function above: addr - arduino i2c adress; cmd - Not sure what this is; array - python array of int numbers. Can this be done? What is actually the cmd?
FWIW, Arduino code, where I receive the array and put it on the byteArray:
void receiveData(int numByte){
int i = 0;
while(wire.available()){
if(i < 4){
byteArray[i] = wire.read();
i++;
}
}
}
It gives me this error: bus.write_i2c_block_data(i2c_adress, 0, decodedArray) IOError: [Errno 5] Input/output error. I tried with this: bus.write_byte(i2c_address, value), and it worked, but only for a value that goes from 0 to 127, but, I need to pass not only a value, but a full array.
The function is the good one.
But you should take care of some points:
bus.write_i2c_block_data(addr, cmd, []) send the value of cmd AND the values in the list on the I2C bus.
So
bus.write_i2c_block_data(0x20, 42, [12, 23, 34, 45])
doesn't send 4 bytes but 5 bytes to the device.
I doesn't know how the wire library work on arduino, but the device only read 4 bytes, it doesn't send the ACK for the last bytes and the sender detect an output error.
Two convention exist for I2C device address. The I2C bus have 7 bits for device address and a bit to indicate a read or a write. An other (wrong) convention is to write the address in 8 bits, and say that you have an address for read, and an other for write. The smbus package use the correct convention (7 bits).
Exemple: 0x23 in 7 bits convention, become 0x46 for writing, and 0x47 for reading.
It took me a while,but i got it working.
On the arduino side:
int count = 0;
...
...
void receiveData(int numByte){
while(Wire.available()){
if(count < 4){
byteArray[count] = Wire.read();
count++;
}
else{
count = 0;
byteArray[count] = Wire.read();
}
}
}
On the raspberry side:
def writeData(arrayValue):
for i in arrayValue:
bus.write_byte(i2c_address, i)
And that's it.
cmd is offset on which you want to write a data.
so its like
bus.write_byte(i2c_address, offset, byte)
but if you want to write array of bytes then you need to write block data so your code will look like this
bus.write_i2c_block_data(i2c_address, offset, [array_of_bytes])

PySerial and microcontroller

I'm trying to communicate with an MCU via UART with pySerial.
I use an Arduino USB2Serial Light Converter to send the data from PC to my PIC33F.
Because of the Arduino Converter I cant use CTS or RTS.
I managed to send data to the MCU and also receive data from it. But the problem is
the data isn't always what it should be.
For example I want to send a whole string from the MCU to the terminal but what I get is not the string given in the code of the MCU programm. Another problem is
that NO comparison between characters work. Which means I send a character to the MCU and the MCU compares it with a character to make decisions.
Ok now to some code:
1. Send string from PIC33F to PC and read it with pySerial
putsU1("string");
// print a string
int putsU1(char *s) {
while(*s)
putU1(*s++);
}
// print a character
int putU1(int c) {
while (_UTXBF);
U1TXREG = c;
return c;
}
The result I get for this code if I read it via pySerial is the following;
In [13]: ser = serial.Serial('/dev/ttyACM0', 9600, bytesize=7, xonxoff=0)
In [14]: while True:
print(struct.unpack('s', ser.read()))
....:
('\x7f',)
('c',)
('\x11',)
('\t',)
('e',)
('y',)
('=',)
('\x7f',)
2. Send data to PIC33F with pySerial and echo it back
This one works only for single characters and not for strings. Also the characters echoed back are switching randomly between uppcase and lowercase.
getsnU1(s, BUF_SIZE);
char *getsnU1(char *s, int len) {
char *p = s;
int cc = 0;
do {
*s = getU1();
putU1(*s);
if ((*s == 0x8) && (s > p)) {
putU1(' ');
putU1(0x8);
len++;
s--;
continue;
}
if (*s == '\n')
continue;
if (*s == '\r')
break;
s++;
len--;
} while (len > 1);
*s = '\0';
return p;
}
The UART configuration:
#define FOSC 8000000
#define FCY (FOSC/2)
#define BAUD 9600
#define U1BRGValue (((FCY/BAUD)/16)-1)
void initUART1(void) {
/* general UART config */
RPINR18bits.U1RXR = U1RXPIN; // set RP5 as UART1 RX
_RP6R = 0b00011; // set RP6 as UART Tx pin
U1RXPINTRIS = 1; // set RP5 (RB5) as input
U1MODEbits.PDSEL = DATAPARITY; // set data bits and parity
U1MODEbits.STSEL = STOPBITS; // set stop bits
U1MODEbits.ABAUD = 0; // auto-baud disabled
U1MODEbits.BRGH = 0; // low speed baud rate
U1BRG = U1BRGValue; // set baud value
/* enable interrupt on error */
IEC4bits.U1EIE = 1; // enable UART1 error interrupt
IPC16bits.U1EIP = 6; // set interrupt priority to 6
/* enable RX buffer interrupt */
IEC0bits.U1RXIE = 0; // enable UART1 RX interrupt
U1STAbits.URXISEL = 0b00; // interrupt flag is set when buffer is 3/4 full
IPC2bits.U1RXIP = 6; // set interrupt priority to 6
/* enable TX interrupt */
IEC0bits.U1TXIE = 0;
/* enable UART1 module */
U1MODEbits.UEN = 0b00;
U1MODEbits.UARTEN = 1;
_UTXEN = 1; // enable UART Tx
}
I'm kinda frustrated because I can't figure out where the problem lies. Been testing and reading for hours and everywhere it seems so simple to get this up and running but it just wont on my end ;(.
I tried to write data to the PIC33F with serial.write('text'.encode('ascii')) and
serial.write(struct.pack('s',('test'))) which all produce the same results.
Any help is highly appreciated!
I don't think you want bytesize=7 (unless you know exactly why).
Also, a classical problem with UARTs is that individual characters work, but whole strings don't - this is usually when the receiving microcontroller does only buffer a single character in hardware. I don't know PIC, but in that code above where you do getU1() followed by putU1() I guess that putU1() waits for the duration of one character, so when you call getU1() again later you will have missed the one character (if the software did send it without pause).
My usual workaround is something like:
def write(s):
for c in s:
ser.write(c)
time.sleep(0.001) # often not required
In general pyserial seems to work okay for all my projects, but you should also check with a normal terminal program to be sure. Apart from that I can only suggest you double-check the baudrate and FOSC on your microcontroller.

Categories

Resources