This commit is contained in:
pszsh 2026-03-09 09:30:33 -07:00
parent 725089a6b7
commit 03d6312e22
13 changed files with 6842 additions and 17 deletions

4844
cue/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

11
cue/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "cue"
version = "0.1.0"
edition = "2024"
[dependencies]
iced = { version = "0.13", features = ["canvas", "tokio"] }
btleplug = "0.11"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
uuid = "1"

234
cue/src/app.rs Normal file
View File

@ -0,0 +1,234 @@
use futures::SinkExt;
use iced::widget::{button, column, container, pick_list, row, scrollable, text, text_input};
use iced::{Element, Length, Subscription, Task, Theme};
use std::time::Duration;
use tokio::sync::mpsc;
use crate::ble::BleEvent;
use crate::protocol::{self, EisMessage, EisPoint, Rcal, Rtia};
#[derive(Debug, Clone)]
pub enum Message {
BleReady(mpsc::UnboundedSender<Vec<u8>>),
BleStatus(String),
BleData(EisMessage),
FreqStartChanged(String),
FreqStopChanged(String),
PpdChanged(String),
RtiaSelected(Rtia),
RcalSelected(Rcal),
ApplySettings,
StartSweep,
}
pub struct App {
status: String,
points: Vec<EisPoint>,
sweep_total: u16,
freq_start: String,
freq_stop: String,
ppd: String,
rtia: Rtia,
rcal: Rcal,
cmd_tx: Option<mpsc::UnboundedSender<Vec<u8>>>,
}
impl App {
pub fn new() -> (Self, Task<Message>) {
(Self {
status: "Starting...".into(),
points: Vec::new(),
sweep_total: 0,
freq_start: "1000".into(),
freq_stop: "200000".into(),
ppd: "10".into(),
rtia: Rtia::R5K,
rcal: Rcal::R3K,
cmd_tx: None,
}, Task::none())
}
pub fn title(&self) -> String {
"Cue".into()
}
pub fn theme(&self) -> Theme {
Theme::Dark
}
fn send_cmd(&self, sysex: &[u8]) {
if let Some(tx) = &self.cmd_tx {
let _ = tx.send(protocol::wrap_ble_midi(sysex));
}
}
pub fn update(&mut self, message: Message) -> Task<Message> {
match message {
Message::BleReady(tx) => {
self.cmd_tx = Some(tx);
self.send_cmd(&protocol::build_sysex_get_config());
}
Message::BleStatus(s) => self.status = s,
Message::BleData(msg) => match msg {
EisMessage::SweepStart { num_points, freq_start, freq_stop } => {
self.points.clear();
self.sweep_total = num_points;
self.status = format!(
"Sweep: {} pts, {:.0}--{:.0} Hz",
num_points, freq_start, freq_stop
);
}
EisMessage::DataPoint { point, .. } => {
self.points.push(point);
self.status = format!(
"Receiving: {}/{}",
self.points.len(), self.sweep_total
);
}
EisMessage::SweepEnd => {
self.status = format!(
"Sweep complete: {} points", self.points.len()
);
}
EisMessage::Config(cfg) => {
self.freq_start = format!("{:.0}", cfg.freq_start);
self.freq_stop = format!("{:.0}", cfg.freq_stop);
self.ppd = format!("{}", cfg.ppd);
self.rtia = cfg.rtia;
self.rcal = cfg.rcal;
self.status = "Config received".into();
}
},
Message::FreqStartChanged(s) => self.freq_start = s,
Message::FreqStopChanged(s) => self.freq_stop = s,
Message::PpdChanged(s) => self.ppd = s,
Message::RtiaSelected(r) => self.rtia = r,
Message::RcalSelected(r) => self.rcal = r,
Message::ApplySettings => {
let fs = self.freq_start.parse::<f32>().unwrap_or(1000.0);
let fe = self.freq_stop.parse::<f32>().unwrap_or(200000.0);
let ppd = self.ppd.parse::<u16>().unwrap_or(10);
self.send_cmd(&protocol::build_sysex_set_sweep(fs, fe, ppd));
self.send_cmd(&protocol::build_sysex_set_rtia(self.rtia));
self.send_cmd(&protocol::build_sysex_set_rcal(self.rcal));
self.send_cmd(&protocol::build_sysex_get_config());
}
Message::StartSweep => {
self.send_cmd(&protocol::build_sysex_start_sweep());
}
}
Task::none()
}
pub fn subscription(&self) -> Subscription<Message> {
Subscription::run_with_id(
"ble",
iced::stream::channel(100, |mut output| async move {
loop {
let (ble_tx, mut ble_rx) =
mpsc::unbounded_channel::<BleEvent>();
let (cmd_tx, cmd_rx) =
mpsc::unbounded_channel::<Vec<u8>>();
let _ = output.send(Message::BleReady(cmd_tx)).await;
let tx = ble_tx.clone();
tokio::spawn(async move {
if let Err(e) = crate::ble::connect_and_run(tx, cmd_rx).await {
eprintln!("BLE: {e}");
}
});
while let Some(ev) = ble_rx.recv().await {
let msg = match ev {
BleEvent::Status(s) => Message::BleStatus(s),
BleEvent::Data(m) => Message::BleData(m),
};
let _ = output.send(msg).await;
}
let _ = output
.send(Message::BleStatus("Reconnecting...".into()))
.await;
tokio::time::sleep(Duration::from_secs(2)).await;
}
}),
)
}
pub fn view(&self) -> Element<'_, Message> {
let status = text(&self.status).size(16);
let controls = row![
column![
text("Start Hz").size(12),
text_input("1000", &self.freq_start)
.on_input(Message::FreqStartChanged)
.width(100),
]
.spacing(2),
column![
text("Stop Hz").size(12),
text_input("200000", &self.freq_stop)
.on_input(Message::FreqStopChanged)
.width(100),
]
.spacing(2),
column![
text("PPD").size(12),
text_input("10", &self.ppd)
.on_input(Message::PpdChanged)
.width(60),
]
.spacing(2),
column![
text("RTIA").size(12),
pick_list(Rtia::ALL, Some(self.rtia), Message::RtiaSelected),
]
.spacing(2),
column![
text("RCAL").size(12),
pick_list(Rcal::ALL, Some(self.rcal), Message::RcalSelected),
]
.spacing(2),
button("Apply").on_press(Message::ApplySettings),
button("Sweep").on_press(Message::StartSweep),
]
.spacing(10)
.align_y(iced::Alignment::End);
let header = row![
text("Freq (Hz)").width(100),
text("|Z| (Ohm)").width(100),
text("Phase (deg)").width(100),
text("Re (Ohm)").width(100),
text("Im (Ohm)").width(100),
]
.spacing(10);
let mut data_rows = column![header].spacing(4);
for pt in &self.points {
data_rows = data_rows.push(
row![
text(format!("{:.1}", pt.freq_hz)).width(100),
text(format!("{:.2}", pt.mag_ohms)).width(100),
text(format!("{:.2}", pt.phase_deg)).width(100),
text(format!("{:.2}", pt.z_real)).width(100),
text(format!("{:.2}", pt.z_imag)).width(100),
]
.spacing(10),
);
}
let content = column![status, controls, scrollable(data_rows)]
.spacing(20)
.padding(20);
container(content)
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}

81
cue/src/ble.rs Normal file
View File

@ -0,0 +1,81 @@
use btleplug::api::{Central, Manager as _, Peripheral as _, ScanFilter, WriteType};
use btleplug::platform::{Adapter, Manager, Peripheral};
use futures::StreamExt;
use std::time::Duration;
use tokio::sync::mpsc;
use uuid::Uuid;
use crate::protocol::{self, EisMessage};
const MIDI_CHR_UUID: Uuid = Uuid::from_u128(0x7772E5DB_3868_4112_A1A9_F2669D106BF3);
const DEVICE_NAME: &str = "EIS4";
#[derive(Debug, Clone)]
pub enum BleEvent {
Status(String),
Data(EisMessage),
}
pub async fn connect_and_run(
tx: mpsc::UnboundedSender<BleEvent>,
mut cmd_rx: mpsc::UnboundedReceiver<Vec<u8>>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let _ = tx.send(BleEvent::Status("Scanning...".into()));
let manager = Manager::new().await?;
let adapter = manager.adapters().await?.into_iter().next()
.ok_or("no BLE adapter")?;
adapter.start_scan(ScanFilter::default()).await?;
tokio::time::sleep(Duration::from_secs(3)).await;
let device = find_device(&adapter).await?;
let _ = tx.send(BleEvent::Status("Connecting...".into()));
device.connect().await?;
device.discover_services().await?;
let chars = device.characteristics();
let midi_chr = chars.iter()
.find(|c| c.uuid == MIDI_CHR_UUID)
.ok_or("MIDI characteristic not found")?
.clone();
device.subscribe(&midi_chr).await?;
let _ = tx.send(BleEvent::Status("Connected".into()));
let mut notifs = device.notifications().await?;
loop {
tokio::select! {
Some(notif) = notifs.next() => {
if notif.uuid == MIDI_CHR_UUID {
if let Some(sysex) = protocol::extract_sysex_from_ble_midi(&notif.value) {
if let Some(msg) = protocol::parse_sysex(&sysex) {
let _ = tx.send(BleEvent::Data(msg));
}
}
}
}
Some(pkt) = cmd_rx.recv() => {
device.write(&midi_chr, &pkt, WriteType::WithoutResponse).await.ok();
}
else => break,
}
}
let _ = tx.send(BleEvent::Status("Disconnected".into()));
Ok(())
}
async fn find_device(adapter: &Adapter) -> Result<Peripheral, Box<dyn std::error::Error + Send + Sync>> {
let peripherals = adapter.peripherals().await?;
for p in peripherals {
if let Some(props) = p.properties().await? {
if props.local_name.as_deref() == Some(DEVICE_NAME) {
return Ok(p);
}
}
}
Err(format!("{} not found", DEVICE_NAME).into())
}

