Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions packages/flutter_gpiod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@
- migrated to null-safety
- `SignalEvent`'s `time` property is no-longer that accurate, but instead two new properties, `timestampNanos` and `timestamp` are now provided which are super accurate. (This is because of changes in the kernel)
- Raspberry Pi's main GPIO chip is no longer called `pinctrl-bcm2835` on Pi 4's with latest kernel version. Instead its called `pinctrl-bcm2711`.
- Raspberry Pi's kernel changed so that its GPIO indexes are no longer a perfect sequence.
Two different device files may now refer to the same GPIO chip.
Therefore, a map represents this relationship instead of a list going forward.

# flutter_gpiod

A dart package for GPIO access on linux / Android (*root required*) using the linux GPIO character-device interface. Tested & working on ARM32 but should work on other 32-bit and 64-bit linux platforms as well.

## Getting Started

Then, you can retrieve the list of GPIO chips attached to
Then, you can retrieve the map of GPIO chips attached to
your system using [FlutterGpiod.chips]. Each chip has a name,
label and a number of GPIO lines associated with it.
label, and a number of GPIO lines associated with it.
```dart
final chips = FlutterGpiod.instance.chips;

for (final chip in chips) {
print("chip name: ${chip.name}, chip label: ${chip.label}");
for (final chip in chips.values) {
print("chip name: ${chip.name}, chip label: ${chip.label}");

for (final line in chip.lines) {
print(" line: $line");
}
for (final line in chip.lines) {
print(" line: $line");
}
}
```

