Python 3 CSV integers to bytes + \n [closed] - python

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I'am Trying to Communicate with Arduino using serial communication in python. There is this program from arduino https://www.arduino.cc/en/Tutorial/ReadASCIIString . Simply sending a " 120,200,100" to control 3 LEDS. When I tried it in python writing data to the arduino so simply
arduino.write(b'120,10,244\n')
and it works. But my main problem is that if I assigned those values to a variable which gets change through a GUI for example a PyQT slider which I'am planning to implement it on, How should I go about this?
How to output 3 integers assigned to variables->to csv -> bytes +\n
For example
P1 = self.PWM1horizontalSlider.value() # ASSUMING a value of 120
P2 = self.PWM2horizontalSlider.value() # ASSUMING a value of 200
P3 = self.PWM3horizontalSlider.value() # ASSUMING a value of 100
into b'120,200,100\n'
Read ASCII String Code
// pins for the LEDs:
const int redPin = 3;
const int greenPin = 5;
const int bluePin = 6;
void setup() {
// initialize serial:
Serial.begin(9600);
// make the pins outputs:
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
}
void loop() {
// if there's any serial available, read it:
while (Serial.available() > 0) {
// look for the next valid integer in the incoming serial stream:
int red = Serial.parseInt();
// do it again:
int green = Serial.parseInt();
// do it again:
int blue = Serial.parseInt();
// look for the newline. That's the end of your sentence:
if (Serial.read() == '\n') {
// constrain the values to 0 - 255 and invert
// if you're using a common-cathode LED, just use "constrain(color, 0, 255);"
red = 255 - constrain(red, 0, 255);
green = 255 - constrain(green, 0, 255);
blue = 255 - constrain(blue, 0, 255);
// fade the red, green, and blue legs of the LED:
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
// print the three numbers in one string as hexadecimal:
Serial.print(red, HEX);
Serial.print(green, HEX);
Serial.println(blue, HEX);
}
}
}

To answer your question, outside of the example:
p1 = 120
p2 = 200
p3 = 100
the_bytes = bytes(f'{p1},{p2},{p3}\n', 'utf-8')
This is assuming you want the bytes to use UTF-8 as an encoding, which is common, but something you'd want to check. It could also be something like cp1252 - more here https://docs.python.org/3/library/codecs.html#standard-encodings
You could then send the_bytes wherever you need them.

Related

Problem with python and arduino in pyserial

I wrote this code to print the sensor values ​​in Python, but the problem is that the soil_sensor prints twice.
This is the code in the Arduino :
#include <DHT.h>
#include <DHT_U.h>
#define DHTPIN 8
#define DHTTYPE DHT11
int msensor = A0;
int msvalue = 0;
int min = 0;
int max = 1024;
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(9600);
pinMode(msensor, INPUT);
dht.begin();
}
void loop() {
msvalue = analogRead(msensor);
float percentage = (float)((msvalue - min) * 100) / (max - min);
percentage = map(msvalue, max, min, 0, 100);
Serial.print("r ");Serial.println(percentage);
int h = dht.readHumidity();
int t = dht.readTemperature();
Serial.print ("h ");
Serial.println (h);
Serial.print ("c ");
Serial.println (t);
delay(2000);
}
And this is the code in Python :
from time import sleep
import serial
arduinoP1 = serial.Serial(port="/dev/ttyUSB0", baudrate=9600)
def rtot():
arduino_data = arduinoP1.read(6)
str_rn = arduino_data.decode()
sleep(1)
return str_rn
for x in range(3):
i = rtot()
if "r" in i:
v1 = int(float(i[1:5].strip('\\r\\nr')))
print(v1, 'soil_sensor')
if "c" in i:
print(i[1:2], 'temperature_sensor')
if "h" in i:
v3 = int(i[2:4])
print(v3, 'Humidity_sensor')
As you can see, the soil sensor is repeated twice :
soil sensor is repeated twice
I want the values ​​to be displayed correctly and in the form of numbers
The first thing you should notice is that sending numbers throug the serial interface will result in different string lenghts depending on the number of digits.
So reading a fixed number of 6 bytes is not a good idea. (actually this is almost never a good idea)
You terminate each sensor reading with a linebreak. So why not use readline instead of read[6].
Here v1 = int(float(i[1:5].strip('\\r\\nr'))) you're trying to remove \r, \n and r from the received string. Unfortunately you escaped the backslash so you're actually stripping \, r and n.
\r is actually something where you need the backslash to represent the carriage return character. Don't escape it!
In the first run loop() will send something like:
r 0.00\r\nh 40\r\nc 25\r\n
So the first 6 bytes are r 0.00. So i[1:5] is 0.0.
As you see there is nothing to escape. Also 5 is excluded so you would have to use i[2:6] to get 0.00. But as mentioned above using fixed lenghts for numbers is a bad idea. You can receive anything between 0.00 and 100.00 here.
So using readline you'll receive
r 0.00\r\n
The first and last two characters are always there and we can use [2,-2] to get the number inbetween regardless of its length.

