I have written a Python script that reads the serial monitor in order to get sensor readings from an Arduino. I've been trying to solve the following problem: I want my script to run exactly for one minute in order to get the data and process it offline. For instance, if I execute the following script, it should be running for a minute and then stop. I have tried using the time module or the sleep function but my script keeps getting data and does not stop. I'm not sure how to break the while loop. Until now I managed to stop the execution by pressing CTRL+C, but it's necessary for the script to stop on its own. Here's my code(I'm also posting the get_readings function):
python
# -*- coding: utf-8 -*-
import chair_functions as cf
import os
if __name__ == '__main__':
file_extension = '.txt'
rec_file = 'chair_'+cf.get_date()+file_extension
raw_data = cf.create_directories()
rec_file = os.path.join(raw_data,rec_file)
cf.get_readings(rec_file)
# -*- coding: utf-8 -*-
from serial import Serial
import pandas as pd
import collections
import logging
import serial
import time
import sys
import csv
import os
def get_readings(output_file):
"""Read the data stream coming from the serial monitor
in order to get the sensor readings
Parameters
----------
output_file : str
The file name, where the data stream will be stored
"""
serial_port = "/dev/ttyACM0"
baud_rate = 9600
ser = serial.Serial(serial_port,baud_rate)
logging.basicConfig(filename=output_file,level=logging.DEBUG,format="%(asctime)s %(message)s")
flag = False
while True:
try:
serial_data = str(ser.readline().decode().strip('\r\n'))
time.sleep(0.2)
tmp = serial_data.split(' ')[0] #Getting Sensor Id
if(tmp == 'A0'):
flag = True
if (flag and tmp != 'A4'):
#print(serial_data)
logging.info(serial_data)
if(flag and tmp == 'A4'):
flag = False
#print(serial_data)
logging.info(serial_data)
except (UnicodeDecodeError, KeyboardInterrupt) as err:
print(err)
print(err.args)
sys.exit(0)
It is the time module itself that does not work. Therefore, instead of using True for the while condition, set start = timer.time(), then while time.time() - start < 60: as below:
start = time.time()
while time.time() - start < 60:
...
Related
I try to make a macro in python, that when pressed execute this folliwing script to add time before a text :
import keyboard # using module keyboard
from datetime import *
from time import sleep
while True:
sleep(0.2)
try:
if keyboard.is_pressed('F12'):
now = datetime.now()
keyboard.press_and_release('Home')
keyboard.write(f"{now.hour}h{now.minute}m{now.second}s :")
keyboard.press_and_release('Enter')
except :
pass
Its work well until I send a message that make multiple lines ! How to solve the issue ?
1 line message :
hello -> press F12 -> send 15h45h07s : hello
multiple lines :
line 1
line 2
line 3
-> press F12
send :
line 1
line 2
15h46m30s : line 3```
You can go to the top of the page using Ctrl+Home, so here is the improved code:
import keyboard # using module keyboard
from datetime import *
from time import sleep
while True:
sleep(0.2)
try:
if keyboard.is_pressed('F12'):
now = datetime.now()
keyboard.press_and_release('Ctrl+Home')
keyboard.write(f"{now.hour}h{now.minute}m{now.second}s :")
keyboard.press_and_release('End')
keyboard.press_and_release('Down')
except :
pass
However, this will ONLY insert the time at the top of the file, and not for any subsequent lines. If you want to use this to do multiple lines, maybe think of a more complicated approach, where you perhaps store a list of lines which have the time before them, and another key to clear this list. Here is my example:
import keyboard # using module keyboard
from datetime import *
from time import sleep
lines = []
while True:
sleep(0.2)
try:
if keyboard.is_pressed('F12'):
now = datetime.now()
if len(lines) == 0:
keyboard.press_and_release('Ctrl+Home')
else:
keyboard.press_and_release('Home')
keyboard.write(f"{now.hour}h{now.minute}m{now.second}s :")
keyboard.press_and_release('End')
keyboard.press_and_release('Down')
lines.append("placeholder")
except :
pass
try:
if keyboard.is_pressed('F11'):
lines = []
except :
pass
P.S. I edited your code so that rather than adding an enter at the end, it goes down a line. This is better for doing multiple lines, as it removes unwanted whitespace.
Example:
I'm currently setting up a some sensors with my raspberry pi in python. Total python newbie here.
Each sensor has it's own script to read the sensor and there is another script that drives an LCD display and displays the imported variable from the sensor script.
I've gotten so far with working scripts that run the sensors and generate output, however, I cannot seem to import the variables (temperature & pH) into the LCD display script. Also, once I have imported the variables, how do I instruct the LCD script to "refresh" the and fetch the updated variable?
Here's a trimmed down version of what I have so far, I've omitted the sensor and data logging parts of each script. For simplicity, script_display is the LCD driver, and pH_script is for pH and temp_script is for temperature.
Here's a simplified version of the scripts:
script_display.py
import sys
sys.path.insert(0, '/home/pi/Raspberry-Pi-sample-code')
import pH_script
import temp_script
from ph_script import ph_main
from temp_script import get_temp
import time
while True:
print PH.ph_main(ph_output)
print get_temp(temp)
time.sleep(1)
temp_script.py
from w1thermsensor import W1ThermSensor
import time
#Get Temperature
def get_temp():
global temp
sensor = W1ThermSensor(W1ThermSensor.THERM_SENSOR_DS18B20, "031683a0a4ff")
activate_temp = sensor.get_temperature()
temp = str(activate_temp)
return temp
#Read Temp Frequency
def read():
threading.Timer(0.5, read).start()
get_temp()
time.sleep(1)
try:
while True:
read()
get_temp()
print get_temp()
except KeyboardInterrupt:
print("Program Ended By User")
pH_script.py
def ph_main():
lots and lots of code to activate the PH probe, and other variables, but the variable ph_output returns as a string, ph_output
try:
while True:
global ph_output
dev.send_cmd("R")
lines = dev.read_lines()
for i in range(len(lines)):
print lines[i]
if lines[i][0] != '*':
print lines[i]
ph_output = str(lines[i])
return ph_output
time.sleep(delaytime)
try:
while True:
ph_main()
except KeyboardInterrupt:
print("Continuous polling stopped")
so, again, first question, how to pass the global variables back to the display script? and two, how to instruct the display script to 'refresh' the variables?
the error I am currently getting is:
Traceback (most recent call last):
File "script_display.py", line 8, in <module>
print PH.ph_main(ph_output)
NameError: name 'ph_output' is not defined
looking forward to any input and thanks for your time + help!
First, I have less than one year experience with Python. I normally have problems with data usage with my ISP such that I discover late that I have overused my data allocation. So, in order to evade this problem, I thought of creating a script that would run immediately I tether my phone to my PC. The script scrapes data from my ISP's website and returns my data bundle balance.
I found out that pyudev is useful in such an instance where I would like to constantly monitor my usb ports activities. Part of the script is as shown below. The problem is identify_phone is called 3 times (from my experience) which tends to call the scraping method 3 times whereas I would like it to be called once. I tried using the global value but it does not work as expected. I have searched online but there are scarce resources on pyudev.
Any help would be appreciated (Including any revision on my code :P)
import glib
import re
import subprocess
import requests
import bs4
import datetime
import sys
from selenium import webdriver
from pyudev import Context, Monitor
def identify_phone(observer, device):
global last_updated
current_time = datetime.datetime.now()
time_diff = current_time - last_updated
if time_diff.seconds < 10:#300:
pass
else:
#print('\Checking USB ports...')
try:
tout = subprocess.check_output("lsusb | grep 1234:1234", shell=True) #check if specific usb device (phone) is connected
except subprocess.CalledProcessError:
tout = None
if tout is not None:
device_found = True
else:
device_found = False
last_updated = datetime.datetime.now()
get_network_data()# scrapes ISP website
try:
device_found = False
last_updated = datetime.datetime.now()
try:
from pyudev.glib import MonitorObserver
except ImportError:
from pyudev.glib import GUDevMonitorObserver as MonitorObserver
context = Context()
monitor = Monitor.from_netlink(context)
monitor.filter_by(subsystem='usb')
observer = MonitorObserver(monitor)
observer.connect('device-added', identify_phone)
monitor.start()
glib.MainLoop().run()
except KeyboardInterrupt:
print('\nShutdown requested.\nExiting gracefully...')
sys.exit(0)
I'm trying to write a python script to read values from the arduino's serial port and write it to a file so I can log the data.
The arduino code is long and complicated, but I'm using Serial.println() to print an integer to the serial port (located at /dev/ttyACM0)
import sys
import serial
import getpass
import datetime
import instrumentDriver
"""location of the arduino"""
location = '/dev/ttyACM0'
"""Connect to the arduino"""
arduino = instrumentDriver.serialDevice(location)
"""Successfully connected!"""
filename = str(sys.argv[1])
dataFile = open(filename+'.dat','w')
"""The main loop -- data is taken here and written to file"""
while True:
try:
datum = arduino.read()
print datum
dataFile.write(datetime.datetime.now().strftime("%Y-%m-%d"))
dataFile.write('\t')
dataFile.write(datum)
dataFile.write('\n')
except:
dataFile.close()
break
The instrumentDriver.py is just a wrapper for pySerial:
class serialDevice:
def __init__(self,location):
self.device = location
self.port = serial.Serial(location,9600)
def write(self,command):
self.port.write(command)
def read(self):
return self.port.readline()
I've used this block of code years ago and it worked fine, but it seems to be failing right now and I'm not entirely sure why. I get a SyntaxError on line 45:
scottnla#computer-1 ~/Documents/sensorTest $ python readSerial.py file
File "readSerial.py", line 45
print datum
^
SyntaxError: invalid syntax
I've tried changing the print statement, but no matter what I'm printing, I get a syntax error -- I speculate that the problem may actually be with the arduino.read() line.
Any advice would be greatly appreciated!
There is still an indentation issue; rewritten as below, it should run:
import sys
import datetime
class serialDevice:
def __init__(self,location):
self.device = location
self.port = sys.stdin # changed to run on terminal
def write(self,command):
self.port.write(command)
def read(self):
return self.port.readline()
"""location of the arduino"""
location = '/dev/ttyACM0'
"""Connect to the arduino"""
arduino = serialDevice(location)
"""Successfully connected!"""
filename = str(sys.argv[1])
dataFile = open(filename+'.dat','w')
"""The main loop -- data is taken here and written to file"""
while True:
try:
"""retrieve the raw analog number from the arduino's ADC"""
datum = arduino.read()
"""write it to file, along with a timestamp"""
print datum
dataFile.write(datetime.datetime.now().strftime("%Y-%m-%d"))
dataFile.write('\t')
dataFile.write(datum)
dataFile.write('\n')
except KeyboardInterrupt:
"""this allows for the user to CTRL-C out of the loop, and closes/saves the file we're writing to."""
dataFile.close()
break
I have created a windwos service utilising the following code:
import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil
import os, sys, string, time
class aservice(win32serviceutil.ServiceFramework):
_svc_name_ = "PAStoDistillerIFC"
_svc_display_name_ = "PAS DW to Distiller Interface"
_svc_description_ = "Service that checks the Clinical Research folder for any new files from PAS to process in Distiller"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
def SvcDoRun(self):
import servicemanager
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, ''))
#self.timeout = 640000 #640 seconds / 10 minutes (value is in milliseconds)
self.timeout = 120000 #120 seconds / 2 minutes
# This is how long the service will wait to run / refresh itself (see script below)
while 1:
# Wait for service stop signal, if timeout, loop again
rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
# Check to see if self.hWaitStop happened
if rc == win32event.WAIT_OBJECT_0:
# Stop signal encountered
servicemanager.LogInfoMsg("PAStoDistillerIFC - STOPPED!") #For Event Log
break
else:
#[actual service code between rests]
try:
file_path = "D:\\SCRIPTS\\script.py"
execfile(file_path) #Execute the script
except:
servicemanager.LogInfoMsg("File CRASHED")
pass
#[actual service code between rests]
def ctrlHandler(ctrlType):
return True
if __name__ == '__main__':
win32api.SetConsoleCtrlHandler(ctrlHandler, True)
win32serviceutil.HandleCommandLine(aservice)
To run this script:
import os, re, urllib, urllib2, time, datetime
def postXML( path, fname):
fileresultop = open("D:\\CLinicalResearch\\SCRIPTS\\LOG.txt", 'a') # open result file
fileresultop.write('CheckXXX ')
fileresultop.close()
now = datetime.datetime.now() #####ALWAYS CRASHES HERE######
fileresult = open("D:\\SCRIPTS\\IFCPYTHONLOG.txt", 'a') # open result file
fileresultop = open("D:\\SCRIPTS\\LOG.txt", 'a')
fileresultop.write('Check2 ')
fileresultop.close()
path="D:\\Test2" # Put location of XML files here.
procpath="D:\\Test2Processed" # Location of processed files
now = datetime.datetime.now()
dirList=os.listdir(path)
for fname in dirList: # For each file in directory
if re.search("PatientIndexInsert", fname): # Brand new patient records
fileresultop = open("D:\\SCRIPTS\\LOG.txt", 'a') # open result file
fileresultop.write('Check1 ')
fileresultop.close()
postXML(path, fname)
I have pared down the script to the bare code where I believe this is crashing.
This works perfectly from the command line, I run the windows service under my own login.
Once I take the datetime function out of the function it seems to work.
Edit 1: I saw that the service runs in a blank environment. I don't have any environmental variables set myself.
Edit 2: Added traceback:
File "D:\ClinicalResearch\SCRIPTS\PAS2DIST.py", line 23, in <module>
postXML(path, fname)
File "D:\ClinicalResearch\SCRIPTS\PAS2DIST.py", line 6, in postXML
now = datetime.datetime.now()
NameError: global name 'datetime' is not defined
I didn't find the cause but I did find a workaround.
I needed to import all the same libraries into the function too. Once I did that, worked like a charm.
Hope this can help someone else.