Introduction: IOT123 - I2C 2CH RELAY BRICK

About: The tension between novelty and familiarity...

The IOT123 BRICKS are DIY modular units that can be mashed up with other IOT123 BRICKS, to add functionality to a node or wearable. They are based on the inch square, double-sided protoboards with interconnected through holes.

A number of these BRICKS are expected to be on multiple nodes (Master MCUs - ESP8266 or ATTINY84) on a site. The MCU needs no prior knowledge of the sensors purpose or software needs. It scans for I2C nodes then requests a property dump (sensor data) from each slave. These BRICKs supply 5.0V, 3.3V and another AUX line which is customizable.

This I2C 2CH RELAY BRICK extends the functionality of the I2C KY019 BRICK, and has two read/write properties:

2CH RELAYS[0] (true/false).

2CH RELAYS[1] (true/false).

The through-holes adjacent to the ATTINY85 have been left unused, to enable a pogo pin programmer while the DIP8 is soldered to the PCB. A further abstraction, packaging the BRICKS in small cylinders that plug into a D1M WIFI BLOCK hub, pumping the values to a MQTT server, is being developed.

Step 1: Materials and Tools

There is a full Bill of Material and Sourcing list.

  1. 2 Channel Relay (1)
  2. ATTINY85 20PU (1)
  3. 1" Double sided protoboard (1)
  4. Male Header 90º (3P, 3P)
  5. Male Header (2P, 2P)
  6. Jumper Shunt (1)
  7. Hookup wire (~7)
  8. Solder and Iron (1)

Step 2: Prepare the ATTINY85

NOTE: If intending to have Crouton integration, please use the library from here, and use the example installed "attiny_2ch_relay".

AttinyCore from the Boards Manager is needed. Burn bootloader "EEPROM Retained", "8mHZ Internal" (all config shown above).

The code repository can be found here.

A ZIP of the library can be found here.

Instructions for "Importing a ZIP Library" here.

Once the library is installed you can open the example "attiny_2ch_relay".

To upload the firmware to the ATTINY85, you may find more details in these instructables:

Best to test via breadboard before continuing.

If you have existing ASSIMILATE SENSORS, make sure the slave address is different on a SENSOR/MCU Host combination i.e. all the Relay actors can have the same address as long as you only have one Relay actor on a MCU/node.

Step 3: Assemble the Circuit

  1. On the front, insert the components ATTINY85 (1), 3P 90deg male headers (2)(3), 3P male headers (4)(5) and solder off on the rear.
  2. On the rear, trace a yellow wire from YELLOW1 to YELLOW2 and solder.
  3. On the rear, trace a orange wire from ORANGE1 to ORANGE2 and solder.
  4. On the rear, trace a blue wire from BLUE1 to BLUE2 and solder.
  5. On the rear, trace a green wire from GREEN1 to GREEN2 and solder.
  6. On the rear, trace a black wire from BLACK1 to BLACK2 and solder.
  7. On the rear, trace a black wire from BLACK3 to BLACK4 and solder.
  8. On the rear, trace a red wire from RED1 to RED2 and solder.
  9. On the rear, trace a bare wire from RED3 to RED4 and solder.
  10. On the rear, trace a bare wire from SILVER1 to SILVER2 and solder.
  11. Add a jumper on the 5V or 3V3 line.

The relay can now be connected directly via its pins to the PCB or via wires, to the points shown in the pin contract.

Step 4: Testing

A number of these BRICKS are expected to be on multiple nodes (MCUs - ESP8266 or ATTINY84) in an environment. This is a unit test: sends I2C commands from the UNO to the ATTINY which opens or closes the relays.

We have previously built a I2C SHIELD for Arduino.

If you want to breadboard it instead:

  1. Connect the 5.0V on UNO to a VCC on BRICK.
  2. Connect the GND on UNO to GND on BRICK.
  3. Connect the A5 on UNO to SCL on BRICK.
  4. Connect the A4 on UNO to SDA on BRICK.
  5. Connect a 4K7 pull-up resistor from SDA to VCC.
  6. Connect a 4K7 pull-up resistor from SCL to VCC.

