Code Monkey home page Code Monkey logo

rppal's Introduction

RPPAL - Raspberry Pi Peripheral Access Library

Build status crates.io MIT licensed Minimum rustc version

RPPAL provides access to the Raspberry Pi's GPIO, I2C, PWM, SPI and UART peripherals through a user-friendly interface. In addition to peripheral access, RPPAL also offers support for USB to serial adapters.

The library can be used in conjunction with a variety of platform-agnostic drivers through its embedded-hal trait implementations. Both embedded-hal v0.2.7 and v1 are supported.

RPPAL requires Raspberry Pi OS or any similar, recent, Linux distribution. Both GNU and musl libc targets are supported. RPPAL is compatible with the Raspberry Pi A, A+, B, B+, 2B, 3A+, 3B, 3B+, 4B, 5, CM, CM 3, CM 3+, CM 4, 400, Zero, Zero W and Zero 2 W. Backwards compatibility for minor revisions isn't guaranteed until v1.

This library is under development on the master branch of the repository on GitHub. If you're looking for the README.md or the examples directory for the latest release or any of the earlier releases, visit crates.io, download an archived release from the GitHub releases page, or clone and checkout the relevant release tag.

Table of contents

Documentation

Online documentation is available for the latest release, older releases, and the version currently in development.

Usage

Add a dependency for rppal to your Cargo.toml using cargo add rppal, or by adding the following line to your dependencies section.

[dependencies]
rppal = "0.17.1"

If your project requires embedded-hal trait implementations, specify either the hal or hal-unproven feature flag in the dependency declaration.

[dependencies]
rppal = { version = "0.17.1", features = ["hal"] }

Call new() on any of the peripherals to construct a new instance.

use rppal::gpio::Gpio;
use rppal::i2c::I2c;
use rppal::pwm::{Channel, Pwm};
use rppal::spi::{Bus, Mode, SlaveSelect, Spi};
use rppal::uart::{Parity, Uart};

let gpio = Gpio::new()?;
let i2c = I2c::new()?;
let pwm = Pwm::new(Channel::Pwm0)?;
let spi = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 16_000_000, Mode::Mode0)?;
let uart = Uart::new(115_200, Parity::None, 8, 1)?;

Access to some peripherals may need to be enabled first through sudo raspi-config or by editing /boot/config.txt. Refer to the relevant module's documentation for any required steps.

Examples

This example demonstrates how to blink an LED connected to a GPIO pin. Remember to add a resistor of an appropriate value in series, to prevent exceeding the maximum current rating of the GPIO pin and the LED.

use std::error::Error;
use std::thread;
use std::time::Duration;

use rppal::gpio::Gpio;
use rppal::system::DeviceInfo;

// Gpio uses BCM pin numbering. BCM GPIO 23 is tied to physical pin 16.
const GPIO_LED: u8 = 23;

fn main() -> Result<(), Box<dyn Error>> {
    println!("Blinking an LED on a {}.", DeviceInfo::new()?.model());

    let mut pin = Gpio::new()?.get(GPIO_LED)?.into_output();

    // Blink the LED by setting the pin's logic level high for 500 ms.
    pin.set_high();
    thread::sleep(Duration::from_millis(500));
    pin.set_low();

    Ok(())
}

Additional examples can be found in the examples directory.

Optional features

By default, all optional features are disabled. You can enable a feature by specifying the relevant feature flag(s) in the dependency declaration for rppal in your Cargo.toml.

  • hal - Enables embedded-hal trait implementations for all supported peripherals. This doesn't include unproven traits.
  • hal-unproven - Enables embedded-hal trait implementations for all supported peripherals, including traits marked as unproven. Note that embedded-hal's unproven traits don't follow semver rules. Patch releases may introduce breaking changes.

Supported peripherals

To ensure fast performance, RPPAL controls the GPIO peripheral by directly accessing the registers through either /dev/gpiomem or /dev/mem. GPIO interrupts are configured using the gpiochip character device.

Features

  • Get/set pin mode and logic level
  • Configure built-in pull-up/pull-down resistors
  • Synchronous and asynchronous interrupt handlers
  • Software-based PWM implementation
  • Optional embedded-hal trait implementations

The Broadcom Serial Controller (BSC) peripheral controls a proprietary bus compliant with the I2C bus/interface. RPPAL communicates with the BSC using the i2cdev character device.

