(#35) What's the Fun in That (Part Three)
The Software
Part one of the series "What's the Fun in That" constructed an elaborate electrical timer from a Raspberry Pi and an 8 Channel Relay board.
Here I'll quickly cover the software side. There are a gazillion projects out there that will show you how to connect Relay Boards to the GPIO pins on a Raspberry Pi. And, once connected, energzing the relays is simply a matter of driving the GPIO pins Low or High.
But here are a few code snippets that might help you along the way. We're doing this in Python. And we're using the RPi.GPIO library:
Import RPi.GPIO
import logging
import RPi.GPIO as GPIO
import time
class ChannelManager(object):
# ---------------------------------------------------------
def __init__(self):
self.socket_pin_assignments = {
1: 2,
2: 3,
3: 4,
4: 22,
5: 10,
6: 9,
7: 27,
8: 17}
The "socket_pin_assignments" dictionary maps a socket number to it's connected GPIO pin number. The pin number depends on how we've wired up the relay to the Raspberry Pi. If you choose different pins, or a smaller relay board then your values would be different.
Before going much farther, we set the library to use BCM style pin numbering and we tell the library we're going to be using the GPIO pins as outputs:
GPIO.setup()
try:
GPIO.setmode(GPIO.BCM)
for key, value in self.socket_pin_assignments.items():
logging.info("Setting socket {} pin {} to
GPIO Output".format(key, value))
GPIO.setup(value, GPIO.OUT)
except Exception:
logging.exception('Exception in setting up GPIO ports')
The Clapper!
When an "On" message comes in, this function is called:def socket_on(self, socket_number, duration=900):
if (socket_number <= 0 or socket_number > self.max_socket_num):
logging.error('Invalid socket number {} passed to the on
command'.format(socket_number))
return
pin_number = self.socket_pin_assignments[socket_number]
try:
GPIO.output(pin_number, GPIO.LOW)
self.socket_states[socket_number] = "on"
self.socket_on_time[socket_number] = time.time()
self.socket_on_max_duration[socket_number] = duration
except Exception:
logging.exception('Exception in setting socket {}
pin {} to GPIO.LOW'
.format(socket_number, pin_number))
When an "Off" message arrives:
def socket_off(self, socket_number):
if (socket_number <= 0 or socket_number > self.max_socket_num):
logging.error('Invalid socket number {} passed to the off
command'.format(socket_number))
return
pin_number = self.socket_pin_assignments[socket_number]
try:
GPIO.output(pin_number, GPIO.HIGH)
self.socket_states[socket_number] = "off"
self.socket_on_time[socket_number] = 0.0
self.socket_on_max_duration[socket_number] = 0
except Exception:
logging.exception('Exception in setting socket {}
pin {} to GPIO.HIGH'
.format(socket_number, pin_number))
That's about all there is for 'the meat' of this software. In my code I also setup MQTT to send and receive messages (using paho-mqtt).
I use ZeroConf to locate the MQTT Broker dynamically (using zeroconf).
Meltdown Prevention
And I have a function that wakes up every five seconds to check if a socket has been left on for too long. [ When sending an 'on' command, you also specify the maximum duration you want it left on. ]def check_for_duration_exceeded(self):
logging.debug('Checking for time exceeded on all on sockets')
time_now = time.time()
for socket_num, pin_number in
self.socket_pin_assignments.items():
if self.socket_states[socket_num] == 'on':
max_seconds_on = self.socket_on_max_duration[socket_num]
total_seconds_on = time_now - self.socket_on_time[socket_num]
if (total_seconds_on > max_seconds_on):
logging.warning('Maximum On Time reached for
socket {}. Sending off command'
.format(socket_num))
self.socket_off(socket_num)
The MQTT Messaging
If nothing else, I'm committed to being Event Driven and Message Based. So at the core of the functionality are a handful of JSON formatted messages sent over MQTT.Clap On!
To turn a socket on, send this message to the MQTT Broker:
{
"topic":"RELAY/RV8.1/CMD",
"datetime":"2020-02-13T13:19:16-07:00",
"id":"RV8.1",
"channel":3,
"command":"on",
"duration":3480
"datetime":"2020-02-13T13:19:16-07:00",
"id":"RV8.1",
"channel":3,
"command":"on",
"duration":3480
Where Channel is the Socket Number [ 1..8 ], the command is "on" and the duration is the maximum number of seconds you want the socket left on.
Clap Off!
The off command is almost identical, with the duration parameter ignored.
{
"topic":"RELAY/RV8.1/CMD",
"datetime":"2020-02-13T13:21:21-07:00",
"id":"RV8.1",
"channel":7,
"command":"off",
"duration":0
"datetime":"2020-02-13T13:21:21-07:00",
"id":"RV8.1",
"channel":7,
"command":"off",
"duration":0
There are two more commands "allon" and "alloff" which affect all of the sockets with a single command.
The TPS Report
Lastly, the system publishes a status message, every minute with the current status of all sockets:
{
"topic":"RELAY/RV8.1/STATUS",
"datetime":"2020-02-13T13:23:00-07:00",
"id":"RV8.1",
"states":[
"duration":0,"id":"RV8.1",
"states":[
"state":"off",
<etc...>
{
No comments :
Post a Comment