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.
Related
Currently, I am working on the part that transmits data through Bluetooth using Arduino Nano 33 BLE.
The part of sending the value from Arduino to Raspberry Pi was completed using Bluetooth during the work, but the output value received using Python from the Raspberry Pi was output as {' rsp ': ['wr']} instead of a number.
I am trying to proceed through this method while browsing various documents. How can I get the output value as a numeric value rather than an output value like {' rsp ': ['wr']} ?
If the value cannot be received as a number, should it be transformed into a Python code written in socket??
First, it is an example related to the Arduino battery, which is a commonly used code, and I tried to transform it in the way I want based on that code.
In that part, I changed the !Serial part to the Serial part so that it works even when not connected to the computer port.
In that case, I don't think there's a problem because it works just as well as I thought.
Arduino Sample Code
#include <ArduinoBLE.h>
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service
// BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central
BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
const int ledPin = LED_BUILTIN; // pin to use for the LED
void setup() {
Serial.begin(9600);
while (!Serial);
// set LED pin to output mode
pinMode(ledPin, OUTPUT);
// begin initialization
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1);
}
// set advertised local name and service UUID:
BLE.setLocalName("LED");
BLE.setAdvertisedService(ledService);
// add the characteristic to the service
ledService.addCharacteristic(switchCharacteristic);
// add service
BLE.addService(ledService);
// set the initial value for the characeristic:
switchCharacteristic.writeValue(0);
// start advertising
BLE.advertise();
Serial.println("BLE LED Peripheral");
}
void loop() {
// listen for BLE peripherals to connect:
BLEDevice central = BLE.central();
// if a central is connected to peripheral:
if (central) {
Serial.print("Connected to central: ");
//prints the centrals MAC address:
Serial.println(central.address());
// while the central is still connected to peripheral:
while (central.connected()) {
// if the remote device wrote to the characteristic,
// use the value to control the LED:
if (switchCharacteristic.written()) {
if (switchCharacteristic.value()) { // any value other than 0
Serial.println("LED on");
digitalWrite(ledPin, HIGH); // will turn the LED on
} else { // a 0 value
Serial.println(F("LED off"));
digitalWrite(ledPin, LOW); // will turn the LED off
}
}
}
// when the central disconnects, print it out:
Serial.print(F("Disconnected from central: "));
Serial.println(central.address());
}
}
Here is the raspberry pi code
import bluepy.btle as btle
p1 = btle.Peripheral("2D:20:48:59:8F:B4")
services1=p1.getServices()
s1 = p1.getServiceByUUID(list(services1)[2].uuid)
c1 = s1.getCharacteristics()[0]
a1=c1.write(bytes("0001".encode()))
p1.disconnect()
When the code is executed, the result is as follows:
{'rsp': ['wr']}
In the above code, I want to output a numeric value from the result. How do I modify the code in Python or Arduino on Raspberry Pi so that the output value in Python on Raspberry Pi comes out as a number?
It looks like you want to write either a zero or a one to the Arduino from the Raspberry Pi. You are using bytes("0001".encode()) but this will use the ASCII values for the characters. For example:
>>> list(bytes("0001".encode()))
[48, 48, 48, 49]
>>> list(bytes("0000".encode()))
[48, 48, 48, 48]
There are a couple of ways to create bytes of a numeric value:
>>> list(struct.pack('b', 0))
[0]
>>> list(struct.pack('b', 1))
[1]
>>> list(int(0).to_bytes(1, byteorder='little'))
[0]
>>> list(int(1).to_bytes(1, byteorder='little'))
[1]
Note: I've wrapped all of the commands in list() just to show the results in common formatting and is unnecessary for your code
I would also recommend being more specific about the services and characteristics you are geting by specifying the UUIDs. For example:
import bluepy.btle as btle
from bluepy.btle import UUID
ledService = UUID("19B10000-E8F2-537E-4F6C-D104768A1214")
switchCharacteristic = UUID("19B10001-E8F2-537E-4F6C-D104768A1214")
p1 = btle.Peripheral("2D:20:48:59:8F:B4")
services1 = p1.getServices()
s1 = p1.getServiceByUUID(ledService)
c1 = s1.getCharacteristics(switchCharacteristic)[0]
a1 = c1.write(struct.pack('b', 1))
p1.disconnect()
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))
how do i change the if statement in arduino in order to move the servo the same degrees as whats randomized in python?
First he's the python code:
import serial
import time
import random
arduinoData = serial.Serial('com4',9600)
while True:
low = 0; high = 180
ran_number = random.randint(low, high)
print ran_number
time.sleep(1)
arduinoData.write(ran_number)
python code is fine works with other things.
Now the arduino code, fix this:
#include <Servo.h>
Servo myservo;
int data;
int pin=9;
int pos = 0;
void setup() {
myservo.attach(9);
pinMode(pin, OUTPUT);
digitalWrite (pin, LOW);
Serial.begin(9600);
}
void loop() {
while (Serial.available()){
data = Serial.read();
}
if statement here.....
}
}
What you are looking for is the ParseInt method. Instead of using read in a loop and constructing your number, Serial.ParseInt does exactly that for you.
The correct code would be:
#include <Servo.h>
Servo myservo;
int pin = 9;
void setup() {
myservo.attach(9);
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
Serial.setTimeout(100);
Serial.begin(9600);
}
void loop() {
int number;
if (Serial.available()) {
number = Serial.parseInt();
myservo.write(number);
delay(0.5);
}
}
Notice that I set the serial timeout period to 100ms. This is to prevent parseInt to wait too much before deciding that it has read an entire int; otherwise when receiving a value (say, 42) it waits for about 1 second waiting for some other digits.
The Python script also has a problem or two. First, you should wait for a bit after establishing the connection because every time the serial is opened the Arduino resets, so it won't read the first couple of values. You could also get a cleaner solution by printing some string from the Arduino when ready (as the last instruction of the setup function, for example) and wait for it in the Python script with readLine.
Secondly, arduinoData.write takes a byte array as input, whereas you pass an int. You need to encode such int in bytes, first converting it to a string with str and then encoding it with encode.
I got the following program working:
import serial
import time
import random
arduinoData = serial.Serial('/dev/ttyUSB0',9600)
# Let Arduino some time to reset
time.sleep(2)
while True:
low = 0; high = 180
ran_number = random.randint(low, high)
print(ran_number)
arduinoData.write(str(ran_number).encode())
time.sleep(1)
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])
As my first foray into programming with python (previous exp only with vba and very basic java), I am trying to replicate some functionality of a GUI application, osPID Frontend, written in Processing, which communicates with an Arduino sketch, osPID-Firmware, over USB serial.
The front end takes a float array, converts it to a byte array, and then sends this over the serial link.
I have been trying to test sending a byte array to the device using the following python code:
import serial
def Send_Dash(myPort):
#To_Controller()
byteSet = 1,2,3 #bytes(1),bytes(4),bytes(3)
toSend = bytearray(byteSet)
myPort.write('5')
myPort.write('1')
myPort.write(toSend)
myPort = serial.Serial('/dev/ttyUSB0',9600,8,'N',timeout=None) # open first serial port
if myPort.isOpen():
myPort.read(10)
#typeReq = bytearray('0,0')
#myPort.write(typeReq)
Send_Dash(myPort)
Unfortunately which I can connect ok, I don't seem to be able to send any successful commands. I'd like to come up with some generic function I can use to send commands to the arduino (either by replicating the Processing code in python, or doing away with the byte array structure entirely (if possible). The original code was written to allow values such as 300000 and 0.000001 to the device. I will be unlikely to send anything greater than 300, or with decimals below 0.001, but as I hope to release this open source, others might.
For reference, the relevant arduino and Processing functions are below:
The function which converts the float array to a byte array is:
byte[] floatArrayToByteArray(float[] input)
{
int len = 4*input.length;
int index=0;
byte[] b = new byte[4];
byte[] out = new byte[len];
ByteBuffer buf = ByteBuffer.wrap(b);
for(int i=0;i<input.length;i++)
{
buf.position(0);
buf.putFloat(input[i]);
for(int j=0;j<4;j++) out[j+i*4]=b[3-j];
}
return out;
}
An example of one of the functions which packs the data is:
// Sending Floating point values to the arduino
// is a huge pain. if anyone knows an easier
// way please let know. the way I'm doing it:
// - Take the 6 floats we need to send and
// put them in a 6 member float array.
// - using the java ByteBuffer class, convert
// that array to a 24 member byte array
// - send those bytes to the arduino
void Send_Dash()//To_Controller()
{
float[] toSend = new float[3];
toSend[0] = float(SPField.getText());
toSend[1] = float(InField.getText());
toSend[2] = float(OutField.getText());
Byte a = (AMLabel.valueLabel().getText()=="Manual")?(byte)0:(byte)1;
byte identifier = 1;
myPort.write(identifier);
myPort.write(a);
myPort.write(floatArrayToByteArray(toSend));
}
A more simple function is:
void Run_Profile()
{
byte[] toSend = new byte[2];
toSend[0]=8;
toSend[1]=1;
myPort.write(toSend);
}
The byte array is made into a union:
boolean ackDash = false, ackTune = false;
union { // This Data structure lets
byte asBytes[32]; // us take the byte array
float asFloat[8]; // sent from processing and
} // easily convert it to a
foo; // float array
And read by SerialReceive(), which takes action based on the data:
void SerialReceive()
{
// read the bytes sent from Processing
byte index=0;
byte identifier=0;
byte b1=255,b2=255;
boolean boolhelp=false;
while(Serial.available())
{
byte val = Serial.read();
if(index==0){
identifier = val;
Serial.println(int(val));
}
else
{
switch(identifier)
{
case 0: //information request
if(index==1) b1=val; //which info type
else if(index==2)boolhelp = (val==1); //on or off
break;
case 1: //dasboard
case 2: //tunings
case 3: //autotune
if(index==1) b1 = val;
else if(index<14)foo.asBytes[index-2] = val;
break;
case 4: //EEPROM reset
if(index==1) b1 = val;
break;
case 5: //input configuration
if (index==1)InputSerialReceiveStart();
InputSerialReceiveDuring(val, index);
break;
case 6: //output configuration
if (index==1)OutputSerialReceiveStart();
OutputSerialReceiveDuring(val, index);
break;
case 7: //receiving profile
if(index==1) b1=val;
else if(b1>=nProfSteps) profname[index-2] = char(val);
else if(index==2) proftypes[b1] = val;
else foo.asBytes[index-3] = val;
break;
case 8: //profile command
if(index==1) b2=val;
break;
default:
break;
}
}
index++;
}
//we've received the information, time to act
switch(identifier)
{
case 0: //information request
switch(b1)
{
case 0:
sendInfo = true;
sendInputConfig=true;
sendOutputConfig=true;
break;
case 1:
sendDash = boolhelp;
break;
case 2:
sendTune = boolhelp;
break;
case 3:
sendInputConfig = boolhelp;
break;
default:
break;
}
break;
case 1: //dashboard
if(index==14 && b1<2)
{
setpoint=double(foo.asFloat[0]);
//Input=double(foo.asFloat[1]); //Not used
if(b1==0)
{
output=double(foo.asFloat[2]);
}
}
break;
case 2: //Tune
if(index==14 && (b1<=1))
{
kp = double(foo.asFloat[0]); //
ki = double(foo.asFloat[1]); //
kd = double(foo.asFloat[2]); //
}
break;
case 3: //ATune
if(index==14 && (b1<=1))
{
aTuneStep = foo.asFloat[0];
aTuneNoise = foo.asFloat[1];
aTuneLookBack = (unsigned int)foo.asFloat[2];
}
break;
default:
break;
}
}
Edit:
Sorry Lesto, yes I did read the duplicate answers, but I've only just figured out how to use struct.pack (I think!).
It appears that the existing Processing code packs (up to) 6 floats into a 32 byte array, using ByteBuffer. 6 floats is 24 bytes using struct.pack - although I gather I can pad this out by adding 1s and 0s into the format string, provided I know where they should go?
Following the comments by lesto, I have had another try (but still not quite there).
import struct
import serial
from array import array
def packByteArray(formatToSend):
bytesToSend = bytearray()
#bytesToSend.extend(struct.pack('#fffff',1,0,20,0,10) #Different result?
bytesToSend.extend(struct.pack(formatToSend,1)) #request Type
bytesToSend.extend(struct.pack(formatToSend,0)) #identifier
bytesToSend.extend(struct.pack(formatToSend,20)) #Setpoint
bytesToSend.extend(struct.pack(formatToSend,0)) #Input
bytesToSend.extend(struct.pack(formatToSend,10)) #Output
return bytesToSend
def main():
myPort = serial.Serial('/dev/ttyUSB0',9600,8,'N',timeout=None)
#Check if port is open, and if so, send the byta array.
if myPort.isOpen():
thisVar = packByteArray('>f')
print len(thisVar)
myPort.write(thisVar)
if __name__ == '__main__':
main()