Initial working exampel of test console
This commit is contained in:
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};
|
||||
use esp_idf_hal::delay::FreeRtos;
|
||||
use esp_idf_sys as _;
|
||||
mod test_console;
|
||||
|
||||
fn main() {
|
||||
esp_idf_sys::link_patches();
|
||||
|
||||
// Bind the log crate to the ESP Logging facilities
|
||||
// Do basic initialization
|
||||
esp_idf_svc::sys::link_patches();
|
||||
esp_idf_svc::log::EspLogger::initialize_default();
|
||||
info!("Logging initialized");
|
||||
|
||||
// Take ownership of device
|
||||
let ble_device = BLEDevice::take();
|
||||
//let peripherals = Peripherals::take().unwrap();
|
||||
|
||||
// Obtain handle for peripheral advertiser
|
||||
let ble_advertiser = ble_device.get_advertising();
|
||||
match future::block_on(main_loop()) {
|
||||
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
|
||||
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;
|
||||
async fn test_loop() -> Result<()> {
|
||||
let timer_service = EspTaskTimerService::new()?;
|
||||
|
||||
let mut async_timer = timer_service.timer_async()?;
|
||||
loop {
|
||||
FreeRtos::delay_ms(1000);
|
||||
my_service_characteristic.lock().set_value(&[val]).notify();
|
||||
val = val.wrapping_add(1);
|
||||
async_timer.after(Duration::from_secs(2)).await?;
|
||||
}
|
||||
}
|
||||
|
||||
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