[Rust no_std] I2C Beschleunigungssensor auslesen

Alles, was nicht direkt mit Python-Problemen zu tun hat. Dies ist auch der perfekte Platz für Jobangebote.
Antworten
Benutzeravatar
Dennis89
User
Beiträge: 1473
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo zusammen,

immer mal wieder versuche ich mich an Rust und den Mikrokontroller (ESP32 C3) und vorgestern habe ich einen Beschleunigungssensor entdeckt mit dem ich mal eine kleine Drohne bauen wollte. Naja und irgendwie muss ich auch mal was anderes, als die Brechnungsprogramme machen und da dachte ich mir, ich schaue mal, ob ich den Sensor mit Rust, so hardwarenah wie möglich auslesen kann. Ich habe dann viel gelesen und bin jetzt am umsetzen.

Das Sensor um den es geht, ist der MPU6050.

In MicroPython sieht das Auslesen bzw. das Ansprechen und Lesen der Werte so aus:

Code: Alles auswählen

class MPU6050:
    def __init__(self, i2c, address=0x68):
        self.i2c = i2c
        self.address = address
        self.buffer = bytearray(14)
        self.i2c.start()
        self.i2c.writeto(self.address, b"\x6b\0")
        self.i2c.stop()

    def fill_buffer(self):
        self.i2c.start()
        self.i2c.readfrom_mem_into(self.address, 0x3B, self.buffer)
        self.i2c.stop()

    def get_values(self):
        self.fill_buffer()
        return dict(zip(
            ["AcX", "AcY", "AcZ", "Tmp", "GyX", "GyY", "GyZ"],
            struct.unpack('<7h', self.buffer)))
Man muss dem Sensor zu Beginn in die Start-Konfiguration bringen, dann den Buffer `b"\x6b\0"` schreiben und ihn wieder in die Stopp-Konfiguration bringen.

Wenn ich jetzt Werte will, dann wieder Start-Konfiguration, und beginnend von der Speicheradresse `0x3B` lesen und in einen Buffer schreiben, danach wieder Stopp.

In der Rust Doku finde ich kein Start/Stopp. Es gibt da aber `transaction` und ich denke, dass das das ist, was ich benötige. Mir ist allerdings die Anwendung nicht klar. Zum einen weis ich nicht wie ich die Speicheradresse angebe, von der ich lesen soll und allgemein weis ich einfach nicht, ob das so richtig ist.
Mein Versuch:

Code: Alles auswählen

#![no_std]
#![no_main]


use esp_hal::clock::CpuClock;
use esp_hal::i2c::master::{I2c, Config, Operation};
use esp_hal::prelude::*;
use esp_backtrace as _;
use esp_println::println;


#[entry]
fn main() -> ! {
    let peripherals = esp_hal::init({
        let mut config = esp_hal::Config::default();
        config.cpu_clock = CpuClock::Clock160MHz;
        config
        });

    let mut i2c = I2c::new(
        peripherals.I2C0,
        Config::default(),
    )
    .with_sda(peripherals.GPIO3)
    .with_scl(peripherals.GPIO2);

    let mut data = [0u8; 14];    
    i2c.transaction(0x68, &mut [
        Operation::Write(&[0x6B]),
        ]).ok();
    
    loop {
        let results = i2c.transaction(0x68, &mut [
            Operation::Read(&mut data)
            ]).and(Ok(data));
        println!("{:#?}", results);
    }
}
Die meisten Ausgaben sind gefüllt mit 0-len, aber ab und an kommen auch ein paar Werte, zum Beispiel:

Code: Alles auswählen

Ok(
    [
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
    ],
)
Ok(
    [
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        64,
    ],
)
Ok(
    [
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        104,
        0,
        0,
        0,
        0,
        0,
    ],
)
Ok(
    [
        0,
        0,
        0,
        0,
        133,
        3,
        2,
        162,
        107,
        190,
        246,
        134,
        254,
        213,
    ],
)
Ok(
    [
        120,
        40,
        141,
        110,
        143,
        74,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
        0,
    ],
)
Ich kann das so aber gar nicht verifizieren.

Danke für eure Hilfe!

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1473
Registriert: Freitag 11. Dezember 2020, 15:13

Und ich habe noch ein Problem, das mich seit Stunden um den Verstand bringt.

