Article original : Embedded Rust Programming on Raspberry Pi Zero W
La programmation embarquée en Rust nécessite une toute nouvelle base de connaissances. En utilisant un Raspberry Pi Zero W, vous pouvez rapidement démarrer avec Rust embarqué.
En commençant par un équivalent embarqué de "Hello World", et en progressant vers un traducteur de texte en code Morse, cet article vous guidera à travers le processus.
- Comment installer le Pi
- Comment configurer la compilation croisée
- Comment programmer un "Hello World" embarqué
- Comment compiler le programme en cross-compilation
- Comment transférer le binaire vers le Pi
- Comment se connecter en SSH au Pi
- Comment coder un traducteur de texte en code Morse
- Annexe
Comment installer le Pi
Formater la carte SD
Utilisez Raspberry Pi Imager qui peut être téléchargé depuis la page Web des logiciels Raspberry Pi.

Flashing de la distribution
Une distribution que je suggère est Raspberry Pi OS Lite. Il s'agit d'une distribution headless, ce qui signifie qu'elle ne dispose pas d'une interface graphique.

Configurer le Wifi et SSH

Une fois cela fait, vous pouvez insérer la carte SD dans le Raspberry Pi et le mettre sous tension.
Compléter le circuit
Schéma du circuit

Brochage du Pi
Connectez le négatif à la masse et le positif à la broche BCM 17 comme indiqué ci-dessous :

