Manual

do

Maker

.

com

Módulo relé de estado sólido da Fulltronic

Módulo relé de estado sólido da Fulltronic

Esse artigo é para mostrar um pouco o módulo relé de estado sólido da Fulltronic. É um módulo compacto de 8 canais, fácil de usar e de baixíssimo consumo em relação aos módulos relé de bobina.

Características

Esse módulo relé de estado sólido suporta cargas de 250VAC@2A e é ideal para aplicações com muito chaveamento, pois não possui partes mecânicas como o relé de bobina, o que garante silêncio em operação, baixo tempo de resposta e vida útil extremamente prolongada. Seu acionamento se dá em nível baixo, ou seja, o ground é no GPIO da MCU ou nos pinos do PCF8574.

Onde comprar?

Esse módulo relé de estado sólido está disponível no nosso parceiro Fulltronic, que você poderá conferir através desse link. Deseja comprá-lo? faça-o com nosso parceiro e fortifique a parceria com o o blog. O interessante da Fulltronic é que eles têm ferramentas, pasta de solda, sugadores, pilhas e baterias e outras coisas que às vezes precisamos comprar separado das nossas plaquinhas. Confira a lista de materiais.

Controlando o módulo relé de estado sólido através do PCF8574

Não só gosto como inclusive prefiro utilizar esse expansor de I/O I2C. A vantagem em utilizá-lo é que precisamos apenas dos pinos SDA e SCL em troca de 8 a 24 I/O. Isso já seria o suficiente para utilizá-lo, mas com um pouco de deslocamento de bits o código acaba ficando reduzido, economizando ciclos e lógica.

Se quiser ler um bom artigo a respeito, clique aqui.

O PCF8574 é endereçável, mas utilizo uma técnica "preguiçosa" para não ler especificações, que descrevo a seguir.

Scanner I2C

Nem sempre temos o endereço do dispositivo à mão, em muitos casos não vem impresso na placa e em alguns casos é possível mudar o endereço. Recorrer ao datasheet nesse momento é uma solução chata e demorada.

Com esse scanner I2C abaixo, tudo o que você precisa fazer é subir o sketch para seu Arduino e então utilizar a letra s no terminal serial. Depois, procure pela linha que esteja preenchida com a letra V.

O código:

//
//    FILE: MultiSpeedI2CScanner.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.7
// PURPOSE: I2C scanner at different speeds
//    DATE: 2013-11-05
//     URL: http://forum.arduino.cc/index.php?topic=197360
//
// Released to the public domain
//

#include <Wire.h>
#include <Arduino.h>

TwoWire *wi;

const char version[] = "0.1.7";

void I2Cscan();
void displayHelp();
char getCommand();
void setSpeed(char sp);
void setAddress();

// INTERFACE COUNT (TESTED TEENSY 3.5 AND ARDUINO DUE ONLY)
int wirePortCount = 1;
int selectedWirePort = 0;


// scans devices from 50 to 800KHz I2C speeds.
// lower than 50 is not possible
// DS3231 RTC works on 800 KHz. TWBR = 2; (?)
const long allSpeed[] = {
  50, 100, 200, 300, 400, 500, 600, 700, 800
};
long speed[sizeof(allSpeed) / sizeof(allSpeed[0])];
int speeds;

int addressStart = 0;
int addressEnd = 127;


// DELAY BETWEEN TESTS
#define RESTORE_LATENCY  5    // for delay between tests of found devices.
bool delayFlag = false;


// MINIMIZE OUTPUT
bool printAll = true;
bool header = true;


// STATE MACHINE
enum states {
  STOP, ONCE, CONT, HELP
};
states state = STOP;


// TIMING
uint32_t startScan;
uint32_t stopScan;


void setup()
{
  Serial.begin(115200);
  delay(2000);
  Serial.println("Starting...");
  Wire.begin();

#if defined WIRE_IMPLEMENT_WIRE1 || WIRE_INTERFACES_COUNT > 1
  Wire1.begin();
  wirePortCount++;
#endif
#if defined WIRE_IMPLEMENT_WIRE2 || WIRE_INTERFACES_COUNT > 2
  Wire2.begin();
  wirePortCount++;
#endif
#if defined WIRE_IMPLEMENT_WIRE3 || WIRE_INTERFACES_COUNT > 3
  Wire3.begin();
  wirePortCount++;
#endif

  wi = &Wire;

  setSpeed('0');
  displayHelp();
}


