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

RMT Onewire Peripheral #454

Merged
merged 26 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 2 additions & 2 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[build]
target = "riscv32imc-esp-espidf"
#target = "xtensa-esp32-espidf"
# target = "riscv32imc-esp-espidf"
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
target = "xtensa-esp32-espidf"
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved

[target.xtensa-esp32-espidf]
linker = "ldproxy"
Expand Down
42 changes: 42 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
"name": "rmt_onewire",
// Select between image and build propieties to pull or build the image.
"image": "docker.io/espressif/idf-rust:esp32_latest",
"customizations": {
"vscode": {
"extensions": [
"rust-lang.rust-analyzer",
"tamasfe.even-better-toml",
"serayuzgur.crates",
"mutantdino.resourcemonitor",
"yzhang.markdown-all-in-one",
"ms-vscode.cpptools",
"actboy168.tasks",
"Wokwi.wokwi-vscode",
"ms-azuretools.vscode-docker",
"ms-python.python"
],
"settings": {
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
"editor.formatOnType": true,
"lldb.executable": "/usr/bin/lldb",
"files.watcherExclude": {
"**/target/**": true
},
"rust-analyzer.checkOnSave.command": "clippy",
"rust-analyzer.checkOnSave.allTargets": false,
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer"
}
}
}
},
"forwardPorts": [
3333,
8000
],
"workspaceMount": "source=${localWorkspaceFolder},target=/home/esp/rmt_onewire,type=bind,consistency=cached",
"workspaceFolder": "/home/esp/rmt_onewire"
}
16 changes: 13 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ std = ["alloc", "esp-idf-sys/std"]
alloc = []
nightly = []
wake-from-isr = [] # Only enable if you plan to use the `edge-executor` crate
embassy-sync = [] # For now, the dependecy on the `embassy-sync` crate is non-optional, but this might change in future
embassy-sync = [
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
] # For now, the dependecy on the `embassy-sync` crate is non-optional, but this might change in future
# Temporary, until (https://github.com/espressif/esp-idf/issues/13938) is addressed
# - When enabled, the code for the legacy ADC oneshot driver will be compiled;
# - When not enabled (default) the code for the new ADC oneshot driver will be compiled;
Expand All @@ -42,13 +43,17 @@ libstart = ["esp-idf-sys/libstart"]
nb = "1"
embedded-can = "0.4.1"
embedded-hal = "1"
embedded-hal-0-2 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] }
embedded-hal-0-2 = { package = "embedded-hal", version = "0.2.7", features = [
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
"unproven",
] }
embedded-hal-nb = "1"
embedded-hal-async = "1"
embedded-io = "0.6"
embedded-io-async = "0.6"
esp-idf-sys = { version = "0.35", default-features = false }
critical-section = { version = "1.1.1", optional = true, features = ["restore-state-none"] }
critical-section = { version = "1.1.1", optional = true, features = [
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
"restore-state-none",
] }
heapless = "0.8"
num_enum = { version = "0.7", default-features = false }
enumset = { version = "1.1", default-features = false }
Expand All @@ -65,3 +70,8 @@ esp-idf-sys = { version = "0.35", features = ["binstart"] }
mipidsi = "0.5.0"
display-interface-spi = "0.4.1"
embedded-graphics = "0.7.1"

[[package.metadata.esp-idf-sys.extra_components]]
remote_component = { name = "ds18b20", version = "^0.1.0" }
bindings_header = "bindings.h"
DaneSlattery marked this conversation as resolved.
Show resolved Hide resolved
bindings_module = "onewire"
2 changes: 2 additions & 0 deletions bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include "ds18b20.h"
#include "onewire_bus.h"
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
21 changes: 21 additions & 0 deletions components_esp32.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
dependencies:
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
espressif/ds18b20:
component_hash: d676429d9f1f686b65ed895e418eb4b331477bcd5d63c27f43dc3d6a2de8f13b
source:
service_url: https://api.components.espressif.com/
type: service
version: 0.1.1
espressif/onewire_bus:
component_hash: c4c781940d8bd988432b47ac3ee68479c69e14e01fdfba0b61365b233a3ba5d6
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.0.2
idf:
component_hash: null
source:
type: idf
version: 5.1.2
manifest_hash: f4c4d8dc4f9e0e9422ee45c389b14c8d3db5ce6258eaec379ea8d50430151679
target: esp32
version: 1.0.0
26 changes: 26 additions & 0 deletions examples/rmt_onewire.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::onewire::BusDriver;
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_hal::rmt::{
FixedLengthSignal, PinState, Pulse, PulseTicks, Receive, RmtReceiveConfig, RmtTransmitConfig,
RxRmtDriver, TxRmtDriver,
DaneSlattery marked this conversation as resolved.
Show resolved Hide resolved
};

