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

Callbacks registered via RegisterCallbackForPinValueChangedEvent are not getting called when pin value changes #2131

Closed
m4schini opened this issue Sep 12, 2023 · 23 comments · Fixed by #2151
Labels
bug Something isn't working Priority:1 Work that is critical for the release, but we could probably ship without Status: Fixed

Comments

@m4schini
Copy link

Describe the bug

After registering a Callback via RegisterCallbackForPinValueChangedEvent and changing the value of the gpio pin, the callback is not being called.
We confirmed that the pin value is being changed (Controller.Read() and gpiod terminal utilities).

Steps to reproduce

The environment has libgpiod-dev installed.

var button_drvGpio = new LibGpiodDriver(BUTTON_GPIOCHIP);
buttonController = new GpioController(PinNumberingScheme.Logical, button_drvGpio);

if (!buttonController.IsPinOpen(BUTTON_PIN))
{
    buttonController.OpenPin(BUTTON_PIN, PinMode.Input);
}
else
{
    logger.LogInformation("BUTTON_PIN is busy");
    return;
}

logger.LogInformation("Registering Callback");
buttonController.RegisterCallbackForPinValueChangedEvent(BUTTON_PIN, PinEventTypes.Rising, (o, e) =>
{
    logger.LogInformation("Button pressed");
});

Expected behavior

The register callback shoud be called when the event happen.

Actual behavior

The register callback is not being called.

Versions used

  • dotnet --info on the machine where app is being run (not applicable for self-contained apps)
Host:
  Version:      7.0.1
  Architecture: arm
  Commit:       97203d38ba

.NET SDKs installed:
  No SDKs were found.

.NET runtimes installed:
  Microsoft.AspNetCore.App 7.0.1 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 7.0.1 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  None

Environment variables:
  Not set

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download
  • Image used in the build stage: mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-amd64

  • Image running in the container: mcr.microsoft.com/dotnet/aspnet:7.0.1-alpine3.17

  • Version of System.Device.Gpio package: 3.0.0

@m4schini m4schini added the bug Something isn't working label Sep 12, 2023
@ghost ghost added the untriaged label Sep 12, 2023
@pgrawehr
Copy link
Contributor

What hardware are you running this on? And is there a specific reason why you run your app in a container? This adds additional complexity.

@m4schini
Copy link
Author

This is running on a custom linux arm iot platform using the azure iot edge runtime. We are using the container runtime from iot edge to deploy our application to the device.

@pgrawehr
Copy link
Contributor

The callback is known to work on other platforms, so there must be something special about yours, maybe the libgpiod driver is not fully compatible. Have you tried the sysfs driver?

@m4schini
Copy link
Author

When using the SysFs driver the following exception was thrown when pressing the button connected to the pin:

