Switch

Since each section of the office had its own central switch that would switch the water in the pipes that flowed to the FCUs, this had to be controllable from the backend server as well. Using a latching relay configuration, it was possible to both switch between heating and cooling configurations, as well as have a memory element that could be read out by the node:

This system was assembled on a single board that was used to replace the original manual switch, using the following module to control it:

#include "base_module.h"

class SwitchModule {
static String publishTopic;

public:
static bool initialize();
static bool start();
static bool shutdown();
static void commandCallback(String message);
};

Its implementation is as follows:

#include "switch_module.h"
#include <Wire.h>
#define SW1_SET_PIN 5
#define SW2_SET_PIN 4
#define SW1_READ_PIN 14
#define SW2_READ_PIN 12
String SwitchModule::publishTopic;
enum {
SWITCH_ONE = 0x01,//Switch the first connected load on, second off.
SWITCH_TWO = 0x02,//Switch the second connected load on, first off.
SWITCH_STATE = 0x04,//Returns position of the switch (0x01/0x02).
};
bool SwitchModule::initialize() {
BaseModule::registerModule(MOD_IDX_SWITCH, SwitchModule::start,
SwitchModule::shutdown);
}
bool SwitchModule::start() {
// Register pins.
if (!OtaCore::claimPin(ESP8266_gpio05)) { return false; }
if (!OtaCore::claimPin(ESP8266_gpio04)) { return false; }
if (!OtaCore::claimPin(ESP8266_gpio14)) { return false; }
if (!OtaCore::claimPin(ESP8266_gpio12)) { return false; }
publishTopic = "switch/response/" + OtaCore::getLocation();
OtaCore::registerTopic("switch/" + OtaCore::getLocation(),
SwitchModule::commandCallback);
// Set the pull-ups on the input pins and configure the output pins.
pinMode(SW1_SET_PIN, OUTPUT);
pinMode(SW2_SET_PIN, OUTPUT);
pinMode(SW1_READ_PIN, INPUT_PULLUP);
pinMode(SW2_READ_PIN, INPUT_PULLUP);
digitalWrite(SW1_SET_PIN, LOW);
digitalWrite(SW2_SET_PIN, LOW);
}
bool SwitchModule::shutdown() {
OtaCore::deregisterTopic("switch/" + OtaCore::getLocation());
// Release the pins.
if (!OtaCore::releasePin(ESP8266_gpio05)) { return false; }
if (!OtaCore::releasePin(ESP8266_gpio04)) { return false; }
if (!OtaCore::releasePin(ESP8266_gpio14)) { return false; }
if (!OtaCore::releasePin(ESP8266_gpio12)) { return false; }
}


void SwitchModule::commandCallback(String message) {
// Message is the command.
OtaCore::log(LOG_DEBUG, "Switch command: " + message);

uint32 mlen = message.length();
if (mlen < 1) { return; }
int index = 0;
uint8 cmd = *((uint8*) &message[index++]);
if (cmd == SWITCH_ONE) {
if (mlen > 1) {
// Report failure. QoS 1.
OtaCore::log(LOG_INFO, "Switching to position 1 failed: too many parameters.");
OtaCore::publish(publishTopic, OtaCore::getLocation() + ";" +
(char) 0x01 + (char) 0x00);
return;
}

// Set the relay to its first position (reset condition).
// This causes pins 3 & 10 on the latching relay to become active.
digitalWrite(SW1_SET_PIN, HIGH);
delay(1000); // Wait 1 second for the relay to switch position.
digitalWrite(SW1_SET_PIN, LOW);

OtaCore::log(LOG_INFO, "Switched to position 1.");
OtaCore::publish(publishTopic, OtaCore::getLocation() + ";" +
(char) 0x01 + (char) 0x01);
}
else if (cmd == SWITCH_TWO) {
if (mlen > 1) {
OtaCore::log(LOG_INFO, "Switching to position 2 failed: too many parameters.");
OtaCore::publish(publishTopic, OtaCore::getLocation() + ";" +
(char) 0x02 + (char) 0x00);
return;
}

// Set the relay to its first position (reset condition).
// This causes pins 3 & 10 on the latching relay to become active.
digitalWrite(SW2_SET_PIN, HIGH);
delay(1000); // Wait 1 second for the relay to switch position.
digitalWrite(SW2_SET_PIN, LOW);

OtaCore::log(LOG_INFO, "Switched to position 1.");
OtaCore::publish(publishTopic, OtaCore::getLocation() + ";" +
(char) 0x02 + (char) 0x01);
}
else if (cmd == SWITCH_STATE) {
if (mlen > 1) {
OtaCore::log(LOG_INFO, "Reading state failed: too many parameters.");
OtaCore::publish(publishTopic, OtaCore::getLocation() + ";" +
(char) 0x04 + (char) 0x00);
return;
}

// Check the value of the two input pins. If one is low, then that
// is the active position.
uint8 active = 2;
if (digitalRead(SW1_READ_PIN) == LOW) { active = 0; }
else if (digitalRead(SW2_READ_PIN) == LOW) { active = 1; }

if (active > 1) {
OtaCore::log(LOG_INFO, "Reading state failed: no active state found.");
OtaCore::publish(publishTopic, OtaCore::getLocation() + ";" +
(char) 0x04 + (char) 0x00);
return;
}

OtaCore::publish(publishTopic, OtaCore::getLocation() + ";" +
(char) 0x04 + (char) 0x01 +
(char) active);
}
}

This module is very similar to the PWM and I/O modules, with the registering of an MQTT topic to allow communication using its own binary protocol. Here, the device that is being controlled is fairly simple. It is a latching relay with two sides, one of which is connected to the connections that are being switched between, while the other side is used as a one-bit memory cell.

As both sides of this type of relay will switch simultaneously, we can count on the side connected to the MCU to match the position of that on the side connected to the rest of the system. Even after a power failure or reset of the MCU, we can simply read out the values of the pins connected to the relay to find out the state of the system.

The resulting protocol looks like this:

Command

Meaning

Payload

Return value

0x01

Switch to Position 1

-

0x01

0x00/0x01

0x02

Switch to Position 2

-

0x02

0x00/0x01

0x04

Return the current state

-

0x04

0x00/0x01 (result)

uint8 (active pin 0x00, 0x01)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.178.9