donutsorelse
Published © GPL3+

Smart Cabinets with a Custom Smart Home

Automatic/Smart Cabinets that are voice activated through a custom Smart Home network.

AdvancedFull instructions providedOver 1 day326

Things used in this project

Hardware components

M5Stack CM4Stack Development Kit
×1
UNIHIKER - IoT Python Programming Single Board Computer with Touchscreen
DFRobot UNIHIKER - IoT Python Programming Single Board Computer with Touchscreen
×1
FireBeetle ESP32 IOT Microcontroller (Supports Wi-Fi & Bluetooth)
DFRobot FireBeetle ESP32 IOT Microcontroller (Supports Wi-Fi & Bluetooth)
×1
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
×1
SG90 Micro-servo motor
SG90 Micro-servo motor
×2

Software apps and online services

Arduino IDE
Arduino IDE
DFRobot Mind+
Thonny

Story

Read more

Schematics

Automatic Cabinet Schematic

This uses an ultrasonic sensor and a servo to automatically open and close a cabinet by proximity.

Smart Cabinets Schematic

This controls 2 servos to demonstrate controlling multiple cabinets with an ESP32.

Code

voice_controller.py

Python
This runs on the UNIHIKER to listen for voice commands that can then communicate with the flask server and control our Smart Cabinets
# -*- coding: UTF-8 -*-

import sys
import speech_recognition as sr
import time
import paho.mqtt.publish as publish

# MQTT Broker Settings
BROKER = "192.168.86.84"  # Change to your MQTT broker's IP address - this is the default one
PORT = 1883
TOPIC = "home/<topic>"
USERNAME = "<username>"  # Change to your MQTT username
PASSWORD = "<password>"  # Change to your MQTT password

said_butler = False  # To check if the last word we heard was "butler" so we know to send the next word(s) as a command

def listen_for_commands(recognizer, audio):
    global said_butler
    """Callback function that processes the audio as soon as it is available."""
    try:
        # Recognize speech using Google's speech recognition
        command = recognizer.recognize_google(audio).lower()
        print("You said:", command)
        
        # Check if the command contains the wake word 'butler'
        if 'butler' in command:
            said_butler = True  # Set flag to true to send next commands
            command = command.split('butler', 1)[1].strip()  # Get everything after 'butler'

        if said_butler and command:  # If the flag is set and there is a command
            print("Command received:", command)
            words = command.split()
            if len(words) > 0:
                first_word = words[0]
                print("First word received:", first_word)
                # Send the first word as a separate command to the MQTT broker
                publish.single(TOPIC, first_word, hostname=BROKER, port=PORT, auth={'username': USERNAME, 'password': PASSWORD})
            
            # Send the full command to the MQTT broker
            publish.single(TOPIC, command, hostname=BROKER, port=PORT, auth={'username': USERNAME, 'password': PASSWORD})
            print("Commands sent to the Flask server via MQTT.")
            said_butler = False  # Reset flag after command is sent

    except sr.UnknownValueError:
        print("Google Speech Recognition could not understand the audio")
    except sr.RequestError as e:
        print(f"Could not request results from Google Speech Recognition service; {e}")

def main():
    # Initialize recognizer class (for recognizing the speech)
    recognizer = sr.Recognizer()
    microphone = sr.Microphone()

    # Adjust the recognizer sensitivity to ambient noise and record audio
    with microphone as source:
        recognizer.adjust_for_ambient_noise(source)
        print("Set minimum energy threshold to:", recognizer.energy_threshold)
        recognizer.pause_threshold = 0.8  # Adjust based on testing; default is 0.8 seconds
        recognizer.non_speaking_duration = 0.4  # Adjust based on testing; default is 0.5 seconds

    # Start listening in the background (non-blocking)
    stop_listening = recognizer.listen_in_background(microphone, listen_for_commands, phrase_time_limit=5)

    # Keep the main thread alive, or the background listener will stop
    try:
        while True:
            time.sleep(0.1)  # Sleep briefly to limit CPU usage
    except KeyboardInterrupt:
        stop_listening(wait_for_stop=False)  # Stop listening when Ctrl+C is pressed
        print("Stopped listening...")