Features

  • Single master, 7-bit slave addresses, transfer rates up to 400 kbit/s (Fast-mode)
  • I2C basic read/write, block read/write, combined write+read
  • SMBus protocols: Quick Command, Send/Receive Byte, Read/Write Byte/Word, Process Call, Block Write, PEC
  • Optional embedded-hal trait implementations

RPPAL controls the Raspberry Pi's PWM peripheral through the pwm sysfs interface.

Features

  • Up to two hardware PWM channels
  • Configurable frequency, duty cycle and polarity
  • Optional embedded-hal trait implementations

RPPAL controls the Raspberry Pi's main and auxiliary SPI peripherals through the spidev character device.

Features

  • SPI master, mode 0-3, Slave Select active-low/active-high, 8 bits per word, configurable clock speed
  • Half-duplex reads, writes, and multi-segment transfers
  • Full-duplex transfers and multi-segment transfers
  • Customizable options for each segment in a multi-segment transfer (clock speed, delay, SS change)
  • Reverse bit order helper function
  • Optional embedded-hal trait implementations

RPPAL controls the Raspberry Pi's UART peripherals through the ttyAMA0 (PL011) and ttyS0 (mini UART) character devices. USB to serial adapters are controlled using the ttyUSBx and ttyACMx character devices.

Features

  • Support for UART peripherals (PL011, mini UART) and USB to serial adapters
  • None/Even/Odd/Mark/Space parity, 5-8 data bits, 1-2 stop bits
  • Transfer rates up to 4 Mbit/s (device-dependent)
  • XON/XOFF software flow control
  • RTS/CTS hardware flow control with automatic pin configuration
  • Optional embedded-hal trait implementations

Cross compilation

If you're not working directly on a Raspberry Pi, you'll have to cross-compile your code for the appropriate ARM architecture. Check out this guide for more information, or try the cross project for "zero setup" cross compilation.

Cargo

For manual cross-compilation without the use of cross, you will need to install the appropriate target. Most Raspberry Pi models either need the armv7-unknown-linux-gnueabihf target for 32-bit Linux distributions, or aarch64-unknown-linux-gnu for 64-bit. For some models, like the Raspberry Pi Zero, a different target triple is required.

Install the relevant target using rustup.

rustup target install armv7-unknown-linux-gnueabihf

In the root directory of your project, create a .cargo subdirectory, and save the following snippet to .cargo/config.

[build]
target = "armv7-unknown-linux-gnueabihf"

Visual Studio Code

The rust-analyzer extension for Visual Studio Code needs to be made aware of the target platform by setting the rust-analyzer.cargo.target configuration option. In the root directory of your project, create a .vscode subdirectory, and then save the following snippet to .vscode/settings.json.

{
    "rust-analyzer.cargo.target": "armv7-unknown-linux-gnueabihf"
}

Caution

Always be careful when working with the Raspberry Pi's peripherals, especially if you attach any external components to the GPIO pins. Improper use can lead to permanent damage.

Copyright and license

Copyright (c) 2017-2024 Rene van der Meer. Released under the MIT license.

rppal's People

Contributors

aehmlo avatar andber1 avatar benkard avatar binarybana avatar cbjamo avatar denisandroid avatar dragonrun1 avatar foxzool avatar gferon avatar golemparts avatar jacobrosenthal avatar lazybun avatar lukeburong avatar majkl578 avatar makerio90 avatar mbuesch avatar mcbridejc avatar reitermarkus avatar rumatoest avatar teykey1 avatar thecatster avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rppal's Issues

Can't create Gpio on Raspberry Pi 4 8GB with 2020-08-20-raspios-buster-arm64

Working with this OS I can't create Gpio::new()?;, getting this error

Io(Os { code: 2, kind: NotFound, message: "No such file or directory" })

I've tracked the file to src/gpio/ioctl.rs:38

On source code there is

const PATH_GPIOCHIP: &str = "/dev/gpiochip";

but on my system there are these

$ ls /dev/gpiochip*
/dev/gpiochip0  /dev/gpiochip1

EDIT: My bad, the Id is added later. Still the problem is on function find_gpiochip. While iterating it passes id 0 and id 1 and panics with id 2.

I'm using a raspberry pi 4 8GB

$ uname -a
Linux pocketvac 5.10.5-v8+ #1392 SMP PREEMPT Sat Jan 9 18:56:30 GMT 2021 aarch64 GNU/Linux