10
cue/src/main.rs Normal file
View File

@ -0,0 +1,10 @@
mod app;
mod ble;
mod protocol;
fn main() -> iced::Result {
iced::application(app::App::title, app::App::update, app::App::view)
.theme(app::App::theme)
.subscription(app::App::subscription)
.run_with(app::App::new)
}

204
cue/src/protocol.rs Normal file
View File

@ -0,0 +1,204 @@
/// EIS4 SysEx Protocol — manufacturer ID 0x7D (non-commercial)
pub const SYSEX_MFR: u8 = 0x7D;
/* ESP32 → Cue */
pub const RSP_SWEEP_START: u8 = 0x01;
pub const RSP_DATA_POINT: u8 = 0x02;
pub const RSP_SWEEP_END: u8 = 0x03;
pub const RSP_CONFIG: u8 = 0x04;
/* Cue → ESP32 */
pub const CMD_SET_SWEEP: u8 = 0x10;
pub const CMD_SET_RTIA: u8 = 0x11;
pub const CMD_SET_RCAL: u8 = 0x12;
pub const CMD_START_SWEEP: u8 = 0x13;
pub const CMD_GET_CONFIG: u8 = 0x14;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Rtia {
R200, R1K, R5K, R10K, R20K, R40K, R80K, R160K, ExtDe0,
}
impl Rtia {
pub fn from_byte(b: u8) -> Option<Self> {
Some(match b {
0 => Self::R200, 1 => Self::R1K, 2 => Self::R5K, 3 => Self::R10K,
4 => Self::R20K, 5 => Self::R40K, 6 => Self::R80K, 7 => Self::R160K,
8 => Self::ExtDe0,
_ => return None,
})
}
pub fn as_byte(self) -> u8 { self as u8 }
pub fn label(self) -> &'static str {
match self {
Self::R200 => "200Ω", Self::R1K => "1kΩ", Self::R5K => "5kΩ",
Self::R10K => "10kΩ", Self::R20K => "20kΩ", Self::R40K => "40kΩ",
Self::R80K => "80kΩ", Self::R160K => "160kΩ", Self::ExtDe0 => "Ext 3kΩ (DE0)",
}
}
pub const ALL: &[Self] = &[
Self::R200, Self::R1K, Self::R5K, Self::R10K,
Self::R20K, Self::R40K, Self::R80K, Self::R160K, Self::ExtDe0,
];
}
impl std::fmt::Display for Rtia {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.label())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Rcal {
R200, R3K,
}
impl Rcal {
pub fn from_byte(b: u8) -> Option<Self> {
Some(match b { 0 => Self::R200, 1 => Self::R3K, _ => return None })
}
pub fn as_byte(self) -> u8 { self as u8 }
pub fn label(self) -> &'static str {
match self { Self::R200 => "200Ω (RCAL0↔RCAL1)", Self::R3K => "3kΩ (RCAL0↔AIN0)" }
}
pub const ALL: &[Self] = &[Self::R200, Self::R3K];
}
impl std::fmt::Display for Rcal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.label())
}
}
#[derive(Debug, Clone)]
pub struct EisPoint {
pub freq_hz: f32,
pub mag_ohms: f32,
pub phase_deg: f32,
pub z_real: f32,
pub z_imag: f32,
}
#[derive(Debug, Clone)]
pub struct EisConfig {
pub freq_start: f32,
pub freq_stop: f32,
pub ppd: u16,
pub rtia: Rtia,
pub rcal: Rcal,
}
#[derive(Debug, Clone)]
pub enum EisMessage {
SweepStart { num_points: u16, freq_start: f32, freq_stop: f32 },
DataPoint { index: u16, point: EisPoint },
SweepEnd,
Config(EisConfig),
}
fn decode_u16(data: &[u8]) -> u16 {
let b0 = data[1] | ((data[0] & 1) << 7);
let b1 = data[2] | ((data[0] & 2) << 6);
u16::from_le_bytes([b0, b1])
}
fn decode_float(data: &[u8]) -> f32 {
let m = data[0];
let b0 = data[1] | ((m & 1) << 7);
let b1 = data[2] | ((m & 2) << 6);
let b2 = data[3] | ((m & 4) << 5);
let b3 = data[4] | ((m & 8) << 4);
f32::from_le_bytes([b0, b1, b2, b3])
}
fn encode_float(val: f32) -> [u8; 5] {
let p = val.to_le_bytes();
[
((p[0] >> 7) & 1) | ((p[1] >> 6) & 2) | ((p[2] >> 5) & 4) | ((p[3] >> 4) & 8),
p[0] & 0x7F, p[1] & 0x7F, p[2] & 0x7F, p[3] & 0x7F,
]
}
fn encode_u16(val: u16) -> [u8; 3] {
let p = val.to_le_bytes();
[((p[0] >> 7) & 1) | ((p[1] >> 6) & 2), p[0] & 0x7F, p[1] & 0x7F]
}
pub fn parse_sysex(data: &[u8]) -> Option<EisMessage> {
if data.len() < 2 || data[0] != SYSEX_MFR { return None; }
match data[1] {
RSP_SWEEP_START if data.len() >= 15 => {
let p = &data[2..];
Some(EisMessage::SweepStart {
num_points: decode_u16(&p[0..3]),
freq_start: decode_float(&p[3..8]),
freq_stop: decode_float(&p[8..13]),
})
}
RSP_DATA_POINT if data.len() >= 30 => {
let p = &data[2..];
Some(EisMessage::DataPoint {
index: decode_u16(&p[0..3]),
point: EisPoint {
freq_hz: decode_float(&p[3..8]),
mag_ohms: decode_float(&p[8..13]),
phase_deg: decode_float(&p[13..18]),
z_real: decode_float(&p[18..23]),
z_imag: decode_float(&p[23..28]),
},
})
}
RSP_SWEEP_END => Some(EisMessage::SweepEnd),
RSP_CONFIG if data.len() >= 17 => {
let p = &data[2..];
Some(EisMessage::Config(EisConfig {
freq_start: decode_float(&p[0..5]),
freq_stop: decode_float(&p[5..10]),
ppd: decode_u16(&p[10..13]),
rtia: Rtia::from_byte(p[13]).unwrap_or(Rtia::R5K),
rcal: Rcal::from_byte(p[14]).unwrap_or(Rcal::R3K),
}))
}
_ => None,
}
}
pub fn build_sysex_set_sweep(freq_start: f32, freq_stop: f32, ppd: u16) -> Vec<u8> {
let mut sx = vec![0xF0, SYSEX_MFR, CMD_SET_SWEEP];
sx.extend_from_slice(&encode_float(freq_start));
sx.extend_from_slice(&encode_float(freq_stop));
sx.extend_from_slice(&encode_u16(ppd));
sx.push(0xF7);
sx
}
pub fn build_sysex_set_rtia(rtia: Rtia) -> Vec<u8> {
vec![0xF0, SYSEX_MFR, CMD_SET_RTIA, rtia.as_byte(), 0xF7]
}
pub fn build_sysex_set_rcal(rcal: Rcal) -> Vec<u8> {
vec![0xF0, SYSEX_MFR, CMD_SET_RCAL, rcal.as_byte(), 0xF7]
}
pub fn build_sysex_start_sweep() -> Vec<u8> {
vec![0xF0, SYSEX_MFR, CMD_START_SWEEP, 0xF7]
}
pub fn build_sysex_get_config() -> Vec<u8> {
vec![0xF0, SYSEX_MFR, CMD_GET_CONFIG, 0xF7]
}
pub fn wrap_ble_midi(sysex: &[u8]) -> Vec<u8> {
let mut pkt = vec![0x80, 0x80];
pkt.extend_from_slice(sysex);
pkt
}
pub fn extract_sysex_from_ble_midi(packet: &[u8]) -> Option<Vec<u8>> {
if packet.len() < 4 { return None; }
let midi = &packet[2..];
if midi.first() != Some(&0xF0) { return None; }
let end = midi.iter().position(|&b| b == 0xF7)?;
Some(midi[1..end].to_vec())
}

View File

@ -1,3 +1,3 @@
idf_component_register(SRCS "eis4.c"
idf_component_register(SRCS "eis4.c" "eis.c" "ble.c"
INCLUDE_DIRS "."
REQUIRES ad5941 ad5941_port)
REQUIRES ad5941 ad5941_port bt nvs_flash)

592
main/ble.c Normal file
View File

