TinyCLR OS 2.2.0.5.1 Release Update

Hi, thanks. There are three options:

  1. Wait for us to fix
  2. If you know how to rebuild library, go I2c.cs, line 718, change to
public TimeSpan Timeout { get ; set ; }
  1. Or just use the the fixed below as external class
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Devices.I2c.Provider;
using System;

namespace GHIElectronics.TinyCLR.Devices.I2c
{
    public class I2cControllerSoftwareProvider2 : II2cControllerProvider
    {
        private readonly bool usePullups;
        private readonly GpioPin sda;
        private readonly GpioPin scl;
        private byte writeAddress;
        private byte readAddress;
        private bool start;

        static string NotSupported = "Not supported.";

        public struct I2cTransferResult2
        {
            public I2cTransferStatus Status { get; }
            public int BytesWritten { get; }
            public int BytesRead { get; }

            public int BytesTransferred => this.BytesWritten + this.BytesRead;

            internal I2cTransferResult2(I2cTransferStatus status, int bytesWritten, int bytesRead)
            {
                this.Status = status;
                this.BytesWritten = bytesWritten;
                this.BytesRead = bytesRead;
            }
        }

        public event ErrorReceivedEventHandler ErrorReceived
        {
            add
            {
                throw new NotSupportedException(NotSupported);
            }

            remove => throw new NotSupportedException(NotSupported);
        }
        public event FrameReceivedEventHandler FrameReceived
        {
            add
            {
                throw new NotSupportedException(NotSupported);
            }
            remove
            {
                throw new NotSupportedException(NotSupported);
            }
        }

        public I2cControllerSoftwareProvider2(GpioPin sdaPin, GpioPin sclPin) : this(sdaPin, sclPin, true) { }

        public I2cControllerSoftwareProvider2(GpioPin sdaPin, GpioPin sclPin, bool usePullups)
        {
            this.usePullups = usePullups;

            this.sda = sdaPin;
            this.scl = sclPin;
        }

        public void Dispose()
        {
            this.sda.Dispose();
            this.scl.Dispose();
        }

        public void SetActiveSettings(I2cConnectionSettings connectionSettings)
        {
            if (connectionSettings.AddressFormat != I2cAddressFormat.SevenBit) throw new NotSupportedException();
            if (connectionSettings.Mode == I2cMode.Slave) throw new NotSupportedException();

            this.writeAddress = (byte)(connectionSettings.SlaveAddress << 1);
            this.readAddress = (byte)((connectionSettings.SlaveAddress << 1) | 1);
            this.start = false;

            this.ReleaseScl();
            this.ReleaseSda();
        }

        public I2cTransferStatus WriteRead(byte[] writeBuffer, int writeOffset, int writeLength, byte[] readBuffer, int readOffset, int readLength, out int written, out int read)
        {
            written = 0;
            read = 0;

            try
            {
                var res = this.Write(writeBuffer, writeOffset, writeLength, true, readLength == 0);

                written = res.BytesWritten;
                read = res.BytesRead;

                if (res.Status == I2cTransferStatus.FullTransfer && readLength != 0)
                {
                    res = this.Read(readBuffer, readOffset, readLength, true, true);

                    written += res.BytesWritten;
                    read += res.BytesRead;
                }

                this.ReleaseScl();
                this.ReleaseSda();

                return res.Status;

            }
            catch (I2cClockStretchTimeoutException)
            {
                return I2cTransferStatus.ClockStretchTimeout;
            }
        }

        private I2cTransferResult2 Write(byte[] buffer, int offset, int length, bool sendStart, bool sendStop)
        {
            if (!this.Send(sendStart, length == 0, this.writeAddress))
                return new I2cTransferResult2(I2cTransferStatus.SlaveAddressNotAcknowledged, 0, 0);

            for (var i = 0; i < length; i++)
                if (!this.Send(false, i == length - 1 && sendStop, buffer[i + offset]))
                    return new I2cTransferResult2(I2cTransferStatus.PartialTransfer, i, 0);

            return new I2cTransferResult2(I2cTransferStatus.FullTransfer, length, 0);
        }

        private I2cTransferResult2 Read(byte[] buffer, int offset, int length, bool sendStart, bool sendStop)
        {
            if (!this.Send(sendStart, length == 0, this.readAddress))
                return new I2cTransferResult2(I2cTransferStatus.SlaveAddressNotAcknowledged, 0, 0);

            for (var i = 0; i < length; i++)
                if (!this.Receive(i < length - 1, i == length - 1 && sendStop, out buffer[i + offset]))
                    return new I2cTransferResult2(I2cTransferStatus.PartialTransfer, 0, i);

            return new I2cTransferResult2(I2cTransferStatus.FullTransfer, 0, length);
        }