EDIT 2: Apparently the function is skipping gpiochip0 because the driver is pinctrl-bcm2711 instead of the expected pinctrl-bcm2835

Guidance on singleton use

Hey there,
I just stumbled upon your project and was wondering whether you could provide some guidance wrt implementing GPIO as a singleton. I've already tried to use lazy_static and instantiating it as a static ref, both approaches do not work.
Best,
D

Uart set custom speed of 100kbs or 100000

Using the S Bus protocol it required to use speed of 10000bps. Quick look in lib code make sure that all speeds are settings by macro. It will great to opportunity to add custom speeds in lib. Is it possible?

 Uart::with_path("/dev/ttyAMA0", 100000, Parity::Even, 8, 2).expect("COULD SET UART");

Gives me error

thread 'main' panicked at 'COULD SET UART: InvalidValue', src/main.rs:8:69
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

All code

use rppal::uart::{Parity, Uart};
use std::time::Duration;


fn main() {
    let mut uart =
        Uart::with_path("/dev/ttyAMA0", 1000000, Parity::Even, 8, 2).expect("COULD SET UART");
    uart.set_read_mode(1, Duration::default())
        .expect("COULD SET UART MODE");
}
 

[Question] UART baud rates

According to the documentation, only a limited set of baud rates are supported. Why is this so? I've never seen any other application/interface restrict the number in one way or another. And why is zero a valid baud rate?

HAL Delay implementation should not use thread::sleep

Hi.

I've found out that you are using thread::sleep for DelayUs impl.
It would not work in cases where it is mostly nedded. Because such approach would not provide exact delays. I've tried to use thread::sleep several times in my serial like bus implementation and it failed.

I suggest you to use https://crates.io/crates/spin_sleep inside delay implementations, thus it will be possible to use this API to interact with hardware.

Replace GPIO sysfs interface for interrupts with GPIO character device

The GPIO sysfs interface (/sys/class/gpio) has been deprecated, with the relatively new GPIO character device (/dev/gpiochipN) interface offered as a replacement. Since GPIO sysfs is currently used for interrupts, a migration to the GPIO cdev interface needs to happen before GPIO sysfs is removed (after 2020).

As an added benefit, this is also a workaround for a bug in Fedora where GPIO sysfs doesn't properly handle interrupts.

Support for more slave select (i.e. chip select/CS) channels on SPI

I'd like to extend the SPI driver to support more slave select options. The default device tree only defines 2 chip select pins, but this can be extended with a device overlay to use any number of GPIOs as chip selects.

I will create a PR, but I'd like to get the maintainer(s) feedback on preferred approach.

Approach 1: Use an integer for slave_select argument. Since the only place slave select is used is in generating the /dev/spidevX.Y string, this seems reasonable to me, but it breaks backwards compatibility.

Approach 2: Extend to SlaveSelect enum to a sufficiently large number (perhaps up to Ss16) to cover all reasonable cases.

Approach 3: We could consider using typenum instead of SlaveSelect for the parameter, but since it's only used at run-time, I'm not sure I see any advantage to this over option 1.

Any advice?

Internal pull resistors do not always work on Raspberry Pi 4B

It appears that attempting to activate internal pull resistors on certain pins does not work on the Raspberry Pi 4B. Specifically, I have noticed that internal pull-up resistors fail to activate when using Pin::into_input_pullup() on BCM pins 17, 27, and 22. The pins remain floating, resulting in undefined behavior on the inputs.

I tested the pins in Python using gpiozero and confirmed that they behave correctly there when using internal pull-up resistors, so the problem seems to be isolated to rppal.

I did some digging and found this C program which caught my attention. On line 117 you'll find the function used to set the internal pull resistors. It appears to have a branch with separate logic for the BCM 2711 SoC (used in the Pi 4B) that appears to modify the GPIO memory in a completely different way.

Perhaps this is an incompatibility problem?

Detect SPI buffer size

Would it be possible to detect the SPI buffer size based on /sys/module/spidev/parameters/bufsiz and automatically chunk SPI transfers?

I am writing a higher-level interface that is meant to work on multiple platforms. However, Raspberry Pi currently has to be special-cased to avoid EMSGSIZE when larger SPI transfers are needed. This will end up affecting all upstream users that depend on embedded_hal.

I accomplished this with minimal overhead by using chunks and chunks_mut on the read and write buffers. If there's interest, I can put together a PR.

Read Output pin level

