Close

Analog Input Online — ADS1115 Setup

A project log for SigCore UC - Universal Controller

Universal industrial I/O controller with relays, analog/digital I/O, Modbus & OPC-UA support. Powered by Raspberry Pi.

edwardEdward 07/31/2025 at 22:360 Comments

I set up the ADS1115 16-bit ADC alongside the MCP23017 GPIO expander on a Raspberry Pi using the I²C bus.

Configuration:

Initial Observations:

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.");
    }
}

Discussions