Unhandled exception. System.IO.IOException: Error while trying to seek in value file.
   at System.Device.Gpio.Drivers.SysFsDriver.WasEventDetected(Int32 pollFileDescriptor, Int32 valueFileDescriptor, Int32& pinNumber, CancellationToken cancellationToken)
   at System.Device.Gpio.Drivers.SysFsDriver.DetectEvents()
   at System.Threading.Thread.StartHelper.Callback(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Thread.StartCallback()

@krwq
Copy link
Member

krwq commented Sep 14, 2023

[Triage] are you forwarding pin related directories to the docker container? what's the exact command line you're using to run your container? (or at least relevant pieces to GPIO) If you can please also share dockerfile

@krwq krwq added the Needs: Author Feedback We are waiting for author to react to feedback (action required) label Sep 14, 2023
@krwq
Copy link
Member

krwq commented Sep 14, 2023

Here are some basic instructions how to run in the container: https://github.com/dotnet/iot/tree/main/samples/led-blink (last command line is for sysfs)

@ghost
Copy link

ghost commented Sep 18, 2023

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.

@m4schini
Copy link
Author

are you forwarding pin related directories to the docker container?

Yes, we run the container privileged and we forwarded the directory to the container.

what's the exact command line you're using [...] please also share dockerfile

# final stage/image
FROM mcr.microsoft.com/dotnet/aspnet:7.0.1-alpine3.17
ARG PROJ_NAME
ARG PORT

VOLUME /hostpipe
VOLUME /scripts
VOLUME /libsFromHost

COPY --from=build /testresults /testresults

WORKDIR /app

COPY --from=build /app .
COPY --from=build /app/CBinaries/*.so /app

EXPOSE $PORT
EXPOSE 80

# Copy repository configuration (optional)
COPY alpinehelper/etc/apk/repositories /etc/apk/repositories
# Copy our local repository
COPY alpinehelper/usr/local/repo /usr/local/repo
# Copy keys which we generated previously
COPY alpinehelper/etc/apk/keys /etc/apk/keys

RUN apk --no-cache add libmodbus libmodbus-dev
RUN echo "chmod a+r  /dev/ttySTM3" > ./entrypoint.sh

RUN apk --no-cache add libgpiod-dev gpsd
RUN echo "gpsd /dev/ttySTM3 -F /var/run/gpsd.sock" >> ./entrypoint.sh
RUN echo "./${PROJ_NAME}" >> ./entrypoint.sh

RUN cat ./entrypoint.sh

RUN chmod 777 ./entrypoint.sh
ENTRYPOINT ./entrypoint.sh

@ghost ghost removed Needs: Author Feedback We are waiting for author to react to feedback (action required) Status: No Recent Activity labels Sep 19, 2023
@joperezr
Copy link
Member

Thanks for the Dockerfile. We would also need the docker run command to see how are you volume-mounting the necessary files for sysfs driver to work.

@m4schini
Copy link
Author

We are using the azure iot edge runtime to deploy and start our containers. To do that we don't provide a command but a manifest where we define modules. We tried three different things, I will list the attempts below:

Attempt 1: only running privileged
          "module": {
            "startupOrder": 1,
            "version": "1.0",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "settings": {
              "image": "",
              "createOptions": {
                "HostConfig": {
                  "LogConfig": {
                    "Type": "json-file",
                    "Config": {
                      "max-size": "5m",
                      "max-file": "1"
                    }
                  },
                  "Privileged": true,
                  "Binds": [
                    "/dev/ttyWifi:/dev/ttyWifi",
                    "/dev/ttyRS485:/dev/ttyRS485",
                    "/sys:/sys"
                  ],
                  "IpcMode": "shareable",
                  "CapAdd": [
                    "NET_ADMIN"
                  ],
                  "Devices": [
                    {
                      "PathOnHost": "/dev/ttyWifi",
                      "PathInContainer": "/dev/ttyWifi",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "PathOnHost": "/dev/ttyRS485",
                      "PathInContainer": "/dev/ttyRS485",
                      "CgroupPermissions": "rwm"
                    }
                  ],
                  "Mounts": [
                    {
                      "Source": "/lib",
                      "Target": "/libsFromHost",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/mnt/pData/scripts",
                      "Target": "/scripts",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/sys",
                      "Target": "/sys",
                      "Type": "bind",
                      "ReadOnly": false
                    }
                  ],
                  "PortBindings": {
                    "4444/tcp": [
                      {
                        "HostPort": "4444"
                      }
                    ],
                    "80/tcp": [
                      {
                        "HostPort": "80"
                      }
                    ]
                  }
                }
              }
            },
Attempt 2: Binding /sys
          "module": {
            "startupOrder": 1,
            "version": "1.0",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "settings": {
              "image": "",
              "createOptions": {
                "HostConfig": {
                  "LogConfig": {
                    "Type": "json-file",
                    "Config": {
                      "max-size": "5m",
                      "max-file": "1"
                    }
                  },
                  "Privileged": true,
                  "Binds": [
                    "/dev/ttyWifi:/dev/ttyWifi",
                    "/dev/ttyRS485:/dev/ttyRS485",
                    "/sys:/sys"
                  ],
                  "IpcMode": "shareable",
                  "CapAdd": [
                    "NET_ADMIN"
                  ],
                  "Devices": [
                    {
                      "PathOnHost": "/dev/ttyWifi",
                      "PathInContainer": "/dev/ttyWifi",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "PathOnHost": "/dev/ttyRS485",
                      "PathInContainer": "/dev/ttyRS485",
                      "CgroupPermissions": "rwm"
                    }
                  ],
                  "Mounts": [
                    {
                      "Source": "/lib",
                      "Target": "/libsFromHost",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/mnt/pData/scripts",
                      "Target": "/scripts",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/sys",
                      "Target": "/sys",
                      "Type": "bind",
                      "ReadOnly": false
                    }
                  ],
                  "PortBindings": {
                    "4444/tcp": [
                      {
                        "HostPort": "4444"
                      }
                    ],
                    "80/tcp": [
                      {
                        "HostPort": "80"
                      }
                    ]
                  }
                }
              }
            },
Attempt 3: Binding gpiochip
"module": {
            "startupOrder": 1,
            "version": "1.0",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "settings": {
              "image": "",
              "createOptions": {
                "HostConfig": {
                  "LogConfig": {
                    "Type": "json-file",
                    "Config": {
                      "max-size": "5m",
                      "max-file": "1"
                    }
                  },
                  "Privileged": true,
                  "Binds": [
                    "/dev/ttyWifi:/dev/ttyWifi",
                    "/dev/ttyRS485:/dev/ttyRS485",
                    "/dev/gpiochip2:/dev/gpiochip2",
                    "/dev/mem:/dev/mem"
                  ],
                  "IpcMode": "shareable",
                  "CapAdd": [
                    "NET_ADMIN"
                  ],
                  "Devices": [
                    {
                      "PathOnHost": "/dev/ttyWifi",
                      "PathInContainer": "/dev/ttyWifi",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "PathOnHost": "/dev/ttyRS485",
                      "PathInContainer": "/dev/ttyRS485",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "PathOnHost": "/dev/gpiochip2",
                      "PathInContainer": "/dev/gpiochip2",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "PathOnHost": "/dev/mem",
                      "PathInContainer": "/dev/mem",
                      "CgroupPermissions": "rwm"
                    }
                  ],
                  "Mounts": [
                    {
                      "Source": "/lib",
                      "Target": "/libsFromHost",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/mnt/pData/scripts",
                      "Target": "/scripts",
                      "Type": "bind",
                      "ReadOnly": false
                    }
                  ],
                  "PortBindings": {
                    "4444/tcp": [
                      {
                        "HostPort": "4444"
                      }
                    ],
                    "80/tcp": [
                      {
                        "HostPort": "80"
                      }
                    ]
                  }
                }
              }
            },

None of these attempts worked out.

@Ellerbach
Copy link
Member

Thanks @m4schini
Can you try priviledged with /dev to /dev and /sys to /sys binding. That should definitely work.
Then, you can restrict, depending on the GPIO you're using. They will bind differently if sysfs or libgpio, etc.
Check out the source code here for the driver you need (assuming you have the proper lib installed into the container): https://github.com/dotnet/iot/tree/main/src/System.Device.Gpio/System/Device/Gpio

@m4schini
Copy link
Author

We tried it as you described and the behavior for both Sysfs and libgpio is unchanged.

For reference the manifests we used:

Deployment manifest (/sys as device)
"module": {
            "startupOrder": 1,
            "version": "1.0",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "settings": {
              "image": "",
              "createOptions": {
                "HostConfig": {
                  "LogConfig": {
                    "Type": "json-file",
                    "Config": {
                      "max-size": "5m",
                      "max-file": "1"
                    }
                  },
                  "Privileged": true,
                  "Binds": [
                    "/dev/ttyWifi:/dev/ttyWifi",
                    "/dev/ttyRS485:/dev/ttyRS485",
                    "/dev:/dev",
                    "/sys:/sys"
                  ],
                  "IpcMode": "shareable",
                  "CapAdd": [
                    "NET_ADMIN"
                  ],
                  "Devices": [
                    {
                      "PathOnHost": "/dev/ttyWifi",
                      "PathInContainer": "/dev/ttyWifi",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "PathOnHost": "/dev/ttyRS485",
                      "PathInContainer": "/dev/ttyRS485",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "PathOnHost": "/dev",
                      "PathInContainer": "/dev",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "Source": "/sys",
                      "Target": "/sys",
                      "CgroupPermissions": "rwm"
                    }
                  ],
                  "Mounts": [
                    {
                      "Source": "/lib",
                      "Target": "/libsFromHost",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/mnt/pData/execpipe",
                      "Target": "/hostpipe",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/mnt/pData/scripts",
                      "Target": "/scripts",
                      "Type": "bind",
                      "ReadOnly": false
                    }
                  ],
                  "PortBindings": {
                    "4444/tcp": [
                      {
                        "HostPort": "4444"
                      }
                    ],
                    "80/tcp": [
                      {
                        "HostPort": "80"
                      }
                    ]
                  }
                }
              }
            },
Deployment manifest (/sys not as device)
"module": {
            "startupOrder": 1,
            "version": "1.0",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "settings": {
              "image": "",
              "createOptions": {
                "HostConfig": {
                  "LogConfig": {
                    "Type": "json-file",
                    "Config": {
                      "max-size": "5m",
                      "max-file": "1"
                    }
                  },
                  "Privileged": true,
                  "Binds": [
                    "/dev/ttyWifi:/dev/ttyWifi",
                    "/dev/ttyRS485:/dev/ttyRS485",
                    "/dev:/dev",
                    "/sys:/sys"
                  ],
                  "IpcMode": "shareable",
                  "CapAdd": [
                    "NET_ADMIN"
                  ],
                  "Devices": [
                    {
                      "PathOnHost": "/dev/ttyWifi",
                      "PathInContainer": "/dev/ttyWifi",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "PathOnHost": "/dev/ttyRS485",
                      "PathInContainer": "/dev/ttyRS485",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "PathOnHost": "/dev",
                      "PathInContainer": "/dev",
                      "CgroupPermissions": "rwm"
                    }
                  ],
                  "Mounts": [
                    {
                      "Source": "/lib",
                      "Target": "/libsFromHost",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/mnt/pData/execpipe",
                      "Target": "/hostpipe",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/mnt/pData/scripts",
                      "Target": "/scripts",
                      "Type": "bind",
                      "ReadOnly": false
                    }
                  ],
                  "PortBindings": {
                    "4444/tcp": [
                      {
                        "HostPort": "4444"
                      }
                    ],
                    "80/tcp": [
                      {
                        "HostPort": "80"
                      }
                    ]
                  }
                }
              }
            },

In addition to that we also tried to do the same with a slightly different manifest. This time instead of "Binds" we used "Mounts" with bind type:

Deployment manifest
"module": {
            "startupOrder": 1,
            "version": "1.0",
            "type": "docker",
            "status": "running",
            "restartPolicy": "always",
            "settings": {
              "image": "",
              "createOptions": {
                "HostConfig": {
                  "LogConfig": {
                    "Type": "json-file",
                    "Config": {
                      "max-size": "5m",
                      "max-file": "1"
                    }
                  },
                  "Privileged": true,
                  "Binds": [
                    "/dev/ttyWifi:/dev/ttyWifi",
                    "/dev/ttyRS485:/dev/ttyRS485"
                  ],
                  "IpcMode": "shareable",
                  "CapAdd": [
                    "NET_ADMIN"
                  ],
                  "Devices": [
                    {
                      "PathOnHost": "/dev/ttyWifi",
                      "PathInContainer": "/dev/ttyWifi",
                      "CgroupPermissions": "rwm"
                    },
                    {
                      "PathOnHost": "/dev/ttyRS485",
                      "PathInContainer": "/dev/ttyRS485",
                      "CgroupPermissions": "rwm"
                    }
                  ],
                  "Mounts": [
                    {
                      "Source": "/lib",
                      "Target": "/libsFromHost",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/mnt/pData/execpipe",
                      "Target": "/hostpipe",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/mnt/pData/scripts",
                      "Target": "/scripts",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "PathOnHost": "/dev",
                      "PathInContainer": "/dev",
                      "Type": "bind",
                      "ReadOnly": false
                    },
                    {
                      "Source": "/sys",
                      "Target": "/sys",
                      "Type": "bind",
                      "ReadOnly": false
                    }
                  ],
                  "PortBindings": {
                    "4444/tcp": [
                      {
                        "HostPort": "4444"
                      }
                    ],
                    "80/tcp": [
                      {
                        "HostPort": "80"
                      }
                    ]
                  }
                }
              }
            },

And got the following error in the iot edge agent (which is starting the container on the device)

System.AggregateException: One or more errors occurred. (Error calling Create module module: runtime operation error: create module "module")
 ---> Microsoft.Azure.Devices.Edge.Agent.Edgelet.EdgeletCommunicationException- Message:Error calling Create module module: runtime operation error: create module "module", StatusCode:400, at:   at Microsoft.Azure.Devices.Edge.Agent.Edgelet.Version_2022_08_03.ModuleManagementHttpClient.HandleException(Exception exception, String operation) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/version_2022_08_03/ModuleManagementHttpClient.cs:line 232
   at Microsoft.Azure.Devices.Edge.Agent.Edgelet.Versioning.ModuleManagementHttpClientVersioned.Execute[T](Func`1 func, String operation) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/versioning/ModuleManagementHttpClientVersioned.cs:line 171
   at Microsoft.Azure.Devices.Edge.Agent.Edgelet.Version_2022_08_03.ModuleManagementHttpClient.CreateModuleAsync(ModuleSpec moduleSpec) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/version_2022_08_03/ModuleManagementHttpClient.cs:line 102
   at Microsoft.Azure.Devices.Edge.Agent.Edgelet.ModuleManagementHttpClient.<>c__DisplayClass26_0.<<Throttle>b__0>d.MoveNext() in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/ModuleManagementHttpClient.cs:line 145
--- End of stack trace from previous location ---
   at Microsoft.Azure.Devices.Edge.Agent.Edgelet.ModuleManagementHttpClient.Throttle[T](Func`1 identityOperation) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/ModuleManagementHttpClient.cs:line 164
   at Microsoft.Azure.Devices.Edge.Agent.Core.Commands.GroupCommand.ExecuteAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/commands/GroupCommand.cs:line 35
   at Microsoft.Azure.Devices.Edge.Agent.Core.LoggingCommandFactory.LoggingCommand.ExecuteAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/LoggingCommandFactory.cs:line 69
   at Microsoft.Azure.Devices.Edge.Agent.Core.Commands.GroupCommand.ExecuteAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/commands/GroupCommand.cs:line 35
   at Microsoft.Azure.Devices.Edge.Agent.Core.LoggingCommandFactory.LoggingCommand.ExecuteAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/LoggingCommandFactory.cs:line 69
   at Microsoft.Azure.Devices.Edge.Agent.Core.PlanRunner.OrderedRetryPlanRunner.ExecuteAsync(Int64 deploymentId, Plan plan, CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/planrunner/OrdererdRetryPlanRunner.cs:line 91
   --- End of inner exception stack trace ---
   at Microsoft.Azure.Devices.Edge.Agent.Core.PlanRunner.OrderedRetryPlanRunner.<>c.<ExecuteAsync>b__7_0(List`1 f) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/planrunner/OrdererdRetryPlanRunner.cs:line 129
   at Microsoft.Azure.Devices.Edge.Agent.Core.PlanRunner.OrderedRetryPlanRunner.ExecuteAsync(Int64 deploymentId, Plan plan, CancellationToken token)
   at Microsoft.Azure.Devices.Edge.Agent.Core.Agent.ReconcileAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/Agent.cs:line 208
<4> 2023-09-21 08:58:23.738 +00:00 [WRN] - Reconcile failed because of the an exception
System.AggregateException: One or more errors occurred. (Error calling Create module module: runtime operation error: create module "module")
 ---> Microsoft.Azure.Devices.Edge.Agent.Edgelet.EdgeletCommunicationException- Message:Error calling Create module module: runtime operation error: create module "module", StatusCode:400, at:   at Microsoft.Azure.Devices.Edge.Agent.Edgelet.Version_2022_08_03.ModuleManagementHttpClient.HandleException(Exception exception, String operation) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/version_2022_08_03/ModuleManagementHttpClient.cs:line 232
   at Microsoft.Azure.Devices.Edge.Agent.Edgelet.Versioning.ModuleManagementHttpClientVersioned.Execute[T](Func`1 func, String operation) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/versioning/ModuleManagementHttpClientVersioned.cs:line 171
   at Microsoft.Azure.Devices.Edge.Agent.Edgelet.Version_2022_08_03.ModuleManagementHttpClient.CreateModuleAsync(ModuleSpec moduleSpec) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/version_2022_08_03/ModuleManagementHttpClient.cs:line 102
   at Microsoft.Azure.Devices.Edge.Agent.Edgelet.ModuleManagementHttpClient.<>c__DisplayClass26_0.<<Throttle>b__0>d.MoveNext() in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/ModuleManagementHttpClient.cs:line 145
--- End of stack trace from previous location ---
   at Microsoft.Azure.Devices.Edge.Agent.Edgelet.ModuleManagementHttpClient.Throttle[T](Func`1 identityOperation) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Edgelet/ModuleManagementHttpClient.cs:line 164
   at Microsoft.Azure.Devices.Edge.Agent.Core.Commands.GroupCommand.ExecuteAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/commands/GroupCommand.cs:line 35
   at Microsoft.Azure.Devices.Edge.Agent.Core.LoggingCommandFactory.LoggingCommand.ExecuteAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/LoggingCommandFactory.cs:line 69
   at Microsoft.Azure.Devices.Edge.Agent.Core.Commands.GroupCommand.ExecuteAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/commands/GroupCommand.cs:line 35
   at Microsoft.Azure.Devices.Edge.Agent.Core.LoggingCommandFactory.LoggingCommand.ExecuteAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/LoggingCommandFactory.cs:line 69
   at Microsoft.Azure.Devices.Edge.Agent.Core.PlanRunner.OrderedRetryPlanRunner.ExecuteAsync(Int64 deploymentId, Plan plan, CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/planrunner/OrdererdRetryPlanRunner.cs:line 91
   --- End of inner exception stack trace ---
   at Microsoft.Azure.Devices.Edge.Agent.Core.PlanRunner.OrderedRetryPlanRunner.<>c.<ExecuteAsync>b__7_0(List`1 f) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/planrunner/OrdererdRetryPlanRunner.cs:line 129
   at Microsoft.Azure.Devices.Edge.Agent.Core.PlanRunner.OrderedRetryPlanRunner.ExecuteAsync(Int64 deploymentId, Plan plan, CancellationToken token)
   at Microsoft.Azure.Devices.Edge.Agent.Core.Agent.ReconcileAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/Agent.cs:line 208
   at Microsoft.Azure.Devices.Edge.Agent.Core.Agent.ReconcileAsync(CancellationToken token) in /mnt/vss/_work/1/s/edge-agent/src/Microsoft.Azure.Devices.Edge.Agent.Core/Agent.cs:line 208

Check out the source code here for the driver you need

We use, depending on what we are testing, either SysFsDriver.cs or LibGpiodDriver.cs.

(assuming you have the proper lib installed into the container)

We confirmed that both work inside the container with the usual command line utilities.

@joperezr
Copy link
Member

[Triage] - Sorry @m4schini I know we suggested above to try sysfs but I somehow missed the part you saying that you were running on iot edge. We believe that this is a limitation of azure iot edge where you can't really volume mount the paths you need in order for sysfs driver to work. All that said, in general we usually prefer libgpiod driver as that is usually much more accurate, so I think it's worth going back to that versio instead and figure out what's wrong there. Is it just eventing that is not working for you? What I mean by this is, if you write to a pin which is connected to another pin, do you see the values changing?

@m4schini
Copy link
Author

Yes we also expect the problem to be Eventing related because of the behavior we observed (inside the container):

  • We can write and read pin values in C# code with the libgpiod driver
  • We can write and read pin values with the gpiolib command line tools
  • We can also observe pin value changes via gpiomon:
gpiomon 2 12
event:  RISING EDGE offset: 12 timestamp: [      54.261767639]
event: FALLING EDGE offset: 12 timestamp: [      54.425665514]

@m4schini
Copy link
Author

I'm going on Vacation, my collegue (@ignaciocaramba) will be taking over this issue from our side.

@ignaciocaramba
Copy link
Contributor

ignaciocaramba commented Sep 25, 2023

As a side note:
If we comment out this section in the SysFsDriver "it works" (we get events on change):

private unsafe bool WasEventDetected(int pollFileDescriptor, int valueFileDescriptor, out int pinNumber, CancellationToken cancellationToken)
    {
        char buf;
        IntPtr bufPtr = new IntPtr(&buf);
        pinNumber = -1;

        while (!cancellationToken.IsCancellationRequested)
        {
            // Wait until something happens
            int waitResult = CustomInterop.CustomInterop.epoll_wait(pollFileDescriptor, out epoll_event events, 1, PollingTimeout);
            if (waitResult == -1)
            {
                var errorCode = Marshal.GetLastWin32Error();
                if (errorCode == ERROR_CODE_EINTR)
                {
                    // ignore Interrupted system call error and retry
                    continue;
                }

                throw new IOException($"Error while waiting for pin interrupts. (ErrorCode={errorCode})");
            }

            if (waitResult > 0)
            {
                pinNumber = events.data.pinNumber;

                /* WE COMMENTED OUT THIS SECTION
                // This entire section is probably not necessary, but this seems to be hard to validate.
                // See https://github.com/dotnet/iot/pull/914#discussion_r389924106 and issue #1024.
                if (valueFileDescriptor == -1)
                {
                    // valueFileDescriptor will be -1 when using the callback eventing. For WaitForEvent, the value will be set.
                    valueFileDescriptor = _devicePins[pinNumber].FileDescriptor;
                }

                int lseekResult = CustomInterop.CustomInterop.lseek(valueFileDescriptor, 0, SeekFlags.SEEK_SET);
                if (lseekResult == -1)
                {
                    throw new IOException("Error while trying to seek in value file.");
                }

                int readResult = CustomInterop.CustomInterop.read(valueFileDescriptor, bufPtr, 1);
                if (readResult != 1)
                {
                    throw new IOException("Error while trying to read value file.");
                }
                */

                return true;
            }
        }

        return false;
    }

