EtherTen and Relay 8 Shields

The EtherTen combines an Uno-equivalent Arduino-compatible board and Wiznet-based Ethernet support, along with a microSD card slot and Power-over-Ethernet support. [Product page]
Post Reply
keiranwyllie
Posts: 40
Joined: Tue Aug 16, 2016 9:17 am

EtherTen and Relay 8 Shields

Post by keiranwyllie » Tue Aug 16, 2016 9:41 am

Hi all,

Firstly, I'm pretty new to the world of arduino and programming but I'm working hard to learn. I've already built one EtherTen/Relay8 combo to replace the switches on our pool fountains/spa/lights and works like a charm over a modified web page.

I'm now trying to run an EtherTen with 2 relay8 shields. I've got it running sweet with a simple web server for relay activation by web page. Now I want to integrate momentary push buttons into the mix (think light switch replacement as well as control via web interface).

I've used the following code from one of the other forum topics and it works to a degree. The push button switches the relay on however it does not turn it off. Sending a zero via serial turns it off fine.

Code: Select all

#include "Wire.h"

#define I2C_ADDR  0x20  // 0x20 is the address with all jumpers removed
const int buttonPin2 = 2;     // the number of the pushbutton pin
int buttonState = 0;         // variable for reading the pushbutton status



void setup()
{
  pinMode(ledPin, OUTPUT);  
  pinMode(buttonPin, INPUT); 
  Serial.begin( 38400 );
  Serial.println("RELAY8 demonstration starting up");

  Wire.begin(); // Wake up I2C bus

  // Set I/O bank A to outputs
  Wire.beginTransmission(I2C_ADDR);
  Wire.write(0x00); // IODIRA register
  Wire.write(0x00); // Set all of bank A to outputs
  Wire.endTransmission();

  Serial.println("Ready. Type 0 to turn off relays, 1 - 8 to activate a relay.");
}

void loop()
{
  int command = 0;   

    if (digitalRead(buttonPin)==HIGH)
    {
      sendValueToLatch(1);
      digitalWrite(ledPin, HIGH);
      Serial.println("Activating relay 1");
      delay(5000);
    }

  if (Serial.available()) 
  {
    command = Serial.read();
    if( command == '0' )
    {
      sendValueToLatch(0);
      digitalWrite(ledPin, LOW);
      Serial.println("Resetting all relays");
    }

    if( command == '2' )
    {
      sendValueToLatch(2);
      Serial.println("Activating relay 2");
    }

  }
}

void sendValueToLatch(int latchValue)
{
  Wire.beginTransmission(I2C_ADDR);
  Wire.write(0x12);        // Select GPIOA
  Wire.write(latchValue);  // Send value to bank A
  Wire.endTransmission();
  }
  
The code I'd like to integrate this into is below:

Code: Select all

/**
 * OutputControllerRelay8x2.pde
 */

#include <SPI.h>
#include "Ethernet.h"
#include "WebServer.h"
#include "Wire.h"


#define SHIELD_1_I2C_ADDRESS  0x20  // 0x20 is the address with all jumpers removed
#define SHIELD_2_I2C_ADDRESS  0x21  // 0x21 is the address with a jumper on position A0

#define MAC_I2C_ADDRESS       0x50  // Microchip 24AA125E48 I2C ROM address

/* If channelInterlocks is set to true, the channels are grouped into
 * pairs starting at 1 (ie: channels 1 & 2, 3 & 4, etc) and only one
 * channel in each pair can be on at any time. For example, if channel
 * 1 is on and 2 is set to on, channel 1 will be turned off first and
 * vice versa. This is to allow control of dual-active devices such as
 * electric curtain motors which must only be driven in one direction
 * at a time. */ 
const byte channelInterlocks = false;

/* CHANGE THIS TO YOUR OWN UNIQUE VALUE.  The MAC number should be
 * different from any other devices on your network or you'll have
 * problems receiving packets. Can be replaced automatically below
 * using a MAC address ROM. */
static uint8_t mac[] = { 0xFE, 0xDE, 0x8A, 0xAA, 0xFF, 0xB4 };

