Python Code for RPM-measurement with Raspberry Pi - python

I'm working on a project where I use a photoelectric sensor to detect a reflector attached to a motor axis. Every time the reflector reflects the light from the LED from the sensor it sends a pulse (voltage) to the raspberry pi (with a voltage divider). With some help I've come to the following code and I've noticed that the results that I'm getting are way too high. Anybody got any suggestions on how to improve it? I've seen other examples of RPM code online, but I wanted to learn it myself and the ones that I've found did not use the same method as me. Thanks in advance!
import time
from gpiozero import Button
sensor = Button(17)
i=1
timestampeven=0
timestamponeven=0
def pulsen():
global i
global timestampeven
global timestamponeven
if (i % 2) ==0:
timestampeven = time.time_ns()
i+=1
elif (i % 2) == 1:
timestamponeven = time.time_ns()
i+=1
periode = timestamponeven-timestampeven
frequentie = 1/(periode*10e09)
rpm = frequentie*60
print("rpm=" + str(rpm))
print("teller = " +str(i))
print("periode = " +str(periode))
print("frequentie = " +str(frequentie))
sensor.when_pressed = pulsen
while True:
pass

Related

Full Weather Station with Raspberry Pi 3 the hard questions writing it with Python

I'm trying to establish a weather station with a Raspberry Pi 3
the code will be written in Python
the Raspberry Pi will be connected directly with an Adafruit DHT-22 Sensor
a BMP-180 Sensor and a DS18B20 Sensor
the remote station will use an ESP-8266
and also the Raspberry Pi will be taking measurements from the Open Weather Maps
there's going to be a GUI and also the measurements will be displayed on Thinkspeak (for the IOT part)
My problem now
I want to display all the measurements from the sensors but also I want to display the difference from the ESP-8266 Sensor and the Open Weather Maps
#something like this#
"the difference in temperature from the station and the satellite is ..."
#or#
"the measurements are equal"
is there a way to create a command in Python that does just that
because Pycharm wasn't happy with my code ...
import Adafruit_DHT
import pyowm
DHT22Sensor = Adafruit_DHT.DHT22
DHTpin = 16
humidity_indoors, temperature_indoors = Adafruit_DHT.read_retry(DHT22Sensor, DHTpin)
degree_sign = u'\N{DEGREE SIGN}'
owm = pyowm.OWM('')
observation = owm.weather_at_place('Larissa, GR')
weather_sat = observation.get_weather()
status_sat = weather.get_detailed_status()
temperature_sat = weather.get_temperature('celsius')['temp']
wind_sat = weather.get_wind('miles_hour')['speed']
humidity_sat = weather.get_humidity()
def mphtokmh(wind):
kmh = (float)(wind * 1.60)
return kmh
if humidity_indoors is not None and temperature_indoors is not None :
print('Temp={0:0.1 f} * C Humidity={1:0.1 f}%' .format(temperature_indoors, humitity_indoors))
else :
print('Failed to get reading, Try Again!')
print("The Sky is...{}".format(status_sat))
print("the temperature in our town is...{}C".format(temperature_sat))
print("the windspeed is...{}Km/h".format(wind_sat))
print("humid is...{}"%.format(humitity_sat))
if temperature_sat==temperature_indoors
print("No Difference to show")
if temperature_sat>temperature_indoors
print(temperature_sat)
if temperature_sat<temperature_indoors
print(temperature_indoors)
(ps this is just a test code)

Micro Switch with pyserial RS232 starts/stops a timer in a tkinter thread but continues to run even when stopped