fn main() -> anyhow::Result<()> {
println!("Starting APP!");

let peripherals = Peripherals::take()?;

let onewire_pin = peripherals.pins.gpio16;

let mut rmt_onewire = BusDriver::new(onewire_pin)?;

let mut search = rmt_onewire.search()?;

let search_device = search.next_device()?;

println!("Found Device: {}", search_device.address());
loop {
FreeRtos::delay_ms(3000);
}
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ pub mod peripheral;
pub mod peripherals;
pub mod prelude;
pub mod reset;
#[cfg(feature = "legacy_rmt")]
pub mod rmt;
//
// #[cfg(not(feature = "legacy_rmt"))]
pub mod onewire;
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
pub mod rom;
pub mod spi;
pub mod sys;
Expand Down
102 changes: 102 additions & 0 deletions src/onewire.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use core::marker::PhantomData;
use core::ptr;

use esp_idf_sys::*;

use crate::peripheral::Peripheral;

#[derive(Debug)]
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
pub struct Device<'b> {
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
_device: onewire::onewire_device_t,
// _bus: &'b BusDriver<'b>, // not sure how I would hold a reference to this aside from the handle
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
_p: PhantomData<&'b ()>, // holds the lifetime since the device is linked to the lifetime of the bus
}

impl<'a> Device<'a> {
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
fn new(device_handle: onewire::onewire_device_t) -> Self {
Self {
_device: device_handle,
_p: PhantomData,
}
}

/// get the handle of the bus the device is attached to
pub fn bus(&self) -> onewire::onewire_bus_handle_t {
self._device.bus
}

/// get the device address
pub fn address(&self) -> u64 {
self._device.address
}
}

/// wrapper around the device iterator to search for available devices on the bus
pub struct DeviceSearch {
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
_search: onewire::onewire_device_iter_handle_t,
}

impl DeviceSearch {
fn new(bus: &mut BusDriver) -> Result<Self, EspError> {
let mut my_iter: onewire::onewire_device_iter_handle_t = ptr::null_mut();

esp!(unsafe { onewire::onewire_new_device_iter(bus._bus, &mut my_iter) })?;

Ok(Self { _search: my_iter })
}

pub fn next_device(&mut self) -> Result<Device, EspError> {
let mut next_onewire_device = onewire::onewire_device_t::default();
esp!(unsafe {
onewire::onewire_device_iter_get_next(self._search, &mut next_onewire_device)
})?;

Ok(Device::new(next_onewire_device))
}
}

impl Drop for DeviceSearch {
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
fn drop(&mut self) {
esp!(unsafe { onewire::onewire_del_device_iter(self._search) }).unwrap();
}
}

/// beep beep
pub struct BusDriver<'d> {
_bus: onewire::onewire_bus_handle_t,
_p: PhantomData<&'d ()>,
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
}

