Initial working exampel of test console
This commit is contained in:
@ -18,7 +18,7 @@ debug = true # Symbols are nice and they don't increase the size on Flash
|
|||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std", "embassy", "esp-idf-svc/native"]
|
default = ["std", "esp-idf-svc/native"]
|
||||||
|
|
||||||
pio = ["esp-idf-svc/pio"]
|
pio = ["esp-idf-svc/pio"]
|
||||||
std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
|
std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
|
||||||
@ -33,6 +33,17 @@ esp-idf-svc = { version = "0.49", default-features = false }
|
|||||||
esp32-nimble = "0.7.0"
|
esp32-nimble = "0.7.0"
|
||||||
esp-idf-hal = "0.44.1"
|
esp-idf-hal = "0.44.1"
|
||||||
esp-idf-sys = "0.35.0"
|
esp-idf-sys = "0.35.0"
|
||||||
|
embedded-cli = "0.2.1"
|
||||||
|
embedded-io = "0.6.1"
|
||||||
|
anyhow = "1.0.86"
|
||||||
|
futures-lite = "2.3.0"
|
||||||
|
async-executor = "1.13.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
embuild = "0.32.0"
|
embuild = "0.32.0"
|
||||||
|
|
||||||
|
# Cargo udeps can check for unused dependencies; but if there is a dependency we want it to ignore put it here.
|
||||||
|
[package.metadata.cargo-udeps.ignore]
|
||||||
|
#normal = []
|
||||||
|
#development = []
|
||||||
|
#build = []
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K)
|
# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K)
|
||||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000
|
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000
|
||||||
|
|
||||||
|
# USB drivers from the ESP IDF, to enable stdin. First one is for sx chips, second is for cx chips (risc)
|
||||||
|
CONFIG_ESP_CONSOLE_USB_CDC=y # for s, s3, etc. chips
|
||||||
|
# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y # For RISC based c3 chips
|
||||||
|
|
||||||
# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
|
# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
|
||||||
# This allows to use 1 ms granuality for thread sleeps (10 ms by default).
|
# This allows to use 1 ms granuality for thread sleeps (10 ms by default).
|
||||||
#CONFIG_FREERTOS_HZ=1000
|
CONFIG_FREERTOS_HZ=1000
|
||||||
|
|
||||||
# Workaround for https://github.com/espressif/esp-idf/issues/7631
|
# Workaround for https://github.com/espressif/esp-idf/issues/7631
|
||||||
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n
|
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n
|
||||||
|
|||||||
83
gem-remotes-esp32/src/ble_server.rs
Normal file
83
gem-remotes-esp32/src/ble_server.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
|
||||||
|
|
||||||
|
// Example for reference; not ready to use at all.
|
||||||
|
|
||||||
|
use esp32_nimble::{enums::*, uuid128, BLEAdvertisementData, BLEDevice, NimbleProperties};
|
||||||
|
use esp_idf_hal::delay::FreeRtos;
|
||||||
|
use esp_idf_sys as _;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
esp_idf_sys::link_patches();
|
||||||
|
|
||||||
|
// Bind the log crate to the ESP Logging facilities
|
||||||
|
esp_idf_svc::log::EspLogger::initialize_default();
|
||||||
|
|
||||||
|
// Take ownership of device
|
||||||
|
let ble_device = BLEDevice::take();
|
||||||
|
|
||||||
|
// Obtain handle for peripheral advertiser
|
||||||
|
let ble_advertiser = ble_device.get_advertising();
|
||||||
|
|
||||||
|
// Configure Device Security
|
||||||
|
ble_device
|
||||||
|
.security()
|
||||||
|
.set_auth(AuthReq::all())
|
||||||
|
.set_passkey(123456)
|
||||||
|
.set_io_cap(SecurityIOCap::DisplayOnly)
|
||||||
|
.resolve_rpa();
|
||||||
|
|
||||||
|
// Obtain handle for server
|
||||||
|
let server = ble_device.get_server();
|
||||||
|
|
||||||
|
// Define server connect behaviour
|
||||||
|
server.on_connect(|server, clntdesc| {
|
||||||
|
// Print connected client data
|
||||||
|
println!("{:?}", clntdesc);
|
||||||
|
// Update connection parameters
|
||||||
|
server
|
||||||
|
.update_conn_params(clntdesc.conn_handle(), 24, 48, 0, 60)
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Define server disconnect behaviour
|
||||||
|
server.on_disconnect(|_desc, _reason| {
|
||||||
|
println!("Disconnected, back to advertising");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a service with custom UUID
|
||||||
|
let my_service = server.create_service(uuid128!("9b574847-f706-436c-bed7-fc01eb0965c1"));
|
||||||
|
|
||||||
|
// Create a characteristic to associate with created service
|
||||||
|
let my_service_characteristic = my_service.lock().create_characteristic(
|
||||||
|
uuid128!("681285a6-247f-48c6-80ad-68c3dce18585"),
|
||||||
|
NimbleProperties::READ | NimbleProperties::READ_ENC,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Modify characteristic value
|
||||||
|
my_service_characteristic.lock().set_value(b"Start Value");
|
||||||
|
|
||||||
|
// Configure Advertiser Data
|
||||||
|
ble_advertiser
|
||||||
|
.lock()
|
||||||
|
.set_data(
|
||||||
|
BLEAdvertisementData::new()
|
||||||
|
.name("ESP32 Server")
|
||||||
|
.add_service_uuid(uuid128!("9b574847-f706-436c-bed7-fc01eb0965c1")),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Start Advertising
|
||||||
|
ble_advertiser.lock().start().unwrap();
|
||||||
|
|
||||||
|
// (Optional) Print dump of local GATT table
|
||||||
|
// server.ble_gatts_show_local();
|
||||||
|
|
||||||
|
// Init a value to pass to characteristic
|
||||||
|
let mut val = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
FreeRtos::delay_ms(1000);
|
||||||
|
my_service_characteristic.lock().set_value(&[val]).notify();
|
||||||
|
val = val.wrapping_add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,81 +1,54 @@
|
|||||||
|
//use esp_idf_hal::peripherals::Peripherals;
|
||||||
|
|
||||||
|
use esp_idf_svc::timer::EspTaskTimerService;
|
||||||
|
use core::time::Duration;
|
||||||
|
use log::{info, error};
|
||||||
|
use anyhow::Result;
|
||||||
|
use async_executor::Executor;
|
||||||
|
use futures_lite::future;
|
||||||
|
|
||||||
use esp32_nimble::{enums::*, uuid128, BLEAdvertisementData, BLEDevice, NimbleProperties};
|
mod test_console;
|
||||||
use esp_idf_hal::delay::FreeRtos;
|
|
||||||
use esp_idf_sys as _;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
esp_idf_sys::link_patches();
|
// Do basic initialization
|
||||||
|
esp_idf_svc::sys::link_patches();
|
||||||
// Bind the log crate to the ESP Logging facilities
|
|
||||||
esp_idf_svc::log::EspLogger::initialize_default();
|
esp_idf_svc::log::EspLogger::initialize_default();
|
||||||
|
info!("Logging initialized");
|
||||||
|
|
||||||
// Take ownership of device
|
//let peripherals = Peripherals::take().unwrap();
|
||||||
let ble_device = BLEDevice::take();
|
|
||||||
|
|
||||||
// Obtain handle for peripheral advertiser
|
match future::block_on(main_loop()) {
|
||||||
let ble_advertiser = ble_device.get_advertising();
|
Ok(_) => {error!("Exited main loop normally, but this should be impossible.")}
|
||||||
|
Err(e) => {error!("Exited main loop with error {}", e)}
|
||||||
|
};
|
||||||
|
|
||||||
// Configure Device Security
|
}
|
||||||
ble_device
|
|
||||||
.security()
|
|
||||||
.set_auth(AuthReq::all())
|
|
||||||
.set_passkey(123456)
|
|
||||||
.set_io_cap(SecurityIOCap::DisplayOnly)
|
|
||||||
.resolve_rpa();
|
|
||||||
|
|
||||||
// Obtain handle for server
|
async fn test_loop() -> Result<()> {
|
||||||
let server = ble_device.get_server();
|
let timer_service = EspTaskTimerService::new()?;
|
||||||
|
|
||||||
// Define server connect behaviour
|
|
||||||
server.on_connect(|server, clntdesc| {
|
|
||||||
// Print connected client data
|
|
||||||
println!("{:?}", clntdesc);
|
|
||||||
// Update connection parameters
|
|
||||||
server
|
|
||||||
.update_conn_params(clntdesc.conn_handle(), 24, 48, 0, 60)
|
|
||||||
.unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Define server disconnect behaviour
|
|
||||||
server.on_disconnect(|_desc, _reason| {
|
|
||||||
println!("Disconnected, back to advertising");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a service with custom UUID
|
|
||||||
let my_service = server.create_service(uuid128!("9b574847-f706-436c-bed7-fc01eb0965c1"));
|
|
||||||
|
|
||||||
// Create a characteristic to associate with created service
|
|
||||||
let my_service_characteristic = my_service.lock().create_characteristic(
|
|
||||||
uuid128!("681285a6-247f-48c6-80ad-68c3dce18585"),
|
|
||||||
NimbleProperties::READ | NimbleProperties::READ_ENC,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Modify characteristic value
|
|
||||||
my_service_characteristic.lock().set_value(b"Start Value");
|
|
||||||
|
|
||||||
// Configure Advertiser Data
|
|
||||||
ble_advertiser
|
|
||||||
.lock()
|
|
||||||
.set_data(
|
|
||||||
BLEAdvertisementData::new()
|
|
||||||
.name("ESP32 Server")
|
|
||||||
.add_service_uuid(uuid128!("9b574847-f706-436c-bed7-fc01eb0965c1")),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Start Advertising
|
|
||||||
ble_advertiser.lock().start().unwrap();
|
|
||||||
|
|
||||||
// (Optional) Print dump of local GATT table
|
|
||||||
// server.ble_gatts_show_local();
|
|
||||||
|
|
||||||
// Init a value to pass to characteristic
|
|
||||||
let mut val = 0;
|
|
||||||
|
|
||||||
|
let mut async_timer = timer_service.timer_async()?;
|
||||||
loop {
|
loop {
|
||||||
FreeRtos::delay_ms(1000);
|
async_timer.after(Duration::from_secs(2)).await?;
|
||||||
my_service_characteristic.lock().set_value(&[val]).notify();
|
|
||||||
val = val.wrapping_add(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn main_loop() -> Result<()> {
|
||||||
|
info!("Entering main loop");
|
||||||
|
|
||||||
|
let executor = Executor::new();
|
||||||
|
let mut tasks:Vec<_> = Vec::new();
|
||||||
|
|
||||||
|
//Queueu up our async tasks
|
||||||
|
tasks.push(executor.spawn(test_console::start_cli()));
|
||||||
|
tasks.push(executor.spawn(test_loop()));
|
||||||
|
|
||||||
|
//Once we have all our tasks, await on them all to run them in parallel.
|
||||||
|
for task in tasks {
|
||||||
|
executor.run(task).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("exiting main loop");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
195
gem-remotes-esp32/src/test_console.rs
Normal file
195
gem-remotes-esp32/src/test_console.rs
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
// === Constants ==============================================================
|
||||||
|
const MAX_COMMAND_SIZE: usize = 64; // in bytes; we want to test wifi ssid which
|
||||||
|
// can be 32 bytes.
|
||||||
|
const MAX_HISTORY_SIZE: usize = 128; // in bytes
|
||||||
|
const SLEEP_TIME_MS: u64 = 10; // should be > 1/CONFIG_FREERTOS_HZ in sdkconfig
|
||||||
|
// currently this is 1ms, which is also about as
|
||||||
|
// long as it would take uart buffers to overflow
|
||||||
|
// at maximum rate. But this is intended for
|
||||||
|
// human input only. It's noticeably laggy at 50.
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
use ::{
|
||||||
|
anyhow::Result,
|
||||||
|
embedded_cli::{
|
||||||
|
cli::{CliBuilder, CliHandle},
|
||||||
|
Command,
|
||||||
|
},
|
||||||
|
esp_idf_svc::timer::EspTaskTimerService,
|
||||||
|
std::{
|
||||||
|
convert::Infallible,
|
||||||
|
io::{stdin, stdout, Read, Write},
|
||||||
|
//time::Duration, // could also use core::time::Duration?
|
||||||
|
},
|
||||||
|
core::time::Duration,
|
||||||
|
log::{info, warn},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Command)]
|
||||||
|
pub enum Menu<'a> {
|
||||||
|
/// Send a button press: Up
|
||||||
|
ButtonUp,
|
||||||
|
|
||||||
|
/// Send a button press: Down
|
||||||
|
ButtonDown,
|
||||||
|
|
||||||
|
/// Send a button press: Auto toggle on/off
|
||||||
|
ButtonAuto,
|
||||||
|
|
||||||
|
/// Send a button press: Pair/forget
|
||||||
|
ButtonPair,
|
||||||
|
|
||||||
|
/// Send a bluetooth characteristic: Up
|
||||||
|
BluetoothUp {
|
||||||
|
/// 0 for not pressed, 1 for pressed
|
||||||
|
data: u8,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Send a bluetooth characteristic: Down
|
||||||
|
BluetoothDown {
|
||||||
|
/// 0 for not pressed, 1 for pressed
|
||||||
|
data: u8,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Send a bluetooth characteristic: Stop
|
||||||
|
BluetoothStop {
|
||||||
|
/// 0 for not pressed, 1 for pressed
|
||||||
|
data: u8,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Send a bluetooth characteristic: Learn
|
||||||
|
BluetoothLearn {
|
||||||
|
/// 0 for not pressed, 1 for pressed
|
||||||
|
data: u8,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Send a bluetooth characteristic: Auto
|
||||||
|
BluetoothAuto {
|
||||||
|
/// 0 for not pressed, 1 for pressed
|
||||||
|
data: u8,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Send a bluetooth characteristic: Limits
|
||||||
|
BluetoothTopLimit { data: u8 },
|
||||||
|
|
||||||
|
/// Send a bluetooth characteristic: Limits
|
||||||
|
BluetoothBottomLimit { data: u8 },
|
||||||
|
/// Send a bluetooth characteristic: Wifi SSID
|
||||||
|
BluetoothWifiSsid { ssid: &'a str },
|
||||||
|
|
||||||
|
/// Send a bluetooth characteristic: Wifi Password
|
||||||
|
BluetoothWifiPassword { wifipass: &'a str },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_menu(
|
||||||
|
cli: &mut CliHandle<'_, SimpleWriter, Infallible>,
|
||||||
|
command: Menu<'_>,
|
||||||
|
) -> Result<(), Infallible> {
|
||||||
|
match command {
|
||||||
|
Menu::ButtonUp => {
|
||||||
|
cli.writer().write_str("TODO: simulate button")?;
|
||||||
|
}
|
||||||
|
Menu::ButtonDown => {
|
||||||
|
cli.writer().write_str("TODO: simulate button")?;
|
||||||
|
}
|
||||||
|
Menu::ButtonAuto => {
|
||||||
|
cli.writer().write_str("TODO: simulate button")?;
|
||||||
|
}
|
||||||
|
Menu::ButtonPair => {
|
||||||
|
cli.writer().write_str("TODO: simulate button")?;
|
||||||
|
}
|
||||||
|
Menu::BluetoothUp { data } => {
|
||||||
|
cli.writer()
|
||||||
|
.write_str("TODO: simulate bluetooth characteristic change")?;
|
||||||
|
let _ = data;
|
||||||
|
}
|
||||||
|
Menu::BluetoothDown { data } => {
|
||||||
|
cli.writer()
|
||||||
|
.write_str("TODO: simulate bluetooth characteristic change")?;
|
||||||
|
let _ = data;
|
||||||
|
}
|
||||||
|
Menu::BluetoothStop { data } => {
|
||||||
|
cli.writer()
|
||||||
|
.write_str("TODO: simulate bluetooth characteristic change")?;
|
||||||
|
let _ = data;
|
||||||
|
}
|
||||||
|
Menu::BluetoothLearn { data } => {
|
||||||
|
cli.writer()
|
||||||
|
.write_str("TODO: simulate bluetooth characteristic change")?;
|
||||||
|
let _ = data;
|
||||||
|
}
|
||||||
|
Menu::BluetoothAuto { data } => {
|
||||||
|
cli.writer()
|
||||||
|
.write_str("TODO: simulate bluetooth characteristic change")?;
|
||||||
|
let _ = data;
|
||||||
|
}
|
||||||
|
Menu::BluetoothTopLimit { data } => {
|
||||||
|
cli.writer()
|
||||||
|
.write_str("TODO: simulate bluetooth characteristic change")?;
|
||||||
|
let _ = data;
|
||||||
|
}
|
||||||
|
Menu::BluetoothBottomLimit { data } => {
|
||||||
|
cli.writer()
|
||||||
|
.write_str("TODO: simulate bluetooth characteristic change")?;
|
||||||
|
let _ = data;
|
||||||
|
}
|
||||||
|
Menu::BluetoothWifiSsid { ssid } => {
|
||||||
|
cli.writer()
|
||||||
|
.write_str("TODO: simulate bluetooth characteristic change")?;
|
||||||
|
let _ = ssid;
|
||||||
|
}
|
||||||
|
Menu::BluetoothWifiPassword { wifipass } => {
|
||||||
|
cli.writer()
|
||||||
|
.write_str("TODO: simulate bluetooth characteristic change {}")?;
|
||||||
|
let _ = wifipass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// === SimpleWriter ===========================================================
|
||||||
|
pub struct SimpleWriter {}
|
||||||
|
|
||||||
|
impl embedded_io::ErrorType for SimpleWriter {
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embedded_io::Write for SimpleWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||||
|
stdout().write(&buf).unwrap(); //TODO: handle errors?
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
|
let _ = stdout().flush(); //TODO currently ignoring this error
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start_cli() -> anyhow::Result<()> {
|
||||||
|
let timer_service = EspTaskTimerService::new().unwrap(); //TODO: handle this error
|
||||||
|
info!("Setting up command line listern and processor");
|
||||||
|
let writer = SimpleWriter {};
|
||||||
|
let mut cli = CliBuilder::default()
|
||||||
|
.writer(writer)
|
||||||
|
.command_buffer([0_u8; MAX_COMMAND_SIZE])
|
||||||
|
.history_buffer([0_u8; MAX_HISTORY_SIZE])
|
||||||
|
.prompt("$> ")
|
||||||
|
.build()?;
|
||||||
|
let mut reader = stdin();
|
||||||
|
let mut buf = [0_u8; 1];
|
||||||
|
let mut async_timer = timer_service.timer_async()?;
|
||||||
|
loop {
|
||||||
|
async_timer.after(Duration::from_millis(SLEEP_TIME_MS)).await?;
|
||||||
|
match reader.read_exact(&mut buf) {
|
||||||
|
Ok(_) => {
|
||||||
|
cli.process_byte::<Menu, _>(buf[0], &mut Menu::processor(process_menu))?;
|
||||||
|
}
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
std::io::ErrorKind::WouldBlock => {} // This is expected if there is no input
|
||||||
|
_ => {warn!("Error waiting for input: {}", e)}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user