if __name__ == "__main__":
    main()

SmartCabinetsPlural_Generified.ino

Arduino
This is an example of how to have one microcontroller run multiple smart cabinets, as well as demonstrating the logic tweaks for handling multiple cabinets in general.
#include <WiFi.h>
#include <PubSubClient.h>
#include <ESP32Servo.h>

const char* ssid = "<Your Wifi>";
const char* password = "<Your wifi password>";

//MQTT Broker settings
const char* mqtt_server = "<server ip>";  // MQTT broker IP address
const int mqtt_port = 1883;
const char* mqtt_user = "<usernames>";
const char* mqtt_password = "<password>";
const char* mqtt_topic = "home/<topic>";

//Servo settings
const int servoPinDishwasher = 32;
const int servoPinStove = 33;
Servo servoDishwasher;
Servo servoStove;
int servoOpenPosition = 180;  // Position to open the cabinet - change as needed
int servoClosePosition = 0;   // Position to close the cabinet - change as needed
bool isDishwasherOpen = false;
bool isStoveOpen = false;

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  Serial.begin(115200);
  delay(5000);

  servoDishwasher.attach(servoPinDishwasher);
  servoStove.attach(servoPinStove);
  servoDishwasher.write(servoClosePosition);
  servoStove.write(servoClosePosition);

  setup_wifi();
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
}

void setup_wifi() {
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;

  for (int i = 0; i < length; i++) {
    messageTemp += (char)message[i];
  }
  Serial.println(messageTemp);

  // Check to use specific cabinets
  if (messageTemp == "dishwasher cabinet") {
    if (isDishwasherOpen) {
      servoDishwasher.write(servoClosePosition);
      isDishwasherOpen = false;
      Serial.println("Dishwasher Cabinet closed");
    } else {
      servoDishwasher.write(servoOpenPosition);
      isDishwasherOpen = true;
      Serial.println("Dishwasher Cabinet opened");
    }
  } else if (messageTemp == "stove cabinet") {
    if (isStoveOpen) {
      servoStove.write(servoClosePosition);
      isStoveOpen = false;
      Serial.println("Stove Cabinet closed");
    } else {
      servoStove.write(servoOpenPosition);
      isStoveOpen = true;
      Serial.println("Stove Cabinet opened");
    }
  }
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
      Serial.println("connected");
      client.subscribe(mqtt_topic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

smartCabinet_Generified.ino

Arduino
This is the code for a singular smart cabinet, run on an ESP32.
#include <WiFi.h>
#include <PubSubClient.h>
#include <ESP32Servo.h>

const char* ssid = "<Your Wifi>";
const char* password = "<Your wifi password>";

//MQTT Broker settings
const char* mqtt_server = "<server ip>";  // MQTT broker IP address
const int mqtt_port = 1883;
const char* mqtt_user = "<usernames>";
const char* mqtt_password = "<password>";
const char* mqtt_topic = "home/<topic>";

const int servoPin = 32;
Servo myServo;
int servoOpenPosition = 180;  // Position to open the cabinet - change as needed
int servoClosePosition = 0;   // Position to close the cabinet - change as needed
bool isCabinetOpen = false;   // Initial state of the cabinet

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  Serial.begin(115200);  // Lowering baud rate for troubleshooting
  delay(5000);

  myServo.attach(servoPin);
  myServo.write(servoClosePosition);  // Ensure servo starts in the closed position

  setup_wifi();
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
}

void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;

  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  if (messageTemp == "cabinet") {
    if (isCabinetOpen) {
      myServo.write(servoClosePosition);
      isCabinetOpen = false;
      Serial.println("Cabinet closed");
    } else {
      myServo.write(servoOpenPosition);
      isCabinetOpen = true;
      Serial.println("Cabinet opened");
    }
  }
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect("ESP32Client", mqtt_user, mqtt_password)) {
      Serial.println("connected");
      // Subscribe to the topic
      client.subscribe(mqtt_topic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");

      delay(5000);
    }
  }
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