@ -0,0 +1,592 @@
#include "ble.h"
#include <string.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/ble_gap.h"
#include "host/util/util.h"
#include "host/ble_store.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
void ble_store_config_init(void);
#define DEVICE_NAME "EIS4"
#define CONNECTED_BIT BIT0
#define CMD_QUEUE_LEN 4
/* BLE MIDI Service 03B80E5A-EDE8-4B33-A751-6CE34EC4C700 */
static const ble_uuid128_t midi_svc_uuid = BLE_UUID128_INIT(
0x00, 0xc7, 0xc4, 0x4e, 0xe3, 0x6c, 0x51, 0xa7,
0x33, 0x4b, 0xe8, 0xed, 0x5a, 0x0e, 0xb8, 0x03);
/* BLE MIDI Characteristic 7772E5DB-3868-4112-A1A9-F2669D106BF3 */
static const ble_uuid128_t midi_chr_uuid = BLE_UUID128_INIT(
0xf3, 0x6b, 0x10, 0x9d, 0x66, 0xf2, 0xa9, 0xa1,
0x12, 0x41, 0x68, 0x38, 0xdb, 0xe5, 0x72, 0x77);
static EventGroupHandle_t ble_events;
static QueueHandle_t cmd_queue;
static uint16_t conn_hdl = BLE_HS_CONN_HANDLE_NONE;
static uint16_t midi_val_hdl;
static uint16_t hid_input_val_hdl;
static bool midi_notify_en;
static bool hid_notify_en;
/* ---- HID keyboard report map ---- */
static const uint8_t hid_report_map[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x06, /* Usage (Keyboard) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x01, /* Report ID (1) */
0x05, 0x07, /* Usage Page (Keyboard) */
0x19, 0xE0, 0x29, 0xE7, /* Usage Min/Max (modifiers) */
0x15, 0x00, 0x25, 0x01, /* Logical Min/Max */
0x75, 0x01, 0x95, 0x08, /* 8x1-bit modifier keys */
0x81, 0x02, /* Input (Variable) */
0x95, 0x01, 0x75, 0x08, /* 1x8-bit reserved */
0x81, 0x01, /* Input (Constant) */
0x05, 0x08, /* Usage Page (LEDs) */
0x19, 0x01, 0x29, 0x05, /* 5 LEDs */
0x95, 0x05, 0x75, 0x01,
0x91, 0x02, /* Output (Variable) */
0x95, 0x01, 0x75, 0x03,
0x91, 0x01, /* Output pad to byte */
0x05, 0x07, /* Usage Page (Keyboard) */
0x19, 0x00, 0x29, 0xFF, /* Key codes 0-255 */
0x15, 0x00, 0x26, 0xFF, 0x00, /* Logical Min/Max (0-255) */
0x95, 0x06, 0x75, 0x08, /* 6x8-bit key array */
0x81, 0x00, /* Input (Array) */
0xC0, /* End Collection */
};
/* bcdHID=1.11, bCountryCode=0, Flags=NormallyConnectable */
static const uint8_t hid_info[] = { 0x11, 0x01, 0x00, 0x02 };
/* PnP ID: src=USB-IF(2), VID=0x1209(pid.codes), PID=0x0001, ver=0x0100 */
static const uint8_t pnp_id[] = { 0x02, 0x09, 0x12, 0x01, 0x00, 0x00, 0x01 };
/* ---- 7-bit MIDI encoding ---- */
static void encode_float(float val, uint8_t *out)
{
uint8_t *p = (uint8_t *)&val;
out[0] = ((p[0] >> 7) & 1) | ((p[1] >> 6) & 2) |
((p[2] >> 5) & 4) | ((p[3] >> 4) & 8);
out[1] = p[0] & 0x7F;
out[2] = p[1] & 0x7F;
out[3] = p[2] & 0x7F;
out[4] = p[3] & 0x7F;
}
static void encode_u16(uint16_t val, uint8_t *out)
{
uint8_t *p = (uint8_t *)&val;
out[0] = ((p[0] >> 7) & 1) | ((p[1] >> 6) & 2);
out[1] = p[0] & 0x7F;
out[2] = p[1] & 0x7F;
}
static float decode_float(const uint8_t *d)
{
uint8_t b[4];
b[0] = d[1] | ((d[0] & 1) << 7);
b[1] = d[2] | ((d[0] & 2) << 6);
b[2] = d[3] | ((d[0] & 4) << 5);
b[3] = d[4] | ((d[0] & 8) << 4);
float v;
memcpy(&v, b, 4);
return v;
}
static uint16_t decode_u16(const uint8_t *d)
{
uint8_t b[2];
b[0] = d[1] | ((d[0] & 1) << 7);
b[1] = d[2] | ((d[0] & 2) << 6);
uint16_t v;
memcpy(&v, b, 2);
return v;
}
/* ---- command parsing from incoming SysEx ---- */
static void parse_command(const uint8_t *data, uint16_t len)
{
if (len < 5) return;
const uint8_t *midi = data + 2;
uint16_t mlen = len - 2;
if (midi[0] != 0xF0 || midi[1] != 0x7D) return;
BleCommand cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.type = midi[2];
switch (cmd.type) {
case CMD_SET_SWEEP:
if (mlen < 16) return;
cmd.sweep.freq_start = decode_float(&midi[3]);
cmd.sweep.freq_stop = decode_float(&midi[8]);
cmd.sweep.ppd = decode_u16(&midi[13]);
break;
case CMD_SET_RTIA:
if (mlen < 5) return;
cmd.rtia = midi[3];
break;
case CMD_SET_RCAL:
if (mlen < 5) return;
cmd.rcal = midi[3];
break;
case CMD_START_SWEEP:
case CMD_GET_CONFIG:
break;
default:
return;
}
xQueueSend(cmd_queue, &cmd, 0);
}
/* ---- GATT access callbacks ---- */
static int midi_access_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
uint16_t len = OS_MBUF_PKTLEN(ctxt->om);
uint8_t buf[64];
if (len > sizeof(buf)) len = sizeof(buf);
os_mbuf_copydata(ctxt->om, 0, len, buf);
parse_command(buf, len);
}
return 0;
}
static int hid_report_map_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR)
os_mbuf_append(ctxt->om, hid_report_map, sizeof(hid_report_map));
return 0;
}
static int hid_info_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR)
os_mbuf_append(ctxt->om, hid_info, sizeof(hid_info));
return 0;
}
static int hid_input_report_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
static const uint8_t empty[8] = {0};
os_mbuf_append(ctxt->om, empty, sizeof(empty));
}
return 0;
}
static int hid_output_report_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
return 0;
}
static int hid_input_ref_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
static const uint8_t ref[] = { 0x01, 0x01 }; /* Report ID 1, Input */
os_mbuf_append(ctxt->om, ref, sizeof(ref));
}
return 0;
}
static int hid_output_ref_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
static const uint8_t ref[] = { 0x01, 0x02 }; /* Report ID 1, Output */
os_mbuf_append(ctxt->om, ref, sizeof(ref));
}
return 0;
}
static int hid_boot_input_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
static const uint8_t empty[8] = {0};
os_mbuf_append(ctxt->om, empty, sizeof(empty));
}
return 0;
}
static int hid_boot_output_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
return 0;
}
static int hid_ctrl_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
return 0;
}
static int hid_proto_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
uint8_t mode = 1; /* Report Protocol */
os_mbuf_append(ctxt->om, &mode, 1);
}
return 0;
}
static int bas_level_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
uint8_t level = 100;
os_mbuf_append(ctxt->om, &level, 1);
}
return 0;
}
static int dis_pnp_cb(uint16_t ch, uint16_t ah,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
(void)ch; (void)ah; (void)arg;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR)
os_mbuf_append(ctxt->om, pnp_id, sizeof(pnp_id));
return 0;
}
/* ---- GATT service table ---- */
static const struct ble_gatt_svc_def gatt_svcs[] = {
/* Device Information Service */
{
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(0x180A),
.characteristics = (struct ble_gatt_chr_def[]) {
{ .uuid = BLE_UUID16_DECLARE(0x2A50), .access_cb = dis_pnp_cb,
.flags = BLE_GATT_CHR_F_READ },
{ 0 },
},
},
/* Battery Service */
{
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(0x180F),
.characteristics = (struct ble_gatt_chr_def[]) {
{ .uuid = BLE_UUID16_DECLARE(0x2A19), .access_cb = bas_level_cb,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY },
{ 0 },
},
},
/* HID Service */
{
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(0x1812),
.characteristics = (struct ble_gatt_chr_def[]) {
{ .uuid = BLE_UUID16_DECLARE(0x2A4B), .access_cb = hid_report_map_cb,
.flags = BLE_GATT_CHR_F_READ },
{ .uuid = BLE_UUID16_DECLARE(0x2A4A), .access_cb = hid_info_cb,
.flags = BLE_GATT_CHR_F_READ },
/* Input Report */
{ .uuid = BLE_UUID16_DECLARE(0x2A4D), .access_cb = hid_input_report_cb,
.val_handle = &hid_input_val_hdl,
.descriptors = (struct ble_gatt_dsc_def[]) {
{ .uuid = BLE_UUID16_DECLARE(0x2908), .att_flags = BLE_ATT_F_READ,
.access_cb = hid_input_ref_cb },
{ 0 },
},
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_READ_ENC },
/* Output Report (LEDs) */
{ .uuid = BLE_UUID16_DECLARE(0x2A4D), .access_cb = hid_output_report_cb,
.descriptors = (struct ble_gatt_dsc_def[]) {
{ .uuid = BLE_UUID16_DECLARE(0x2908), .att_flags = BLE_ATT_F_READ,
.access_cb = hid_output_ref_cb },
{ 0 },
},
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE |
BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_READ_ENC |
BLE_GATT_CHR_F_WRITE_ENC },
/* Boot Keyboard Input Report */
{ .uuid = BLE_UUID16_DECLARE(0x2A22), .access_cb = hid_boot_input_cb,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_READ_ENC },
/* Boot Keyboard Output Report */
{ .uuid = BLE_UUID16_DECLARE(0x2A32), .access_cb = hid_boot_output_cb,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE |
BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_READ_ENC },
{ .uuid = BLE_UUID16_DECLARE(0x2A4C), .access_cb = hid_ctrl_cb,
.flags = BLE_GATT_CHR_F_WRITE_NO_RSP },
{ .uuid = BLE_UUID16_DECLARE(0x2A4E), .access_cb = hid_proto_cb,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE_NO_RSP },
{ 0 },
},
},
/* MIDI Service */
{
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &midi_svc_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]) {
{ .uuid = &midi_chr_uuid.u, .access_cb = midi_access_cb,
.val_handle = &midi_val_hdl,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE_NO_RSP | BLE_GATT_CHR_F_NOTIFY },
{ 0 },
},
},
{ 0 },
};
/* ---- send empty keyboard report ---- */
static void send_empty_hid_report(void)
{
if (conn_hdl == BLE_HS_CONN_HANDLE_NONE || !hid_notify_en)
return;
static const uint8_t empty[8] = {0};
struct os_mbuf *om = ble_hs_mbuf_from_flat(empty, sizeof(empty));
if (om)
ble_gatts_notify_custom(conn_hdl, hid_input_val_hdl, om);
}
/* ---- GAP / advertising ---- */
static void start_adv(void);
static int gap_event_cb(struct ble_gap_event *event, void *arg)
{
(void)arg;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
if (event->connect.status == 0) {
conn_hdl = event->connect.conn_handle;
xEventGroupSetBits(ble_events, CONNECTED_BIT);
ble_att_set_preferred_mtu(128);
ble_gattc_exchange_mtu(conn_hdl, NULL, NULL);
ble_gap_security_initiate(conn_hdl);
printf("BLE: connected\n");
} else {
start_adv();
}
break;
case BLE_GAP_EVENT_DISCONNECT:
conn_hdl = BLE_HS_CONN_HANDLE_NONE;
midi_notify_en = false;
hid_notify_en = false;
xEventGroupClearBits(ble_events, CONNECTED_BIT);
printf("BLE: disconnected\n");
start_adv();
break;
case BLE_GAP_EVENT_SUBSCRIBE:
if (event->subscribe.attr_handle == midi_val_hdl)
midi_notify_en = event->subscribe.cur_notify;
if (event->subscribe.attr_handle == hid_input_val_hdl) {
hid_notify_en = event->subscribe.cur_notify;
if (hid_notify_en)
send_empty_hid_report();
}
break;
case BLE_GAP_EVENT_REPEAT_PAIRING: {
struct ble_gap_conn_desc desc;
ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
ble_store_util_delete_peer(&desc.peer_id_addr);
return BLE_GAP_REPEAT_PAIRING_RETRY;
}
default:
break;
}
return 0;
}
static void start_adv(void)
{
struct ble_hs_adv_fields fields = {0};
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
fields.name = (uint8_t *)DEVICE_NAME;
fields.name_len = strlen(DEVICE_NAME);
fields.name_is_complete = 1;
fields.appearance = 0x03C1; /* Keyboard */
fields.appearance_is_present = 1;
ble_uuid16_t adv_uuids[] = {
BLE_UUID16_INIT(0x1812), /* HID */
BLE_UUID16_INIT(0x180F), /* Battery */
};
fields.uuids16 = adv_uuids;
fields.num_uuids16 = 2;
fields.uuids16_is_complete = 0;
ble_gap_adv_set_fields(&fields);
struct ble_hs_adv_fields rsp = {0};
rsp.uuids128 = (ble_uuid128_t *)&midi_svc_uuid;
rsp.num_uuids128 = 1;
rsp.uuids128_is_complete = 1;
ble_gap_adv_rsp_set_fields(&rsp);
struct ble_gap_adv_params params = {0};
params.conn_mode = BLE_GAP_CONN_MODE_UND;
params.disc_mode = BLE_GAP_DISC_MODE_GEN;
ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER,
&params, gap_event_cb, NULL);
}
static void on_sync(void)
{
uint8_t addr_type;
ble_hs_id_infer_auto(0, &addr_type);
start_adv();
printf("BLE: advertising as \"%s\"\n", DEVICE_NAME);
}
static void on_reset(int reason) { (void)reason; }
static void host_task(void *param)
{
(void)param;
nimble_port_run();
nimble_port_freertos_deinit();
}
/* ---- SysEx send ---- */
static int send_sysex(const uint8_t *sysex, uint16_t len)
{
if (conn_hdl == BLE_HS_CONN_HANDLE_NONE || !midi_notify_en)
return -1;
uint16_t pkt_len = 2 + len;
uint8_t pkt[80];
if (pkt_len > sizeof(pkt))
return -1;
pkt[0] = 0x80;
pkt[1] = 0x80;
memcpy(&pkt[2], sysex, len);
struct os_mbuf *om = ble_hs_mbuf_from_flat(pkt, pkt_len);
if (!om) return -1;
return ble_gatts_notify_custom(conn_hdl, midi_val_hdl, om);
}
/* ---- public API ---- */
int ble_init(void)
{
ble_events = xEventGroupCreate();
cmd_queue = xQueueCreate(CMD_QUEUE_LEN, sizeof(BleCommand));
int rc = nimble_port_init();
if (rc != ESP_OK) return rc;
ble_hs_cfg.sync_cb = on_sync;
ble_hs_cfg.reset_cb = on_reset;
ble_hs_cfg.sm_bonding = 1;
ble_hs_cfg.sm_mitm = 0;
ble_hs_cfg.sm_sc = 1;
ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_NO_IO;
ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID;
ble_svc_gap_init();
ble_svc_gatt_init();
ble_svc_gap_device_name_set(DEVICE_NAME);
ble_svc_gap_device_appearance_set(0x03C1);
rc = ble_gatts_count_cfg(gatt_svcs);
if (rc) return rc;
rc = ble_gatts_add_svcs(gatt_svcs);
if (rc) return rc;
ble_store_config_init();
nimble_port_freertos_init(host_task);
return 0;
}
int ble_is_connected(void)
{
return conn_hdl != BLE_HS_CONN_HANDLE_NONE;
}
void ble_wait_for_connection(void)
{
xEventGroupWaitBits(ble_events, CONNECTED_BIT, pdFALSE, pdTRUE, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(1000));
}
int ble_recv_command(BleCommand *cmd, uint32_t timeout_ms)
{
TickType_t ticks = (timeout_ms == UINT32_MAX) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
return xQueueReceive(cmd_queue, cmd, ticks) == pdTRUE ? 0 : -1;
}
int ble_send_sweep_start(uint32_t num_points, float freq_start, float freq_stop)
{
uint8_t sx[20];
uint16_t p = 0;
sx[p++] = 0xF0; sx[p++] = 0x7D; sx[p++] = RSP_SWEEP_START;
encode_u16((uint16_t)num_points, &sx[p]); p += 3;
encode_float(freq_start, &sx[p]); p += 5;
encode_float(freq_stop, &sx[p]); p += 5;
sx[p++] = 0xF7;
return send_sysex(sx, p);
}
int ble_send_eis_point(uint16_t index, const EISPoint *pt)
{
uint8_t sx[36];
uint16_t p = 0;
sx[p++] = 0xF0; sx[p++] = 0x7D; sx[p++] = RSP_DATA_POINT;
encode_u16(index, &sx[p]); p += 3;
encode_float(pt->freq_hz, &sx[p]); p += 5;
encode_float(pt->mag_ohms, &sx[p]); p += 5;
encode_float(pt->phase_deg, &sx[p]); p += 5;
encode_float(pt->z_real, &sx[p]); p += 5;
encode_float(pt->z_imag, &sx[p]); p += 5;
sx[p++] = 0xF7;
return send_sysex(sx, p);
}
int ble_send_sweep_end(void)
{
uint8_t sx[] = { 0xF0, 0x7D, RSP_SWEEP_END, 0xF7 };
return send_sysex(sx, sizeof(sx));
}
int ble_send_config(const EISConfig *cfg)
{
uint8_t sx[32];
uint16_t p = 0;
sx[p++] = 0xF0; sx[p++] = 0x7D; sx[p++] = RSP_CONFIG;
encode_float(cfg->freq_start_hz, &sx[p]); p += 5;
encode_float(cfg->freq_stop_hz, &sx[p]); p += 5;
encode_u16(cfg->points_per_decade, &sx[p]); p += 3;
sx[p++] = (uint8_t)cfg->rtia;
sx[p++] = (uint8_t)cfg->rcal;
sx[p++] = 0xF7;
return send_sysex(sx, p);
}

