10. Exploring EMIT's GPIO Expansion Connector (Arduino) - ControlBits/EMIT GitHub Wiki
To enable further expansion, the EMIT development board includes a GPIO Expansion Connector. This connector is ideal for interfacing to additional sensors such as a soil moisture probe or an ambient light sensor.
The GPIO Expansion Connector has 8x GPIO pins plus 1x 3V3 power supply and 2x GND pins. The VIN supply is also available on the connector.
The GPIO Interface pins can be configured to provide any combination of:
- 6x digital inputs/outputs
- 4x analog inputs (12-bit resolution)
- 1x I2C serial port
- 1x Serial UART
The full expansion connector layout is shown in the table below:
To configure or use any of EMIT's IO pins, you must first include the Arduino.h
which is already included by default in Arduino ide .ino:
#include<Arduino.h>
EMIT's expansion connector pins (IO1 to IO4, IO7 & IO8) can be configured as digital outputs as long as the pins are not already configured for some other purpose.
Each individual IO pin is capable of driving 40mA at 3.3v and sinking 28mA at 0v but this capability drops to less than 30mA when multiple pins are being driven at the same time.
Our recommendation is to limit the current driven or sunk by any pin to 20mA max.
A pin is defined and configured as an output using the pinMode
function as follows:
// where the pin number = ESP32's GPIO
pinMode(36,OUTPUT);
Note: The pin number referred to in the configuration is the ESP32's GPIO Pin number. This pin number is shown in the table above: EMIT's Expansion Connector Pin-out. In this example, IO1 (connector pin 2) is connected to the ESP32's GPIO 36.
Once configured, the output 'state' of the pin is set using the digitalWrite
function. Setting the value to LOW sets the pins output LOW (0 volts). Setting the value to HIGH sets the pins output HIGH (3.3v).
// LOW (0 volt), HIGH (3.3 volt)
digitalWrite(36,LOW);
EMIT's expansion connector pins (IO1 to IO4, IO7 & IO8) can be configured as digital inputs as long as the pins are not already configured for some other purpose.
Once configured as an input, each pin has a high impedance, sinking or sourcing just 50nA per pin.
A pin is defined and configured as an input using the 'machine.Pin' function as follows:
// where the pin number = ESP32's GPIO
pinMode(39,INPUT);
Note: please refer to the table above: EMIT's Expansion Connector Pin-out to determine the ESP32's GPIO pin assigned to EMIT's expansion connector.
The ESP32 has configurable pull-ups. To add a pull-up, just add 'machine.Pin.PULL_UP' to the configuration:
// where the pin number = ESP32's GPIO
pinMode(39,INPUT_PULLUP);
Once configured, as an input, the 'state' of the pin can be read using the 'value()' function:
//read state of input, 0 = LOW, 1 = HIGH
char IO2state = digitalRead(39);
When the value returns '0' the input is LOW (<1.0 volt). A value to '1' is returned when the input is HIGH (> 2.4v).
Pins IO1 to IO4 of EMIT's expansion connector can be configured as analog inputs (A1 to A4) as long as the pins are not already configured for some other purpose.
The ESP32 has an integrated, 12-bit Analog to Digital Converter (ADC). The ADC converts the analog input voltage on a configured analog input pin to a digital value ranging from 1 to 1024.
The input voltage range of the analog input pins is 0 mV to 1000mV DC, where 0mV is represented as a digital '0' and 1000mV is represented by the digital value '4096'. Any voltage higher than 1000mV will be represented as '4096'. This gives an ADC resolution of 1000mV/4096 = 0.244mV.
To read the ADC value of a given pin use the read() function:
// read analog value 0 to 4096 on GPIO 36
int AnalogValue = analogRead(36);
To convert the ADC value to a voltage, simply multiply it by the ADC resolution (0.244mV).
e.g. an ADC value of '1000' = 1000 * 0.244mV = 244.4mV (0.2444 Volts).
To measure voltages greater than 1 Volt, simply use a potential divider network on the input and multiply the result by the scaling factor of the potential divider.
Pins IO7 and IO8 of EMIT's expansion connector are connected to the ESP32 hardware I2C port, where:
- IO7 = GPI21 = I2C SCL (Clock)
- IO8 = GPI22 = I2C SDA (Data)
NOTE: To use these pins, you will need to connect a 10k pull-up resistor to each of these pins (to pull the pins up to 3.3v).
We will first include the Wire
library that has the functions needed to establish a I2C communication.
#include <Wire.h>
To configure the I2C port, use the Wire.begin
function:
Wire.begin(22, //SDA pin on GPIO 22
21, //SCL pin on GPIO 21
400000 // I2C frequency.
);
In this case we are setting the frequency of the clock to 400kHz.
To write to an I2C slave, use the Wire.beginTransmission()
function, where its only parameter is the 7-bit address of the slave we wish to write to, and then call Wire.write() the value we wish to write to the device - in this case three bytes "012"
, we will pass two parameters to that function the string "012"
and its length which is 3.
// write bytes '012' to slave address 112
Wire.beginTransmission(112);
Wire.write((const uint8_t*)"012",3);
Reading from an I2C slave is by using the Wire.read()
function, after requesting the data from the slave using Wire.requestFrom()
function which takes two parameters the first parameter is the 7-bit address of the slave we wish to read from and the second value is the number of bytes we want to read.
Wire.requestFrom()
returns the number of bytes available that is checked if its not zero, We then wait for the data becoming available on I2C memory using Wire.available()
function. then finally we read the bytes requested from the slave using Wire.read()
. a Arduino code example for reading from the slave is below.
int count=Wire.requestFrom(112, // slave address
8 // number of bytes
);
if(count==0){ // read failed
// print data on the screen on failure.
Serial.print("Bad Stuff!! Read Failed\n");
}
else {
// value to read.
char value[8];
while(!Wire.available()); // wait for data.
for (int i=0;i<8;i++)
{
// read available bytes..
value[i]=Wire.read();
}
You can also scan the I2C port for slave devices, using this function below i2c_scanner
. A simple scanning process, which displays a list of I2C slave addresses by looping through all available addresses and tries to begin a I2C transmission:
void i2c_scanner(void){
// helper variables
byte error, address;
int nDevices;
Serial.println("Scanning...");
// number of discovered devices..
nDevices = 0;
// loop through all available I2c Addresses
for(address = 1; address < 127; address++ )
{
// try to begin a transmission
Wire.beginTransmission(address);
error = Wire.endTransmission();
// if successful print the address achieved.
if (error == 0)
{
Serial.print("I2C device found at address 0x");
if (address<16)
Serial.print("0");
Serial.print(address,HEX);
Serial.println(" !");
nDevices++;
}
}
// finally print a final status.
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");
}
Pins IO5 and IO6 of EMIT's expansion connector are connected to the ESP32 UART0, where:
- IO5 = GPIO1 = UART0 TX
- IO6 = GPIO3 = UART0 RX
IMPORTANT NOTE: By default, the ESP32's UART0 is assigned to Serial debugger is used to upload the firmware and that drives the Serial monitor we've been using in Arduino for debugging messages. so keep that in mind, To avoid this complication, you might like to consider using a software UART implementation on pins IO1 to IO4, IO7 or IO8
NOTE: The maximum input/output voltage of these pins is 3.3v. To drive higher voltages (such as an RS-232 interface) an external high-voltage line driver will be required.
More details on using UART0 to follow ...