Ich möchte diese MCPWM Funktion nutzen. Die ist aber in `esp32-hal` und ich nutze bis jetzt `esp-hal`. Auf der Startseite der Doku zum Crate `esp32-hal` steht aber auch, dass man das nicht mehr verwenden soll und dass das in `esp-hal` integriert worden sei.
Hört sich für mich ja gut an.
Die Doku verlinkt auch auf eine GitHub-Seite auf der beschrieben wird, wie man sein Projekt updatet, wenn man bis jetzt `esp32-hal` genutzt hat. Sieht easy aus, einfach in die `Cargo.toml` folgendes eintragen:

Code: Alles auswählen

esp-hal = { version = "0.22.0", features = ["esp32c3"] }
Auf Github steht Version 0.16.0, aber 0.22.0 ist aktuell. Und dann muss man nur noch die `use esp32_hal` gegen `use esp_hal` tauschen.

Ich verstehe das so, ich muss die oben gezeigte Zeile in meiner `cargp.toml` haben und dann sollte

Code: Alles auswählen

use esp_hal::mcpwm::MCPWM;
funktionieren.

Ich bekomme aber:

Code: Alles auswählen

[dennis@dennis drone]$ cargo run --release
   Compiling drone v0.1.0 (/home/dennis/PicoDrone/drone)
error[E0432]: unresolved import `esp_hal::mcpwm`
   --> src/bin/main.rs:7:14
    |
7   | use esp_hal::mcpwm::MCPWM;
    |              ^^^^^ could not find `mcpwm` in `esp_hal`
Es gibt auch in der Doku zu `esp-hal` kein `mcpwm`, aber die haben doch geschrieben, dass das migriert worden ist.
Seht ihr meinen Fehler?

Danke und Grüße
Dennis

Achja, ich habe es auch mit der Version "0.16.0" wie beschrieben versucht.
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1473
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

Rückmeldung, das mit den Dokus zu `mcpwm` finde ich immer noch verwirrend, aber ich habe jetzt den ESP32c3 gegen einen ESP32-WROOM-32 getauscht, das Projekt an die Hardware angepasst und ich habe jetzt Zugriff auf `mcpwm`.
Damit ist wenigstens ein Problem behoben.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1473
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen,

könnt ihr mir bitte bestätigen (oder mich verbessern), ob ich das richtig verstanden habe.
Im MicroPython-Code steht

Code: Alles auswählen

self.i2c.writeto(self.address, b"\x6b\0")
Ist dass in Rust so richtig ausgedrückt:

Code: Alles auswählen

Write(&[0x6B, 0])
Ich verstehe `b"\x6b\0"` so, dass ist ein Bytes-Objekt und besteht aus 0x6b und 0. Ich weis nicht wie ich das auf "ein mal" in Rust ausdrücke, deswegen dachte ich, er kann ja erst die 0x6b und dann die 0 schreiben?

Dann noch mal auf diese MicroPython-Zeile:

Code: Alles auswählen

self.i2c.readfrom_mem_into(self.address, 0x3B, self.buffer)
Die aufgerufene Funktion konnte ich in der Dokumentation vom ESP32 nicht finden. Im Quellcode von MicroPython habe ich sie (glaube ich) gefunden. Ich kann das leider nicht wirklich lesen/verstehen. Meine Frage, ist die Funktion von den MicroPython Entwickler selbst geschrieben bzw. aus vorhandenen ESP-Funktionen zu einer zusammen gebaut?
Falls ja, besteht die Möglichkeit das realistisch in Rust esp_hal zu implementieren?

Oder wenn ich keine Adresse angebe, ab welcher Adresse wird dann gelesen? Und kann ich dann die kommenden Daten bearbeiten, bzw. "einfach" die falschen wegwerfen? Aber wie finde ich die, ich weis gar nicht was sich hinter 0x3B verbirgt bzw. wie groß der Buffer sein muss, dass ich auch das drin habe, was ich will.


Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1473
Registriert: Freitag 11. Dezember 2020, 15:13