41
main/ble.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef BLE_H
#define BLE_H
#include "eis.h"
/* Commands: Cue → ESP32 (0x1x) */
#define CMD_SET_SWEEP 0x10
#define CMD_SET_RTIA 0x11
#define CMD_SET_RCAL 0x12
#define CMD_START_SWEEP 0x13
#define CMD_GET_CONFIG 0x14
/* Responses: ESP32 → Cue (0x0x) */
#define RSP_SWEEP_START 0x01
#define RSP_DATA_POINT 0x02
#define RSP_SWEEP_END 0x03
#define RSP_CONFIG 0x04
typedef struct {
uint8_t type;
union {
struct { float freq_start, freq_stop; uint16_t ppd; } sweep;
uint8_t rtia;
uint8_t rcal;
};
} BleCommand;
int ble_init(void);
int ble_is_connected(void);
void ble_wait_for_connection(void);
/* blocking receive from command queue */
int ble_recv_command(BleCommand *cmd, uint32_t timeout_ms);
/* outbound data */
int ble_send_sweep_start(uint32_t num_points, float freq_start, float freq_stop);
int ble_send_eis_point(uint16_t index, const EISPoint *pt);
int ble_send_sweep_end(void);
int ble_send_config(const EISConfig *cfg);
#endif

361
main/eis.c Normal file
View File

