I have a program that is supposed to take joystick position readings from an arduino (over serial), and translate them to mouse movements on my computer.
There is only one problem with this...
The string to integer conversion is way too slow, and it takes forever for the movements to register. I need either a faster way to convert a string to an integer value, or a way to skip the conversion altogether.
This is my current code:
import serial
import pyautogui
import time
ser = serial.Serial('COM3', 9600, timeout=1)
while True:
time.sleep(0.0001)
ser_bytes = ser.readline()
decoded_bytes = ser_bytes[0:len(ser_bytes)-2].decode("utf-8")
pos = decoded_bytes.split(':')
xpos = int(pos[0])
ypos = int(pos[1])
print("x:", xpos, " y:", ypos)
pyautogui.move(xpos, ypos)
Note: Output from arduino has 3 values:
0:0:0
First number: x
Second number: y
Third number: joystick button
Perhaps something like this will work? This way you can read more than one line of input for each time you call move(). Some lines of input will be ignored, but that seems necessary if you're getting input faster than you can use it.
import serial
import pyautogui
import time
ser = serial.Serial('COM3', 9600, timeout=1)
while True:
time_to_move = time.time() + 0.001
while True:
ser_bytes = ser.readline()
if time.time() >= time_to_move:
break
x_bytes, y_bytes = ser_bytes[:-2].split(b':')
x, y = int(x_bytes), int(y_bytes)
pyautogui.move(x, y)
Im having trouble reading the analog value of my Arduino using pyfirmata. Every time I ask to read the value of ('a:0:i) it will always return a random set of numbers, regardless of whether I have an analog pin plugged in or not.
import pyfirmata
board = pyfirmata.Arduino('COM4')
it = pyfirmata.util.Iterator(board)
it.start()
analog_read = board.get_pin('a:0:i')
while True:
print(analog_read.read())
time.sleep(2)
The output will return
0.3255
0.3255
0.3294
#and up
I am using a VB-8200 vibration meter for my project. It gives vibration measure in a 16 digit format. Eg. 000001????0004. This means vibration value is 0.4 mm/s. I want to write a python code for sending 0.4 to a text file after recieving it.
The code I have written is given below:
import string
import time
import serial
ser = serial.Serial(port='/dev/ttyS3', baudrate=9600, parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,bytesize=serial.EIGHTBITS,timeout=0.0005)try:
ser.isOpen()
print("serial port is open")
except:
print("error")
exit()
if(ser.isOpen()):
try:
while(1):
x=ser.readline().rstrip('\0')
print x
y= x[11:]
textdata = str(y)/10
f = open('/home/root/sensor_data.txt','a')
f.write(textdata+'\n')
time.sleep(1)
f.close()
except Exception:
print("error")
else:
print("cannot open serial terminal")
However it is not working. Can you help me in correcting this. When I am not doing /10 in, textdata = str(y)/10, it is working. But i need to send the data in integer format to text file, so that i can plot it in thingspeak.
In this line:
textdata = str(y)/10
you are attempting to divide a string by 10, which doesn't make sense. Since y is already a string (you obtained it above by stripping the first 11 characters from x), you first want to convert it to an integer:
iy = int(y)
That function will handle leading zeros for you.
You know that iy is the flow rate in units of 0.1mm/s. To get the flow rate in real units, you do:
flow_rate = iy * 0.1
To write integers to the file, just do:
f.write(str(iy) + '\n')
It won't have any leading zeros.
I am trying to basically write a simple script that will tell the fan (plugged into pin 4 on the GPIO) to turn on at a certain temp, and if anything less, turn the fan off. I am starting with something simple just to see if I can control the fan based on temperature. Here is what I have so far:
import os
from time import sleep
import signal
import sys
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.OUT)
temp = os.popen('vcgencmd measure_temp').readline()
if temp > 65:
GPIO.output(4, True)
else:
GPIO.output(4, False)
When I run this, it either appears to run but the fan doesn't turn off even though the temp hasn't nearly reached the level I wrote, or it will tell me that the pin is already in use but it will continue anyways. Either way, the fan still runs regardless of current temp.
vgencmd returns as:
temp-37.0'C
How would I remove the non-numerical characters so I am stuck with an int? When I execute it I get this:
ValueError: invalid literal for int() with base 10: "temp=37.6'C\n"
NOTE: Some of the imported modules aren't in use yet, that's why they are there.
You're very close. These two lines are problematic:
temp = os.popen('vcgencmd measure_temp').readline()
if temp > 65:
Here, temp is a string. You need to convert temp to an integer before trying to compare it against an integer. Assuming the line you're reading is just a decimal string corresponding to some temperature, you simply call int(), like this:
temp = os.popen('vcgencmd measure_temp').readline()
temp = int(temp)
Update Since you posted the output you're actually trying to parse, we can use regular expressions to match the output, with the re module. We'll also put this in a function:
def measure_temp():
raw = os.popen('vcgencmd measure_temp').readline()
m = re.match("temp=(\d+\.?\d*)'C", raw)
if not m:
raise ValueError("Unexpected temperature string: " + raw)
return float(m.group(1))
temp = measure_temp()
Note that I used a capture group around the actual temperature decimal in the string, and accessed it using m.group(1).
Let's put it together now. Also, when your code isn't doing what you expect, it is extremely helpful to include some "debug prints", like this:
def measure_temp():
raw = os.popen('vcgencmd measure_temp').readline()
m = re.match("temp=(\d+\.?\d*)'C", raw)
if not m:
raise ValueError("Unexpected temperature string: " + raw)
return float(m.group(1))
temp = measure_temp()
print 'Temperature from vcgencmd: {}'.format(temp)
if temp > 65:
print 'Turning on GPIO 4'
GPIO.output(4, True)
else:
print 'Turning off GPIO 4'
GPIO.output(4, False)
Once you get the basics working, there are a few other things you're going to run into:
Your script checks the temperature and toggles the GPIO once. If you want this thing to operate like a thermostat, you're going to need to keep doing these actions, using a while loop.
If your while loop runs very fast, and the temperature fluctuates right around your setpoint (65), you're going to find your code rapidly turning the fan on/off. It may help to add a bit of hysteresis to the system. For example, if you set your home thermostat (heating) to 70 degrees, it may come on at 69, but turn off at 71. Or it may simply not change states if it has already changed states within the last X seconds.
The simplest solution would be to sleep() for a short period of time between checks:
while True: # Loop forever
# Read the current temperature
temp = os.popen('vcgencmd measure_temp').readline()
temp = int(temp)
print 'Temperature from vcgencmd: {}'.format(temp)
# Control the fan
if temp > 65:
print 'Turning on GPIO 4'
GPIO.output(4, True)
else:
print 'Turning off GPIO 4'
GPIO.output(4, False)
# Wait before the next iteration
time.sleep(5)
I need to read a temperature reading from a DS18B20 sensor using Raspberry Pi 3 and Python.
The problem is the refresh rate of the sensor (~1 sec)
I need to read from sys/bus/w1/devices/28-041670f43bff/w1_slave and use the integer i get to display a temperature on a 7 segment display connected directly to my GPIOs (not using any hardware multiplexing - i2c....etc)
In order to display a two digit temperature, I need to turn on and off the digits really fast (faster than the sensor refreshes)
This is the small piece of code used to get the integer temperature:
def temperature():
with open ("/sys/bus/w1/devices/28-041670f43bff/w1_slave") as q:
r=q.read()
temp=r[69:71]
t=int (temp)
return t
But i need to call this function many times per second in order to get a good display on the 7 segment display.
This is how i thought of doing it:
#the temperature() function returns a two digit int
while True:
GPIO.output(31,0)
GPIO.output(temp[temperature()/10], 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temp[temperature()%10], 1)
time.sleep(0.0005)
GPIO.output(37,1)
But this code just makes one digit light up, wait ~1sec, light up the other digit, wait ~1sec.....and so on.
Any ideas of how to do this are very appreciated.
Rather than implement this functionality on your own, you should instead use the libraries out there that address this particular bit of your code inherently. In this case, I'd suggest you use W1ThermSensor. You can find the documentation at:
https://github.com/timofurrer/w1thermsensor
and you can install it using:
pip install w1thermsensor
It does support the DS18B20, and offers an exact analogue to your use case in the README.
From the docs for the package:
from w1thermsensor import W1ThermSensor
sensor = W1ThermSensor()
temperature_in_celsius = sensor.get_temperature()
temperature_in_fahrenheit = sensor.get_temperature(W1ThermSensor.DEGREES_F)
temperature_in_all_units = sensor.get_temperatures([
W1ThermSensor.DEGREES_C,
W1ThermSensor.DEGREES_F,
W1ThermSensor.KELVIN
])
In many cases, particularly for popular hardware devices, you'll find that there are libraries already available to use within python, and that will all you to quickly move on to writing the bits of code unique to your own particular needs.
Note: According to the technical discussion in the following link, if the DS18B20 is set to 12-bit temperature resolution, the temperature conversion will take 750 ms, or 3/4 of a second. If you set the hardware to do 9-bit resolution, the conversion time in hardware is 93.75 ms. I suspect this is the root of your once-per-second issue.
https://www.maximintegrated.com/en/app-notes/index.mvp/id/4377
There is some discussion of this issue in this Question:
https://raspberrypi.stackexchange.com/questions/14278/how-to-change-ds18b20-reading-resolution
See the second Answer, regarding the configDS18B20 utility.
With the resolution set to 9-bit, you may be able to adjust the w1thermsensor RETRY_DELAY_SECONDS / RETRY_ATTEMPTS value combination in the source code and get what you need. It's unclear to me if the retry delay has any affect on the actual polling of the device. It looks like it is there for device finding. Though, as I said, that interval may impact polling a single device. I simply didn't read through the source code enough to see when and where it comes into play.
Happy New Year!
I'd throw the display routine into its own thread so that you don't have to think about it in your main loop. The code below should demonstrate this concept. Set "testing" to False to see if it works with your hardware.
#!/usr/bin/python
import time
import threading
import Queue
import random
# Set this to False to read the temperature from a real sensor and display it on a 7-digit display.
testing = True
def temperature_read(q):
# Read the temperature at one second intervals.
while True:
if testing:
r = '-' * 69 + '%02d' % (random.randrange(100)) + 'blahblah' * 4
else:
r = open('/sys/bus/w1/devices/28-041670f43bff/w1_slave', 'r').read()
print r
# The temperature is represented as two digits in a long string.
# Push the digits into the queue as a tuple of integers (one per digit).
q.put((int(r[69]), int(r[70])))
# Wait for next reading.
# (Will w1_slave block until the next reading? If so, this could be eliminated.)
time.sleep(1.0)
def temperature_display(q):
# Display the temperature.
# Temperature is two digits, stored separately (high/low) for more efficient handling.
temperature_h = temperature_l = 0
while True:
# Is there a new temperature reading waiting for us?
if not q.empty():
temperature = q.get()
# If it's None, we're done.
if temperature is None:
break
# Load the two digits (high and low) representing the temperature.
(temperature_h, temperature_l) = temperature
if testing:
print 'displayH', temperature_h
time.sleep(0.05)
print 'displayL', temperature_l
time.sleep(0.05)
else:
GPIO.output(31,0)
GPIO.output(temperature_h, 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temperature_l, 1)
time.sleep(0.0005)
GPIO.output(37,1)
# Clean up here. Turn off all pins?
# Make a queue to communicate with the display thread.
temperature_queue = Queue.Queue()
# Run the display in a separate thread.
temperature_display_thread = threading.Thread(target=temperature_display, args=(temperature_queue,))
temperature_display_thread.start()
# Run the reader.
try:
temperature_read(temperature_queue)
except:
# An uncaught exception happened. (It could be a keyboard interrupt.)
None
# Tell the display thread to stop.
temperature_queue.put(None)
# Wait for the thread to end.
temperature_display_thread.join()
To support another reading (transmission), I just put it in the read loop rather than adding another thread for it. I changed the queue so that you could easily move it to another thread but I suspect you'll add more inputs so this is probably a reasonable way to do it unless the read frequency of one needs to be much different. (Even then, you could do things with counters in the loop.)
#!/usr/bin/python
import time
import threading
import Queue
import random
# Set this to False to read the temperature from a real sensor and display it on a 7-digit display.
testing = True
def observe(q):
while True:
# Make a temperature reading.
if testing:
r = '-' * 69 + '%02d' % (random.randrange(100)) + 'blahblah' * 4
else:
r = open('/sys/bus/w1/devices/28-041670f43bff/w1_slave', 'r').read()
print 'temperature ->', r
# The temperature is represented as two digits in a long string.
# Push the digits into the queue as a tuple of integers (one per digit).
q.put(('temperature', int(r[69]), int(r[70])))
# Make a transmission reading.
if testing:
r = random.randrange(1,6)
else:
r = 0 # Put your transmission reading code here.
print 'transmission ->', r
q.put(('transmission', r))
# Wait for next reading.
# (Will w1_slave block until the next reading? If so, this could be eliminated.)
time.sleep(1.0)
def display(q):
# Display the temperature.
# Temperature is two digits, stored separately (high/low) for more efficient handling.
temperature_h = temperature_l = transmission = 0
while True:
# Is there a new temperature reading waiting for us?
if not q.empty():
reading = q.get()
# If it's None, we're done.
if reading is None:
break
elif reading[0] == 'temperature':
# Load the two digits (high and low) representing the temperature.
(x, temperature_h, temperature_l) = reading
elif reading[0] == 'transmission':
(x, transmission) = reading
if testing:
print 'displayH', temperature_h
time.sleep(0.05)
print 'displayL', temperature_l
time.sleep(0.05)
print 'transmission', transmission
time.sleep(0.05)
else:
GPIO.output(31,0)
GPIO.output(temperature_h, 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temperature_l, 1)
time.sleep(0.0005)
GPIO.output(37,1)
# Clean up here. Turn off all pins?
# Make a queue to communicate with the display thread.
readings_queue = Queue.Queue()
# Run the display in a separate thread.
display_thread = threading.Thread(target=display, args=(readings_queue,))
display_thread.start()
# Observe the inputs.
try:
observe(readings_queue)
except:
# An uncaught exception happened. (It could be a keyboard interrupt.)
None
# Tell the display thread to stop.
readings_queue.put(None)
# Wait for the thread to end.
display_thread.join()
Here's a version which only reads the temperature every tenth time but reads the transmission every time. I think you'll see how to easily tweak this to meet your needs.
I would make separate threads for each reader but it would complicate the thread management quite a bit.
#!/usr/bin/python
import time
import threading
import Queue
import random
# Set this to False to read the temperature from a real sensor and display it on a 7-digit display.
testing = True
def observe(q):
count = 0
while True:
# Only read the temperature every tenth time.
if (count % 10 == 0):
# Make a temperature reading.
if testing:
r = '-' * 69 + '%02d' % (random.randrange(100)) + 'blahblah' * 4
else:
r = open('/sys/bus/w1/devices/28-041670f43bff/w1_slave', 'r').read()
print 'temperature ->', r
# The temperature is represented as two digits in a long string.
# Push the digits into the queue as a tuple of integers (one per digit).
q.put(('temperature', int(r[69]), int(r[70])))
# Make a transmission reading.
if testing:
r = random.randrange(1,6)
else:
r = 0 # Put your transmission reading code here.
print 'transmission ->', r
q.put(('transmission', r))
# Wait for next reading.
if testing:
time.sleep(0.5)
else:
time.sleep(0.1)
count += 1
def display(q):
# Display the temperature.
# Temperature is two digits, stored separately (high/low) for more efficient handling.
temperature_h = temperature_l = transmission = 0
while True:
# Is there a new temperature reading waiting for us?
if not q.empty():
reading = q.get()
# If it's None, we're done.
if reading is None:
break
elif reading[0] == 'temperature':
# Load the two digits (high and low) representing the temperature.
(x, temperature_h, temperature_l) = reading
elif reading[0] == 'transmission':
(x, transmission) = reading
if testing:
print 'displayH', temperature_h
time.sleep(0.05)
print 'displayL', temperature_l
time.sleep(0.05)
print 'transmission', transmission
time.sleep(0.05)
else:
GPIO.output(31,0)
GPIO.output(temperature_h, 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temperature_l, 1)
time.sleep(0.0005)
GPIO.output(37,1)
# Clean up here. Turn off all pins?
# Make a queue to communicate with the display thread.
readings_queue = Queue.Queue()
# Run the display in a separate thread.
display_thread = threading.Thread(target=display, args=(readings_queue,))
display_thread.start()
# Observe the inputs.
try:
observe(readings_queue)
except:
# An uncaught exception happened. (It could be a keyboard interrupt.)
None
# Tell the display thread to stop.
readings_queue.put(None)
# Wait for the thread to end.
display_thread.join()