Le brochage peut être vu ici : https://pinout.xyz/
Comment configurer la compilation croisée
Installer la cible
Utilisez rustup pour installer la cible nécessaire pour votre Raspberry Pi :
my-pc$ rustup add target arm-unknown-linux-gnueabihf
Annexe pour plus d'informations sur les cibles dans Rust.
Spécifier le linker
Téléchargez le dépôt raspberrypi/tools dans un répertoire nommé rpi_tools :
my-pc:~ $ git clone https://github.com/raspberrypi/tools $HOME/rpi_tools
Éditez le fichier ~/.cargo/config en utilisant votre éditeur de texte préféré :
my_pc:~ $ sudo nano ~/.cargo/config
Indiquez à Cargo d'utiliser une version spécifique du linker pour votre cible :
[target.arm-unknown-linux-gnueabihf]
linker = "/rpi_tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc"
Comment programmer un "Hello World" embarqué
Commencez par créer un nouveau projet Rust et ouvrez le fichier main.rs dans votre éditeur de texte préféré :
my-pc:~ $ cargo new blink
my-pc:~ $ cd blink
my-pc:~/blink $ nano src/main.rs
Importez la crate rust_gpiozero et programmez une LED pour qu'elle alterne entre allumée et éteinte toutes les secondes :
use rust_gpiozero::*;
fn main() {
// Créez une nouvelle LED attachée à la broche 17
let mut led = LED::new(17);
led.blink(1.0, 1.0);
led.wait();
}
Assurez-vous d'ajouter la dépendance au fichier Cargo.toml :
[dependencies]
rust-gpiozero = "0.2.1"
Quitter le programme avec succès
Depuis rustc 1.61.0 [1], vous pouvez utiliser la structure std::process::ExitCode pour spécifier le code de statut retourné au parent du processus :
use std::process::ExitCode;
fn main() -> ExitCode {
// ...
if error {
return ExitCode::from(1);
}
ExitCode::SUCCESS
}
Sinon, vous pouvez simplement retourner un Result :
fn main() -> Result<(), std::io::Error> {
// ...
Ok(())
}
Comment compiler le programme en cross-compilation
Construisez une version de votre programme, en ciblant l'architecture requise :
my-pc:~/blink $ cargo build --release --target=arm-unknown-linux-gnueabihf
Comment transférer le binaire vers le Pi
Utilisez scp pour transférer le binaire compilé de votre ordinateur hôte vers le Raspberry Pi via SSH :
my-pc:~/blink $ scp target/arm-unknown-linux-gnueabihf/release/blink pi@192.168.1.199:~/blink
Note : L'IP locale de votre Pi sera probablement différente.
Comment se connecter en SSH au Pi
Connectez-vous en SSH au Raspberry Pi via son adresse IP locale :
my-pc:~ $ ssh pi@192.168.1.199
Exécuter le programme
Depuis le Raspberry Pi, exécutez le binaire compilé :
pi:~ $ ./blink
Comment coder un traducteur de texte en code Morse
Voici un exemple d'application qui lit l'entrée standard ligne par ligne, traduit l'entrée en code Morse et fait clignoter la LED en fonction du code Morse des caractères.
use rust_gpiozero::*;
use std::io::{BufRead, self};
use std::collections::HashMap;
use std::thread::sleep;
use std::time::Duration;
fn main() -> Result<(), std::io::Error> {
println!("Démarrage...\n- Tapez du texte pour le convertir en code Morse\n- Tapez `quit()` pour quitter\n");
// Créez une nouvelle LED attachée à la broche 17
let led = LED::new(17);
/// Durée d'un point en millisecondes
const DOT_DELAY: u64 = 80;
/// Durée d'un trait en millisecondes
const DASH_DELAY: u64 = DOT_DELAY * 3;
/// Délai entre les entrées en millisecondes
const PUSH_DELAY: u64 = DOT_DELAY;
/// Délai entre les lettres en millisecondes
const LETTER_DELAY: u64 = DOT_DELAY * 3;
/// Délai entre les mots en millisecondes
const WORD_DELAY: u64 = DOT_DELAY * 7;
let morse_code_alphabet: HashMap<char, &'static str> =
[
('a', ".-"),
('b', "-..."),
('c', "-.-."),
('d', "-.."),
('e', "."),
('f', "..-."),
('g', "--."),
('h', "...."),
('i', ".."),
('j', ".---"),
('k', "-.-"),
('l', ".-.."),
('m', "--"),
('n', "-."),
('o', "---"),
('p', ".--."),
('q', "--.-"),
('r', ".-."),
('s', "..."),
('t', "-"),
('u', "..-"),
('v', "...-"),
('w', ".--"),
('x', "-..-"),
('y', "-.--"),
('z', "--.."),
('1', ".----"),
('2', "..---"),
('3', "...--"),
('4', "....-"),
('5', "....."),
('6', "-...."),
('7', "--..."),
('8', "---.."),
('9', "----."),
('0', "-----"),
('.', ".-.-.-"),
(',', "--..--"),
('?', "..--.."),
('\'', ".----."),
('!', "-.-.--"),
('/', "-..-."),
('(', "-.--."),
(')', "-.--.-"),
('&', ".-..."),
(':', "---..."),
(';', "-.-.-."),
('=', "-...-"),
('+', ".-.-."),
('-', "-....-"),
('_', "..--.-"),
('"', ".-..-."),
('$', "...-..-"),
('@', ".--.-."),
(' ', " "),
].iter().cloned().collect();
// Lire l'entrée standard ligne par ligne
for line_res in io::stdin().lock().lines() {
let line = line_res?;
if line == "quit()" {
break;
}
// Transformer la ligne en code Morse
let mut morse = String::new();
for c in line.chars() {
if let Some(morse_code_char) = morse_code_alphabet.get(&c) {
morse.push_str(morse_code_char);
// Séparer les caractères avec une virgule
morse.push_str(",");
}
}
// Faire clignoter la LED en fonction des caractères
for c in morse.chars() {
match c {
'.' => {
led.on();
sleep(Duration::from_millis(DOT_DELAY));
led.off();
sleep(Duration::from_millis(PUSH_DELAY));
},
'-' => {
led.on();
sleep(Duration::from_millis(DASH_DELAY));
led.off();
sleep(Duration::from_millis(PUSH_DELAY));
},
',' => {
sleep(Duration::from_millis(LETTER_DELAY));
},
' ' => {
sleep(Duration::from_millis(WORD_DELAY));
},
_ => {
println!("Caractère inconnu : {}", c);
break;
}
}
}
sleep(Duration::from_millis(WORD_DELAY));
}
// Libérer la variable et les ressources associées
led.close();
Ok(())
}
Annexe
Cibles
Dans Rust, la cible est la plateforme (architecture) pour laquelle le programme est compilé. Cargo détecte automatiquement la cible, en fonction de la structure du système de fichiers [2].
Vous pouvez voir la liste des cibles intégrées en exécutant :
rustc --print target-list
# OU
rustup target list
À partir de là, vous pouvez ajouter une nouvelle cible à votre projet en exécutant :
rustup target add <cible>
La cible donnée est souvent sous la forme d'un triplet [3] :
- L'architecture
- Le fournisseur
- Le type de système d'exploitation
- Le type d'environnement
Cela est appelé un 'triplet de cible', car la quatrième partie est optionnelle.