@ -0,0 +1,361 @@
#include "eis.h"
#include <math.h>
#include <string.h>
#include <stdio.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/* resolved hardware state */
static struct {
EISConfig cfg;
float sys_clk;
float rcal_ohms;
uint32_t rcal_sw_d, rcal_sw_p, rcal_sw_n, rcal_sw_t;
uint32_t rtia_reg;
uint32_t dertia_reg;
} ctx;
static const uint32_t rtia_map[] = {
[RTIA_200] = HSTIARTIA_200,
[RTIA_1K] = HSTIARTIA_1K,
[RTIA_5K] = HSTIARTIA_5K,
[RTIA_10K] = HSTIARTIA_10K,
[RTIA_20K] = HSTIARTIA_20K,
[RTIA_40K] = HSTIARTIA_40K,
[RTIA_80K] = HSTIARTIA_80K,
[RTIA_160K] = HSTIARTIA_160K,
[RTIA_EXT_DE0] = HSTIARTIA_OPEN,
};
static void resolve_config(void)
{
/* RTIA */
ctx.rtia_reg = rtia_map[ctx.cfg.rtia];
ctx.dertia_reg = (ctx.cfg.rtia == RTIA_EXT_DE0) ? HSTIADERTIA_TODE : HSTIADERTIA_OPEN;
/* RCAL */
switch (ctx.cfg.rcal) {
case RCAL_200R:
ctx.rcal_ohms = 200.0f;
ctx.rcal_sw_d = SWD_RCAL0;
ctx.rcal_sw_p = SWP_RCAL0;
ctx.rcal_sw_n = SWN_RCAL1;
ctx.rcal_sw_t = SWT_RCAL1 | SWT_TRTIA;
break;
default: /* RCAL_3K */
ctx.rcal_ohms = 3000.0f;
ctx.rcal_sw_d = SWD_RCAL0;
ctx.rcal_sw_p = SWP_RCAL0;
ctx.rcal_sw_n = SWN_AIN0;
ctx.rcal_sw_t = SWT_AIN0 | SWT_TRTIA;
break;
}
}
static void apply_hsloop(void)
{
HSLoopCfg_Type hs;
AD5940_StructInit(&hs, sizeof(hs));
hs.HsDacCfg.ExcitBufGain = EXCITBUFGAIN_2;
hs.HsDacCfg.HsDacGain = HSDACGAIN_0P2;
hs.HsDacCfg.HsDacUpdateRate = 7;
hs.HsTiaCfg.HstiaBias = HSTIABIAS_1P1;
hs.HsTiaCfg.HstiaRtiaSel = ctx.rtia_reg;
hs.HsTiaCfg.HstiaCtia = 16;
hs.HsTiaCfg.HstiaDeRtia = ctx.dertia_reg;
hs.HsTiaCfg.HstiaDeRload = HSTIADERLOAD_OPEN;
hs.HsTiaCfg.HstiaDe1Rtia = HSTIADERTIA_OPEN;
hs.HsTiaCfg.HstiaDe1Rload = HSTIADERLOAD_OPEN;
hs.HsTiaCfg.DiodeClose = bFALSE;
hs.WgCfg.WgType = WGTYPE_SIN;
hs.WgCfg.GainCalEn = bTRUE;
hs.WgCfg.OffsetCalEn = bTRUE;
hs.WgCfg.SinCfg.SinAmplitudeWord = ctx.cfg.excit_amp;
hs.WgCfg.SinCfg.SinFreqWord = 0;
hs.WgCfg.SinCfg.SinOffsetWord = 0;
hs.WgCfg.SinCfg.SinPhaseWord = 0;
hs.SWMatCfg.Dswitch = ctx.rcal_sw_d;
hs.SWMatCfg.Pswitch = ctx.rcal_sw_p;
hs.SWMatCfg.Nswitch = ctx.rcal_sw_n;
hs.SWMatCfg.Tswitch = ctx.rcal_sw_t;
AD5940_HSLoopCfgS(&hs);
if (ctx.cfg.rtia == RTIA_EXT_DE0)
AD5940_WriteReg(REG_AFE_DE0RESCON, 0x97);
}
/* ---------- public ---------- */
void eis_default_config(EISConfig *cfg)
{
memset(cfg, 0, sizeof(*cfg));
cfg->freq_start_hz = 1000.0f;
cfg->freq_stop_hz = 200000.0f;
cfg->points_per_decade = 10;
cfg->rtia = RTIA_5K;
cfg->rcal = RCAL_3K;
cfg->pga = ADCPGA_1P5;
cfg->excit_amp = 500;
}
uint32_t eis_calc_num_points(const EISConfig *cfg)
{
if (cfg->freq_stop_hz <= cfg->freq_start_hz || cfg->points_per_decade == 0)
return 1;
float decades = log10f(cfg->freq_stop_hz / cfg->freq_start_hz);
uint32_t n = (uint32_t)(decades * cfg->points_per_decade + 0.5f) + 1;
if (n > EIS_MAX_POINTS) n = EIS_MAX_POINTS;
if (n < 2) n = 2;
return n;
}
void eis_init(const EISConfig *cfg)
{
memcpy(&ctx.cfg, cfg, sizeof(EISConfig));
ctx.sys_clk = 16000000.0f;
resolve_config();
CLKCfg_Type clk;
memset(&clk, 0, sizeof(clk));
clk.HFOSCEn = bTRUE;
clk.HfOSC32MHzMode = bFALSE;
clk.SysClkSrc = SYSCLKSRC_HFOSC;
clk.ADCCLkSrc = ADCCLKSRC_HFOSC;
clk.SysClkDiv = SYSCLKDIV_1;
clk.ADCClkDiv = ADCCLKDIV_1;
clk.LFOSCEn = bTRUE;
clk.HFXTALEn = bFALSE;
AD5940_CLKCfg(&clk);
AFERefCfg_Type ref;
AD5940_StructInit(&ref, sizeof(ref));
ref.HpBandgapEn = bTRUE;
ref.Hp1V1BuffEn = bTRUE;
ref.Hp1V8BuffEn = bTRUE;
ref.HSDACRefEn = bTRUE;
ref.LpBandgapEn = bFALSE;
ref.LpRefBufEn = bFALSE;
AD5940_REFCfgS(&ref);
AD5940_AFEPwrBW(AFEPWR_LP, AFEBW_250KHZ);
AD5940_INTCCfg(AFEINTC_0, AFEINTSRC_DFTRDY, bTRUE);
AD5940_INTCCfg(AFEINTC_1, AFEINTSRC_DFTRDY, bTRUE);
AD5940_INTCClrFlag(AFEINTSRC_ALLINT);
AGPIOCfg_Type gpio;
AD5940_StructInit(&gpio, sizeof(gpio));
gpio.FuncSet = GP0_INT;
gpio.OutputEnSet = AGPIO_Pin0;
AD5940_AGPIOCfg(&gpio);
FIFOCfg_Type fifo;
AD5940_StructInit(&fifo, sizeof(fifo));
fifo.FIFOEn = bFALSE;
fifo.FIFOMode = FIFOMODE_FIFO;
fifo.FIFOSize = FIFOSIZE_2KB;
fifo.FIFOSrc = FIFOSRC_DFT;
fifo.FIFOThresh = 4;
AD5940_FIFOCfg(&fifo);
SEQCfg_Type seq;
seq.SeqMemSize = SEQMEMSIZE_4KB;
seq.SeqBreakEn = bFALSE;
seq.SeqIgnoreEn = bFALSE;
seq.SeqCntCRCClr = bFALSE;
seq.SeqEnable = bTRUE;
seq.SeqWrTimer = 0;
AD5940_SEQCfg(&seq);
apply_hsloop();
ADCBaseCfg_Type adc;
adc.ADCMuxP = ADCMUXP_P_NODE;
adc.ADCMuxN = ADCMUXN_N_NODE;
adc.ADCPga = cfg->pga;
AD5940_ADCBaseCfgS(&adc);
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
}
void eis_reconfigure(const EISConfig *cfg)
{
memcpy(&ctx.cfg, cfg, sizeof(EISConfig));
resolve_config();
apply_hsloop();
}
/* ---------- internal helpers ---------- */
static void configure_freq(float freq_hz)
{
FreqParams_Type fp = AD5940_GetFreqParameters(freq_hz);
if (fp.HighPwrMode) {
fp.DftSrc = DFTSRC_ADCRAW;
fp.ADCSinc3Osr = ADCSINC3OSR_2;
fp.ADCSinc2Osr = 0;
fp.DftNum = DFTNUM_16384;
}
AD5940_WriteReg(REG_AFE_WGFCW,
AD5940_WGFreqWordCal(freq_hz, ctx.sys_clk));
ADCFilterCfg_Type filt;
AD5940_StructInit(&filt, sizeof(filt));
filt.ADCSinc3Osr = fp.ADCSinc3Osr;
filt.ADCSinc2Osr = fp.ADCSinc2Osr;
filt.ADCAvgNum = ADCAVGNUM_16;
filt.ADCRate = ADCRATE_800KHZ;
filt.BpNotch = bTRUE;
filt.BpSinc3 = bFALSE;
filt.Sinc2NotchEnable = bTRUE;
filt.Sinc3ClkEnable = bTRUE;
filt.Sinc2NotchClkEnable = bTRUE;
filt.DFTClkEnable = bTRUE;
filt.WGClkEnable = bTRUE;
AD5940_ADCFilterCfgS(&filt);
DFTCfg_Type dft;
dft.DftNum = fp.DftNum;
dft.DftSrc = fp.DftSrc;
dft.HanWinEn = bTRUE;
AD5940_DFTCfgS(&dft);
}
static int32_t sign_extend_18(uint32_t v)
{
return (v & (1UL << 17)) ? (int32_t)(v | 0xFFFC0000UL) : (int32_t)v;
}
static void dft_measure(uint32_t mux_p, uint32_t mux_n, iImpCar_Type *out)
{
AD5940_ADCMuxCfgS(mux_p, mux_n);
AD5940_AFECtrlS(AFECTRL_WG | AFECTRL_ADCPWR, bTRUE);
AD5940_Delay10us(25);
AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT, bTRUE);
uint32_t timeout = 10000000;
while (AD5940_INTCTestFlag(AFEINTC_1, AFEINTSRC_DFTRDY) == bFALSE) {
if (--timeout == 0) break;
}
AD5940_AFECtrlS(AFECTRL_ADCCNV | AFECTRL_DFT |
AFECTRL_WG | AFECTRL_ADCPWR, bFALSE);
AD5940_INTCClrFlag(AFEINTSRC_DFTRDY);
out->Real = sign_extend_18(AD5940_ReadAfeResult(AFERESULT_DFTREAL));
out->Image = sign_extend_18(AD5940_ReadAfeResult(AFERESULT_DFTIMAGE));
out->Image = -out->Image;
}
/* ---------- measurement ---------- */
int eis_measure_point(float freq_hz, EISPoint *out)
{
configure_freq(freq_hz);
iImpCar_Type v_rcal, v_rtia, v_tia, v_sense;
/* Phase 1: RTIA calibration through RCAL */
SWMatrixCfg_Type sw;
sw.Dswitch = ctx.rcal_sw_d;
sw.Pswitch = ctx.rcal_sw_p;
sw.Nswitch = ctx.rcal_sw_n;
sw.Tswitch = ctx.rcal_sw_t;
AD5940_SWMatrixCfgS(&sw);
AD5940_AFECtrlS(AFECTRL_HPREFPWR | AFECTRL_HSTIAPWR | AFECTRL_INAMPPWR |
AFECTRL_EXTBUFPWR | AFECTRL_DACREFPWR | AFECTRL_HSDACPWR |
AFECTRL_SINC2NOTCH, bTRUE);
dft_measure(ADCMUXP_P_NODE, ADCMUXN_N_NODE, &v_rcal);
dft_measure(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N, &v_rtia);
v_rtia.Real = -v_rtia.Real;
v_rtia.Image = -v_rtia.Image;
fImpCar_Type rtia = AD5940_ComplexDivInt(&v_rtia, &v_rcal);
rtia.Real *= ctx.rcal_ohms;
rtia.Image *= ctx.rcal_ohms;
/* Phase 2: DUT — software 4-wire */
sw.Dswitch = SWD_AIN3;
sw.Pswitch = SWP_AIN3;
sw.Nswitch = SWN_AIN0;
sw.Tswitch = SWT_AIN0 | SWT_TRTIA;
AD5940_SWMatrixCfgS(&sw);
AD5940_Delay10us(50);
dft_measure(ADCMUXP_HSTIA_P, ADCMUXN_HSTIA_N, &v_tia);
v_tia.Real = -v_tia.Real;
v_tia.Image = -v_tia.Image;
dft_measure(ADCMUXP_AIN2, ADCMUXN_AIN1, &v_sense);
AD5940_AFECtrlS(AFECTRL_WG | AFECTRL_ADCPWR | AFECTRL_ADCCNV |
AFECTRL_DFT | AFECTRL_SINC2NOTCH | AFECTRL_HSDACPWR |
AFECTRL_HSTIAPWR | AFECTRL_INAMPPWR |
AFECTRL_EXTBUFPWR, bFALSE);
sw.Dswitch = SWD_OPEN;
sw.Pswitch = SWP_OPEN;
sw.Nswitch = SWN_OPEN;
sw.Tswitch = SWT_OPEN;
AD5940_SWMatrixCfgS(&sw);
/* Z_DUT = V_sense × Rtia_cal / V_TIA */
fImpCar_Type fv_sense = { (float)v_sense.Real, (float)v_sense.Image };
fImpCar_Type fv_tia = { (float)v_tia.Real, (float)v_tia.Image };
fImpCar_Type num = AD5940_ComplexMulFloat(&fv_sense, &rtia);
fImpCar_Type z = AD5940_ComplexDivFloat(&num, &fv_tia);
out->freq_hz = freq_hz;
out->z_real = z.Real;
out->z_imag = z.Image;
out->mag_ohms = AD5940_ComplexMag(&z);
out->phase_deg = AD5940_ComplexPhase(&z) * (float)(180.0 / M_PI);
return 0;
}
int eis_sweep(EISPoint *out, uint32_t max_points)
{
uint32_t n = eis_calc_num_points(&ctx.cfg);
if (n > max_points) n = max_points;
SoftSweepCfg_Type sweep;
sweep.SweepEn = bTRUE;
sweep.SweepStart = ctx.cfg.freq_start_hz;
sweep.SweepStop = ctx.cfg.freq_stop_hz;
sweep.SweepPoints = n;
sweep.SweepLog = bTRUE;
sweep.SweepIndex = 0;
printf("\n%10s %12s %10s %12s %12s\n",
"Freq(Hz)", "|Z|(Ohm)", "Phase(deg)", "Re(Ohm)", "Im(Ohm)");
printf("--------------------------------------------------------------\n");
eis_measure_point(ctx.cfg.freq_start_hz, &out[0]);
printf("%10.1f %12.2f %10.2f %12.2f %12.2f\n",
out[0].freq_hz, out[0].mag_ohms, out[0].phase_deg,
out[0].z_real, out[0].z_imag);
for (uint32_t i = 1; i < n; i++) {
float freq;
AD5940_SweepNext(&sweep, &freq);
eis_measure_point(freq, &out[i]);
printf("%10.1f %12.2f %10.2f %12.2f %12.2f\n",
out[i].freq_hz, out[i].mag_ohms, out[i].phase_deg,
out[i].z_real, out[i].z_imag);
}
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
return (int)n;
}

