Skip to content

Commit d4fd3fe

Browse files
Merge pull request #1 from supersciencechris/ds18b20_sensor
Ds18b20 sensor (see: mudpi#42 (comment))
2 parents fb206b1 + 91fd7b9 commit d4fd3fe

File tree

4 files changed

+195
-0
lines changed

4 files changed

+195
-0
lines changed

mudpi/extensions/ds18b20/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""
2+
DS18B20 Extension
3+
Includes sensor interface for DS18B20.
4+
Works for Dallas 1-wire temperature sensors.
5+
"""
6+
from mudpi.extensions import BaseExtension
7+
8+
9+
class Extension(BaseExtension):
10+
namespace = 'ds18b20'
11+
update_interval = 60
12+

mudpi/extensions/ds18b20/docs.html

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# DS18B20
2+
The `ds18b20` extension connects to a DS18B20 device to gather temperature readings. The sensor will return `temperature`.
3+
4+
This extension does not take an extension level configs and is focused on [interfaces.]({{url('docs/developers-interfaces')}})
5+
6+
---
7+
<div class="mb-2"></div>
8+
9+
## Sensor Interface
10+
Provides a [sensor]({{url('docs/sensors')}}) that returns a DS18B20 readings.
11+
12+
<table class="mt-2 mb-4">
13+
<thead><tr><td width="15%">Option</td><td width="15%">Type</td><td width="15%">Required</td><td width="55%">Description</td></tr></thead>
14+
<tbody>
15+
<tr><td class="font-600">key</td><td class="text-italic text-sm">[String]</td><td>Yes</td><td class="text-xs">Unique slug id for the component</td></tr>
16+
<tr><td class="font-600">name</td><td class="text-italic text-sm">[String]</td><td>No</td><td class="text-xs">Friendly display name of component. Useful for UI.</td></tr>
17+
<tr><td class="font-600">one_wire_ID</td><td class="text-italic text-sm">[String]</td><td>No</td><td class="text-xs">The address of the DS18B20 sensor. This should be set if using mulitple DS18B20 sensors and can be found as a directory in `/sys/bus/w1/devices/` in the format of `28-XXXXXXXXXXXXX` . <strong>Default 10</strong></td></tr>
18+
</tbody>
19+
</table>
20+
21+
### Config Examples
22+
Here is a config of a complete example sensor.
23+
24+
```json
25+
"sensor": [{
26+
"key": "ds18b20_outside",
27+
"interface": "ds18b20",
28+
"name": "Outside temperature",
29+
"one_wire_ID": "28-0000000000001"
30+
}]
31+
```
32+
33+
### Data
34+
Here is an example of the data returned by the DS18B20:
35+
36+
```json
37+
{
38+
"temperature": 50
39+
}
40+
```
41+
---
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "DS18B20 1-Wire Temperature Sensor",
3+
"namespace": "ds18b20",
4+
"details": {
5+
"description": "Provides interface for ds18b20 sensors to take tempurature readings.",
6+
"documentation": "https://mudpi.app/docs/sensors/ds18b20"
7+
},
8+
"requirements": ["glob2", "python-time"]
9+
}

mudpi/extensions/ds18b20/sensor.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"""
2+
DS18B20 Sensor Interface
3+
Connects to a DS18B20 device to get
4+
temperature readings.
5+
"""
6+
import os
7+
import glob
8+
import time
9+
10+
from mudpi.constants import METRIC_SYSTEM
11+
from mudpi.extensions import BaseInterface
12+
from mudpi.extensions.sensor import Sensor
13+
from mudpi.logger.Logger import Logger, LOG_LEVEL
14+
from mudpi.exceptions import MudPiError, ConfigError
15+
16+
os.system('modprobe w1-gpio')
17+
os.system('modprobe w1-therm')
18+
19+
20+
#device_folder = '/sys/bus/w1/devices/28-XXXXXXXXXXXXX'
21+
#device_folder = glob.glob(base_dir + '/28*')[0]
22+
#device_file = device_folder + '/w1_slave'
23+
24+
class Interface(BaseInterface):
25+
26+
def load(self, config):
27+
""" Load DS18B20 sensor component from configs """
28+
sensor = ds18b20(self.mudpi, config)
29+
self.add_component(sensor)
30+
return True
31+
32+
def validate(self, config):
33+
if not isinstance(config, list):
34+
config = [config]
35+
36+
for conf in config:
37+
""" See if 1-wire ID was passed by the user in the config file """
38+
if not conf.get('one_wire_ID'):
39+
Logger.log(
40+
LOG_LEVEL["debug"],
41+
'DS18B20 one_wire_ID not set. Will search for device.'
42+
)
43+
44+
return config
45+
46+
class ds18b20(Sensor):
47+
""" DS18B20 Sensor get readings temperature. """
48+
49+
""" Properties """
50+
@property
51+
def id(self):
52+
""" Return a unique id for the component """
53+
return self.config['key']
54+
55+
@property
56+
def name(self):
57+
""" Return the display name of the component """
58+
return self.config.get('name') or f"{self.id.replace('_', ' ').title()}"
59+
60+
@property
61+
def state(self):
62+
""" Return the state of the component (from memory, no IO!) """
63+
return self._state
64+
65+
@property
66+
def classifier(self):
67+
""" Classification further describing it, effects the data formatting """
68+
return 'temperature'
69+
70+
@property
71+
def one_wire_ID(self):
72+
return self.config['one_wire_ID']
73+
74+
75+
""" Methods """
76+
def init(self):
77+
"""To support multiple 1-wire devices check to see if the ID is set in the config.
78+
If the ID is not set, there should only be a single 28-xxxxxxxxx directory in the base directory, so we use that. """
79+
80+
base_dir = '/sys/bus/w1/devices'
81+
82+
if self.config.get('one_wire_ID') and os.path.isdir(base_dir + '/' + self.config.get('one_wire_ID')):
83+
self.device_file = base_dir + '/' + self.config.get('one_wire_ID') + '/w1_slave'
84+
Logger.log(
85+
LOG_LEVEL["debug"],
86+
'Setting device file to ' + self.device_file
87+
)
88+
else:
89+
Logger.log(
90+
LOG_LEVEL["debug"],
91+
'DS18B20 one_wire_ID not set or not found.'
92+
)
93+
""" Make sure 1-wire device directory exists """
94+
try:
95+
device_folder = glob.glob(base_dir + '/28*')[0]
96+
except:
97+
Logger.log(
98+
LOG_LEVEL["error"],
99+
'Failed to find 1-wire device directory. Ensure device is connected and one_wire_ID corret..'
100+
)
101+
else:
102+
self.device_file = device_folder + '/w1_slave'
103+
return True
104+
105+
def update(self):
106+
107+
def read_temp_raw():
108+
f = open(self.device_file, 'r')
109+
lines = f.readlines()
110+
f.close()
111+
return lines
112+
113+
lines = read_temp_raw()
114+
while lines[0].strip()[-3:] != 'YES':
115+
time.sleep(0.2)
116+
lines = read_temp_raw()
117+
equals_pos = lines[1].find('t=')
118+
if equals_pos != -1:
119+
temp_string = lines[1][equals_pos+2:]
120+
temperature_c = float(temp_string) / 1000.0
121+
temperature_f = temperature_c * 9.0 / 5.0 + 32.0
122+
_temperature = round(temperature_c if self.mudpi.unit_system == METRIC_SYSTEM else temperature_f, 2)
123+
readings = {
124+
'temperature': _temperature,
125+
}
126+
self._state = readings
127+
return readings
128+
else:
129+
Logger.log(
130+
LOG_LEVEL["error"],
131+
'Failed to get reading [DS18B20]. Try again!'
132+
)
133+
return None

0 commit comments

Comments
 (0)