Buenos días
Estoy haciendo un proyecto para calcular un peso determinado con arduino. El proyecto basicamente consiste en verificar que el usuario ha puesto un peso especifico y activar un electroiman que abrirá un cajón.
Lo estoy haciendo con una WEMOS D1R2 (WEMOS A) ya que mi cliente desea que vaya por wifi. Estoy haciendo uso de Node-Red y MQTT.
Mi cliente quiere que además de poder activar el electroiman a través de la consola de Node-red, tener un control manual, por botón (un switch on-off), para poder activarlo si el usuario no consigue poner el peso indicado por lo que entiendo que tendré que usar otra WEMOS B
La duda que tengo es:
¿Necesito configurar una tarjeta como servidor y otro como cliente?. Ahora mismo, el WEMOS A lo tengo definido como cliente.
¿Puedo publicar mensajes desde el WEMOS B para que se active el electroiman en el WEMOS A?
Os añado mi esquema, código y Node-Red por si me podeis ayudar.
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <HX711.h>
const int DOUT=16; //GPIO16 - D0
const int CLK=5; //GPIO05 - D1
const byte lockPin=4; // GPIO04 - D2 A este pin se le enviará un pulso ALTO cuando se ingrese la solución correcta para abrir cajón
// Modifica estos valores para tu red.
const char* ssid = "***********";
const char* password = "*********";
//const char* ssid = "***********";
//const char* password = "*************";
const char* mqtt_server = "broker.mqtt-dashboard.com";
WiFiClient espClient;
PubSubClient client(espClient);
int value = 0;
// Timers auxiliar variables
long now = millis();
long lastMeasure = 0;
HX711 balanza;
void(* Resetea) (void) = 0;
void setup_wifi() {
delay(10);
// Arrancamos la conexión via WIFI a la red
Serial.println();
Serial.print("Conectando a... ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi conectada");
Serial.print("Dirección IP: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Llega mensaje [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
char receivedChar = (char)payload[i];
Serial.print(receivedChar);
ctrlMensajes(topic, receivedChar);
}
Serial.println();
}
void ctrlMensajes(String topic, byte receivedChar){
if (topic == "peso/desbloquear"){
if(receivedChar == '1'){
Serial.println("Control wifi resuelve");
desbloquea();
} else {
Serial.println("Control wifi NO resuelve");
digitalWrite(lockPin, LOW);
delay(50);
digitalWrite(lockPin, HIGH);
}
}
if (topic == "peso/reset"){
if(receivedChar == '1'){
Serial.println("Control wifi resetea");
Resetea();
}
}
if (topic == "peso/boton/desbloquear"){
if(receivedChar == '0'){
Serial.println("Control botón resuelve");
desbloquea();
} else {
Serial.println("Control botón NO resuelve");
digitalWrite(lockPin, LOW);
delay(50);
digitalWrite(lockPin, HIGH);
}
}
if (topic == "peso/boton/reset"){
if(receivedChar == '0'){
Serial.println("Control botón resetea");
Resetea();
}
}
}
void reconnect() {
// Bucle hasta reconectar
while (!client.connected()) {
Serial.print("Esperando conexión MQTT...");
// Crea un client ID aleatorio
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
// Esperando conectar
if (client.connect(clientId.c_str())) {
Serial.println("conectado");
// Una vez conectado, publica...
// ... y subscribe
client.subscribe("peso/desbloquear");
//client.subscribe("peso/control");
client.subscribe("peso/reset");
} else {
Serial.print("fallo, rc=");
Serial.print(client.state());
Serial.println(" intentalo de nuevo en 5 segundos");
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
balanza.begin(DOUT, CLK);
pinMode(lockPin, OUTPUT); // Pone el lockPin como salida
digitalWrite(lockPin, HIGH);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
Serial.print("Lectura del valor del ADC: ");
Serial.println(balanza.read());
Serial.println("No ponga ningun objeto sobre la balanza");
Serial.println("Destarando...");
Serial.println("...");
balanza.set_scale(401842.69); // Establecemos la escala
balanza.tare(20); //El peso actual es considerado Tara.
Serial.println("Listo para pesar");
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
now = millis();
// Publica nuevo peso cada medio segundos
if (now - lastMeasure > 500) {
lastMeasure = now;
Serial.print("Peso: ");
Serial.print(balanza.get_units(20),3);
Serial.println(" gr");
// Publica el valor del peso al topic "peso/gramos"
char pesoLeido[7];
dtostrf(balanza.get_units(20), 1, 3, pesoLeido);
client.publish("peso/gramos", pesoLeido);
}
}
void desbloquea() {
digitalWrite(lockPin, HIGH);
delay(50);
digitalWrite(lockPin, LOW);
}
[
{
"id": "51f63d20.78e724",
"type": "tab",
"label": "Prop Control ABADIA",
"disabled": false,
"info": ""
},
{
"id": "2b28b35b.5ac42c",
"type": "mqtt in",
"z": "51f63d20.78e724",
"name": "peso/gramos",
"topic": "peso/gramos",
"qos": "2",
"datatype": "auto",
"broker": "678a13cd.298acc",
"x": 150,
"y": 460,
"wires": [
[
"1ceeeb19.704b85"
]
]
},
{
"id": "a69f0c94.0b13b",
"type": "ui_button",
"z": "51f63d20.78e724",
"name": "",
"group": "1a2f4016.8732a",
"order": 1,
"width": 2,
"height": 1,
"passthru": false,
"label": "Comenzar",
"tooltip": "",
"color": "",
"bgcolor": "green",
"icon": "",
"payload": "{\"payload\":\"on\",\"timeout\":600,\"warning\":30}",
"payloadType": "json",
"topic": "",
"x": 150,
"y": 680,
"wires": [
[
"ffbf4444.f7fcc8"
]
]
},
{
"id": "8aba63f3.1051b",
"type": "ui_button",
"z": "51f63d20.78e724",
"name": "",
"group": "1a2f4016.8732a",
"order": 2,
"width": 2,
"height": 1,
"passthru": false,
"label": "Parar",
"tooltip": "",
"color": "",
"bgcolor": "red",
"icon": "",
"payload": "{ \"payload\": \"cancel\" }",
"payloadType": "json",
"topic": "",
"x": 130,
"y": 719,
"wires": [
[
"6fb4b3c7.fd909c"
]
]
},
{
"id": "28353e08.f85972",
"type": "ui_button",
"z": "51f63d20.78e724",
"name": "",
"group": "1a2f4016.8732a",
"order": 3,
"width": 2,
"height": 1,
"passthru": false,
"label": "Reset",
"tooltip": "",
"color": "",
"bgcolor": "orange",
"icon": "",
"payload": "{\"timeout\": 600 }",
"payloadType": "json",
"topic": "",
"x": 130,
"y": 780,
"wires": [
[
"fce9982a.c1f0d8"
]
]
},
{
"id": "5470c7ee.ab25e8",
"type": "inject",
"z": "51f63d20.78e724",
"name": "Tick",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "1",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 138,
"y": 835,
"wires": [
[
"8602322c.8d193"
]
]
},
{
"id": "ae79e7a4.c9c6f8",
"type": "change",
"z": "51f63d20.78e724",
"name": "",
"rules": [
{
"t": "set",
"p": "gameState",
"pt": "global",
"to": "playing",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 540,
"y": 680,
"wires": [
[
"c4878c1.1239f7"
]
]
},
{
"id": "73bd1313.f4dfcc",
"type": "change",
"z": "51f63d20.78e724",
"name": "",
"rules": [
{
"t": "set",
"p": "gameState",
"pt": "global",
"to": "stopped",
"tot": "str"
},
{
"t": "set",
"p": "timeRemaining",
"pt": "msg",
"to": "timeRemaining",
"tot": "global"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 520,
"y": 720,
"wires": [
[
"c4878c1.1239f7"
]
]
},
{
"id": "ffbf4444.f7fcc8",
"type": "switch",
"z": "51f63d20.78e724",
"name": "",
"property": "gameState",
"propertyType": "global",
"rules": [
{
"t": "neq",
"v": "playing",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 310,
"y": 680,
"wires": [
[
"ae79e7a4.c9c6f8"
]
]
},
{
"id": "6fb4b3c7.fd909c",
"type": "switch",
"z": "51f63d20.78e724",
"name": "",
"property": "gameState",
"propertyType": "global",
"rules": [
{
"t": "neq",
"v": "stopped",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 310,
"y": 720,
"wires": [
[
"73bd1313.f4dfcc"
]
]
},
{
"id": "8602322c.8d193",
"type": "function",
"z": "51f63d20.78e724",
"name": "Update Game Timer",
"func": "// The current timestamp is injected at the start of the flow\nvar currentTime = msg.payload;\nvar lastTickTime = global.get(\"lastTickTime\")||currentTime;\nvar timeRemaining = global.get(\"timeRemaining\")||0;\nvar gameState = global.get(\"gameState\");\n\nif(gameState == \"playing\") {\n // Reduce the time remaining based on the amount of time\n // that has elapsed since the last tick count\n timeRemaining -= (currentTime - lastTickTime);\n \n // Update the global variable\n global.set(\"timeRemaining\", timeRemaining);\n\n if(timeRemaining < 60000) {\n msg.timeColour = \"red\";\n }\n else {\n msg.timeColour = \"limegreen\";\n }\n}\nelse {\n msg.timeColour = \"white\";\n}\n\n\n // Update the msg to carry formatted string \n msg.timeRemaining = timeRemaining;\n \n global.set(\"lastTickTime\", currentTime);\n node.status({fill:\"green\",shape:\"dot\",text:timeRemaining});\n // Only need to update UI is timeRemaining has changed\n return msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 320.5,
"y": 835,
"wires": [
[
"c4878c1.1239f7"
]
]
},
{
"id": "fce9982a.c1f0d8",
"type": "change",
"z": "51f63d20.78e724",
"name": "",
"rules": [
{
"t": "set",
"p": "timeRemaining",
"pt": "global",
"to": "3599999",
"tot": "num"
},
{
"t": "set",
"p": "timeRemaining",
"pt": "msg",
"to": "3599999",
"tot": "num"
},
{
"t": "set",
"p": "gameState",
"pt": "global",
"to": "stopped",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 320,
"y": 780,
"wires": [
[
"c4878c1.1239f7"
]
]
},
{
"id": "90ae97d4.f95298",
"type": "ui_template",
"z": "51f63d20.78e724",
"group": "1a2f4016.8732a",
"name": "",
"order": 4,
"width": 0,
"height": 0,
"format": "<div layout=\"row\" layout-align=\"space-between\"> <!-- Possible layout-aligns include \"start center\" -->\n <span class=\"label\">\n Tiempo restante\n </span> \n <span class=\"value\" style=\"color:{{msg.timeColour}}; font-size:xx-large;\">\n {{msg.formattedTimeRemaining}}\n </span>\n</div>",
"storeOutMessages": true,
"fwdInMessages": true,
"resendOnRefresh": false,
"templateScope": "local",
"x": 780,
"y": 780,
"wires": [
[]
]
},
{
"id": "c4878c1.1239f7",
"type": "function",
"z": "51f63d20.78e724",
"name": "Format Time",
"func": "// Message from preceeding node is in milliseconds\nvar t = msg.timeRemaining / 1000;\nvar h = Math.floor(t / 3600);\nvar m = Math.floor(t % 3600 / 60);\nvar s = Math.floor(t % 3600 % 60);\n\n// Format into hh:mm:ss\nmsg.formattedTimeRemaining = (\"0\" + h).slice(-2) + \":\" + (\"0\" + m).slice(-2) + \":\" + (\"0\" + s).slice(-2);\n\n\nnode.status({fill:\"green\", shape:\"dot\", text:msg.formattedTimeRemaining});\n \nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 550,
"y": 780,
"wires": [
[
"90ae97d4.f95298"
]
]
},
{
"id": "e8ec36e8.2c42d8",
"type": "comment",
"z": "51f63d20.78e724",
"name": "GAME COUNTER",
"info": "",
"x": 150,
"y": 640,
"wires": []
},
{
"id": "40b5a290.0ec24c",
"type": "ui_switch",
"z": "51f63d20.78e724",
"name": "",
"label": "Reset",
"tooltip": "",
"group": "b2133989.aed9f8",
"order": 3,
"width": 0,
"height": 0,
"passthru": false,
"decouple": "false",
"topic": "peso/reset",
"style": "",
"onvalue": "1",
"onvalueType": "num",
"onicon": "fa-toggle-on",
"oncolor": "",
"offvalue": "0",
"offvalueType": "num",
"officon": "fa-toggle-off",
"offcolor": "",
"x": 130,
"y": 520,
"wires": [
[
"43baacab.f02624"
]
]
},
{
"id": "43baacab.f02624",
"type": "mqtt out",
"z": "51f63d20.78e724",
"name": "",
"topic": "peso/reset",
"qos": "",
"retain": "",
"broker": "678a13cd.298acc",
"x": 390,
"y": 520,
"wires": []
},
{
"id": "b977fd75.386d8",
"type": "ui_switch",
"z": "51f63d20.78e724",
"name": "",
"label": "Desbloquear",
"tooltip": "",
"group": "b2133989.aed9f8",
"order": 2,
"width": 0,
"height": 0,
"passthru": true,
"decouple": "false",
"topic": "peso/desbloquear",
"style": "",
"onvalue": "1",
"onvalueType": "num",
"onicon": "fa-toggle-on",
"oncolor": "",
"offvalue": "0",
"offvalueType": "num",
"officon": "fa-toggle-off",
"offcolor": "",
"x": 150,
"y": 560,
"wires": [
[
"d3153453.dd1c48"
]
]
},
{
"id": "d3153453.dd1c48",
"type": "mqtt out",
"z": "51f63d20.78e724",
"name": "",
"topic": "peso/desbloquear",
"qos": "",
"retain": "",
"broker": "678a13cd.298acc",
"x": 410,
"y": 560,
"wires": []
},
{
"id": "60db7816.122ce8",
"type": "comment",
"z": "51f63d20.78e724",
"name": "PESAR ALMAS",
"info": "",
"x": 120,
"y": 420,
"wires": []
},
{
"id": "1ceeeb19.704b85",
"type": "ui_gauge",
"z": "51f63d20.78e724",
"name": "Numero almas",
"group": "b2133989.aed9f8",
"order": 1,
"width": 0,
"height": 0,
"gtype": "compass",
"title": "",
"label": "gr",
"format": "{{value}}",
"min": 0,
"max": "1000",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"x": 400,
"y": 460,
"wires": []
},
{
"id": "9cc1acd3.ecf69",
"type": "ui_switch",
"z": "51f63d20.78e724",
"name": "",
"label": "Reset",
"tooltip": "",
"group": "13297f90.c9646",
"order": 3,
"width": 0,
"height": 0,
"passthru": true,
"decouple": "false",
"topic": "ledStatus/red",
"style": "",
"onvalue": "1",
"onvalueType": "num",
"onicon": "fa-toggle-on",
"oncolor": "",
"offvalue": "0",
"offvalueType": "num",
"officon": "fa-toggle-off",
"offcolor": "",
"x": 150,
"y": 980,
"wires": [
[
"fb9df25b.e5e51"
]
]
},
{
"id": "fb9df25b.e5e51",
"type": "mqtt out",
"z": "51f63d20.78e724",
"name": "",
"topic": "abadia/demonios/reset",
"qos": "",
"retain": "",
"broker": "678a13cd.298acc",
"x": 440,
"y": 980,
"wires": []
},
{
"id": "ab308dc.d41877",
"type": "ui_switch",
"z": "51f63d20.78e724",
"name": "",
"label": "Desbloquear",
"tooltip": "",
"group": "13297f90.c9646",
"order": 2,
"width": 0,
"height": 0,
"passthru": true,
"decouple": "false",
"topic": "ledStatus/yellow",
"style": "",
"onvalue": "1",
"onvalueType": "num",
"onicon": "fa-toggle-on",
"oncolor": "",
"offvalue": "0",
"offvalueType": "num",
"officon": "fa-toggle-off",
"offcolor": "",
"x": 170,
"y": 1020,
"wires": [
[
"efdf423d.f3a11"
]
]
},
{
"id": "efdf423d.f3a11",
"type": "mqtt out",
"z": "51f63d20.78e724",
"name": "",
"topic": "abadia/demonios/desbloquear",
"qos": "",
"retain": "",
"broker": "678a13cd.298acc",
"x": 470,
"y": 1020,
"wires": []
},
{
"id": "7c27af33.1b07",
"type": "comment",
"z": "51f63d20.78e724",
"name": "ALUMBRAR DEMONIOS",
"info": "",
"x": 170,
"y": 920,
"wires": []
},
{
"id": "678a13cd.298acc",
"type": "mqtt-broker",
"name": "",
"broker": "broker.mqtt-dashboard.com",
"port": "1883",
"clientid": "",
"usetls": false,
"compatmode": true,
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthRetain": "false",
"birthPayload": "",
"closeTopic": "",
"closePayload": "",
"willTopic": "",
"willQos": "0",
"willRetain": "false",
"willPayload": ""
},
{
"id": "1a2f4016.8732a",
"type": "ui_group",
"name": "CONTADOR",
"tab": "5039d03.0a76f3",
"order": 1,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "b2133989.aed9f8",
"type": "ui_group",
"name": "PESAR ALMAS",
"tab": "5039d03.0a76f3",
"order": 2,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "13297f90.c9646",
"type": "ui_group",
"name": "ALUMBRAR DEMONIOS",
"tab": "5039d03.0a76f3",
"order": 3,
"disp": true,
"width": "6",
"collapse": false
},
{
"id": "5039d03.0a76f3",
"type": "ui_tab",
"name": "EL MISTERIO DE LA SAGRA v2.0",
"icon": "dashboard",
"disabled": false,
"hidden": false
}
]