47
main/eis.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef EIS_H
#define EIS_H
#include "ad5940.h"
#define EIS_MAX_POINTS 100
typedef enum {
RTIA_200 = 0, RTIA_1K, RTIA_5K, RTIA_10K,
RTIA_20K, RTIA_40K, RTIA_80K, RTIA_160K,
RTIA_EXT_DE0,
RTIA_COUNT
} EISRtia;
typedef enum {
RCAL_200R = 0, /* RCAL0 ↔ RCAL1 */
RCAL_3K, /* RCAL0 ↔ AIN0 */
RCAL_COUNT
} EISRcal;
typedef struct {
float freq_start_hz;
float freq_stop_hz;
uint16_t points_per_decade;
EISRtia rtia;
EISRcal rcal;
uint32_t pga;
uint32_t excit_amp;
} EISConfig;
typedef struct {
float freq_hz;
float mag_ohms;
float phase_deg;
float z_real;
float z_imag;
} EISPoint;
void eis_default_config(EISConfig *cfg);
void eis_init(const EISConfig *cfg);
void eis_reconfigure(const EISConfig *cfg);
int eis_measure_point(float freq_hz, EISPoint *out);
int eis_sweep(EISPoint *out, uint32_t max_points);
uint32_t eis_calc_num_points(const EISConfig *cfg);
#endif

View File

@ -1,32 +1,106 @@
#include <stdio.h>
#include "ad5940.h"
#include "ad5941_port.h"
#include "eis.h"
#include "ble.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#define AD5941_EXPECTED_ADIID 0x4144
static EISConfig cfg;
static EISPoint results[EIS_MAX_POINTS];
static bool afe_inited;
static void do_sweep(void)
{
if (!afe_inited) {
eis_init(&cfg);
afe_inited = true;
}
uint32_t n = eis_calc_num_points(&cfg);
int got = eis_sweep(results, n);
printf("Sweep complete: %d points\n", got);
ble_send_sweep_start(got, cfg.freq_start_hz, cfg.freq_stop_hz);
for (int i = 0; i < got; i++) {
ble_send_eis_point(i, &results[i]);
vTaskDelay(pdMS_TO_TICKS(10));
}
ble_send_sweep_end();
}
void app_main(void)
{
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
nvs_flash_erase();
nvs_flash_init();
}
printf("EIS4: AD5941 bring-up\n");
AD5940_MCUResourceInit(NULL);
vTaskDelay(pdMS_TO_TICKS(50));
AD5940_HWReset();
vTaskDelay(pdMS_TO_TICKS(200));
AD5940_Initialize();
uint32_t adiid = AD5940_ReadReg(REG_AFECON_ADIID);
uint32_t chipid = AD5940_ReadReg(REG_AFECON_CHIPID);
printf("ADIID: 0x%04lX %s\n", adiid,
adiid == AD5941_EXPECTED_ADIID ? "(OK)" : "(FAIL)");
if (adiid != AD5941_EXPECTED_ADIID) return;
printf("ADIID : 0x%04lX %s\n", adiid,
adiid == AD5941_EXPECTED_ADIID ? "(OK)" : "(UNEXPECTED)");
printf("CHIPID: 0x%04lX\n", chipid);
eis_default_config(&cfg);
if (adiid != AD5941_EXPECTED_ADIID)
printf("FAIL: cannot communicate with AD5941\n");
else
printf("AD5941 alive and responding\n");
ble_init();
printf("Waiting for BLE connection...\n");
ble_wait_for_connection();
ble_send_config(&cfg);
BleCommand cmd;
for (;;) {
if (ble_recv_command(&cmd, UINT32_MAX) != 0)
continue;
switch (cmd.type) {
case CMD_SET_SWEEP:
cfg.freq_start_hz = cmd.sweep.freq_start;
cfg.freq_stop_hz = cmd.sweep.freq_stop;
cfg.points_per_decade = cmd.sweep.ppd;
eis_reconfigure(&cfg);
printf("Sweep: %.0f-%.0f Hz, %u ppd\n",
cfg.freq_start_hz, cfg.freq_stop_hz, cfg.points_per_decade);
ble_send_config(&cfg);
break;
case CMD_SET_RTIA:
if (cmd.rtia < RTIA_COUNT) {
cfg.rtia = cmd.rtia;
eis_reconfigure(&cfg);
printf("RTIA: %u\n", cfg.rtia);
}
ble_send_config(&cfg);
break;
case CMD_SET_RCAL:
if (cmd.rcal < RCAL_COUNT) {
cfg.rcal = cmd.rcal;
eis_reconfigure(&cfg);
printf("RCAL: %u\n", cfg.rcal);
}
ble_send_config(&cfg);
break;
case CMD_START_SWEEP:
do_sweep();
break;
case CMD_GET_CONFIG:
ble_send_config(&cfg);
break;
}
}
}