Hey there. Is there a way to read the current level of an OutputPin?
My use case if being able to tell if pin is active or not. For now, I am saving state everytime I change the pin level.

use of deprecated `std::mem::uninitialized`

std::mem::uninitialized() used in init_array!() is deprecated as of Rust 1.38.0 and should be replaced by MaybeUninit<T>.

The deprecation is explained as follows:

The reason for deprecation is that the function basically cannot be used correctly: the Rust compiler assumes that values are properly initialized. As a consequence, calling e.g. mem::uninitialized::() causes immediate undefined behavior for returning a bool that is not definitely either true or false. Worse, truly uninitialized memory like what gets returned here is special in that the compiler knows that it does not have a fixed value. This makes it undefined behavior to have uninitialized data in a variable even if that variable has an integer type. (Notice that the rules around uninitialized integers are not finalized yet, but until they are, it is advisable to avoid them.)

Example with reading voltage via SPI

Could we add an example of how to read a voltage, for example an analog sensor input connected to a SPI ADC? I'm trying to do this, but am getting [0, 0, 0, 0, 0] using the SPI example. (Confirmed not a hardware or setup problem)

PWM help

image

Just need a little help, I'm trying to have a 1kHz frequency, 10% duty cycle, and I've got cross compilation all set up, and since I'm brand new to Rust, could you point me in the right direction on how I enable the pwm "Result." I'm still trying to figure out Results, Options, and all that... not sure what to do next. Thanks, I'm going to get a book, but in the meantime it would be amazing for some quick help. :-)

SetInterrupt for input with pulldown resistor

Hey, I am trying to set a synchronous interrupt(trigger is Rising Edge) on a GPIO which has been configured as input with pulldown resistor. Assuming I am connecting a push button, with one side connected to GPIO input and the other to 3.3V. The expected behaviour is that the interrupt fires when button is pressed. However, this only happens if I connect other side of button to ground instead of 3.3v.
For context, I am trying to port this:
https://gpiozero.readthedocs.io/en/stable/api_input.html#button
How can I resolve this please?
Thanks

Research level-triggered interrupts

Check if the new /dev/gpiochipN interface with epoll allows level-triggered interrupts as well as the current edge-triggered interrupts. Initial research seems to indicate epoll uses edge triggers regardless of whether or not EPOLLET is set. If the drivers don't support it, see if it makes sense to add level-triggered interrupts in software.

when ::new() is called error "No such file or directory" sets

Hi I'm seeing an issue where any time I call new() (in regards to registering a GPIO pin or I2C) I get the error "No such file or directory" Im using the latest version of rrpal (0.11.3) and a Raspberry pi 2B. Is this a known issue or am I missing files? I dont think its the permissions issue since I'm not getting that error.

Thanks
Fred

UART multithreading example

I'd like to read data from a UART line in one thread and write other data to it in another thread. I don't know Rust well enough to let the borrow checker use me that uart variable in both threads. Can you please add an example doing this?

UART write not working

Has anyone successfully gotten UART write to work? I had no issue getting read to work but I cant seem to get the Raspberry pi to write to the serial bus.

Raspberry PI as i2c follower?

Hi,
Using Rppal, is it possible to make the Raspberry Pi (3B or Zero) act as a i2c follower too ?

I know that kind of behavior is possible with the Pigipio library and its Bsci2c function (or bscxfer) but now that I've switched to Rust I'm wondering if something similar is possible with Rppal too.

Thank you.

Error when rppal 0.11.0 is compiled for 64-bit linux

Hey @golemparts! I am using rppal 0.11 in https://github.com/rahul-thakoor/rust_gpiozero which works fine but when I am generating the docs using cargo doc --no-deps I get the following errors:

 Checking rppal v0.11.0
error[E0308]: mismatched types
   --> /home/vagrant/.cargo/registry/src/github.com-1ecc6299db9ec823/rppal-0.11.0/src/gpio/soft_pwm.rs:232:17
    |
232 |         tv_sec: (ns / 1_000_000_000) as i32,
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i64, found i32
help: you can cast an `i32` to `i64`, which will sign-extend the source value
    |
232 |         tv_sec: ((ns / 1_000_000_000) as i32).into(),
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
   --> /home/vagrant/.cargo/registry/src/github.com-1ecc6299db9ec823/rppal-0.11.0/src/gpio/soft_pwm.rs:233:18
    |
