eculocate

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

ecu.rs (5177B)


      1 use esp_hal::{
      2     gpio::{Level, Output, OutputConfig},
      3     uart::{Uart,DataBits},
      4     Async
      5 };
      6 
      7 use embassy_time::{
      8     Timer,
      9     Instant,
     10     Duration
     11 };
     12 
     13 use log::{info};
     14 
     15 
     16 pub enum Measure {
     17     Rpm(i32),
     18     Tps(u16, u8)		// millivolts, %
     19 }
     20 
     21 pub struct Ecu<'a> {
     22     tx_peripheral : esp_hal::peripherals::GPIO21<'a>,
     23     rx_peripheral : esp_hal::peripherals::GPIO20<'a>,
     24     uart_peripheral: esp_hal::peripherals::UART0<'a>,
     25     initialized_at: Option<Instant>
     26 
     27 }
     28 
     29 impl Ecu<'_> {
     30     pub fn new<'a>(
     31 	tx_peripheral : esp_hal::peripherals::GPIO21<'a>,
     32 	rx_peripheral : esp_hal::peripherals::GPIO20<'a>,
     33 	uart_peripheral: esp_hal::peripherals::UART0<'a>
     34     ) -> Ecu<'a> {
     35 	Ecu {
     36 	    uart_peripheral,
     37 	    tx_peripheral,
     38 	    rx_peripheral,
     39 	    initialized_at: None
     40 	}
     41     }
     42 
     43     fn is_initialized(&self) -> bool {
     44 	match self.initialized_at {
     45 	    None => false,
     46 	    Some(i) => i.elapsed() < Duration::from_millis(2500)
     47 	}
     48     }
     49 
     50     fn checksum(bytes: &mut [u8]) -> bool {
     51 	let last = bytes.len() - 1;
     52 	let mut sum : u8 = 0;
     53 	for b in &bytes[0..last] {
     54 	    sum = sum.wrapping_sub(*b);
     55 	}
     56 	if sum == bytes[last] {
     57 	    true
     58 	} else {
     59 	    bytes[last] = sum;
     60 	    false
     61 	}
     62     }
     63 
     64 
     65     async fn chat<'z>(uart: &mut Uart<'_, Async>,
     66 		      send: &mut [u8],
     67 		      receive: &'z mut [u8]) -> &'z [u8] {
     68 	let _ = Self::checksum(send);
     69 	uart.write(send).expect("write failed");
     70 	let _ = uart.flush();
     71 	let mut received = 0;
     72 	let mut buf = [0u8; 80];
     73 	let discard_count = send.len();
     74 
     75 	if send.len() > buf.len() {
     76 	    panic!("send string limited to {} or fewer bytes", buf.len());
     77 	}
     78 
     79 	while received < send.len() {
     80 	    received += uart.read_async(&mut buf[..discard_count]).await.expect("read failed");
     81 	}
     82 
     83 	if receive.len() > 0 {
     84 	    let _ = uart.read_async(&mut receive[0..2]).await.expect("read failed");
     85 	    let expected = receive[1].into();
     86 
     87 	    received = 2;
     88 	    while received < expected {
     89 		received += uart.read_async(&mut receive[received..]).await.expect("read failed");
     90 	    }
     91 	    &receive[0..expected]
     92 	} else {
     93 	    &[]
     94 	}
     95     }
     96 
     97     async fn run_command<'a>(&mut self, command: &mut [u8], buf: &'a mut [u8]) -> &'a [u8] {
     98 	let need_init = !self.is_initialized();
     99 
    100 	if need_init {
    101 	    let mut output = Output::new(
    102 		self.tx_peripheral.reborrow(),
    103 		Level::High, OutputConfig::default()
    104 	    );
    105 	    info!("pull");
    106 	    output.set_low();
    107 	    Timer::after_millis(70).await;
    108 	    output.set_high();
    109 	    Timer::after_millis(120).await;
    110 	}
    111 	let mut uart = Uart::new(
    112 	    self.uart_peripheral.reborrow(),
    113 	    esp_hal::uart::Config::default()
    114 		.with_baudrate(10_400)
    115 		.with_data_bits(DataBits::_8)
    116 	)
    117 	    .expect("now I got a reason")
    118 	    .with_rx(self.rx_peripheral.reborrow())
    119 	    .with_tx(self.tx_peripheral.reborrow())
    120 	    .into_async();
    121 
    122 	if need_init {
    123 	    info!("atz");
    124 	    let _ = Self::chat(&mut uart,
    125 			       &mut [0xfe, 0x04, 0xff, 0xff],
    126 			       &mut buf[0..0]).await;
    127 	    Timer::after_millis(200).await;
    128 
    129 	    let handshake = Self::chat(&mut uart,
    130 				       &mut [0x72, 0x05, 0x00, 0xF0, 0x99],
    131 				       buf).await;
    132 	    info!("matches {}", handshake == b"\x02\x04\x00\xfa");
    133 	}
    134 	let now = Instant::now();
    135 	self.initialized_at = Some(now);
    136 	let ticks = now.as_ticks();
    137 	buf[0] = (ticks >> 56) as u8 & 0xff;
    138 	buf[1] = (ticks >> 48) as u8 & 0xff;
    139 	buf[2] = (ticks >> 40) as u8 & 0xff;
    140 	buf[3] = (ticks >> 32) as u8 & 0xff;
    141 	buf[4] = (ticks >> 24) as u8 & 0xff;
    142 	buf[5] = (ticks >> 16) as u8 & 0xff;
    143 	buf[6] = (ticks >> 8) as u8 & 0xff;
    144 	buf[7] = ticks as u8 & 0xff;
    145 
    146 	let len = (Self::chat(&mut uart, command, &mut buf[8..]).await).len();
    147 	&buf[0..len+8]
    148     }
    149 
    150     pub async fn fetch_table<'a>(&mut self, n: u8, buf: &'a mut [u8]) -> &'a [u8] {
    151 	self.run_command(&mut [0x72, 0x07, 0x72, n, 0, 0x14, 0x0], buf).await
    152     }
    153 
    154     pub async fn poll(&mut self, callback: impl AsyncFn(Measure)) {
    155 	let mut buf = [0u8; 80];
    156 	let table = self.fetch_table(0x11, &mut buf).await;
    157 	match table {
    158 	    [rpm_h, rpm_l,
    159 	     tps_v,
    160 	     tps_100,
    161 	     _ect_v,
    162 	     _ect_deg,
    163 	     _iat_v,
    164 	     _iat_deg,
    165 	     _map_v,
    166 	     _map_kpa,
    167 	     _, _,
    168 	     _supply_v,
    169 	     _speed,
    170 	     _fuel_h, _fuel_l,
    171 	     ..
    172 	     ] => {
    173 		let rpm : i32 = (rpm_h << 8 + rpm_l).into();
    174 		callback(Measure::Rpm(rpm)).await;
    175 		callback(Measure::Tps((*tps_v as u16)  * 5000u16/256, tps_100  / 16)).await;
    176 	    },
    177 	    _ => ()
    178 	}
    179     }
    180 
    181     pub async fn do_stuff(&mut self) {
    182 	let mut buf = [0u8; 80];
    183 
    184 	let table = self.fetch_table(0x11, &mut buf).await;
    185 	if table.len() > 0 {
    186 	    for b in table {
    187 		info!("table {} {:x?}", 0x11, b);
    188 	    }
    189 	}
    190 	// reply for table 11 query
    191 	//  2   - dest
    192 	//  1a  - (26) chars in message
    193 	//  72  - query type
    194 	//  11  - table mumber
    195 	//  0   - offset
    196 	// ** table starts here
    197 	//  0 0 - rpm
    198 	//  6   - tps volt * 5/256
    199 	//  0   - tps %
    200 	//  ff  - ect volt * 5/256
    201 	//  a   - ect deg c
    202 	//  ff  - iat volt * 5/256
    203 	//  a   - iat deg c
    204 	//  f1  - map volt
    205 	//  a8  - map kPa
    206 	//  ff
    207 	//  ff
    208 	//  7c  - battery voltage (0.1v)  = 12.4
    209 	//  0   - speed
    210 	//  0 0 - fuel inj  (?)
    211 	// 80   - ctx-obd says (?) and = 80
    212 	// 93   - ctx-obd says 63
    213 	// 5a   - ctx-obd says 1a
    214 	// 37   - ctx-obd says 51
    215 	// 92   - checksum
    216     }
    217 }