/* CHANGE THIS TO MATCH YOUR HOST NETWORK.  Most home networks are in
 * the 192.168.0.XXX or 192.168.1.XXX subrange.  Pick an address
 * that's not in use and isn't going to be automatically allocated by
 * DHCP from your router. Can be replaced automatically using DHCP. */
static uint8_t ip[] = { 192, 168, 2, 111 };

#define PREFIX "/control"  // This will be appended to the IP address as the URL
WebServer webserver(PREFIX, 80);

byte shield1BankA = 0; // Current status of all outputs on first shield, one bit per output
byte shield2BankA = 0; // Current status of all outputs on second shield, one bit per output

/* This command is set as the default command for the server.  It
 * handles both GET and POST requests.  For a GET, it returns a simple
 * page with some buttons.  For a POST, it saves the value posted to
 * the buzzDelay variable, affecting the output of the speaker */
void serverCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
  /* If we've received a POST request we need to process the submitted form values */
  if (type == WebServer::POST)
  {
    bool repeat;
    char name[20], value[16];
    do
    {
      /* readPOSTparam returns false when there are no more parameters
       * to read from the input.  We pass in buffers for it to store
       * the name and value strings along with the length of those
       * buffers. */
      repeat = server.readPOSTparam( name, 20, value, 16);

      /* This is a standard string comparison function.  It returns 0
       * when there's an exact match. */
      if (strcmp( name, "On" ) == 0)
      {
        setLatchChannelOn( atoi(value) );
      }
      
      if (strcmp( name, "Off" ) == 0)
      {
        setLatchChannelOff( atoi(value) );
      }
      
      if (strcmp( name, "AllOff" ) == 0)
      {
        sendRawValueToLatch1(0);
        sendRawValueToLatch2(0);
      }
      
    } while (repeat);  
    
    // After procesing the POST data, tell the web browser to reload
    // the page using a GET method. 
    server.httpSeeOther(PREFIX);
    return;
  }

  /* for a GET or HEAD, send the standard "it's all OK" headers */
  server.httpSuccess();

  /* Don't output the body for a HEAD request, only for GET */
  if (type == WebServer::GET)
  {
    /* store the HTML in program memory using the P macro */
    P(message) = 
      "<html><head><title>16 Channel Test</title>"
      "<body style='background-color:powderblue;'>"
      "<form action='/control' method='POST'>"
      "<h1 style='color:red;font-family:comic sans ms;font-size:200%;text-align:center;'>16 Channel Test</h1><hr>"
      "<div style='float:left;width:60%;'>"
      "<p style='font-size:140%;'><button style='height:40px;width:75px' type='submit' name='On' value='1'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='1'>Off</button>1<button style='height:40px;width:75px' type='submit' name='On' value='2'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='2'>Off</button>2</p>"
      "<p style='font-size:140%;'><button style='height:40px;width:75px' type='submit' name='On' value='3'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='3'>Off</button>3<button style='height:40px;width:75px' type='submit' name='On' value='4'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='4'>Off</button>4</p>"
      "<p style='font-size:140%;'><button style='height:40px;width:75px' type='submit' name='On' value='5'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='5'>Off</button>5<button style='height:40px;width:75px' type='submit' name='On' value='6'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='6'>Off</button>6</p>"
      "<p style='font-size:140%;'><button style='height:40px;width:75px' type='submit' name='On' value='7'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='7'>Off</button>7<button style='height:40px;width:75px' type='submit' name='On' value='8'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='8'>Off</button>8</p>"
      "<p style='font-size:140%;'><button style='height:40px;width:75px' type='submit' name='On' value='9'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='9'>Off</button>9<button style='height:40px;width:75px' type='submit' name='On' value='10'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='10'>Off</button>10</p>"
      "<p style='font-size:140%;'><button style='height:40px;width:75px' type='submit' name='On' value='11'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='11'>Off</button>11<button style='height:40px;width:75px' type='submit' name='On' value='12'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='12'>Off</button>12</p>"
      "<p style='font-size:140%;'><button style='height:40px;width:75px' type='submit' name='On' value='13'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='13'>Off</button>13<button style='height:40px;width:75px' type='submit' name='On' value='14'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='14'>Off</button>14</p>"
      "<p style='font-size:140%;'><button style='height:40px;width:75px' type='submit' name='On' value='15'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='15'>Off</button>15<button style='height:40px;width:75px' type='submit' name='On' value='16'>On</button><button style='height:40px;width:75px' type='submit' name='Off' value='16'>Off</button>16</p>"
      "<p style='font-size:140%;text-align:center;'><button style='height:40px;width:75px' type='submit' name='AllOff' value='0'>Off</button>&nbsp&nbspTurn Everything Off</p></div>"
      "</form>"
      "</body></html>";

    server.printP(message);
  }
}