233 |         tv_nsec: (ns % 1_000_000_000) as i32,
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i64, found i32
help: you can cast an `i32` to `i64`, which will sign-extend the source value
    |
233 |         tv_nsec: ((ns % 1_000_000_000) as i32).into(),
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Any suggestions? Thanks

embedded hal gpio InputPin OutputPin traits

Was writing a bit banged proprietary serial implementation and found your lib was the only one that could perform. I was hoping to embedded hal traits which are implemented but to my eyes not for InputPin and OutputPin. Any reason not to?

Thanks!

Asynchronous interrupt trigger usage clarification

I'm trying to use this library to receive signals with generic 433mhz receiver (sender is a door sensor operating on the same freq). it seems like set_async_interrupt runs a lambda on another thread and will reset interrupt handler back to none, so that next interrupt doesnt trigger any lambda.

However radio receiver will trigger many interrupts, so I was wondering if you could provide a useful example of how to handle these signals

Add poll_interrupts() method to wait for multiple synchronous interrupts

Add Gpio::poll_interrupts() to wait for multiple synchronous interrupts.

  • Move Interrupt::poll() event loop to a new struct (interrupt::EventLoop).
  • Allow (de)registering of one or more fd's.
  • Return one or more triggered interrupt pins, with their current logic level.
  • Add a reset option to clear any cached trigger events.
  • Rework Gpio::poll_interrupt() to internally use poll_interrupts().

Open drain pin emulation

Hello.

I've spent some time trying link my HAL based driver for DHT sensors with rppal.

I think that rppal missing mostly 2 things right now:

  • software implementation of open drain pin
  • latest embedded-hal implementation for v2 traits

BTW I can create pull request if you are OK with open drain pin emulation

Error: Io(Os { code: 2, kind: NotFound, message: "No such file or directory" })

When I run the 0.11.3 build on a Raspberry Pi 4B I get a weird error.

Cargo.toml

[package]
name = "gpio_test"
version = "0.1.0"
authors = ["pi"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rppal = "0.11.3"

Running the example code yields the following results:

use std::error::Error;
use std::thread;
use std::time::Duration;

use rppal::gpio::Gpio;
use rppal::system::DeviceInfo;

// Gpio uses BCM pin numbering. BCM GPIO 23 is tied to physical pin 16.
const GPIO_LED: u8 = 23;

fn main() -> Result<(), Box<dyn Error>> {
    println!("Blinking an LED on a {}.", DeviceInfo::new()?.model());

    let mut pin = Gpio::new()?.get(GPIO_LED)?.into_output();

    // Blink the LED by setting the pin's logic level high for 500 ms.
    pin.set_high();
    thread::sleep(Duration::from_millis(500));
    pin.set_low();

    Ok(())
}

The results are in the attached image.

image

Whenever I test the GPIO with the pre-loaded python library I have full access to the pins. Is there something I'm missing or is there some permission that I need to give?

Stubbing?

I'd like to be able to enable a stub feature (or whatever) and be able to use rppal GPIO functions on CI (which obviously isn't a Raspberry Pi). I currently implement this myself, but it'd be great to have this capability upstream (and thereby reduce the surface area of my own code).

How to control a servo?

Hi there,

I want to attach a servo to my Raspberry Pi 3 B+. I think they are controlled using PWM. Could you give me a hint how I could use the libraries's PWM module to control it?

InputPin debounce support

I'm using this library on a Pi Zero W to interface with an old rotary phone, and switch transitions definitely aren't clean 100% of the time.

Previously I have used the gpiozero Python library, which has the option to "debounce" inputs: a user-supplied time delay controls how soon before new input states can be registered. As far as I can tell, this crate does not have any such capability.

What I would love to see is a new method for InputPin that sets a debounce time using a Duration. This might work something like the following:

const BCM_INPUT: u8 = 2;
let gpio = Gpio::new()?;
// Ignore state changes on `button` less than 50 ms apart.
let debounce_time = Duration::from_millis(50);
let button = gpio.get(BCM_INPUT)?.into_input_pulldown().debounce(debounce_time);
// ...

Of course, I could implement my own debouncing code on top of rppal, but I strongly feel that this is something that should be implemented in this crate directly, as debouncing digital inputs from switches is an extremely common practice.

Support conversion of a u8 to gpio::Level

As far as I can tell, there isn't any implementation for Level that supports conversion of a u8 literal to a Level.

