I set up the ADS1115 16-bit ADC alongside the MCP23017 GPIO expander on a Raspberry Pi using the I²C bus.
Configuration:
-
ADS1115 at address 0x48
-
MCP23017 at address 0x27
-
Pull-up resistors: 4.7kΩ to 3.3V on SDA and SCL
-
MCP23017 used for LED output control
-
ADS1115 configured for single-ended reads on channels AIN0 through AIN3
Initial Observations:
-
Floating analog input returned approximately 0.5V, consistent with high impedance
-
Jumper to GND read 0.000V
-
Jumper to +5.0V returned values between 5.008V and 5.045V, depending on system noise
Everything looked good—until the full program was run.
Problem:
The program consistently crashed on the first write to the MCP23017 with an I/O exception: "System.IO.IOException: Error 5 performing I2C data transfer."
The first LED (PA7) would light, then the system would halt.
Resolution:
After verifying wiring, I²C addresses, and device response using i2cdetect, I created a minimal test program to isolate MCP23017 writes. That test ran without issues.
This pointed to a startup timing issue.
I inserted a 10-millisecond delay (Thread.Sleep(10)) after MCP23017 initialization but before the first write. This resolved the issue completely.
Even a 1-millisecond delay worked. No delay caused repeatable failure. The chip responds to i2cdetect but clearly isn't ready for full register-level communication immediately on startup.
Conclusion:
The issue was caused by the MCP23017 not being fully ready when the first I²C write occurred. A short delay after initialization ensures reliable operation. The final program now reads all four ADS1115 channels and drives the MCP23017 without errors.
using System;
using System.Device.I2c;
using System.Threading;
using Iot.Device.Mcp23xxx;
using Iot.Device.Ads1115;
class Program {
static void Main() {
// --- MCP23017 SETUP ---
I2cConnectionSettings mcpSettings = new I2cConnectionSettings(1, 0x27);
I2cDevice mcpDevice = I2cDevice.Create(mcpSettings);
Mcp23017 mcp = new Mcp23017(mcpDevice);
// Set Port A as output, Port B as input
mcp.WriteUInt16(Register.IODIR, 0xFF00);
const byte PA7 = 1 << 7;
ushort output = PA7;
// Turn on PA7 initially
mcp.WriteUInt16(Register.OLAT, output);
// --- ADS1115 SETUP ---
I2cConnectionSettings adsSettings = new I2cConnectionSettings(1, 0x48);
I2cDevice adsDevice = I2cDevice.Create(adsSettings);
using (Ads1115 ads = new Ads1115(adsDevice)) {
ads.MeasuringRange = MeasuringRange.FS6144; // ±6.144V input range
Thread.Sleep(10); // Delay to allow initialization to complete
for (int i = 0; i < 10; i++) {
for (int bit = 0; bit <= 2; bit++) {
byte ledMask = (byte)(1 << bit);
output = (ushort)(PA7 | ledMask);
mcp.WriteUInt16(Register.OLAT, output);
// Read all four single-ended channels (AIN0 to AIN3)
for (int ch = 0; ch < 4; ch++) {
ads.InputMultiplexer = (InputMultiplexer)(0x04 + ch); // AIN0=0x04, AIN1=0x05, etc.
double voltage = ads.ReadVoltage().Volts;
Console.WriteLine($"AIN{ch}: {voltage:0.000} V");
}
Console.WriteLine(); // Blank line between cycles
Thread.Sleep(1000);
}
}
}
// Leave PA7 on
mcp.WriteUInt16(Register.OLAT, PA7);
Console.WriteLine("Done. PA7 remains ON.");
}
}

Edward
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.