We get really cold winters here in Kyiv, Ukraine so central heating is essential to keep our apartments warm. Yet, apart from warming up my home, central heating also dries up the air in my apartment. Dry air parches my sinuses and makes it difficult to get through the winter season without getting sick. Tired of getting sick, I decided to manage the dry air in my home this year. While there are plenty of humidifiers available at the stores, I decided that making a smart, Alexa-powered humidifier for myself would be an interesting challenge.
Reverse Engineering of NumidifierTo control the humidifier module we need know how to turn it on and off and how to get the current status of the humidifier (whether it is turned on or turned off).
On the image we can see the bottom side of the humidifier board. It has a power connector and button. From the module's manual I know that the inner contact of the round power connector is positive power and outer contact is the ground. The module is controlled by clicking the button. On the image we can see that the button has 4 pins: two of them are connected to the ground, one is not connected, and one is connected to route which is connected to 4th pin of the black square chip. To toggle the module we need to short this pin with the ground. I figured out which point on the board is changing its voltage when the module is turning on or turning off by clicking on the button continuously and checking the voltage at random points of the board. The 5th pin of the black square chip is 0v when the humidifier is turned off and 2.5v when it is turned on. So the status of humidifier can be known by reading state on this pin.
Now we have almost everything needed to control this module. The fives pin of the chip is looks like output. I measured the frequency on this pin and it is about 110 kHz. It is not convenient to measure the modulated signal like this with Arduino because it can be HIGH or LOW (at any time) so I decided to use passive integrator circuit to transform PWM signal from module to DC signal easier to read with Arduino.
To make humidifier look nicer I decided to add some back light to it. I chose the WS2812 RGB LEDs for this purpose.
There are many libraries available to control these LEDs and it is easy to connect them using just a few wires.
SchematicsThe connections between all components are exhibited on the schema above. I soldered the wires to the GND, +5v, Button and State pins and inserted into the breadboard. the Rest elements were assembled on the breadboard. I connected the power cord to the breadboard power supply and placed it all under the humidifier's lid.
The CodeThe device connects to the Internet using the MQTT protocol. I used test.mosquito.org for the demonstration because it does not require authentication and does not call for additional settings. After the device connects to the network which you may indicate directly in the sketch code, it connects to broker and subscribes to the following topics:
"humidifier/on" - to receive the "turn on" command
"humidifier/off" - to receive the "turn off" command
"humidifier/reportState" - to receive a status report (on/off) command
"humidifier/reportColorState" - to receive a backlight status report command
"humidifier/color" - to receive a backlight color change command
Whenever the device receives one of these messages, it executes the corresponding command and messages back information about the status of the device.
#include <SoftwareReset.h>
// Select your modem:
#define TINY_GSM_MODEM_ESP8266
//setup pins for your board
#define LED_STRIP_PIN 5
#define TOGGLE_PIN 4
#define STATE_PIN 2
#define ESP_ENABLE_PIN 6
#define LED_NUM 8
uint32_t color;
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel strip = Adafruit_NeoPixel(LED_NUM, LED_STRIP_PIN, NEO_GRB + NEO_KHZ800);
#include <TinyGsmClient.h>
#include <PubSubClient.h>
const char ssid[] = "ssid";
const char pass[] = "password";
//// Use Hardware Serial on Mega, Leonardo, Micro
#define SerialAT Serial1
TinyGsm modem(SerialAT);
TinyGsmClient client(modem);
PubSubClient mqtt(client);
//setup the broker
const char* broker = "test.mosquitto.org";
const char* mqttDeviceId = "yourDeviceId";
const char* mqttUsername = "";
const char* mqttPass = "";
//setup the topics
const char* topicTurnOn = "humidifier/on";
const char* topicTurnOff = "humidifier/off";
const char* topicReportState = "humidifier/reportState";
const char* topicStateReport = "humidifier/stateReport";
const char* topicReportColorState = "humidifier/reportColorState";
const char* topicSetColor = "humidifier/color";
const char* topicColorStateReport = "humidifier/colorStateReport";
long lastReconnectAttempt = 0;
//set the color of the strip or any ws2812 nodule
void setStripColor(uint32_t c) {
// Fill the dots one after the other with a color
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, c);
}
strip.show();
}
// turn on or turn off humidifier
void ToggleHumidifier() {
digitalWrite(TOGGLE_PIN, LOW);
delay(100);
digitalWrite(TOGGLE_PIN, HIGH);
}
void setup() {
pinMode(TOGGLE_PIN, OUTPUT);
digitalWrite(TOGGLE_PIN, HIGH);
pinMode(ESP_ENABLE_PIN, OUTPUT);
digitalWrite(ESP_ENABLE_PIN, HIGH);
pinMode(STATE_PIN, INPUT);
// Set console baud rate
Serial.begin(115200);
delay(100);
// Set GSM module baud rate
SerialAT.begin(115200);
delay(100);
modem.restart();
Serial.print("Connecting to ");
Serial.print(ssid);
if (!modem.networkConnect(ssid, pass)) {
Serial.println(" fail");
SWRST_STD();
}
Serial.println(" OK");
// MQTT Broker setup
mqtt.setServer(broker, 1883);
mqtt.setCallback(mqttCallback);
strip.begin();
strip.show(); // Initialize all pixels to 'off'
setStripColor(strip.Color(0, 0, 0));
}
//connect to broker
boolean mqttConnect() {
Serial.print("Connecting to ");
Serial.print(broker);
if (strlen(mqttUsername) > 0 ) {
if (!mqtt.connect(mqttDeviceId, mqttUsername, mqttPass )) {
Serial.println(" fail");
return false;
}
} else {
if (!mqtt.connect(mqttDeviceId)) {
Serial.println(" fail");
return false;
}
}
Serial.println(" OK");
//subscribing to topics
mqtt.subscribe(topicTurnOn);
mqtt.subscribe(topicTurnOff);
mqtt.subscribe(topicSetColor);
mqtt.subscribe(topicReportState);
mqtt.subscribe(topicReportColorState);
return mqtt.connected();
}
//main loop
void loop() {
if (mqtt.connected()) {
//handle mqtt connection
mqtt.loop();
} else {
// Reconnect every 10 seconds
unsigned long t = millis();
if (t - lastReconnectAttempt > 10000L) {
lastReconnectAttempt = t;
if (mqttConnect()) {
lastReconnectAttempt = 0;
}
}
}
}
//handle mqtt messages
void mqttCallback(char* topic, byte * payload, unsigned int len) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("]: ");
Serial.write(payload, len);
Serial.println();
// Only proceed if incoming message's topic matches
if (String(topic) == topicTurnOn) {
if (!digitalRead(STATE_PIN)) {
Serial.println("turning on");
ToggleHumidifier();
delay(100);
}
mqtt.publish(topicStateReport, digitalRead(STATE_PIN) ? "1" : "0");
} else if (String(topic) == topicTurnOff) {
if (digitalRead(STATE_PIN)) {
Serial.println("turning off");
ToggleHumidifier();
delay(100);
}
mqtt.publish(topicStateReport, digitalRead(STATE_PIN) ? "1" : "0");
} else if (String(topic) == topicSetColor) {
if (len = 3) {
uint8_t r = payload[0];
uint8_t g = payload[1];
uint8_t b = payload[2];
Serial.print("red ");
Serial.println(r);
Serial.print("green ");
Serial.println(g);
Serial.print("blue ");
Serial.println(b);
color = strip.Color(r, g, b);
setStripColor(color);
}
mqtt.publish(topicColorStateReport, String(color).c_str());
} else if (String(topic) == topicReportColorState) {
mqtt.publish(topicColorStateReport, String(color).c_str());
} else if (String(topic) == topicReportState) {
mqtt.publish(topicStateReport, digitalRead(STATE_PIN) ? "1" : "0");
}
}
Smart home skill is based on Lambda, which I created using Pyhton. I used this repository as the groundwork with some modifications. Namely, I added the handling of individual devices in separate files. Secondly, I added file names to the endpoint descriptions. This will allow me to simply add an endpoint according to the set file schema endpoints.json and add the file to the handlers folder whenever adding a new device. I implemented the state reporting for back-light and humidifier so any changes of the device state by voice are visible int the Alexa Companion App. To deploy the skill you need to configure the new smart home skill in the Amazon developer account and configure the Account linking(the easiest way is to use Login With Amazon). Then you need to create a Lambda function and upload the zip archive of /lambda/smartHome/ folder contents from my repository.
Building the DeviceTo build this project, I designed 4 elements to be printed on the 3D printer. These elements were designed using Kompas 3D software.
The water is supplied to the humidifier via a cotton wadding-filled tube, which was printed on a 3D printer. The diffusion ensures that the cotton is evenly dump. Even when there's just a little water left on the bottom of the tank it can still wet all the cotton in the tube and deliver water to piezo element that placed in the top of the tube.
On the photo below you can see all the parts together with cross-section to see whats inside and how to assemble it.
Below you can see the video demonstration of my project.
Comments