Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error Toggling GPIO pin #2384

Open
rathbone01 opened this issue Feb 24, 2025 · 18 comments
Open

Error Toggling GPIO pin #2384

rathbone01 opened this issue Feb 24, 2025 · 18 comments
Labels
bug Something isn't working untriaged

Comments

@rathbone01
Copy link

Describe the bug

When trying to use a GpioController and opening pin 10 as an output on a RasPi CM4, then call .Toggle I am getting an exception.

Steps to reproduce

private async void BlinkLoop()
{
    if (controller is null)
    {
        _logger?.LogError("GpioController not initialized");
        return;
    }

    try
    {
        controller.OpenPin(actLedPin, PinMode.Output);

    }
    catch (Exception ex)
    {
        _logger?.LogError($"Error opening actLedPin: {ex.Message}");
    }

    try
    {
        while (!powerPanic)
        {
            controller.Toggle(actLedPin);
            await Task.Delay(500);
        }
    }
    catch (Exception ex)
    {
        _logger?.LogError($"Error in BlinkLoop: {ex.Message}: {ex.StackTrace}");
    }
}

Expected behavior

I expect that Gpio to toggle on and off.

Actual behavior

Get the following exception:
2025-02-21 08:35:49.307 -06:00 [ERR] Error in BlinkLoop: The given key '10' was not present in the dictionary.: at System.Collections.Concurrent.ConcurrentDictionary2.ThrowKeyNotFoundException(TKey key) at System.Collections.Concurrent.ConcurrentDictionary2.get_Item(TKey key)
at System.Device.Gpio.Drivers.Libgpiod.V1.LibGpiodV1Driver.Toggle(Int32 pinNumber)
at System.Device.Gpio.Drivers.RaspberryPi3LinuxDriver.Toggle(Int32 pinNumber)
at System.Device.Gpio.Drivers.RaspberryPi3Driver.Toggle(Int32 pinNumber)
at System.Device.Gpio.GpioController.Toggle(Int32 pinNumber)
at GapMonitor.Services.GpioService.BlinkLoop() in C:\Users\heatb\Desktop\GitHub_Repositories\DaTran-Fueling-Server\GapMonitor\Services\GpioService.cs:line 102

Versions used
8.0.8 - dotnet --info on the machine being used to build
8.0.8 - dotnet --info on the machine where app is being run (not applicable for self-contained apps)
3.2.0 - Version of System.Device.Gpio package
3.2.0 - Version of Iot.Device.Bindings package (not needed if bug is in System.Device.Gpio)

@rathbone01 rathbone01 added the bug Something isn't working label Feb 24, 2025
@raffaeler
Copy link
Contributor

Hi @rathbone01, sorry for the problem.

Your exception shows RaspberryPi3LinuxDriver in the stack trace which means that you are using a RPi3 driver instead of a RPi4, which can most probably depends on the detection algorithm.

The Raspberry boards continuously change revision and model numbers and this may affect the ability to recognize the board. This in turn affects the driver detection algorithm.

Please provide the model number of your CM4 as described here:
http://www.raspberrypi-spy.co.uk/2012/09/checking-your-raspberry-pi-board-version/

The detection happens with the following model numbers: https://github.com/dotnet/iot/blob/main/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs#L155

Please post the controller initialization code too.

@rathbone01
Copy link
Author

Alright, I will provide that when I get a chance.
I would like to add, in trying to fix some bugs before doing a deployment tomorrow I switched to .Write instead of .Toggle and that is functioning correctly.

Here is where I init:

        public bool Initalized { get; private set; } = false;

        private const int powerPanicPin = 14;
        private bool powerPanic = false;
        private int debounce = 10;

        private const int stResetPin = 27;
        private const int actLedPin = 10;
        private const int usbPowerControl = 3;
        private const int piPowerOverrride = 11;

        private ILogger<GpioService>? _logger;
        private GpioController? controller;
        private GapMonitorService? GapMonitorService;

        private List<int> relays = new() { 5, 6, 13, 19, 26, 7, 8, 9 }; // 1 is gpio 5 on rev D
        private IServiceProvider serviceProvider;

        public GpioService(ILogger<GpioService> logger, IServiceProvider serviceProvider)
        {
            _logger = logger;
            _logger.LogInformation("GpioService created");
            this.serviceProvider = serviceProvider;
        }



        public void Setup()
        {
            // if not on linux, don't setup the GPIO
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                _logger?.LogInformation("Not on linux, skipping GPIO setup");
                return;
            }

            GapMonitorService = serviceProvider?.GetService<GapMonitorService>();
            if (GapMonitorService is null)
                _logger?.LogError("GpioService: LevelMonitorService is null");

            controller = new GpioController();

            // Setup power signal pin
            controller.OpenPin(usbPowerControl, PinMode.Output, PinValue.High);

            // Setup power override pin
            controller.OpenPin(piPowerOverrride, PinMode.Output, PinValue.Low);

            // Setup power panic pin
            controller.OpenPin(powerPanicPin, PinMode.InputPullUp);
            controller.RegisterCallbackForPinValueChangedEvent(powerPanicPin, PinEventTypes.Falling, PowerPanicPinChanged);

            foreach (int i in relays)
            {
                controller.OpenPin(i, PinMode.Output, PinValue.Low);
            }

            _logger?.LogInformation("GpioService setup");
            Initalized = true;

            // Start blinking light
            BlinkLoop();
        }

@raffaeler
Copy link
Contributor

