En resumen, este proyecto tiene dos características. Primero, la mesa está conectada desde Google Smart Home a Heroku usando comandos de voz, y segundo: Heroku y la propia mesa se comunican usando el protocolo MQTT Internet of Things. MQTT es una buena solución para el Internet de las cosas, así como para superar algunos otros obstáculos que tendremos que afrontar.
En primer lugar, diré que hice este proyecto solo por diversión. Espero que este artículo le resulte entretenido y le motive a tomarse el tiempo para hacer algo por su cuenta.
Pieza de hardware
La primera y probablemente la parte más difícil del trabajo es rediseñar la mesa. En una vida pasada, la mesa tenía un asa desmontable, estaba ubicada en el borde de la mesa. Al principio pensé en colocar algo en el orificio del asa sin tener que interferir con el diseño de la mesa. Compré varias unidades para averiguar cómo conectar el motor a la mesa, pero fue en vano. Entonces surgió la idea: una varilla, que recorre todo el largo de la mesa, que uniría sus patas para que subieran y bajaran al mismo tiempo. Si coloco una transmisión que se ajusta a la varilla, entonces puedo usar una correa para conectar la varilla al motor. También sería posible equipar la mesa con un motor sin mucha interferencia con su diseño.
La importancia del torque
Después de pedir la transmisión y la correa correctas, comencé a buscar en Amazon un motor de alto par. Y ¡oh, milagro! - ¡He encontrado muchos motores adecuados! O eso me pareció a mí ... Habiendo comprado un pequeño motor, durante aproximadamente un mes esperé su llegada desde China. ¡Estaba tan emocionado cuando finalmente llegó el motor! No podía esperar al fin de semana para finalmente armar todo y tener mi escritorio motorizado.
Las cosas no salieron según lo planeado. Pasé el día haciendo un agujero para una varilla en los paneles metálicos de la mesa. En ese momento, solo tenía herramientas manuales, por lo que el proceso tomó más tiempo de lo que esperaba. Hacia el final del día, terminé de armar la mesa y estaba listo para probarla.
Encendí el motor, el voltaje en la fuente de alimentación de mi escritorio y ... no pasó nada. Momentos después, el motor comenzó a girar y rechinar los dientes del cinturón adquirido. Aprendí dos lecciones de esto: la correa obviamente no está haciendo su trabajo, y la palabra "Motor en alto par" no significa "Puedo levantar nada". La segunda lección es mirar qué tan grande es el motor en comparación con sus dedos. ¡El mío resultó ser diminuto!
A la izquierda de la foto hay un motor y un cinturón. Arriba a la derecha hay un motor acoplado a la mesa (verá más sobre esto más adelante). En la parte inferior derecha, el motor está sobre la mesa.
Motor adecuado
Para seleccionar el motor correcto, era necesario calcular cuánto par se requería para levantar la mesa. Me sorprendió lo fácil que es hacer esto.
El par es la fuerza multiplicada por la longitud del brazo de palanca.
Bueno, tenía un brazo de palanca (este es un mango de mesa), solo era necesario calcular la fuerza que fácilmente haría girar el brazo de palanca. Cargué la mesa atando la jarra de leche al asa y gradualmente agregué agua a la jarra hasta que la palanca comenzó a girar. Al girar la manija hacia arriba con la jarra llena, me aseguré de que el peso gire la manija fácilmente. Descubrí que el brazo de palanca mide 11 cm de largo y la fuerza requerida es de 4 libras. Sustituyendo estos números en la fórmula, descubrí que el motor debe producir un par de al menos 19,95 kg / cm. Y empezó a buscarlo.
Decidí rehacer la mesa de forma irreversible. Sabía que la varilla que atravesaba el centro de la mesa estaba hueca. Después de buscar un motor de doble eje, pude cortar la varilla y volver a montarla con el motor en el medio. Al comprar dos motores con un par de 20 kg / cm, me aseguré de que hubiera suficiente par para levantar la mesa.
Otro hermoso sábado, desmonté mi mesa en cuatro partes, aserrando los ejes del motor para que pudieran usarse al ensamblar la varilla. Hice más agujeros en el metal para que encajaran los motores. Esta vez no había cinturón: los motores estaban conectados directamente a la varilla, los agujeros eran bastante grandes. Al caer la tarde, volví a armar el escritorio y lo cargué con material de oficina.
Las dos fotos superiores son motores completamente montados sobre la mesa. Las dos fotos inferiores son una varilla integrada que corre a lo largo de la mesa con la ayuda de motores.
Conecté los motores y los conecté a la fuente de alimentación. Encendiendo la energía, ¡vi moverse la mesa! Esta vez tuve más confianza, porque dimensioné correctamente los motores. Dupliqué la potencia de los motores en aras de la confianza, ¡pero fue increíble verlos moverse!
Sin embargo, déjeme aclarar que la mesa fue lenta. Grabé un video para mostrarle a un amigo cómo funciona la mesa, pero él tuvo que activar la aceleración de tiempo en el video para no ver cómo la mesa cambia la posición de la mesa durante 5 minutos.
La rotación es importante. Versión final
Finalmente me di cuenta de que todo se reduce a dos cosas: par y revoluciones. Era necesario encontrar un motor con un número suficiente de revoluciones a un par ya conocido.
No fue tan difícil. Aunque no encontré un motor de eje doble, encontré una caja de cambios rectangular que convierte un motor de eje único en un motor de eje doble.
En resumen, el mes siguiente fue un mes de espera por una caja de cambios de China, y el siguiente sábado después de esperar, tenía una mesa moviéndose a la velocidad adecuada.
El último motor en sí está a la izquierda y el motor instalado está a la derecha. Poco hardware y mucho software.
No estaba contento con la enorme fuente de alimentación en mi escritorio, acostado solo para controlar la altura de la mesa. Además, para cambiar la posición de la mesa de una a otra y viceversa, cambié los cables. Pequeño problema, pero el proyecto se hizo idealmente para presionar un botón y tener múltiples ajustes preestablecidos de altura.
Bluetooth
La primera solución fue agregar Bluetooth a la mesa. Al final del día, parece que casi todos los dispositivos de la casa tienen Nluetooth, y el teléfono parece ser una interfaz de control conveniente para algo como mi escritorio.
Así que ahora tengo una placa controladora de motor, una placa bluetooth Nordic NRF52, sensores de distancia y comencé a jugar con el firmware del controlador.
Al final del artículo, dejaré enlaces al software y firmware que escribí para el proyecto. No dude en comentar sobre el código: no soy un desarrollador de firmware profesional y me gustaría recibir alguna orientación.
Como introducción rápida: ESP32 está escrito en C ++ usando bibliotecas Arduino para interactuar con la aplicación BLE Terminal en el teléfono. Instalar y configurar BLE es bastante complejo. Primero, necesita crear todas las características para los valores que le gustaría controlar a través de BLE. Piense en una característica como una variable en su código. BLE envuelve una variable en varios controladores para obtener y establecer el valor de esa variable.
Luego, las características se empaquetan en un servicio con su propio UUID, lo que hace que el servicio sea único e identificable desde la aplicación. Finalmente, debe agregar este servicio a la carga de anuncios para que el dispositivo pueda descubrir su servicio. Cuando un dispositivo remoto se conecta a su servicio y envía datos a través de las especificaciones, la mesa reconoce que el usuario desea ajustar la altura a un valor predeterminado diferente y comienza a moverse.
Para el ajuste de altura, el tablero de la mesa tiene un sensor LiDAR TFMini-S que detecta la altura actual. Este es un sensor divertido: se llama LiDAR cuando en realidad es un láser. Utiliza ópticas y LED para determinar el tiempo de vuelo de la radiación infrarroja. De una forma u otra, el sensor determina la altura de la mesa. El tablero de control luego detecta la diferencia entre la altitud actual y la altitud solicitada y enciende el motor, que gira en la dirección deseada. Algunas de las partes principales del código se muestran a continuación, pero puede ver el archivo completo aquí .
void setup()
{
Serial.begin(115200);
Serial2.begin(TFMINIS_BAUDRATE);
EEPROM.begin(3); // used for saving the height presets between reboots
tfminis.begin(&Serial2);
tfminis.setFrameRate(0);
ledcSetup(UP_PWM_CHANNEL, PWM_FREQUENCY, PWM_RESOLUTION);
ledcAttachPin(UP_PWM_PIN, UP_PWM_CHANNEL);
ledcSetup(DOWN_PWM_CHANNEL, PWM_FREQUENCY, PWM_RESOLUTION);
ledcAttachPin(DOWN_PWM_PIN, DOWN_PWM_CHANNEL);
state_machine = new StateMachine();
state_machine->begin(*t_desk_height, UP_PWM_CHANNEL, DOWN_PWM_CHANNEL);
BLEDevice::init("ESP32_Desk");
...
BLEServer *p_server = BLEDevice::createServer();
BLEService *p_service = p_server->createService(BLEUUID(SERVICE_UUID), 20);
/* ------------------- SET HEIGHT TO PRESET CHARACTERISTIC -------------------------------------- */
BLECharacteristic *p_set_height_to_preset_characteristic = p_service->createCharacteristic(...);
p_set_height_to_preset_characteristic->setCallbacks(new SetHeightToPresetCallbacks());
/* ------------------- MOVE DESK UP CHARACTERISTIC ---------------------------------------------- */
BLECharacteristic *p_move_desk_up_characteristic = p_service->createCharacteristic(...);
p_move_desk_up_characteristic->setCallbacks(new MoveDeskUpCallbacks());
/* ------------------- MOVE DESK UP CHARACTERISTIC ---------------------------------------------- */
BLECharacteristic *p_move_desk_down_characteristic = p_service->createCharacteristic(...);
p_move_desk_down_characteristic->setCallbacks(new MoveDeskDownCallbacks());
/* ------------------- GET/SET HEIGHT 1 CHARACTERISTIC ------------------------------------------ */
BLECharacteristic *p_get_height_1_characteristic = p_service->createCharacteristic(...);
p_get_height_1_characteristic->setValue(state_machine->getHeightPreset1(), 1);
BLECharacteristic *p_save_current_height_as_height_1_characteristic = p_service->createCharacteristic(...);
p_save_current_height_as_height_1_characteristic->setCallbacks(new SaveCurrentHeightAsHeight1Callbacks());
/* ------------------- GET/SET HEIGHT 2 CHARACTERISTIC ------------------------------------------ */
...
/* ------------------- GET/SET HEIGHT 3 CHARACTERISTIC ------------------------------------------ */
...
/* ------------------- END CHARACTERISTIC DEFINITIONS ------------------------------------------ */
p_service->start();
BLEAdvertising *p_advertising = p_server->getAdvertising();
p_advertising->start();
xTaskCreate(
updateDeskHeight, // Function that should be called
"Update Desk Height", // Name of the task (for debugging)
1024, // Stack size
NULL, // Parameter to pass
5, // Task priority
NULL // Task handle
);
}
Hay mucho más en el archivo, pero este código tiene suficiente contexto para comprender lo que está sucediendo. Tenga en cuenta que creamos y configuramos todas las devoluciones de llamada BLE para todas las características, incluido el movimiento manual, el establecimiento y la recuperación de valores preestablecidos y, lo más importante, la alineación de la tabla con el preestablecido.
La siguiente imagen muestra la interacción con las características para ajustar la altura de la mesa. La última pieza del rompecabezas es una máquina de estado que conoce la altura actual de la mesa, la altura deseada por el usuario y trabaja con estos dos valores.
Así que finalmente tuve una mesa que hacía lo que quería. Podría guardar la altura en presets y extraer las alturas de la memoria para poner la mesa en mis posiciones favoritas. Solía BLE Terminalen mi teléfono y computadora, para poder enviar mensajes sin procesar a mi escritorio y monitorear su posición. Funcionó, pero sabía que la batalla con BLE apenas comenzaba.
Interfaz bluetooth desnuda ... Todo lo que quedaba por el momento era aprender a escribir aplicaciones para iOS ...
Después de todo esto, mi esposa dijo algo que cambió todo el proyecto: "¿Y si haces el control de tu voz?"
Además de ser genial y agregar un nuevo dispositivo a la lista del Asistente de Google, no es necesario escribir una aplicación iOS para controlar la mesa. Y ya no tuvo que alcanzar su teléfono para ajustar la altura. ¡Otra pequeña victoria!
Añadiendo el Internet de las cosas
Ahora hablemos de cómo actualizar su escritorio al control por voz a través de Google Smart Home y cómo hacerlo compatible con Wi-Fi.
Agregar Wi-Fi fue bastante fácil. Reemplacé el microcontrolador Nordic NRF52 por un ESP32 con WiFi incorporado. La mayor parte del software era portátil porque estaba escrito en C ++, y ambos dispositivos se podían programar con Platform.IO y las bibliotecas Arduino, incluido el tfmini-s que escribí para medir la altura actual de la mesa.
A continuación se muestra la arquitectura del sistema de interacción de la mesa con Google Smart Home. Hablemos de la interacción entre Google y yo.
Entonces Bluetooth se ha encendido. Es hora de descubrir cómo interactuar con Google Smart Home. Esta tecnología controlaba el hogar mediante Smart Home Actions . Lo interesante de sus acciones es que el servicio actúa como un servidor OAuth2, no como un cliente. La mayor parte del trabajo realizado con el servidor ha sido implementar una aplicación OAuth2 Node.js Express que llega a Heroku y se comunica como un proxy entre Google y mi escritorio.
Tuve suerte: hubo una implementación de servidor decente usando dos bibliotecas. La primera biblioteca, node-oauth2-server, se encontró aquí . La segunda biblioteca express-oauth-server para la conexión Express se encontró aquí .
const { Pool } = require("pg");
const crypto = require("crypto");
const pool = new Pool({
connectionString: process.env.DATABASE_URL
});
module.exports.pool = pool;
module.exports.getAccessToken = (bearerToken) => {...};
module.exports.getClient = (clientId, clientSecret) => {...};
module.exports.getRefreshToken = (bearerToken) => {...};
module.exports.getUser = (email, password) => {...};
module.exports.getUserFromAccessToken = (token) => {...};
module.exports.getDevicesFromUserId = (userId) => {...};
module.exports.getDevicesByUserIdAndIds = (userId, deviceIds) => {...};
module.exports.setDeviceHeight = (userId, deviceId, newCurrentHeight) => {...};
module.exports.createUser = (email, password) => {...};
module.exports.saveToken = (token, client, user) => {...};
module.exports.saveAuthorizationCode = (code, client, user) => {...};
module.exports.getAuthorizationCode = (code) => {...};
module.exports.revokeAuthorizationCode = (code) => {...};
module.exports.revokeToken = (code) => {...};
Luego viene la configuración de la propia aplicación Express. A continuación, se muestran los puntos finales necesarios para un servidor OAuth, pero puede leer el archivo completo aquí.
const express = require("express");
const OAuth2Server = require("express-oauth-server");
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
const flash = require("express-flash-2");
const session = require("express-session");
const pgSession = require("connect-pg-simple")(session);
const morgan = require("morgan");
const { google_actions_app } = require("./google_actions");
const model = require("./model");
const { getVariablesForAuthorization, getQueryStringForLogin } = require("./util");
const port = process.env.PORT || 3000;
// Create an Express application.
const app = express();
app.set("view engine", "pug");
app.use(morgan("dev"));
// Add OAuth server.
app.oauth = new OAuth2Server({
model,
debug: true,
});
// Add body parser.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static("public"));
// initialize cookie-parser to allow us access the cookies stored in the browser.
app.use(cookieParser(process.env.APP_KEY));
// initialize express-session to allow us track the logged-in user across sessions.
app.use(session({...}));
app.use(flash());
// This middleware will check if user's cookie is still saved in browser and user is not set, then automatically log the user out.
// This usually happens when you stop your express server after login, your cookie still remains saved in the browser.
app.use((req, res, next) => {...});
// Post token.
app.post("/oauth/token", app.oauth.token());
// Get authorization.
app.get("/oauth/authorize", (req, res, next) => {...}, app.oauth.authorize({...}));
// Post authorization.
app.post("/oauth/authorize", function (req, res) {...});
app.get("/log-in", (req, res) => {...});
app.post("/log-in", async (req, res) => {...});
app.get("/log-out", (req, res) => {...});
app.get("/sign-up", async (req, res) => {...});
app.post("/sign-up", async (req, res) => {...});
app.post("/gaction/fulfillment", app.oauth.authenticate(), google_actions_app);
app.get('/healthz', ((req, res) => {...}));
app.listen(port, () => {
console.log(`Example app listening at port ${port}`);
});
Hay bastante código, pero explicaré los puntos principales. Las dos rutas OAuth2 que se utilizan para el servidor son / oauth / token y / oauth / authorize. Se utilizan para recibir un nuevo token o actualizar los tokens caducados. A continuación, debe hacer que el servidor responda a la acción de Google. Notará que el punto final de / gaction / fulment apunta a un objeto
google_actions_app
.
Google envía solicitudes a su servidor en un formato específico y proporciona una biblioteca para ayudar a procesar esas solicitudes. A continuación se muestran las funciones necesarias para comunicarse con Google, y el archivo completo está aquí. Finalmente, está el punto final / healthz, que cubriré al final del artículo.
El punto final / gaction / fulment usa un middleware llamado app.oauth.authenticate (), el arduo trabajo de hacer que el servidor OAuth2 funcione fue lograr que este middleware funcionara. Verifica que el token de portador que nos proporcionó Google se refiere a un usuario existente y no ha caducado. Luego, la ruta envía la solicitud y la respuesta al objeto
google_actions_app
.
Google envía solicitudes a su servidor en un formato específico y proporciona una biblioteca para ayudar a analizar y procesar esas solicitudes. A continuación se muestran las funciones que necesita para comunicarse con Google, pero puede ver el archivo completo aquí .
const { smarthome } = require('actions-on-google');
const mqtt = require('mqtt');
const mqtt_client = mqtt.connect(process.env.CLOUDMQTT_URL);
const model = require('./model');
const { getTokenFromHeader } = require('./util');
mqtt_client.on('connect', () => {
console.log('Connected to mqtt');
});
const updateHeight = {
"preset one": (deviceId) => {
mqtt_client.publish(`/esp32_iot_desk/${deviceId}/command`, "1");
},
"preset two": (deviceId) => {
mqtt_client.publish(`/esp32_iot_desk/${deviceId}/command`, "2");
},
"preset three": (deviceId) => {
mqtt_client.publish(`/esp32_iot_desk/${deviceId}/command`, "3");
},
};
const google_actions_app = smarthome({...});
google_actions_app.onSync(async (body, headers) => {...});
google_actions_app.onQuery(async (body, headers) => {...});
google_actions_app.onExecute(async (body, headers) => {...});
module.exports = { google_actions_app };
Cuando agrega una acción inteligente a su cuenta de Google, Google realizará una solicitud de sincronización. Esta solicitud le permite saber qué dispositivos están disponibles en su cuenta. Luego viene una consulta de sondeo: Google consulta sus dispositivos para determinar su estado actual.
Cuando agregue por primera vez una acción de Google a su cuenta de Smart Home, notará que Google primero envía una solicitud de sincronización y luego una solicitud de sondeo para obtener una vista holística de sus dispositivos. La última es la solicitud, es una solicitud para cumplir que Google le dice a sus dispositivos que hagan algo.
"Funciones" (rasgo) del dispositivo Google Smart Home
Google utiliza funciones específicas del dispositivo para proporcionar elementos de interfaz de usuario para controlar sus dispositivos a Google y para crear plantillas de comunicación de control por voz. Algunas de las funciones incluyen las siguientes configuraciones: ColorSetting, Modos, OnOff y StartStop. Me tomó un tiempo decidir qué función funcionaría mejor en mi aplicación, pero luego elegí modos.
Puede pensar en los modos como una lista desplegable donde se selecciona uno de los N valores predefinidos o, en mi caso, una altura preestablecida. Llamé a mi modo "altura" y los valores posibles son "preajuste uno", "preajuste dos" y "preajuste tres". Esto me permite controlar mi escritorio diciendo: "Oye Google, configura la altura de mi escritorio en uno predeterminado", y Google enviará una solicitud de ejecución correspondiente a mi sistema. Puede leer más sobre las funciones de los dispositivos de Google aquí .
Proyecto en acción
Finalmente, Google Smart Home y mi computadora comenzaron a comunicarse. Antes de eso, usé ngrok para ejecutar el servidor Express localmente . Ahora que mi servidor finalmente funciona lo suficientemente bien, es hora de ponerlo a disposición de Google en cualquier momento. Por lo tanto, era necesario alojar la aplicación en Heroku : este es un proveedor de PaaS que facilita la implementación y la administración de aplicaciones.
Una de las principales ventajas de Heroku es el modo adicional. Con los complementos, es muy fácil agregar CloudMQTT y el servidor Postgres a su aplicación. Otro beneficio de usar Heroku es su facilidad de ensamblaje e implementación. Heroku detecta automáticamente qué código está utilizando y lo compila / implementa por usted. Puede encontrar más detalles sobre esto leyendo sobre Heroku Buildpacks . En mi caso, cada vez que presiono el código para git remote Heroku, instala todos mis paquetes, elimina todas las dependencias de desarrollo y despliega la aplicación, todo con un simple comando "git push heroku main".
Con solo unos pocos clics, CloudMQTT y Postgres estaban disponibles para mi aplicación, y solo necesitaba usar algunas variables de entorno para integrar estos servicios con mi aplicación. Heroku no pidió dinero. Sin embargo, CloudMQTT es un complemento de terceros por $ 5 por mes.
Creo que la necesidad de Postgres se explica por sí misma, pero CloudMQTT merece más atención.
De Internet a una red privada. El camino difícil
Hay varias formas de dar acceso a una aplicación o, en mi caso, a un dispositivo IoT. La primera es abrir un puerto en mi red doméstica para llevar el dispositivo a Internet. En este caso, mi aplicación Heroku Express enviará una solicitud a mi dispositivo utilizando la dirección IP pública. Esto requeriría que tuviera una IP estática pública, así como una IP estática para el ESP32. ESP32 también tendría que actuar como un servidor HTTP y escuchar las instrucciones de Heroku todo el tiempo. Esta es una gran sobrecarga para un dispositivo que recibe instrucciones varias veces al día.
El segundo método se llama "perforadora". Con él, puede usar un servidor externo de terceros para acceder al dispositivo a Internet sin tener que reenviar puertos Su dispositivo básicamente se conecta a un servidor que configura un puerto abierto. Luego, otro servicio puede conectarse directamente a su dispositivo interno obteniendo un puerto abierto desde el servidor externo. Finalmente, se conecta directamente al dispositivo usando este puerto abierto. El enfoque puede o no ser del todo correcto: solo leí parte del artículo al respecto.
Están sucediendo muchas cosas dentro de la "perforadora", y no entiendo completamente lo que está sucediendo. Sin embargo, si está interesado, hay algunos artículos interesantes que explican más. Aquí hay dos artículos que leí para comprender mejor el perforador: Wikipediay un artículo del MIT de Brian Ford y otros .
De Internet a una red privada a través de IoT
No estaba muy contento con estas soluciones. Conecté muchos dispositivos inteligentes a mi casa y nunca tuve que abrir un puerto en mi enrutador, por lo que no hubo reenvío de puertos. Además, hacer agujeros parece mucho más difícil de lo que estoy buscando y es más adecuado para redes P2P. Como resultado de más investigaciones, descubrí MQTT y aprendí que es un protocolo para IoT. Tiene algunas ventajas, como bajo consumo de energía, tolerancia a fallas configurable y no requiere reenvío de puertos. MQTT es un protocolo de editor / suscriptor, lo que significa que la tabla es el suscriptor de un tema determinado y la aplicación Heroku es el editor de este tema.
Entonces Google contacta a Heroku, esta solicitud es analizada para determinar el dispositivo solicitado y su nuevo estado o modo. La aplicación Heroku luego publica un mensaje en el servidor CloudMQTT implementado como un complemento de Heroku que le indica a la tabla que navegue hasta el nuevo ajuste preestablecido. Finalmente, la mesa se suscribe al tema y recibe un mensaje publicado por la aplicación Heroku, ¡finalmente, la mesa ajusta su altura según lo solicitado! En el archivo googleactionsapp, observará que hay una función updateHeight que publica un número MQTT para un ID de dispositivo específico. Así es como la aplicación Heroku publica una solicitud de movimiento de tabla en MQTT.
El último paso es recibir el mensaje en el ESP32 y mover la mesa. Le mostraré algunos de los aspectos más destacados del código de la tabla a continuación, y todo el código fuente está aquí .
void setup()
{
Serial.begin(115200);
...
tfminis.begin(&Serial2);
tfminis.setFrameRate(0);
...
state_machine = new StateMachine();
state_machine->begin(*t_desk_height, UP_PWM_CHANNEL, DOWN_PWM_CHANNEL);
setup_wifi();
client.setServer(MQTT_SERVER_DOMAIN, MQTT_SERVER_PORT);
client.setCallback(callback);
...
}
Cuando la mesa está cargada, primero iniciamos la comunicación entre el TFMini-S, el sensor de distancia, para obtener la altura actual de la mesa. Luego configuramos la máquina de estado para el movimiento de la mesa. La máquina de estado recibe comandos a través de MQTT y luego es responsable de hacer coincidir la solicitud del usuario con la altura real de la mesa según la lectura del sensor de distancia. Finalmente, nos conectamos a la red Wi-Fi, nos conectamos al servidor MQTT y configuramos una devolución de llamada para cualquier dato recibido sobre el tema MQTT al que estamos suscritos. A continuación, mostraré la función de devolución de llamada.
void callback(char *topic, byte *message, unsigned int length)
{
...
String messageTemp;
for (int i = 0; i < length; i++)
{
messageTemp += (char)message[i];
}
if (messageTemp == "1") {
state_machine->requestStateChange(ADJUST_TO_PRESET_1_HEIGHT_STATE);
}
if (messageTemp == "2") {
state_machine->requestStateChange(ADJUST_TO_PRESET_2_HEIGHT_STATE);
}
if (messageTemp == "3") {
state_machine->requestStateChange(ADJUST_TO_PRESET_3_HEIGHT_STATE);
}
...
}
La máquina de estado registra el cambio de estado recibido en el tema MQTT. Luego procesa el nuevo estado en el bucle principal.
void loop()
{
if (!client.connected())
{
reconnect();
}
client.loop();
state_machine->processCurrentState();
}
El bucle principal hace varias cosas: primero, se vuelve a conectar al servidor MQTT si aún no está conectado. Luego procesa todos los datos recibidos a través del tema MQTT. Finalmente, el código funciona moviendo la mesa a la ubicación deseada solicitada en el tema MQTT.
¡Eso es todo! La mesa está totalmente controlada por voz y se comunica con Google para recibir comandos.
Notas recientes
El último punto final que no mencioné es el punto final / healthz. Esto se debe al hecho de que Google espera una respuesta bastante rápida y, en mi caso, cargar la aplicación Heroku en cada solicitud no funciona. Configuré un servicio de ping para hacer ping al punto final / healthz cada minuto para mantener el servicio en buen estado y listo para responder. Si planea hacer algo como esto, recuerde que todas las horas libres en el stand se dedicarán a ello. Todo está bien ahora: esta es la única aplicación utilizada en Heroku. Además, por $ 7 al mes, puede actualizar al plan Hobby de Heroku que mantiene la aplicación en funcionamiento.
La creación de un dispositivo IoT conlleva muchos gastos generales al principio. Diseñé el hardware, construí el esquema de control, configuré el servidor MQTT, escribí el servidor Express OAuth2 y aprendí cómo interactuar con Google Smart Home a través de acciones. La sobrecarga inicial fue enorme, ¡pero siento que he logrado mucho! Sin mencionar que MQTT Server, Express OAuth2 Application Server y Google Smart Home Actions se pueden usar para otro proyecto. Estoy interesado en hogares inteligentes y puedo intentar expandir mi repertorio de dispositivos de IoT para incluir sensores que rastrean lo que está sucediendo en mi casa e informan a través de MQTT. Los sensores para monitorear el suelo, la temperatura y los sensores de luz serán muy interesantes de monitorear y analizar.
¿Que sigue?
Las alturas de las encimeras ahora se miden como poco confiables. En general, estoy usando un sensor de distancia por infrarrojos TFMini-S que funciona. Se ha observado que la altura de la mesa cambia ligeramente durante el día cuando cambia la iluminación ambiental de la habitación. Ordené un sensor de ángulo de rotación para contar las revoluciones de una varilla a través de la mesa. Esto debería darme un movimiento más preciso en cualquier momento del día. También tengo acceso al servidor que tengo en el sótano. En él puedo explorar mi propio servidor Mosquitto MQTT, aplicaciones Node-RED y Express OAuth2 si quiero alojar algo yo mismo. Finalmente, ahora toda la electrónica está en mi escritorio. ¡Planeo organizar los dispositivos para que todo sea agradable y ordenado!
¡Gracias por leer el articulo! Por conveniencia, doy todos los enlaces.
- Torque Calculator
- 90 degree right angle gear box
- BLE Terminal
- Platform.IO
- TFMini-S Arduino Driver
- Google Smart Home Actions
- Node OAuth2 Server
- Express OAuth2 Server
- ESP32 IoT Desk Server model.js
- ESP32 IoT Desk Server index.js
- ESP32 IoT Desk Server google_actions.js
- Google Smart Home Device Traits
- NGROK
- ESP32 IoT Desk Firmware
- Node-RED
- Heroku
- Heroku Hobby Plan
- Heroku Buildpacks
- Wikipedia Hole Punching
- MIT Paper on Hole Punching by Bryan Ford et al.