diff --git a/bpdt-firmware/bpdt-adapter-stm32h5/.cproject b/bpdt-firmware/bpdt-adapter-stm32h5/.cproject index 5598d8b..b3b7c90 100644 --- a/bpdt-firmware/bpdt-adapter-stm32h5/.cproject +++ b/bpdt-firmware/bpdt-adapter-stm32h5/.cproject @@ -32,6 +32,10 @@ + @@ -48,6 +52,8 @@ + + @@ -105,12 +111,16 @@ diff --git a/bpdt-firmware/bpdt-adapter-stm32h5/Core/BT_HC06_Libs/hc_06.c b/bpdt-firmware/bpdt-adapter-stm32h5/Core/BT_HC06_Libs/hc_06.c new file mode 100644 index 0000000..71f4f3c --- /dev/null +++ b/bpdt-firmware/bpdt-adapter-stm32h5/Core/BT_HC06_Libs/hc_06.c @@ -0,0 +1,743 @@ +#include "hc_06.h" +#include +#include +#include +#include "IKW1281Connection.h" +#include "kline.h" + +extern UART_HandleTypeDef huart2; + +/* ========================= + TX delay / non-blocking queue + ========================= + Goal: delay every binary reply by ~10ms without blocking the CPU. + Implementation: enqueue TX buffers with a due timestamp, then send using + HAL_UART_Transmit_IT() when due, chaining via TxCplt callback. + + Notes: + - Only binary frames (HC06_SendFrame / HC06_SendDataReply) use this queue. + - ASCII helpers remain blocking (debug only). +*/ +#define HC06_TX_DELAY_MS 10u +#define HC06_TX_QUEUE_LEN 8u +#define HC06_TX_BUF_MAX 80u // enough for max CMD_DATA_REQUEST reply (~70B) + +typedef struct { + uint32_t due_ms; + uint16_t len; + uint8_t buf[HC06_TX_BUF_MAX]; +} hc06_tx_item_t; + +static volatile uint8_t s_tx_head = 0; +static volatile uint8_t s_tx_tail = 0; +static volatile uint8_t s_tx_count = 0; +static volatile uint8_t s_tx_busy = 0; +static hc06_tx_item_t s_tx_q[HC06_TX_QUEUE_LEN]; + +static void HC06_TxReset(void) +{ + s_tx_head = 0; + s_tx_tail = 0; + s_tx_count = 0; + s_tx_busy = 0; +} + +static HAL_StatusTypeDef HC06_TxEnqueue(const uint8_t *data, uint16_t len) +{ + if (!data || len == 0u || len > HC06_TX_BUF_MAX) return HAL_ERROR; + if (s_tx_count >= HC06_TX_QUEUE_LEN) return HAL_BUSY; + + hc06_tx_item_t *it = &s_tx_q[s_tx_tail]; + memcpy(it->buf, data, len); + it->len = len; + it->due_ms = HAL_GetTick() + HC06_TX_DELAY_MS; + + s_tx_tail = (uint8_t)((s_tx_tail + 1u) % HC06_TX_QUEUE_LEN); + s_tx_count++; + return HAL_OK; +} + +static void HC06_TxKick(void) +{ + if (s_tx_busy) return; + if (s_tx_count == 0u) return; + + hc06_tx_item_t *it = &s_tx_q[s_tx_head]; + + // wait until due timestamp + if ((int32_t)(HAL_GetTick() - it->due_ms) < 0) return; + + if (HAL_UART_Transmit_IT(&huart2, it->buf, it->len) == HAL_OK) { + s_tx_busy = 1u; + } +} + +// Call this from HAL_UART_TxCpltCallback() for USART2 +void HC06_UART_TxCpltCallback(UART_HandleTypeDef *huart) +{ + if (!huart) return; + if (huart->Instance != huart2.Instance) return; + if (!s_tx_busy) return; + + // pop the item that just finished + if (s_tx_count > 0u) { + s_tx_head = (uint8_t)((s_tx_head + 1u) % HC06_TX_QUEUE_LEN); + s_tx_count--; + } + s_tx_busy = 0u; + + // send next (if already due) + HC06_TxKick(); +} + +/* ========================= + Your project functions + ========================= */ +extern void InitPSG5Comm(void); +extern float ReadDfi(void); +extern int WriteDfi(float dfi, int version); +extern int ClearFaultCodes(void); +extern uint8_t ReadAudiPin(uint16_t* pin); +static void HC06_ForceRearmRx(void); + +static void BT_InitPSG5Comm(void); +static void BT_ReadDfi(void); +static void BT_WriteDfi(void); +static void BT_ReadDTC(void); +static void BT_ClearDTC(void); +static void BT_ReadAudiPin(void); + +/* ========================= + USER SETTINGS (edit) + ========================= */ +#define HC06_DO_AT_SETUP_AT_BOOT 1 +#define HC06_NAME "HC - dFi Tool v1" + +// Many HC-06 firmwares do NOT support PIN change; leave disabled unless proven. +#define HC06_TRY_SET_PIN 0 +#define HC06_PIN_STR "1234" + +// AT command send style: you already confirmed AT\r\n works. +#define HC06_AT_USE_CRLF 1 + +// AT wait timeouts (ms) +#define HC06_AT_WAIT_MS_AT 600 +#define HC06_AT_WAIT_MS_NAME 800 +#define HC06_AT_WAIT_MS_PIN 800 + +/* ========================= + Globals + ========================= */ +volatile uint8_t g_hc06_frame_ready = 0; +hc06_frame_t g_hc06_frame; + +/* 1-byte RX for everything */ +static uint8_t rx_byte; +uint8_t commandPending = 0; + +/* ========================= + Mode + ========================= */ +typedef enum { + HC06_MODE_AT = 0, + HC06_MODE_BIN = 1 +} hc06_mode_t; + +static volatile hc06_mode_t s_mode = HC06_MODE_AT; + +/* ========================= + AT capture + ========================= */ +static volatile char s_at_buf[64]; +static volatile uint8_t s_at_len = 0; +static volatile uint8_t s_at_done = 0; + +static void AT_Clear(void) +{ + s_at_len = 0; + s_at_done = 0; + memset((void*)s_at_buf, 0, sizeof(s_at_buf)); +} + +static void AT_Wait(uint32_t timeout_ms) +{ + uint32_t t0 = HAL_GetTick(); + while (!s_at_done && (HAL_GetTick() - t0) < timeout_ms) { + // idle + } +} + +/* ========================= + Binary frame parser + ========================= */ +typedef enum { + ST_SYNC = 0, + ST_CMD, + ST_REQ_ID, + ST_REQ_CRC, + ST_D0, ST_D1, ST_D2, ST_D3, ST_D4, ST_D5, + ST_CRC +} hc06_rx_state_t; + +static hc06_rx_state_t st = ST_SYNC; +static uint8_t payload[7]; // [0]=cmd, [1..6]=data bytes +static uint8_t pi = 0; +static uint8_t crc = 0; + +static uint16_t u16_le(uint8_t lo, uint8_t hi) +{ + return (uint16_t)lo | ((uint16_t)hi << 8); +} + +static void u16_to_le(uint16_t v, uint8_t *lo, uint8_t *hi) +{ + *lo = (uint8_t)(v & 0xFF); + *hi = (uint8_t)((v >> 8) & 0xFF); +} + +/* CRC-8 poly 0x07, init 0x00, over [cmd + 6 data bytes] */ +static uint8_t crc8_update(uint8_t c, uint8_t data) +{ + c ^= data; + for (int i = 0; i < 8; i++) { + c = (c & 0x80) ? (uint8_t)((c << 1) ^ 0x07) : (uint8_t)(c << 1); + } + return c; +} + +/* ========================= + Public: DFI scaling + ========================= */ +int16_t HC06_DfiFloatToS16(float dfi) +{ + float s = dfi * (256.0f / 3.0f); + if (s > 32767.0f) s = 32767.0f; + if (s < -32768.0f) s = -32768.0f; + return (int16_t)lroundf(s); +} + +float HC06_DfiS16ToFloat(int16_t s) +{ + return ((float)s) * (3.0f / 256.0f); +} + +/* ========================= + TX helpers + ========================= */ +HAL_StatusTypeDef HC06_SendAscii(const char *s) +{ + if (!s) return HAL_ERROR; + return HAL_UART_Transmit(&huart2, (uint8_t*)s, (uint16_t)strlen(s), 200); +} + +HAL_StatusTypeDef HC06_SendAsciiLn(const char *s) +{ + HAL_StatusTypeDef a = HC06_SendAscii(s); + HAL_StatusTypeDef b = HC06_SendAscii("\r\n"); + return (a == HAL_OK && b == HAL_OK) ? HAL_OK : HAL_ERROR; +} + +HAL_StatusTypeDef HC06_SendFrame(uint8_t cmd, uint16_t w1, uint16_t w2, uint16_t w3) +{ + uint8_t buf[9]; // sync + cmd + 6 data + crc + uint8_t c = 0; + + buf[0] = HC06_SYNC; + buf[1] = cmd; + + uint8_t lo, hi; + u16_to_le(w1, &lo, &hi); buf[2] = lo; buf[3] = hi; + u16_to_le(w2, &lo, &hi); buf[4] = lo; buf[5] = hi; + u16_to_le(w3, &lo, &hi); buf[6] = lo; buf[7] = hi; + + for (int i = 1; i <= 7; i++) c = crc8_update(c, buf[i]); + buf[8] = c; + + // Non-blocking + delayed TX + HAL_StatusTypeDef st = HC06_TxEnqueue(buf, (uint16_t)sizeof(buf)); + HC06_TxKick(); + return st; +} + +/* ========================= + AT send helpers + ========================= */ +static void AT_SendRaw(const char *s) +{ + (void)HC06_SendAscii(s); +} + +static void AT_SendCmd(const char *cmd_no_prefix) +{ + char buf[96]; +#if HC06_AT_USE_CRLF + snprintf(buf, sizeof(buf), "AT+%s\r\n", cmd_no_prefix); +#else + snprintf(buf, sizeof(buf), "AT+%s", cmd_no_prefix); +#endif + AT_SendRaw(buf); +} + +/* ========================= + Init + ========================= */ +void HC06_Init(void) +{ + s_mode = HC06_MODE_AT; + HC06_TxReset(); + g_hc06_frame_ready = 0; + HC06_ForceRearmRx(); +} + +/* Call once at boot (optional). Module must be NOT connected (LED blinking). */ +void HC06_AT_BootSetup(void) +{ +#if HC06_DO_AT_SETUP_AT_BOOT + // Ensure we're in AT mode and buffer is clean + s_mode = HC06_MODE_AT; + AT_Clear(); + + // 1) Basic AT check +#if HC06_AT_USE_CRLF + AT_SendRaw("AT\r\n"); +#else + AT_SendRaw("AT"); +#endif + AT_Wait(HC06_AT_WAIT_MS_AT); + // Expect "OK" in s_at_buf (you saw OKsetname for name) + + // 2) Set name + AT_Clear(); + { + char cmd[64]; + snprintf(cmd, sizeof(cmd), "NAME%s", HC06_NAME); + AT_SendCmd(cmd); + } + AT_Wait(HC06_AT_WAIT_MS_NAME); + // Expect "OKsetname" + +#if HC06_TRY_SET_PIN + AT_Clear(); + { + char cmd[32]; + snprintf(cmd, sizeof(cmd), "PIN%s", HC06_PIN_STR); + AT_SendCmd(cmd); + } + AT_Wait(HC06_AT_WAIT_MS_PIN); +#endif + + // Switch to BIN mode after setup + s_mode = HC06_MODE_BIN; +#else + // Directly start in binary mode + s_mode = HC06_MODE_BIN; +#endif + HC06_ForceRearmRx(); +} + +// Variable-length reply for CMD 0x84: SYNC, CMD, STATUS, LEN(u16 LE), PAYLOAD, CRC +// CRC is over [CMD, STATUS, LENlo, LENhi, PAYLOAD] +#define HC06_MAX_PAYLOAD 64 +static HAL_StatusTypeDef HC06_SendDataReply(uint8_t status, const uint8_t *payloadIn, uint16_t len) +{ + if (status != 0x00) { + len = 0; + payloadIn = NULL; + } + if (len > HC06_MAX_PAYLOAD) len = HC06_MAX_PAYLOAD; + + uint8_t buf[1 + 1 + 1 + 2 + HC06_MAX_PAYLOAD + 1]; + uint16_t idx = 0; + uint8_t c = 0; + + buf[idx++] = HC06_SYNC; + buf[idx++] = 0x84; + buf[idx++] = status; + + uint8_t lo, hi; + u16_to_le(len, &lo, &hi); + buf[idx++] = lo; + buf[idx++] = hi; + + for (uint16_t i = 0; i < len; i++) { + buf[idx++] = payloadIn ? payloadIn[i] : 0; + } + + for (uint16_t i = 1; i < idx; i++) c = crc8_update(c, buf[i]); + buf[idx++] = c; + + // Non-blocking + delayed TX + HAL_StatusTypeDef st = HC06_TxEnqueue(buf, idx); + HC06_TxKick(); + return st; +} + +/* ========================= + RX callback (1 byte) + ========================= */ +void HC06_UART_RxByteCallback(UART_HandleTypeDef *huart) +{ + if (huart->Instance != huart2.Instance) return; + + uint8_t x = rx_byte; + + if (s_mode == HC06_MODE_AT) + { + char c = (char)x; + + // Capture until newline or full + if (c == '\n' || s_at_len >= (sizeof(s_at_buf) - 1)) + { + s_at_buf[s_at_len] = 0; + s_at_done = 1; + } + else if (c != '\r') + { + s_at_buf[s_at_len++] = c; + } + + // continue RX + HAL_UART_Receive_IT(&huart2, &rx_byte, 1); + return; + } + + // Binary frame parsing (robust resync + CRC) + switch (st) + { + case ST_SYNC: + if (x == HC06_SYNC) { + st = ST_CMD; + pi = 0; + crc = 0; + } + break; + + case ST_CMD: + payload[pi++] = x; // cmd + crc = crc8_update(crc, x); + if (x == CMD_DATA_REQUEST) { + // short request: SYNC, CMD(0x04), REQ_ID, CRC + st = ST_REQ_ID; + } else { + st = ST_D0; + } + break; + + case ST_REQ_ID: + payload[pi++] = x; // req_id + crc = crc8_update(crc, x); + st = ST_REQ_CRC; + break; + + case ST_REQ_CRC: + if (x == crc) { + g_hc06_frame.cmd = payload[0]; + g_hc06_frame.w1 = (uint16_t)payload[1]; // REQ_ID in low byte + g_hc06_frame.w2 = 0; + g_hc06_frame.w3 = 0; + g_hc06_frame_ready = 1; + } + st = ST_SYNC; + break; + + case ST_D0: case ST_D1: case ST_D2: case ST_D3: case ST_D4: case ST_D5: + payload[pi++] = x; // data bytes + crc = crc8_update(crc, x); + if (pi >= 7) st = ST_CRC; + else st = (hc06_rx_state_t)((int)st + 1); + break; + + case ST_CRC: + if (x == crc) { + g_hc06_frame.cmd = payload[0]; + g_hc06_frame.w1 = u16_le(payload[1], payload[2]); + g_hc06_frame.w2 = u16_le(payload[3], payload[4]); + g_hc06_frame.w3 = u16_le(payload[5], payload[6]); + g_hc06_frame_ready = 1; + } + st = ST_SYNC; // resync always + break; + + default: + st = ST_SYNC; + break; + } + + HAL_UART_Receive_IT(&huart2, &rx_byte, 1); +} + +/* ========================= + Command processing + ========================= */ +static uint8_t reply_cmd(uint8_t cmd) { return (uint8_t)(cmd | 0x80); } + +// ------------------------------- +// Data Request placeholder handlers +// ------------------------------- +static uint8_t HC06_HandleReq_FwVersion(uint8_t *out, uint16_t *outLen) { (void)out; *outLen = 0; return HC06_STATUS_OK; } + +// These must exist somewhere in your project (or change to match your actual symbols) +extern char identStr[11]; +extern uint8_t connectionAlive; +//extern uint8_t BitBang; + +static uint8_t HC06_HandleReq_Ident(uint8_t *out, uint16_t *outLen) +{ + // Send exactly 11 bytes (no null terminator expected/required) + uint8_t empty = 1; + for (uint16_t i = 0; i < 11; i++) { + if((uint8_t)identStr[i]){ + empty = 0; + break; + } + } + if(empty){ + IdentifyEcu(); + } + for (uint16_t i = 0; i < 11; i++) { + out[i] = (uint8_t)identStr[i]; + } + *outLen = 11; + return HC06_STATUS_OK; +} + +static uint8_t HC06_HandleReq_Status(uint8_t *out, uint16_t *outLen) +{ + out[0] = connectionAlive; + out[1] = BitBang; + *outLen = 2; + return HC06_STATUS_OK; +} + +int N_fc = 0; +FaultCode fault_codes[16]; + +static uint8_t HC06_HandleReq_Error(uint8_t *out, uint16_t *outLen) +{ + for (uint16_t i = 0; i < N_fc; i++) { + uint8_t ind = 2*i; + out[ind] = fault_codes[i].dtc; + out[ind+1] = fault_codes[i].status; + } + *outLen = 2*N_fc; + return HC06_STATUS_OK; +} + +static uint8_t HC06_HandleReq_Config(uint8_t *out, uint16_t *outLen) { (void)out; *outLen = 0; return HC06_STATUS_NOT_IMPLEMENTED; } +static uint8_t HC06_HandleReq_Reserved(uint8_t *out, uint16_t *outLen) { (void)out; *outLen = 0; return HC06_STATUS_NOT_IMPLEMENTED; } + +static uint8_t HC06_DispatchDataRequest(uint8_t reqId, uint8_t *out, uint16_t *outLen) +{ + switch (reqId) { + case REQ_FW_VERSION: return HC06_HandleReq_FwVersion(out, outLen); + case REQ_IDENT: return HC06_HandleReq_Ident(out, outLen); + case REQ_STATUS: return HC06_HandleReq_Status(out, outLen); + case REQ_CONFIG: return HC06_HandleReq_Config(out, outLen); + case REQ_ERROR: return HC06_HandleReq_Error(out, outLen); + case REQ_RESERVED: + default: return HC06_HandleReq_Reserved(out, outLen); + } +} + +void HC06_Process(void) +{ + // Always allow delayed TX queue to progress + HC06_TxKick(); + + if (!g_hc06_frame_ready) return; + g_hc06_frame_ready = 0; + + const uint8_t cmd = g_hc06_frame.cmd; + const uint16_t w3 = g_hc06_frame.w3; + + if(commandPending){return;} + + commandPending = 1; + switch (cmd) + { + case CMD_INIT_COMM: + { + BT_InitPSG5Comm(); + } break; + + case CMD_READ_DFI: + { + //first should check if connection is alive + BT_ReadDfi(); + } break; + + case CMD_WRITE_DFI: + { + BT_WriteDfi(); + } break; + + case CMD_DATA_REQUEST: + { + uint8_t reqId = (uint8_t)(g_hc06_frame.w1 & 0xFF); + uint8_t out[HC06_MAX_PAYLOAD]; + uint16_t outLen = 0; + + uint8_t stc = HC06_DispatchDataRequest(reqId, out, &outLen); + (void)HC06_SendDataReply(stc, out, outLen); + } break; + + case CMD_READ_DTC: + { + BT_ReadDTC(); + } break; + + case CMD_ERASE_DTC: + { + BT_ClearDTC(); + } break; + case CMD_READ_AUDI_PIN: + { + BT_ReadAudiPin(); + } break; + case CMD_WRITE_AUDI_PIN: + { + BT_ReadAudiPin(); + } break; + + default: + { + HC06_SendAsciiLn("ERR Unknown CMD"); + // Return error frame with original cmd in w3 + HC06_SendFrame(0xFF, 0, 0, (uint16_t)cmd); + } break; + } + commandPending = 0; +} + +static void HC06_ForceRearmRx(void) +{ + // Stop anything currently running on UART2 + HAL_UART_AbortReceive_IT(&huart2); + HAL_UART_AbortReceive(&huart2); + HAL_UART_AbortTransmit(&huart2); + + // Reset queued TX as well (avoid sending stale frames after re-arm) + HC06_TxReset(); + + // Clear UART error flags properly + __HAL_UART_CLEAR_OREFLAG(&huart2); + __HAL_UART_CLEAR_NEFLAG(&huart2); + __HAL_UART_CLEAR_FEFLAG(&huart2); + __HAL_UART_CLEAR_PEFLAG(&huart2); + + // Ensure peripheral interrupt enable bits are set + __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE); + __HAL_UART_ENABLE_IT(&huart2, UART_IT_ERR); + + // Also clear any pending interrupt in NVIC (rare but helps) + NVIC_ClearPendingIRQ(USART2_IRQn); + + // Reset parser/AT state so it doesn't wait on stale state + st = ST_SYNC; + pi = 0; + crc = 0; + AT_Clear(); + + // Rearm 1-byte RX no matter what mode + HAL_StatusTypeDef rst = HAL_UART_Receive_IT(&huart2, &rx_byte, 1); + (void)rst; + crc = 0; +} + + +/* + * + * API + * + */ + +void BT_KLINE_ERROR(){ + HC06_SendFrame(reply_cmd(CMD_INIT_COMM), CONN_HEAVY, 0, 0); +} + +void BT_ReadDfi(){ + if(connectionAlive){ + float dfi = ReadDfi(); + int16_t s = HC06_DfiFloatToS16(dfi); + HC06_SendFrame(reply_cmd(CMD_READ_DFI), 0, 0, (uint16_t)(uint16_t)s); + }else{ + HC06_SendFrame(reply_cmd(CMD_READ_DFI), CONN_DEAD, 0, 0); + } +} + +void BT_InitPSG5Comm(){ + if(!connectionAlive){ + BitBang = 1; + }else{ + HC06_SendFrame(reply_cmd(CMD_INIT_COMM), CONN_ALIVE, 0, 0); + } +} + +void BT_WriteDfi(){ + if(connectionAlive){ + int16_t s = (int16_t)g_hc06_frame.w3; + float dfi = HC06_DfiS16ToFloat(s); + + int res=WriteDfi(dfi, 0); + HC06_SendFrame(reply_cmd(CMD_WRITE_DFI), !res * KLINE_ERROR, 0, 0); + }else{ + HC06_SendFrame(reply_cmd(CMD_WRITE_DFI), CONN_DEAD, 0, 0); + } +} + +void BT_OnPSG5CommEstablished(){ + if(KeepAlive()){ + HC06_SendFrame(reply_cmd(CMD_INIT_COMM), 0, 0, 0); + }else{ + HC06_SendFrame(reply_cmd(CMD_INIT_COMM), CONN_HEAVY, 0, 0); + } +} + +void BT_ReadDTC(){ + if(connectionAlive){ + N_fc = ReadFaultCodes(fault_codes, 16); + int res = (N_fc == -1) ? 0 : 1; + HC06_SendFrame(reply_cmd(CMD_READ_DTC), !res * KLINE_ERROR, 0, (uint16_t)N_fc); + } + else{ + HC06_SendFrame(reply_cmd(CMD_READ_DTC), CONN_DEAD, 0, 0); + } +} + +void BT_ClearDTC(){ + if(connectionAlive){ + int res = ClearFaultCodes(); + HC06_SendFrame(reply_cmd(CMD_ERASE_DTC), !res * KLINE_ERROR, 0, 0); + } + else{ + HC06_SendFrame(reply_cmd(CMD_ERASE_DTC), CONN_DEAD, 0, 0); + } +} +void BT_ReadAudiPin(){ + uint16_t pin = 0; + if(connectionAlive){ + HC06_SendFrame(reply_cmd(CMD_READ_AUDI_PIN), !ReadAudiPin(&pin) * KLINE_ERROR, 0, (uint16_t)pin); + } + else{ + HC06_SendFrame(reply_cmd(CMD_READ_AUDI_PIN), CONN_DEAD, 0, 0); + } +} +void BT_WriteAudiPin(){ + /*if(connectionAlive){ + uint16_t pin = (uint16_t)g_hc06_frame.w3; + HC06_SendFrame(reply_cmd(CMD_READ_AUDI_PIN), !WriteAudiPin(pin) * KLINE_ERROR, 0, 0); + } + else{ + HC06_SendFrame(reply_cmd(CMD_READ_AUDI_PIN), CONN_DEAD, 0, 0); + }*/ +} + +void BT_SendCommStatus(){ + uint16_t volt = 0; + if(connectionAlive){ + HC06_SendFrame(reply_cmd(CMD_TOOL_COMM_STATUS), !ReadVoltage(&volt) * KLINE_ERROR, 0, (uint16_t)volt); + } + else{ + HC06_SendFrame(reply_cmd(CMD_TOOL_COMM_STATUS), CONN_DEAD, 0, 0); + } +} diff --git a/bpdt-firmware/bpdt-adapter-stm32h5/Core/BT_HC06_Libs/hc_06.h b/bpdt-firmware/bpdt-adapter-stm32h5/Core/BT_HC06_Libs/hc_06.h new file mode 100644 index 0000000..b6506ee --- /dev/null +++ b/bpdt-firmware/bpdt-adapter-stm32h5/Core/BT_HC06_Libs/hc_06.h @@ -0,0 +1,98 @@ +#ifndef INC_HC_06_H_ +#define INC_HC_06_H_ + +#include "main.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* ========================= + User config + ========================= */ +#define HC06_SYNC 0xA5 + +/* ========================= + Frame type + ========================= */ +typedef struct { + uint8_t cmd; + uint16_t w1; + uint16_t w2; + uint16_t w3; +} hc06_frame_t; + +typedef enum { + CMD_INIT_COMM = 0x01, + CMD_READ_DFI = 0x02, + CMD_WRITE_DFI = 0x03, + CMD_DATA_REQUEST = 0x04, + CMD_READ_DTC = 0x05, + CMD_ERASE_DTC = 0x06, + CMD_READ_AUDI_PIN = 0x07, + CMD_WRITE_AUDI_PIN = 0x08, + CMD_TOOL_COMM_STATUS = 0x09, + +} hc_06_CMD; + +typedef enum { + REQ_FW_VERSION = 0x00, + REQ_IDENT = 0x01, + REQ_STATUS = 0x02, + REQ_CONFIG = 0x03, + REQ_ERROR = 0x04, + REQ_RESERVED = 0x05, +} hc06_req_id_t; + +typedef enum { + HC06_STATUS_OK = 0x00, + CONN_DEAD = 0x01, + CONN_ALIVE = 0x02, + CONN_HEAVY = 0x03, + KLINE_ERROR = 0x04, + HC06_STATUS_NOT_IMPLEMENTED = 0x10, +} hc06_status_t; + +/* Latest received frame */ +extern volatile uint8_t g_hc06_frame_ready; +extern hc06_frame_t g_hc06_frame; + +/* ========================= + Public API + ========================= */ +void BT_KLINE_ERROR(void); + +void BT_OnPSG5CommEstablished(void); + +void BT_SendCommStatus(void); +// Call once after MX_USART2_UART_Init() +void HC06_Init(void); + +// Optional: call once at boot to configure HC-06 (name, optional PIN) +void HC06_AT_BootSetup(void); + +// Call from HAL_UART_RxCpltCallback() +void HC06_UART_RxByteCallback(UART_HandleTypeDef *huart); + +// Call from HAL_UART_TxCpltCallback() +void HC06_UART_TxCpltCallback(UART_HandleTypeDef *huart); + +// Call repeatedly in main loop +void HC06_Process(void); + +// TX helpers +HAL_StatusTypeDef HC06_SendAscii(const char *s); +HAL_StatusTypeDef HC06_SendAsciiLn(const char *s); +HAL_StatusTypeDef HC06_SendFrame(uint8_t cmd, uint16_t w1, uint16_t w2, uint16_t w3); + +// DFI scaling helpers (signed int16, factor 256/3) +int16_t HC06_DfiFloatToS16(float dfi); // s16 = round(dfi * 256/3) +float HC06_DfiS16ToFloat(int16_t s); // dfi = s * 3/256 + +#ifdef __cplusplus +} +#endif + +#endif /* INC_HC_06_H_ */ diff --git a/bpdt-firmware/bpdt-adapter-stm32h5/Core/Inc/main.h b/bpdt-firmware/bpdt-adapter-stm32h5/Core/Inc/main.h index 1a934a9..cb9ec44 100644 --- a/bpdt-firmware/bpdt-adapter-stm32h5/Core/Inc/main.h +++ b/bpdt-firmware/bpdt-adapter-stm32h5/Core/Inc/main.h @@ -36,7 +36,8 @@ extern "C" { /* Exported types ------------------------------------------------------------*/ /* USER CODE BEGIN ET */ - +extern UART_HandleTypeDef huart1; +extern UART_HandleTypeDef huart2; /* USER CODE END ET */ /* Exported constants --------------------------------------------------------*/ diff --git a/bpdt-firmware/bpdt-adapter-stm32h5/Core/Kline_Master_Libs/IKW1281Connection.c b/bpdt-firmware/bpdt-adapter-stm32h5/Core/Kline_Master_Libs/IKW1281Connection.c new file mode 100644 index 0000000..6ec905d --- /dev/null +++ b/bpdt-firmware/bpdt-adapter-stm32h5/Core/Kline_Master_Libs/IKW1281Connection.c @@ -0,0 +1,294 @@ +/* + * IKW1281Connection.c + * + * Created on: Apr 9, 2025 + * Author: herli + */ + +#include +#include +#include +#include + +#include "IKW1281Connection.h" +#include "main.h" + + +static void SendAckPacket(void); + +static void WriteComplement(uint8_t b); +static void WriteByteAndDiscardEcho(uint8_t b); +static void WriteByteAndReadAck(uint8_t b); +static void WriteByteRaw(uint8_t b); + +uint8_t timeoutdata[] = "TimeoutNigga\r\n"; +//uint8_t RxData[1]; +uint8_t TxData[KLINE_BUFFER_SIZE]; +uint8_t KlineData[KLINE_BUFFER_SIZE]; +volatile uint8_t rx_done_flag = 0; +uint8_t connectionAlive = 0; + +uint8_t ReadByte(){ + if (!connectionAlive){ return 0;} + rx_done_flag = 0; + uint32_t timeout = HAL_GetTick() + 1000; // 1-second timeout + + while (!rx_done_flag) + { + if (HAL_GetTick() > timeout) { + // Timeout occurred + + //HAL_UART_Transmit(&huart2, (uint8_t*)timeoutdata, strlen(timeoutdata), 1000); + //CDC_Transmit_FS((uint8_t*)timeoutdata, strlen(timeoutdata)); + connectionAlive = 0; + return 0xFF; // Or some special error code + } + + // Optional: add a timeout check here + // This loop blocks until RX is done + } + /*for (int k = 0; k < KLINE_BUFFER_SIZE ; k++){ + KlineData[k]=KlineData[k+1]; + } + KlineData[KLINE_BUFFER_SIZE] = 0; + return KlineData[0];*/ + return RxData[0]; +} +uint8_t ReadAndAckByte(void){ + uint8_t b = ReadByte(); + WriteComplement(b); + return b; +} +void ReadComplement(uint8_t b){ + uint8_t expectedComplement = (uint8_t)~b; + uint8_t actualComplement = ReadByte(); + //if(actualComplement != expectedComplement){return;} +} +void WriteComplement(uint8_t b){ + uint8_t complement = (uint8_t)~b; + WriteByteAndDiscardEcho(complement); +} +void WriteByteAndDiscardEcho(uint8_t b){ + WriteByteRaw(b); + uint8_t echo = ReadByte(); + //if(echo != b){return;} +} + +void WriteByteRaw(uint8_t b){ + TxData[0] = b; + HAL_UART_Transmit(&huart1, TxData, 1, 100); +} + +#define MAX_PACKETS 16 // adjust to your needs +static ParsedPacket packets_buffer[MAX_PACKETS]; +#define MAX_PACKETS 16 + +// static buffer, persistent between calls +static ParsedPacket packets_buffer[MAX_PACKETS]; + +ParsedPacket* ReceivePackets(int *out_count) +{ + int count = 0; + + while (1) { + if (count >= MAX_PACKETS) { + // reached max, stop receiving more + break; + } + + ParsedPacket packet = ReceivePacket(); + packets_buffer[count++] = packet; + + if (packet.isAckNak) { + break; + } + + SendAckPacket(); + } + + if (out_count) { + *out_count = count; + } + return packets_buffer; // returns pointer to static buffer +} +/* +ParsedPacket* ReceivePackets(int *out_count) { + int capacity = 16; // initial capacity + int count = 0; + ParsedPacket *packets = malloc(capacity * sizeof(ParsedPacket)); + if (!packets) return NULL; + + while (1) { + ParsedPacket packet = ReceivePacket(); + + // Resize array if needed + if (count >= capacity) { + capacity *= 2; + ParsedPacket *new_packets = realloc(packets, capacity * sizeof(ParsedPacket)); + if (!new_packets) { + free(packets); + return NULL; + } + packets = new_packets; + } + + packets[count++] = packet; + + if (packet.isAckNak) break; + + SendAckPacket(); + } + + if (out_count) *out_count = count; + return packets; +}*/ + +uint8_t _packetCounter = 0; +uint8_t _packetCounterInitialized = 0; + +ParsedPacket ReceivePacket() +{ + ParsedPacket packet = {0}; + uint8_t index = 0; + + uint8_t packetLength = ReadAndAckByte(); + //packet.raw = (uint8_t*)malloc(packetLength*sizeof(uint8_t)); + + packet.raw[index++] = packetLength; + + uint8_t packetCounter = ReadPacketCounter(); + packet.raw[index++] = packetCounter; + + uint8_t packetCommand = ReadAndAckByte(); + packet.raw[index++] = packetCommand; + + for (int i = 0; i < packetLength - 3; i++) { + uint8_t b = ReadAndAckByte(); + packet.raw[index++] = b; + } + + uint8_t packetEnd = ReadByte(); // no ACK + packet.raw[index++] = packetEnd; + packet.length = index; //tiene que ser index + + if (packetEnd != 0x03) { + // Protocol error handling here + return packet; + } + + // Now classify the packet type + packet.title = packetCommand; + + switch (packetCommand) { + case PACKET_CMD_ACK: + packet.type = PACKET_TYPE_ACK; + packet.isAckNak = 1; + break; + + case PACKET_CMD_AsciiData: + if (packet.raw[3] == 0x00) + packet.type = PACKET_TYPE_CODING_WSC; + else + packet.type = PACKET_TYPE_ASCII_DATA; + break; + + case PACKET_CMD_ReadEepromResponse: + packet.type = PACKET_TYPE_READ_EEPROM_RESPONSE; + break; + + case PACKET_CMD_ReadRomEepromResponse: + packet.type = PACKET_TYPE_READ_ROM_EEPROM_RESPONSE; + break; + + case PACKET_CMD_NAK: + packet.type = PACKET_TYPE_NAK; + packet.isAckNak = 1; + default: + packet.type = PACKET_TYPE_UNKNOWN; + break; + } + return packet; +} +void ResetPacketCounter(){ + _packetCounter = 0; +} +uint8_t ReadPacketCounter() +{ + uint8_t packetCounter = ReadAndAckByte(); + + if (!_packetCounterInitialized) + { + _packetCounter = packetCounter; + _packetCounterInitialized = 1; + } + else if (packetCounter != _packetCounter) + { + // Handle mismatch (drop packet, reset, etc.) + } + + _packetCounter++; + return packetCounter; +} +void SendAckPacket() +{ + uint8_t ackByte = (uint8_t)PACKET_CMD_ACK; + SendPacket(&ackByte, 1); +} +void SendPacket(uint8_t* payload, uint8_t length) +{ + uint8_t packetLength = length + 2; // +2 for length and counter + uint8_t packet[packetLength]; + + int index = 0; + + packet[index++] = packetLength; //0 + packet[index++] = _packetCounter++; //1 + + for (int i = 0; i < length; i++) + { + packet[index++] = payload[i]; + } + for (int i = 0; i < index; i++) + { + WriteByteAndReadAck(packet[i]); + HAL_Delay(5); // Small delay between bytes + } + WriteByteRaw(PACKET_END_EXPECTED); // End byte, no ACK expected +} + +void WriteByteAndReadAck(uint8_t b) +{ + WriteByteRaw(b); + uint8_t ack = ReadByte(); + + uint8_t expectedAck = (uint8_t)~b; + if (ack != expectedAck) + { + // Handle NAK or retry + } +} + +// Placeholder for platform-specific read functions + +uint8_t KeepAlive() { + SendAckPacket(); + ParsedPacket packet = ReceivePacket(); + if(packet.type != PACKET_TYPE_ACK){ + return 0; + } + return 1; +} +void EndCommunication(){ + uint8_t endPacket = (uint8_t)PACKET_CMD_End; + SendPacket((uint8_t*)endPacket, 1); + ResetPacketCounter(); +} + +ParsedPacket* SendCustom(const uint8_t* data, int len, int *out_count) { + SendPacket((uint8_t*)data, len); //antes & + int count = 0; + ParsedPacket* packets = ReceivePackets(&count); + *out_count = count; + return packets; +} + diff --git a/bpdt-firmware/bpdt-adapter-stm32h5/Core/Kline_Master_Libs/IKW1281Connection.h b/bpdt-firmware/bpdt-adapter-stm32h5/Core/Kline_Master_Libs/IKW1281Connection.h new file mode 100644 index 0000000..9f49127 --- /dev/null +++ b/bpdt-firmware/bpdt-adapter-stm32h5/Core/Kline_Master_Libs/IKW1281Connection.h @@ -0,0 +1,101 @@ +/* + * IKW1281Connection.h + * + * Created on: Apr 9, 2025 + * Author: herli + */ + +#ifndef INC_IKW1281CONNECTION_H_ +#define INC_IKW1281CONNECTION_H_ + +#include +#include "stdint.h" + +extern uint8_t RxData[]; +extern volatile uint8_t rx_done_flag; +extern uint8_t connectionAlive; + +#define KLINE_BUFFER_SIZE 20 + +#define PACKET_END_EXPECTED 0x03 + + + + +//typedef uint8_t PacketCommand; +#define MAX_PACKET_SIZE 512 // if too big it will crash 128length - 1; i++) { + char c = packet->raw[i] & 0x7F; + if (len < sizeof(data) - 2) { + data[len++] = c; + } + } + + // Null-terminate and add newline + data[len++] = '\r\n'; + data[len] = '\0'; + + HAL_UART_Transmit(huart, (uint8_t*)data, len, 1000); + + // Optional: report more data + if (packet->raw[3] > 0x7F) { + const char more[] = "More data available via ReadIdent\r\n"; + HAL_UART_Transmit(huart, (uint8_t*)more, sizeof(more) - 1, 1000); + } +}*/ + +int FilterNonAckNak(ParsedPacket* all, int total, ParsedPacket* filtered, int max) +{ + int j = 0; + for (int i = 0; i < total && j < max; i++) { + if (!all[i].isAckNak) { + filtered[j++] = all[i]; + } + } + return j; +} +static ControllerInfo info = {0}; // Zero init all strings + +ControllerInfo ReadEcuInfo() { + int packet_count = 0; + ParsedPacket *packets = ReceivePackets(&packet_count); + + char combined[128] = {0}; // Temporary buffer to build full ASCII text + + if (packets != NULL) { + for (int i = 0; i < packet_count; i++) { + ParsedPacket *p = &packets[i]; + if (p->type == PACKET_TYPE_ASCII_DATA && p->length > 4) { + size_t len = p->length - 4; // Exclude first 3 and last byte + strncat(combined, (char*)(p->raw + 3), len); + } + } + //free(packets); // clean up when done + } + + memset(&info, 0, sizeof(ControllerInfo)); + + // Fill ControllerInfo fields from combined text + strncpy(info.client_ident, combined, 12); + strncpy(info.unk_ident1, combined + 12, 10); + strncpy(info.soft_info, combined + 22, 10); + + if (strlen(combined) > 40) + strncpy(info.unk_ident2, combined + 32, 10); + if (strlen(combined) > 50) + strncpy(info.unk_ident3, combined + 42, 10); + + //free(combined); + return info; +} + +/*void PrintEcuInfo(ControllerInfo* info) { + char data[64]; + + sprintf(data, "client_ident: %s\t\r\n", info->client_ident); + //HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000); + CDC_Transmit_FS((uint8_t*)data, strlen(data)); + HAL_Delay(10); // allow USB stack to flush + + sprintf(data, "unk_ident1: %s\t\r\n", info->unk_ident1); + //HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000); + CDC_Transmit_FS((uint8_t*)data, strlen(data)); + HAL_Delay(10); // allow USB stack to flush + + sprintf(data, "soft_info: %s\t\r\n", info->soft_info); + //HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000); + CDC_Transmit_FS((uint8_t*)data, strlen(data)); + HAL_Delay(10); // allow USB stack to flush + + if (strlen(info->unk_ident2) > 0) { + sprintf(data, "unk_ident2: %s\t\r\n", info->unk_ident2); + //HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000); + CDC_Transmit_FS((uint8_t*)data, strlen(data)); + HAL_Delay(10); // allow USB stack to flush + + } + + if (strlen(info->unk_ident3) > 0) { + sprintf(data, "unk_ident3: %s\t\r\n", info->unk_ident3); + CDC_Transmit_FS((uint8_t*)data, strlen(data)); + HAL_Delay(10); // allow USB stack to flush + +// HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000); + } +}*/ +#define MAX_EEPROM_DATA 256 // adjust to your ECU’s max response +#define MAX_PACKETS 16 + +static ParsedPacket packet_buffer[MAX_PACKETS]; +static uint8_t eeprom_result[MAX_EEPROM_DATA]; + +uint8_t* ReadRomEeprom(uint16_t address, uint8_t count, int* outLength) +{ + uint8_t request[4] = { + PACKET_CMD_ReadRomEeprom, // 0x08 usually + count, + (uint8_t)(address >> 8), + (uint8_t)(address & 0xFF) + }; + + int packetCount = 0; + ParsedPacket* packets = SendCustom(request, sizeof(request), &packetCount); + if (!packets || packetCount == 0) { + *outLength = 0; + return NULL; + } + + // Check for NAK + if (packetCount == 1 && packets[0].type == PACKET_TYPE_NAK) { + *outLength = 0; + return NULL; + } + + // Collect useful packets (non-Ack/Nak) + int usefulCount = 0; + for (int i = 0; i < packetCount && usefulCount < MAX_PACKETS; i++) { + if (!packets[i].isAckNak) { + packet_buffer[usefulCount++] = packets[i]; + } + } + + if (usefulCount != 1) { + *outLength = 0; + return NULL; // Expect exactly one useful packet + } + + int dataLen = packet_buffer[0].length - 4; // skip 3-byte header + 1-byte end + if (dataLen > MAX_EEPROM_DATA) { + dataLen = MAX_EEPROM_DATA; // clamp to buffer size + } + + memcpy(eeprom_result, packet_buffer[0].raw + 3, dataLen); + *outLength = dataLen; + + return eeprom_result; // static buffer pointer +} +/* +uint8_t* ReadRomEeprom(uint16_t address, uint8_t count, int* outLength) { + uint8_t request[4] = { + PACKET_CMD_ReadRomEeprom, // 0x08 usually + count, + (uint8_t)(address >> 8), + (uint8_t)(address & 0xFF) + }; + + int packetCount = 0; + ParsedPacket* packets = SendCustom(request, sizeof(request), &packetCount, 0); + if (!packets || packetCount == 0) { + *outLength = 0; + return NULL; + } + + // Check for NAK + if (packetCount == 1 && packets[0].type == PACKET_TYPE_NAK) { + freeParsedPackets(packets, packetCount); + *outLength = 0; + return NULL; + } + + // Filter to exclude Ack/Nak + ParsedPacket* useful = malloc(packetCount * sizeof(ParsedPacket)); + int usefulCount = 0; + for (int i = 0; i < packetCount; i++) { + if (!packets[i].isAckNak) { + useful[usefulCount++] = packets[i]; + } else { + // If dynamically allocated, free individual packet.raw + free(packets[i].raw); + } + } + free(packets); // Free the original array pointer (but not the reused raw data) + + if (usefulCount != 1) { + freeParsedPackets(useful, usefulCount); + *outLength = 0; + return NULL; // Or handle as error + } + + uint8_t* result = malloc(useful[0].length - 4); // Remove 3-byte header + end (or however yours works) + if (!result) { + freeParsedPackets(useful, usefulCount); + *outLength = 0; + return NULL; + } + + memcpy(result, useful[0].raw + 3, useful[0].length - 4); // Adjust if header is different + *outLength = useful[0].length - 4; + + freeParsedPackets(useful, usefulCount); + return result; +}*/ + +void ReadSerialNumber(char* serialOut, int maxLen) { + uint8_t request[] = { 0x19, 0x06, 0x00, 0x80 }; + + int packetCount = 0; + ParsedPacket* packets = SendCustom(request, sizeof(request), &packetCount); + + if (!packets || packetCount == 0) { + serialOut[0] = '\0'; + return; + } + + for (int i = 0; i < packetCount; i++) { + ParsedPacket* p = &packets[i]; + if (!p->isAckNak && p->type == PACKET_TYPE_READ_EEPROM_RESPONSE) { + int dataLen = p->length - 4; // Skip length, counter, command, end byte + if (dataLen > maxLen - 1) { + dataLen = maxLen - 1; + } + + memcpy(serialOut, p->raw + 3, dataLen); // Adjust if needed + serialOut[dataLen] = '\0'; + + //freeParsedPackets(packets, packetCount); + return; + } + } + + serialOut[0] = '\0'; + //freeParsedPackets(packets, packetCount); +} + +/*void freeParsedPackets(ParsedPacket* packets, int count) { + if (!packets) return; + + for (int i = 0; i < count; i++) { + if (packets[i].raw != NULL) { + //free(packets[i].raw); + //packets[i].raw = NULL; // Avoid double free + } + } + + //free(packets); // Free the packet array itself +}*/ + +uint16_t ExtractEepromAddress(ParsedPacket *packets, int packet_count) { + for (int i = 0; i < packet_count; i++) { + ParsedPacket *p = &packets[i]; + if (!p->isAckNak && p->length > 4) { + uint32_t address = (p->raw[3+1] << 8) | p->raw[3]; + return address - 10; + } + } + return 0; +} +float ReadDfi(){ + float Dfi = 0.0; + if(!KeepAlive()){ + KLINE_THROW_NONALIVE_EXCEPTION(0); + return 0.0; + } + uint8_t request[] = { 0x18, 0x00, 0x03, 0xFF, 0xFF }; //TODO ERROR ENVIA AC 7E 00 20 00 + int packet_count = 0; + //ParsedPacket* packets = SendCustom(request, 5, &packet_count, 1); //this changes with pump version but for now no exceptions + ParsedPacket* packets = SendCustom((uint8_t[]){ 0x18, 0x00, 0x03, 0xFF, 0xFF }, 5, &packet_count); + + //Dfi = 1.0; + if(!KeepAlive()){ + KLINE_THROW_NONALIVE_EXCEPTION(0); + return 0.0; + } + + uint8_t request2[] = { 0x19, 0x02, 0x00, 0x44}; + packets = SendCustom(request2, 4, &packet_count); //this changes with pump version but for now no exceptions + + + // Filter to exclude Ack/Nak + for (int i = 0; i < packet_count; i++) { + if (!packets[i].isAckNak) { + Dfi = ((int8_t)packets[i].raw[3] * 3.0) / 256.0; + } + } + //free(packets); // Free the original array pointer (but not the reused raw data) + /*if(Dfi != 0.0){ + + }*/ + return Dfi; +} + +/** + * Write DFI parameter (EEPROM 0x44) after password unlock. + * + * version mapping (as in your C#): + * default: {18 00 03 2F FF ...} + * version==1: {18 00 03 2F F2 ...} + * version==2 or 3: {18 00 03 FF F2 ...} + * + * Returns 1 on success, 0 on failure (non-alive). + */ +int WriteDfi(float dfi, int version) +{ + if (!KeepAlive()) { + KLINE_THROW_NONALIVE_EXCEPTION(0); + return 0; + } + + // Password packets (14 bytes) + static const uint8_t pass_default[14] = { + 0x18, 0x00, 0x03, 0x2F, 0xFF, 0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45 + }; // V1 KHTCA8G0E (per your comment) + static const uint8_t pass_v2[14] = { + 0x18, 0x00, 0x03, 0x2F, 0xF2, 0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45 + }; // V2 KHTCA8G0E + static const uint8_t pass_v3v4[14] = { + 0x18, 0x00, 0x03, 0xFF, 0xF2, 0x4B, 0x48, 0x54, 0x43, 0x41, 0x38, 0x47, 0x30, 0x45 + }; // V3, V4 + + const uint8_t *password_packet = pass_default; + if (version == 1) { + password_packet = pass_v2; + } else if (version == 2 || version == 3) { + password_packet = pass_v3v4; + } + + int packet_count = 0; + (void)SendCustom(password_packet, 14, &packet_count); // throwOnNak=1 (strict) + + if (!KeepAlive()) { + KLINE_THROW_NONALIVE_EXCEPTION(0); + return 0; + } + + // C#: + // sbyte value = (sbyte)((dfi * 256.0) / 3); + // if (value == 0) value = 1; + // + // NOTE: with dfi up to +1.5, math gives 128.0. That does not fit in int8_t. + // We clamp to [-128..127] to avoid wrap to -128. + int vi = (int)((dfi * 256.0f) / 3.0f); // trunc toward 0 (matches typical C# unchecked cast behavior for in-range) + vi = vi > 127 ? 127:vi; + vi = vi < -128 ? -128:vi; + //vi = clamp_int(vi, -128, 127); + int8_t value = (int8_t)vi; + if (value == 0) value = 1; + + // byte value_csum = (byte)(0 - (byte)value); + uint8_t value_u = (uint8_t)value; + uint8_t value_csum = (uint8_t)(0u - value_u); + + // Write command + uint8_t req[7] = { 0x1A, 0x02, 0x00, 0x44, value_u, value_csum, 0x03 }; + (void)SendCustom(req, 7, &packet_count); // throwOnNak=1 (strict) + + if (!KeepAlive()) { + KLINE_THROW_NONALIVE_EXCEPTION(0); + return 0; + } + + return 1; +} +void ReadCustomerChangeIndex(){ + if(!KeepAlive()){ + KLINE_THROW_NONALIVE_EXCEPTION(0); + return; + } + uint8_t request[] = { 0x18, 0x00, 0x00, 0x82, 0x33 }; + + int packet_count = 0; + ParsedPacket* packets = SendCustom(request, 5, &packet_count); //this changes with pump version but for now no exceptions + + char message[40]; + //sprintf(message, "Reading customer change index\r\n"); + //HAL_UART_Transmit(&huart2, (uint8_t*)message, strlen(message), 1000); + + if(!KeepAlive()){ + KLINE_THROW_NONALIVE_EXCEPTION(12); + return; + } + + uint16_t cust_change_address = 0; + + int data_length = 0; + + uint8_t* test_data = ReadRomEeprom(0x9FFE, 2, &data_length); + if(data_length > 1){ + cust_change_address = (uint16_t)((test_data[1] << 8) | test_data[0]); + cust_change_address += 3; + } + + test_data = ReadRomEeprom(cust_change_address, 6, &data_length); + + char cust_change_index[12]; + + if (test_data && data_length > 0) { + for (int i = 0; i < data_length; i++) { + cust_change_index[i] = (char)test_data[i]; // Convert byte to char + } + } + //sprintf(message, "Mod. Index : %s\r\n", cust_change_index); + //CDC_Transmit_FS((uint8_t*)message, strlen(message)); + /*char cust_change_index[12]; + + if (test_data && data_length > 0) { + int copy_len = (data_length < (sizeof(cust_change_index) - 1)) + ? data_length + : (sizeof(cust_change_index) - 1); + + for (int i = 0; i < copy_len; i++) { + cust_change_index[i] = (char)test_data[i]; + } + cust_change_index[copy_len] = '\0'; // Null-terminate + } + + sprintf(message, "Mod. Index : %s\r\n", cust_change_index); + CDC_Transmit_FS((uint8_t*)message, strlen(message));*/ + //HAL_UART_Transmit(&huart2, (uint8_t*)cust_change_index, strlen(cust_change_index), 1000); +} +char identStr[11]; //11+1 + +void IdentifyEcu() { + char outputStr[35]; + //float dFi = ReadDfi(); + //sprintf(outputStr, "dFi: %d.%02d\t\t\r\n", (int)dFi, (int)((dFi - (int)dFi) * 100)); + + //CDC_Transmit_FS((uint8_t*)outputStr, strlen(outputStr)); + + ReadCustomerChangeIndex();//Necessary + + if(!KeepAlive()){ + KLINE_THROW_NONALIVE_EXCEPTION(0); + return; + } + int packet_count = 0; + ParsedPacket* packets = SendCustom((uint8_t[]){ 0x01, 0x02, 0x00, 0xC6 }, 4, &packet_count); + + //uint16_t address = ExtractEepromAddress(packets, count); + + uint16_t address = ExtractEepromAddress(packets, packet_count); + + if(!KeepAlive()){ + KLINE_THROW_NONALIVE_EXCEPTION(1); + return; + } + + if (address != 0) { + int data_length = 0; + uint8_t* test_data = ReadRomEeprom(address, 10, &data_length); + + for (int i = 0; i < data_length && i < sizeof(identStr) - 1; i++) { + identStr[i] = (char)test_data[i]; + } //identStr[10] = '\0'; + //HAL_UART_Transmit(&huart2, (uint8_t*)identStr, strlen(identStr), 1000); + //sprintf(outputStr, "Ident. : %s\t\r\n", identStr); + + //CDC_Transmit_FS((uint8_t*)outputStr, strlen(outputStr)); + + } + + if(!KeepAlive()){ + KLINE_THROW_NONALIVE_EXCEPTION(2); + return; + } + + char serial[20]; + ReadSerialNumber(serial, sizeof(serial)); + //sprintf(serial, "serial numb: %s\r\n", serial); + //HAL_UART_Transmit(&huart2, (uint8_t*)serial, strlen(serial), 1000); + /*sprintf(outputStr, "Serial N. : %s\t\r\n", serial); + + CDC_Transmit_FS((uint8_t*)outputStr, strlen(outputStr));*/ + + //sprintf(serial, "serial numb: %s\r\n", serial); + //HAL_UART_Transmit(&huart2, (uint8_t*)data, strlen(data), 1000); +} + + +/* FAULT CODE READING */ + + +#define FAULTCODE_NONE_DTC 0x00 + +/* --- Tuning limits (adapt to your max) --- */ +#define MAX_FAULTCODES 20 +#define MAX_FC_AGGREGATE_LEN 512 // total concatenated body bytes + +int ReadFaultCodes(FaultCode* outCodes, int maxCodes) +{ + char logmsg[64]; + + if (!outCodes || maxCodes <= 0) return -1; + + // Keep the link alive as you do elsewhere TODO + if (!KeepAlive()) { + KLINE_THROW_NONALIVE_EXCEPTION(0); + return -1; + } + + // "Sending ReadFaultCodes packet" + /*sprintf(logmsg, "Sending ReadFaultCodes packet\r\n"); + CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/ + + // Send single-byte payload with command 0x07 + uint8_t cmd = (uint8_t)PACKET_CMD_FaultCodesRead; // already defined in your enum + SendPacket(&cmd, 1); + + // Receive all response packets + int packet_count = 0; + ParsedPacket* packets = ReceivePackets(&packet_count); + if (!packets || packet_count <= 0) { + return 0; // no codes + } + + // Concatenate all bodies from non-ACK/NAK packets + // Body = raw[3 .. (3 + body_len - 1)], where body_len = (length - 3). + // Full raw frame is: [len][counter][command][body...][end] + // Your receiver code writes the end byte separately (0x03). + // NOTE: guard against malformed lengths. + uint8_t fc_buf[64]; + int fc_len = 0; + + for (int i = 0; i < packet_count; i++) { + if (packets[i].isAckNak) continue; + + // If you classify types and want to be strict, enforce here: + if (packets[i].title != PACKET_CMD_FaultCodesResponse) { + /*sprintf(logmsg, "Expected FaultCodesPacket but got type=0x%02X\r\n", packets[i].type); + CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/ + //free(packets); + return -1; + } + + if (!packets[i].raw || packets[i].length < 3) continue; + + int body_len = (int)packets[i].length - 4; // subtract [len, counter, command] + if (body_len <= 0) continue; + + // Defensive: make sure the raw buffer actually has those bytes (+1 end is read separately) + // We assume your parser filled .raw accordingly. + + if ((fc_len + body_len) > (int)sizeof(fc_buf)) { + /*sprintf(logmsg, "Fault code buffer overflow\r\n"); + CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/ + //free(packets); + return -1; + } + + memcpy(&fc_buf[fc_len], &packets[i].raw[3], (size_t)body_len); + fc_len += body_len; + } + + // Done with the packet list (your comment: free only the array, not reused raw) + //free(packets); + + // Parse aggregated data: + // C# does: take first 3 bytes (dtc,status,extra), then Skip(8) per record. + // That means record stride = 8 bytes, but only first 3 matter here. + // Stop when fewer than 3 bytes remain. + int produced = 0; + int off = 0; + + while (off + 3 <= fc_len) { + uint8_t dtc = fc_buf[off + 0]; + uint8_t status = fc_buf[off + 1]; + uint8_t extra = fc_buf[off + 2]; + + if (dtc != FAULTCODE_NONE_DTC) { + if (produced < maxCodes) { + outCodes[produced].dtc = dtc; + outCodes[produced].status = status; + outCodes[produced].extra = extra; + produced++; + } else { + /*sprintf(logmsg, "Fault code output full (%d)\r\n", maxCodes); + CDC_Transmit_FS((uint8_t*)logmsg, (uint16_t)strlen(logmsg));*/ + break; + } + } + + // Advance to next 8-byte record + off += 8; + } + + return produced; // number of valid fault codes in outCodes[] +} +int ClearFaultCodes(void) +{ + int packet_count = 0; + uint8_t req[1] = { PACKET_CMD_FaultCodesDelete }; + + ParsedPacket *packets = SendCustom(req, 1, &packet_count); + + // C# expects exactly 1 packet + if (packet_count != 1) { + // in embedded: return error code (or assert/log) + return 0; + } + + ParsedPacket *p = &packets[0]; + + // Must be ACK or NAK + if (!p->isAckNak) { + return 0; + } + + if (p->type == PACKET_TYPE_NAK) return 0; + if (p->type == PACKET_TYPE_ACK) return 1; + + return 0; +} + +/*void PrintFaultCodes(const FaultCode* list, int n) +{ + if (!list || n <= 0) { + const char* msg = "No fault codes\r\n"; + CDC_Transmit_FS((uint8_t*)msg, (uint16_t)strlen(msg)); + return; + } + for (int i = 0; i < n; i++) { + PrintFaultCode(&list[i]); + HAL_Delay(10); + } +}*/ + +typedef struct { uint8_t code; const char* text; } DtcMapEntry; + +static const DtcMapEntry DTC_MAP[] = { + {0x50, "Fuel quantity solenoid valve Output stage error"}, + {0x51, "Fuel quantity solenoid valve."}, + {0x52, "Angle sensor/ IWZ system."}, + {0x53, "Angle sensor/ IWZ system"}, + {0x54, "Control unit temperature sensor, temperature too high"}, + {0x55, "Control unit temperature sensor"}, + {0x56, "Battery voltage out of range"}, + {0x57, "Timing device control. Permanent control deviation"}, + {0x58, "Fuel quantity / timing solenoid valve"}, + {0x59, "BIP Fault (Begin of Injection Point)"}, + {0x5A, "Engine speed signal"}, + {0x5B, "Engine speed signal"}, + {0x5C, "CAN-Bus (sporadic)"}, + {0x5D, "CAN bus error"}, + {0x5E, "Self-test error"}, +}; + +static const char* GetDtcText(uint8_t code) +{ + for (unsigned i = 0; i < (sizeof(DTC_MAP)/sizeof(DTC_MAP[0])); i++) { + if (DTC_MAP[i].code == code) return DTC_MAP[i].text; + } + return "Unknown Error Code"; +} +/*int FaultCode_ToString(const FaultCode* fc, char* dst, int dst_len) +{ + if (!fc || !dst || dst_len <= 0) return 0; + + uint8_t status1 = (uint8_t)(fc->status & 0x7F); + const char* txt = GetDtcText(fc->dtc); + + // Format like: "5A Engine speed signal (03)\r\n" + // (use %02u to mimic ":d2" from your C# example) + int n = snprintf(dst, (size_t)dst_len, "%02X %s (%02u)\r\n", + fc->dtc, txt, status1); + if (n < 0) n = 0; + if (n >= dst_len) n = dst_len - 1; + return n; +}*/ +/*void PrintFaultCode(const FaultCode* fc) +{ + char line[128]; + int n = FaultCode_ToString(fc, line, sizeof(line)); + if (n > 0) { + CDC_Transmit_FS((uint8_t*)line, (uint16_t)n); + } +}*/ + +void KLINE_THROW_NONALIVE_EXCEPTION(uint8_t id){ + char erroralive[20]; + //sprintf(erroralive, "isnt alive %u\r\r\n", id); + BT_KLINE_ERROR(); + //HAL_UART_Transmit(&huart2, (uint8_t*)erroralive, strlen(erroralive), 1000); + //CDC_Transmit_FS((uint8_t*)erroralive, strlen(erroralive)); + +} + +uint8_t ReadAudiPin(uint16_t* pin) { + int data_length = 0; + uint8_t* data = ReadRomEeprom(0x04FA, 2, &data_length); + if(data_length < 1){return 0;} + *pin = (uint16_t)((data[1] << 8) | data[0]); + return 1; +} +uint8_t WriteAudiPin(uint16_t pin) { + /*uint8_t* data = ReadRomEeprom(0x04FA, 2, &data_length); + if(data_length < 1){return 0;} + *pin = (uint16_t)((data[1] << 8) | data[0]);*/ + return 1; +} + +uint8_t ReadVoltage(uint16_t* volt) { + int data_length = 0; + uint8_t* data = ReadRomEeprom(0x0142, 2, &data_length); + if(data_length < 1){return 0;} + *volt = (uint16_t)((data[1] << 8) | data[0]); + return 1; +} diff --git a/bpdt-firmware/bpdt-adapter-stm32h5/Core/Kline_Master_Libs/kline.h b/bpdt-firmware/bpdt-adapter-stm32h5/Core/Kline_Master_Libs/kline.h new file mode 100644 index 0000000..19b8ecd --- /dev/null +++ b/bpdt-firmware/bpdt-adapter-stm32h5/Core/Kline_Master_Libs/kline.h @@ -0,0 +1,34 @@ +/* + * kline.h + * + * Created on: Aug 18, 2025 + * Author: herli + */ + +#ifndef INC_KLINE_H_ +#define INC_KLINE_H_ + +#include "IKW1281Connection.h" + +#define ECU_INIT_ADDRESS 0xF1 + +#define KLINE_GPIO_PORT GPIOA +#define KLINE_PIN GPIO_PIN_9 + +extern uint8_t BitBang; + +typedef struct { + uint8_t dtc; // Primary DTC byte + uint8_t status; // Status byte + uint8_t extra; // Third byte in the 3-byte header (kept for parity with C#) +} FaultCode; + +extern ControllerInfo ReadEcuInfo(void); +int WakeUp(uint8_t controllerAddress, uint8_t evenParity); +void IdentifyEcu(void); +int ReadFaultCodes(FaultCode* outCodes, int maxCodes); +uint8_t ReadVoltage(uint16_t* volt); +extern void KLINE_THROW_NONALIVE_EXCEPTION(uint8_t id); +int FaultCode_ToString(const FaultCode* fc, char* dst, int dst_len); + +#endif /* INC_KLINE_H_ */ diff --git a/bpdt-firmware/bpdt-adapter-stm32h5/Core/Src/main.c b/bpdt-firmware/bpdt-adapter-stm32h5/Core/Src/main.c index 38f3c28..10ffbe0 100644 --- a/bpdt-firmware/bpdt-adapter-stm32h5/Core/Src/main.c +++ b/bpdt-firmware/bpdt-adapter-stm32h5/Core/Src/main.c @@ -21,7 +21,9 @@ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ - +#include "kline.h" +#include "IKW1281Connection.h" +#include "hc_06.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -74,7 +76,70 @@ static void MX_USART6_UART_Init(void); /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ +uint8_t RxData[1]; +ControllerInfo ecuinfo = {0}; +void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) +{ + if (huart->Instance == huart2.Instance) + { + HC06_UART_RxByteCallback(huart); + }else{ + rx_done_flag = 1; + HAL_UART_Receive_IT(&huart1, RxData, 1); + } +} +void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) +{ + HC06_UART_TxCpltCallback(huart); +} +uint8_t pc_connected = 0; +uint8_t sent_init_message = 0; + +uint8_t psg_connected = 0; + +void OnEnterReceived(void) { + BitBang = 1; +} + +uint32_t alive_due_ms =0; + +void TryConnection(void){ + BitBang = 0; + + char data[32]; + + /*sprintf(data, "Starting communication with PSGx...\r\n"); + CDC_Transmit_FS((uint8_t*)data, strlen(data)); + sprintf(data, "\r\n"); + CDC_Transmit_FS((uint8_t*)data, strlen(data)); + */ + int protocolVersion = WakeUp(ECU_INIT_ADDRESS, 0); + //WriteByteRaw(0x69); + //memset(data, 0, sizeof data); + /*sprintf(data, "Protocol Version : %d\t\r\n", protocolVersion); + CDC_Transmit_FS((uint8_t*)data, strlen(data));*/ + + //HAL_UART_Transmit(&huart2, data, 12, 1000); + + ecuinfo = ReadEcuInfo(); + + //PrintEcuInfo(&ecuinfo); + if(!KeepAlive()){ + KLINE_THROW_NONALIVE_EXCEPTION(4); + return; + } + IdentifyEcu(); + + /*FaultCode fault_codes[20]; // buffer for up to 32 codes (tune size as needed) + int N_fc = ReadFaultCodes(fault_codes, 20); + PrintFaultCodes(fault_codes, N_fc);*/ + //free(fault_code) + psg_connected = 1; + alive_due_ms = HAL_GetTick() + 500; + + BT_OnPSG5CommEstablished(); +} /* USER CODE END 0 */ /** @@ -119,7 +184,10 @@ int main(void) MX_USART3_UART_Init(); MX_USART6_UART_Init(); /* USER CODE BEGIN 2 */ - + HAL_UART_Receive_IT(&huart1, RxData, 1); + //HAL_Delay(25); // Wait before starting UART (standard KWP wait time) + HC06_Init(); + HC06_AT_BootSetup(); // sets NAME, then switches to BIN mode /* USER CODE END 2 */ /* Infinite loop */ @@ -129,6 +197,36 @@ int main(void) /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ + + HC06_Process(); + + /*if(pc_connected && !sent_init_message){ + sprintf(data, "Press any key to read data...\r\n"); + CDC_Transmit_FS((uint8_t*)data, strlen(data)); + sent_init_message = 1; + }*/ + //HAL_UART_Transmit_DMA(&huart1, data, 5); + //CDC_Transmit_FS((uint8_t*)data, strlen(data)); + + + if(BitBang){ + //HAL_GPIO_WritePin(KLINE_GPIO_PORT, GPIO_PIN_8, GPIO_PIN_RESET); + TryConnection(); + //AQUI HARIA FALTA IMPLEMENTAR UN TIMER QUE CADA x ms haga un keep alive + //EndCommunication(); + } + if(psg_connected){ + if ((int32_t)(HAL_GetTick() - alive_due_ms) < 0){ + if(!KeepAlive()){ + KLINE_THROW_NONALIVE_EXCEPTION(4); + psg_connected = 0; + //return; + } + BT_SendCommStatus(); + alive_due_ms = HAL_GetTick() + 500; + } + + } } /* USER CODE END 3 */ }