This would be handy for my project, where I am sequentially bit-banging a pin with a byte's worth of bits at a time. Currently, using rppal, my code looks like this:

for i in 7..=0 {
        pin.write(if ((byte>>i) & 1) > 0 { Level::High } else { Level::Low });
}

Not nearly as pretty as I'd like. This can be improved if, for example, TryFrom were implemented for Level and u8, it would simplify user code by being able to rewrite the above loop as:

for i in 7..=0 {
        pin.write(((byte>>i) & 1).try_into().unwrap());
}

The main question I have for this sort of enhancement is would it be better to implement From so that any non-zero u8 is treated as Level::High, or adhere strictly to the u8 needing to be either 0 or 1 to be converted to a Level

Gpio::new() PermissionDenied

Consider the following program:

extern crate rppal;

fn main() {
    rppal::gpio::Gpio::new().unwrap();
}

Using rppal 0.8.0 or higher on Raspberry Pi, I get a Permission error:

thread 'main' panicked at 'called Result::unwrap() on an Err value: Io(Os { code: 13, kind: PermissionDenied, message: "Permission denied" })', libcore/result.rs:1009:5

But it runs fine on rppal 0.7.1. And with a longer program, I'm able to read and write to GPIO.

So it looks like this is a regression introduced between 0.7.1 and 0.8.0.

Raspberry Pi 3 Model B Rev 1.2
OS: Raspbian GNU/Linux 8 (jessie)
rustc: 1.31.0

Async Interrupts stop responding if process is suspended/resumed

Callbacks set with set_async_interrupt() no longer get called if the process is suspended and then resumed. Indeed, the background thread disappears entirely.

It appears that the interrupt's polling loop does not handle EINTR from epoll wait, causing it to terminate.

i2c permits forgetting to call set_slave_address

The rppal gpio functions use the Rust type system to stop you forgetting to set the pin mode, etc. This kind of thing is a good idea.

i2c should ideally have something similar, where you can't forget to call set_slave_address. (AFAICT none of the other setters are mandatory.) Unfortunately I think this would be a breaking change.

set_async_interrupt not working

I'm no rust expert, but I think this should work....

the println in the set_async_interrupt handler never fires.
the println in the loop faithfully reports the correct level of the pin.

Am I doing something wrong? If so, please provide a valid example in the docs.

This is on a Raspberry Pi Zero W

use std::{thread, time};
use rppal::gpio::Gpio;
use rppal::gpio::Trigger;
use rppal::gpio::Level;

fn main() {
	let gpio = Gpio::new().expect("Gpio new");
	let pin = gpio.get(12).expect("gpio get");
	let mut pin = pin.into_input_pullup();

	pin.set_async_interrupt(Trigger::Both, move |level: Level|{
		println!("This never works!! {}", level);
	}).unwrap();

	loop {
		thread::sleep(time::Duration::from_millis(1000));
		println!("button is {}.", pin.read());
	}
}

GPIO with `embedded_hal`

Hi. How can we use the GPIO pins with functions that expected embedded_hal implementations?

What I'm attempting:
Cargo.toml:

rppal = { version = "0.11.3", features=["hal"] }

Code:

let gpio = Gpio::new().unwrap();
let pin = gpio.get(7).unwrap().into_io(Mode::Alt1);  // or into_input()

find_devices(pin);

Error:

the trait bound `rppal::gpio::pin::IoPin: embedded_hal::digital::v1::InputPin` is not satisfied

This file, if I understand correctly, may point to the cause of the error, by omission:
https://github.com/golemparts/rppal/blob/master/src/gpio/hal.rs

edit: It also looks like it would be easy to support StatefulOutputPin, since the included methods are supported in rppal. Am I missing something re why it's not working? A workaround is to use thin wrappers that impl the hal traits, and wrap the rppal methods.

Should I PR this?

Reasoning why read requres &mut self

I can not see from your code any necessity of using &mut in read and even write functions.
Ok it is undertandable that write have to be &mut

But why pin read function require &mut self

It makes code to work with your library to be more complicated, because in most cases I do not expect to have mutable readings plus Rust compiler force me to do attional stuff to make &mut usage safe.

Could you remove &mut from value getters on gpio?

Why Raspberry Pi specific?

