Is this a “reasonable” approach for using I2C outside of Gadgeteer?

I am using a G120E Dev Board 1.2 and I have all devices working except CAN 1 and that is because I do not have a CAN device to test it on.

My question is about how I used I2C.

I could not get it working at first due to my lack of knowledge of using references to Gadgeteer module drivers.

So I will show how I got it working and ask for opinions. (I have thick skin so say what you want to).

My code is shown below and I ask if there is a better way than my using:

//Fudge a Gadgeteer socket
socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(5);
ETC.

The I2C code is working. BUT I’m sure there must be a better way?..


namespace MFConsoleTest
{
    public class Program
    {
        private static GT.Socket socket;
        private const byte MEASURE_HUMIDITY_HOLD = 0xE5;
        private const byte READ_TEMP_FROM_PREVIOUS = 0xE0;
        private const byte I2C_ADDRESS = 0x40;

        private static GTI.SoftwareI2CBus i2c;
        private static byte[] writeBuffer1;
        private static byte[] writeBuffer2;
        private static byte[] readBuffer1;
        private static byte[] readBuffer2;

        private static Font fontSmall = null;
        private static Font fontLarge = null;

        private static Bitmap bmp;
        private static MSPM.Color backColor;

        public static void Main()
        {
            Debug.Print(Resources.GetString(Resources.StringResources.Test));
            Debug.Print(Resources.GetString(Resources.StringResources.String1));

            fontLarge = Resources.GetFont(Resources.FontResources.NinaB);
            fontSmall = Resources.GetFont(Resources.FontResources.small);
            backColor = MSPM.ColorUtility.ColorFromRGB(0X00, 0XCC, 0XFF); //Light blue

            //The display
            bmp = new Bitmap(SystemMetrics.ScreenWidth, SystemMetrics.ScreenHeight);
            bmp.Clear();
            DrawBackgroundBmp();

            //Fudge a Gadgeteer socket
            socket = GT.Socket.SocketInterfaces.CreateNumberedSocket(5);
            //socket.SupportedTypes = new[] { 'F', 'Y' };
            //socket.CpuPins[3] = Cpu.Pin.GPIO_NONE;
            socket.CpuPins[4] = GHI.Pins.G120E.Gpio.P0_28; //SCL
            socket.CpuPins[5] = GHI.Pins.G120E.Gpio.P0_27; //SDA
            //socket.CpuPins[6] = Cpu.Pin.GPIO_NONE;
            //socket.CpuPins[7] = Cpu.Pin.GPIO_NONE;
            //socket.CpuPins[8] = Cpu.Pin.GPIO_NONE;
            //socket.CpuPins[9] = Cpu.Pin.GPIO_NONE;

            i2c = new GTI.SoftwareI2CBus(socket, Gadgeteer.Socket.Pin.Five, Gadgeteer.Socket.Pin.Four, I2C_ADDRESS, 400, null);

            writeBuffer1 = new byte[1] { MEASURE_HUMIDITY_HOLD };
            writeBuffer2 = new byte[1] { READ_TEMP_FROM_PREVIOUS };
            readBuffer1 = new byte[2];
            readBuffer2 = new byte[2];

            //Measurement m = TakeMeasurement();
            //Debug.Print(m.ToString());

            //System.Threading.Timer MyTimer = new System.Threading.Timer(new TimerCallback(timer_Tick), null, 500, 0); //For Oneshot use
            System.Threading.Timer MyTimer = new System.Threading.Timer(new TimerCallback(timer_Tick), null, 1000, 5000); //Every 5 seconds

            //Debug.GC(true);
            Thread.Sleep(Timeout.Infinite);
        }
        //

        private static bool isfirstTime = true;
        private static string oldStringF = String.Empty;
        private static string oldStringC = String.Empty;


        static void timer_Tick(object o)
        {
            if (isfirstTime)
            {
                isfirstTime = false;
                oldStringF = String.Empty;
                oldStringC = String.Empty;
                DrawBackgroundBmp();
            }
            //
            Measurement m = TakeMeasurement();
            //Debug.Print(m.ToString());

            //Erase old text
            bmp.DrawTextInRect(oldStringC, 3, 200, 317, 18, 2, backColor, fontLarge);
            bmp.DrawTextInRect(oldStringF, 3, 218, 317, 18, 2, backColor, fontLarge);
            bmp.Flush();

            string tempF = m.ToStringF();
            string tempC = m.ToStringC();

            bmp.DrawTextInRect(tempC, 3, 200, 317, 18, 2, MSPM.Colors.White, fontLarge);
            bmp.DrawTextInRect(tempF, 3, 218, 317, 18, 2, MSPM.Colors.White, fontLarge);

            bmp.Flush();
            //Save current temp for erase on new temp
            oldStringF = tempF;
            oldStringC = tempC;
        }
        //

