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

Multiple controllers, same pin inconsistency #939

Closed
DanielSSilva opened this issue Jan 13, 2020 · 14 comments
Closed

Multiple controllers, same pin inconsistency #939

DanielSSilva opened this issue Jan 13, 2020 · 14 comments
Labels
area-System.Device.Gpio Contains types for using general-purpose I/O (GPIO) pins bug Something isn't working Design Discussion Ongoing discussion about design without consensus

Comments

@DanielSSilva
Copy link

This might not actually be a bug but it's something that we might need to discuss.

I was testing the framework, and created the following scenario:

  • Have an app that deals with turning a light on and off
  • Have another app that checks if a led is on, and if so do something else.

Essentially, we will have 2 controllers that will use the same pin.

From my understanding, 1 controller should only work with a set of pins, and it should not "play around" with pins used by another controller.

Steps to reproduce

using System;
using System.Device.Gpio;
using System.Threading;

namespace temp
{
    class Program
    {
        static void Main(string[] args)
        {
			GpioController controller1 = new GpioController();
			controller1.OpenPin(10, PinMode.Output);
			controller1.Write(10, PinValue.High);
			Thread.Sleep(5000);
			GpioController controller2 = new GpioController();
			controller2.OpenPin(10, PinMode.Input);
			controller2.Read(10);

			controller1.ClosePin(10);
			controller2.ClosePin(10);
        }
    }
}

Expected behavior
I would expect to be able to read the value and the pin keep the same state (High - meaning the led is on).

Actual behavior
The LED turns on, but when I read the value from it, it reports as LOW and the LED turns off, although it was expected to be HIGH.

If I try to simplify (this might actually be another issue, let me know if I should create another) and do:

  • First app turns ON the LED and exits (closes the pin and ends)
  • As soon as I close the Pin, the LED turns off because the state is set to LOW. (why does this even happen?)
  • Second app will always read low, since the first app set it to LOW when closed the pin

Versions used

Add following information:

  • dotnet --info on the machine being used to build
.NET Core SDK (reflecting any global.json):
 Version:   2.2.402
 Commit:    c7f2f96116

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.18363
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\2.2.402\

Host (useful for support):
  Version: 2.2.7
  Commit:  b1e29ae826

.NET Core SDKs installed:
  2.2.402 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]```

- `dotnet --info` on the machine where app is being run (not applicable for self-contained apps)

It was not possible to find any installed .NET Core SDKs
Did you mean to run .NET Core SDK commands? Install a .NET Core SDK from:
https://aka.ms/dotnet-download

Host (useful for support):
Version: 3.0.0
Commit: 7d57652f33

.NET Core SDKs installed:
No SDKs were found.

.NET Core runtimes installed:
Microsoft.AspNetCore.App 3.0.0 [/home/pi/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.0.0 [/home/pi/dotnet/shared/Microsoft.NETCore.App]

To install additional .NET Core runtimes or SDKs:
https://aka.ms/dotnet-download```

  • Version of System.Device.Gpio package: Version="1.0.0"
@DanielSSilva DanielSSilva added the bug Something isn't working label Jan 13, 2020
@joperezr
Copy link
Member

Thanks for logging this @DanielSSilva.

I may very well be misunderstanding the scenario, but I believe that if you use one pin (as output) to turn on an LED, you won't be able to turn that into an input pin and expect the LED to stay on. You can think of it as turning the pin to Input as having it flowing the current on the other way, so now instead of the pin flowing positive to the LED so it turns on, now that "one-way-street-if-you-will" ( 😄 ) is going the other way and the pin is now getting current from the LED (which obviously isn't producing any) so the value would read Low and the LED will turn off. What we could fix here, could be to allow reads on output pins, which I think we already have an issue for in the repo.

One other thing that scenarios like this help me realize, is that it might be a bit dangerous to keep our current design where a controller will close and dispose all the pins that it used when you dispose the controller, since it is possible that in some scenarios you might want to have two different controllers to use the same pin. We might want in this case to allow some advanced mode in a GpioController where we allow not closing pins on controller dispose, in case that is appropriate for your scenario.

@joperezr joperezr added Design Discussion Ongoing discussion about design without consensus area-System.Device.Gpio Contains types for using general-purpose I/O (GPIO) pins labels Jan 13, 2020
@joperezr joperezr added this to the Future milestone Jan 13, 2020
@pgrawehr
Copy link
Contributor

@joperezr The second point you mention is related to #878 and another instance of the problem discussed in #919. I was also using the GpioController always as singleton instance, but some bindings prevent that now (because they free the controller on dispose). With the next iteration, we should be more clear about the object lifetimes, especially of the GpioController instances, though.

I suggest to continue the design discussion in #878, as it affects that overall concept.

@DanielSSilva
Copy link
Author

Hello @joperezr.

Regarding your first statement

but I believe that if you use one pin (as output) to turn on an LED, you won't be able to turn that into an input pin and expect the LED to stay on.