Hi,
First of all, thank you for this very nice crate, with a lot of features and a nicely documented.
But I wonder why did you make it Raspberry Pi specific?
The strength of an OS like Linux is to provide common abstraction layers to different hardware.
So using i2cdev, spidev, gpiochip interfaces should allow to use any Linux board like all the fruitPi boards based on different chips from Allwinner, Rockchip, Amlogic and so on.
From what I understand of your crate, mainly the GPIO interface through direct memory access and maybe the software PWM are using BCM specific interfaces.
Would it be possible to use gpiochip character device to interact with the GPIO and make this crate hardware agnostic or it would result in slower operations making the software PWM too slow?

Can not use embedded-hal features

There is a possibility that this is not a bug and I'm just a dumb coder but right now I believe in opposite.

I've implemented DHT sensor driver based on HAL and I want to test it on RPI via rppal.

But I've found that I'm not able to use HAL traits in my code.
Looks like in your code all traits implementation for HAL are in non public module.

Duplicating embedded-hal API

I was taking a look at your SPI documentation and there i see:

pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize>
pub fn write(&mut self, buffer: &[u8]) -> Result<usize>
pub fn transfer(
    &self,
    read_buffer: &mut [u8],
    write_buffer: &[u8]
) -> Result<usize>

But you also implement the embedded-hal traits which in turn provide

fn write(&mut self, words: &[W]) -> Result<(), Self::Error>
fn transfer<'w>(&mut self, words: &'w mut [W]) -> Result<&'w [W], Self::Error>
fn read(&mut self) -> Result<Word, Self::Error>
fn send(&mut self, word: Word) -> Result<(), Self::Error>

Which also provide a read, write and transfer functionality.

So I'm wondering, is there any reason you're essentially duplicating the embedded-hal API here while also at the same time implementing the embedded-hal traits? Wouldn't it make more sense to just implement the embedded-hal traits like the vast majority of HALs (if not all of them) do?

Spi::transfer_segments seems to require both read and write buffers

I'm trying to interface a CC1101 radio via SPI on a RPi2 running rasbian stretch with kernel 4.14.50-v7+.

The interface protocol is to send a command byte while simultaneously receving a status byte, then, while CS is still asserted, read or write 1 or more bytes depending on the command.

When I do the following, my 'register' array is not modified.

let cmd_ts = TransferSegment::new(Some(&mut status), Some(&cmd));
let reg_ts = TransferSegment::new(Some(&mut register), None);
let segments = [cmd_ts, reg_ts];
spi.transfer_segments(&segments).unwrap();

Note, the second segment has a receive buffer but 'None' for the send buffer.

However, if I do this, add an array of zeros for the send buffer, it works.

let cmd_ts = TransferSegment::new(Some(&mut status), Some(&cmd));
let reg_ts = TransferSegment::new(Some(&mut register), Some(&[0; 1]));
let segments = [cmd_ts, reg_ts];
spi.transfer_segments(&segments).unwrap();

Interestingly, I can specify None for the 1st segment's receive buffer and it works. So, perhaps the issue is only with the send buffer.

Is setting alt mode necessary to use PWM?

Is setting a pin to the correct alt mode necessary to use hardware PWM?

The example doesn't seem to do that, which strikes me as confusing. It doesn't look like the source code for PWM creation sets the alt mode under the hood. Aren't all GPIO pins set to "input" mode by default?

Thanks!

Wrong revision numbers on Fedora

I've tried to use the library under Fedora and ran into a simple Error. Fedora does not recognise the revision number correctly and always shows 0000 in /proc/cpuinfo. This leads to an Error::UnkownModel in any case. Currently, I'm on a 3B and I also tested it on a 2B.
I'm using a small hack in system.rs line 179 to identify the model manually since I know which version I'm using:

"0000" => Model::RaspberryPi3B,

Maybe there could be an option to configure the model manually.

Here's the end of my /proc/cpuinfo (it seems that it also gets the SoC wrong):

Hardware:    BCM2835
Revision:    0000
Serial:      00000000e07fdbcc

If you need more information or any help, I'd be glad to help. Cheers!