Running the test.

    1. Connect your UNO to your Dev PC with USB.
    2. Upload the code to the UNO.
    3. Open the Arduino Console.
    4. Choose 9600 baud (restart the UNO and reopen the console if you have to).
    5. The address of the slave will print to the console.
    6. When <Arduino is ready>, enter in the send box <ADDRESS> 2 0:1 (so 15 2 0:1), and the CH1 relay turns on.

    7. When <Arduino is ready>, enter in the send box <ADDRESS> 2 0:0 (so 15 2 0:0), and the CH1 relay turns off.
    8. When <Arduino is ready>, enter in the send box <ADDRESS> 2 1:1 (so 15 2 1:1), and the CH2 relay turns on.
    9. When <Arduino is ready>, enter in the send box <ADDRESS> 2 1:0 (so 15 2 0:0), and the CH2 relay turns off.

      I2C BRICK adhoc commands for slaves from UNO master.

      const byte _num_chars = 32;
      char _received_chars[_num_chars]; // an array to store the received data
      boolean _has_new_data = false;
      voidsetup() {
      Serial.println("ensure newline selected in console window");
      Serial.println("ADDRESS 2 ACTOR COMMAND");
      Serial.println("ADDRESSES ON BUS:");
      int device_count = 0;
      for (byte address = 8; address < 127; address++)
      const byte error = Wire.endTransmission();
      if (error == 0)
      voidloop() {
      voidrecv_with_end_marker() {
      static byte ndx = 0;
      char end_marker = '\n';
      char rc;
      while (Serial.available() >0 && _has_new_data == false) {
      rc =;
      if (rc != end_marker) {
      _received_chars[ndx] = rc;
      if (ndx >= _num_chars) {
      ndx = _num_chars - 1;
      else {
      _received_chars[ndx] = '\0'; // terminate the string
      ndx = 0;
      _has_new_data = true;
      voidsend_to_i2c() {
      char param_buf[16];
      const String received_string = String(_received_chars);
      if (_has_new_data == true) {
      int idx1 = received_string.indexOf('');
      String address = received_string.substring(0, idx1);
      int address_int = address.toInt();
      if (address_int < 8 || address_int >127){
      Serial.println("INVALID ADDRESS INPUT:");
      int idx2 = received_string.indexOf('', idx1+1);
      String code;
      if (idx2 == -1){
      code = received_string.substring(idx1+1);
      code = received_string.substring(idx1+1, idx2+1);
      int code_int = code.toInt();
      if (code_int < 0 || code_int >5){
      Serial.println("INVALID CODE INPUT:");
      bool has_parameter = idx2 > -1;
      String parameter;
      if (has_parameter){
      parameter = received_string.substring(idx2 + 1, idx2 + 17); // 16 chars max
      if (parameter.length() < 1){
      Serial.println("PARTAMETER MIN. LENGTH 1");
      _has_new_data = false;
      if (code_int >1){
      Serial.println("PARAMETER REQUIRED!");
      _has_new_data = false;
      Serial.print("input orig = ");
      Serial.print("address = ");
      Serial.print("code = ");
      Serial.print("parameter = ");
      // SEND VIA I2C
      if (has_parameter){
      strcpy(param_buf, parameter.c_str());
      Serial.println("SENT VIA I2C!");
      _has_new_data = false;

      Step 5: Next Steps

      The follow-up ASSIMILATE ACTOR: 2CH RELAY which uses this brick has automatic configuration for Crouton via the metadata already installed in the ATTINY85 here. The JSON packet sent to Crouton is sent via the latest firmware for the ICOS10. You can do a Proof-of-concept on an ordinary ESP8266, if the build is a too much for now.

      The UNO sketch used in Testing has a function for saving a new slave address to EEPROM on the ATTINY85, if you have a clash on your target I2C bus. A few schematics have been added, but there are various ways to wire the downstream circuit depending on what you want to achieve, so I'll leave that for you : )

      Check out the ASSIMILATE SENSORS and IOT123 BRICKS.