Expand All @@ -31,9 +34,10 @@ The information can change at any time if the line is not owned/requested by you
// Get the main Raspberry Pi GPIO chip.
// On Raspberry Pi 4 the main GPIO chip is called `pinctrl-bcm2711` and
// on older Pi's or a Pi 4 with older kernel version it's called `pinctrl-bcm2835`.
final chip = FlutterGpiod.instance.chips.singleWhere(
// On newer kernel version the chips may appear twice, so using `singleWhere` would fail.
final chip = FlutterGpiod.instance.chips.values.firstWhere(
(chip) => chip.label == 'pinctrl-bcm2711',
orElse: () => FlutterGpiod.instance.chips.singleWhere((chip) => chip.label == 'pinctrl-bcm2835'),
orElse: () => FlutterGpiod.instance.chips.values.firstWhere((chip) => chip.label == 'pinctrl-bcm2835'),
);

// Get line 22 of the GPIO chip.
Expand All @@ -46,7 +50,7 @@ print("line info: ${line.info}")
To control a line (to read or write values or to listen for edges),
you need to request it using [GpioLine.requestInput] or [GpioLine.requestOutput].
```dart
final chip = FlutterGpiod.instance.chips.singleWhere((chip) => chip.label == 'pinctrl-bcm2835');
final chip = FlutterGpiod.instance.chips.values.firstWhere((chip) => chip.label == 'pinctrl-bcm2835');
final line = chip.lines[22];

// request it as input.
Expand Down
10 changes: 6 additions & 4 deletions packages/flutter_gpiod/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import 'package:async/async.dart';
import 'package:flutter_gpiod/flutter_gpiod.dart';

void main() async {
/// Retrieve the list of GPIO chips.
/// Retrieve the map of GPIO chips.
final chips = FlutterGpiod.instance.chips;

/// Print out all GPIO chips and all lines
/// for all GPIO chips.
for (var chip in chips) {
for (var chip in chips.values) {
print("$chip");

for (var line in chip.lines) {
Expand All @@ -29,9 +29,11 @@ void main() async {
/// The main GPIO chip is called `pinctrl-bcm2711` on Pi 4 and `pinctrl-bcm2835`
/// on older Raspberry Pis and it was also called that way on Pi 4 with older
/// kernel versions.
final chip = chips.singleWhere(
/// On newer kernel version the chips may appear twice, so using `singleWhere`
/// would fail.
final chip = chips.values.firstWhere(
(chip) => chip.label == 'pinctrl-bcm2711',
orElse: () => chips.singleWhere((chip) => chip.label == 'pinctrl-bcm2835'),
orElse: () => chips.values.firstWhere((chip) => chip.label == 'pinctrl-bcm2835'),
);

final line1 = chip.lines[23];
Expand Down
53 changes: 33 additions & 20 deletions packages/flutter_gpiod/lib/src/gpiod.dart
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ Future<void> _eventIsolateEntry2(List args) async {
class PlatformInterface {
PlatformInterface._construct(
this.libc,
this._numChips,
this._chipIndexToFd,
this._epollFd,
this._eventReceivePort,
Expand All @@ -247,26 +246,34 @@ class PlatformInterface {
libc = LibC(ffi.DynamicLibrary.open("libc.so.6"));
}

final numChips = Directory('/dev')
final deviceType = 'gpiochip';

final chips = Directory('/dev')
.listSync(followLinks: false, recursive: false)
.where((element) => basename(element.path).startsWith('gpiochip'))
.length;
.where((element) => basename(element.path).startsWith(deviceType));

final chipIndexToFd = <int, int>{};

for (var i = 0; i < numChips; i++) {
final pathPtr = '/dev/gpiochip$i'.toNativeUtf8();
for (final chip in chips) {
final path = chip.absolute.path;
final pathPtr = path.toNativeUtf8();

final chipIndex = int.parse(
basename(path).substring(deviceType.length),
radix: 10,
);

final fd = libc.open(pathPtr.cast<ffi.Char>(), O_RDWR | O_CLOEXEC);

ffi.malloc.free(pathPtr);

if (fd < 0) {
chipIndexToFd.values.forEach((fd) => libc.close(fd));
throw FileSystemException('Could not open GPIO chip $i', '/dev/gpiochip$i');

throw FileSystemException('Could not open GPIO chip $chipIndex', path);
}

chipIndexToFd[i] = fd;
chipIndexToFd[chipIndex] = fd;
}

final epollFd = libc.epoll_create1(0);
Expand All @@ -291,11 +298,10 @@ class PlatformInterface {
throw RemoteError(message[0], message[1]);
});

return PlatformInterface._construct(libc, numChips, chipIndexToFd, epollFd, receivePort);
return PlatformInterface._construct(libc, chipIndexToFd, epollFd, receivePort);
}

final LibC libc;
final int _numChips;
final Map<int, int> _chipIndexToFd;
final int _epollFd;
final ReceivePort _eventReceivePort;
Expand All @@ -322,6 +328,7 @@ class PlatformInterface {
}

int _chipFdFromChipIndex(int chipIndex) {
assert(_chipIndexToFd.containsKey(chipIndex));
return _chipIndexToFd[chipIndex]!;
}

Expand All @@ -330,7 +337,10 @@ class PlatformInterface {
}

int _chipIndexFromLineHandle(int lineHandle) {
return lineHandle >> 32;
final chipIndex = lineHandle >> 32;
assert(_chipIndexToFd.containsKey(chipIndex));

return chipIndex;
}

int _chipFdFromLineHandle(int lineHandle) {
Expand Down Expand Up @@ -375,8 +385,8 @@ class PlatformInterface {
return _eventStream!;
}

int getNumChips() {
return _numChips;
Iterable<int> getChipIndexes() {
return _chipIndexToFd.keys;
}

Map<String, dynamic> getChipDetails(int chipIndex) {
Expand Down Expand Up @@ -720,8 +730,8 @@ class FlutterGpiod {

static FlutterGpiod? _instance;

/// The list of GPIO chips attached to this system.
final List<GpioChip> chips;
/// The map of GPIO chips and their indexes attached to this system.
final Map<int, GpioChip> chips;

/// Whether setting and getting GPIO line bias is supported.
///
Expand All @@ -741,13 +751,14 @@ class FlutterGpiod {
/// If none exists, one will be constructed.
static FlutterGpiod get instance {
if (_instance == null) {
final chips =
List.generate(PlatformInterface.instance.getNumChips(), (i) => GpioChip._fromIndex(i), growable: false);
final chips = {
for (final index in PlatformInterface.instance.getChipIndexes()) index: GpioChip._fromIndex(index)
};

final bias = PlatformInterface.instance.supportsBias();
final reconfig = PlatformInterface.instance.supportsLineReconfiguration();

_instance = FlutterGpiod._internal(List.unmodifiable(chips), bias, reconfig);
_instance = FlutterGpiod._internal(Map.unmodifiable(chips), bias, reconfig);
}

return _instance!;
Expand All @@ -767,8 +778,10 @@ class FlutterGpiod {
/// some number of GPIO lines / pins.
@immutable
class GpioChip {
/// The index of the GPIO chip in the [FlutterGpiod.chips] list,
/// and at the same time, the numerical suffix of [name].
/// The key of the GPIO chip in the [FlutterGpiod.chips] map,
/// and most of the time, the numerical suffix of [name].
/// A noteable exception to this rule is the Raspberry Pi's main GPIO chip
/// when using newer kernel versions.
final int index;

/// The name of this GPIO chip.
Expand Down
Loading