/**
 */
void setup()
{
  Wire.begin(); // Wake up I2C bus
  Serial.begin( 38400 );
  Serial.println("Test Control Output Controller starting up. v1.0, 16 channel relay shield");
  
  Serial.print("Getting MAC address from ROM: ");
  mac[0] = readRegister(0xFA);
  mac[1] = readRegister(0xFB);
  mac[2] = readRegister(0xFC);
  mac[3] = readRegister(0xFD);
  mac[4] = readRegister(0xFE);
  mac[5] = readRegister(0xFF);
  char tmpBuf[17];
  sprintf(tmpBuf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  Serial.println(tmpBuf);
  
  // setup the Ethernet library to talk to the Wiznet board
  Ethernet.begin(mac, ip);  // Use static address defined above
  //Ethernet.begin(mac);      // Use DHCP
  
  // Print IP address:
  Serial.print("My URL: http://");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    if( thisByte < 3 )
    {
      Serial.print(".");
    }
  }
  Serial.println("/control");

  /* Register the default command (activated with the request of
   * http://x.x.x.x/control */
  webserver.setDefaultCommand(&serverCmd);

  /* start the server to wait for connections */
  webserver.begin();

  /* Set up the Relay8 shields */
  initialiseShield(SHIELD_1_I2C_ADDRESS);
  sendRawValueToLatch1(0);  // If we don't do this, channel 6 turns on! I don't know why
  
  initialiseShield(SHIELD_2_I2C_ADDRESS);
  sendRawValueToLatch2(0);  // If we don't do this, channel 6 turns on! I don't know why

  Serial.println("Ready.");
}

/**
 */
void loop()
{
  // Process incoming connections one at a time forever
  webserver.processConnection();
}

/**
 */
void initialiseShield(int shieldAddress)
{
  // Set addressing style
  Wire.beginTransmission(shieldAddress);
  Wire.write(0x12);
  Wire.write(0x20); // use table 1.4 addressing
  Wire.endTransmission();

  // Set I/O bank A to outputs
  Wire.beginTransmission(shieldAddress);
  Wire.write(0x00); // IODIRA register
  Wire.write(0x00); // Set all of bank A to outputs
  Wire.endTransmission();
}

/**
 */
void toggleLatchChannel(byte channelId)
{
  if( channelId >= 1 && channelId <= 8 )
  {
    byte shieldOutput = channelId;
    byte channelMask = 1 << (shieldOutput - 1);
    shield1BankA = shield1BankA ^ channelMask;
    sendRawValueToLatch1(shield1BankA);
  }
  else if( channelId >= 9 && channelId <= 16 )
  {
    byte shieldOutput = channelId - 8;
    byte channelMask = 1 << (shieldOutput - 1);
    shield2BankA = shield2BankA ^ channelMask;
    sendRawValueToLatch2(shield2BankA);
  }
}

/**
 */