334
sdkconfig
View File

@ -650,16 +650,296 @@ CONFIG_APPTRACE_LOCK_ENABLE=y
#
# Bluetooth
#
# CONFIG_BT_ENABLED is not set
CONFIG_BT_ENABLED=y
# CONFIG_BT_BLUEDROID_ENABLED is not set
CONFIG_BT_NIMBLE_ENABLED=y
# CONFIG_BT_CONTROLLER_ONLY is not set
CONFIG_BT_CONTROLLER_ENABLED=y
# CONFIG_BT_CONTROLLER_DISABLED is not set
#
# NimBLE Options
#
CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y
# CONFIG_BT_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set
# CONFIG_BT_NIMBLE_LOG_LEVEL_NONE is not set
# CONFIG_BT_NIMBLE_LOG_LEVEL_ERROR is not set
# CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING is not set
CONFIG_BT_NIMBLE_LOG_LEVEL_INFO=y
# CONFIG_BT_NIMBLE_LOG_LEVEL_DEBUG is not set
CONFIG_BT_NIMBLE_LOG_LEVEL=1
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1
CONFIG_BT_NIMBLE_MAX_BONDS=3
CONFIG_BT_NIMBLE_MAX_CCCDS=8
CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM=0
CONFIG_BT_NIMBLE_PINNED_TO_CORE_0=y
# CONFIG_BT_NIMBLE_PINNED_TO_CORE_1 is not set
CONFIG_BT_NIMBLE_PINNED_TO_CORE=0
CONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=4096
CONFIG_BT_NIMBLE_ROLE_CENTRAL=y
CONFIG_BT_NIMBLE_ROLE_PERIPHERAL=y
CONFIG_BT_NIMBLE_ROLE_BROADCASTER=y
CONFIG_BT_NIMBLE_ROLE_OBSERVER=y
CONFIG_BT_NIMBLE_GATT_CLIENT=y
CONFIG_BT_NIMBLE_GATT_SERVER=y
CONFIG_BT_NIMBLE_NVS_PERSIST=y
# CONFIG_BT_NIMBLE_SMP_ID_RESET is not set
CONFIG_BT_NIMBLE_SECURITY_ENABLE=y
CONFIG_BT_NIMBLE_SM_LEGACY=y
CONFIG_BT_NIMBLE_SM_SC=y
# CONFIG_BT_NIMBLE_SM_SC_DEBUG_KEYS is not set
CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_ENCRYPTION=y
CONFIG_BT_NIMBLE_SM_LVL=0
CONFIG_BT_NIMBLE_SM_SC_ONLY=0
CONFIG_BT_NIMBLE_PRINT_ERR_NAME=y
# CONFIG_BT_NIMBLE_DEBUG is not set
# CONFIG_BT_NIMBLE_DYNAMIC_SERVICE is not set
CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="nimble"
CONFIG_BT_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31
CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=256
CONFIG_BT_NIMBLE_ATT_MAX_PREP_ENTRIES=64
CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE=0
#
# Memory Settings
#
CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=12
CONFIG_BT_NIMBLE_MSYS_1_BLOCK_SIZE=256
CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=24
CONFIG_BT_NIMBLE_MSYS_2_BLOCK_SIZE=320
CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=24
CONFIG_BT_NIMBLE_TRANSPORT_ACL_SIZE=255
CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=70
CONFIG_BT_NIMBLE_TRANSPORT_EVT_COUNT=30
CONFIG_BT_NIMBLE_TRANSPORT_EVT_DISCARD_COUNT=8
CONFIG_BT_NIMBLE_L2CAP_COC_SDU_BUFF_COUNT=1
# end of Memory Settings
CONFIG_BT_NIMBLE_GATT_MAX_PROCS=4
# CONFIG_BT_NIMBLE_HS_FLOW_CTRL is not set
CONFIG_BT_NIMBLE_RPA_TIMEOUT=900
# CONFIG_BT_NIMBLE_MESH is not set
CONFIG_BT_NIMBLE_CRYPTO_STACK_MBEDTLS=y
CONFIG_BT_NIMBLE_HS_STOP_TIMEOUT_MS=2000
CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=y
CONFIG_BT_NIMBLE_MAX_CONN_REATTEMPT=3
# CONFIG_BT_NIMBLE_HANDLE_REPEAT_PAIRING_DELETION is not set
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=y
CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_2M_PHY=y
CONFIG_BT_NIMBLE_LL_CFG_FEAT_LE_CODED_PHY=y
# CONFIG_BT_NIMBLE_EXT_ADV is not set
CONFIG_BT_NIMBLE_EXT_SCAN=y
CONFIG_BT_NIMBLE_ENABLE_PERIODIC_SYNC=y
CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=0
# CONFIG_BT_NIMBLE_GATT_CACHING is not set
# CONFIG_BT_NIMBLE_INCL_SVC_DISCOVERY is not set
CONFIG_BT_NIMBLE_WHITELIST_SIZE=12
# CONFIG_BT_NIMBLE_TEST_THROUGHPUT_TEST is not set
# CONFIG_BT_NIMBLE_BLUFI_ENABLE is not set
CONFIG_BT_NIMBLE_USE_ESP_TIMER=y
CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE=y
# CONFIG_BT_NIMBLE_BLE_GATT_BLOB_TRANSFER is not set
#
# BLE Services
#
CONFIG_BT_NIMBLE_PROX_SERVICE=y
CONFIG_BT_NIMBLE_ANS_SERVICE=y
CONFIG_BT_NIMBLE_CTS_SERVICE=y
CONFIG_BT_NIMBLE_HTP_SERVICE=y
CONFIG_BT_NIMBLE_IPSS_SERVICE=y
CONFIG_BT_NIMBLE_TPS_SERVICE=y
CONFIG_BT_NIMBLE_IAS_SERVICE=y
CONFIG_BT_NIMBLE_LLS_SERVICE=y
CONFIG_BT_NIMBLE_SPS_SERVICE=y
CONFIG_BT_NIMBLE_HR_SERVICE=y
# CONFIG_BT_NIMBLE_HID_SERVICE is not set
CONFIG_BT_NIMBLE_BAS_SERVICE=y
# CONFIG_BT_NIMBLE_SVC_BAS_BATTERY_LEVEL_NOTIFY is not set
CONFIG_BT_NIMBLE_DIS_SERVICE=y
# CONFIG_BT_NIMBLE_SVC_DIS_MANUFACTURER_NAME is not set
# CONFIG_BT_NIMBLE_SVC_DIS_SERIAL_NUMBER is not set
# CONFIG_BT_NIMBLE_SVC_DIS_HARDWARE_REVISION is not set
# CONFIG_BT_NIMBLE_SVC_DIS_FIRMWARE_REVISION is not set
# CONFIG_BT_NIMBLE_SVC_DIS_SOFTWARE_REVISION is not set
# CONFIG_BT_NIMBLE_SVC_DIS_SYSTEM_ID is not set
# CONFIG_BT_NIMBLE_SVC_DIS_PNP_ID is not set
# CONFIG_BT_NIMBLE_SVC_DIS_INCLUDED is not set
CONFIG_BT_NIMBLE_GAP_SERVICE=y
#
# GAP Appearance write permissions
#
# CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE is not set
CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM=0
CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ENC=0
CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ATHN=0
CONFIG_BT_NIMBLE_SVC_GAP_APPEAR_WRITE_PERM_ATHR=0
# end of GAP Appearance write permissions
CONFIG_BT_NIMBLE_SVC_GAP_CAR_CHAR_NOT_SUPP=y
# CONFIG_BT_NIMBLE_SVC_GAP_CAR_NOT_SUPP is not set
# CONFIG_BT_NIMBLE_SVC_GAP_CAR_SUPP is not set
CONFIG_BT_NIMBLE_SVC_GAP_CENT_ADDR_RESOLUTION=-1
#
# GAP device name write permissions
#
# CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE is not set
# end of GAP device name write permissions
#
# Peripheral Preferred Connection Parameters (PPCP) settings
#
CONFIG_BT_NIMBLE_SVC_GAP_PPCP_MAX_CONN_INTERVAL=0
CONFIG_BT_NIMBLE_SVC_GAP_PPCP_MIN_CONN_INTERVAL=0
CONFIG_BT_NIMBLE_SVC_GAP_PPCP_SLAVE_LATENCY=0
CONFIG_BT_NIMBLE_SVC_GAP_PPCP_SUPERVISION_TMO=0
# end of Peripheral Preferred Connection Parameters (PPCP) settings
CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM=0
CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_ENC=0
CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHEN=0
CONFIG_BT_NIMBLE_SVC_GAP_NAME_WRITE_PERM_AUTHOR=0
# CONFIG_BT_NIMBLE_SVC_GAP_GATT_SECURITY_LEVEL is not set
# CONFIG_BT_NIMBLE_SVC_GAP_RPA_ONLY is not set
# end of BLE Services
# CONFIG_BT_NIMBLE_VS_SUPPORT is not set
# CONFIG_BT_NIMBLE_ENC_ADV_DATA is not set
# CONFIG_BT_NIMBLE_HIGH_DUTY_ADV_ITVL is not set
# CONFIG_BT_NIMBLE_HOST_ALLOW_CONNECT_WITH_SCAN is not set
# CONFIG_BT_NIMBLE_HOST_QUEUE_CONG_CHECK is not set
# CONFIG_BT_NIMBLE_GATTC_PROC_PREEMPTION_PROTECT is not set
# CONFIG_BT_NIMBLE_GATTC_AUTO_PAIR is not set
#
# Host-controller Transport
#
CONFIG_UART_HW_FLOWCTRL_DISABLE=y
# CONFIG_UART_HW_FLOWCTRL_CTS_RTS is not set
CONFIG_BT_NIMBLE_HCI_UART_FLOW_CTRL=0
CONFIG_BT_NIMBLE_HCI_UART_RTS_PIN=19
CONFIG_BT_NIMBLE_HCI_UART_CTS_PIN=23
# end of Host-controller Transport
CONFIG_BT_NIMBLE_EATT_CHAN_NUM=0
# CONFIG_BT_NIMBLE_SUBRATE is not set
# end of NimBLE Options
#
# Controller Options
#
CONFIG_BT_CTRL_MODE_EFF=1
CONFIG_BT_CTRL_BLE_MAX_ACT=6
CONFIG_BT_CTRL_BLE_MAX_ACT_EFF=6
CONFIG_BT_CTRL_BLE_STATIC_ACL_TX_BUF_NB=0
CONFIG_BT_CTRL_PINNED_TO_CORE_0=y
# CONFIG_BT_CTRL_PINNED_TO_CORE_1 is not set
CONFIG_BT_CTRL_PINNED_TO_CORE=0
CONFIG_BT_CTRL_HCI_MODE_VHCI=y
# CONFIG_BT_CTRL_HCI_MODE_UART_H4 is not set
CONFIG_BT_CTRL_HCI_TL=1
CONFIG_BT_CTRL_ADV_DUP_FILT_MAX=30
CONFIG_BT_BLE_CCA_MODE_NONE=y
# CONFIG_BT_BLE_CCA_MODE_HW is not set
# CONFIG_BT_BLE_CCA_MODE_SW is not set
CONFIG_BT_BLE_CCA_MODE=0
CONFIG_BT_CTRL_HW_CCA_VAL=75
CONFIG_BT_CTRL_HW_CCA_EFF=0
CONFIG_BT_CTRL_CE_LENGTH_TYPE_ORIG=y
# CONFIG_BT_CTRL_CE_LENGTH_TYPE_CE is not set
# CONFIG_BT_CTRL_CE_LENGTH_TYPE_SD is not set
CONFIG_BT_CTRL_CE_LENGTH_TYPE_EFF=0
CONFIG_BT_CTRL_TX_ANTENNA_INDEX_0=y
# CONFIG_BT_CTRL_TX_ANTENNA_INDEX_1 is not set
CONFIG_BT_CTRL_TX_ANTENNA_INDEX_EFF=0
CONFIG_BT_CTRL_RX_ANTENNA_INDEX_0=y
# CONFIG_BT_CTRL_RX_ANTENNA_INDEX_1 is not set
CONFIG_BT_CTRL_RX_ANTENNA_INDEX_EFF=0
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N24 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N21 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N18 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N15 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N12 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N9 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N6 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N3 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_N0 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P3 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P6 is not set
CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P9=y
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P12 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P15 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P18 is not set
# CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_P20 is not set
CONFIG_BT_CTRL_DFT_TX_POWER_LEVEL_EFF=11
CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y
CONFIG_BT_CTRL_BLE_ADV_REPORT_FLOW_CTRL_NUM=100
CONFIG_BT_CTRL_BLE_ADV_REPORT_DISCARD_THRSHOLD=20
CONFIG_BT_CTRL_BLE_SCAN_DUPL=y
CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DEVICE=y
# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA is not set
# CONFIG_BT_CTRL_SCAN_DUPL_TYPE_DATA_DEVICE is not set
CONFIG_BT_CTRL_SCAN_DUPL_TYPE=0
CONFIG_BT_CTRL_SCAN_DUPL_CACHE_SIZE=100
CONFIG_BT_CTRL_DUPL_SCAN_CACHE_REFRESH_PERIOD=0
# CONFIG_BT_CTRL_BLE_MESH_SCAN_DUPL_EN is not set
# CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
CONFIG_BT_CTRL_COEX_PHY_CODED_TX_RX_TLIM_EFF=0
#
# MODEM SLEEP Options
#
# CONFIG_BT_CTRL_MODEM_SLEEP is not set
# end of MODEM SLEEP Options
CONFIG_BT_CTRL_SLEEP_MODE_EFF=0
CONFIG_BT_CTRL_SLEEP_CLOCK_EFF=0
CONFIG_BT_CTRL_HCI_TL_EFF=1
# CONFIG_BT_CTRL_AGC_RECORRECT_EN is not set
# CONFIG_BT_CTRL_SCAN_BACKOFF_UPPERLIMITMAX is not set
# CONFIG_BT_BLE_ADV_DATA_LENGTH_ZERO_AUX is not set
CONFIG_BT_CTRL_CHAN_ASS_EN=y
CONFIG_BT_CTRL_LE_PING_EN=y
#
# BLE disconnects when Instant Passed (0x28) occurs
#
# CONFIG_BT_CTRL_BLE_LLCP_CONN_UPDATE is not set
# CONFIG_BT_CTRL_BLE_LLCP_CHAN_MAP_UPDATE is not set
# CONFIG_BT_CTRL_BLE_LLCP_PHY_UPDATE is not set
# end of BLE disconnects when Instant Passed (0x28) occurs
# CONFIG_BT_CTRL_RUN_IN_FLASH_ONLY is not set
CONFIG_BT_CTRL_DTM_ENABLE=y
CONFIG_BT_CTRL_BLE_MASTER=y
# CONFIG_BT_CTRL_BLE_TEST is not set
CONFIG_BT_CTRL_BLE_SCAN=y
CONFIG_BT_CTRL_BLE_SECURITY_ENABLE=y
CONFIG_BT_CTRL_BLE_ADV=y
# CONFIG_BT_CTRL_CHECK_CONNECT_IND_ACCESS_ADDRESS is not set
#
# Controller debug log Options (Experimental)
#
# end of Controller debug log Options (Experimental)
# end of Controller Options
#
# Common Options
#
CONFIG_BT_ALARM_MAX_NUM=50
# CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set
# CONFIG_BT_BLE_LOG_UHCI_OUT_ENABLED is not set
# end of Common Options
# CONFIG_BT_HCI_LOG_DEBUG_EN is not set
# end of Bluetooth
# CONFIG_BLE_MESH is not set
#
# Console Library
#
@ -789,7 +1069,8 @@ CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y
# Wireless Coexistence
#
CONFIG_ESP_COEX_ENABLED=y
# CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE is not set
CONFIG_ESP_COEX_SW_COEXIST_ENABLE=y
# CONFIG_ESP_COEX_POWER_MANAGEMENT is not set
# CONFIG_ESP_COEX_GPIO_DEBUG is not set
# end of Wireless Coexistence
@ -2233,6 +2514,11 @@ CONFIG_WL_SECTOR_SIZE=4096
#
CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16
CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30
# CONFIG_WIFI_PROV_BLE_BONDING is not set
CONFIG_WIFI_PROV_BLE_SEC_CONN=y
# CONFIG_WIFI_PROV_BLE_FORCE_ENCRYPTION is not set
# CONFIG_WIFI_PROV_BLE_NOTIFY is not set
# CONFIG_WIFI_PROV_KEEP_BLE_ON_AFTER_PROV is not set
CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
# CONFIG_WIFI_PROV_STA_FAST_SCAN is not set
# end of Wi-Fi Provisioning Manager
@ -2275,8 +2561,48 @@ CONFIG_STACK_CHECK_NONE=y
# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set
CONFIG_ESP32_APPTRACE_DEST_NONE=y
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
# CONFIG_EXTERNAL_COEX_ENABLE is not set
# CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set
# CONFIG_BLUEDROID_ENABLED is not set
CONFIG_NIMBLE_ENABLED=y
CONFIG_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y
# CONFIG_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set
CONFIG_NIMBLE_MAX_CONNECTIONS=1
CONFIG_NIMBLE_MAX_BONDS=3
CONFIG_NIMBLE_MAX_CCCDS=8
CONFIG_NIMBLE_L2CAP_COC_MAX_NUM=0
CONFIG_NIMBLE_PINNED_TO_CORE_0=y
# CONFIG_NIMBLE_PINNED_TO_CORE_1 is not set
CONFIG_NIMBLE_PINNED_TO_CORE=0
CONFIG_NIMBLE_TASK_STACK_SIZE=4096
CONFIG_BT_NIMBLE_TASK_STACK_SIZE=4096
CONFIG_NIMBLE_ROLE_CENTRAL=y
CONFIG_NIMBLE_ROLE_PERIPHERAL=y
CONFIG_NIMBLE_ROLE_BROADCASTER=y
CONFIG_NIMBLE_ROLE_OBSERVER=y
CONFIG_NIMBLE_NVS_PERSIST=y
CONFIG_NIMBLE_SM_LEGACY=y
CONFIG_NIMBLE_SM_SC=y
# CONFIG_NIMBLE_SM_SC_DEBUG_KEYS is not set
CONFIG_BT_NIMBLE_SM_SC_LVL=0
# CONFIG_NIMBLE_DEBUG is not set
CONFIG_NIMBLE_SVC_GAP_DEVICE_NAME="nimble"
CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31
CONFIG_NIMBLE_ATT_PREFERRED_MTU=256
CONFIG_NIMBLE_SVC_GAP_APPEARANCE=0
CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT=12
CONFIG_BT_NIMBLE_ACL_BUF_COUNT=24
CONFIG_BT_NIMBLE_ACL_BUF_SIZE=255
CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70
CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT=30
CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT=8
# CONFIG_NIMBLE_HS_FLOW_CTRL is not set
CONFIG_NIMBLE_RPA_TIMEOUT=900
# CONFIG_NIMBLE_MESH is not set
CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS=y
# CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
CONFIG_SW_COEXIST_ENABLE=y
CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y
CONFIG_ESP_WIFI_SW_COEXIST_ENABLE=y
# CONFIG_CAM_CTLR_DVP_CAM_ISR_IRAM_SAFE is not set
# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set
# CONFIG_MCPWM_ISR_IRAM_SAFE is not set