Arduino inputString.indexOf with multiple variables

I am currently working on making an arduino monitoring device. The data is collected in Python and then the string is sent via serial to the arduino.
In Python the string looks like this:
cpu1 = space_pad(int(my_info['cpu_load']), 2)
cpu2 = space_pad(int(my_info['cpu_temp']), 2)
cpu3 = space_pad(int(my_info['cpu_fan']), 5)
# Send the strings via serial to the Arduino
arduino_str = \
'A' + cpu1 + '|B' + cpu2 + '|C' + cpu3 + '|'
if serial_debug:
print(arduino_str)
else:
ser.write(arduino_str.encode())
Ideally I want to make this string as large as possible, to include 10 variables, which I want to send to the arduino.
The arduino code looks at the string and it is supposed to read parts of the string and place them neatly on a display, each in it's own reserved space.
The problem is that I get garbled results. When the string is only made out of just one variable, then it shows just fine, where it should, as it should.
When adding an additional variable to the string, the code breaks and it mixes the results or displays them chaotically. My variables are all clean, just numbers, nothing fancy.
Below is the code I use on the arduino
#include <Wire.h>
#include <LiquidCrystal_I2C.h> // Library for LCD
LiquidCrystal_I2C lcd(0x27,20,4); // I2C address 0x27, 20 column and 4 rows
String inputString = ""; // String for buffering the message
boolean stringComplete = false; // Indicates if the string is complete
unsigned long previousUpdate = 0; // Long to keep the time since last received message
void printInitialLCDStuff() {
lcd.setCursor(0, 0);
lcd.print("CPU ");
lcd.setCursor(7, 0);
lcd.print("%");
lcd.setCursor(11, 0);
lcd.print("C");
lcd.setCursor(17, 0);
lcd.print("RPM");
lcd.setCursor(0, 1);
lcd.print("GPU ");
lcd.setCursor(7, 1);
lcd.print("%");
lcd.setCursor(11, 1);
lcd.print("C");
lcd.setCursor(17, 1);
lcd.print("RPM");
lcd.setCursor(0, 2);
lcd.print("MEM");
lcd.setCursor(8, 2);
lcd.print("MB");
lcd.setCursor(17, 2);
lcd.print("PWM");
lcd.setCursor(0, 3);
lcd.print("RAM ");
lcd.setCursor(8, 3);
lcd.print("GBU");
lcd.setCursor(17, 3);
lcd.print("GBF");
}
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inputString += inChar;
if (inChar == '|') {
stringComplete = true;
}
}
}
void setup() {
// Setup LCD
lcd.init(); //initialize the lcd
lcd.backlight(); //open the backlight
lcd.setCursor(0, 0);
printInitialLCDStuff();
// Setup serial
Serial.begin(9600);
inputString.reserve(200);
}
void loop() {
serialEvent();
if (stringComplete) {
// CPU1
int cpu1StringStart = inputString.indexOf("A");
int cpu1StringLimit = inputString.indexOf("|");
String cpu1String = inputString.substring(cpu1StringStart + 1, cpu1StringLimit);
lcd.setCursor(4, 0);
lcd.print(cpu1String);
// CPU2
int cpu2StringStart = inputString.indexOf("B", cpu1StringLimit);
int cpu2StringLimit = inputString.indexOf("|", cpu2StringStart);
String cpu2String = inputString.substring(cpu2StringStart + 1, cpu2StringLimit);
lcd.setCursor(9, 0);
lcd.print(cpu2String);
// CPU3
int cpu3StringStart = inputString.indexOf("C", cpu2StringLimit);
int cpu3StringLimit = inputString.indexOf("|", cpu3StringStart);
String cpu3String = inputString.substring(cpu3StringStart + 1, cpu3StringLimit);
lcd.setCursor(13, 0);
lcd.print(cpu3String);
inputString = "";
stringComplete = false;
previousUpdate = millis();
}
}
My code is very dirty and it mostly an adaptation of another code, because while I can read code, I am terrible at writing it. Apologies if I made horrible mistakes that would make anybody cringe. I admit I am just dabbling with coding. This is why I made notes in the code often.
I expect my display to show like this:
CPU 60% 45C 900RPM
Where
cpu1=60
cpu2=45
cpu3=900
The "CPU" "%", "C" and "RPM" are written by the arduino on printInitialLCDStuff() { and not Python.
Instead I get this
CPU B45% B45|B45|C
and then the RPM is listed on line 3 at (0,0) as "900|"
Ideally I want to expand the string sorting to collect about 10 variables.
It looks to me like the problem is in the arduino code, since the Python script kinda checks out and outputs the string correctly. But I could be wrong.
The question is: am I using the wrong code to extract these variables and place them in their reserved space on the display?
Should I use something else to get the job done? I have been looking at documentation for the past 3 days but I couldn't find someone with a similar case. I found some questions here, but again, not quite what I am looking for.
Any help is appreciated. I am so frustrated with this code after trying hours daily for the past days that I am willing to reward anyone that can assist me with this code with a steam digital gift card as way to show my appreciation.
Best regards,
M
I figured out. Instead of trying to separate the string and allocate it all in a preselected space I just made one single string and just edited the string format itself. Now I have a 80 char string and is auto arranged by (0, 20), (20, 40), (40, 60) and (60, 80).
Because this is just a simple resource monitor I didn't really need anything fancy, just to display the info on the screen.
Here is what I did
# Prepare CPU string line #1
cpu1 = space_pad(int(my_info['cpu_load']), 3) + '% '
cpu2 = space_pad(int(my_info['cpu_temp']), 2) + 'C '
cpu3 = space_pad(int(my_info['cpu_fan']), 4) + 'RPM'
CPU = 'CPU ' + cpu1 + cpu2 + cpu3
# Prepare GPU string line #2
gpu1 = space_pad(int(my_info['gpu_load']), 3) + '% '
gpu2 = space_pad(int(my_info['gpu_temp']), 2) + 'C '
gpu3 = space_pad(int(my_info['gpu_fan']), 4) + 'RPM'
GPU1 = 'GPU ' + gpu1 + gpu2 + gpu3
# Prepare GPU string line #3
gpu4 = space_pad(int(my_info['gpu_mem']), 4) + 'MB '
gpu5 = space_pad(int(my_info['gpu_pwm']), 3) + '% PWM'
GPU2 = 'MEM ' + gpu4 + gpu5
# Prepare RAM strng line #4
ram1 = space_pad(float(my_info['ram_used']), 4) + 'GBU '
ram2 = space_pad(float(my_info['ram_free']), 4) + 'GB'
RAM = 'RAM ' + ram1 + ram2
# Send the strings via serial to the Arduino
arduino_str = \
CPU + GPU1 + GPU2 + RAM + 'F'
if serial_debug:
print(arduino_str)
else:
ser.write(arduino_str.encode())
Because the '|' separator was constantly showing up as the last character of the string, I just switched to a letter that I wanted to appear instead, basically duck-taping it. I used the character 'F' which also acts as the letter and is shown in the string.
As for the Arduino code:
#include <Wire.h>
#include <LiquidCrystal_I2C.h> // Library for LCD
LiquidCrystal_I2C lcd(0x27,20,4); // I2C address 0x27, 20 column and 4 rows
String inputString = ""; // String for buffering the message
boolean stringComplete = false; // Indicates if the string is complete
unsigned long previousUpdate = 0; // Long to keep the time since last received message
void printInitialLCDStuff() {
}
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read();
inputString += inChar;
if (inChar == 'F') {
stringComplete = true;
}
}
}
void setup() {
// Setup LCD
lcd.init(); //initialize the lcd
lcd.backlight(); //open the backlight
printInitialLCDStuff();
lcd.setCursor(0, 0);
lcd.print("Arduino PC Monitor");
lcd.setCursor(0, 1);
lcd.print("Waiting for data...");
lcd.setCursor(12, 3);
lcd.print("Ver 1.0");
// Setup serial
Serial.begin(9600);
inputString.reserve(200);
}
void loop() {
serialEvent();
if (stringComplete) {
// 1st line
String cpuString = inputString.substring(0, 20);
lcd.setCursor(0, 0);
lcd.print(cpuString);
// 2nd line
String gpu1String = inputString.substring(20, 40);
lcd.setCursor(0, 1);
lcd.print(gpu1String);
// 3rd line
String gpu2String = inputString.substring(40, 60);
lcd.setCursor(0, 2);
lcd.print(gpu2String);
// 4th line
String ramString = inputString.substring(60, 80);
lcd.setCursor(0, 3);
lcd.print(ramString);
inputString = "";
stringComplete = false;
previousUpdate = millis();
}
}
Yes, it is that lazy, it is almost as sophisticated as counting on your fingers. I love how simple it is. Anything can be edited on the fly and all it takes is minimal knowledge.
I can understand if I get banned for being this lazy.
Thank you for your help. I have noted the advice on string splits if I need to do something more complicated.
Best regards,
M
Your code can be changed to make it work, however, it would be like patching it up. I think it is better to take a different approach.
First, on your Python code, the prefix A, B are become redundant and not helpful on the receiving side to parse the data. If you format your data as a string with | as the separator, it make it easier to parse, and it is also much easier to create a string like that in Python.
arduino_str = "{}|{}|{}|{}|{}|{}|{}|{}|{}|{}".format(
data0, data1, data2, data3, data4,
data5, data6, data7, data8, data9)
if serial_debug:
print(arduino_str)
else:
ser.write(arduino_str.encode())
On the Arduino side, Serial.readStringUntil() would make the reading entire string until a \n (end of the string) is encountered. Once the entire string is received, you can use the strtok() function in C++ to split the string by the delimiter (in this case it is |) into an array, so the splitted array would looks like this:
splitted[0] = data0;
splitted[1] = data1;
....
splitted[9] = data9;
You can then print the data in the array to the LCD.
#include <Wire.h>
#include <LiquidCrystal_I2C.h> // Library for LCD
#define NUMBER_OF_DATA 10
LiquidCrystal_I2C lcd(0x27,20,4); // I2C address 0x27, 20 column and 4 rows
String incomingString = "";
void setup()
{
// Setup LCD
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.setCursor(0, 0);
lcd.print("CPU % C RPM");
lcd.setCursor(0, 1);
lcd.print("GPU % C RPM");
lcd.setCursor(0, 2);
lcd.print("MEM MB PWM");
lcd.setCursor(0, 3);
lcd.print("RAM GBU GBF");
// Setup serial
Serial.begin(9600);
}
void loop()
{
// read data from Serial until '\n' is received
while (Serial.available()) {
incomingString = Serial.readBytesUntil('\n');
}
if (incomingString) {
// convert the String object to a c_string
char *c_string = incomingString.c_str();
// make a copy of received data so that incoming data would not override the received data
char temp[strlen(c_string)+1];
strcpy(temp, c_string);
incomingString = "";
// parse the received string separated by '|' into an array
char splitted[NUMBER_OF_DATA][10] = {'\0'}; // initialise an array of 10-ch string
int i = 0;
char *p = strtok(temp, '|'); // parse first element in the string
while(p != NULL) { // loop through the string to fill the array
splitted[i++] = p;
p = strtok(NULL, '|');
}
// update LCD with the data in the array
lcd.setCursor(4, 0);
lcd.print(splitted[0]);
lcd.setCursor(9, 0);
lcd.print(splitted[1]);
lcd.setCursor(13, 0);
lcd.print(splitted[2]);
// print the rest of data
lcd.setCursor(13, 3);
lcd.print(splitted[9]);
}
}
I wrote this based on your code and have not debug on an Arduino yet, so it might need some debugging if it is not work out-of-the-box. I hope this help you in learn some trick and a little bit of C++ string and char array.