TODOs to research/implement before gpio is ready for a 0.10.0 release

  • InputPins created using different instances of Gpio won't share the same EventLoop, which means they'll be added to the wrong EventLoop instance by InputPin::set_interrupt() when a user tries to poll multiple sync interrupts using Gpio::poll_interrupts(). Either share a single static EventLoop instance, or combine add/poll/remove into a single method.
  • Running InputPin::poll_interrupt() for different pins on multiple threads simultaneously will block all but one due to the required Mutex lock. Find a better solution. Update documentation.
  • 32-bit reads/writes to 32-bit aligned memory locations are atomic on ARMv6/v7/v8 (see ARM Architecture Reference Manuals). Verify GpioMem's mmap configuration (O_SYNC, MAP_SHARED) doesn't negate atomicity and doesn't cause any caching-related issues, and then remove the locking mechanism for single reads/writes. Locks are still needed when multiple operations need to be synchronized (set_mode, set_pullupdown).
  • Consider the consequences of removing the single instance limitation on Gpio, now that Pins survive a Gpio instance going out of scope.
  • Consider moving the contents of Gpio to a static variable, and have Gpio::new() either clone the static variable if it has been created already, or initialize it and return any relevant errors. (This also means GPIO_MEM_LOCKS can be moved back to a local struct field)
  • Give each Pin a cloned GpioState.
  • Use a weak reference for the shared static GpioState, so it'll automatically get dropped when all Gpio and Pin instances go out of scope.
  • Move the static PINS_TAKEN to a local GpioState struct field.
  • Consider Gpio::take() versus Gpio::get(). Option::take() and HashSet::take() similarly remove the returned value from where it was taken. However, Pin can be taken again after it goes out of scope, so that makes the expected functionality different. get() methods usually return a temporary reference instead of an owned value, but after the returned reference goes out of scope it can be retrieved again, similar to what happens when a Pin is dropped.
  • Disable pull-up/pull-down when InputPin is dropped and clear_on_drop==true.
  • Expand pin documentation in the main section of the gpio module.
  • Add a section on interrupts to the gpio module documentation.
  • Investigate if we can lock a pin through /dev/gpiochipN to prevent other processes from gaining access simultaneously, while still using /dev/gpiomem for fast register access. See comment below.
  • Using the gpio module for charlieplexing or bitbanging I2C requires switching a pin between input and output mode. This should be easier to do than dropping the derived pin and calling Gpio::get() again for each mode switch. Possible solutions:
    • Add into_ methods to InputPin and OutputPin to switch pin types.
    • Add a new pin type (IoPin?) that allows users to switch between input and output mode. The new pin type could allow users to set any mode, replacing AltPin.
    • Add a set of methods to either Pin or InputPin and OutputPin that either return a temporary reference, or take a closure that is called with a reference to a pin of the appropriate type.
  • Change Gpio::get() to return a Result rather than an Option for uniform error handling. Return an Error::PinNotAvailable when a pin is already in use, or when the pin number isn't supported on the current Pi model.
  • Consider removing Gpio::new(), and instantiate/clone GpioState from within Gpio::get(). Gpio::poll_interrupts() would need some tweaks as well. Pro: Single method that can return any applicable errors. Con: Need to lock a mutex for each call. No uniform API for all peripherals. Reconsider for 0.11.0.

Docker container - UnknownModel

Hi,

I'm trying to run my application in a docker container. The code is the one listed in rppal main page:

use ...
const GPIO_LED: u8 = 23;

fn main() -> Result<(), Box<dyn Error>> {
    println!("Blinking an LED on a {}.", DeviceInfo::new()?.model());
    let mut pin = Gpio::new()?.get(GPIO_LED)?.into_output();
    ...
}
  • running on raspberry pi4 (r.os x64) works fine
  • running inside docker alpine with --privileged flag works fine (but a little bit too permissive)
  • running inside docker alpine fails (even with --device /dev/gpiomem --device /dev/mem -v /sys:/sys flags) with following errors:
    • UnknownModel calling DeviceInfo::new()
    • io(Os { code: 2, kind: NotFound, message: "No such file or directory" }) calling Gpio::new()

in all the cases I'm able to see /proc/cpuinfo. What am I missing?

/proc/cpuinfo contains
Hardware : BCM2835
Revision : c03112
and it should be parsed correctly to RP4B. I'll try to debug DeviceInfo::new() code.

Executing the code in master branch (parse_proc_cpuinfo function) everything works fine.
Is it possible that rppal 0.11.3 isn't aligned with master code (so it is not able to read from /proc/cpuinfo, tries to read from other locations /sys/firmware/* that without --privileged docker flag aren't availble by default)?

Compare with rust-sysfs-gpio

Can you help clear up whether I should use this library or the one from the rust-embedded org for a Raspberry Pi GPIO project? I can't really tell if there is an officially sanctioned one or if this does something differently?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.