danrubel / rpi_gpio.dart

Dart library for accessing the Raspberry Pi GPIO pins
Other
47 stars 5 forks source link

Add pulse width modulation support #18

Closed danrubel closed 3 years ago

danrubel commented 3 years ago

Per conversation in API Overhaul issue, I'm adding PWM (pulse width modulation) support. It appears that there are 2 PWM channels that can be assigned to a limited number of pins, but that the Raspberry Pi only exposes 1 of those PWM channels on physical pin 12 which is GPIO 18.

Proposed API...

abstract class Gpio {
  /// Return a GPIO pin configured for pulse width modulation,
  /// where [physicalPin] is the physical pin number not the GPIO number.
  GpioPwm pwm(int physicalPin);

  ... etc ...
}

/// A GPIO pin used for PWM (pulse width modulation).
abstract class GpioPwm {
  /// Specifies the divider used to generate the PWM frequency from the PWM clock.
  /// This value must be a power of two divisor between 2 and 4096.
  /// The Raspberry Pi PWM clock has a base frequency of 19.2 MHz,
  /// thus a clockDivider of 128 would result in a PWM frequency of 150kHz.
  set clockDivider(int divider);

  /// Specifies the number of units in a PWM cycle,
  /// which is the maximum value for [value].
  set maxValue(int maxValue);

  /// Specifies the current amount of time
  /// that the PWM output is HIGH relative to [maxValue].
  /// Thus a `value = 128` and `maxValue = 512`
  /// would result in a PWM output that is HIGH 25% of the time.
  set value(int value);
}
danrubel commented 3 years ago

@J4ckTh3R1qp3r Over the weekend I dusted off an older RPi 2B and loaded the latest RPi OS for testing. Above is the proposed API. I'll start implementing next weekend.

J4ckTh3R1qp3r commented 3 years ago

@danrubel Thats great! If you will need smth to test on Zero W just let me know.

danrubel commented 3 years ago

@J4ckTh3R1qp3r This past weekend I hooked up an LED to pin 12 (GPIO 18) for testing and I'm able to blink the LED using standard GPIO output but I have not been able to get PWM working. More digging required next weekend...

J4ckTh3R1qp3r commented 3 years ago

@danrubel Thanks. And I am able to run Dart on RPI Zero, but only version 2.0. The support of armv6 ended somewhere at <2.9, so even if I could compile dart engine (sorry if it is unappropriate term) and upgrade my current 2.0 to 2.9 I will be still not able to use your package as it is 2.10>..<3.0. That was close 😜 The 4b is still on its way so I am not able to test anything for now. I will let you know once I am ready.

danrubel commented 3 years ago

Spent the day banging on this code. My current codebase is derived/extracted from wiringPi (thank you Gordon!) but I cannot seem to get PWM running. I decided to pour over PiGpio code for a while to see what I can learn there. Ultimately I'm looking for a short C code sample to help me understand what I'm doing wrong. I found this post with a small C code sample and it does indeed work, but it needs sudo access to run so does not fit with my goal of sudo-less support.

danrubel commented 3 years ago

@J4ckTh3R1qp3r This is going to take me longer than I thought. There's some subtleties in the underlying C++ code that I'm not understanding to make it work. For now I recommend that you build your software using software based PWM rather than hardware based.

J4ckTh3R1qp3r commented 3 years ago

@danrubel Thanks for the update. Sadly Dart is too slow on the RPI to make PWM through a loop, so I will use Python for that purpose.

guyluz11 commented 3 years ago

@J4ckTh3R1qp3r how did you run dart?.

Dart can run on vm and as native code that need to be compiled.

You can learn more about the native code way to run it from here: https://dart.dev/tools/dart-compile I used to use dart2native and dart2aot and it was much much faster. Good to know that there is a new one now named dart compile that replace them .

J4ckTh3R1qp3r commented 3 years ago

@guyluz11 I run dart as VM and tried compiled file for RPI OS. Both options can't execute commands faster than 200-500ms. I mean, some code like this:

for (var i = 0; i<100; i++){
   print(i);
}

This will spam 'i' to console not very fast. And I tried to use this loop to switch on/off GPIO every 10ms and it didn't work out. May be RPI Zero is just too slow, or may be I am doing it wrong.

guyluz11 commented 3 years ago

Tow things

  1. As far as I know print takes time to show and you should not determine the speed of a program with it.
  2. Run in vm when testing and native/compiled when you want to run it in production/ as smooth and as fast as it can run.
    Todo so we need to first compile it and make executable of you program with the command dart compile pathToMainFile/main.dart -o the executableNameYouWantToMake. And than run it as the same way you run executable file ./executableNameYouWantToMake. I didn't test this command but I guess that compile should be the same as the one I know dart2native.

Update comment The command is different a little bit from what I used, you need to choose what file type to compile your program into image

J4ckTh3R1qp3r commented 3 years ago

@guyluz11 I tried to simulate PWM like this:

for (int i =0; i < 1000; i++){
    led.value = false;
    await Future.delayed(Duration(milliseconds: i));
    led.value = true;
  }

And it didn't work. Do you have a better idea how to check the led brightness control? Maybe the problem is in the "Duration" and it can be changed for something faster?

danrubel commented 3 years ago

@J4ckTh3R1qp3r Perhaps try something like this...

import 'dart:async';

import 'package:rpi_gpio/gpio.dart';
import 'package:rpi_gpio/rpi_gpio.dart';

main() async {
  final gpio = RpiGpio();
  final led = gpio.output(12);

  const duration = Duration(seconds: 4);

  print('LED on ...');
  led.value = true;
  await Future.delayed(duration);

  print('LED 50% ...');
  await pwm(led, duration, 50);

  print('LED on ...');
  led.value = true;
  await Future.delayed(duration);

  print('LED 25% ...');
  await pwm(led, duration, 25);

  print('LED on ...');
  led.value = true;
  await Future.delayed(duration);

  print('LED 10% ...');
  await pwm(led, duration, 10);

  print('LED on ...');
  led.value = true;
  await Future.delayed(duration);

  led.value = false;
  print('Done');
}

/// Turn on LED dutyCycle% of the time for the specified duration
Future pwm(GpioOutput led, Duration duration, int dutyCycle) async {
  var end = DateTime.now().microsecondsSinceEpoch + duration.inMicroseconds;
  while (DateTime.now().microsecondsSinceEpoch < end) {
    led.value = true;
    await Future.delayed(Duration(microseconds: dutyCycle));
    led.value = false;
    await Future.delayed(Duration(microseconds: 100 - dutyCycle));
  }
}
danrubel commented 3 years ago

@J4ckTh3R1qp3r I just pushed a new branch https://github.com/danrubel/rpi_gpio.dart/tree/pub_dev that has new software based PWM via GpioPwm. See the revised example. If this branch provides what you need, I can publish it as work toward version 0.5.1.

@danrubel Thanks. And I am able to run Dart on RPI Zero, but only version 2.0. The support of armv6 ended somewhere at <2.9, so even if I could compile dart engine (sorry if it is unappropriate term) and upgrade my current 2.0 to 2.9 I will be still not able to use your package as it is 2.10>..<3.0. That was close 😜 The 4b is still on its way so I am not able to test anything for now. I will let you know once I am ready.

If you can identify the newest Dart SDK that still runs on the RPI Zero, maybe I can loosen the required SDK to match.

danrubel commented 3 years ago

It took me longer than I thought, but I finished adding PWM support and published 0.6.0. See https://pub.dev/packages/rpi_gpio