How to send float values from Python to Arduino LCD?

My classmate and I have been working on this project based on this Instructables article https://www.instructables.com/id/Building-a-Simple-Pendulum-and-Measuring-Motion-Wi/, our idea is to make a pendulum, calculate the g force (from the pendulum's period) and then show its value on a LCD we got connected to the Arduino. We got the code up and running (it calculates the period), and we understood that the Arduino has to do some type of conversion (utf-8) to pass the values it gets from the potentiometer to Python. However when we try to send the value we get from calculating the period of the graph back to the arduino and show it on the LCD, it shows 634 or other similiar values, we tried to instead of the decode it does initially, go the other way around with encode, but it won't work. We can't check the value it is getting from the serial, because the serial monitor simply doesn't open while the python script is running. What is the most pratical way we can use to "transfer" floats calculated in a Python script to the Arduino, so that we can calculate g and show it on the screen. Many forums advice to instead of transferring the floats, convert them to strings since it would be easy for the arduino to receive, but we aren't sure that would even work. I'm sure this is a simple question, but we just can't seem to get it. If you find anything else wrong with the code please let me know, we know it's a bit sketchy. Thanks.
Python code:
arduino = serial.Serial('COM3', 115200, timeout=.1) #Open connection to Arduino
samples = 200 #We will take this many readings
angle_data = np.zeros(samples) #Creates a vector for our angle data
time_data = np.zeros(samples) #Creates a vector of same length for time
i = 0;
calibrate = 123 #Value to zero potentiometer reading when pendulum is motionless, read from Arduino
while i!=samples:
data = arduino.readline()[0:-2].decode('utf-8')
if data:
angle_data[i] = (float(data) - calibrate)*math.pi/180
time_data[i] = time.perf_counter()
print(angle_data[i])
i = i + 1
min = np.min(angle_data)
print (min)
min_pos, = np.where(angle_data == min)
min_x = time_data[min_pos]
print (min_x)
nos_left = int(min_pos)
max = 0;
for i in range(nos_left,200):
if angle_data[i] > max: max = angle_data[i]
print (max)
max_pos, = np.where(angle_data == max)
max_x = time_data[max_pos]
print (max_x)
period = (max_x - min_x) * 2
print (period)
gforce = (0.165 * 4 * (math.pi) * (math.pi)) / ((period) * (period))
print (gforce)
value_g = arduino.write(gforce)
plt.plot(time_data,angle_data,'ro')
plt.axis([0,time_data[samples-1],-math.pi,math.pi])
plt.xlabel("Time (seconds)")
plt.ylabel("Angle (Radians)")
plt.title("Pendulum Motion - Measured with Arduino and potentiometer")
plt.show()
arduino.close()
Arduino code
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int period = 0;
void setup() {
lcd.begin(16, 2);
Serial.begin(115200); // use the same baud-rate as the python side
pinMode(A0,INPUT);
lcd.print(" Pendulo ");
int gforce = 0;
}
void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
// print the number of seconds since reset:
int degrees;
degrees = getDegree();
Serial.println(degrees);
Serial.println("\n");
delay(50);
if (Serial.available() > 0) {
// read the incoming byte:
gforce = Serial.read();
Serial.print("I received: ");
Serial.println(gforce, DEC);
}
lcd.setCursor(0, 1);
lcd.print(gforce);
}
int getDegree()
{
int sensor_value = analogRead(A0);
float voltage;
voltage = (float)sensor_value*5/1023;
float degrees = (voltage*300)/5;
return degrees;
}
This appears to be a good case for the Arduino Serial's parseFloat() method:
if (Serial.available() > 0) {
/* instead of Serial.read(), use: */
gforce = Serial.parseFloat();
Serial.print("I received: ");
Serial.println(gforce, DEC);
}
Essentially, it pulls out anything that looks like a float within the received serial data, even if it's mixed with other non-numerical characters.
It also works with Software Serial.