The Toggle function uses Write to toggle the value.
https://github.com/dotnet/iot/blob/29639448a0a0bbf6a1841dea7b2fc76bf4dfa300/src/System.Device.Gpio/System/Device/Gpio/Drivers/Libgpiod/V1/LibGpiodV1Driver.cs#L244C5-L244C103

So the problem is that you probably does not initialize the pin by reading or writing an initial state.
After initializing the controller, set an initial value (or just read it) so that the ConcurrentDictionary contains the value for that pin. After this Toggle should work as expected.

@rathbone01
Copy link
Author

would this also work, setting it when it is opened?

controller.OpenPin(actLedPin, PinMode.Output, PinValue.High);

@raffaeler
Copy link
Contributor

raffaeler commented Feb 25, 2025

Yes:

public GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue)

    public GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue)
    {
        var pin = OpenPin(pinNumber);
        // Set the desired initial value
        _openPins[pinNumber] = initialValue;

        SetPinMode(pinNumber, mode);
        return pin;
    }

@rathbone01
Copy link
Author

Alright thanks!

@raffaeler
Copy link
Contributor

@rathbone01 We would appreciate if you could run the diagnostics on your Raspberry to verify the id number of your CM4 board.
Thanks

@rathbone01
Copy link
Author

Probably more than you wanted, but here is the output of each of those commands:

Image

FlowTran@CBMD-9d56:/ $ cat /proc/cpuinfo
processor : 0
BogoMIPS : 108.00
Features : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3

processor : 1
BogoMIPS : 108.00
Features : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3

processor : 2
BogoMIPS : 108.00
Features : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3

processor : 3
BogoMIPS : 108.00
Features : fp asimd evtstrm crc32 cpuid
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x0
CPU part : 0xd08
CPU revision : 3

Revision : b03141
Serial : 10000000dc9e3749
Model : Raspberry Pi Compute Module 4 Rev 1.1

FlowTran@CBMD-9d56:/ $ cat /proc/device-tree/model Raspberry Pi Compute Module 4 Rev 1.1

@raffaeler
Copy link
Contributor

Thank you. Apparently the "b03141" should match 0x3141 on our code but it doesn't.

@pgrawehr I believe the "b" pre-pending the revision of this CM4 board is killing the detection. What do you think?
https://github.com/dotnet/iot/blob/main/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs#L120

@rathbone01
Copy link
Author

Correct, the b will be included in the tryparse as a hex value

@rathbone01
Copy link
Author

int.TryParse("b03141", NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int firmware);
Console.WriteLine("0x" + firmware.ToString("X"));
string Connections = File.ReadAllText("connections.txt");
if (firmware == 0x3141)
{
    Console.WriteLine("Firmware is 0x3141");
}
else if (firmware == 0xB03141)
{
    Console.WriteLine("Firmware is 0xB03141");
}
else
{
    Console.WriteLine("Firmware is unknown");
}

gives the following output:
0xB03141
Firmware is 0xB03141

@krwq
Copy link
Member

krwq commented Feb 26, 2025

@raffaeler we should fix issue detection or create a new issue detailing what needs to be done before we close this

@krwq krwq reopened this Feb 26, 2025
@krwq
Copy link
Member

krwq commented Feb 26, 2025

btw there is masking in that logic so in theory that should work

@krwq
Copy link
Member

krwq commented Feb 26, 2025

Ok, I just did some quick checks to make sure this works as expected:

bool success = int.TryParse("b03141", NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int firmware);
Console.WriteLine($"TryParse: {success}");
Console.WriteLine($"Firmware: {firmware:X2}");

var maskedFirmware = firmware & 0xFFFF;
Console.WriteLine($"Firmware (masked): {maskedFirmware:X2}");

and that worked as expected.

I believe the problem will be in the settings loading:
https://github.com/dotnet/iot/blob/main/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs#L230
I suspect either exception or suffix is an issue (having said that I can't spot the error so someone will need to debug this if you also don't see anything obviously wrong).

@raffaeler
Copy link
Contributor

@raffaeler we should fix issue detection or create a new issue detailing what needs to be done before we close this

Totally agree, this is why I asked the OP to report the board result here.

Given the growing revision numbers, I believe it would be better to entirely change the detection algorithm and search for the model string instead:

cat /proc/device-tree/model Raspberry Pi Compute Module 4 Rev 1.1

@raffaeler
Copy link
Contributor

@rathbone01 could you please, cut-paste this class and run the detection code method on your CM4 to understand if this fails?
https://github.com/dotnet/iot/blob/main/src/System.Device.Gpio/System/Device/Gpio/RaspberryBoardInfo.cs#L230

Thank you

@krwq
Copy link
Member

krwq commented Feb 26, 2025

Also if you find a simple way to fix please send it here (or send a PR)

@pgrawehr
Copy link
Contributor

pgrawehr commented Feb 27, 2025

@rathbone01 Can you provide the output of gpioController.QueryComponentInformation()? That will give us a hint what hardware is actually detected. The Raspberrry3Driver is definitelly not the problem, because that is used for the RPI4 as well, and as long as you're not using the built-in pull-up or pull-down feature of the GPIO lines, even a wrong detection will not have an effect.

Also, I think we can remove the RaspberryBoardInfo or move it to a better place. At the moment, it's only use is to distinguish between "standard" and "CM" type boards, for which there should be an easier way. The information from this class might be interesting, but since it's fully internal at the moment, it's overdesigned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working untriaged
Projects
None yet
Development

No branches or pull requests

4 participants