I'm not sure if that's totally true. The reason why I'm saying this is because on our current PowerShell.IoT module, which relies on the Unosquare.Raspberry.IO, if we set the pin to be output and then read from that same pin as input we can read the value from it, while it keeps the state.
In this blog post I demo that ( you can scroll until the "The final script" section), by checking if a LED is ON or OFF.

There might be some work that's being done by the underlying library that's allowing me to do it, I don't know

@pgrawehr
Copy link
Contributor

Unosquare uses wiringpi as low-level interface, and that one internally uses the exactly same interface than this library. Also, I would be very confused about how that would physically work, since if an input pin can be used as a power source, it couldn't be used as a sensitive input for external data. Somehow, your observation most probably relies on some bug somewhere (i.e. pin status not really changed). Can you test whether it works for you also the other way round, that means switching the LED off, set the pin to input and read the state?
Note that you do not need to set the pin to input to be able to read the state. Reading from an output pin should be perfectly valid, and that's all what you need here.

The pins do have internal pull-up or pull-down resistors, that can make them read a certain value when set as input (even without anything connected), but these are too large to be able to drive an LED.

@microhobby
Copy link
Contributor

Wiringpi and /sys/class/gpio are deprecated, Kernel developers hate these interfaces, and discourage the use, because of behaviors like that. I would really like to see dotnet/iot project in the future just supporting libgpiod for Linux. The new user space GPIO management interfaces are the most secure, reliable and recommended way to work with GPIOs. They are designed to work in conjunction with the kernel space, to keep track of hardware and resource states in the best way.

@pgrawehr
Copy link
Contributor

pgrawehr commented Feb 7, 2020

For most purposes, we use /dev/gpiomem directly (that's also what wiringpi does, therefore my above reference). SysFs is still being used for interrupt driven callbacks, because that doesn't work directly on gpiomem. I haven't done many tests with Libgpiod yet.
Anyway, the OP described a problem where (so it seems) an output pin used to read wheter something was connected did not work as expected. It's unclear what he is expecting from that.

@joperezr
Copy link
Member

joperezr commented Feb 7, 2020

I would really like to see dotnet/iot project in the future just supporting libgpiod for Linux.

Our thinking here was that because libgpiod does require an extra library to be installed on your board, we wanted to also provide a way to have the library work without the need of installing anything else. That is why we use runtime detection to see if this library is installed and if so that is the one we use, but if not we fallback to the other drivers.

@microhobby
Copy link
Contributor

I would really like to see dotnet/iot project in the future just supporting libgpiod for Linux.

Our thinking here was that because libgpiod does require an extra library to be installed

ok, I get it. Maybe we can write the library part that we use with the interop, cause libgpiod abstract the ioctl(), read(), open() and poll() calls for the /dev/gpiochip char devices. But I don't know, maybe this is rewriting the wheel, libgpiod is a very complete library. But it is an option.

Another option, which I think would be easier, would be to deliver the library, compiled for each architecture, together with the Nuget package.

What do you think @joperezr ? Do you think it is valid to open a discussion about this around here?

@joperezr
Copy link
Member

TBH I don't think it isn't a very good idea to package a third party library and distribute it through our NuGet packages since that would mean that everytime that libgpiod has a fix, update or new feature we would have to react by shipping a new package again. We have cases in dotnet core where we depend on native libraries that are not preinstalled on all distros (like icu library for all of our globalization and localization libraries) which we don't package with our product, but instead just call out that if consumers want to use that feature they will have to preinstall some libs. I'm totally fine with communicating that installing libgpiod is the recommended way, or even condition some future new features only if you have libgpiod if not possible to do them with sysfs, but having the ability to fallback in case you really don't want to install the native library is still valuable I believe.

@microhobby
Copy link
Contributor

@joperezr ok, make sense. I fully agree to communicate and encourage the use of libgpiod. In the future I see no way out, these interfaces will be disabled in the distros. Fedora has started shipping without /sys/class/gpio for example: https://fedoraproject.org/wiki/Architectures/ARM/gpio

And I think other distros will follow this move soon.

@joperezr
Copy link
Member

Ideally, distros should start just having this library as part of the distro which will make it always pick that driver by default.

@pgrawehr
Copy link
Contributor

We could be providing a script that installs dependencies (i.e. the dotnet framework as well), which can be used as example for somebody distributing an application based on this library.

@joperezr
Copy link
Member

we could either do that or at least provide more docs on our repo on how to install libgpiod. The problem with the script is having to update it as it could come out of date fast, that said the same goes for the documentation.

@Ellerbach
Copy link
Member

[Triage] Depending on the hardware, this scenario will work or not work. And you most of the time can't just open the same pin 2 times with output and input at the same time.
If you really need this scenario, then use 2 different pins, let say 10 and 20, attache them together, the first controller will get the output pin 10 and the second the input pin 20. Both 10 and 20 are connected together with a resistor.
So closing this issue.

@ghost ghost locked as resolved and limited conversation to collaborators Aug 1, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Device.Gpio Contains types for using general-purpose I/O (GPIO) pins bug Something isn't working Design Discussion Ongoing discussion about design without consensus
Projects
None yet
Development

No branches or pull requests

5 participants