Print Python output on Arduino display [duplicate]

This question already has answers here:
Serial communication between Arduino and Matlab is losing data
(2 answers)
Closed 4 years ago.
I am completely lost. I'm trying to print something on the Arduino's display using Python. I'm aware that this can be accomplished without Python by using:
lcd.write("my string");
But I'd like to use the pySerial library to do this.
This is my Python code:
import serial
arduino = serial.Serial('COM3', 9600)
myvar1 = "text"
arduino.write(myvar1.encode())
And this is the code I have on my Arduino:
// include the library code:
#include <LiquidCrystal.h>
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 8, en = 9, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// initialize the serial communications:
Serial.begin(9600);
}
void loop() {
// when characters arrive over the serial port...
if (Serial.available()) {
// wait a bit for the entire message to arrive
delay(100);
// clear the screen
lcd.clear();
// read all the available characters
while (Serial.available() > 0) {
// display each character to the LCD
lcd.write(Serial.read());
}
}
}
Also, when I enter text using the Serial Monitor, it shows up on the Arduino like it should (Just has a little sideways "HI!" at the end but that's not a problem).
The serial data may be buffered by Python.
Try:
arduino.write(myvar1.encode())
arduino.flush()

How do I convert float value to string without using sstream for arduino?

I have a DHT11 sensor connected to a Yún shield, and I am reading data from the sensor using DHT library:
indoorHumidity = dhtBedRom.readHumidity();
// Read temperature as Celsius
indorTempinC = dhtBedRom.readTemperature();
// Read temperature as Fahrenheit
indorTempinF = dhtBedRom.readTemperature(true);
// Compute heat index, Must send in temp in Fahrenheit!
hi = dhtBedRom.computeHeatIndex(indorTempinF, indoorHumidity);
hIinCel = (hi + 40) / 1.8 - 40;
dP = (dewPointFast(indorTempinC, indoorHumidity));
dPF = ((dP * 9) / 5) + 32;
and then I am trying to put the data dew point and temperature, humidity and heat index to BridgeClient key so I can read it in a python program that renders HTML and displays using Python's bottle wsgi framework.
These lines produce errors:
Bridge.put(DEWPNTkey, dP);
Bridge.put(HEADINDXkey, hIinCel);
saying:
no matching function for call to 'SerialBridgeClass::put(String&, float&)'
The Bridge.put() method requires a char or a string as its second parameter. So we can use the String constructor to do that.
void setup()
{
Serial.begin(115200); // To test this make sure your serial monitor's baud matches this, or change this to match your serial monitor's baud rate.
double floatVal = 1234.2; // The value we want to convert
// Using String()
String arduinoString = String(floatVal, 4); // 4 is the decimal precision
Serial.print("String(): ");
Serial.println(arduinoString);
// You would use arduinoString now in your Bridge.put() method.
// E.g. Bridge.put("Some Key", arduinoString)
//
// In your case arduinoString would have been dP or hIinCel.
// In case you need it as a char* at some point
char strVal[arduinoString.length() + 1]; // +1 for the null terminator.
arduinoString.toCharArray(strVal, arduinoString.length() + 1);
Serial.print("String() to char*: ");
Serial.println(strVal);
}
void loop()
{
}
And we get:
String(): 1234.2000
String() to char*: 1234.2000
Go here to read about the null terminator.

Categories