embassy-st7789v-plot 0.1.1

Moteur de tracé de graphiques cartésiens (X, Y) adaptatifs et configurables pour écrans ST7789V,ring buffer, axes statiques, grille, zéro allocation
Documentation

embassy-st7789v-plot

Moteur de tracé de graphiques cartésiens (X, Y) adaptatifs et configurables pour écrans TFT LCD ST7789V 240×320, construit au-dessus de embassy-st7789v.

🎯 Caractéristiques

  • #![no_std] + #![forbid(unsafe_code)] : Entièrement sûr et embarqué
  • Zéro allocation heap : Buffers statiques uniquement (ring buffer fixe)
  • Ring buffer circulaire : Jusqu'à 240 points (limite physique écran 240px)
  • Axes configurables : Graduations statiques avec pas personnalisable
  • Grille adaptative : Grille horizontale/verticale avec labels
  • API asynchrone : Intégration complète Embassy pour non-bloquant
  • Protection des bordures : Labels de graduations restent toujours visibles
  • Rendu ligne Bresenham : Courbes lisses entre points de données

Changelog

Voir CHANGELOG.md pour l'historique des versions et modifications.


📦 Installation

[dependencies]
embassy-st7789v-plot = "0.1.1"
embassy-st7789v = "0.1"
embedded-hal = "1.0"
embedded-hal-async = "1.0"

🚀 Utilisation rapide

Exemple basique : Single plot

use embassy_st7789v::{Color, St7789v, NoPin};
use embassy_st7789v_plot::{Graphics, AxisConfig, PlotConfig, LineChart};

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    // Initialiser le display
    let mut display: St7789v<_, _, NoPin> = /* ... */;

    // Créer les axes
    let x_axis = AxisConfig::new(0.0, 10.0, 2.0, b"Time (s)");
    let y_axis = AxisConfig::new(0.0, 100.0, 20.0, b"Temperature (C)");

    // Configurer le graphique
    let config = PlotConfig {
        x: 10,
        y: 10,
        width: 220,
        height: 200,
        margin_left: 40,      // Pour labels Y
        margin_right: 10,
        margin_top: 10,
        margin_bottom: 30,    // Pour labels X
        x_axis,
        y_axis,
        bg_color: Color::BLACK,
        line_color: Color::GREEN,
        axis_color: Color::WHITE,
        grid_color: Color::from_rgb(64, 64, 64),
        text_color: Color::WHITE,
        label_color: Color::CYAN,
    };

    // Créer le gestionnaire de graphique (100 points max)
    let mut chart: LineChart<100> = LineChart::new(config);

    // Ajouter des données
    chart.push(45.2);
    chart.push(47.8);
    chart.push(52.1);
    chart.push(48.9);

    // Afficher
    let mut gfx = Graphics::new_no_rst(&mut display);
    chart.render(&mut gfx).await;
}

Configuration des axes

// Axe X : temps de 0 à 60s, graduations tous les 10s
let time_axis = AxisConfig::new(0.0, 60.0, 10.0, b"Time (s)");

// Axe Y : température -10 à +50°C, graduations tous les 10°C
let temp_axis = AxisConfig::new(-10.0, 50.0, 10.0, b"Temp (C)");

// Axe Y : tensions 0 à 3.3V, graduations tous les 0.5V
let voltage_axis = AxisConfig::new(0.0, 3.3, 0.5, b"U (V)");

// Nombre de graduations affiché
assert_eq!(time_axis.tick_count(), 7);  // 0, 10, 20, 30, 40, 50, 60

Validation de configuration

let axis = AxisConfig::new(0.0, 10.0, 0.5, b"X");
assert!(axis.is_valid());  // step > 0 && end > start

let invalid = AxisConfig::new(10.0, 0.0, 1.0, b"X");
assert!(!invalid.is_valid());  // end <= start

🎨 Schéma de positionnement

┌─────────────────────────────────┐  y
│ (x, y)                          │
│ ┌──────────────────────────┐    │
│ │  Label Y (amplitude)     │    │ margin_top
│ │  ┌────────────────────┐  │    │
│ │  │  100  ┼─────────┐  │  │    │
│ │  │   80  │  •     │  │  │    │
│ │  │   60  │    •   │  │  │    │
│ │  │   40  │      •–┤  │  │    │
│ │  │   20  │        │  │  │    │
│ │  │    0  └────────┘  │  │    │
│ │  │       0  2  4  6  8 10    │ margin_bottom
│ │  │       Time (s)            │
│ │  └────────────────────────   │
│ └──────────────────────────────┘
  ↑                              ↑
