dart_periphery 0.8.25-RC
dart_periphery: ^0.8.25-RC copied to clipboard

dart_periphery is a Dart port of the native c-periphery library for Linux Peripheral I/O (GPIO, LED, PWM, SPI, I2C, MMIO and Serial peripheral I/O).

dart_periphery #

alt text

pub package

Introduction #

dart_periphery is a Dart port of the native c-periphery library for Linux Peripheral I/O (GPIO, LED, PWM, SPI, I2C, MMIO and Serial peripheral I/O). This package is specially intended for SoCs like Raspberry Pi, NanoPi, Banana Pi et al.

What is c-periphery? #

Abstract from the project web site:

c-periphery is a small C library for

  • GPIO,
  • LED,
  • PWM,
  • SPI,
  • I2C,
  • MMIO (Memory Mapped I/O)
  • Serial peripheral I/O

interface access in userspace Linux. c-periphery simplifies and consolidates the native Linux APIs to these interfaces. c-periphery is useful in embedded Linux environments (including Raspberry Pi, BeagleBone, etc. platforms) for interfacing with external peripherals. c-periphery is re-entrant, has no dependencies outside the standard C library and Linux, compiles into a static library for easy integration with other projects, and is MIT licensed

dart_periphery binds the c-periphery library with the help of the dart:ffi mechanism. Nevertheless, dart_periphery tries to be close as possible to the original library. See following documentation. Thanks to Vanya Sergeev for his great job!

Why c-periphery? #

The number of GPIO libraries/interfaces is becoming increasingly smaller.

dart_periphery currently has beta status. All interfaces are ported:

Examples #

GPIO #

alt text

import 'package:dart_periphery/dart_periphery.dart';
import 'dart:io';

void main() {
  var config = GPIOconfig();
  config.direction = GPIOdirection.GPIO_DIR_OUT;
  print('Native c-periphery Version :  ${getCperipheryVersion()}');
  print('GPIO test');
  var gpio = GPIO(18, GPIOdirection.GPIO_DIR_OUT);
  var gpio2 = GPIO(16, GPIOdirection.GPIO_DIR_OUT);
  var gpio3 = GPIO.advanced(5, config);

  print('GPIO info: ' + gpio.getGPIOinfo());

  print('GPIO native file handle: ${gpio.getGPIOfd()}');
  print('GPIO chip name: ${gpio.getGPIOchipName()}');
  print('GPIO chip label: ${gpio.getGPIOchipLabel()}');
  print('GPIO chip name: ${gpio.getGPIOchipName()}');
  print('CPIO chip label: ${gpio.getGPIOchipLabel()}');

  for (var i = 0; i < 10; ++i) {
    gpio.write(true);
    gpio2.write(true);
    gpio3.write(true);
    sleep(Duration(milliseconds: 200));
    gpio.write(false);
    gpio2.write(false);
    gpio3.write(false);
    sleep(Duration(milliseconds: 200));
  }

  gpio.dispose();
  gpio2.dispose();
  gpio3.dispose();
}

I2C #

alt text

import 'package:dart_periphery/dart_periphery.dart';

/// https://wiki.seeedstudio.com/Grove-Barometer_Sensor-BME280/
/// Grove - Temp&Humi&Barometer Sensor (BME280) is a breakout board for Bosch BMP280 high-precision,
/// low-power combined humidity, pressure, and temperature sensor.
void main() {
  // Select the right I2C bus number /dev/i2c-?
  // 1 for Raspbery Pi, 0 for NanoPi (Armbian), 2 Banana Pi (Armbian)
  var i2c = I2C(1);
  try {
    print('I2C info:' + i2c.getI2Cinfo());
    var bme280 = BME280(i2c);
    var r = bme280.getValues();
    print('Temperature [°] ${r.temperature.toStringAsFixed(1)}');
    print('Humidity [%] ${r.humidity.toStringAsFixed(1)}');
    print('Pressure [hPa] ${r.pressure.toStringAsFixed(1)}');
  } finally {
    i2c.dispose();
  }
}


alt text

import 'package:dart_periphery/dart_periphery.dart';

/// Grove - Temp&Humi Sensor(SHT31) is a highly reliable, accurate, 
/// quick response and integrated temperature & humidity sensor.
void main() {
  // Select the right I2C bus number /dev/i2c-?
  // 1 for Raspbery Pi, 0 for NanoPi (Armbian), 2 Banana Pi (Armbian)
  var i2c = I2C(1);
  try {
    var sht31 = SHT31(i2c);
    print(sht31.getStatus());
    print('Serial number ${sht31.getSerialNumber()}');
    print('Sensor heater active: ${sht31.isHeaterOn()}');

    var r = sht31.getValues();
    print('SHT31 [t°] ${r.temperature.toStringAsFixed(2)}');
    print('SHT31 [%°] ${r.humidity.toStringAsFixed(2)}');
  } finally {
    i2c.dispose();
  }
}

