Neste tutorial, veremos como endereçar cada relé individualmente usando um microcontrolador NodeMCU32S e registos de deslocamento 74HC595. No final deste tutorial, será também capaz de acionar cada relé utilizando o monitor série. Este tutorial vem na sequência do projeto Controlar 8 relés usando um ESP32 e um registo de deslocamento.
Este tutorial pode ser aplicado a qualquer microcontrolador. Terá de ter o cuidado de modificar o diagrama de ligação e o código para se adequar à sua utilização.
Hardware
- NodeMCU 32S
- Prancheta
- Cabo de ligação
- Módulo de 8 relés
- 74hc595 registo de deslocação
Princípio
No tutorial anterior, vimos como gerir 8 relés usando um registo de deslocamento. Agora vamos endereçar cada relé independentemente usando o monitor serial do Arduino IDE. É possível comunicar com o microcontrolador através da porta USB utilizando a porta série. Assim, vamos definir uma estrutura de mensagem para dizer ao microcontrolador qual o relé a ligar.
Diagrama
Para recordar, eis o esquema de ligação do projeto
- GND Terra do circuito integrado
- Pino de alimentação Vdc. Normalmente ligado a 5V
- SH_CP ou RCLK ligado ao pino 33
- ST_CP ou SRCLK ligado ao pino 32
- DS ou SER ligado ao pino 25
Código
Vamos repetir o código do tutorial anterior e adicionar funções para receber mensagens do monitor de série e obter comandos de controlo do relé. Para as mensagens de comando, escolhemos a forma “YYxZ”, sendo YY, o identificador do relé, um número de 0 a 7; e Z, o estado do relé 0-aberto, 1- fechado.
A função readSerialPort recupera os caracteres enviados pelo monitor de série na cadeia de caracteres “msg”.
a função convertMsgToCmd traduzirá a cadeia de caracteres num número de identificação relayId e no 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 = ""; } }
Depois de termos recuperado os valores relayId e relayState, podemos utilizá-los como entrada para a função setRegisterPin(relayId, relayState) para ativar ou desativar o relé. Pode criar o número de mensagens que quiser. Aqui, por exemplo, adicionei o comando “reset” para abrir todos os relés e o comando “states” para mostrar os 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 = ""; } }
Resultados
Uma vez carregado o código no microcontrolador, abrir o monitor série. Pode introduzir o identificador do relé (0 – 7) e o estado desejado (0-aberto, 1- fechado). Por exemplo, para fechar o relé 2, digite 2×1 e depois “enter” ou “send”.