impl<'d> BusDriver<'d> {
pub fn new(
pin: impl Peripheral<P = impl crate::gpio::InputPin + crate::gpio::OutputPin> + 'd,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since internally the ESP IDF onewire driver uses the new RMT driver (a single channel for TX/RX) (plus the pin thst you already track) you need to somehow track the usage of the RMT channel peripheral, as it is still a peripheral - just like the pin which you track already.

If you don't track it, nothing major would happen because the old and the new RMT driver cannot co-exist, yet would still be good to follow the pattern established with the other drivers.

Now, some of the "new" (ESP IDF 5+) C drivers no longer take a peripheral "id"/"number". For example, the old RMT driver used to take a channel number, but the new C RMT driver doesn't as it internally manages what channel to be used. If you try to instantiate more driver instances than hardware channels you have available, it would likely return a runtime EspError. So in a way - with the new RMT C driver you don't really know which concrete hardware channel peripheral will be used by that particular C driver instance.

With that said, nothing stops us from continuing the old way and passing to the driver a RMT "peripheral" (note that you don't need "new" RMT channel peripherals for the new C RMT driver, because the peripherals are ghost structures which however model actual hardware on the chip and not higher-level C driver concepts).

So what I suggest is to just enhance the new constructor by passing an impl ... Channel ... peripheral that you can copy from the old RMT driver wrapper. While there is no warranty that the C driver will use exactly the same channel that you pass to it - we don't care, as the channels are indistinguishable from each other. We just use - with the new C driver - the channels as a way of glorified "reference counting" of sorts.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Just mentioning that this comment ^^^ is still valid.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I had planned to look into this. It becomes quite problematic as you mentioned below because I ifdef the whole rmt module.

Copy link
Collaborator

@ivmarkov ivmarkov Jul 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I would not call it "quite problematic". #ifdef-ing only the driver inside rmt module is hopefully not a rocket science. Easiest way to do it is what I suggested for the examples:

  • Define a mod driver {} internal module (not public) - inside the rmt module itself. no need for a separate file, just an inline module.
  • Move all code except the peripheral! peripherals' definitions as well as the RMT peripheral traits (if any) inside the driver module
  • Put a cfg on top of the driver module
  • Put a cfg-ed pub use driver::* in the rmt module

No big deal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No big deal. It's done.

) -> Result<Self, EspError> {
let mut bus: onewire::onewire_bus_handle_t = ptr::null_mut();

let pin = pin.into_ref().pin();
let bus_config = esp_idf_sys::onewire::onewire_bus_config_t { bus_gpio_num: pin };

let rmt_config = esp_idf_sys::onewire::onewire_bus_rmt_config_t { max_rx_bytes: 10 };
DaneSlattery marked this conversation as resolved.
Show resolved Hide resolved

esp!(unsafe { onewire::onewire_new_bus_rmt(&bus_config, &rmt_config, &mut bus as _) })?;

Ok(Self {
_bus: bus,
_p: PhantomData,
})
}
/// send reset pulse to the bus
pub fn reset_bus(&mut self) -> Result<(), EspError> {
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
esp!(unsafe { onewire::onewire_bus_reset(self._bus) })
}

pub fn search(&mut self) -> Result<DeviceSearch, EspError> {
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
DeviceSearch::new(self)
}
}

impl<'d> Drop for BusDriver<'d> {
fn drop(&mut self) {
esp!(unsafe { onewire::onewire_bus_del(self._bus) }).unwrap();
}
}
3 changes: 3 additions & 0 deletions src/peripherals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::mac;
use crate::modem;
#[cfg(any(esp32, esp32s2, esp32s3, esp32c6))]
use crate::pcnt;
#[cfg(feature = "legacy_rmt")]
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
use crate::rmt;
use crate::spi;
#[cfg(any(
Expand Down Expand Up @@ -71,6 +72,7 @@ pub struct Peripherals {
pub ledc: ledc::LEDC,
#[cfg(esp32)]
pub hledc: ledc::HLEDC,
#[cfg(feature = "legacy_rmt")]
pub rmt: rmt::RMT,
#[cfg(all(
any(esp32, esp32s2, esp32s3, esp32c6, esp32p4),
Expand Down Expand Up @@ -168,6 +170,7 @@ impl Peripherals {
ledc: ledc::LEDC::new(),
#[cfg(esp32)]
hledc: ledc::HLEDC::new(),
#[cfg(feature = "legacy_rmt")]
rmt: rmt::RMT::new(),
#[cfg(all(
any(esp32, esp32s2, esp32s3, esp32c6, esp32p4),
Expand Down
2 changes: 1 addition & 1 deletion src/rmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//!
//! Not supported:
//! * Interrupts.
//! * Receiving.
//! * ~Receiving.~
DaneSlattery marked this conversation as resolved.
Show resolved Hide resolved
//! * Change of config after initialisation.
//!
//! # Example
Expand Down
Loading