Updated C code (fixed ~50 errors) and cleaned up vestigials, should test now

This commit is contained in:
2026-03-31 16:01:10 +02:00
parent d1fc7a55f8
commit f7d6b3c782
9 changed files with 2168 additions and 5 deletions

View File

@@ -32,6 +32,10 @@
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.definedsymbols.166077643" name="Define symbols (-D)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.definedsymbols" valueType="definedSymbols"> <option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.definedsymbols.166077643" name="Define symbols (-D)" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.definedsymbols" valueType="definedSymbols">
<listOptionValue builtIn="false" value="DEBUG"/> <listOptionValue builtIn="false" value="DEBUG"/>
</option> </option>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.includepaths.295803752" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.includepaths" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Core/Kline_Master_Libs}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Core/BT_HC06_Libs}&quot;"/>
</option>
<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input.419205202" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input"/> <inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input.419205202" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input"/>
</tool> </tool>
<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.686310707" name="MCU/MPU GCC Compiler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler"> <tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.686310707" name="MCU/MPU GCC Compiler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler">
@@ -48,6 +52,8 @@
<listOptionValue builtIn="false" value="../Drivers/STM32H5xx_HAL_Driver/Inc/Legacy"/> <listOptionValue builtIn="false" value="../Drivers/STM32H5xx_HAL_Driver/Inc/Legacy"/>
<listOptionValue builtIn="false" value="../Drivers/CMSIS/Device/ST/STM32H5xx/Include"/> <listOptionValue builtIn="false" value="../Drivers/CMSIS/Device/ST/STM32H5xx/Include"/>
<listOptionValue builtIn="false" value="../Drivers/CMSIS/Include"/> <listOptionValue builtIn="false" value="../Drivers/CMSIS/Include"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Core/Kline_Master_Libs}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Core/BT_HC06_Libs}&quot;"/>
</option> </option>
<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c.1903972581" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c"/> <inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c.1903972581" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c"/>
</tool> </tool>
@@ -105,12 +111,16 @@
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board.113809730" name="Board" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board" useByScannerDiscovery="false" value="NUCLEO-H533RE" valueType="string"/> <option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board.113809730" name="Board" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.target_board" useByScannerDiscovery="false" value="NUCLEO-H533RE" valueType="string"/>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults.165871031" name="Defaults" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults" useByScannerDiscovery="false" value="com.st.stm32cube.ide.common.services.build.inputs.revA.1.0.6 || Release || false || Executable || com.st.stm32cube.ide.mcu.gnu.managedbuild.option.toolchain.value.workspace || NUCLEO-H533RE || 0 || 0 || arm-none-eabi- || ${gnu_tools_for_stm32_compiler_path} || ../Core/Inc | ../Drivers/STM32H5xx_HAL_Driver/Inc | ../Drivers/STM32H5xx_HAL_Driver/Inc/Legacy | ../Drivers/CMSIS/Device/ST/STM32H5xx/Include | ../Drivers/CMSIS/Include || || || USE_HAL_DRIVER | STM32H533xx || || Drivers | Core/Startup | Core || || || ${workspace_loc:/${ProjName}/STM32H533RETX_FLASH.ld} || true || NonSecure || || secure_nsclib.o || || None || || || || ::" valueType="string"/> <option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults.165871031" name="Defaults" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.defaults" useByScannerDiscovery="false" value="com.st.stm32cube.ide.common.services.build.inputs.revA.1.0.6 || Release || false || Executable || com.st.stm32cube.ide.mcu.gnu.managedbuild.option.toolchain.value.workspace || NUCLEO-H533RE || 0 || 0 || arm-none-eabi- || ${gnu_tools_for_stm32_compiler_path} || ../Core/Inc | ../Drivers/STM32H5xx_HAL_Driver/Inc | ../Drivers/STM32H5xx_HAL_Driver/Inc/Legacy | ../Drivers/CMSIS/Device/ST/STM32H5xx/Include | ../Drivers/CMSIS/Include || || || USE_HAL_DRIVER | STM32H533xx || || Drivers | Core/Startup | Core || || || ${workspace_loc:/${ProjName}/STM32H533RETX_FLASH.ld} || true || NonSecure || || secure_nsclib.o || || None || || || || ::" valueType="string"/>
<option id="com.st.stm32cube.ide.mcu.debug.option.cpuclock.1430752187" name="Cpu clock frequence" superClass="com.st.stm32cube.ide.mcu.debug.option.cpuclock" useByScannerDiscovery="false" value="32" valueType="string"/> <option id="com.st.stm32cube.ide.mcu.debug.option.cpuclock.1430752187" name="Cpu clock frequence" superClass="com.st.stm32cube.ide.mcu.debug.option.cpuclock" useByScannerDiscovery="false" value="32" valueType="string"/>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.converthex.247049569" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.converthex" value="true" valueType="boolean"/> <option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.converthex.247049569" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.converthex" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.convertbinary.1040440588" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.convertbinary" value="true" valueType="boolean"/> <option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.convertbinary.1040440588" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.option.convertbinary" useByScannerDiscovery="false" value="true" valueType="boolean"/>
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform.987603817" isAbstract="false" osList="all" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform"/> <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform.987603817" isAbstract="false" osList="all" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.targetplatform"/>
<builder buildPath="${workspace_loc:/bpdt-adapter-stm32h5}/Release" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder.568789294" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder"/> <builder buildPath="${workspace_loc:/bpdt-adapter-stm32h5}/Release" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder.568789294" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.builder"/>
<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.1736608173" name="MCU/MPU GCC Assembler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler"> <tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.1736608173" name="MCU/MPU GCC Assembler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler">
<option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel.10517238" name="Debug level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel.value.g0" valueType="enumerated"/> <option id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel.10517238" name="Debug level" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel" value="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.debuglevel.value.g0" valueType="enumerated"/>
<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.includepaths.1084208325" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.option.includepaths" valueType="includePath">
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Core/Kline_Master_Libs}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Core/BT_HC06_Libs}&quot;"/>
</option>
<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input.1231801121" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input"/> <inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input.1231801121" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.assembler.input"/>
</tool> </tool>
<tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.1085501967" name="MCU/MPU GCC Compiler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler"> <tool id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.1085501967" name="MCU/MPU GCC Compiler" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler">
@@ -126,6 +136,8 @@
<listOptionValue builtIn="false" value="../Drivers/STM32H5xx_HAL_Driver/Inc/Legacy"/> <listOptionValue builtIn="false" value="../Drivers/STM32H5xx_HAL_Driver/Inc/Legacy"/>
<listOptionValue builtIn="false" value="../Drivers/CMSIS/Device/ST/STM32H5xx/Include"/> <listOptionValue builtIn="false" value="../Drivers/CMSIS/Device/ST/STM32H5xx/Include"/>
<listOptionValue builtIn="false" value="../Drivers/CMSIS/Include"/> <listOptionValue builtIn="false" value="../Drivers/CMSIS/Include"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Core/Kline_Master_Libs}&quot;"/>
<listOptionValue builtIn="false" value="&quot;${workspace_loc:/${ProjName}/Core/BT_HC06_Libs}&quot;"/>
</option> </option>
<inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c.393750469" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c"/> <inputType id="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c.393750469" superClass="com.st.stm32cube.ide.mcu.gnu.managedbuild.tool.c.compiler.input.c"/>
</tool> </tool>