void loop()
{
  char command = getCommand();
  switch (command)
  {
    case '@':
      selectedWirePort = (selectedWirePort + 1) % wirePortCount;
      Serial.print(F("I2C PORT=Wire"));
      Serial.println(selectedWirePort);
      switch (selectedWirePort)
      {
        case 0:
          wi = &Wire;
          break;
        case 1:
#if defined WIRE_IMPLEMENT_WIRE1 || WIRE_INTERFACES_COUNT > 1
          wi = &Wire1;
#endif
          break;
        case 2:
#if defined WIRE_IMPLEMENT_WIRE2 || WIRE_INTERFACES_COUNT > 2
          wi = &Wire2;
#endif
          break;
        case 3:
#if defined WIRE_IMPLEMENT_WIRE3 || WIRE_INTERFACES_COUNT > 3
          wi = &Wire3;
#endif
          break;
      }
      break;

    case 's':
      state = ONCE;
      break;
    case 'c':
      state = CONT;
      break;
    case 'd':
      delayFlag = !delayFlag;
      Serial.print(F("<delay="));
      Serial.println(delayFlag ? F("5>") : F("0>"));
      break;

    case 'e':
      // eeprom test TODO
      break;

    case 'h':
      header = !header;
      Serial.print(F("<header="));
      Serial.println(header ? F("yes>") : F("no>"));
      break;
    case 'p':
      printAll = !printAll;
      Serial.print(F("<print="));
      Serial.println(printAll ? F("all>") : F("found>"));
      break;

    case '0':
    case '1':
    case '2':
    case '4':
    case '8':
      setSpeed(command);
      break;

    case 'a':
      setAddress();
      break;

    case 'q':
    case '?':
      state = HELP;
      break;
    default:
      break;
  }

  switch (state)
  {
    case ONCE:
      I2Cscan();
      state = HELP;
      break;
    case CONT:
      I2Cscan();
      delay(1000);
      break;
    case HELP:
      displayHelp();
      state = STOP;
      break;
    case STOP:
      break;
    default: // ignore all non commands
      break;
  }
}


void setAddress()
{
  if (addressStart == 0)
  {
    addressStart = 8;
    addressEnd = 120;
  }
  else
  {
    addressStart = 0;
    addressEnd = 127;
  }
  Serial.print(F("<address Range = "));
  Serial.print(addressStart);
  Serial.print(F(".."));
  Serial.print(addressEnd);
  Serial.println(F(">"));

}

void setSpeed(char sp)
{
  switch (sp)
  {
    case '1':
      speed[0] = 100;
      speeds = 1;
      break;
    case '2':
      speed[0] = 200;
      speeds = 1;
      break;
    case '4':
      speed[0] = 400;
      speeds = 1;
      break;
    case '8':
      speed[0] = 800;
      speeds = 1;
      break;
    case '0':  // reset
      speeds = sizeof(allSpeed) / sizeof(allSpeed[0]);
      for (int i = 0; i < speeds; i++)
      {
        speed[i] = allSpeed[i];
      }
      break;
  }
}

char getCommand()
{
  char c = '\0';
  if (Serial.available())
  {
    c = Serial.read();
  }
  return c;
}

void displayHelp()
{
  Serial.print(F("\nArduino MultiSpeed I2C Scanner - "));
  Serial.println(version);
  Serial.println();
  Serial.print(F("I2C ports: "));
  Serial.println(wirePortCount);
  Serial.println(F("\t@ = toggle Wire - Wire1 - Wire2 [TEENSY 3.5 or Arduino Due]"));
  Serial.println(F("Scanmode:"));
  Serial.println(F("\ts = single scan"));
  Serial.println(F("\tc = continuous scan - 1 second delay"));
  Serial.println(F("\tq = quit continuous scan"));
  Serial.println(F("\td = toggle latency delay between successful tests. 0 - 5 ms"));
  Serial.println(F("Output:"));
  Serial.println(F("\tp = toggle printAll - printFound."));
  Serial.println(F("\th = toggle header - noHeader."));
  Serial.println(F("\ta = toggle address range, 0..127 - 8..120"));
  Serial.println(F("Speeds:"));
  Serial.println(F("\t0 = 50 - 800 Khz"));
  Serial.println(F("\t1 = 100 KHz only"));
  Serial.println(F("\t2 = 200 KHz only"));
  Serial.println(F("\t4 = 400 KHz only"));
  Serial.println(F("\t8 = 800 KHz only"));
  Serial.println(F("\n\t? = help - this page"));
  Serial.println();
}


