Feedback della community:aggiunta della diagnostica alla libreria Magellanic.I2C

Feedback della community:aggiunta della diagnostica alla libreria Magellanic.I2C

Di recente ho chattato con un altro ingegnere che utilizza Raspberry Pi con Windows IoT e che ha riscontrato alcuni problemi con un dispositivo di cui ho pubblicato di recente. Durante la nostra conversazione per cercare di identificare il problema, mi è venuto in mente che le informazioni diagnostiche sarebbero state davvero utili, ma non c'era un modo semplice per scoprirlo dai miei messaggi di errore. È sembrata un'opportunità per soddisfare un'esigenza della comunità, quindi scriverò di cogliere questa opportunità per migliorare il mio codice in questo post.

Tutto il codice seguente si trova nel repository GitHub Magellanic.I2C.

Migliore diagnostica della versione del dispositivo

Quando ho cercato di eseguire il debug dei problemi con il mio codice su un Raspberry Pi, una delle informazioni chiave da sapere è il numero di versione del software in esecuzione sul Pi e anche i dettagli della piattaforma hardware. Questo è abbastanza ben nascosto nell'API IoT Core di Windows 10, ma è sicuramente lì. Ho creato una nuova classe di diagnostica nel mio pacchetto Magellanic.I2C NuGet, che restituisce facilmente informazioni sul sistema operativo e sulle versioni hardware e software.

public static class I2cDiagnostics
{
    public static string GetDeviceOperatingSystem()
    {
        return new EasClientDeviceInformation().OperatingSystem;
    }
 
    public static string GetDeviceHardwareInformation()
    {
        var device = new EasClientDeviceInformation();
 
        return $"{device.SystemManufacturer}, {device.SystemProductName} ({device.SystemSku})";
    }
 
    public static string GetDeviceOperatingSystemVersion()
    {
        ulong version = 0;
        if (!ulong.TryParse(Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamilyVersion, out version))
        {
            return null;
        }
        else
        {
            var versionComponent1 = (version & 0xFFFF000000000000) >> 48;
            var versionComponent2 = (version & 0x0000FFFF00000000) >> 32;
            var versionComponent3 = (version & 0x00000000FFFF0000) >> 16;
            var versionComponent4 = version & 0x000000000000FFFF;
 
            return $"{versionComponent1}.{versionComponent2}.{versionComponent3}.{versionComponent4}";
        }
    }
}

Il codice seguente mostra come questo può essere utilizzato.

// This gives the hardware type and version of the device, as well as the SKU (stock-keeping unit) information
// e.g. Raspberry Pi, Raspberry Pi 3 (RPi3-1GB)
Debug.WriteLine(I2cDiagnostics.GetDeviceHardwareInformation());
 
// Normally expect this to be Windows!
Debug.WriteLine(I2cDiagnostics.GetDeviceOperatingSystem());
 
// This will be a version number in the format of "10.0.14342.1000"
Debug.WriteLine(I2cDiagnostics.GetDeviceOperatingSystemVersion());

Migliore gestione delle eccezioni

Un altro problema era che il mio codice non aveva eccezioni personalizzate:l'avevo scritto per lanciare un System.Exception standard con un messaggio descrittivo quando si verificava uno scenario imprevisto (per i motivi descritti qui). Tuttavia, dopo alcune eccezioni reali, so che gli utenti si aspettano di essere in grado di intercettare diverse condizioni di errore, quindi ho creato alcune eccezioni personalizzate per diversi scenari eccezionali.

Vale la pena notare che accresco il messaggio di errore con alcuni dei metodi diagnostici statici di cui sopra:ciò renderà molto più semplice individuare i problemi, soprattutto mentre il framework IoT Core di Windows 10 sta ancora cambiando rapidamente come parte del programma di anteprima di Windows Insider .

public class I2cDeviceConnectionException : Exception
{
    public I2cDeviceConnectionException(string message) : base($"{message} Device: {GetDeviceHardwareInformation()}, {GetDeviceOperatingSystem()} {GetDeviceOperatingSystemVersion()}")
    {
    }
}
public class I2cDeviceNotFoundException : Exception
{
    public I2cDeviceNotFoundException(string message) : base($"{message} Device: {GetDeviceHardwareInformation()}, {GetDeviceOperatingSystem()} {GetDeviceOperatingSystemVersion()}")
    {
    }
}
public class I2cSlaveAddressInUseException : Exception
{
    public I2cSlaveAddressInUseException(string message) : base($"{message} Device: {GetDeviceHardwareInformation()}, {GetDeviceOperatingSystem()} {GetDeviceOperatingSystemVersion()}")
    {
    }
}

Scansione I2C più intelligente e informazioni migliori

Nelle comunità Raspberry Pi che supportano altri sistemi operativi, è comunemente possibile verificare se sul bus del Pi sono disponibili dispositivi I2C e anche quali indirizzi slave consentono il trasferimento delle istruzioni. Questo è sicuramente possibile con C# su Raspberry Pi usando una leggera modifica al codice di inizializzazione I2C standard, quindi ho aggiunto un metodo statico chiamato DetectI2cDevicesAsync() alla classe I2cDiagnostics.

public async static Task<List<byte>> DetectI2cDevicesAsync()
{
    string advancedQueryString = I2cDevice.GetDeviceSelector();
 
    var deviceInformations = await DeviceInformation.FindAllAsync(advancedQueryString);
 
    if (!deviceInformations.Any())
    {
        throw new I2cDeviceNotFoundException("No I2C controllers are connected.");
    }
 
    var matchingAddresses = new List<byte>();
 
    for (byte i = 0; i < 128; i++)
    {
        var i2cSettings = new I2cConnectionSettings(i);
                
        i2cSettings.BusSpeed = I2cBusSpeed.FastMode;
                
        var i2cDevice = await I2cDevice.FromIdAsync(deviceInformations[0].Id, i2cSettings);
 
        var addressToReadFrom = new byte[] { 0x00, 0x00 };
 
        var result = i2cDevice.ReadPartial(addressToReadFrom);
 
        if (result.Status == I2cTransferStatus.FullTransfer)
        {
            matchingAddresses.Add(i);
        }
    }
 
    if (!matchingAddresses.Any())
    {
        throw new I2cDeviceNotFoundException("No I2C Devices found on the controller.");
    }
 
    return matchingAddresses;
}

Codice di esempio migliore

Infine, per ora, ho aggiunto un codice di esempio più completo ai file ReadMe.md del mio progetto su GitHub. In precedenza ho mostrato solo un metodo semplice su come utilizzare il codice, ma non su come chiamare quel metodo. Ho modificato i campioni dei miei sensori per essere descrizioni più complete di come utilizzare il codice con quei sensori.

Ad esempio, il codice seguente mostra come chiamare il metodo DetectI2cDevicesAsync() (senza avvisi del compilatore) e scrivere informazioni sullo standard output (indipendentemente dal fatto che sia l'indirizzo slave che risponde o informazioni sull'eccezione).

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
 
        Loaded += MainPage_Loaded;
    }
 
    private async void MainPage_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
    {
        try
        {
            var i2cDevices = await I2cDiagnostics.DetectI2cDevicesAsync();
 
            // Writes the first I2C device found to the standard output.
            Debug.WriteLine(i2cDevices[0]);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}

Spero che queste informazioni diagnostiche aggiuntive aiutino la community a utilizzare il pacchetto Magellanic.I2C NuGet.