void setLatchChannelOn (byte channelId)
{
/*  if( channelInterlocks == true )
  {
    if ( (channelId % 2) == 0)  // This is an even number channel, so turn off the channel before it
    {
      setLatchChannelOff( channelId - 1 );
    } else {                    // This is an odd number channel, so turn off the channel after it
      setLatchChannelOff( channelId + 1 );
    }
  } */
  
  if( channelId >= 1 && channelId <= 8 )
  {
    byte shieldOutput = channelId;
    byte channelMask = 1 << (shieldOutput - 1);
    shield1BankA = shield1BankA | channelMask;
    sendRawValueToLatch1(shield1BankA);
  }
  else if( channelId >= 9 && channelId <= 16 )
  {
    byte shieldOutput = channelId - 8;
    byte channelMask = 1 << (shieldOutput - 1);
    shield2BankA = shield2BankA | channelMask;
    sendRawValueToLatch2(shield2BankA);
  }
}


/**
 */
void setLatchChannelOff (byte channelId)
{
  if( channelId >= 1 && channelId <= 8 )
  {
    byte shieldOutput = channelId;
    byte channelMask = 255 - ( 1 << (shieldOutput - 1));
    shield1BankA = shield1BankA & channelMask;
    sendRawValueToLatch1(shield1BankA);
  }
  else if( channelId >= 9 && channelId <= 16 )
  {
    byte shieldOutput = channelId - 8;
    byte channelMask = 255 - ( 1 << (shieldOutput - 1));
    shield2BankA = shield2BankA & channelMask;
    sendRawValueToLatch2(shield2BankA);
  }
}

/**
 */
void sendRawValueToLatch1(byte rawValue)
{
  Wire.beginTransmission(SHIELD_1_I2C_ADDRESS);
  Wire.write(0x12);        // Select GPIOA
  Wire.write(rawValue);    // Send value to bank A
  shield1BankA = rawValue;
  Wire.endTransmission();
}

/**
 */
void sendRawValueToLatch2(byte rawValue)
{
  Wire.beginTransmission(SHIELD_2_I2C_ADDRESS);
  Wire.write(0x12);        // Select GPIOA
  Wire.write(rawValue);    // Send value to bank A
  shield2BankA = rawValue;
  Wire.endTransmission();
}

/**
 * Required to read the MAC address ROM
 */
byte readRegister(byte r)
{
  unsigned char v;
  Wire.beginTransmission(MAC_I2C_ADDRESS);
  Wire.write(r);  // Register to read
  Wire.endTransmission();

  Wire.requestFrom(MAC_I2C_ADDRESS, 1); // Read a byte
  while(!Wire.available())
  {
    // Wait
  }
  v = Wire.read();
  return v;
}
If anyone can point me in the right direction I'd be forever grateful.

Keiran.
Last edited by keiranwyllie on Thu Aug 18, 2016 10:43 am, edited 1 time in total.

keiranwyllie
Posts: 40
Joined: Tue Aug 16, 2016 9:17 am

Re: EtherTen and Relay 8 Shields

Post by keiranwyllie » Thu Aug 18, 2016 10:42 am

Well I've had a win with the following code:

Code: Select all

if (shield1BankA==1 && digitalRead(buttonPin2)==HIGH)
    {
    sendRawValueToLatch1(0);
    Serial.println("Deactivating relay 1");
    delay(2000);
    }
else if (digitalRead(buttonPin2)==HIGH)
    {
    sendRawValueToLatch1(1);
    Serial.println("Activating relay 1");
    delay(1000);
    }
I've inserted this in the "loop" function and it works like a charm. Now to add the rest of the code for the other relays.

keiranwyllie
Posts: 40
Joined: Tue Aug 16, 2016 9:17 am

Re: EtherTen and Relay 8 Shields

Post by keiranwyllie » Sat Aug 20, 2016 7:45 am

Jumped the gun a little here. It seems the code I've used above turns everything off instead of just the first channel.

Any suggestions?

keiranwyllie
Posts: 40
Joined: Tue Aug 16, 2016 9:17 am

Re: EtherTen and Relay 8 Shields

Post by keiranwyllie » Fri Aug 26, 2016 2:55 am

Well the answer was staring me right in the face and I missed it...obviously looking too deep. Changed the code to this and it works like a charm, both from web interface and push buttons.

Code: Select all

