Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Пишем код на Rust для погодной панели на e-Paper с ESP32

Давайте сделаем с e-ink дисплеем что-нибудь поинтереснее, чем вывод статичного текста или изображений. Мы создадим простую метеостанцию, которая получает актуальные данные о погоде из интернета по HTTP API и обновляет дисплей свежей информацией. Это что-то вроде программы «Hello, World» для e-paper.

Предварительные требования

Мы будем использовать API сайта «openweathermap.org», чтобы получать данные о погоде. Сайт выдаёт бесплатный API-ключ с лимитом 1000 запросов в день, этого должно хватить. Зайдите на сайт, зарегистрируйте бесплатный аккаунт, и вы получите собственный API-ключ.

Создание проекта с помощью esp-generate

Чтобы создать проект, используйте команду esp-generate. Выполните:

esp-generate --chip esp32 weather-station

Откроется экран с вопросом о выборе опций.

  • Сначала выберите опцию «Enable unstable HAL features.»
  • Выберите опцию «Enable allocations via the esp-alloc crate.»
  • Затем можно включить «Enable Wi-Fi via esp-radio crate.»
  • Выберите опцию «Adds embassy framework support».

Сохраните настройки, нажав на клавиатуре «s».

Зависимости

Обновите embassy-net и добавьте feature «dns»:

embassy-net = { version = "0.7.1", features = [
  "defmt",
  "dhcpv4",
  "medium-ethernet",
  "tcp",
  "udp",
  "dns",
] }

Дополнительные зависимости:

Вкратце расскажу о новых зависимостях. Крейты embedded-graphics и tinybmp мы уже использовали много раз, в том числе в прошлой главе, так что вы должны быть с ними хорошо знакомы. Мы будем использовать их, чтобы рисовать фигуры и загружать BMP-файлы.

embedded-graphics = "0.8.1"
tinybmp = "0.6.0"
profont = "0.7.0"
chrono = { version = "0.4.40", default-features = false, features = ["serde"] }
serde = { version = "1.0.219", default-features = false, features = ["derive"] }
serde-json-core = "0.6.0"
serde_repr = "0.1.20"

epd-waveshare = { features = [
  "graphics",
], git = "https://github.com/ImplFerris/epd-waveshare" }

embedded-hal-bus = { version = "0.3" }

heapless = { version = "0.9.2", features = ["serde"] }

reqwless = { version = "0.13.0", default-features = false, features = [
  "embedded-tls",
  "alloc",
] }
  • profont: крейт предоставляет моноширинный программистский шрифт ProFont для embedded-graphics. Мы используем его, потому что для погодной панели нужен шрифт немного большего размера.
  • chrono: для работы с датой и временем. Мы будем использовать его, чтобы десериализовать текущую дату и время, полученные из API.
  • serde: фреймворк для сериализации и десериализации структур данных Rust.
  • serde_json_core: для десериализации JSON в структуру Rust мы используем этот крейт. Он создан специально для окружений no_std, а обычно вместо него применяют serde_json.
  • serde_repr: крейт позволяет сериализовать и десериализовать перечисления по их числовым значениям, что упрощает сопоставление числовых кодов погодных условий из API (например, 200 или 201) с вариантами перечисления Rust.
  • reqwless: мы уже использовали этот крейт — это HTTP-клиент, который работает в окружении no_std. С его помощью будем отправлять запросы к API и получать ответы.
  • epd-waveshare: простой драйвер для e-Ink дисплеев Waveshare по SPI. Оригинальный крейт не работал должным образом с 1,54-дюймовым вариантом, поэтому мне пришлось сделать форк и пропатчить код. Это скорее обходное решение, чем полноценное исправление, поэтому я пока не отправлял PR в оригинальный репозиторий. Пока будем использовать форк.

Структура проекта

Это общая структура проекта, и мы пройдём её по шагам.

├── build.rs
├── src
│   ├── bin
│   │   └── main.rs
│   ├── ca_cert.pem
│   ├── dashboard.rs
│   ├── icons
│   │   ├── air.bmp
│   │   ├── ...bmp
│   │   └── ...bmp
│   ├── icons.rs
│   ├── lib.rs
│   ├── weather.rs
│   └── wifi.rs
├── ...

Так как глава получится большой, рекомендую сверяться с готовым проектом здесь. Там могут быть импорты, которые я не буду описывать в руководстве, и другие второстепенные детали.

Модуль lib

В lib.rs мы объявляем подмодули вместе с вспомогательным макросом, который использовали в разделе о Wi-Fi. Этот макрос резервирует память на этапе компиляции для значения static, но позволяет инициализировать его во время выполнения.

#![allow(unused)]
#![no_std]
fn main() {
pub mod dashboard;
pub mod icons;
pub mod weather;
pub mod wifi;

#[macro_export]
macro_rules! mk_static {
    ($t:ty,$val:expr) => {{
        static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
        #[deny(unused_attributes)]
        let x = STATIC_CELL.uninit().write(($val));
        x
    }};
}
}