Introduction: Intro: IOT123 - D1M BLOCK - ARDPM+ Pin Extender Assembly

About: The tension between novelty and familiarity...

D1M BLOCKS add tactile cases, labels, polarity guides and breakouts for the popular Wemos D1 Mini SOC/Shields/Clones. One of the issues with the ESP8266 chip is it has only one analog IO pin and 9 dedicated Digital pins available.

This D1M BLOCK adds an additional 16 digital pins and 4 analog pins via I2C (only using 2 reusable I2C pins). This is an integrated extender (extra pins and breakouts combined) and exposes the 2 main rows of the Arduino Pro Mini. The initial footprint of the D1M BLOCKS is preserved and the Pro Mini is powered from the breakout pins.

Although the wiring is simpler (than the D1M ADS1115 BLOCK), there is the added overhead of flashing firmware on the Arduino Pro Mini. The extra pins and easy I2C interface may be just what you needed.

Step 1: Material and Tools

    There is now a full Bill of Materials and Sources list.

    1. The Wemos D1 Mini Protoboard shield and long pin female headers
    2. 3D printed parts.
    3. A set of D1M BLOCK - Install Jigs
    4. Arduino Pro Mini 3.3V 8mHz.
    5. 1 off 40P female header (to be cut) or 2 off 12P female headers.
    6. Hookup wire.
    7. Solder and Iron.
    8. Strong Cyanoachrylate Adhesive (preferably brush on)
    9. Hot glue gun and hot glue sticks
    10. FT232RL USB to TTL converter
    11. Assorted vitamins for testing.

    Step 2: Uploading the Sketch to the Arduino Pro Mini

    The Arduino Pro Mini is conveniently flashed using a FTDI232 USB to TTL converter. If careful, you can insert the programmer pins into the through holes on the Arduino without soldering a header (this assumes that you will only be flashing the chip once).

    Ensure the 3.3V setting is chosen on your FTDI232. Follow the instructions here using the code below (use link to GIST).

    Code from ESPEasy used for extending DIGITAL/ANALOG pins on D1M WIFI SOC using I2C

    /****************************************************************************************************************************\
    * Arduino project "ESP Easy" Copyright www.esp8266.nu
    *
    * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
    * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
    * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    * You received a copy of the GNU General Public License along with this program in file 'License.txt'.
    *
    * IDE download : https://www.arduino.cc/en/Main/Software
    * ESP8266 Package : https://github.com/esp8266/Arduino
    *
    * Source Code : https://sourceforge.net/projects/espeasy/
    * Support : http://www.esp8266.nu
    * Discussion : http://www.esp8266.nu/forum/
    *
    * Additional information about licensing can be found at : http://www.gnu.org/licenses
    \*************************************************************************************************************************/
    // This file is to be loaded onto an Arduino Pro Mini so it will act as a simple IO extender to the ESP module.
    // Communication between ESP and Arduino is using the I2C bus, so only two wires needed.
    // It best to run the Pro Mini on 3V3, although the 16MHz versions do not officially support this voltage level on this frequency.
    // That way, you can skip levelconverters on I2C.
    // Arduino Mini Pro uses A4 and A5 for I2C bus. ESP I2C can be configured but they are on GPIO-4 and GPIO-5 by default.
    #include<Wire.h>
    #defineI2C_MSG_IN_SIZE4
    #defineI2C_MSG_OUT_SIZE4
    #defineCMD_DIGITAL_WRITE1
    #defineCMD_DIGITAL_READ2
    #defineCMD_ANALOG_WRITE3
    #defineCMD_ANALOG_READ4
    volatileuint8_t sendBuffer[I2C_MSG_OUT_SIZE];
    voidsetup()
    {
    Wire.begin(0x7f);
    Wire.onReceive(receiveEvent);
    Wire.onRequest(requestEvent);
    }
    voidloop() {}
    voidreceiveEvent(int count)
    {
    if (count == I2C_MSG_IN_SIZE)
    {
    byte cmd = Wire.read();
    byte port = Wire.read();
    int value = Wire.read();
    value += Wire.read()*256;
    switch(cmd)
    {
    case CMD_DIGITAL_WRITE:
    pinMode(port,OUTPUT);
    digitalWrite(port,value);
    break;
    case CMD_DIGITAL_READ:
    pinMode(port,INPUT_PULLUP);
    clearSendBuffer();
    sendBuffer[0] = digitalRead(port);
    break;
    case CMD_ANALOG_WRITE:
    analogWrite(port,value);
    break;
    case CMD_ANALOG_READ:
    clearSendBuffer();
    int valueRead = analogRead(port);
    sendBuffer[0] = valueRead & 0xff;
    sendBuffer[1] = valueRead >>8;
    break;
    }
    }
    }
    voidclearSendBuffer()
    {
    for(byte x=0; x < sizeof(sendBuffer); x++)
    sendBuffer[x]=0;
    }
    voidrequestEvent()
    {
    Wire.write((constuint8_t*)sendBuffer,sizeof(sendBuffer));
    }

    Step 3: Soldering the Header Pins (using the SOCKET JIG)

    As the D1 Mini male pins will not be exposed on this D1M BLOCK, the socket jig can be used. As the excess male pins will be cut off, all the pins can be soldered in the initial position.

    1. Feed the header pins through bottom of the board (TX top-left on the top side).
    2. Feed jig over plastic header and level both surfaces.
    3. Turn jig and assembly over and firmly press header onto a hard flat surface.
    4. Press the board down firmly onto the jig.
    5. Solder the 4 corner pins using minimal solder (just temporary alignment of pins).
    6. Reheat and re position board/pins if needed (board or pins not aligned or plumb).
    7. Solder the rest of the pins.

    Step 4: Soldering Connectors to the Arduino Pro Mini

    The connections are simple: 4 hookup wires are soldered for later connection to the matching pin on the Wemos D1 Mini Shield later.

    1. Place a 30mmgreen hookup wire from underside to A4
    2. Solder A4 wire on topside
    3. Place a 30mmblue hookup wire from underside to A5
    4. Solder A5 wire on topside
    5. Solder a 20mmred hookup wire to pin VCC underside
    6. Solder a 30mmblack hookup wire to pin GND underside

    Step 5: Connecting the Boards

    The boards have spacers glued and the previous wires are matched and soldered.

    1. Glue the spacers onto the topside of the Wemos Shield.
    2. Pre-bend and place the red wire through the 3V3 hole on the topside.
    3. Solder the placed red wire on the underside.
    4. Place the green wire through the D2 hole on the topside.
    5. Solder the placed green wire on the underside.
    6. Place the blue wire through the D1 hole on the topside.
    7. Solder the placed blue wire on the underside.
    8. Place the black wire through the GND hole on the topside.
    9. Solder the placed black wire on the underside.
    10. Place a dob of glue on top of placed spacers.
    11. Joinboards together assuring bottom ends level and wires cleared.

    Step 6: Gluing the Component to the Base

    Not covered in the video, but recommended: put a large dob of hot glue in the emptybase before quickly inserting board and aligning - this will create compression keys on either side of the board. Please do a dry run in placing the shields in the base. If the gluing was not very accurate, you may need to do some light filing of the edge of the PCB.

    1. With the base casing bottom surface pointing down, place the soldered assembly plastic header through the holes in the base; the (TX pin will be on side with the central groove).
    2. Place the hot glue jig under the base with the plastic headers placed through its grooves.
    3. Sit the hot glue jig on a firm flat surface and carefully push the PCB down until the plastic headers hit the surface; this should have the pins positioned correctly.
    4. When using the hot glue keep it away from the header pins and at least 2mm from where the lid will be positioned.
    5. Apply glue to all 4 corners of the PCB ensuring contact with the base walls; allow seepage to both sides of the PCB if possible.
    6. On some PCBs where the board ends close to the pins, dob a large amount of glue onto the base to the height of the PCB; when this cools apply more glue on the top of the PCB bridging to the lower glue.

    Step 7: Gluing the Lid to the Base

    1. Ensure the pins are free of glue and the top 2mm of the base is free of hot glue.
    2. Pre-fit the lid (dry run) making sure no print artifacts are in the way.
    3. Take appropriate precautions when using the Cyanoachrylate adhesive.
    4. Apply Cyanoachrylate to the bottom corners of the lid ensuring coverage of the adjacent ridge.
    5. Quickly fit the lid to the base; clamping shut the corners if possible.
    6. After the lid is dry manually bend each pin so it is central in the void if necessary (see video).

    Step 8: Adding the Adhesive Labels

    1. Apply pinout label on underside of base, with RST pin on side with groove.
    2. Apply identifier label on flat non-grooved side, with the pins void being the top of the label.
    3. Press labels down firmly, with a flat tool if needed.
    4. Working off the diagrams above, apply the Arduino pinout stickers to the female headers

    Step 9: Testing the Analog Pins

      The analogRead uses the map() function on the raw values. It is expected that you will apply your own parameters to your particular use case.

      1. Upload the embedded script onto the D1M WIFI BLOCK.
      2. Disconnect the USB cable.
      3. Attach the D1M ARDPM+ BLOCK.
      4. Breaddboard the test circuit as above (analogRead with POT and analogWrite with LED).
      5. Attach the USB cable to the D1M WIFI BLOCK again.
      6. Open the serial console at the baud rate stated in the setup().
      7. The PINS available (14 to 17) can be tested for analogWrite (the LED) and analogRead (the pot).
      8. For analogWrite, connect the yellow wire to one of the pins, enter the serial input "3 1 127" (the second numeral being the pin, the third value being the intensity); also try input "3 1 200" brighten the LED.
      9. For analogRead, connect the yellow wire to one of the pins, enter the serial input "4 1 0" (the second numeral being the pin) to read the state of the pin to the console window. Change the value of the pot and enter the serial input "4 1 0" again.

      Use serial input from your D1M WIFI BLOCK to test the analog and digital pins via I2C to the D1M ARDUPOMI+ BLOCK.

      #include<Wire.h>
      #defineI2C_MSG_IN_SIZE4
      #defineI2C_MSG_OUT_SIZE4
      #defineCMD_DIGITAL_WRITE1
      #defineCMD_DIGITAL_READ2
      #defineCMD_ANALOG_WRITE3
      #defineCMD_ANALOG_READ4
      boolean _newData = false;
      const byte _numChars = 32;
      char _receivedChars[_numChars]; // an array to store the received data
      volatileuint8_t sendBuffer[2];
      voidsetup() {
      Serial.begin(9600);
      Wire.begin(D2, D1);
      delay(5000);
      }
      voidloop() {
      recvWithEndMarker();
      parseSendCommands();
      }
      voidrecvWithEndMarker() {
      static byte ndx = 0;
      char endMarker = '\n';
      char rc;
      while (Serial.available() >0 && _newData == false) {
      rc = Serial.read();
      if (rc != endMarker) {
      _receivedChars[ndx] = rc;
      ndx++;
      if (ndx >= _numChars) {
      ndx = _numChars - 1;
      }
      } else {
      _receivedChars[ndx] = '\0'; // terminate the string
      ndx = 0;
      _newData = true;
      }
      }
      }
      voidparseSendCommands() {
      if (_newData == true) {
      constchar delim[2] = "";
      char *token;
      token = strtok(_receivedChars, delim);
      byte cmd = atoi(token);
      byte port = 0;
      int value = 0;
      intindex = 0;
      while( token != NULL ) {
      index++;
      switch (index){
      case1:
      token = strtok(NULL, delim);
      port = atoi(token);
      break;
      case2:
      token = strtok(NULL, delim);
      value = atoi(token);
      break;
      default:
      token = NULL;
      }
      }
      sendCmd(cmd, port, value);
      _newData = false;
      }
      }
      voidsendCmd(byte cmd, byte port, int value) {
      Serial.println("-----");
      Serial.println(cmd);
      Serial.println(port);
      Serial.println(value);
      Serial.println("-----");
      Wire.beginTransmission(11); // transmit to device #8
      Wire.write(cmd); // sends a char
      Wire.write(port); // sends one byte
      sendBuffer[0] = value & 0xff;
      sendBuffer[1] = value >>8;
      Wire.write((constuint8_t*)sendBuffer,sizeof(sendBuffer));
      Wire.endTransmission();
      byte byteResponse = 0;
      int intResponse = 0;
      bool hadResponse = false;
      if (cmd == CMD_DIGITAL_READ){
      Wire.requestFrom(11,2);
      while(Wire.available()) // slave may send less than requested
      {
      hadResponse = true;
      byteResponse = Wire.read(); // receive a byte
      }
      }elseif (cmd == CMD_ANALOG_READ){
      Wire.requestFrom(11,2);
      while(Wire.available()) // slave may send less than requested
      {
      hadResponse = true;
      intResponse = Wire.read();
      intResponse += Wire.read() << 8;
      }
      }
      Serial.println("Sending command:");
      Serial.println("\t" + String(cmd) + "" + String(port) + "" + String(value) );
      if (cmd == CMD_DIGITAL_READ){
      if (hadResponse){
      Serial.println("Getting response:");
      if (byteResponse == 15){
      Serial.println("\tHIGH");
      }else{
      Serial.println("\tLOW");
      }
      }else{
      Serial.println("No response, check the address/connection");
      }
      } elseif (cmd == CMD_ANALOG_READ){
      if (hadResponse){
      Serial.println("Getting response:");
      int mappedVal = map(intResponse, 3840, 61187, 0, 1023);
      Serial.println("\t" + String(intResponse));
      Serial.println("\tMapped as " + String(mappedVal));
      }else{
      Serial.println("No response, check the address/connection");
      }
      }
      }

      Step 10: Testing the Digital Pins

        The sketch is the same as the previous step (included in case you missed that step).

        1. Upload the embedded script onto the D1M WIFI BLOCK.
        2. Disconnect the USB cable.
        3. Attach the D1M

          ARDPM

          + BLOCK.

        4. Breaddboard the test circuit as above.
        5. Attach the USB cable to the D1M WIFI BLOCK again.
        6. Open the serial console at the baud rate stated in the setup().
        7. The PINS available (2 to 17) can be tested for digitalWrite (the LED) and digitalRead (the switch).
        8. For digitalWrite, slide the switch to the far right, connect the yellow wire to one of the pins, enter the serial input "1 1 1" (the second numeral being the pin) and to turn the LED on; input "1 1 0" and to turn the LED off.
        9. For digitalRead, the circuit is in a normal closed state (INPUT_PULLUP), connect the yellow wire to one of the pins, enter the serial input "2 1 0" (the second numeral being the pin) to read the state of the pin to the console window. Change the state of the switch and enter the serial input "2 1 0" again. Take no notice of the state of the LED in this test.

        Use serial input from your D1M WIFI BLOCK to test the analog and digital pins via I2C to the D1M ARDUPOMI+ BLOCK.

        #include<Wire.h>
        #defineI2C_MSG_IN_SIZE4
        #defineI2C_MSG_OUT_SIZE4
        #defineCMD_DIGITAL_WRITE1
        #defineCMD_DIGITAL_READ2
        #defineCMD_ANALOG_WRITE3
        #defineCMD_ANALOG_READ4
        boolean _newData = false;
        const byte _numChars = 32;
        char _receivedChars[_numChars]; // an array to store the received data
        volatileuint8_t sendBuffer[2];
        voidsetup() {
        Serial.begin(9600);
        Wire.begin(D2, D1);
        delay(5000);
        }
        voidloop() {
        recvWithEndMarker();
        parseSendCommands();
        }
        voidrecvWithEndMarker() {
        static byte ndx = 0;
        char endMarker = '\n';
        char rc;
        while (Serial.available() >0 && _newData == false) {
        rc = Serial.read();
        if (rc != endMarker) {
        _receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= _numChars) {
        ndx = _numChars - 1;
        }
        } else {
        _receivedChars[ndx] = '\0'; // terminate the string
        ndx = 0;
        _newData = true;
        }
        }
        }
        voidparseSendCommands() {
        if (_newData == true) {
        constchar delim[2] = "";
        char *token;
        token = strtok(_receivedChars, delim);
        byte cmd = atoi(token);
        byte port = 0;
        int value = 0;
        intindex = 0;
        while( token != NULL ) {
        index++;
        switch (index){
        case1:
        token = strtok(NULL, delim);
        port = atoi(token);
        break;
        case2:
        token = strtok(NULL, delim);
        value = atoi(token);
        break;
        default:
        token = NULL;
        }
        }
        sendCmd(cmd, port, value);
        _newData = false;
        }
        }
        voidsendCmd(byte cmd, byte port, int value) {
        Serial.println("-----");
        Serial.println(cmd);
        Serial.println(port);
        Serial.println(value);
        Serial.println("-----");
        Wire.beginTransmission(11); // transmit to device #8
        Wire.write(cmd); // sends a char
        Wire.write(port); // sends one byte
        sendBuffer[0] = value & 0xff;
        sendBuffer[1] = value >>8;
        Wire.write((constuint8_t*)sendBuffer,sizeof(sendBuffer));
        Wire.endTransmission();
        byte byteResponse = 0;
        int intResponse = 0;
        bool hadResponse = false;
        if (cmd == CMD_DIGITAL_READ){
        Wire.requestFrom(11,2);
        while(Wire.available()) // slave may send less than requested
        {
        hadResponse = true;
        byteResponse = Wire.read(); // receive a byte
        }
        }elseif (cmd == CMD_ANALOG_READ){
        Wire.requestFrom(11,2);
        while(Wire.available()) // slave may send less than requested
        {
        hadResponse = true;
        intResponse = Wire.read();
        intResponse += Wire.read() << 8;
        }
        }
        Serial.println("Sending command:");
        Serial.println("\t" + String(cmd) + "" + String(port) + "" + String(value) );
        if (cmd == CMD_DIGITAL_READ){
        if (hadResponse){
        Serial.println("Getting response:");
        if (byteResponse == 15){
        Serial.println("\tHIGH");
        }else{
        Serial.println("\tLOW");
        }
        }else{
        Serial.println("No response, check the address/connection");
        }
        } elseif (cmd == CMD_ANALOG_READ){
        if (hadResponse){
        Serial.println("Getting response:");
        int mappedVal = map(intResponse, 3840, 61187, 0, 1023);
        Serial.println("\t" + String(intResponse));
        Serial.println("\tMapped as " + String(mappedVal));
        }else{
        Serial.println("No response, check the address/connection");
        }
        }
        }

        Step 11: Next Steps

        1. Program your D1M BLOCK with D1M BLOCKLY
        2. Check out Thingiverse
        3. Ask a question at the ESP8266 Community Forum