        /// <summary>Obtains a single measurement.</summary>
        /// <returns>The measurement.</returns>
        public static Measurement TakeMeasurement()
        {
            i2c.WriteRead(writeBuffer1, readBuffer1);
            i2c.WriteRead(writeBuffer2, readBuffer2);

            int rawRH = readBuffer1[0] << 8 | readBuffer1[1];
            int rawTemp = readBuffer2[0] << 8 | readBuffer2[1];

            double temperature = 175.72 * rawTemp / 65536.0 - 46.85;
            double relativeHumidity = 125.0 * rawRH / 65536.0 - 6.0;

            if (relativeHumidity < 0.0)
                relativeHumidity = 0.0;

            if (relativeHumidity > 100.0)
                relativeHumidity = 100.0;

            return new Measurement(temperature, relativeHumidity);
        }
        //

        private static void DrawBackgroundBmp()
        {
            //Fill with background color
            bmp.DrawRectangle(Colors.Red, // outline color
                 1, // no outline
                 0, // x
                 0, // y
                 SystemMetrics.ScreenWidth, // width
                 SystemMetrics.ScreenHeight,    // height
                 0, // x corner radius
                 0, // y corner radius
                 backColor,  // start gradient color
                 0, // start gradient x
                 0, // start gradient y
                 backColor,  // end gradient color
                 SystemMetrics.ScreenWidth, // end gradient x
                 SystemMetrics.ScreenHeight, // end gradient y
                 0xFF); // opacity
            bmp.Flush();
        }
    }
    //End class

    /// <summary>Result of a measurement.</summary>
    public class Measurement
    {
        /// <summary>The measured temperature in degrees Celsius.</summary>
        public double Temperature { get; private set; }

        /// <summary>The measured temperature in degrees Fahrenheit.</summary>
        public double TemperatureFahrenheit { get; private set; }

        /// <summary>The measured relative humidity.</summary>
        public double RelativeHumidity { get; private set; }

        internal Measurement(double temperature, double relativeHumidity)
        {
            this.RelativeHumidity = relativeHumidity;
            this.Temperature = temperature;
            this.TemperatureFahrenheit = temperature * 1.8 + 32.0;
        }

        /// <summary>Returns a string representation for Fahrenheit.</summary>
        public string ToStringF()
        {
            //Fahrenheit
            return this.TemperatureFahrenheit.ToString("F1") + " Deg. F, " + this.RelativeHumidity.ToString("F1") + "% relative humidity.";
        }
        //

        /// <summary>Returns a string representation for Celsius.</summary>
        public string ToStringC()
        {
            //Celsius
            return this.Temperature.ToString("F1") + " Deg. C, " + this.RelativeHumidity.ToString("F1") + "% relative humidity.";
        }
        //

    }
    //End class
} //End namespace

Thanks for any and all opinions ....


@ andre.m -

Thanks for the reply…

a different way if you like to use hardware i2c instead of software i2c

That is one of my problems. I really do not know what the difference is.

I assume that hardware i2c is if you use CPU pins that are identified as i2c pins but I reall do not know what the difference is.

Could you inform me?

Image shows I2C which I am using

Your image shows you’re using the actual I2C hardware pins. You can still use “software I2C” with those pins, if you want/need for some reason.

You’re largely right - the I2C hardware pins are handling a lot of the transmission autonomously rather than requiring active software control. This is the same as any form of software vs hardware peripheral control; you can fudge it in software, or you can leave the hardware to deal with it on your behalf. But given sometimes you have limited hardware peripherals, a software option often helps you achieve more with the limited resources you have.

For I2C general use patterns, have you spent time reading this? https://www.ghielectronics.com/docs/12/i2c Do you have any specific questions coming from that?

I have read the text at your link several times in the past.

When using I2C, it is highly recommended that you use the built-in hardware support for I2C. In some cases though it may be necessary to have another I2C bus or it is necessary to use specific pins that are not I2C pins. In this case, I2C can be handled all in software, though performance will be lower.

The GHI libraries includes a software I2C implementation in the GHI.Hardware assembly (GHI.IO.SoftwareI2CBus).

The statement is not clear to me… "it is highly recommended that you use the built-in hardware support for I2C"
I can’t wrap my mind around what exactly the built-in hardware support for I2C is.

Is this something in the firmware downloaded to the CPU?

Lets ask it another way… As in the code I show, do you consider it Hardware or Software I2C?

Yes - I am hard headed often…

But that’s just the way I was created…

Thanks for your reply!

OK, simplistically, here’s what your code does:


that is software I2C, pretty clearly.

Hardware I2C support is baked into the firmware, not something you need to do unless you're building firmware yourself. If you include the appropriate I2C module in your references, the interfaces will be exposed to you.

@ Brett -

Thank you again…

Have a happy holiday!

1 Like