dotnet / iot

This repo includes .NET Core implementations for various IoT boards, chips, displays and PCBs.
MIT License
2.18k stars 586 forks source link

Raspberry Pi 4 Model B Support #524

Closed shaggygi closed 5 years ago

shaggygi commented 5 years ago

Most likely is supported, but needs to be verified and added to list.

https://www.raspberrypi.org/blog/raspberry-pi-4-on-sale-now-from-35/

EDIT (@krwq) Seems Raspberry Pi 4 is supported but we need to update board detection logic and pick LibgpiodDriver automatically

joperezr commented 5 years ago

Awesome, yeah I saw the announcement yesterday. I'm almost sure that .net core should run just fine there, so we shouldn't have to worry but it is nice to have this to be able to track the documentation change.

dobova86 commented 5 years ago

Not working for me under netcore 3.0 preview6. The code is working fine on pi 3/3+/cm3+. Pi 4 (2GB) with Buster installed. Unless I forgot some dependancy for System.Device.Gpio.Driver on the Pi 4! nuget pkg are 0.1.0 pre-19310.5

Unhandled Exception: System.AggregateException: One or more errors occurred. (Error 0 initializing the Gpio driver.) ---> System.IO.IOException: Error 0 initializing the Gpio driver. at System.Device.Gpio.Drivers.RaspberryPi3Driver.Initialize() at System.Device.Gpio.Drivers.RaspberryPi3Driver.OpenPin(Int32 pinNumber) at System.Device.Gpio.GpioController.OpenPin(Int32 pinNumber) at System.Device.Gpio.GpioController.OpenPin(Int32 pinNumber, PinMode mode) at Device.BG96Iot.BG96Iot..ctor() in H:\PROJECTS.2019\RPITest\RpiBG96Test\ConsoleApp1\BG96Iot.cs:line 61 at RpiBG96Test.Program.MainSetup() in H:\PROJECTS.2019\RPITest\RpiBG96Test\ConsoleApp1\Program.cs:line 30 at RpiBG96Test.Program.MainLoop() in H:\PROJECTS.2019\RPITest\RpiBG96Test\ConsoleApp1\Program.cs:line 38 --- End of inner exception stack trace --- at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at RpiBG96Test.Program.Main(String[] args) in H:\PROJECTS.2019\RPITest\RpiBG96Test\ConsoleApp1\Program.cs:line 24 ---> (Inner Exception #0) System.IO.IOException: Error 0 initializing the Gpio driver. at System.Device.Gpio.Drivers.RaspberryPi3Driver.Initialize() at System.Device.Gpio.Drivers.RaspberryPi3Driver.OpenPin(Int32 pinNumber) at System.Device.Gpio.GpioController.OpenPin(Int32 pinNumber) at System.Device.Gpio.GpioController.OpenPin(Int32 pinNumber, PinMode mode) at Device.BG96Iot.BG96Iot..ctor() in H:\PROJECTS.2019\RPITest\RpiBG96Test\ConsoleApp1\BG96Iot.cs:line 61 at RpiBG96Test.Program.MainSetup() in H:\PROJECTS.2019\RPITest\RpiBG96Test\ConsoleApp1\Program.cs:line 30 at RpiBG96Test.Program.MainLoop() in H:\PROJECTS.2019\RPITest\RpiBG96Test\ConsoleApp1\Program.cs:line 38<---

Aborted

joperezr commented 5 years ago

mm interesting, I wonder if that has anything to do with buster being installed instead of Raspbian. Can you see the file /dev/gpiomem? Are you running as sudo?

dobova86 commented 5 years ago

Hi @joperezr , I'm using official Pi Raspbian Buster distribution downloaded from the Raspberrypi site. The file /dev/gpiomem has root, gpio:

crw-rw---- 1 root gpio 247, 0 Jun 27 15:23 /dev/gpiomem

using sudo same exception. Maybe I need to grant "pi" user the "gpio" group . Edit: pi is in the gpio group already

dobova86 commented 5 years ago

If it can help, the problem is related to Pi 4. I compiled a blink test (iot/sample), and on Pi4 Buster I get the exception (user pi or root), but with the same Buster on a Pi3, it is working fine. I simply swaped the SD between boards.

        private void Initialize()
        {
            if (_registerViewPointer != null)
            {
                return;
            }

            lock (s_initializationLock)
            {
                if (_registerViewPointer != null)
                {
                    return;
                }

                int fileDescriptor = Interop.open(GpioMemoryFilePath, FileOpenFlags.O_RDWR | FileOpenFlags.O_SYNC);
                if (fileDescriptor < 0)
                {
                    throw new IOException($"Error {Marshal.GetLastWin32Error()} initializing the Gpio driver.");
                }

                IntPtr mapPointer = Interop.mmap(IntPtr.Zero, Environment.SystemPageSize, (MemoryMappedProtections.PROT_READ | MemoryMappedProtections.PROT_WRITE), MemoryMappedFlags.MAP_SHARED, fileDescriptor, GpioRegisterOffset);
                if (mapPointer.ToInt32() < 0)
                {
                    throw new IOException($"Error {Marshal.GetLastWin32Error()} initializing the Gpio driver.");
                }

                Interop.close(fileDescriptor);
                _registerViewPointer = (RegisterView*)mapPointer;
            }
        }

The Interop.open and/or Interop.mmap is not working on Pi4.

joperezr commented 5 years ago

Interesting. Can you try a couple of other scenarios as well:

  1. What happens if you force the use of the sysfs driver, does that work?
    using (GpioController controller = new GpioController(PinNumberingScheme.Logical, new SysFsDriver()))
    {
    //..
    }
  2. What happens if you install libgpiod library and then force the use of the libgpiod driver. To install libgpiod, run the following commands
    wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/libgpiod.sh
    chmod +x libgpiod.sh
    ./libgpiod.sh

    And after installing modify your controller so that it is newed up like this:

    using (GpioController controller = new GpioController(PinNumberingScheme.Logical, new LibGpiodDriver()))
    {
    //..
    }

Thanks in advance for trying this out 😄

dobova86 commented 5 years ago

Interesting. Can you try a couple of other scenarios as well:

1. What happens if you force the use of the sysfs driver, does that work?
using (GpioController controller = new GpioController(PinNumberingScheme.Logical, new SysFsDriver()))
{
  //..
}
1. What happens if you install libgpiod library and then force the use of the libgpiod driver. To install libgpiod, run the following commands
wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/libgpiod.sh
chmod +x libgpiod.sh
./libgpiod.sh

And after installing modify your controller so that it is newed up like this:

using (GpioController controller = new GpioController(PinNumberingScheme.Logical, new LibGpiodDriver()))
{
  //..
}

Thanks in advance for trying this out 😄

I will do, and let youk know the result... but very very strange behaviour.

dobova86 commented 5 years ago

@joperezr using SysFsDriver() it works but only as "root" using sudo. As a user it does throw an exception:

Unhandled Exception: System.UnauthorizedAccessException: Setting a mode to a pin requires root permissions. ---> System.UnauthorizedAccessException: Access to the path '/sys/class/gpio/gpio27/direction' is denied. ---> System.IO.IOException: Permission denied --- End of inner exception stack trace --- at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirectory, Func`2 errorRewriter)

As a user "pi" I can execute 'echo "out" /sys/class/gpio/gpio27/direction' without any issue. NOTE: the first run as user "pi" it throws execption, but second run it works fine. Third exception again, fourth it run fine. 😄

dobova86 commented 5 years ago

After compiling libgpiod, and modified blink test it is working fine, no exception.

Frankenslag commented 5 years ago

I am also seeing this issue. I haven't tried the libgpiod solution.

krwq commented 5 years ago

we need to update board detection logic and pick LibgpiodDriver automatically. cc: @buyaa-n I'll try to get us some RPi4s for testing (seems they might be out of stock already so we might need to wait with that unless someone who has it does the changes)

Frankenslag commented 5 years ago

I am not sure that this is an issue with board detection as the stack trace indicates that the RaspberryPi3Driver is being chosen. As @dobova86 has indicated it looks like the issue is around the interrop calls into libc.

Frankenslag commented 5 years ago

@krwq, @joperezr, @dobova86 I have had a think about this issue. The clue I believe is that Marshal.GetLastWin32Error() is not actually indicating that an error occurred. I believe that what has happened is that the address returned by mmap is in the upper half of the address range and thus converting it to an Int32 and then testing for it being negative causes the exception to be thrown even though the mapped pointer is just fine.

A suggested fix would be as follows :

                int fileDescriptor = Interop.open(GpioMemoryFilePath, FileOpenFlags.O_RDWR | FileOpenFlags.O_SYNC);
                if (fileDescriptor != -1)
                {
                    throw new IOException($"Error {Marshal.GetLastWin32Error()} initializing the Gpio driver.");
                }

                IntPtr mapPointer = Interop.mmap(IntPtr.Zero, Environment.SystemPageSize, (MemoryMappedProtections.PROT_READ | MemoryMappedProtections.PROT_WRITE), MemoryMappedFlags.MAP_SHARED, fileDescriptor, GpioRegisterOffset);
                if (mapPointer.ToInt32() != -1)
                {
                    throw new IOException($"Error {Marshal.GetLastWin32Error()} initializing the Gpio driver.");
                }

I wonder if the larger size RaspberryPi's (mine is 4GB) are bringing out issues that we may have not seen before.

krwq commented 5 years ago

@Frankenslag I'd recommend to send a PR with above with quick summary and example of what didn't work before and works after.

Since we don't have RPi4s yet we will test if it doesn't break RPi3 and if someone else confirms RPi4 issue is fixed we can merge.

Once we get RPi4 we will likely quickly know if fix works

joperezr commented 5 years ago

I haven't been able to test this as I don't have a RPi4 yet, but my hunch is that similar to what @Frankenslag suggests, the issue seems to be that the new BCM chip may have a different register distribution in order to interact with the pins. Once we are able to repro, it would be great if we can get our hands on the datasheet for this BCM chip in order to be able to see what is going on here, and if the theory of the shifted registers make sense.

Frankenslag commented 5 years ago

@joperezr, @dobova86, @krwq PR #540 submitted. To confirm the theory I looked at the values being returned from mmap and in my case they were in the 0xB0000000 range so whatever the cause, be it buster or hardware, then this is clearly a value that the code wasn't expecting.

Will do some more confirmation testing but even if this isn't the whole story then the code from this PR follows the expected output of mmap.

krwq commented 5 years ago

@Frankenslag do the default options work with #540 merged or does it still require explicit LibGpiod driver? (i.e. will https://github.com/dotnet/iot/blob/master/samples/led-blink/Program.cs run without any modifications except for pin number?) cc: @RheaAyase

Frankenslag commented 5 years ago

I used the Ssd1351 for testing which just creates a default device (_gpioDevice = new GpioController();)

Also see resoponse to #557

RheaAyase commented 5 years ago

(i.e. will https://github.com/dotnet/iot/blob/master/samples/led-blink/Program.cs run without any modifications except for pin number?)

Pretty much the code I used. I did not use this exact example but the important bits are the same.

krwq commented 5 years ago

Ok, updating the doc and closing this then

pawelpetruch commented 5 years ago

I have a similar issue to @dobova86 on RPi 2, Raspbian Buster, netcore 3.0, Device.Gpio 1.0.0.


Unhandled exception. System.UnauthorizedAccessException: Setting a mode to a pin requires root permissions.
 ---> System.UnauthorizedAccessException: Access to the path '/sys/class/gpio/gpio16/direction' is denied.
 ---> System.IO.IOException: Permission denied
   --- End of inner exception stack trace ---
   at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirectory, Func`2 errorRewriter)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String path, OpenFlags flags, Int32 mode)
   at System.IO.FileStream.OpenHandle(FileMode mode, FileShare share, FileOptions options)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at System.IO.StreamWriter.ValidateArgsAndOpenPath(String path, Boolean append, Encoding encoding, Int32 bufferSize)
   at System.IO.StreamWriter..ctor(String path)
   at System.IO.File.WriteAllText(String path, String contents)
   at System.Device.Gpio.Drivers.SysFsDriver.SetPinMode(Int32 pinNumber, PinMode mode)
   --- End of inner exception stack trace ---
   at System.Device.Gpio.Drivers.SysFsDriver.SetPinMode(Int32 pinNumber, PinMode mode)
   at System.Device.Gpio.GpioController.SetPinMode(Int32 pinNumber, PinMode mode)
   at System.Device.Gpio.GpioController.OpenPin(Int32 pinNumber, PinMode mode)

