Arduino-Python Serial Communication HC-SR04 - python

I'm trying to measure distance using HC-SR04 then writing it to python's monitor. The first output that python shows is true but second and the other outputs looks like python read two parts of the distance value. I think, i am having some timing issues but i tried to change delays in two of them and that didn't work.
When I run the code output looks like this:
C:\Users\mobyr\PycharmProjects\1\venv\Scripts\python.exe C:/Users/mobyr/Desktop/mesafeolc.py
distance 2.18 m
distance
2. m
distance 18
m
Process finished with exit code 0
I get the true values on
Arduino's Serial Monitor
2.18
2.19
2.18
2.17
2.17
2.17
2.18
2.20
2.17
Python Codes:
import serial
import time
arduino = serial.Serial('COM3', 9600)
def Measure():
distance = arduino.read(4)
time.sleep(1)
print ("distance "+distance+" m")
return float(distance)
while True:
output = Measure()
if output > 5:
break
Arduino Codes:
const int echo_pin = 9;
const int trig_pin = 10;
void setup() {
Serial.begin(9600);
pinMode(echo_pin, INPUT);
pinMode(trig_pin, OUTPUT);
}
void loop() {
double period, distance;
digitalWrite(trig_pin, LOW);
delayMicroseconds(2);
digitalWrite(trig_pin, HIGH);
delayMicroseconds(10);
digitalWrite(trig_pin, LOW);
period = pulseIn(echo_pin, HIGH);
distance = (period / 2) / 29.1;
distance = distance / 100; //to convert cm to m
delay(500);
Serial.println(distance);
}

The line distance = arduino.read(4) reads 4 chars, but the output you are after is actually 5 chars, cause the linebreak is also counted as a char.
I would recommend using distance = arduino.readline() instead, then you can handle values of any size.

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.

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.

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.

.append() method with pyserial and arduino gives me ridiculous output

I' m basically trying to get voltage from arduino, and make an array from them.
Arduino code is below:
void setup() {
Serial.begin(9600);
}
void loop() {
int sensorValue = analogRead(A0);
float voltage = sensorValue*(5.0/1023);
Serial.println(voltage);
}
On serial monitor, everything seems fine.
When i try to make an array with python, used this code:
import serial
ser = serial.Serial("COM5", 9600)
voltage = []
while True:
volt = ser.readline()
voltage.insert(volt)
print voltage
Just gave me something but not an array from voltage measurements. I also tried .apppend() and .extend(), same results.

Writing data to arduino python

