Posts: 20
Threads: 8
Joined: Dec 2017
Hi All,
I have concocted a piece of coding to read and log temperatures from DS18B20 sensors.
def read_ds18b20(sensorid): #Functon reads DS15B20 sensor
tfile = open("/sys/bus/w1/devices/"+ sensorid +"/w1_slave")
text = tfile.read()
# Close file after reading it.
tfile.close()
# Select 21st word starting from 0.
sensor_data =text.split(" ")[20]
# Remove 't=' and make floating.
temp_raw = float(sensor_data[2:])
#print (temp_raw)
temp = temp_raw / 1000
digitemp = '%.1f' % temp #round to 1 decimal
#print ("digitemp = " + digitemp)
return digitemp The ID's (like 28-000009adc801) of the sensors appear as files in /sys/bus/w1/devices/ .
I use the following to read the multiple sensors:
# Execute DS18B20 function for sensor ID's below.
temp1 = float(read_ds18b20('28-000009aeba12')) #Motor 1 temp.
temp2 = float(read_ds18b20('28-000009aeba12')) #Motor 2 temp.
temp3 = float(read_ds18b20('28-000009adc801')) #outside temp. If there is a change of motor and thus another sensor id, the program halts with "file not found" because the new ID has no file in /sys/bus/w1/devices/.
How can i make the program continue without intervention?
Any comments on the construction of the function are also welcomed.
Rgds Steffen
Posts: 116
Threads: 1
Joined: Apr 2018
There are 2 ways you can do this:
1) Check if the file exist and then open it (typical from other languages like C)
2) try to open it and capture the the error ("ask forgiveness not permission", normal in languages with exceptions like python)
The 2nd one has also big advantages when you add security concerns...
In this case your code can be rewritten as:
def read_ds18b20(sensorid):
"""
Functon reads DS15B20 sensor
"""
device = f"/sys/bus/w1/devices/{sensorid}/w1_slave"
try:
# No need to close, the with block does it for us...
with open(device, 'rt') as tfile:
text = tfile.read()
except FileNotFoundError:
# Things to do when the sensor id is invalid...
# I just going to return None.
return None
except PermissionError:
# The file exists, but the user is not in the right group
print(f"You must be in the right group to access:\n{device}")
# Just continue with the error...
raise
# Ok, everything was correct, let's continue
# Select 21st word starting from 0.
sensor_data =text.split(" ")[20]
# Remove 't=' and make floating.
temp = float(sensor_data[2:]) / 1000
#return rounded to 1 decimal
return round(temp, 1) I am using 2 features that are from recent versions of python, the string interpolation f"bla bla {variable} bla bla" that you can change it to "bla bla {} bla bla".format(variable) and the new exceptions FileNotFoundError and PermissionError... if your python is too old and returns other exception, just experiment from the terminal and use the proper ones...
Posts: 20
Threads: 8
Joined: Dec 2017
May-21-2018, 09:37 AM
(This post was last modified: May-21-2018, 09:40 AM by buran.)
Hi killerrex,
Tks for your quick reply.
Tried your solution but i run into an syntax error on
device = f"/sys/bus/w1/devices/{sensorid}/w1_slave"
^
i could not find any typos so somthing else must be wrong.
I run python 3.5.3 and faithfully updated/upgraded so i think i'm ok there.
Any thoughts?
Steffen
Posts: 8,160
Threads: 160
Joined: Sep 2016
(May-20-2018, 05:58 PM)killerrex Wrote: I am using 2 features that are from recent versions of python, the string interpolation f"bla bla {variable} bla bla" that you can change it to "bla bla {} bla bla".format(variable) and the new exceptions FileNotFoundError and PermissionError... if your python is too old and returns other exception, just experiment from the terminal and use the proper ones... f-strings are 3.6+ feature, so with 3.5 you are not OK to use them
Posts: 20
Threads: 8
Joined: Dec 2017
Thanks everybody and my apologies for the confusion caused by my unfamiliarity with the BB.
I got the problem solved with thanks to killirrex for putting me on the right track.
The whole scripot now looks like:
#!/usr/bin/env python
#Versie 2.3; 21mei2018
#Dit programma leest elke sensor apart uit en slaat aan het einde
#de resultaten op in een csv file.
#Importeer benodigde libraries.
#21/05File not found opgelost.
import sys
import time
import csv
import os
import spidev
import RPi.GPIO as GPIO
spi = spidev.SpiDev()
spi.open(0,0)
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(22,GPIO.IN)
GPIO.setup(27,GPIO.OUT)
GPIO.setup(23,GPIO.OUT)
GPIO.setup(24,GPIO.OUT)
GPIO.setup(25,GPIO.OUT)
def read_MCP(sensorid): #Function reads MCP3008.
adc = spi.xfer2([1,(8+sensorid)<<4,0],20000)
adcread = ((adc[1]&3) << 8) + adc[2]
return adcread
def read_ds18b20(sensorid): #Function reads DS18B20 sensor.
device="/sys/bus/w1/devices/"+ sensorid +"/w1_slave"
try:
with open (device) as tfile:
text=tfile.read()
except FileNotFoundError:
GPIO.output(25,True)
notemp = 9999
return notemp
sensor_data =text.split(" ")[20] #Select 21st word start with zero.
# Haal 't=' weg en maak floating
temp_raw = float(sensor_data[2:]) #Remove 'T="and make floating.
#print (temp_raw)
temp = temp_raw
digitemp = '%.1f' % temp #Round to one decimal.
return digitemp
datum = (time.strftime('%d/%m/%Y')) # Define date/time variables.
tijd = (time.strftime('%H:%M:%S'))
GPIO.output(25,False) #reset FileNotFound LED
while True:
while True:
time.sleep(1)
if GPIO.input(22): #Contact key is ON
GPIO.output(27,True) #Turn on LED indicator.
break
else: #Contact key is dus OFF
GPIO.output(27,False) #LED indicator OFF
print ('Schakelaar is UIT')
# Checked water level BB with key OFF
#Check watersensor SB, channel0 of ADC.
level = read_MCP(0)
level_SB = round(level,1)
print (level_SB)
# Turn on the SB pump if value > 510.
if (level_SB) > 510:
GPIO.output(23,GPIO.HIGH) #LED/pomprelais SB ON.
else:
GPIO.output(23,GPIO.LOW) #LED/pomprelais SB OFF.
#Check watersensor BB, channel1 of ADC.
level =read_MCP(1)
level_BB= round(level,1)
print (level_BB)
# Turn on the BB pump if value > 510
if (level) > 510:
GPIO.output(24,GPIO.HIGH) #LED/pomprelais BB ON.
else:
GPIO.output(24,GPIO.LOW) #LED/pomprelais BB OFF.
# Exec DS18B20 function for sensorID's below.
temp1 = float(read_ds18b20('28-000009aeba12')) #Motor 1
temp2 = float(read_ds18b20('28-000009aeba12')) #Motor 2
temp3 = float(read_ds18b20('28-000009adc801')) #Buitentemperatuur
# Define 'lijst'.
lijst = [0,0,0,0,0,0,0,0]
# Exec function 'read_temp36' for each ADC channel.
for offset in range(0,8):
adcread = read_MCP(offset)
print (adcread)
result = (adcread*330)/float(1024)
lijst[offset] = adcread
offset +=1 #Decrease offset by 1
data = [datum,tijd,temp1,temp2,temp3,round(lijst[0],1),round(lijst[1],1),round((((lijst[2]*3.3/1024)-0.52)/0.08),1),round((((lijst[3]*3.3/1024)-0.52)/0.08),1)]
# lijst 0 is watersensor SB, lijst 1 is idem BB, lijst 3 is stroom SB, lijst 4 is stroom BB
# Turn on the SB pump if value > 510. if (lijst[0]) > 510:
GPIO.output(23,GPIO.HIGH) #LED/pomprelais SB ON
else:
GPIO.output(23,GPIO.LOW) #LED/pomprelais SB ON
# Turn on the BB pump if value > 510.
if (lijst[1]) > 510:
GPIO.output(24,GPIO.HIGH) #LED/pomprelais BB ON
else:
GPIO.output(24,GPIO.LOW) #LED/pomprelais BB ON
print (data)
#Output naar csv file
#csvfile = "/home/pi/programs/testdata.csv"
#with open (csvfile,'a') as output:
# writer = csv.writer(output, delimiter = ',' ,lineterminator = '\n')
# writer.writerow(data)
time.sleep(0.5) Now i will try to get a 4 x 20 LCD screen incorporated to display various values.
Should i do that in the same script or write a new one and somehow point it??
Tks Steffen
Posts: 116
Threads: 1
Joined: Apr 2018
It is better always to keep the code in a single point (don't repeat yourself) in python you can always use the import machinery to do this.
The only problem is that when you "import" the scripts is executed, so it is common to guard the run part with the __main__ trick.
In your case, there is some mix between proper functions and data, but if you modify the read_MCP to request the spi from the arguments you can store the 2 functions in a separate file:
#!/usr/bin/env python
# This shall be in a file at the same folder as all your programs
# I used low_level.py, but you can have more imagination than me...
import RPi.GPIO as GPIO
def read_MCP(spi, sensorid): #Function reads MCP3008.
adc = spi.xfer2([1,(8+sensorid)<<4,0],20000)
adcread = ((adc[1]&3) << 8) + adc[2]
return adcread
def read_ds18b20(sensorid): #Function reads DS18B20 sensor.
device="/sys/bus/w1/devices/"+ sensorid +"/w1_slave"
try:
with open (device) as tfile:
text=tfile.read()
except FileNotFoundError:
GPIO.output(25,True)
notemp = 9999
return notemp
sensor_data =text.split(" ")[20] #Select 21st word start with zero.
# Haal 't=' weg en maak floating
temp_raw = float(sensor_data[2:]) #Remove 'T="and make floating.
#print (temp_raw)
temp = temp_raw
digitemp = '%.1f' % temp #Round to one decimal.
return digitemp
def _test():
# Define here if you want some simple tests to check your functions
# You can run it as python low_level.py
pass
if __name__ == '__main__':
# This part runs only when this script is the main script, not when imported from other
_test() Then in your other scripts can import from this other -imagine you call it low_level.py) with from low_level import read_MCP, read_ds18b20
Now your main script will be:
#!/usr/bin/env python
#Versie 2.3; 21mei2018
#Dit programma leest elke sensor apart uit en slaat aan het einde
#de resultaten op in een csv file.
#Importeer benodigde libraries.
#21/05File not found opgelost.
import sys
import time
import csv
import os
import spidev
import RPi.GPIO as GPIO
from low_level import read_MCP, read_ds18b20
spi = spidev.SpiDev()
spi.open(0,0)
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(22,GPIO.IN)
GPIO.setup(27,GPIO.OUT)
GPIO.setup(23,GPIO.OUT)
GPIO.setup(24,GPIO.OUT)
GPIO.setup(25,GPIO.OUT)
datum = (time.strftime('%d/%m/%Y')) # Define date/time variables.
tijd = (time.strftime('%H:%M:%S'))
GPIO.output(25,False) #reset FileNotFound LED
while True:
while True:
time.sleep(1)
if GPIO.input(22): #Contact key is ON
GPIO.output(27,True) #Turn on LED indicator.
break
else: #Contact key is dus OFF
GPIO.output(27,False) #LED indicator OFF
print ('Schakelaar is UIT')
# Checked water level BB with key OFF
#Check watersensor SB, channel0 of ADC.
level = read_MCP(spi, 0)
level_SB = round(level,1)
print (level_SB)
# Turn on the SB pump if value > 510.
if (level_SB) > 510:
GPIO.output(23,GPIO.HIGH) #LED/pomprelais SB ON.
else:
GPIO.output(23,GPIO.LOW) #LED/pomprelais SB OFF.
#Check watersensor BB, channel1 of ADC.
level =read_MCP(1)
level_BB= round(level,1)
print (level_BB)
# Turn on the BB pump if value > 510
if (level) > 510:
GPIO.output(24,GPIO.HIGH) #LED/pomprelais BB ON.
else:
GPIO.output(24,GPIO.LOW) #LED/pomprelais BB OFF.
# Exec DS18B20 function for sensorID's below.
temp1 = float(read_ds18b20('28-000009aeba12')) #Motor 1
temp2 = float(read_ds18b20('28-000009aeba12')) #Motor 2
temp3 = float(read_ds18b20('28-000009adc801')) #Buitentemperatuur
# Define 'lijst'.
lijst = [0,0,0,0,0,0,0,0]
# Exec function 'read_temp36' for each ADC channel.
for offset in range(0,8):
adcread = read_MCP(spi, offset)
print (adcread)
result = (adcread*330)/float(1024)
lijst[offset] = adcread
offset +=1 #Decrease offset by 1
data = [datum,tijd,temp1,temp2,temp3,round(lijst[0],1),round(lijst[1],1),round((((lijst[2]*3.3/1024)-0.52)/0.08),1),round((((lijst[3]*3.3/1024)-0.52)/0.08),1)]
# lijst 0 is watersensor SB, lijst 1 is idem BB, lijst 3 is stroom SB, lijst 4 is stroom BB
# Turn on the SB pump if value > 510. if (lijst[0]) > 510:
GPIO.output(23,GPIO.HIGH) #LED/pomprelais SB ON
else:
GPIO.output(23,GPIO.LOW) #LED/pomprelais SB ON
# Turn on the BB pump if value > 510.
if (lijst[1]) > 510:
GPIO.output(24,GPIO.HIGH) #LED/pomprelais BB ON
else:
GPIO.output(24,GPIO.LOW) #LED/pomprelais BB ON
print (data)
#Output naar csv file
#csvfile = "/home/pi/programs/testdata.csv"
#with open (csvfile,'a') as output:
# writer = csv.writer(output, delimiter = ',' ,lineterminator = '\n')
# writer.writerow(data)
time.sleep(0.5) You now can re-use that functions from other scripts. In general is always better to keep the things in a single part than abuse from the copy-paste or create a gargantuous script that tries to do everything and each time you try to fix one of the functions you break other three (Keep it simple....)
If you finish having too many small functions and want to organise them properly. you can create easily a package... but that is more advanced python.
Posts: 20
Threads: 8
Joined: Dec 2017
I followed killerex'es suggestion and made base.py
#!/bin/python
# baseprogram; calls the function status from key_check.py
import sys
import os
import time
from key_check import status
while True:
on_or_off=status
print (on_or_off)
time.sleep(3) and key_check.py that containes the function 'status'
#!/bin/python
#import RPi.GPIO as GPIO
import time
import os
#GPIO.setup(22, GPIO.IN)
def status():
foo = 5 # Lees de status van de GPIO pin uit.
if (foo) == 4:
#Schakelaar is AAN.
key = 'ON'
else:
#Schakelaar is UIT.
key = 'OFF'
return (key)
datum = (time.strftime('%d/%m/%Y'))
#print(datum)
#keystat = status()
#print (keystat)
def _test():
pass
if __name__ == '__main__':
# This part runs only when this script is the main script, not when imported from other
_test() Running key_check on its own is OK.
When i call keycheck.status i get the following response:
Output: Python 3.6.4
>>> %Run base.py
<function status at 0x037E4E40>
<function status at 0x037E4E40>
KeyboardInterrupt: Forced reset
>>>
Any ideas about this?
As you will probably have noticed i am very much a novice, but i'll learn.
is there a site where i can learn about those situation?
Posts: 116
Threads: 1
Joined: Apr 2018
You need to call the functions, remember that in python a function is an object like any other, so when you write:
on_or_off=status
print (on_or_off) Is not different to:
on_or_off=5
print (on_or_off) If you change to:
# Notice that here I call the function, not just store it in a variable
on_or_off=status()
print (on_or_off)
Posts: 20
Threads: 8
Joined: Dec 2017
Tks for the reply. I forgot to put the brackets after the function name.
A step ahead again tks to you. i'l keep plodding on.
|