void I2Cscan()
{
  startScan = millis();
  uint8_t count = 0;

  if (header)
  {
    Serial.print(F("TIME\tDEC\tHEX\t"));
    for (uint8_t s = 0; s < speeds; s++)
    {
      Serial.print(F("\t"));
      Serial.print(speed[s]);
    }
    Serial.println(F("\t[KHz]"));
    for (uint8_t s = 0; s < speeds + 5; s++)
    {
      Serial.print(F("--------"));
    }
    Serial.println();
  }

  // TEST
  // 0.1.04: tests only address range 8..120
  // --------------------------------------------
  // Address  R/W Bit Description
  // 0000 000   0 General call address
  // 0000 000   1 START byte
  // 0000 001   X CBUS address
  // 0000 010   X reserved - different bus format
  // 0000 011   X reserved - future purposes
  // 0000 1XX   X High Speed master code
  // 1111 1XX   X reserved - future purposes
  // 1111 0XX   X 10-bit slave addressing
  for (uint8_t address = addressStart; address <= addressEnd; address++)
  {
    bool printLine = printAll;
    bool found[speeds];
    bool fnd = false;

    for (uint8_t s = 0; s < speeds ; s++)
    {
#if ARDUINO >= 158
      wi->setClock(speed[s] * 1000);
#else
      TWBR = (F_CPU / (speed[s] * 1000) - 16) / 2;
#endif
      wi->beginTransmission (address);
      found[s] = (wi->endTransmission () == 0);
      fnd |= found[s];
      // give device 5 millis
      if (fnd && delayFlag) delay(RESTORE_LATENCY);
    }

    if (fnd) count++;
    printLine |= fnd;

    if (printLine)
    {
      Serial.print(millis());
      Serial.print(F("\t"));
      Serial.print(address, DEC);
      Serial.print(F("\t0x"));
      if (address < 0x10) Serial.print(0, HEX);
      Serial.print(address, HEX);
      Serial.print(F("\t"));

      for (uint8_t s = 0; s < speeds ; s++)
      {
        Serial.print(F("\t"));
        Serial.print(found[s] ? F("V") : F("."));
      }
      Serial.println();
    }
  }

  stopScan = millis();
  if (header)
  {
    Serial.println();
    Serial.print(count);
    Serial.print(F(" devices found in "));
    Serial.print(stopScan - startScan);
    Serial.println(F(" milliseconds."));
  }
}

Descobriu o endereço de sua placa? Ótimo, agora é só fazer o wiring.

Código de teste do módulo relé de estado sólido

Fiz um código bem simples. O estado inicial dos pinos é HIGH, pois como descrito anteriormente, o GND é o GPIO. Em seguida, inicio a inversão do estado de cada pino, com intervalo de 3 segundos, para dar tempo de mostrar no vídeo o comparativo de consumo do módulo relé de estado sólido contra o consumo do relé de bobina.

Tanto no Arduino UNO como no Leonardo, SDA e SCL são os pinos 2 e 3, respectivamente. O código utilizado é esse:

#include <Wire.h>
#include <Arduino.h>

int bytes;
uint8_t i = -1;
uint8_t val = 255;

void setup(){
  Serial.begin(115200);
   Wire.begin();
   Wire.beginTransmission(0x22);
   //nas versões mais atuais, Wire.write()

   Wire.write(255);
   Wire.endTransmission();
}

void loop(){
   i++;
   val = val^(1<<i);
   
   Serial.print("bit: ");
   Serial.println(val);
   Serial.print("Rele: ");
   Serial.println(i);

   Wire.beginTransmission(0x22);
   Wire.write(val);
   Wire.endTransmission();
   i = i > 6 ? -1 : i;

   //bytes =  Wire.requestFrom(0x22,1);
   // bytes = Wire.read();
   delay(3000);
  
}

Se ficou com dúvida em relação ao deslocamento de bits, não deixe de ler esse artigo. Uma vez compreendido, você não quererá mais utilizar outra coisa.

Vídeo

Publiquei um vídeo mostrando o consumo e o acionamento desse módulo, vale dar uma olhada. O link para o vídeo em nosso canal Dobitaobytebrasil no Youtube é esse. Deixe seu like, se inscreva no canal e clique no sininho para receber notificações.

Até a próxima!

Inscreva-se no nosso canal Manual do Maker no YouTube.

Também estamos no Instagram.

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.