Skip to content

Commit 8b35417

Browse files
authored
Merge pull request #1 from P33ry/master
Added MCP3008 Node Support, Introduced Node types
2 parents 95e0101 + fb93ea6 commit 8b35417

15 files changed

+251
-22
lines changed

mudpi.config.example

+19
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"nodes": [
1818
{
1919
"name": "Arduino Box 1",
20+
"type": "arduino",
2021
"address": "/dev/ttyUSB0",
2122
"sensors": [
2223
{
@@ -41,6 +42,7 @@
4142
},
4243
{
4344
"name": "Arduino Box 2",
45+
"type": "arduino",
4446
"address": "/dev/ttyUSB1",
4547
"sensors": [
4648
{
@@ -50,6 +52,23 @@
5052
"name": "Weather Station 2"
5153
}
5254
]
55+
},
56+
{
57+
"name": "MCP3008 #1",
58+
"type": "ADC-MCP3008",
59+
"pin": "22",
60+
"sensors": [
61+
{
62+
"pin": "0",
63+
"type": "Soil",
64+
"name": "Soil Sensor #1"
65+
},
66+
{
67+
"pin": "1",
68+
"type": "Soil",
69+
"name": "Soil Sensor #1"
70+
}
71+
]
5372
}
5473
],
5574
"sensors": [

mudpi.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
sys.path.append('..')
99
from workers.lcd_worker import LCDWorker
1010
from workers.sensor_worker import SensorWorker
11+
from workers.adc_worker import ADCMCP3008Worker
1112
from workers.pi_sensor_worker import PiSensorWorker
1213
from workers.pump_worker import PumpWorker
1314
from workers.relay_worker import RelayWorker
@@ -136,13 +137,18 @@
136137

137138
try:
138139
for node in CONFIGS['nodes']:
139-
#Create sensor worker for node
140-
t = SensorWorker(node, main_thread_running, system_ready)
140+
# Create sensor worker for node
141+
if node['type'] == "arduino":
142+
t = SensorWorker(node, main_thread_running, system_ready)
143+
elif node['type'] == "ADC-MCP3008":
144+
t = ADCMCP3008Worker(node, main_thread_running, system_ready)
145+
else:
146+
raise Exception("Unknown Node Type: " + node['type'])
141147
t = t.run()
142148
if t is not None:
143149
threads.append(t)
144150
except KeyError:
145-
print('No Nodes Found to Load')
151+
print('Invalid or no Nodes found to Load')
146152

147153

148154
#Decided not to build server worker (this is replaced with nodejs, expressjs)

sensors/MCP3xxx/__init__.py

Whitespace-only changes.

sensors/MCP3xxx/sensor.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import adafruit_mcp3xxx.mcp3008 as MCP
2+
3+
4+
# Base sensor class to extend all other mcp3xxx sensors from.
5+
class Sensor:
6+
7+
PINS = {
8+
'0': MCP.P0,
9+
'1': MCP.P1,
10+
'2': MCP.P2,
11+
'3': MCP.P3,
12+
'4': MCP.P4,
13+
'5': MCP.P5,
14+
'6': MCP.P6,
15+
'7': MCP.P7,
16+
}
17+
18+
def __init__(self, pin: int, mcp, name='Sensor', key=None):
19+
self.pin = pin
20+
self.mcp = mcp
21+
self.channel = None
22+
23+
self.name = name
24+
self.key = key.replace(" ", "_").lower() if key is not None else self.name\
25+
.replace(" ", str(pin))
26+
27+
def init_sensor(self):
28+
"""
29+
Initialize the sensor here (i.e. set pin mode, get addresses, etc)
30+
:return:
31+
"""
32+
raise NotImplementedError
33+
34+
def read(self):
35+
"""
36+
Read the sensor(s), parse the data and store it in redis if redis is configured
37+
:return:
38+
"""
39+
raise NotImplementedError
40+
41+
def readRaw(self):
42+
"""
43+
Read the sensor(s) but return the raw voltage, useful for debugging
44+
:return:
45+
"""
46+
return self.channel.voltage
47+
48+
def readPin(self):
49+
""" Read the pin from the MCP3xxx as unaltered digital value"""
50+
return self.channel.value

sensors/MCP3xxx/soil_sensor.py

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import time
2+
import datetime
3+
import json
4+
import redis
5+
from .sensor import Sensor
6+
import sys
7+
from adafruit_mcp3xxx.analog_in import AnalogIn
8+
9+
sys.path.append('..')
10+
11+
import variables
12+
13+
# Tested using Sun3Drucker Model SX239
14+
# Wet Water = 287
15+
# Dry Air = 584
16+
AirBounds = 43700
17+
WaterBounds = 13000
18+
intervals = int((AirBounds - WaterBounds) / 3)
19+
20+
21+
class SoilSensor(Sensor):
22+
23+
def __init__(self, pin, mcp, name='SoilSensor', key=None):
24+
super().__init__(pin, name=name, key=key, mcp=mcp)
25+
return
26+
27+
def init_sensor(self):
28+
self.channel = AnalogIn(self.mcp, Sensor.PINS[self.pin])
29+
30+
def read(self):
31+
resistance = self.readPin()
32+
moistpercent = ((resistance - WaterBounds) / (AirBounds - WaterBounds)) * 100
33+
if moistpercent > 80:
34+
moisture = 'Very Dry - ' + str(int(moistpercent))
35+
elif 80 >= moistpercent > 45:
36+
moisture = 'Dry - ' + str(int(moistpercent))
37+
elif 45 >= moistpercent > 25:
38+
moisture = 'Wet - ' + str(int(moistpercent))
39+
else:
40+
moisture = 'Very Wet - ' + str(int(moistpercent))
41+
# print("Resistance: %d" % resistance)
42+
# TODO: Put redis store into sensor worker
43+
variables.r.set(self.key,
44+
resistance) # TODO: CHANGE BACK TO 'moistpercent' (PERSONAL CONFIG)
45+
46+
print("moisture: {0}".format(moisture))
47+
return resistance

sensors/arduino/__init__.py

Whitespace-only changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

workers/adc_worker.py

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import busio
2+
import digitalio
3+
import board
4+
import adafruit_mcp3xxx.mcp3008 as MCP
5+
from adafruit_mcp3xxx.analog_in import AnalogIn
6+
7+
import threading
8+
import variables
9+
import time
10+
import json
11+
import importlib
12+
13+
14+
class ADCMCP3008Worker:
15+
"""
16+
Analog-Digital-Converter Worker
17+
"""
18+
PINS = {
19+
'4': board.D4,
20+
'17': board.D17,
21+
'27': board.D27,
22+
'22': board.D22,
23+
'5': board.D5,
24+
'6': board.D6,
25+
'13': board.D13,
26+
'19': board.D19,
27+
'26': board.D26,
28+
'18': board.D18,
29+
'23': board.D23,
30+
'24': board.D24,
31+
'25': board.D25,
32+
'12': board.D12,
33+
'16': board.D16,
34+
'20': board.D20,
35+
'21': board.D21
36+
}
37+
38+
def __init__(self, config: dict, main_thread_running, system_ready):
39+
self.config = config
40+
self.main_thread_running = main_thread_running
41+
self.system_ready = system_ready
42+
self.node_ready = False
43+
44+
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
45+
cs = digitalio.DigitalInOut(ADCMCP3008Worker.PINS[config['pin']])
46+
47+
self.mcp = MCP.MCP3008(spi, cs)
48+
49+
self.sensors = []
50+
self.init_sensors()
51+
52+
self.node_ready = True
53+
54+
def dynamic_sensor_import(self, path):
55+
components = path.split('.')
56+
57+
s = ''
58+
for component in components[:-1]:
59+
s += component + '.'
60+
61+
parent = importlib.import_module(s[:-1])
62+
sensor = getattr(parent, components[-1])
63+
64+
return sensor
65+
66+
def init_sensors(self):
67+
for sensor in self.config['sensors']:
68+
69+
if sensor.get('type', None) is not None:
70+
# Get the sensor from the sensors folder {sensor name}_sensor.{SensorName}Sensor
71+
sensor_type = 'sensors.MCP3xxx.' + sensor.get('type').lower() + '_sensor.' + sensor.get(
72+
'type').capitalize() + 'Sensor'
73+
# analog_pin_mode = False if sensor.get('is_digital', False) else True
74+
imported_sensor = self.dynamic_sensor_import(sensor_type)
75+
new_sensor = imported_sensor(sensor.get('pin'),
76+
name=sensor.get('name', sensor.get('type')),
77+
key=sensor.get('key', None),
78+
mcp=self.mcp)
79+
new_sensor.init_sensor()
80+
self.sensors.append(new_sensor)
81+
print('{type} Sensor {pin}...\t\t\t\033[1;32m Ready\033[0;0m'.format(**sensor))
82+
83+
def run(self):
84+
if self.node_ready:
85+
t = threading.Thread(target=self.work, args=())
86+
t.start()
87+
print(str(self.config['name']) + ' Node Worker [' + str(
88+
len(self.config['sensors'])) + ' Sensors]...\t\033[1;32m Running\033[0;0m')
89+
return t
90+
else:
91+
print("Node Connection...\t\t\t\033[1;31m Failed\033[0;0m")
92+
return None
93+
94+
def work(self):
95+
96+
while self.main_thread_running.is_set():
97+
if self.system_ready.is_set() and self.node_ready:
98+
message = {'event': 'SensorUpdate'}
99+
readings = {}
100+
for sensor in self.sensors:
101+
result = sensor.read()
102+
readings[sensor.key] = result
103+
# r.set(sensor.get('key', sensor.get('type')), value)
104+
105+
print(readings)
106+
message['data'] = readings
107+
variables.r.publish('sensors', json.dumps(message))
108+
109+
time.sleep(15)
110+
# This is only ran after the main thread is shut down
111+
print("{name} Node Worker Shutting Down...\t\t\033[1;32m Complete\033[0;0m".format(
112+
**self.config))

workers/sensor_worker.py

+14-19
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
import time
2-
import datetime
32
import json
4-
import redis
53
import threading
6-
from nanpy import (ArduinoApi, SerialManager, DHT)
4+
from nanpy import (SerialManager)
75
from nanpy.serialmanager import SerialManagerError
86
import sys
97
sys.path.append('..')
10-
from sensors.float_sensor import (FloatSensor)
11-
from sensors.rain_sensor import (RainSensor)
12-
from sensors.light_sensor import (LightSensor)
13-
from sensors.humidity_sensor import (HumiditySensor)
14-
from sensors.soil_sensor import (SoilSensor)
15-
from sensors.temperature_sensor import (TemperatureSensor)
168

179
import variables
10+
import importlib
1811

1912
#r = redis.Redis(host='127.0.0.1', port=6379)
2013

@@ -42,21 +35,23 @@ def __init__(self, config, main_thread_running, system_ready):
4235
break
4336
return
4437

45-
def dynamic_sensor_import(self, name):
46-
#Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor
47-
components = name.split('.')
48-
#Dynamically import root of component path
49-
module = __import__(components[0])
50-
#Get component attributes
51-
for component in components[1:]:
52-
module = getattr(module, component)
53-
return module
38+
def dynamic_sensor_import(self, path):
39+
components = path.split('.')
40+
41+
s = ''
42+
for component in components[:-1]:
43+
s += component + '.'
44+
45+
parent = importlib.import_module(s[:-1])
46+
sensor = getattr(parent, components[-1])
47+
48+
return sensor
5449

5550
def init_sensors(self):
5651
for sensor in self.config['sensors']:
5752
if sensor.get('type', None) is not None:
5853
#Get the sensor from the sensors folder {sensor name}_sensor.{SensorName}Sensor
59-
sensor_type = 'sensors.' + sensor.get('type').lower() + '_sensor.' + sensor.get('type').capitalize() + 'Sensor'
54+
sensor_type = 'sensors.arduino.' + sensor.get('type').lower() + '_sensor.' + sensor.get('type').capitalize() + 'Sensor'
6055
#analog_pin_mode = False if sensor.get('is_digital', False) else True
6156
imported_sensor = self.dynamic_sensor_import(sensor_type)
6257
new_sensor = imported_sensor(sensor.get('pin'), name=sensor.get('name', sensor.get('type')), connection=self.connection, key=sensor.get('key', None))

0 commit comments

Comments
 (0)