I have libgpiod built and in both cases:

GpioController(PinNumberingScheme.Logical, new SysFsDriver()); and GpioController(); the same error.

dobova86 commented 5 years ago

For me, now it is working ok in 1.0.0, and I don't need to sudo my program. I use : gpio = new GpioController(PinNumbering.Logical); and no problem now using gpio.OpenPin(), gpio.Write(). I don't use lipgpiod becouse it needs sudo.

pawelpetruch commented 5 years ago

@dobova86 'GpioController(PinNumberingScheme.Logical);' doesn't seem to be helping. With or without libgpiod the same issue.

Frankenslag commented 5 years ago

@pawelpetruch

does it work with sudo ?

If you just use GpioController() without specifying the device. is the exception stack exactly the same ? if not can we see it.

I think that this needs an issue of it's own as the current issue is about a fairly specific problem with where Pi4's have their IO mapped to.

pawelpetruch commented 5 years ago

@Frankenslag I can't run 'sudo dotnet ...' if that's what you mean ( sudo: dotnet: command not found).

Without specifying the device:

Unhandled exception. System.UnauthorizedAccessException: Setting a mode to a pin requires root permissions.
 ---> System.UnauthorizedAccessException: Access to the path '/sys/class/gpio/gpio16/direction' is denied.
 ---> System.IO.IOException: Permission denied
   --- End of inner exception stack trace ---
   at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirectory, Func`2 errorRewriter)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String path, OpenFlags flags, Int32 mode)
   at System.IO.FileStream.OpenHandle(FileMode mode, FileShare share, FileOptions options)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at System.IO.StreamWriter.ValidateArgsAndOpenPath(String path, Boolean append, Encoding encoding, Int32 bufferSize)
   at System.IO.StreamWriter..ctor(String path)
   at System.IO.File.WriteAllText(String path, String contents)
   at System.Device.Gpio.Drivers.SysFsDriver.SetPinMode(Int32 pinNumber, PinMode mode)
   --- End of inner exception stack trace ---
   at System.Device.Gpio.Drivers.SysFsDriver.SetPinMode(Int32 pinNumber, PinMode mode)
   at System.Device.Gpio.Drivers.RaspberryPi3Driver.AddCallbackForPinValueChangedEvent(Int32 pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback)
   at System.Device.Gpio.GpioController.RegisterCallbackForPinValueChangedEvent(Int32 pinNumber, PinEventTypes eventTypes, PinChangeEventHandler callback)

To get it running I need to firstly create gpio pins using:

sudo sh -c "echo 16 > /sys/class/gpio/export

Frankenslag commented 5 years ago

@pawelpetruch

running something like below seems to solve the issue

sudo dotnet testprog.dll

I have had a look at this on a Pi3B and checked out the following to code snippets

Snippet A:

            GpioController c1 = new GpioController(PinNumberingScheme.Logical, new SysFsDriver());
            c1.OpenPin(16, PinMode.Input);
            c1.RegisterCallbackForPinValueChangedEvent(16, PinEventTypes.Rising | PinEventTypes.Falling, (o, e) => { /* do nothing */ });

Snippet B:

            GpioController c1 = new GpioController();
            c1.OpenPin(16, PinMode.Input);
            c1.RegisterCallbackForPinValueChangedEvent(16, PinEventTypes.Rising | PinEventTypes.Falling, (o, e) => { /* do nothing */ });

In both cases an exception is thrown in SetPinMode. However if I try for a second time then both these snippets work without issue. (note that you have to reboot between tests so that you start from a known state).

I then tried using a VSDBG to debug and I got no errors at all.

I then changed snippet A to look like below

            GpioController c1 = new GpioController(PinNumberingScheme.Logical, new SysFsDriver());
            c1.OpenPin(16, PinMode.Input);
            System.Threading.Thread.Sleep(1000);
            c1.SetPinMode(16, PinMode.Input);
            c1.RegisterCallbackForPinValueChangedEvent(16, PinEventTypes.Rising | PinEventTypes.Falling, (o, e) => { /* do nothing */ });

That worked just fine every time. But if I changed snippet B in the same fashion then it made no difference.

My guesses are that there is a delay in SYSFS between opening the pin with an export and the pin appearing with the correct permissions in the userland file system. Then when SetPinMode is called the exception happens.

Snippet B doesn't work with the delay as it is using the /dev/gpiomem interface for general IO but when using GPIO eventing then falls back to SYSFS and you get the exception. I have found that the following code works but it's a bit horrid. Don't dispose c2 as long as you need the IO though

        GpioController c1 = new GpioController();
        GpioController c2 = new GpioController(PinNumberingScheme.Logical, new SysFsDriver());
        c2.OpenPin(16);
        System.Threading.Thread.Sleep(1000);
        c2.SetPinMode(16, PinMode.Input);
        c1.OpenPin(16);
        c1.RegisterCallbackForPinValueChangedEvent(16, PinEventTypes.Rising | PinEventTypes.Falling, (o, e) => { /* do nothing */ });

@krwq @joperezr Have you any comments on this. It looks like we may have to check that the SYSFS gpio pin is really open before trying to manipulate the pin.

krwq commented 5 years ago

@Frankenslag can you open a new issue with a simple repro we can take a look whenever we got time? I currently don't have cycles to look at this at the moment and it will get lost on the closed issue.

teisnet commented 4 years ago

I'm experiencing this issue too (System.UnauthorizedAccessException: Setting a mode to a pin requires root permissions.).

MilanNemet commented 4 years ago

Hi All,

Since the last raspbian update on my Raspberry Pi 4, I also got this:

Unhandled exception. System.UnauthorizedAccessException: Setting a mode to a pin requires root permissions.
 ---> System.UnauthorizedAccessException: Access to the path '/sys/class/gpio/gpio25/direction' is denied.
 ---> System.IO.IOException: Permission denied
   --- End of inner exception stack trace ---
   at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirectory, Func`2 errorRewriter)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String path, OpenFlags flags, Int32 mode)
   at System.IO.FileStream.OpenHandle(FileMode mode, FileShare share, FileOptions options)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at System.IO.StreamWriter.ValidateArgsAndOpenPath(String path, Boolean append, Encoding encoding, Int32 bufferSize)
   at System.IO.StreamWriter..ctor(String path)
   at System.IO.File.WriteAllText(String path, String contents)
   at System.Device.Gpio.Drivers.SysFsDriver.SetPinMode(Int32 pinNumber, PinMode mode)
   --- End of inner exception stack trace ---
   at System.Device.Gpio.Drivers.SysFsDriver.SetPinMode(Int32 pinNumber, PinMode mode)
   at System.Device.Gpio.GpioController.SetPinMode(Int32 pinNumber, PinMode mode)
   at System.Device.Gpio.GpioController.OpenPin(Int32 pinNumber, PinMode mode)
   at System.Device.Pwm.Drivers.SoftwarePwmChannel..ctor(Int32 pinNumber, Int32 frequency, Double dutyCycle, Boolean usePrecisionTimer, GpioController controller)
   at RasPi.MotorController..ctor() in /home/pi/Desktop/RasPi/MotorController.cs:line 26
   at RasPi.Program.Main() in /home/pi/Desktop/RasPi/Program.cs:line 11

