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 }