I'm trying to write data from python to serial with this sketch
import serial
import smtplib
import time
s=serial.Serial('/dev/tty.usbserial-AH01LLHS',9600)
precx=500
ist=30
i=0
while True:
s.write('1')
stringa=s.readline()
array=stringa.split(',')
x=int(array[0])
y=int(array[1])
z=int(array[2])
print(x,y,z)
if(precx!=500):
diffx=abs(x-precx)
diffy=abs(y-precy)
diffz=abs(z-precz)
if((diffx>ist)|(diffy>ist)|(diffz>ist)):
print('Ohu god',i)
i+=1
#time.sleep(2)
precx=x
precy=y
precz=z
But when I execute the program I see nothing on the console, and the Arduino Rx led is off.
This is my Arduino sketch (nano atmega 328):
//Add the SPI library so we can communicate with the ADXL345 sensor
#include <SPI.h>
//Assign the Chip Select signal to pin 10.
int CS=10;
//This is a list of some of the registers available on the ADXL345.
//To learn more about these and the rest of the registers on the ADXL345, read the datasheet!
char POWER_CTL = 0x2D; //Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32; //X-Axis Data 0
char DATAX1 = 0x33; //X-Axis Data 1
char DATAY0 = 0x34; //Y-Axis Data 0
char DATAY1 = 0x35; //Y-Axis Data 1
char DATAZ0 = 0x36; //Z-Axis Data 0
char DATAZ1 = 0x37; //Z-Axis Data 1
//This buffer will hold values read from the ADXL345 registers.
char values[10];
//These variables will be used to hold the x,y and z axis accelerometer values.
int x,y,z;
boolean snd;
void setup(){
//Initiate an SPI communication instance.
SPI.begin();
//Configure the SPI connection for the ADXL345.
SPI.setDataMode(SPI_MODE3);
//Create a serial connection to display the data on the terminal.
Serial.begin(9600);
//Set up the Chip Select pin to be an output from the Arduino.
pinMode(CS, OUTPUT);
//Before communication starts, the Chip Select pin needs to be set high.
digitalWrite(CS, HIGH);
//Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
writeRegister(DATA_FORMAT, 0x01);
//Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
writeRegister(POWER_CTL, 0x08); //Measurement mode
}
void loop(){
//snd=true;
//Reading 6 bytes of data starting at register DATAX0 will retrieve the x,y and z acceleration values from the ADXL345.
//The results of the read operation will get stored to the values[] buffer.
readRegister(DATAX0, 6, values);
//The ADXL345 gives 10-bit acceleration values, but they are stored as bytes (8-bits). To get the full value, two bytes must be combined for each axis.
//The X value is stored in values[0] and values[1].
x = ((int)values[1]<<8)|(int)values[0];
//The Y value is stored in values[2] and values[3].
y = ((int)values[3]<<8)|(int)values[2];
//The Z value is stored in values[4] and values[5].
z = ((int)values[5]<<8)|(int)values[4];
if (Serial.available() > 0) {
if(Serial.read()=='1'){
snd=true;
}
else{
snd=false;
}
}
if(snd){
//Print the results to the terminal.
Serial.print(x, DEC);
Serial.print(',');
Serial.print(y, DEC);
Serial.print(',');
Serial.print(z, DEC);
Serial.println("");
delay(10);
}
}
//This function will write a value to a register on the ADXL345.
//Parameters:
// char registerAddress - The register to write a value to
// char value - The value to be written to the specified register.
void writeRegister(char registerAddress, char value){
//Set Chip Select pin low to signal the beginning of an SPI packet.
digitalWrite(CS, LOW);
//Transfer the register address over SPI.
SPI.transfer(registerAddress);
//Transfer the desired register value over SPI.
SPI.transfer(value);
//Set the Chip Select pin high to signal the end of an SPI packet.
digitalWrite(CS, HIGH);
}
//This function will read a certain number of registers starting from a specified address and store their values in a buffer.
//Parameters:
// char registerAddress - The register addresse to start the read sequence from.
// int numBytes - The number of registers that should be read.
// char * values - A pointer to a buffer where the results of the operation should be stored.
void readRegister(char registerAddress, int numBytes, char * values){
//Since we're performing a read operation, the most significant bit of the register address should be set.
char address = 0x80 | registerAddress;
//If we're doing a multi-byte read, bit 6 needs to be set as well.
if(numBytes > 1)address = address | 0x40;
//Set the Chip select pin low to start an SPI packet.
digitalWrite(CS, LOW);
//Transfer the starting register address that needs to be read.
SPI.transfer(address);
//Continue to read registers until we've read the number specified, storing the results to the input buffer.
for(int i=0; i<numBytes; i++){
values[i] = SPI.transfer(0x00);
}
//Set the Chips Select pin high to end the SPI packet.
digitalWrite(CS, HIGH);
}
Thanks in advance.
You should definitely check the suggestions from the comments:
make the program simpler so that it always sends values without "flow control" (using the "1" byte you send),
use a serial monitor to check the output.
First, you shall check if you add a s.flush() before and after your s.write('1') helps. Then you may want to add flush() in your arduino code too.
To me it really looks like a starving issue at the initialization of your "flow control" algorithm. On the paper it looks good, but in real life, it may not. Very often, the Arduino sends a lot of garbage when it starts filling up the host serial port's buffer. And the other way around may be true. So instead of having a 1 on the arduino side, you may get a !##$##%##%!##$ that's totally useless.
So on either side, before getting in the loops, you should add an init protocol. The arduino sends READY, the host sends READY, you flush out everything until you get READY on both sides. Something that would look like:
void setup() {
Serial.begin(9600);
Serial.println('READY');
Serial.flush();
char ch1 = 0, ch2 = 0;
char incomingByte = 0;
while (ch1 != 'O' && ch2 != 'K') {
ch1 = Serial.read();
if (ch1 == 'O' && ch2 == 'K')
break;
ch2 = Serial.read();
}
}
and on the python side:
with serial.Serial('/dev/tty.usbserial-AH01LLHS',9600) as s:
s.flush()
ready = ""
while ready != "READY":
ready = s.readline()
s.flush()
while True:
# ... you stuff
of course that's only an idea of what you may do. You can make it more sophisticated, or way more simple. But before making such flow control, you really need to create a meeting point.

Categories