I have been using a micro switch connected to an RS232/USB serial converter cable on my windows PC to start stop and reset a timer.
The program runs smoothly most of the time but every so often updating the timer widget gets stuck running and the timer will not stop.
With the serial protocol i want to receive 1 byte b'\x00' for off and anything that's not b'\x00' should signify on.
I have replaced the micro switch with button widgets to simulate the switch and don't get the same error or i just have not kept at it for long enough.
It could be an issue with the RS232 causing an error i cannot see but my knowledge on this is sketchy and have exhausted all avenues looking online for any information on this.
import time
import sys
import serial
import threading
from tkinter import *
from tkinter import ttk
class Process(Frame):
def __init__(self, root, parent=None, **kw):
Frame.__init__(self, parent, kw)
self.root = root
self._cycStart = 0.0
self._cycTimeElapsed = 0.0
self._cycRunning = 0.0
self.cycTimeStr = StringVar()
self.cycTime_label_widget()
self.ser = serial.Serial(
port='COM4',
baudrate=1200,
timeout=0
)
self.t1 = threading.Thread(target=self.start_stop, name='t1')
self.t1.start()
def initUI(self):
root.focus_force()
root.title("")
root.bind('<Escape>', lambda e: root.destroy())
def cycTime_label_widget(self):
# Make the time label
cycTimeLabel = Label(root, textvariable=self.cycTimeStr, font=
("Ariel 12"))
self._cycleSetTime(self._cycTimeElapsed)
cycTimeLabel.place(x=1250, y=200)
cycTimeLabel_2 = Label(root, text="Cycle Timer:", font=("Ariel
12"))
cycTimeLabel_2.place(x=1150, y=200)
def _cycleUpdate(self):
""" Update the label with elapsed time. """
self._cycTimeElapsed = time.time() - self._cycStart
self._cycleSetTime(self._cycTimeElapsed)
self._cycTimer = self.after(50, self._cycleUpdate)
def _cycleSetTime(self, elap):
""" Set the time string to Minutes:Seconds:Hundreths """
minutes = int(elap/60)
seconds = int(elap - minutes*60.0)
hseconds = int((elap - minutes*60.0 - seconds)*100)
self.cycTimeStr.set('%02d:%02d:%02d' % (minutes, seconds,
hseconds))
return
def cycleStart(self):
""" Start the stopwatch, ignore if running. """
if not self._cycRunning:
self._cycStart = time.time() - self._cycTimeElapsed
self._cycleUpdate()
self._cycRunning = 1
else:
self.cycleReset()
def cycleStop(self):
""" Stop the stopwatch, ignore if stopped. """
if self._cycRunning:
self.after_cancel(self._cycTimer)
self._cycTimeElapsed = time.time() - self._cycStart
self._cycleSetTime(self._cycTimeElapsed)
self._cycRunning = 0
self._cycTimeElapsed = round(self._cycTimeElapsed, 1)
self.cycleTimeLabel = Label(root, text=(self._cycTimeElapsed,
"seconds"), font=("Ariel 35"))
self.cycleTimeLabel.place(x=900, y=285)
self.cycleReset()
def cycleReset(self):
""" Reset the stopwatch. """
self._cycStart = time.time()
self._cycTimeElapsed = 0
self._cycleSetTime(self._cycTimeElapsed)
def start_stop(self):
while True :
try:
data_to_read = self.ser.inWaiting()
if data_to_read != 0: # read if there is new data
data = self.ser.read(size=1).strip()
if data == bytes(b'\x00'):
self.cycleStop()
print("Off")
elif data is not bytes(b'\x00'):
self.cycleStart()
print("On")
except serial.SerialException as e:
print("Error")
if __name__ == '__main__':
root = Tk()
application = Process(root)
root.mainloop()
I expect the timer to start running when the micro switch is pressed. when depressed it should stop and reset back to zero and wait for the next press
With a better understanding of what you're trying to do better solutions come to mind.
As it turns out, you're not using your serial port to send or receive serial data. What you're actually doing is wiring a switch to its RX line and toggling it manually with a mechanical switch, feeding a high or low level depending on the position of the switch.
So what you're trying to do is emulating a digital input line with the RX line of your serial port. If you take a look a how a serial port works you'll see that when you send a byte the TX line toggles from low to high at the baud rate, but on top of the data you have to consider the start and stop bits. So, why your solution works (at least sometimes): that's easy to see when you look at a scope picture:
This is a screenshot of the TX line sending the \x00 byte, measured between pins 3 (TX) and 5 (GND) with no parity bit. As you can see the step only lasts for 7.5 ms (with a 1200 baud rate). What you are doing with your switch is something similar but ideally infinitely long (or until you toggle your switch back, which will be way after 7.5 ms no matter how fast you do it). I don't have a switch to try but if I open a terminal on my port and use a cable to shortcircuit the RX line to pin 4 (on a SUB-D9 connector) sometimes I do get a 0x00 byte, but mostly it's something else. You can try this experiment yourself with PuTTy or RealTerm and your switch, I guess you'll get better results but still not always the byte you expect because of the contacts bouncing.
Another approach: I'm sure there might be ways to improve on what you have, maybe reducing the baud rate to 300 or 150 bps, checking for a break in the line or other creative ideas.
But what you're trying to do is more akin to reading a GPIO line, and actually, the serial port has several digital lines intended (in the old days) for flow control.
To use these lines you should connect the common pole on your switch to the DSR line (pin 6 on a SUB-D9) and the NO and NC poles to lines DTR (pin 4) and RTS (pin 7).
The software side would be actually simpler than reading bytes: you just have to activate hardware flow control :
self.ser = serial.Serial()
self.ser.port='COM4'
self.ser.baudrate=1200 #Baud rate does not matter now
self.ser.timeout=0
self.ser.rtscts=True
self.ser.dsrdtr=True
self.ser.open()
Define the logical levels for your switch:
self.ser.setDTR(False) # We use DTR for low level state
self.ser.setRTS(True) # We use RTS for high level state
self.ser.open() # Open port after setting everything up, to avoid unkwnown states
And use ser.getDSR() to check the logical level of the DSR line in your loop:
def start_stop(self):
while True :
try:
switch_state = self.ser.getDSR()
if switch_state == False and self._cycRunning == True:
self.cycleStop()
print("Off")
elif switch_state == True and self._cycRunning == False:
self.cycleStart()
print("On")
except serial.SerialException as e:
print("Error")
I defined your self._cycRunning variable as boolean (in your initialization code you had defined it as float, but that was probably a typo).
This code works with no glitches at all even using a stripped wire as a switch.
You don't explain very well how your protocol works (I mean what is your switch supposed to be sending, or if it's sending a state change only once or several times or continuously).
But there are some red flags on your code anyway:
-With data = self.ser.read(size=1).strip() you read 1 byte but immediately you check if you have received 2 bytes. Is there a reason to do that?
-Your timer stop condition works comparing with the NULL character. That should not be a problem, but depending on your particular configuration it might (in some configurations the NULL character is read as something else, so it's wise to make sure you're really receiving it correctly).
-Your timer start condition seems too loose. Whatever you receive on the port, if it's one byte, you start your timer. Again, I don't know if that's the way your protocol works but it seems prone to trouble.
-When you replace your hardware switch with a software emulation it works as intended, but that is not surprising since you're probably imposing the condition. When you read from the serial port you have to deal with real world issues like noise, communication errors or the switch bouncing back and forth from ON to OFF. Maybe for a very simple protocol you don't need to use any error checking method, but it seems wise to at least check for parity errors. I'm not completely sure it would be straight-forward to do that with pyserial; on a quick glance I found this issue that's been open for a while.
-Again, the lack of info on your protocol: should you be using XON-XOFF flow control and two stop bits? I guess you have a reason to do it, but you should be very aware of why and how you're using those.
EDIT: With the comments below I can try to improve a bit my answer. This is just an idea for you to develop: instead of making the stop condition comparing exactly with 0x00 you can count the number of bits set to 1 and stop the counter if it's less or equal to 2. That way you can account for bits that are not received correctly.
You can do the same with the start condition but I don't know what hex value you send.
Credits for the bit counting function go to this question.
...
def numberOfSetBits(i):
i = i - ((i >> 1) & 0x55555555)
i = (i & 0x33333333) + ((i >> 2) & 0x33333333)
return (((i + (i >> 4) & 0xF0F0F0F) * 0x1010101) & 0xffffffff) >> 24
def start_stop(self):
while True :
try:
data_to_read = self.ser.inWaiting()
if data_to_read != 0: # read if there is new data
data = self.ser.read(size=1).strip()
if numberOfSetBits(int.from_bytes(data, "big")) <= 2:
self.cycleStop()
print("Off")
elif numberOfSetBits(int.from_bytes(data, "big")) >= 3: #change the condition here according to your protocol
self.cycleStart()
print("On")
except serial.SerialException as e:
print("Error")

How to play a sound when a value increases by 1 between loops? What does the code look like that identifies the increment by 1?

I have designed and created a program using Python 3 that reads unread messages in my Gmail inbox under two labels.
By using tkinter I have two lovely boxes that display the total messages in each label. One for sales of one particular product and the other for another.
They use the update loop to recheck each label every few seconds.
Then after the business day, I use a cleanup script in Gmail that flushes the inboxes two labels.
The program is for use on my team's sales floor. They can see daily, the number of sales, and get a real-time readout to the success of certain marketing campaigns. It has done wonders for morale.
Now I would like to implement some sounds when sales go up. A cliche bell ring, a "chhaaaching" perhaps, you get the drift.
So, I am currently tackling with my limited knowledge and have searched all throughout StackOverflow and other sites for an answer. My guess is that I need something like the following...
"if an integer value changes on the next loop from it's previous value, by an increment of 1 play soundA, or, play soundB if that value decreases by 1."
I can't for the life of me figure out what would be the term for 'increases by 1', I am also clueless on how to attach a sound to any changes made to the integer on the proceeding loop. Help!!
If I wasn't clear enough, I am more than happy to explain and go into this further.
Thank you so much guys.
Here is my code as it stands so far...
#! /usr/bin/python3
import imaplib
import email
import tkinter as tk
WIDTH = 1000
HEIGHT = 100
def update():
mail=imaplib.IMAP4_SSL('imap.gmail.com',993)
mail.login('email#gmail.com','MyPassword')
mail.select("Submissions")
typ, messageIDs = mail.search(None, "UNSEEN")
FirstDataSetSUBS = str(messageIDs[0], encoding='utf8')
if FirstDataSetSUBS == '':
info1['text'] = 'no submissions'
else:
SecondDataSetSUBS = FirstDataSetSUBS.split(" ")
nosubs = len(SecondDataSetSUBS)
nosubs = int(nosubs)
info1['text'] = '{} submission[s]'.format(nosubs)
subs.after(1000, update)
def update_2():
mail=imaplib.IMAP4_SSL('imap.gmail.com',993)
mail.login('email#gmail.com','MyPassword')
mail.select("Memberships")
typ, messageIDs = mail.search(None, "UNSEEN")
FirstDataSetMSGS = str(messageIDs[0], encoding='utf8')
if FirstDataSetMSGS == '':
info2['text'] = 'no memberships'
else:
SecondDataSetMSGS = FirstDataSetMSGS.split(" ")
memberships = len(SecondDataSetMSGS)
memberships = int(memberships)
info2['text'] = '{} membership[s]'.format(memberships)
membs.after(1000, update_2)
membs = tk.Tk()
subs = tk.Tk()
membs.title('memberships counter')
membs.configure(background="black")
subs.title('submissions counter')
subs.configure(background="black")
x = (subs.winfo_screenwidth()//5) - (WIDTH//5)
y = (subs.winfo_screenheight()//5) - (HEIGHT//5)
subs.geometry('{}x{}+{}+{}'.format(WIDTH, HEIGHT, x, y))
info1 = tk.Label(subs, text='nothing to display', bg="black", fg="green", font="Lucida_Console 40")
info1.pack()
x = (membs.winfo_screenwidth()//2) - (WIDTH//2)
y = (membs.winfo_screenheight()//2) - (HEIGHT//2)
membs.geometry('{}x{}+{}+{}'.format(WIDTH, HEIGHT, x, y))
info2 = tk.Label(membs, text='nothing to display', bg="black", fg="red", font="Lucida_Console 40")
info2.pack()
update()
update_2()
membs.mainloop
subs.mainloop()
for playing audio you can use pyaudio module
in ubuntu, install by pip3 install pyaudio include sudo if required
import pygame
pygame.init()
increased=0
inc_music=pygame.mixer.music
dec_music=pygame.mixer.music
inc_music.load("/home/pratik/Documents/pos.wav")
dec_music.load("/home/pratik/Documents/neg.wav")
def get_inc():
global increased
a=increased
return a
def pl():
global inc_music
global dec_music
while True:
increased=get_inc()
if increased == 1:
inc_music.play()
increased=increased-1
elif increased == -1:
dec_music.play()
increased=increased+1
here increased is a global variable. Make sure whenever your sales is increased it is set to +1 and whenever it is decreased it is set to -1 and call pl function in a separate thread by using threading library in the background. Since, it is a forever loop it will continuosly run in background and ring the bells.
from threading import Thread
th=Thread(target=pl)
th.setDaemon(True)
th.start()
While writing the above I assumed at a moment there is either an increase or a decrease in sales, both dont occour simultaneously. If they do, use another global variable decreased which is also initialised with 0 and decreased by -1 each time decrease in sales. And return both of them from get_inc() .
Below code produces a label that gets updated randomly, and plays the sound files located in "C:\Windows\Media\" based on the change:
import tkinter as tk
import random
from playsound import playsound
def sound(is_positive=True):
if is_positive:
playsound(r"C:\Windows\Media\chimes.wav", False)
else:
playsound(r"C:\Windows\Media\chord.wav", False)
def random_update():
_old = label['text']
_new = random.randint(1, 100)
if _new > _old: # use (_new - _old) == 1 for checking
sound() # increment of 1 exclusively
elif _new < _old: # use (_new - _old) == -1 for checking
sound(False) # decrement of 1 exclusively
label['text'] = _new
label.after(2000, random_update)
if __name__ == '__main__':
root = tk.Tk()
label = tk.Label(root, text=1)
label.after(2000, random_update)
label.pack()
root.mainloop()
playsound is not a built-in package so it needs to be installed separately. You can install using:
pip install playsound
or:
python -m pip install playsound
in the command prompt on windows.

python run a scapy function as a background process

I would like to know in any way that i can run a function as a background process.
I have gone through reading threading but i am still unclear on how to implement it.
In my system , the scapy detection script run when i click on the Button.But then the system will be hang as the scapy function is running.
What i want to achieve is that when i click on the button that initiate the scapy detection script , i would like to be able to do other function in the same system.(general idea was to prevent the system from hang)
def startMonitor(self,event):
selectedInterface = self.interfaces_cblist.GetValue()
#selectInterfaceStr = str(selectedInterface)
if len(selectedInterface) == 0:
noInterfaceSelected = wx.MessageDialog(None,"Please select an interface","",wx.ICON_ERROR)
noInterfaceSelected.ShowModal()
else:
#confirmMonitor = wx.MessageDialog(None,"Monitoring Start on %s interface"%selectedInterface,"",wx.OK)
#confirmMonitor.ShowModal()
x = selectedInterface
thread.start_new_thread(self.camtableDetection(x))
def camtableDetection(self,a):
global interface
interface = str(a)
THRESH=(254/4)
START = 5
def monitorPackets(p):
if p.haslayer(IP):
hwSrc = p.getlayer(Ether).src
if hwSrc not in hwList:
hwList.append(hwSrc)
delta = datetime.datetime.now() - start
if((delta.seconds > START) and ((len(hwList)/delta.seconds) > THRESH)):
camAttackDetected = wx.MessageDialog(None,"Cam Attack Detected","",wx.ICON_ERROR)
camAttackDetected.ShowModal()
hwList = []
start = datetime.datetime.now()
sniff(iface=interface,prn=monitorPackets)

Use only the most recent value read from an Arduino in a python script, not the values stored in the buffer?

I have been working on a project using Python to read values from an arduino and then control video cameras. The Arduino controls two ultrasonic sensors and reports distance in cm. The python script then reads the distances from the Arduino using ser.readline(). When the script reads values outside the range everything works fine. However if it goes into the loop for the distance inside the required range it works correctly once and then reads old values from the Arduino instead of current "live" values which causes it to continue the record loop instead of exiting the loop. What can I do to get rid of the old values in the buffer and only read the most current value? I have found several methods and tested them but so far no luck.
Here is the code I am using (i know its not well written but its my first try using python and writing code outside of matlab)
import sys, time
import serial
import cv
import os
from time import strftime
#Create window for Camera 0
cv.NamedWindow("Camera 0", cv.CV_WINDOW_AUTOSIZE)
capture0 = cv.CreateCameraCapture(2)
cv.ResizeWindow("Camera 1", 640, 480)
cv.MoveWindow("Camera 0", 0, 0)
#Create window for Camera 1
cv.NamedWindow("Camera 1", cv.CV_WINDOW_AUTOSIZE)
capture1 = cv.CreateCameraCapture(1)
cv.MoveWindow("Camera 1", 150, 150)
#Initialize connection to Arduino
arduino = serial.Serial('COM12', 9600)
connected = False
#Confirm that Arduino is connected and software is able to read inputs
while not connected:
serin = arduino.readline()
connected = True
f = 'Sensor Connected'
print f
'''#Dummy variables for testing
value1 = 145
value2 = 30'''
#Initialize video record on as false (0)
vid = 0
#Initialize counter
counter_vid = 0
counter = 0
Accel = 1
def Camera0():
frame0=cv.QueryFrame(capture0)
cv.WriteFrame(writer0,frame0)
cv.ShowImage("Camera 0", frame0)
def Camera1():
frame1=cv.QueryFrame(capture1)
cv.WriteFrame(writer1,frame1)
cv.ShowImage("Camera 1", frame1)
while True:
status = arduino.readline()
value1=int((status[6:10]))-1000
value2=int((status[17:21]))-1000
print(value1)
print(value2)
if value1>180 and value2>180 and vid==0:
vid = 0
elif value1>180 and value2>180 and vid==1:
vid = 0
elif value1<180 and vid==0 or value2<180 and vid==0:
filename0 = strftime("OUTPUT\%Y_%m_%d %H_%M_%S") + " camera0.avi"
writer0=cv.CreateVideoWriter(filename0, 1, 15.0, (640,480), is_color=1)
filename1 = strftime("OUTPUT\%Y_%m_%d %H_%M_%S") + " camera1.avi"
writer1=cv.CreateVideoWriter(filename1, 1, 15.0, (640,480), is_color=1)
vid=1
while counter_vid<25 and vid==1:
Camera0()
Camera1()
counter_vid += 1
print(counter_vid)
cv.WaitKey(10)
else:
while counter_vid<25 and vid==1:
Camera0()
Camera1()
counter_vid += 1
print(counter_vid)
cv.WaitKey(10)
cv.WaitKey(25)
counter_vid = 0
counter += 1
print('End of Loop Counter')
print(counter)
You're right about the buffer filling up. You need a way to always get the most recent value out of the buffer.
I would suggest replacing this:
status = arduino.readline()
with this:
status = getLatestStatus()
and then further up towards the top, by your camera functions:
def getLatestStatus():
while arduino.inWaiting() > 0:
status = arduino.readline()
return status
This function getLatestStatus will go through the entire buffer every time it is called and only return the latest status, disregarding all the statuses returned in the meantime.
Your other option is to modify the "firmware" for your arduino to return a distance sensor value every time it receives a command, (say "M\n") so that way you don't have to worry about buffer problems. That's what I did for an arduino-powered ultrasonic distance device and I felt it was cleaner than the "read through the entire buffer" solution. It will introduce a bit more latency into your distance measurement though.

Categories