SPI #

import 'package:dart_periphery/dart_periphery.dart';

void main() {
  var spi = SPI(0, 0, SPImode.MODE0, 1000000);
  try {
    print('SPI info:' + spi.getSPIinfo());
    var bme280 = BME280.spi(spi);
    var r = bme280.getValues();
    print('Temperature [°] ${r.temperature.toStringAsFixed(1)}');
    print('Humidity [%] ${r.humidity.toStringAsFixed(1)}');
    print('Pressure [hPa] ${r.pressure.toStringAsFixed(1)}');
  } finally {
    spi.dispose();
  }
}

Serial #

alt text

import 'package:dart_periphery/dart_periphery.dart';
import 'dart:io';

///
/// [COZIR CO2 Sensor](https://co2meters.com/Documentation/Manuals/Manual_GC_0024_0025_0026_Revised8.pdf)
///
void main() {
  print('Serial test - COZIR CO2 Sensor');
  var s = Serial('/dev/serial0', Baudrate.B9600);
  try {
    print('Serial interface info: ' + s.getSerialInfo());

    // Return firmware version and sensor serial number - two lines
    s.writeString('Y\r\n');
    var event = s.read(256, 1000);
    print(event.toString());

    // Request temperature, humidity and CO2 level.
    s.writeString('M 4164\r\n');
    // Select polling mode
    s.writeString('K 2\r\n');
    // print any response
    event = s.read(256, 1000);
    print('Response ${event.toString()}');
    sleep(Duration(seconds: 1));
    for (var i = 0; i < 5; ++i) {
      s.writeString('Q\r\n');
      event = s.read(256, 1000);
      print(event.toString());
      sleep(Duration(seconds: 5));
    }
  } finally {
    s.dispose();
  }
}

Led #

alt text

import 'package:dart_periphery/dart_periphery.dart';
import 'dart:io';

void main() {
  /// Nano Pi power led - see 'ls /sys/class/leds/'
  var led = Led('nanopi:red:pwr');
  try {
    print('Led handle: ${led.getLedInfo()}');
    print('Led name: ${led.getLedName()}');
    print('Led brightness: ${led.getBrightness()}');
    print('Led maximum brightness: ${led.getMaxBrightness()}');
    var inverse = !led.read();
    print('Original led status: ${(!inverse)}');
    print('Toggle led');
    led.write(inverse);
    sleep(Duration(seconds: 5));
    inverse = !inverse;
    print('Toggle led');
    led.write(inverse);
    sleep(Duration(seconds: 5));
    print('Toggle led');
    inverse = !inverse;
    led.write(inverse);
    sleep(Duration(seconds: 5));
    print('Toggle led');
    led.write(!inverse);
  } finally {
    led.dispose();
  }
}

PWM #

Ensure that PWM is correct enabled. e.g. see the following documentation for the Raspberry Pi.

import 'package:dart_periphery/dart_periphery.dart';
import 'dart:io';

void main() {
  var pwm = PWM(0, 0);
  try {
    print(pwm.getPWMinfo());
    pwm.setPeriodNs(10000000);
    pwm.setDutyCycleNs(8000000);
    print(pwm.getPeriodNs());
    pwm.enable();
    print("Wait 20 seconds");
    sleep(Duration(seconds: 20));
    pwm.disable();
  } finally {
    pwm.dispose();
  }
}

MMIO #

Memory Mapped I/O: Turns on a led at pin 18 on a Raspberry Pi using MMIO. This direct register access example is derived from elinux.org.

import 'package:dart_periphery/dart_periphery.dart';
import 'dart:io';

const int BCM2708_PERI_BASE = 0x3F000000; // Raspberry Pi 3
const int GPIO_BASE = BCM2708_PERI_BASE + 0x200000;
const int BLOCK_SIZE = 4 * 1024;

/// Helper class for the hardcore bit manipulation.
class MemMappedGPIO {
  MMIO mmio;
  MemMappedGPIO(this.mmio);

  // #define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
  void setPinInput(final int pin) {
    var offset = (pin ~/ 10) * 4;
    var value = mmio[offset];
    value &= (~(7 << (((pin) % 10) * 3)));
    mmio[offset] = value;
  }

  // #define OUT_GPIO(g) *(gpio+((g)/10)) |=  (1<<(((g)%10)*3))
  void setPinOutput(final int pin) {
    setPinInput(pin);
    var offset = (pin ~/ 10) * 4;
    var value = mmio[offset];
    value |= (1 << (((pin) % 10) * 3));
    mmio[offset] = value;
  }

  // #define GPIO_SET *(gpio+7) - sets bits which are 1 ignores bits which are 0
  void setPinHigh(int pin) {
    mmio[7 * 4] = 1 << pin;
  }