I also tried to run the build with sudo:

A fatal error occurred. The required library libhostfxr.so could not be found.
If this is a self-contained application, that library should exist in [/home/pi/Desktop/RasPi/bin/Debug/netcoreapp3.1/].
If this is a framework-dependent application, install the runtime in the global location [/usr/share/dotnet] or use the DOTNET_ROOT environment variable to specify the runtime location or register the runtime location in [/etc/dotnet/install_location].

So I installed dotnet to /usr/share/dotnet, and now if I try to run the build as root it says:

Unhandled exception. System.IO.IOException: Device or resource busy
   at System.IO.FileStream.WriteNative(ReadOnlySpan`1 source)
   at System.IO.FileStream.FlushWriteBuffer()
   at System.IO.FileStream.FlushInternalBuffer()
   at System.IO.FileStream.Flush(Boolean flushToDisk)
   at System.IO.FileStream.Flush()
   at System.IO.StreamWriter.Flush(Boolean flushStream, Boolean flushEncoder)
   at System.IO.StreamWriter.Dispose(Boolean disposing)
   at System.IO.TextWriter.Dispose()
   at System.IO.File.WriteAllText(String path, String contents)
   at System.Device.Gpio.Drivers.SysFsDriver.OpenPin(Int32 pinNumber)
   at System.Device.Gpio.GpioController.OpenPin(Int32 pinNumber)
   at System.Device.Gpio.GpioController.OpenPin(Int32 pinNumber, PinMode mode)
   at RasPi.MotorController.InitMotors() in /home/pi/Desktop/RasPi/MotorController.cs:line 44
   at RasPi.MotorController..ctor() in /home/pi/Desktop/RasPi/MotorController.cs:line 32
   at RasPi.Program.Main() in /home/pi/Desktop/RasPi/Program.cs:line 11
Aborted

Btw, I did the OS upgrade, because between two release builds, my adafruit-blinka python library has crashed. Here's the repo related to this project: RasPi

Ellerbach commented 4 years ago

Since the last raspbian update on my Raspberry Pi 4, I also got this

@MilanNemet, Maybe you should use the latest preview versions for System.Device.Gpio. See https://github.com/dotnet/iot#how-to-install You're using the latest release which for sure have issues with RPI4.

Also so far, you may need to force the usage of the RPI3 driver when creating the Gpiocontroller.