automatic_cabinet.ino

Arduino
This is the setup for an automatic cabinet that opens based on proximity and shuts after a given amount of time.
#include <Servo.h>

const int trigPin = 7;
const int echoPin = 6;
const int servoPin = 9;

Servo myServo;

//These are the angles that were good for my setup - change based on what works for yours!
const int openAngle = 0;
const int closeAngle = 90;

const int distanceThreshold = 30; //Setting the threshold to around 1 ft

long getDistance() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  long duration = pulseIn(echoPin, HIGH);
  long distance = duration * 0.034 / 2; //Convert to cm

  return distance;
}

void setup() {
  Serial.begin(115200);

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  myServo.attach(servoPin);

  Serial.println("Setup complete.");
}

void loop() {
  long distance = getDistance();

  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.println(" cm");

  if (distance <= distanceThreshold) {
    myServo.write(openAngle);
    Serial.println("Opening cabinet");
    delay(60000); //Sleep for 1 minute to leave the cabinet open that long
    Serial.println("Done sleeping.");
  } else {
    myServo.write(closeAngle);
    Serial.println("Closing cabinet");
  }

  delay(500); //Half a second delay
}

smart_house.py

Python
This is what we use to run our Flask server on the CM4Stack. This acts as the middleman between giving the voice commands and the Smart Cabinets.
from flask import Flask, request, jsonify
import paho.mqtt.client as mqtt
import logging
import os

# Configuration parameters
broker_address = os.getenv('MQTT_BROKER_ADDRESS', '192.168.86.84')  # Default to internal IP address
mqtt_topic = os.getenv('MQTT_TOPIC', 'home/<topic>')
http_server_port = int(os.getenv('HTTP_SERVER_PORT', 5000))
mqtt_username = os.getenv('MQTT_USERNAME', '<username>')
mqtt_password = os.getenv('MQTT_PASSWORD', '<password>')

# Create Flask app
app = Flask(__name__)

# Setup logging
logging.basicConfig(level=logging.INFO)

# MQTT setup
mqtt_client = mqtt.Client(client_id="<client id>", protocol=mqtt.MQTTv311)
mqtt_client.username_pw_set(username=mqtt_username, password=mqtt_password)

# Define on_connect callback
def on_connect(client, userdata, flags, rc):
    if rc == 0:
        logging.info("Connected to MQTT Broker")
        client.subscribe(mqtt_topic)
    elif rc == 5:
        logging.error("Authentication failed - check username and password")
    else:
        logging.error(f"Failed to connect, return code {rc}")

# Define on_message callback
def on_message(client, userdata, message):
    logging.info(f"Received message: {message.payload.decode()} on topic {message.topic}")

mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message

try:
    mqtt_client.connect(broker_address)
    mqtt_client.loop_start()
except Exception as e:
    logging.error(f"Failed to connect to MQTT Broker: {e}")

@app.route('/command', methods=['POST'])
def handle_command():
    try:
        data = request.json
        action = data.get('action', '')
        if action:
            mqtt_client.publish(mqtt_topic, action)
            logging.info(f"Command '{action}' sent to MQTT topic.")
            return jsonify({"status": "success", "message": f"Command '{action}' sent to MQTT topic."}), 200
        logging.warning("No action specified in the command.")
        return jsonify({"status": "error", "message": "No action specified in the command."}), 400
    except Exception as e:
        logging.error(f"Error handling command: {e}")
        return jsonify({"status": "error", "message": "Failed to process command."}), 500

if __name__ == '__main__':
    try:
        app.run(host='0.0.0.0', port=http_server_port)
    except Exception as e:
        logging.error(f"Exception occurred: {e}")
    finally:
        mqtt_client.loop_stop()
        mqtt_client.disconnect()

Credits

donutsorelse

donutsorelse

15 projects • 15 followers
I make different stuff every week of all kinds. Usually I make funny yet useful inventions.

Comments