  // #define GPIO_CLR *(gpio+10) - clears bits which are 1 ignores bits which are 0
  void setPinLow(int pin) {
    mmio[10 * 4] = 1 << pin;
  }

  // #define GET_GPIO(g) (*(gpio+13)&(1<<g)) - 0 if LOW, (1<<g) if HIGH
  int getPin(int pin) {
    return mmio[13 * 4] & (1 << pin);
  }
}

void main() {
  // Needs root rights and the GPIO_BASE must be correct!
  // var mmio = MMIO(GPIO_BASE, BLOCK_SIZE);
  
  var mmio = MMIO.advanced(0, BLOCK_SIZE, '/dev/gpiomem');
  var gpio = MemMappedGPIO(mmio);
  try {
    print(mmio.getMMIOinfo());
    var pin = 18;
    print('Led (pin=18) on');
    gpio.setPinOutput(pin);
    gpio.setPinHigh(pin);
    sleep(Duration(seconds: 10));
    gpio.setPinLow(pin);
    print('Led (pin=18) off');
  } finally {
    mmio.dispose();
  }
}

Install Dart on Raspian and Armbian #

1.) Go to the home directory

cd ~

2.) Download the last stable Dart SDK form archiv for your CPU architecture/OS.

ARMv7 #

wget https://storage.googleapis.com/dart-archive/channels/stable/release/2.12.4/sdk/dartsdk-linux-arm-release.zip
unzip dartsdk-linux-arm-release.zip

ARMv8 #

wget https://storage.googleapis.com/dart-archive/channels/stable/release/2.12.4/sdk/dartsdk-linux-arm64-release.zip
unzip dartsdk-linux-arm64-release.zip

x86 #

https://storage.googleapis.com/dart-archive/channels/stable/release/2.12.4/sdk/dartsdk-linux-ia32-release.zip
unzip dartsdk-linux-ia32-release.zip

x86_64 #

https://storage.googleapis.com/dart-archive/channels/stable/release/2.12.4/sdk/dartsdk-linux-x64-release.zip
unzip dartsdk-linux-x64-release.zip

3.) Unpack and install SDK

sudo mv dart-sdk /opt/
sudo chmod -R +rx /opt/dart-sdk

4.) Add the Dart SDK to the path

nano ~/.profile

following command

export PATH=$PATH:/opt/dart-sdk/bin

at the end of the file and call

source ~/.profile

to apply the changes.

Test the installion

pi@raspberrypi:~ $ dart --version
Dart SDK version: 2.12.4 (stable) (Thu Apr 15 12:26:53 2021 +0200) on "linux_arm"

Native libraries #

Currently dart_periphery ships with four prebuild native c-periphery libraries for ARMv7/ARMv8/X86/X86_64

Following methods can be used to overwrite the loading of the prebuild library.

But be aware, any of these methods must be called before any dart_periphery interface is used!

useSharedLibray();

If this method is called, dart_periphery loads the shared library. For this case c-periphery must be installed as a shared library. See for section Shared Library for details.

To load a custom library call

setCustomLibrary(String absolutePath)

This method can be helpful in any case of a problem and for a currently not supported platform.

For a dart native binary, which can be deployed

dart compile exe i2c_example.dart

call

void useLocalLibrary()

The appropriate library should be in same dirctory as the exe.

flutter-pi #

dart_periphery works with flutter-pi, a light-weight Flutter Engine Embedder for Raspberry Pi. For futter-pi the appropriate library must be copied inside the flutter asset directory.

See last section, native libraries for details.

Tested SoC hardware #

Supported devices (sensors, actuators, expansion hats and displays) #

Next steps #

Test matrix #

Test suite

ArchitectureGPIOGPIOsysfsI2CSPISerialMMIO¹PWMLED
ARM ²
AARCH64 ³❌⁴
X86
X86_64

☐ missing test | ✅ test passed | ❌ test failed

¹ Delayed until FFI inline @Array() support in Dart Version >=2.13 is available

² Raspberry Pi 3 Model B

³ NanoPi Neo2 with a Allwinner H5, Quad-core 64-bit CPU

⁴ Fails for NanoPi, NanoPi Neo2 and Banana Pi on Armbian- same behaviour like the original c-peripherey test program. Point of deeper investigations

⁵ no X86/X86_64 SOC for testing available

Help wanted #

  • Testing dart_periphery on different SoC platforms
  • Documentation review - I am not a native speaker.
  • Code review - this is my first public Dart project, I am a Java developer and probably I tend to solve problems rather in the Java than in the Dart way.
8
likes
120
pub points
41%
popularity

Publisher

Unknown

dart_periphery is a Dart port of the native c-periphery library for Linux Peripheral I/O (GPIO, LED, PWM, SPI, I2C, MMIO and Serial peripheral I/O).

Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD (LICENSE)

Dependencies

collection, ffi, path, pedantic, process, stack_trace, system_info

More

Packages that depend on dart_periphery