@pgrawehr
Copy link
Contributor

That's interesting, in particular due to the comment there. Can you check what the value of valueFileDescriptor is before and after the first "if" in that section?

@ignaciocaramba
Copy link
Contributor

@pgrawehr
before: -1
after: 329

@krwq krwq added the Priority:1 Work that is critical for the release, but we could probably ship without label Sep 28, 2023
@ghost ghost removed the untriaged label Sep 28, 2023
@ignaciocaramba
Copy link
Contributor

@pgrawehr any updates?

@pgrawehr
Copy link
Contributor

pgrawehr commented Oct 9, 2023

@ignaciocaramba Sorry, I didn't have time to look into this. Since it appears to be working if you comment out that block of code (whose existing comment already says that it's questionable), we could verify whether removing that would cause the other boards to break. Can you submit a PR with that piece of code commented out? That would trigger the CI run and we would see whether it directly causes any issues. CI is running a bunch of GPIO tests on actual hardware.

@ignaciocaramba
Copy link
Contributor

@pgrawehr Pr created and All tests have passed

@ghost ghost added the Status: Fixed label Oct 19, 2023
@ignaciocaramba
Copy link
Contributor

The pr fixes the issue with the SysfsDriver, but our initial problem is not fixed: if instead of using Sysfs we use LibGpiod when registering a Callback via RegisterCallbackForPinValueChangedEvent and changing the value of the gpio pin, the callback is not being called.

Also, is there a problem to use SysfsDriver instead of LibgpiodDriver for listening to button events?

@pgrawehr
Copy link
Contributor

@ignaciocaramba You seem to have some special hardware there, and it was kind of a coincidence that it could be so easily fixed for Sysfs. When it doesn't work with libgpiod the problem is probably in the library or the hardware and there's likely nothing we can do on our side.

SysFs is generally much slower than libgpiod. If you use this only for buttons, the impact will be negligible. If you need to handle events in the Kilohertz range, then sysfs might not be fast enough and you will loose events.

@ghost ghost locked as resolved and limited conversation to collaborators Nov 19, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working Priority:1 Work that is critical for the release, but we could probably ship without Status: Fixed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants