En este tutorial, veremos cómo dirigir cada relé individualmente con un microcontrolador NodeMCU32S y registros de desplazamiento 74HC595. Al final de este tutorial, también serás capaz de manejar cada relé utilizando el monitor en serie. Este tutorial es una continuación del proyecto Piloto 8 relés utilizando un ESP32 y un registro de desplazamiento.
Este tutorial se puede aplicar a cualquier microcontrolador. Tendrá que tener cuidado de modificar el esquema de conexión y el código para que se ajuste a su uso.
Hardware
- NodeMCU 32S
- Breadboard
- Jumper cable
- Módulo de 8 relés
- Registro de desplazamiento 74hc595
Principio
Hemos visto, en el tutorial anterior, cómo gestionar 8 relés utilizando un registro de desplazamiento. Ahora pasaremos a dirigirnos a cada relé de forma independiente utilizando el monitor de serie de Arduino IDE. Es posible comunicarse con el microcontrolador a través del puerto USB utilizando el puerto serie. Por lo tanto, definiremos una estructura de mensajes para especificar al microcontrolador qué relé debe encender.
Esquema
A modo de recordatorio, este es el diagrama de conexión del proyecto
- GND tierra del circuito integrado
- Pin de alimentación Vdc. Normalmente se conecta a 5V
- SH_CP o RCLK conectado al pin 33
- ST_CP o SRCLK conectado al pin 32
- DS o SER conectado al pin 25
Código
Vamos a tomar el código del tutorial anterior y añadir las funciones para recibir mensajes del monitor serie y recuperar los comandos de control del relé. Para los mensajes de comando, elegimos la forma «YYxZ», con YY, el identificador del relé, un número de 0 a 7; y Z, el estado del relé 0-abierto, 1-cerrado.
La función readSerialPort, recupera los caracteres enviados por el monitor serie en la cadena «msg».
la función convertMsgToCmd traducirá la cadena en un número de identificación relayId y el estado relayState.
void readSerialPort() { while (Serial.available()) { delay(10); if (Serial.available() > 0) { char c = Serial.read(); //gets one byte from serial buffer msg += c; } } } void convertMsgToCmd() { relayId = -1; relayState = -1; if (msg.length() > 0) { Serial.println(msg); sep = msg.indexOf('x'); if (sep > 0) { m1 = msg.substring(0, sep); //get servo id m2 = msg.substring(sep + 1, msg.length()); //get servo pos char carray1[6]; //magic needed to convert string to a number m1.toCharArray(carray1, sizeof(carray1)); relayId = atoi(carray1); char carray2[6]; m2.toCharArray(carray2, sizeof(carray2)); relayState = atoi(carray2); relayState = 1 - relayState; Serial.print(F("Set relay n° ")); Serial.print(relayId); Serial.print(F(" to ")); Serial.println(!relayState ? "HIGH" : "LOW"); } sep = msg.indexOf("reset"); if (sep == 0) { m1 = "reset"; } sep = msg.indexOf("states"); if (sep == 0) { m1 = "states"; } msg = ""; } }
Una vez recuperados los valores relayId y relayState, podemos ponerlos a la entrada de la función setRegisterPin (relayId, relayState); para activar o desactivar el relé. Es posible crear tantos mensajes como quieras. Aquí, por ejemplo, he añadido el comando «reset» para abrir todos los relés y el comando «states» para mostrar los estados de cada relé.
//Constants #define number_of_74hc595s 1 #define numOfRegisterPins number_of_74hc595s * 8 #define SER_Pin 25 #define RCLK_Pin 33 #define SRCLK_Pin 32 //Variables boolean registers[numOfRegisterPins]; String msg, m1, m2; int sep, relayId, relayState; void setup() { delay(100); //Init Serial USB Serial.begin(115200); Serial.println(F("Initialize System")); //Init register pinMode(SER_Pin, OUTPUT); pinMode(RCLK_Pin, OUTPUT); pinMode(SRCLK_Pin, OUTPUT); clearRegisters(); writeRegisters(); delay(500); Serial.println(F("Enter Relay ID ans state (IDxSTATE):")); } void loop() { readSerialPort(); convertMsgToCmd(); if (relayId >= 0 and relayState >= 0) { setRegisterPin(relayId, relayState); writeRegisters(); } if (m1 == "reset") { Serial.println(F("Reset all relays")); clearRegisters(); writeRegisters(); } if (m1 == "states") { Serial.println(F("print relays states")); printRegisters(); } m1 = ""; } void clearRegisters() { /* function clearRegisters */ //// Clear registers variables for (int i = numOfRegisterPins - 1; i >= 0; i--) { registers[i] = HIGH; } printRegisters(); } void writeRegisters() { /* function writeRegisters */ //// Write register after being set digitalWrite(RCLK_Pin, LOW); for (int i = numOfRegisterPins - 1; i >= 0; i--) { digitalWrite(SRCLK_Pin, LOW); digitalWrite(SER_Pin, registers[i]); digitalWrite(SRCLK_Pin, HIGH); } digitalWrite(RCLK_Pin, HIGH); } void setRegisterPin(int index, int value) { /* function setRegisterPin */ ////Set register variable to HIGH or LOW registers[index] = value; } void printRegisters() { /* function clearRegisters */ //// Clear registers variables for (int i = 0; i < numOfRegisterPins; i++) { Serial.print(registers[i]); Serial.print(F(" ,")); } Serial.println(); } void readSerialPort() { while (Serial.available()) { delay(10); if (Serial.available() > 0) { char c = Serial.read(); //gets one byte from serial buffer msg += c; } } } void convertMsgToCmd() { relayId = -1; relayState = -1; if (msg.length() > 0) { Serial.println(msg); sep = msg.indexOf('x'); if (sep > 0) { m1 = msg.substring(0, sep); //get servo id m2 = msg.substring(sep + 1, msg.length()); //get servo pos char carray1[6]; //magic needed to convert string to a number m1.toCharArray(carray1, sizeof(carray1)); relayId = atoi(carray1); char carray2[6]; m2.toCharArray(carray2, sizeof(carray2)); relayState = atoi(carray2); relayState = 1 - relayState; Serial.print(F("Set relay n° ")); Serial.print(relayId); Serial.print(F(" to ")); Serial.println(!relayState ? "HIGH" : "LOW"); } sep = msg.indexOf("reset"); if (sep == 0) { m1 = "reset"; } sep = msg.indexOf("states"); if (sep == 0) { m1 = "states"; } msg = ""; } }
Resultado
Una vez cargado el código en el microcontrolador, abre el monitor de serie. Puede introducir el identificador del relé (0 – 7) y el estado deseado (0-abierto, 1-cerrado). Por ejemplo, para cerrar el relé 2, escriba 2×1 y luego «enter» o «send».