if (digitalRead(2) == HIGH)
  {
    toggleLatchChannel(1);
    delay(1000);
  }
  else if (digitalRead(3) == HIGH)
  {
    toggleLatchChannel(2);
    delay(1000);
  }
  

bigd003
Posts: 10
Joined: Thu Apr 14, 2016 2:30 am

Re: EtherTen and Relay 8 Shields

Post by bigd003 » Sat Sep 10, 2016 11:09 am

keiranwyllie wrote:Well the answer was staring me right in the face and I missed it...obviously looking too deep. Changed the code to this and it works like a charm, both from web interface and push buttons.

Code: Select all

if (digitalRead(2) == HIGH)
  {
    toggleLatchChannel(1);
    delay(1000);
  }
  else if (digitalRead(3) == HIGH)
  {
    toggleLatchChannel(2);
    delay(1000);
  }
  
I am learning as well, and I want to be sure I understand what you are achieving here. With this bit of code, are you able to make either channel 1 or 2 turn on for a second in response to you pushing a button that is connected to digital pin 2 or 3, respectively?

And this also works when the web interface button is clicked also? Man, it almost seems too simple...

keiranwyllie
Posts: 40
Joined: Tue Aug 16, 2016 9:17 am

Re: EtherTen and Relay 8 Shields

Post by keiranwyllie » Sat Sep 10, 2016 1:08 pm

bigd003 wrote:
I am learning as well, and I want to be sure I understand what you are achieving here. With this bit of code, are you able to make either channel 1 or 2 turn on for a second in response to you pushing a button that is connected to digital pin 2 or 3, respectively?

And this also works when the web interface button is clicked also? Man, it almost seems too simple...
The delay doesn't turn the channels on for only a second. Basically it's there to deny any other process from running for a second after the switch has been pressed.

And yep, it also works through the web interface, but not via this code. It uses the code further up in what I attached.

bigd003
Posts: 10
Joined: Thu Apr 14, 2016 2:30 am

Re: EtherTen and Relay 8 Shields

Post by bigd003 » Sat Sep 10, 2016 1:28 pm

Okay, yes, I did the code you've posted above. ( I actually use the same base code for my etherten / relay 8 shield project. ) This is sort of why I am interesting in what you are doing.

So, I think I understand what you have done. You can control on/off via the web interface, but you also have 2 real button switches that will allow you some control while you are physically with your project. In the way you have it written, will the relay channel turn on for as long as you hold the button, and then off when you let go of the button?

(That is sort of what I would like to be able to do via the browser buttons, but I need to get the java correct and the "loop" portion as well. Making it work from the web button seems a bit more complex, to me anyway)

Thank you for the reply!

keiranwyllie
Posts: 40
Joined: Tue Aug 16, 2016 9:17 am

Re: EtherTen and Relay 8 Shields

Post by keiranwyllie » Sun Sep 11, 2016 10:52 am

bigd003 wrote:Okay, yes, I did the code you've posted above. ( I actually use the same base code for my etherten / relay 8 shield project. ) This is sort of why I am interesting in what you are doing.

So, I think I understand what you have done. You can control on/off via the web interface, but you also have 2 real button switches that will allow you some control while you are physically with your project. In the way you have it written, will the relay channel turn on for as long as you hold the button, and then off when you let go of the button?

(That is sort of what I would like to be able to do via the browser buttons, but I need to get the java correct and the "loop" portion as well. Making it work from the web button seems a bit more complex, to me anyway)

Thank you for the reply!
The push button is literally turning the shield channel on or off, therefore the light stays on or off (in the code it's toggling the channel state between on/off).

charlesjudd
Posts: 3
Joined: Tue Feb 07, 2017 11:13 pm

Re: EtherTen and Relay 8 Shields

Post by charlesjudd » Tue May 02, 2017 8:33 am

Hi
What version of EtherTen are you using please? I can get two Relay8 shields working ok on an arduino uno, but cannot get it working on a Rev3 Etherten.

Did you need to disable the etherten i2c pull-up resistor to allow the i2c communication to work correctly?

Any other advice issues you faced with etherten & Relay8 appreciated also.

Thanks

Charles

Post Reply