        private void ClearScl()
        {
            this.scl.SetDriveMode(GpioPinDriveMode.Output);
            this.scl.Write(GpioPinValue.Low);
        }

        private void ClearSda()
        {
            this.sda.SetDriveMode(GpioPinDriveMode.Output);
            this.sda.Write(GpioPinValue.Low);
        }

        private void ReleaseScl()
        {
            this.scl.SetDriveMode(this.usePullups ? GpioPinDriveMode.InputPullUp : GpioPinDriveMode.Input);
            this.ReadScl();
        }

        private void ReleaseSda()
        {
            this.sda.SetDriveMode(this.usePullups ? GpioPinDriveMode.InputPullUp : GpioPinDriveMode.Input);
            this.ReadSda();
        }

        private bool ReadScl()
        {
            this.scl.SetDriveMode(this.usePullups ? GpioPinDriveMode.InputPullUp : GpioPinDriveMode.Input);
            return this.scl.Read() == GpioPinValue.High;
        }

        private bool ReadSda()
        {
            this.sda.SetDriveMode(this.usePullups ? GpioPinDriveMode.InputPullUp : GpioPinDriveMode.Input);
            return this.sda.Read() == GpioPinValue.High;
        }

        private void WaitForScl()
        {
            const long TimeoutInTicks = 1000 * 1000 * 10; // Timeout: 1 second
            const long DelayInTicks = (1000000 / 2000) * 10; // Max frequency: 2KHz

            var currentTicks = DateTime.Now.Ticks;
            var timeout = true;

            while (DateTime.Now.Ticks - currentTicks < DelayInTicks / 2) ;

            while (timeout && DateTime.Now.Ticks - currentTicks < TimeoutInTicks)
            {
                if (this.ReadScl()) timeout = false;
            }

            if (timeout)
                throw new I2cClockStretchTimeoutException();

            var periodClockInTicks = DateTime.Now.Ticks - currentTicks;

            currentTicks = DateTime.Now.Ticks;

            while (DateTime.Now.Ticks - currentTicks < periodClockInTicks) ;
        }

        private bool WriteBit(bool bit)
        {
            if (bit)
                this.ReleaseSda();
            else
                this.ClearSda();

            this.WaitForScl();

            if (bit && !this.ReadSda())
                return false;

            this.ClearScl();

            return true;
        }

        private bool ReadBit()
        {
            this.ReleaseSda();

            this.WaitForScl();

            var bit = this.ReadSda();

            this.ClearScl();

            return bit;
        }

        private bool SendStart()
        {
            if (this.start)
            {
                this.ReleaseSda();

                this.WaitForScl();
            }

            if (!this.ReadSda())
                return false;

            this.ClearSda();

            this.ClearScl();

            this.start = true;

            return true;
        }

        private bool SendStop()
        {
            this.ClearSda();

            this.WaitForScl();

            if (!this.ReadSda())
                return false;

            this.start = false;

            return true;
        }

        private bool Send(bool sendStart, bool sendStop, byte data)
        {
            if (sendStart)
                this.SendStart();

            for (var bit = 0; bit < 8; bit++)
            {
                this.WriteBit((data & 0x80) != 0);

                data <<= 1;
            }

            var nack = this.ReadBit();

            if (sendStop)
                this.SendStop();

            return !nack;
        }

        private bool Receive(bool sendAck, bool sendStop, out byte data)
        {
            data = 0;

            for (var bit = 0; bit < 8; bit++)
                data = (byte)((data << 1) | (this.ReadBit() ? 1 : 0));

            var res = this.WriteBit(!sendAck);

            return (!sendStop || this.SendStop()) && res;
        }

        private class I2cClockStretchTimeoutException : Exception
        {

        }

        public int WriteBufferSize { get => throw new NotSupportedException(NotSupported); set => throw new NotSupportedException(NotSupported); }
        public int ReadBufferSize { get => throw new NotSupportedException(NotSupported); set => throw new NotSupportedException(NotSupported); }
        public int BytesToWrite => throw new NotSupportedException(NotSupported);
        public int BytesToRead => throw new NotSupportedException(NotSupported);

        public TimeSpan Timeout { get ; set ; }

        public void ClearWriteBuffer() => throw new NotSupportedException(NotSupported);
        public void ClearReadBuffer() => throw new NotSupportedException(NotSupported);
    }
}

usage: same, but here just in case.

var sda = GpioController.GetDefault().OpenPin(sdaPin);
var scl = GpioController.GetDefault().OpenPin(sclPin);

var provider = new I2cControllerSoftwareProvider2(sda, scl, true);

 var i2cControllers = I2cController.FromProvider(provider);