margin_left              margin_right

📊 Structure de données

Ring buffer (historique circulaire)

Stockage interne : array[100]
head = 3, count = 100 (buffer plein)

data[0] = valeur 97e
data[1] = valeur 98e
data[2] = valeur 99e  ← head (prochaine écriture)
data[3] = valeur 1ère  ← oldest (plus ancienne)
data[4] = valeur 2e
...

Workflow de rendu

  1. Fond → Rectangle de fond couleur (bg_color)
  2. Grille Y → Lignes horizontales + labels Y
  3. Grille X → Lignes verticales + labels X
  4. Labels des axes → Texte des titres
  5. Bordures → Cadre (axis_color)
  6. Courbe → Lignes Bresenham reliant les points

🔧 Cas d'usage

Oscilloscope 1 canal

let config = PlotConfig {
    x: 0, y: 0, width: 240, height: 320,
    margin_left: 40, margin_right: 10,
    margin_top: 10, margin_bottom: 30,
    x_axis: AxisConfig::new(0.0, 240.0, 30.0, b"Samples"),
    y_axis: AxisConfig::new(-5.0, 5.0, 1.0, b"Voltage (V)"),
    // ... colors ...
};
let mut oscilloscope: LineChart<240> = LineChart::new(config);

Moniteur de température

let config = PlotConfig {
    x: 10, y: 10, width: 220, height: 150,
    margin_left: 45, margin_right: 15,
    margin_top: 15, margin_bottom: 30,
    x_axis: AxisConfig::new(0.0, 120.0, 20.0, b"Time (min)"),
    y_axis: AxisConfig::new(15.0, 35.0, 5.0, b"T (°C)"),
    // ... colors ...
};
let mut temp_chart: LineChart<120> = LineChart::new(config);

Graphique de pression

let config = PlotConfig {
    x: 5, y: 5, width: 230, height: 310,
    margin_left: 35, margin_right: 5,
    margin_top: 5, margin_bottom: 30,
    x_axis: AxisConfig::new(0.0, 1000.0, 100.0, b"Pa"),
    y_axis: AxisConfig::new(900.0, 1050.0, 30.0, b"P (hPa)"),
    // ... colors ...
};
let mut pressure_chart: LineChart<100> = LineChart::new(config);

🛠️ API complète

LineChart<N>

Méthode Description
new(config) Crée un nouveau graphique
push(value) Ajoute une valeur à l'historique
clear() Efface l'historique
config() Retourne la configuration
render(gfx) Affiche le graphique (async)

AxisConfig

Méthode Description
new(start, end, step, label) Crée une config d'axe
is_valid() Vérifie cohérence (step > 0 && end > start)
tick_count() Nombre de graduations affiché

Graphics<'a, SPI, DC, RST>

Méthode Description
new(display) Crée contexte avec RST
new_no_rst(display) Crée contexte sans RST
pixel(x, y, color) Trace pixel (async)

Fonction globale

Fonction Description
line(gfx, x0, y0, x1, y1, color) Trace ligne Bresenham (async)

⚠️ Limitations

  • Nombre de points : Maximum 240 (largeur écran ST7789V)
  • Précision : Axes en virgule flottante, pixel en entier
  • Pas adaptatif : Les graduations sont statiques (pas de zoom automatique)
  • Labels : Seulement texte ASCII sur Y-axis, valeurs flottantes sur graduations

📝 Exemple complet avec boucle acquisition

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let mut display = /* init ST7789V */;
    
    let config = PlotConfig { /* ... */ };
    let mut chart: LineChart<200> = LineChart::new(config);
    let mut gfx = Graphics::new_no_rst(&mut display);

    // Boucle d'acquisition
    loop {
        // Lire capteur
        let value = sensor.read().await;
        chart.push(value);

        // Afficher tous les 100ms
        Timer::after(Duration::from_millis(100)).await;
        chart.render(&mut gfx).await;
    }
}

�📜 License

GPL-2.0-or-later

Copyright (C) 2026 Jorge Andre Castro


🔗 Dépendances