So ich bin ein Stück weiter. Ich muss vor dem Lesen die Adresse ab der ich lesen will, erst schreiben.
Dann bekomme ich 14 Bytes von denen sind immer zwei zusammenhängend. Das erste Byte ist high, das zweite low und so weiter. Also muss ich immer zwei nehmen und zusammen führen. Das High-Byte nach links verschieben und dann mit Bitwise Oder das Low-Byte verarbeiten.(?)

Die Tabelle der Register ist hier auf Seite 7.

Mein Code sieht so aus:

Code: Alles auswählen

#![no_std]
#![no_main]


use esp_hal::clock::CpuClock;
use esp_hal::i2c::master::{I2c, Config, Operation};
use esp_hal::delay::Delay;
use esp_hal::prelude::*;
use esp_backtrace as _;
use esp_println::println;


#[entry]
fn main() -> ! {
    let peripherals = esp_hal::init({
        let mut config = esp_hal::Config::default();
        config.cpu_clock = CpuClock::Clock160MHz;
        config
        });

    let mut i2c = I2c::new(
        peripherals.I2C0,
        Config::default(),
    )
    .with_sda(peripherals.GPIO18)
    .with_scl(peripherals.GPIO19);

    let mut data = [0u8; 14];    
    i2c.transaction(0x68, &mut [
        Operation::Write(&[0x6B, 0]),
        ]).ok();
    let delay = Delay::new();
    loop {
        let results = i2c.transaction(0x68, &mut [
            Operation::Write(&[0x3B]),
            Operation::Read(&mut data)
            ]).and(Ok(data));
        println!("{:#?}", results);
        for pair in results.expect("REASON").chunks(2) {
            let result: u16 = u16::try_from(pair[0]) << 8 | u8::try_from(pair[1]);
            println!("{}", result);
        }
        println!("------------------------");
        delay.delay_millis(500);

    }
}
Allerdings haut da was nicht hin, weil ich wohl was machen will, das nicht implementiert ist. Ich verstehe nur nicht ganz was ich da falsch mache. Ich denke, wäre mein Vorgehen richtig, wäre das auch implementiert.

Code: Alles auswählen

[dennis@dennis drone]$ cargo run --release
   Compiling drone v0.1.0 (/home/dennis/PicoDrone/drone)
error[E0369]: no implementation for `Result<u16, Infallible> << {integer}`
   --> src/bin/main.rs:40:54
    |
40  | ...ult: u16 = u16::try_from(pair[0]) << 8 | u8::try_from(pair[1]);
    |               ---------------------- ^^ - {integer}
    |               |
    |               Result<u16, Infallible>
    |
note: the foreign item type `Result<u16, Infallible>` doesn't implement `Shl<{integer}>`
   --> /home/dennis/.rustup/toolchains/esp/lib/rustlib/src/rust/library/core/src/result.rs:527:1
    |
