Files
eculocate/ecu-esp32/src/ecu.rs
T

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
}
}