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».

