218 lines
5.1 KiB
Rust
218 lines
5.1 KiB
Rust
use esp_hal::{
|
|
gpio::{Level, Output, OutputConfig},
|
|
uart::{Uart,DataBits},
|
|
Async
|
|
};
|
|
|
|
use embassy_time::{
|
|
Timer,
|
|
Instant,
|
|
Duration
|
|
};
|
|
|
|
use log::{info};
|
|
|
|
|
|
pub enum Measure {
|
|
Rpm(i32),
|
|
Tps(u16, u8) // millivolts, %
|
|
}
|
|
|
|
pub struct Ecu<'a> {
|
|
tx_peripheral : esp_hal::peripherals::GPIO21<'a>,
|
|
rx_peripheral : esp_hal::peripherals::GPIO20<'a>,
|
|
uart_peripheral: esp_hal::peripherals::UART0<'a>,
|
|
initialized_at: Option<Instant>
|
|
|
|
}
|
|
|
|
impl Ecu<'_> {
|
|
pub fn new<'a>(
|
|
tx_peripheral : esp_hal::peripherals::GPIO21<'a>,
|
|
rx_peripheral : esp_hal::peripherals::GPIO20<'a>,
|
|
uart_peripheral: esp_hal::peripherals::UART0<'a>
|
|
) -> Ecu<'a> {
|
|
Ecu {
|
|
uart_peripheral,
|
|
tx_peripheral,
|
|
rx_peripheral,
|
|
initialized_at: None
|
|
}
|
|
}
|
|
|
|
fn is_initialized(&self) -> bool {
|
|
match self.initialized_at {
|
|
None => false,
|
|
Some(i) => i.elapsed() < Duration::from_millis(2500)
|
|
}
|
|
}
|
|
|
|
fn checksum(bytes: &mut [u8]) -> bool {
|
|
let last = bytes.len() - 1;
|
|
let mut sum : u8 = 0;
|
|
for b in &bytes[0..last] {
|
|
sum = sum.wrapping_sub(*b);
|
|
}
|
|
if sum == bytes[last] {
|
|
true
|
|
} else {
|
|
bytes[last] = sum;
|
|
false
|
|
}
|
|
}
|
|
|
|
|
|
async fn chat<'z>(uart: &mut Uart<'_, Async>,
|
|
send: &mut [u8],
|
|
receive: &'z mut [u8]) -> &'z [u8] {
|
|
let _ = Self::checksum(send);
|
|
uart.write(send).expect("write failed");
|
|
let _ = uart.flush();
|
|
let mut received = 0;
|
|
let mut buf = [0u8; 80];
|
|
let discard_count = send.len();
|
|
|
|
if send.len() > buf.len() {
|
|
panic!("send string limited to {} or fewer bytes", buf.len());
|
|
}
|
|
|
|
while received < send.len() {
|
|
received += uart.read_async(&mut buf[..discard_count]).await.expect("read failed");
|
|
}
|
|
|
|
if receive.len() > 0 {
|
|
let _ = uart.read_async(&mut receive[0..2]).await.expect("read failed");
|
|
let expected = receive[1].into();
|
|
|
|
received = 2;
|
|
while received < expected {
|
|
received += uart.read_async(&mut receive[received..]).await.expect("read failed");
|
|
}
|
|
&receive[0..expected]
|
|
} else {
|
|
&[]
|
|
}
|
|
}
|
|
|
|
async fn run_command<'a>(&mut self, command: &mut [u8], buf: &'a mut [u8]) -> &'a [u8] {
|
|
let need_init = !self.is_initialized();
|
|
|
|
if need_init {
|
|
let mut output = Output::new(
|
|
self.tx_peripheral.reborrow(),
|
|
Level::High, OutputConfig::default()
|
|
);
|
|
info!("pull");
|
|
output.set_low();
|
|
Timer::after_millis(70).await;
|
|
output.set_high();
|
|
Timer::after_millis(120).await;
|
|
}
|
|
let mut uart = Uart::new(
|
|
self.uart_peripheral.reborrow(),
|
|
esp_hal::uart::Config::default()
|
|
.with_baudrate(10_400)
|
|
.with_data_bits(DataBits::_8)
|
|
)
|
|
.expect("now I got a reason")
|
|
.with_rx(self.rx_peripheral.reborrow())
|
|
.with_tx(self.tx_peripheral.reborrow())
|
|
.into_async();
|
|
|
|
if need_init {
|
|
info!("atz");
|
|
let _ = Self::chat(&mut uart,
|
|
&mut [0xfe, 0x04, 0xff, 0xff],
|
|
&mut buf[0..0]).await;
|
|
Timer::after_millis(200).await;
|
|
|
|
let handshake = Self::chat(&mut uart,
|
|
&mut [0x72, 0x05, 0x00, 0xF0, 0x99],
|
|
buf).await;
|
|
info!("matches {}", handshake == b"\x02\x04\x00\xfa");
|
|
}
|
|
let now = Instant::now();
|
|
self.initialized_at = Some(now);
|
|
let ticks = now.as_ticks();
|
|
buf[0] = (ticks >> 56) as u8 & 0xff;
|
|
buf[1] = (ticks >> 48) as u8 & 0xff;
|
|
buf[2] = (ticks >> 40) as u8 & 0xff;
|
|
buf[3] = (ticks >> 32) as u8 & 0xff;
|
|
buf[4] = (ticks >> 24) as u8 & 0xff;
|
|
buf[5] = (ticks >> 16) as u8 & 0xff;
|
|
buf[6] = (ticks >> 8) as u8 & 0xff;
|
|
buf[7] = ticks as u8 & 0xff;
|
|
|
|
let len = (Self::chat(&mut uart, command, &mut buf[8..]).await).len();
|
|
&buf[0..len+8]
|
|
}
|
|
|
|
pub async fn fetch_table<'a>(&mut self, n: u8, buf: &'a mut [u8]) -> &'a [u8] {
|
|
self.run_command(&mut [0x72, 0x07, 0x72, n, 0, 0x14, 0x0], buf).await
|
|
}
|
|
|
|
pub async fn poll(&mut self, callback: impl AsyncFn(Measure)) {
|
|
let mut buf = [0u8; 80];
|
|
let table = self.fetch_table(0x11, &mut buf).await;
|
|
match table {
|
|
[rpm_h, rpm_l,
|
|
tps_v,
|
|
tps_100,
|
|
_ect_v,
|
|
_ect_deg,
|
|
_iat_v,
|
|
_iat_deg,
|
|
_map_v,
|
|
_map_kpa,
|
|
_, _,
|
|
_supply_v,
|
|
_speed,
|
|
_fuel_h, _fuel_l,
|
|
..
|
|
] => {
|
|
let rpm : i32 = (rpm_h << 8 + rpm_l).into();
|
|
callback(Measure::Rpm(rpm)).await;
|
|
callback(Measure::Tps((*tps_v as u16) * 5000u16/256, tps_100 / 16)).await;
|
|
},
|
|
_ => ()
|
|
}
|
|
}
|
|
|
|
pub async fn do_stuff(&mut self) {
|
|
let mut buf = [0u8; 80];
|
|
|
|
let table = self.fetch_table(0x11, &mut buf).await;
|
|
if table.len() > 0 {
|
|
for b in table {
|
|
info!("table {} {:x?}", 0x11, b);
|
|
}
|
|
}
|
|
// reply for table 11 query
|
|
// 2 - dest
|
|
// 1a - (26) chars in message
|
|
// 72 - query type
|
|
// 11 - table mumber
|
|
// 0 - offset
|
|
// ** table starts here
|
|
// 0 0 - rpm
|
|
// 6 - tps volt * 5/256
|
|
// 0 - tps %
|
|
// ff - ect volt * 5/256
|
|
// a - ect deg c
|
|
// ff - iat volt * 5/256
|
|
// a - iat deg c
|
|
// f1 - map volt
|
|
// a8 - map kPa
|
|
// ff
|
|
// ff
|
|
// 7c - battery voltage (0.1v) = 12.4
|
|
// 0 - speed
|
|
// 0 0 - fuel inj (?)
|
|
// 80 - ctx-obd says (?) and = 80
|
|
// 93 - ctx-obd says 63
|
|
// 5a - ctx-obd says 1a
|
|
// 37 - ctx-obd says 51
|
|
// 92 - checksum
|
|
}
|
|
}
|