View File

@@ -0,0 +1,743 @@
#include "hc_06.h"
#include <string.h>
#include <stdio.h>
#include <math.h>
#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);
}
}

View File

@@ -0,0 +1,98 @@
#ifndef INC_HC_06_H_
#define INC_HC_06_H_
#include "main.h"
#include <stdint.h>
#include <stdbool.h>
#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_ */

View File

@@ -36,7 +36,8 @@ extern "C" {
/* Exported types ------------------------------------------------------------*/ /* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */ /* USER CODE BEGIN ET */
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
/* USER CODE END ET */ /* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/

View File

@@ -0,0 +1,294 @@
/*
* IKW1281Connection.c
*
* Created on: Apr 9, 2025
* Author: herli
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@@ -0,0 +1,101 @@
/*
* IKW1281Connection.h
*
* Created on: Apr 9, 2025
* Author: herli
*/
#ifndef INC_IKW1281CONNECTION_H_
#define INC_IKW1281CONNECTION_H_
#include <string.h>
#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 128<max<256 //funcionando en 256
//si es menor, no funciona????
#define EEPROM_RESPONSE_BODY_MAX 64 // Adjust as needed
#define MAX_CONTROLLER_PACKETS 8
#define MAX_PACKET_BODY_LENGTH 64
typedef struct {
char client_ident[13]; // 12 + null
char unk_ident1[11];
char soft_info[11];
char unk_ident2[11];
char unk_ident3[11];
} ControllerInfo;
typedef struct {
uint8_t data[EEPROM_RESPONSE_BODY_MAX];
uint8_t length;
uint8_t valid;
} EepromResult;
typedef enum {
PACKET_CMD_ReadIdent = 0x00,
PACKET_CMD_ReadRomEeprom = 0x03,
PACKET_CMD_ActuatorTest = 0x04,
PACKET_CMD_FaultCodesDelete = 0x05,
PACKET_CMD_End = 0x06,
PACKET_CMD_FaultCodesRead = 0x07,
PACKET_CMD_ACK = 0x09,
PACKET_CMD_NAK = 0x0A,
PACKET_CMD_SoftwareCoding = 0x10,
PACKET_CMD_ReadEeprom = 0x19,
PACKET_CMD_WriteEeprom = 0x1A,
PACKET_CMD_Custom = 0x1B,
PACKET_CMD_GroupReading = 0x29,
PACKET_CMD_Login = 0x2B,
PACKET_CMD_GroupReadingResponse = 0xE7,
PACKET_CMD_ReadEepromResponse = 0xEF,
PACKET_CMD_ActuatorTestResponse = 0xF5,
PACKET_CMD_AsciiData = 0xF6,
PACKET_CMD_WriteEepromResponse = 0xF9,
PACKET_CMD_FaultCodesResponse = 0xFC,
PACKET_CMD_ReadRomEepromResponse = 0xFD
} PacketCommand;
typedef enum {
PACKET_TYPE_UNKNOWN = 0,
PACKET_TYPE_ACK,
PACKET_TYPE_NAK,
PACKET_TYPE_ASCII_DATA,
PACKET_TYPE_CODING_WSC,
PACKET_TYPE_READ_EEPROM_RESPONSE,
PACKET_TYPE_READ_ROM_EEPROM_RESPONSE
} PacketType;
typedef struct {
PacketType type;
uint8_t title;
uint8_t length;
uint8_t raw[MAX_PACKET_SIZE]; // raw bytes
uint8_t isAckNak;
} ParsedPacket;
extern void ResetPacketCounter(void);
extern uint8_t ReadPacketCounter(void);
extern ParsedPacket ReceivePacket(void);
extern uint8_t ReadByte();
extern uint8_t ReadAndAckByte(void);
extern uint8_t KeepAlive(void);
void SendPacket(uint8_t* payload, uint8_t length);
ParsedPacket* SendCustom(const uint8_t* data, int len, int *out_count);
ParsedPacket* ReceivePackets(int *out_count);
#endif /* INC_IKW1281CONNECTION_H_ */

View File

@@ -0,0 +1,782 @@
/*
* kline.c
*
* Created on: Aug 18, 2025
* Author: herli
*/
#include "kline.h"
#include "IKW1281Connection.h"
#include "hc_06.h"
#include "stdint.h"
#include "main.h"
uint8_t BitBang = 0;
void KLine_Send5Baud(uint8_t data)
{
HAL_UART_DeInit(&huart1);
// Reconfigure TX pin as output
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = KLINE_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(KLINE_GPIO_PORT, &GPIO_InitStruct);
// 5 baud = 200ms per bit
const uint32_t bit_delay_ms = 200;
// Start bit (low)
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_RESET);
HAL_Delay(bit_delay_ms);
// Send 8 bits, LSB first
for (int i = 0; i < 8; i++) {
if (data & (1 << i)) {
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_RESET);
}
HAL_Delay(bit_delay_ms);
}
// Stop bit (high)
HAL_GPIO_WritePin(KLINE_GPIO_PORT, KLINE_PIN, GPIO_PIN_SET);
HAL_Delay(bit_delay_ms);
HAL_UART_Init(&huart1); // ensures TX idles high, necessary for g431
HAL_UART_Receive_IT(&huart1, RxData, 1);
}
int WakeUp(uint8_t controllerAddress, uint8_t evenParity){
KLine_Send5Baud(controllerAddress); // ECU address
connectionAlive = 1;
uint8_t syncByte = ReadByte();
if(syncByte != 0x55){return 0;}
uint8_t keywordLsb = ReadByte();
uint8_t keywordMsb = ReadAndAckByte();
int protocolVersion = ((keywordMsb & 0x7F) << 7) + (keywordLsb & 0x7F);
return protocolVersion;
}
/*void PrintAsciiPayload(const ParsedPacket* packet, UART_HandleTypeDef* huart)
{
char data[128]; // adjust size as needed
int len = 0;
// Start with a label
len += sprintf(&data[len], "ASCII: ");
for (int i = 4; i < packet->length - 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 ECUs 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;
}

View File

@@ -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_ */

View File

@@ -21,7 +21,9 @@
/* Private includes ----------------------------------------------------------*/ /* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */ /* USER CODE BEGIN Includes */
#include "kline.h"
#include "IKW1281Connection.h"
#include "hc_06.h"
/* USER CODE END Includes */ /* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/
@@ -74,7 +76,70 @@ static void MX_USART6_UART_Init(void);
/* Private user code ---------------------------------------------------------*/ /* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */ /* 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 */ /* USER CODE END 0 */
/** /**
@@ -119,7 +184,10 @@ int main(void)
MX_USART3_UART_Init(); MX_USART3_UART_Init();
MX_USART6_UART_Init(); MX_USART6_UART_Init();
/* USER CODE BEGIN 2 */ /* 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 */ /* USER CODE END 2 */
/* Infinite loop */ /* Infinite loop */
@@ -129,6 +197,36 @@ int main(void)
/* USER CODE END WHILE */ /* USER CODE END WHILE */
/* USER CODE BEGIN 3 */ /* 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 */ /* USER CODE END 3 */
} }