Skip to content

Added MCP3008 Node Support #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions mudpi.config.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"nodes": [
{
"name": "Arduino Box 1",
"type": "arduino",
"address": "/dev/ttyUSB0",
"sensors": [
{
Expand All @@ -41,6 +42,7 @@
},
{
"name": "Arduino Box 2",
"type": "arduino",
"address": "/dev/ttyUSB1",
"sensors": [
{
Expand All @@ -50,6 +52,23 @@
"name": "Weather Station 2"
}
]
},
{
"name": "MCP3008 #1",
"type": "ADC-MCP3008",
"pin": "22",
"sensors": [
{
"pin": "0",
"type": "Soil",
"name": "Soil Sensor #1"
},
{
"pin": "1",
"type": "Soil",
"name": "Soil Sensor #1"
}
]
}
],
"sensors": [
Expand Down
12 changes: 9 additions & 3 deletions mudpi.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
sys.path.append('..')
from workers.lcd_worker import LCDWorker
from workers.sensor_worker import SensorWorker
from workers.adc_worker import ADCMCP3008Worker
from workers.pi_sensor_worker import PiSensorWorker
from workers.pump_worker import PumpWorker
from workers.relay_worker import RelayWorker
Expand Down Expand Up @@ -136,13 +137,18 @@

try:
for node in CONFIGS['nodes']:
#Create sensor worker for node
t = SensorWorker(node, main_thread_running, system_ready)
# Create sensor worker for node
if node['type'] == "arduino":
t = SensorWorker(node, main_thread_running, system_ready)
elif node['type'] == "ADC-MCP3008":
t = ADCMCP3008Worker(node, main_thread_running, system_ready)
else:
raise Exception("Unknown Node Type: " + node['type'])
t = t.run()
if t is not None:
threads.append(t)
except KeyError:
print('No Nodes Found to Load')
print('Invalid or no Nodes found to Load')


#Decided not to build server worker (this is replaced with nodejs, expressjs)
Expand Down
Empty file added sensors/MCP3xxx/__init__.py
Empty file.
50 changes: 50 additions & 0 deletions sensors/MCP3xxx/sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import adafruit_mcp3xxx.mcp3008 as MCP


# Base sensor class to extend all other mcp3xxx sensors from.
class Sensor:

PINS = {
'0': MCP.P0,
'1': MCP.P1,
'2': MCP.P2,
'3': MCP.P3,
'4': MCP.P4,
'5': MCP.P5,
'6': MCP.P6,
'7': MCP.P7,
}

def __init__(self, pin: int, mcp, name='Sensor', key=None):
self.pin = pin
self.mcp = mcp
self.channel = None

self.name = name
self.key = key.replace(" ", "_").lower() if key is not None else self.name\
.replace(" ", str(pin))

def init_sensor(self):
"""
Initialize the sensor here (i.e. set pin mode, get addresses, etc)
:return:
"""
raise NotImplementedError

def read(self):
"""
Read the sensor(s), parse the data and store it in redis if redis is configured
:return:
"""
raise NotImplementedError

def readRaw(self):
"""
Read the sensor(s) but return the raw voltage, useful for debugging
:return:
"""
return self.channel.voltage

def readPin(self):
""" Read the pin from the MCP3xxx as unaltered digital value"""
return self.channel.value
47 changes: 47 additions & 0 deletions sensors/MCP3xxx/soil_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import time
import datetime
import json
import redis
from .sensor import Sensor
import sys
from adafruit_mcp3xxx.analog_in import AnalogIn

sys.path.append('..')

import variables

# Tested using Sun3Drucker Model SX239
# Wet Water = 287
# Dry Air = 584
AirBounds = 43700
WaterBounds = 13000
intervals = int((AirBounds - WaterBounds) / 3)


class SoilSensor(Sensor):

def __init__(self, pin, mcp, name='SoilSensor', key=None):
super().__init__(pin, name=name, key=key, mcp=mcp)
return

def init_sensor(self):
self.channel = AnalogIn(self.mcp, Sensor.PINS[self.pin])

def read(self):
resistance = self.readPin()
moistpercent = ((resistance - WaterBounds) / (AirBounds - WaterBounds)) * 100
if moistpercent > 80:
moisture = 'Very Dry - ' + str(int(moistpercent))
elif 80 >= moistpercent > 45:
moisture = 'Dry - ' + str(int(moistpercent))
elif 45 >= moistpercent > 25:
moisture = 'Wet - ' + str(int(moistpercent))
else:
moisture = 'Very Wet - ' + str(int(moistpercent))
# print("Resistance: %d" % resistance)
# TODO: Put redis store into sensor worker
variables.r.set(self.key,
resistance) # TODO: CHANGE BACK TO 'moistpercent' (PERSONAL CONFIG)

print("moisture: {0}".format(moisture))
return resistance
Empty file added sensors/arduino/__init__.py
Empty file.
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.
112 changes: 112 additions & 0 deletions workers/adc_worker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import busio
import digitalio
import board
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn

import threading
import variables
import time
import json
import importlib


class ADCMCP3008Worker:
"""
Analog-Digital-Converter Worker
"""
PINS = {
'4': board.D4,
'17': board.D17,
'27': board.D27,
'22': board.D22,
'5': board.D5,
'6': board.D6,
'13': board.D13,
'19': board.D19,
'26': board.D26,
'18': board.D18,
'23': board.D23,
'24': board.D24,
'25': board.D25,
'12': board.D12,
'16': board.D16,
'20': board.D20,
'21': board.D21
}

def __init__(self, config: dict, main_thread_running, system_ready):
self.config = config
self.main_thread_running = main_thread_running
self.system_ready = system_ready
self.node_ready = False

spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
cs = digitalio.DigitalInOut(ADCMCP3008Worker.PINS[config['pin']])

self.mcp = MCP.MCP3008(spi, cs)

self.sensors = []
self.init_sensors()

self.node_ready = True

def dynamic_sensor_import(self, path):
components = path.split('.')

s = ''
for component in components[:-1]:
s += component + '.'

parent = importlib.import_module(s[:-1])
sensor = getattr(parent, components[-1])

return sensor

def init_sensors(self):
for sensor in self.config['sensors']:

if sensor.get('type', None) is not None:
# Get the sensor from the sensors folder {sensor name}_sensor.{SensorName}Sensor
sensor_type = 'sensors.MCP3xxx.' + sensor.get('type').lower() + '_sensor.' + sensor.get(
'type').capitalize() + 'Sensor'
# analog_pin_mode = False if sensor.get('is_digital', False) else True
imported_sensor = self.dynamic_sensor_import(sensor_type)
new_sensor = imported_sensor(sensor.get('pin'),
name=sensor.get('name', sensor.get('type')),
key=sensor.get('key', None),
mcp=self.mcp)
new_sensor.init_sensor()
self.sensors.append(new_sensor)
print('{type} Sensor {pin}...\t\t\t\033[1;32m Ready\033[0;0m'.format(**sensor))

def run(self):
if self.node_ready:
t = threading.Thread(target=self.work, args=())
t.start()
print(str(self.config['name']) + ' Node Worker [' + str(
len(self.config['sensors'])) + ' Sensors]...\t\033[1;32m Running\033[0;0m')
return t
else:
print("Node Connection...\t\t\t\033[1;31m Failed\033[0;0m")
return None

def work(self):

while self.main_thread_running.is_set():
if self.system_ready.is_set() and self.node_ready:
message = {'event': 'SensorUpdate'}
readings = {}
for sensor in self.sensors:
result = sensor.read()
readings[sensor.key] = result
# r.set(sensor.get('key', sensor.get('type')), value)

print(readings)
message['data'] = readings
variables.r.publish('sensors', json.dumps(message))

time.sleep(15)
# This is only ran after the main thread is shut down
print("{name} Node Worker Shutting Down...\t\t\033[1;32m Complete\033[0;0m".format(
**self.config))
33 changes: 14 additions & 19 deletions workers/sensor_worker.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import time
import datetime
import json
import redis
import threading
from nanpy import (ArduinoApi, SerialManager, DHT)
from nanpy import (SerialManager)
from nanpy.serialmanager import SerialManagerError
import sys
sys.path.append('..')
from sensors.float_sensor import (FloatSensor)
from sensors.rain_sensor import (RainSensor)
from sensors.light_sensor import (LightSensor)
from sensors.humidity_sensor import (HumiditySensor)
from sensors.soil_sensor import (SoilSensor)
from sensors.temperature_sensor import (TemperatureSensor)

import variables
import importlib

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

Expand Down Expand Up @@ -42,21 +35,23 @@ def __init__(self, config, main_thread_running, system_ready):
break
return

def dynamic_sensor_import(self, name):
#Split path of the class folder structure: {sensor name}_sensor . {SensorName}Sensor
components = name.split('.')
#Dynamically import root of component path
module = __import__(components[0])
#Get component attributes
for component in components[1:]:
module = getattr(module, component)
return module
def dynamic_sensor_import(self, path):
components = path.split('.')

s = ''
for component in components[:-1]:
s += component + '.'

parent = importlib.import_module(s[:-1])
sensor = getattr(parent, components[-1])

return sensor

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