wip display
This commit is contained in:
parent
7546d06a4d
commit
bdd4c935a3
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
target
|
target
|
||||||
result
|
result
|
||||||
mosquitto
|
mosquitto
|
||||||
|
s140_*_softdevice.hex
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"app", "migration",
|
"app", "migration"
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
@ -10,7 +10,7 @@ ciborium = "0.2.2"
|
|||||||
clap = { version = "4.5.4", features = ["derive"] }
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
config = "0.14.0"
|
config = "0.14.0"
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
itertools = "0.12.1"
|
itertools = "0.13.0"
|
||||||
reqwest = { version = "0.12.4", features = ["json", "rustls-tls"], default-features = false }
|
reqwest = { version = "0.12.4", features = ["json", "rustls-tls"], default-features = false }
|
||||||
rumqttc = "0.24.0"
|
rumqttc = "0.24.0"
|
||||||
sea-orm = { workspace = true, features = [ "with-time", "sqlx-sqlite", "sqlx-postgres", "sqlx-mysql", "runtime-tokio-rustls", "macros" ] }
|
sea-orm = { workspace = true, features = [ "with-time", "sqlx-sqlite", "sqlx-postgres", "sqlx-mysql", "runtime-tokio-rustls", "macros" ] }
|
||||||
|
8
display/.cargo/config.toml
Normal file
8
display/.cargo/config.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
runner = "probe-rs run --chip nRF52840_xxAA --allow-erase-all"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
target = "thumbv7em-none-eabi"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "trace"
|
1042
display/Cargo.lock
generated
Normal file
1042
display/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
display/Cargo.toml
Normal file
38
display/Cargo.toml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
[workspace]
|
||||||
|
[package]
|
||||||
|
name = "alert-me-display"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies.embassy-nrf]
|
||||||
|
version = "0.1.0"
|
||||||
|
git = "https://github.com/embassy-rs/embassy.git"
|
||||||
|
rev = "3c52ef60b19468a8700d612699c869f0bdcae339"
|
||||||
|
features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"]
|
||||||
|
|
||||||
|
[dependencies.embassy-executor]
|
||||||
|
version = "0.5.0"
|
||||||
|
git = "https://github.com/embassy-rs/embassy.git"
|
||||||
|
rev = "3c52ef60b19468a8700d612699c869f0bdcae339"
|
||||||
|
features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"]
|
||||||
|
|
||||||
|
[dependencies.embassy-time]
|
||||||
|
version = "0.3.0"
|
||||||
|
git = "https://github.com/embassy-rs/embassy.git"
|
||||||
|
rev = "3c52ef60b19468a8700d612699c869f0bdcae339"
|
||||||
|
features = ["defmt", "defmt-timestamp-uptime"]
|
||||||
|
|
||||||
|
[dependencies.nrf-softdevice]
|
||||||
|
version = "0.1.0"
|
||||||
|
git = "https://github.com/embassy-rs/nrf-softdevice.git"
|
||||||
|
rev = "3c53b8c454cc9331082053033485e713abcadbb5"
|
||||||
|
features = ["defmt", "ble-peripheral", "ble-central", "critical-section-impl", "ble-gatt-server", "nrf52840", "s140"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
defmt = "0.3.6"
|
||||||
|
defmt-rtt = "0.4.0"
|
||||||
|
|
||||||
|
panic-probe = { version = "0.3.1", features = ["print-defmt"] }
|
||||||
|
|
||||||
|
cortex-m = { version = "0.7.7", features = ["inline-asm"] }
|
||||||
|
cortex-m-rt = "0.7.4"
|
12
display/README.md
Normal file
12
display/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
The softdevice blob is not distributed with this repository for licensing
|
||||||
|
reasons (even though it would be possible if my understanding is correct).
|
||||||
|
|
||||||
|
Download the
|
||||||
|
[S140 Softdevice](https://www.nordicsemi.com/Products/Development-software/s140/download)
|
||||||
|
and flash it to your chip.
|
||||||
|
|
||||||
|
_Example command to flash the softdevice_
|
||||||
|
|
||||||
|
```
|
||||||
|
probe-rs download --verify --format hex --chip nRF52840_xxAA ./s140_nrf52_7.3.0_softdevice.hex
|
||||||
|
```
|
35
display/build.rs
Normal file
35
display/build.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//! This build script copies the `memory.x` file from the crate root into
|
||||||
|
//! a directory where the linker can always find it at build time.
|
||||||
|
//! For many projects this is optional, as the linker always searches the
|
||||||
|
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||||
|
//! are using a workspace or have a more complicated build setup, this
|
||||||
|
//! build script becomes required. Additionally, by requesting that
|
||||||
|
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||||
|
//! updating `memory.x` ensures a rebuild of the application with the
|
||||||
|
//! new memory settings.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Put `memory.x` in our output directory and ensure it's
|
||||||
|
// on the linker search path.
|
||||||
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
File::create(out.join("memory.x"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(include_bytes!("memory.x"))
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
|
// By default, Cargo will re-run a build script whenever
|
||||||
|
// any file in the project changes. By specifying `memory.x`
|
||||||
|
// here, we ensure the build script is only re-run when
|
||||||
|
// `memory.x` is changed.
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||||
|
}
|
11
display/memory.x
Normal file
11
display/memory.x
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* NOTE 1 K = 1 KiBi = 1024 bytes */
|
||||||
|
/*
|
||||||
|
FLASH : ORIGIN = 0x00000000, LENGTH = 1024K
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||||
|
*/
|
||||||
|
|
||||||
|
FLASH : ORIGIN = 0x00027000, LENGTH = 868K
|
||||||
|
RAM : ORIGIN = 0x20007b08, LENGTH = 128K
|
||||||
|
}
|
31
display/src/ble/advertisment.rs
Normal file
31
display/src/ble/advertisment.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use defmt::{info, unwrap};
|
||||||
|
use nrf_softdevice::ble::advertisement_builder::{
|
||||||
|
Flag, LegacyAdvertisementBuilder, LegacyAdvertisementPayload, ServiceList, ServiceUuid16,
|
||||||
|
};
|
||||||
|
use nrf_softdevice::ble::peripheral::ConnectableAdvertisement;
|
||||||
|
use nrf_softdevice::ble::{peripheral, Connection};
|
||||||
|
use nrf_softdevice::Softdevice;
|
||||||
|
|
||||||
|
static SCAN_DATA: LegacyAdvertisementPayload = LegacyAdvertisementBuilder::new()
|
||||||
|
.services_128(
|
||||||
|
ServiceList::Complete,
|
||||||
|
&[0x9e7312e0_2354_11eb_9f10_fbc30a62cf38_u128.to_le_bytes()],
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
pub async fn run(sd: &Softdevice, device_name: &str) -> Connection {
|
||||||
|
let config = peripheral::Config::default();
|
||||||
|
|
||||||
|
let adv_data = LegacyAdvertisementBuilder::new()
|
||||||
|
.flags(&[Flag::GeneralDiscovery, Flag::LE_Only])
|
||||||
|
.services_16(ServiceList::Complete, &[ServiceUuid16::BATTERY])
|
||||||
|
.full_name(device_name)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let adv = ConnectableAdvertisement::ScannableUndirected {
|
||||||
|
adv_data: &adv_data,
|
||||||
|
scan_data: &SCAN_DATA,
|
||||||
|
};
|
||||||
|
info!("advertising started");
|
||||||
|
unwrap!(peripheral::advertise_connectable(sd, adv, &config).await)
|
||||||
|
}
|
5
display/src/ble/battery_service.rs
Normal file
5
display/src/ble/battery_service.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#[nrf_softdevice::gatt_service(uuid = "180f")]
|
||||||
|
pub struct BatteryService {
|
||||||
|
#[characteristic(uuid = "2a19", read, notify)]
|
||||||
|
pub battery_level: u8,
|
||||||
|
}
|
24
display/src/ble/gatt.rs
Normal file
24
display/src/ble/gatt.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use crate::ble::battery_service::BatteryService;
|
||||||
|
use crate::ble::battery_service::BatteryServiceEvent;
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
use nrf_softdevice::ble::gatt_server;
|
||||||
|
use nrf_softdevice::ble::Connection;
|
||||||
|
use nrf_softdevice::ble::DisconnectedError;
|
||||||
|
|
||||||
|
#[nrf_softdevice::gatt_server]
|
||||||
|
pub struct Server {
|
||||||
|
pub bas: BatteryService,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(conn: &Connection, server: &Server) -> DisconnectedError {
|
||||||
|
info!("gatt started");
|
||||||
|
gatt_server::run(conn, server, |e| match e {
|
||||||
|
ServerEvent::Bas(e) => match e {
|
||||||
|
BatteryServiceEvent::BatteryLevelCccdWrite { notifications } => {
|
||||||
|
info!("battery notifications: {}", notifications)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
58
display/src/ble/softdevice.rs
Normal file
58
display/src/ble/softdevice.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use core::mem;
|
||||||
|
|
||||||
|
use crate::ble::gatt::Server;
|
||||||
|
use defmt::{info, unwrap};
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use nrf_softdevice::{raw, Softdevice};
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn softdevice_task(sd: &'static Softdevice) -> ! {
|
||||||
|
sd.run().await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure(device_name: &str) -> nrf_softdevice::Config {
|
||||||
|
nrf_softdevice::Config {
|
||||||
|
clock: Some(raw::nrf_clock_lf_cfg_t {
|
||||||
|
source: raw::NRF_CLOCK_LF_SRC_RC as u8,
|
||||||
|
rc_ctiv: 16,
|
||||||
|
rc_temp_ctiv: 2,
|
||||||
|
accuracy: raw::NRF_CLOCK_LF_ACCURACY_500_PPM as u8,
|
||||||
|
}),
|
||||||
|
conn_gap: Some(raw::ble_gap_conn_cfg_t {
|
||||||
|
conn_count: 6,
|
||||||
|
event_length: 24,
|
||||||
|
}),
|
||||||
|
conn_gatt: Some(raw::ble_gatt_conn_cfg_t { att_mtu: 256 }),
|
||||||
|
gatts_attr_tab_size: Some(raw::ble_gatts_cfg_attr_tab_size_t {
|
||||||
|
attr_tab_size: raw::BLE_GATTS_ATTR_TAB_SIZE_DEFAULT,
|
||||||
|
}),
|
||||||
|
gap_role_count: Some(raw::ble_gap_cfg_role_count_t {
|
||||||
|
adv_set_count: 1,
|
||||||
|
periph_role_count: 3,
|
||||||
|
central_role_count: 3,
|
||||||
|
central_sec_count: 0,
|
||||||
|
_bitfield_1: raw::ble_gap_cfg_role_count_t::new_bitfield_1(0),
|
||||||
|
}),
|
||||||
|
gap_device_name: Some(raw::ble_gap_cfg_device_name_t {
|
||||||
|
p_value: device_name.as_bytes().as_ptr() as *const u8 as _,
|
||||||
|
current_len: 9,
|
||||||
|
max_len: 9,
|
||||||
|
write_perm: unsafe { mem::zeroed() },
|
||||||
|
_bitfield_1: raw::ble_gap_cfg_device_name_t::new_bitfield_1(
|
||||||
|
raw::BLE_GATTS_VLOC_STACK as u8,
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run<'a>(spawner: Spawner, device_name: &str) -> (&'a Softdevice, Server) {
|
||||||
|
let config = configure(device_name);
|
||||||
|
|
||||||
|
let sd = Softdevice::enable(&config);
|
||||||
|
let server = unwrap!(Server::new(sd));
|
||||||
|
unwrap!(spawner.spawn(softdevice_task(sd)));
|
||||||
|
info!("softdevice started");
|
||||||
|
|
||||||
|
(sd, server)
|
||||||
|
}
|
36
display/src/main.rs
Normal file
36
display/src/main.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use ble::gatt::Server;
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use embassy_nrf as _;
|
||||||
|
use nrf_softdevice::Softdevice;
|
||||||
|
use panic_probe as _;
|
||||||
|
|
||||||
|
use defmt::{info, unwrap};
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
|
||||||
|
pub mod ble {
|
||||||
|
pub mod advertisment;
|
||||||
|
pub mod battery_service;
|
||||||
|
pub mod gatt;
|
||||||
|
pub mod softdevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEVICE_NAME: &str = "alert-me display";
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn ble(spawner: Spawner) {
|
||||||
|
let (sd, server) = ble::softdevice::run(spawner, DEVICE_NAME);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let conn = ble::advertisment::run(sd, DEVICE_NAME).await;
|
||||||
|
let error = ble::gatt::run(&conn, &server).await;
|
||||||
|
info!("gatt_server run exited with error: {:?}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
unwrap!(spawner.spawn(ble(spawner)));
|
||||||
|
}
|
11
flake.nix
11
flake.nix
@ -69,16 +69,23 @@
|
|||||||
system:
|
system:
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { inherit system; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
rust = fenix.packages.${system}.stable;
|
# rust = fenix.packages.${system}.stable;
|
||||||
|
rust =
|
||||||
|
with fenix.packages.${system};
|
||||||
|
fromToolchainFile {
|
||||||
|
file = ./rust-toolchain.toml;
|
||||||
|
sha256 = "sha256-iUnN1Tn3SDUo5JvS1QZjjvA2adh7URLeQyXnQHYjCik=";
|
||||||
|
};
|
||||||
in
|
in
|
||||||
with pkgs;
|
with pkgs;
|
||||||
{
|
{
|
||||||
devShells.default = mkShell {
|
devShells.default = mkShell {
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
mosquitto
|
mosquitto
|
||||||
rust.toolchain
|
rust
|
||||||
rust-analyzer
|
rust-analyzer
|
||||||
sea-orm-cli
|
sea-orm-cli
|
||||||
|
probe-rs
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
5
rust-toolchain.toml
Normal file
5
rust-toolchain.toml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
|
components = [ "rustfmt" ]
|
||||||
|
targets = [ "thumbv7em-none-eabi", "thumbv7em-none-eabihf" ]
|
||||||
|
profile = "minimal"
|
Loading…
Reference in New Issue
Block a user