527 | pub enum Result<T, E> {
    | ^^^^^^^^^^^^^^^^^^^^^ not implement `Shl<{integer}>`

For more information about this error, try `rustc --explain E0369`.
error: could not compile `drone` (bin "main") due to 1 previous error
Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 13754
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Dennis89: Die try_*-Funktionen/Methoden liefern wie die Fehlermeldung sagt `Result`-Objekte, also etwas das entweder ein Fehler ist oder ein Ergebniswert. Was sollte denn da Shl/``<<`` machen wenn es ein Fehler war? Das ist doch aber normale Rust-Grundlage diese `Result`-Typen um sowohl Ergebnisse als auch Fehler zurückgeben zu können.
“The city's central computer told you? R2D2, you know better than to trust a strange computer!” — C3PO
Benutzeravatar
Dennis89
User
Beiträge: 1473
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für den Hinweis. Ja jetzt wenn man sich die Fehlermeldung nach deinem Post noch mal anschaut, hätte man eigentlich selbst darauf kommen können. Die Grundlagen sind bei mir nicht immer "griffbereit", dazu mache ich viel zu selten etwas mit der Sprache. Gut möglich dass das Projekt in Rust für mich noch zu "groß" ist, aber wenn ich wenigstens ein paar Erfolgserlebnisse habe, bin ich schon einen Schritt weiter.

Habe den `try`-Teil mal weggelassen, `from()` gibt mir direkt den Wert und da da meiner Meinung nach immer irgendwas kommt, das gewandelt werden kann, benötige ich da keine Fehlerbehandlung. Oder?


So habe ich jetzt meine 7 Werte. Jetzt muss ich morgen noch rausfinden, wie ich die Rohdaten in für mich brauchbare Daten wandle. Aber heute nicht mehr.

Code: Alles auswählen

#![no_std]
#![no_main]


use esp_hal::clock::CpuClock;
use esp_hal::i2c::master::{I2c, Config, Operation};
use esp_hal::delay::Delay;
use esp_hal::prelude::*;
use esp_backtrace as _;
use esp_println::println;


#[entry]
fn main() -> ! {
    let peripherals = esp_hal::init({
        let mut config = esp_hal::Config::default();
        config.cpu_clock = CpuClock::Clock160MHz;
        config
        });

    let mut i2c = I2c::new(
        peripherals.I2C0,
        Config::default(),
    )
    .with_sda(peripherals.GPIO18)
    .with_scl(peripherals.GPIO19);

    let mut data = [0u8; 14];    
    i2c.transaction(0x68, &mut [
        Operation::Write(&[0x6B, 0]),
        ]).ok();
    let delay = Delay::new();
    loop {
        let results = i2c.transaction(0x68, &mut [
            Operation::Write(&[0x3B]),
            Operation::Read(&mut data)
            ]).and(Ok(data));
        println!("{:#?}", results);
        for pair in results.expect("REASON").chunks(2) {
            let result: u16 = u16::from(pair[0]) << 8 | u16::from(pair[1]);
            println!("{}", result);
        }
        println!("------------------------");
        delay.delay_millis(500);

    }
}
Grüße und Danke
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1473
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo mal wieder,

ich arbeite weiter an diesem Code. Und zwar will ich die Abfrage der Werte etwas benutzerfreundlicher gestalten, aber die Fehlermeldung bringt mich um den Verstand.

Code: Alles auswählen

#![no_std]
#![no_main]


use esp_hal::clock::CpuClock;
use esp_hal::i2c::master::{I2c, Config, Operation};
//use esp_hal::delay::Delay;
//use esp_hal::peripherals::I2C0;
use esp_hal::prelude::*;
use esp_backtrace as _;
use esp_println::println;

#[derive(Debug)]
pub struct Acceleration {
    pub x: u16,
    pub y: u16,
    pub z: u16
}

pub struct MPU6050<I2c> {
    i2c: I2c,
}

impl <I2c: embedded_hal::i2c::I2c> MPU6050<I2c> {
    pub fn new(i2c: I2c) -> Self {
        MPU6050 {
            i2c
        }
    }

    pub fn get_ready(&mut self) {
        self.i2c.transaction(0x68, &mut [
            Operation::Write(&[0x6B, 0]),
            ]).ok();
    }

    fn chain_high_low_bytes(byte: [u8; 2]) -> u16 {
        u16::from(byte[0]) << 8 | u16::from(byte[1])
    }

    pub fn get_acceleration(&mut self) -> Acceleration {
        let mut data: [u8; 6] = [0; 6];
        let _ = self.i2c.transaction(0x68, &mut [
            Operation::Write(&[0x3B]),
            Operation::Read(&mut data)
            ]).and(Ok(data));
        Acceleration {
            x: Self::chain_high_low_bytes([data[0], data[1]]),
            y: Self::chain_high_low_bytes([data[2], data[3]]),
            z: Self::chain_high_low_bytes([data[4], data[5]])
        }
    }

}

#[entry]
fn main() -> ! {
    let peripherals = esp_hal::init({
        let mut config = esp_hal::Config::default();
        config.cpu_clock = CpuClock::Clock160MHz;
        config
        });

    let i2c = I2c::new(
        peripherals.I2C0,
        Config::default(),
    )
    .with_sda(peripherals.GPIO18)
    .with_scl(peripherals.GPIO19);
    let mut mpu6050 = MPU6050::new(i2c);
    mpu6050.get_ready();
    let acc = mpu6050.get_acceleration();
    println!("{:?}", acc);
    loop{}
}

Code: Alles auswählen

[dennis@dennis drone]$ cargo run --release
   Compiling drone v0.1.0 (/home/dennis/PicoDrone/drone)
error[E0308]: mismatched types
   --> src/bin/main.rs:33:13
    |
33  |             Operation::Write(&[0x6B, 0]),
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Operation<'_>`, found a different `Operation<'_>`
    |
    = note: `Operation<'_>` and `Operation<'_>` have similar names, but are actually distinct types
note: `Operation<'_>` is defined in crate `esp_hal`
   --> /home/dennis/.cargo/registry/src/index.crates.io-6f17d22bba15001f/esp-hal-0.22.0/src/i2c/master/mod.rs:127:1
    |
127 | pub enum Operation<'a> {
    | ^^^^^^^^^^^^^^^^^^^^^^
note: `Operation<'_>` is defined in crate `embedded_hal`
   --> /home/dennis/.cargo/registry/src/index.crates.io-6f17d22bba15001f/embedded-hal-1.0.0/src/i2c.rs:305:1
    |
305 | pub enum Operation<'a> {
    | ^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
   --> src/bin/main.rs:44:13
    |
44  |             Operation::Write(&[0x3B]),
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Operation<'_>`, found a different `Operation<'_>`
    |
    = note: `Operation<'_>` and `Operation<'_>` have similar names, but are actually distinct types
note: `Operation<'_>` is defined in crate `esp_hal`
   --> /home/dennis/.cargo/registry/src/index.crates.io-6f17d22bba15001f/esp-hal-0.22.0/src/i2c/master/mod.rs:127:1
    |
127 | pub enum Operation<'a> {
    | ^^^^^^^^^^^^^^^^^^^^^^
note: `Operation<'_>` is defined in crate `embedded_hal`
   --> /home/dennis/.cargo/registry/src/index.crates.io-6f17d22bba15001f/embedded-hal-1.0.0/src/i2c.rs:305:1
    |
305 | pub enum Operation<'a> {
    | ^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `drone` (bin "main") due to 2 previous errors
[dennis@dennis drone]$ 
Irgendwie komme ich mir etwas verarscht vor. Ich habe doch `Operation` explizit von `esp_hal` importiert, wie kommt die Meldung zustande, dass das eine in `embedded_hal` drin ist. Im Code aus dem vorherigen Post, hat das doch auch so funktioniert.
Allgemein bin ich da jetzt schon lange an dem Code dran und bevor ich den PC aus mache, wollte ich das euch noch fragen. Ich hoffe ich übersehe mit meinen glasigen Augen nicht wieder eine Kleinigkeit.

Viele Dank und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
Dennis89
User
Beiträge: 1473
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Morgen,

ach wenn ich natürlich den Typ von `embedded_hal` angebe, sehr geistreich.

Code: Alles auswählen

#![no_std]
#![no_main]


use esp_hal::clock::CpuClock;
use esp_hal::i2c::master::{I2c, Config, Operation};
//use esp_hal::delay::Delay;
use esp_hal::prelude::*;
use esp_backtrace as _;
use esp_println::println;

#[derive(Debug)]
pub struct Acceleration {
    pub x: u16,
    pub y: u16,
    pub z: u16
}

pub struct MPU6050<I2c> {
    i2c: I2c,
}

impl <I2c> MPU6050<I2c> {
    pub fn new(i2c: I2c) -> Self {
        MPU6050 {
            i2c
        }
    }

    pub fn get_ready(&mut self) {
        self.i2c.transaction(0x68, &mut [
            Operation::Write(&[0x6B, 0]),
            ]).ok();
    }

    fn chain_high_low_bytes(byte: [u8; 2]) -> u16 {
        u16::from(byte[0]) << 8 | u16::from(byte[1])
    }

    pub fn get_acceleration(&mut self) -> Acceleration {
        let mut data: [u8; 6] = [0; 6];
        let _ = self.i2c.transaction(0x68, &mut [
            Operation::Write(&[0x3B]),
            Operation::Read(&mut data)
            ]).and(Ok(data));
        Acceleration {
            x: Self::chain_high_low_bytes([data[0], data[1]]),
            y: Self::chain_high_low_bytes([data[2], data[3]]),
            z: Self::chain_high_low_bytes([data[4], data[5]])
        }
    }

}

#[entry]
fn main() -> ! {
    let peripherals = esp_hal::init({
        let mut config = esp_hal::Config::default();
        config.cpu_clock = CpuClock::Clock160MHz;
        config
        });

    let i2c = I2c::new(
        peripherals.I2C0,
        Config::default(),
    )
    .with_sda(peripherals.GPIO18)
    .with_scl(peripherals.GPIO19);
    let mut mpu6050 = MPU6050::new(i2c);
    mpu6050.get_ready();
    let acc = mpu6050.get_acceleration();
    println!("{:?}", acc);
    loop{}
}
Jetzt ist nur `transaction` nicht mehr verfügbar.

Code: Alles auswählen

[dennis@dennis drone]$ cargo run --release
   Compiling drone v0.1.0 (/home/dennis/PicoDrone/drone)
error[E0599]: no method named `transaction` found for type parameter `I2c` in the current scope
  --> src/bin/main.rs:32:18
   |
24 | impl <I2c> MPU6050<I2c> {
   |       --- method `transaction` not found for this type parameter
...
32 |         self.i2c.transaction(0x68, &mut [
   |         ---------^^^^^^^^^^^ method not found in `I2c`
   |
   = help: items from traits can only be used if the type parameter is bounded by the trait
Da bin ich mir jetzt auch noch nicht so ganz schlüssig, lebt hier `i2c` nicht lange genug? Aber dann wäre der Name ja nicht mehr im Speicher und ich könnt gar nicht darauf zugreifen. Hmm nicht so einfach das Thema.

Grüße
Dennis

Edit: Das mit der Lebensdauer ist Quatsch, in der Fehlermeldung steht ja `method not found in I2c`, dann gibt es das Objekt ja auf jeden Fall noch.
"When I got the music, I got a place to go" [Rancid, 1993]
narpfel
User
Beiträge: 677
Registriert: Freitag 20. Oktober 2017, 16:10

Dein Struct `MPU6050` ist generisch mit dem (etwas verwirrend benannten) Typparameter `I2c`. Das ist nicht das in Zeile 6 importierte `I2c`.

Dieses Beispiel hier hat genau das gleiche Problem wie dein Code:

Code: Alles auswählen

fn convert_to_string<T>(something: T) -> String {
    something.to_string()
}

fn main() {
    println!("{}", convert_to_string(42));
}
(Godbolt-Link)

Verstehst du hier, warum du eine Fehlermeldung bekommst und wie die behoben wird? Falls nicht: Kapitel 10 und 10.1 im Rust-Buch beschreiben Generics.

Wieso hast du `MPU6050` generisch gemacht? Vielleicht ist es einfacher, wenn du das alles erstmal ohne Generics schreibst?
Benutzeravatar
Dennis89
User
Beiträge: 1473
Registriert: Freitag 11. Dezember 2020, 15:13

Danke für die Antwort.
Ja ich verstehe, das ein Type `T` nicht zwangsläufig `to_string()` als Methode haben muss und dass das dann so nicht funktioniert. Die Lösung wäre, dass die Funktion einen definierten Typ als Argument erwartet, der die Methode hat.

Ich habe ohne generische Typen begonnen, dann bekam ich die Meldung, das Lifetime-Parameter und mindestens ein generischer Typ fehlt und so hat sich das alles dann in diese Richtung entwickelt. Ich verstehe das mit den Lifetime-Parameter auch nur so ein bisschen im Ansatz. Ich hatte das Kapitel auch noch mal gelesen und auch Erklärungen auf StackOverflow gelesen, aber es hat noch nicht Klick gemacht um es auf ein beliebiges Problem anzuwenden. Das ist für mich nicht greifbar. Das merke ich, weil ich das Kapitel jeden Tag aufs Neue lesen könnte.

Das war der Code ohne generische Typen:

Code: Alles auswählen

#![no_std]
#![no_main]


use esp_hal::clock::CpuClock;
use esp_hal::i2c::master::{I2c, Config, Operation};
//use esp_hal::delay::Delay;
use esp_hal::prelude::*;
use esp_backtrace as _;
use esp_println::println;

#[derive(Debug)]
pub struct Acceleration {
    pub x: u16,
    pub y: u16,
    pub z: u16
}

pub struct MPU6050 {
    i2c: esp_hal::i2c::master::I2c
}

impl MPU6050 {
    pub fn new(i2c: I2c) -> Self {
        MPU6050 {
            i2c
        }
    }

    pub fn get_ready(&mut self) {
        self.i2c.transaction(0x68, &mut [
            Operation::Write(&[0x6B, 0]),
            ]).ok();
    }

    fn chain_high_low_bytes(byte: [u8; 2]) -> u16 {
        u16::from(byte[0]) << 8 | u16::from(byte[1])
    }

    pub fn get_acceleration(&mut self) -> Acceleration {
        let mut data: [u8; 6] = [0; 6];
        let _ = self.i2c.transaction(0x68, &mut [
            Operation::Write(&[0x3B]),
            Operation::Read(&mut data)
            ]).and(Ok(data));
        Acceleration {
            x: Self::chain_high_low_bytes([data[0], data[1]]),
            y: Self::chain_high_low_bytes([data[2], data[3]]),
            z: Self::chain_high_low_bytes([data[4], data[5]])
        }
    }

}

#[entry]
fn main() -> ! {
    let peripherals = esp_hal::init({
        let mut config = esp_hal::Config::default();
        config.cpu_clock = CpuClock::Clock160MHz;
        config
        });

    let i2c = I2c::new(
        peripherals.I2C0,
        Config::default(),
    )
    .with_sda(peripherals.GPIO18)
    .with_scl(peripherals.GPIO19);
    let mut mpu6050 = MPU6050::new(i2c);
    mpu6050.get_ready();
    let acc = mpu6050.get_acceleration();
    println!("{:?}", acc);
    loop{}
}
und das sind dann die Fehlermeldungen:

Code: Alles auswählen

[dennis@dennis drone]$ cargo run --release
   Compiling drone v0.1.0 (/home/dennis/PicoDrone/drone)
error[E0106]: missing lifetime specifier
  --> src/bin/main.rs:20:32
   |
20 |     i2c: esp_hal::i2c::master::I2c
   |                                ^^^ expected named lifetime parameter
   |
help: consider introducing a named lifetime parameter
   |
19 ~ pub struct MPU6050<'a> {
20 ~     i2c: esp_hal::i2c::master::I2c<'a>
   |

error[E0107]: missing generics for struct `I2c`
   --> src/bin/main.rs:20:32
    |
20  |     i2c: esp_hal::i2c::master::I2c
    |                                ^^^ expected at least 1 generic argument
    |
note: struct defined here, with at least 1 generic parameter: `DM`
   --> /home/dennis/.cargo/registry/src/index.crates.io-6f17d22bba15001f/esp-hal-0.22.0/src/i2c/master/mod.rs:272:12
    |
272 | pub struct I2c<'d, DM: Mode, T = AnyI2c> {
    |            ^^^     --
help: add missing generic argument
    |
20  |     i2c: esp_hal::i2c::master::I2c<DM>
    |                                   ++++

error[E0107]: missing generics for struct `I2c`
   --> src/bin/main.rs:24:21
    |
24  |     pub fn new(i2c: I2c) -> Self {
    |                     ^^^ expected at least 1 generic argument
    |
note: struct defined here, with at least 1 generic parameter: `DM`
   --> /home/dennis/.cargo/registry/src/index.crates.io-6f17d22bba15001f/esp-hal-0.22.0/src/i2c/master/mod.rs:272:12
    |
272 | pub struct I2c<'d, DM: Mode, T = AnyI2c> {
    |            ^^^     --
help: add missing generic argument
    |
24  |     pub fn new(i2c: I2c<DM>) -> Self {
    |                        ++++

Some errors have detailed explanations: E0106, E0107.
For more information about an error, try `rustc --explain E0106`.
error: could not compile `drone` (bin "main") due to 3 previous errors
[dennis@dennis drone]$ 
Habe ein mal explizit `esp_hal::i2c::master::I2C` angegeben und ein mal nur `I2c` als Typ, das war nur zum schauen, ob es einen Unterschied machen würde, weil ich alles in Frage gestellt habe.

Wenn ich jetzt anfange die vorgeschlagenen Lifetime Parameter in `pub struct MPU6050` einzutragen endet das in weiteren Meldungen, die ich aber nicht nach vollziehen kann, weil ich schon gar nicht verstehe, ob und wieso `struct` da jetzt den Parameter benötigt und wie sich das dann auf `impl` auswirkt.

Hm ja irgendwie stehe ich wieder bei dem Grundlagenproblem.

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten