1+ const twai_message_t
2+ ehu_bong_start={ .identifier =0x201 , .data_length_code =3 , .data ={0x01 , 0xFF , 0x00 }},
3+ ehu_bong_end={ .identifier =0x201 , .data_length_code =3 , .data ={0x01 , 0xFF , 0x32 }},
4+ opcom_helloEHU={ .identifier =0x241 , .data_length_code =8 , .data ={0x01 , 0x20 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }}, // expect 641 # 01 60 ...
5+ opcom_resetEHU={ .identifier =0x241 , .data_length_code =8 , .data ={0x03 , 0x3B , 0x5E , 0x01 , 0x55 , 0x55 , 0x55 , 0x55 }}, // expect 641 # 03 7F 3B 78 ... and 641 # 02 7B 5E afterwards
6+ opcom_requestPartID={ .identifier =0x241 , .data_length_code =8 , .data ={0x02 , 0x1A , 0x9A , 0x55 , 0x55 , 0x55 , 0x55 , 0x55 }},
7+ opcom_requestCodeIndex={ .identifier =0x241 , .data_length_code =8 , .data ={0x02 , 0x1A , 0x73 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
8+ opcom_requestSwVer={ .identifier =0x241 , .data_length_code =8 , .data ={0x02 , 0x1A , 0x7B , 0x55 , 0x55 , 0x55 , 0x55 , 0x55 }},
9+ ISO_AcceptTransmission={ .identifier =0x241 , .data_length_code =8 , .data ={0x30 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
10+ opcom_requestCodeIndexDump={ .identifier =0x241 , .data_length_code =8 , .data ={0x06 , 0x23 , 0x00 , 0x00 , 0x00 , 0x00 , 0xAB , 0x00 }};
11+
12+
13+ const twai_message_t
14+ codeIndex01201[26 ]={ // entire struct of the 01201 code index
15+ { .identifier =0x241 , .data_length_code =8 , .data ={0x10 , 0xB0 , 0x3B , 0x3E , 0x00 , 0x00 , 0xAB , 0x30 }},
16+ { .identifier =0x241 , .data_length_code =8 , .data ={0x21 , 0x31 , 0x32 , 0x30 , 0x31 , 0x00 , 0x00 , 0x02 }},
17+ { .identifier =0x241 , .data_length_code =8 , .data ={0x22 , 0x05 , 0x0A , 0x00 , 0x00 , 0xDE , 0x03 , 0x00 }},
18+ { .identifier =0x241 , .data_length_code =8 , .data ={0x23 , 0xE4 , 0x00 , 0x04 , 0x00 , 0x00 , 0x0E , 0x00 }},
19+ { .identifier =0x241 , .data_length_code =8 , .data ={0x24 , 0x00 , 0xE9 , 0x00 , 0x00 , 0x00 , 0xE8 , 0x28 }},
20+ { .identifier =0x241 , .data_length_code =8 , .data ={0x25 , 0x00 , 0x01 , 0x64 , 0xC8 , 0xFF , 0x01 , 0x64 }},
21+ { .identifier =0x241 , .data_length_code =8 , .data ={0x26 , 0xC8 , 0xFF , 0xB3 , 0x00 , 0x00 , 0x00 , 0x32 }},
22+ { .identifier =0x241 , .data_length_code =8 , .data ={0x27 , 0x48 , 0x48 , 0x4C , 0x1E , 0x46 , 0xB4 , 0x50 }},
23+ { .identifier =0x241 , .data_length_code =8 , .data ={0x28 , 0x96 , 0x02 , 0x32 , 0x48 , 0x00 , 0x28 , 0x4C }},
24+ { .identifier =0x241 , .data_length_code =8 , .data ={0x29 , 0x28 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
25+ { .identifier =0x241 , .data_length_code =8 , .data ={0x2A , 0x00 , 0x48 , 0x00 , 0x00 , 0x00 , 0x00 , 0x05 }},
26+ { .identifier =0x241 , .data_length_code =8 , .data ={0x2B , 0x0A , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
27+ { .identifier =0x241 , .data_length_code =8 , .data ={0x2C , 0x00 , 0x00 , 0x00 , 0x15 , 0x23 , 0x03 , 0x01 }},
28+ { .identifier =0x241 , .data_length_code =8 , .data ={0x2D , 0x00 , 0x11 , 0x1F , 0x03 , 0x00 , 0x00 , 0x27 }},
29+ { .identifier =0x241 , .data_length_code =8 , .data ={0x2E , 0x00 , 0x00 , 0x00 , 0x21 , 0x09 , 0x03 , 0x00 }},
30+ { .identifier =0x241 , .data_length_code =8 , .data ={0x2F , 0x00 , 0x00 , 0x00 , 0x00 , 0x04 , 0x00 , 0x00 }},
31+ { .identifier =0x241 , .data_length_code =8 , .data ={0x20 , 0x00 , 0x0E , 0x00 , 0x02 , 0x00 , 0x0E , 0x00 }},
32+ { .identifier =0x241 , .data_length_code =8 , .data ={0x21 , 0x02 , 0x00 , 0x0E , 0x00 , 0x00 , 0x00 , 0x00 }},
33+ { .identifier =0x241 , .data_length_code =8 , .data ={0x22 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
34+ { .identifier =0x241 , .data_length_code =8 , .data ={0x23 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
35+ { .identifier =0x241 , .data_length_code =8 , .data ={0x24 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
36+ { .identifier =0x241 , .data_length_code =8 , .data ={0x25 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
37+ { .identifier =0x241 , .data_length_code =8 , .data ={0x26 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
38+ { .identifier =0x241 , .data_length_code =8 , .data ={0x27 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
39+ { .identifier =0x241 , .data_length_code =8 , .data ={0x28 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }},
40+ { .identifier =0x241 , .data_length_code =8 , .data ={0x29 , 0x00 , 0x5A , 0x00 , 0x00 , 0xAE , 0x00 , 0x00 }}
41+ };
42+
43+ // below functions are used to simplify interaction with freeRTOS eventGroups
44+ void setFlag (uint32_t bit){
45+ xEventGroupSetBits (eventGroup, bit);
46+ }
47+
48+ // clears an event bit
49+ void clearFlag (uint32_t bit){
50+ xEventGroupClearBits (eventGroup, bit);
51+ }
52+
53+ // waits for an event bit to be set, blocking indefinitely if 2nd argument not provided
54+ void waitForFlag (uint32_t bit, TickType_t ticksToWait=portMAX_DELAY){
55+ xEventGroupWaitBits (eventGroup, bit, pdFALSE, pdTRUE, ticksToWait);
56+ }
57+
58+ // Check if a specific event bit is set (without blocking)
59+ bool checkFlag (uint32_t bit){
60+ EventBits_t bits=xEventGroupGetBits (eventGroup);
61+ return (bits&bit)!=0 ;
62+ }
63+ // initializing CAN communication
64+ void twai_init (){
65+ twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT (GPIO_NUM_5, GPIO_NUM_4, TWAI_MODE_NORMAL); // CAN bus set up
66+ g_config.rx_queue_len =40 ;
67+ g_config.tx_queue_len =5 ;
68+ g_config.intr_flags =(ESP_INTR_FLAG_NMI & ESP_INTR_FLAG_IRAM); // run the TWAI driver at the highest possible priority
69+ twai_timing_config_t t_config = {.brp = 42 , .tseg_1 = 15 , .tseg_2 = 4 , .sjw = 3 , .triple_sampling = false }; // set CAN prescalers and time quanta for 95kbit
70+ twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL ();
71+ DEBUG_PRINT (" \n CAN/TWAI SETUP => " );
72+ if (twai_driver_install (&g_config, &t_config, &f_config) == ESP_OK) {
73+ DEBUG_PRINT (" DRV_INSTALL: OK " );
74+ } else {
75+ DEBUG_PRINT (" DRV_INST: FAIL " );
76+ }
77+ if (twai_start () == ESP_OK) {
78+ DEBUG_PRINT (" DRV_START: OK " );
79+ } else {
80+ DEBUG_PRINT (" DRV_START: FAIL " );
81+ }
82+ uint32_t alerts_to_enable=TWAI_ALERT_TX_SUCCESS;
83+ if (twai_reconfigure_alerts (alerts_to_enable, NULL ) == ESP_OK){
84+ DEBUG_PRINTLN (" ALERTS: OK \n " );
85+ } else {
86+ DEBUG_PRINTLN (" ALERTS: FAIL \n " );
87+ }
88+ }
89+
90+ // this task only reads CAN messages, filters them and enqueues them to be decoded ansynchronously
91+ void canReceiveTask (void *pvParameters){
92+ twai_message_t Recvd_CAN_MSG;
93+ while (1 ){
94+ if (twai_receive (&Recvd_CAN_MSG, portMAX_DELAY)==ESP_OK){
95+ switch (Recvd_CAN_MSG.identifier ){
96+ case 0x201 :
97+ case 0x641 :
98+ xQueueSend (canRxQueue, &Recvd_CAN_MSG, portMAX_DELAY); // queue the message contents to be read at a later time
99+ break ;
100+ default : break ;
101+ }
102+ }
103+ }
104+ }
105+
106+ // this task processes filtered CAN frames read from canRxQueue
107+ void canProcessTask (void *pvParameters){
108+ static twai_message_t RxMsg;
109+ uint8_t sw_year=0 , sw_month[2 ]={0 }, sw_day[2 ]={0 };
110+ while (1 ){
111+ xQueueReceive (canRxQueue, &RxMsg, portMAX_DELAY); // receives data from the internal queue
112+ switch (RxMsg.identifier ){
113+ case 0x201 : {
114+ switch (RxMsg.data [1 ]){
115+ case 0x37 :{
116+ if (!checkFlag (CAN_softwareVersionOkay) && RxMsg.data [2 ]==20 ){
117+ xQueueSend (canTxQueue, &opcom_requestSwVer, portMAX_DELAY);
118+ setFlag (CAN_softwareVersionRequested);
119+ DEBUG_PRINTF (" Checking the software version...\r\n " );
120+ } else {
121+ if (checkFlag (CAN_softwareVersionOkay) && RxMsg.data [2 ]==20 ){
122+ setFlag (CAN_startDumping);
123+ xQueueSend (canTxQueue, &opcom_requestCodeIndexDump, portMAX_DELAY);
124+ }
125+ }
126+ break ;
127+ }
128+ case 0x39 :{
129+ if (RxMsg.data [2 ]==0x0A && checkFlag (CAN_dumpingFinished) && checkFlag (CAN_softwareVersionOkay)) setFlag (CAN_startProgramming);
130+ break ;
131+ }
132+ default : break ;
133+ }
134+ break ;
135+ }
136+ case 0x641 :{ // this will dump all the received data from 0x641
137+ if (checkFlag (CAN_waitingForISO) && RxMsg.data [0 ]==0x10 ){ // this is a mess, yes. but it does work - if it ain't broken, don't fix it.
138+ xQueueSend (canTxQueue, &ISO_AcceptTransmission, portMAX_DELAY);
139+ clearFlag (CAN_waitingForISO);
140+ }
141+ if (checkFlag (CAN_softwareVersionOkay) && !checkFlag (CAN_softwareVersionWrong) && checkFlag (CAN_softwareVersionReceived) && !checkFlag (CAN_programmingFinished)){
142+ if (checkFlag (CAN_startDumping) && !checkFlag (CAN_startProgramming) && !checkFlag (CAN_dumpingFinished) && RxMsg.data [0 ]!=0x30 ){
143+ if (RxMsg.data [0 ]==0x10 ) DEBUG_PRINTF (" Starting index code dump, make sure to save this data:\r\n " );
144+ DEBUG_PRINTF (" %03X # %02X %02X %02X %02X %02X %02X %02X %02X\r\n " , RxMsg.identifier , RxMsg.data [0 ], RxMsg.data [1 ], RxMsg.data [2 ], RxMsg.data [3 ], RxMsg.data [4 ], RxMsg.data [5 ], RxMsg.data [6 ], RxMsg.data [7 ], RxMsg.data [8 ]);
145+ }
146+ if (checkFlag (CAN_startProgramming) && checkFlag (CAN_dumpingFinished) && RxMsg.data [0 ]==0x30 ){
147+ DEBUG_PRINTF (" Got a programming accept sequence!\r\n " );
148+ }
149+ if (checkFlag (CAN_startDumping) && RxMsg.data [1 ]==0x5A ){
150+ DEBUG_PRINTF (" Index code dump finished!\r\n " );
151+ xQueueSend (canTxQueue, &ehu_bong_start, portMAX_DELAY);
152+ xQueueSend (canTxQueue, &ehu_bong_end, portMAX_DELAY);
153+ setFlag (CAN_dumpingFinished);
154+ clearFlag (CAN_startDumping);
155+ DEBUG_PRINTF (" \r\n ======== DUMPING FINISHED! ========\r\n Now press 9 for about 2 seconds to attempt the code index programming.\r\n " );
156+ DEBUG_PRINTF (" Please note that this is experimental and you're solely responsible for whatever happens.\r\n " );
157+ }
158+ if (checkFlag (CAN_dumpingFinished) && RxMsg.identifier ==0x641 && RxMsg.data [0 ]==0x30 && checkFlag (CAN_requestedCodeIndexProgramming)){
159+ setFlag (CAN_receivedProgrammingAllow);
160+ }
161+ if (checkFlag (CAN_receivedProgrammingAllow) && RxMsg.data [0 ]==0x02 && RxMsg.data [1 ]==0x7B && RxMsg.data [2 ]==0x3E ){
162+ setFlag (CAN_programmingFinished);
163+ }
164+ } else { // software version not yet checked, do that now
165+ if (checkFlag (CAN_softwareVersionRequested) && !checkFlag (CAN_softwareVersionReceived)){
166+ if (RxMsg.data [0 ]==0x10 && RxMsg.data [3 ]==0x7B ){
167+ sw_day[0 ]=RxMsg.data [4 ];
168+ sw_day[1 ]=RxMsg.data [5 ];
169+ sw_month[0 ]=RxMsg.data [7 ];
170+ }
171+ if (RxMsg.data [0 ]==0x21 ){
172+ sw_month[1 ]=RxMsg.data [1 ];
173+ sw_year=RxMsg.data [6 ];
174+ setFlag (CAN_softwareVersionReceived);
175+ }
176+ }
177+ if (checkFlag (CAN_softwareVersionReceived) && !checkFlag (CAN_programmingFinished)){
178+ if ((sw_day[0 ]==0x31 && sw_day[1 ]==0x30 && sw_month[0 ]==0x31 && sw_month[1 ]==0x30 && sw_year==0x37 ) || (sw_day[0 ]==0x32 && sw_day[1 ]==0x30 && sw_month[0 ]==0x30 && sw_month[1 ]==0x36 && sw_year==0x38 )){ // either 10.10.2007 or 20.06.2008
179+ DEBUG_PRINTF (" Detected valid software version to attempt the code index programming! Now hold 7 for 2 seconds to begin dumping the code index.\r\n " );
180+ setFlag (CAN_softwareVersionOkay);
181+ ehu_force_testmode ();
182+ } else {
183+ DEBUG_PRINTF (" Got version %02X %02X %02X %02X %02X\r\n " , sw_day[0 ], sw_day[1 ], sw_month[0 ], sw_month[1 ], sw_year);
184+ setFlag (CAN_softwareVersionWrong);
185+ }
186+ }
187+ }
188+ break ;
189+ }
190+ default : break ;
191+ }
192+ }
193+ }
194+
195+ // this task receives CAN messages from canTxQueue and transmits them asynchronously
196+ void canTransmitTask (void *pvParameters){
197+ static twai_message_t TxMessage;
198+ uint32_t alerts_triggered;
199+ int alert_result;
200+ while (1 ){
201+ xQueueReceive (canTxQueue, &TxMessage, portMAX_DELAY);
202+ TxMessage.extd =0 ;
203+ TxMessage.rtr =0 ;
204+ TxMessage.ss =0 ;
205+ TxMessage.self =0 ;
206+ // DEBUG_PRINTF("%03X # %02X %02X %02X %02X %02X %02X %02X %02X", TxMessage.identifier, TxMessage.data[0], TxMessage.data[1], TxMessage.data[2], TxMessage.data[3], TxMessage.data[4], TxMessage.data[5], TxMessage.data[6], TxMessage.data[7]);
207+ if (twai_transmit (&TxMessage, pdMS_TO_TICKS (50 ))==ESP_OK) {
208+ // DEBUG_PRINT(" Q:OK ");
209+ } else {
210+ // DEBUG_PRINT("Q:FAIL ");
211+ // setFlag(CAN_prevTxFail);
212+ }
213+ alert_result=twai_read_alerts (&alerts_triggered, pdMS_TO_TICKS (10 )); // read stats
214+ if (alert_result==ESP_OK){
215+ // DEBUG_PRINT("AR:OK ");
216+ if (alerts_triggered & TWAI_ALERT_TX_SUCCESS){
217+ // DEBUG_PRINTLN("TX:OK ");
218+ if (TxMessage.identifier ==0x241 ){ // transmitted the request for programming the code index, set the flag.
219+ setFlag (CAN_waitingForISO);
220+ }
221+
222+ } else {
223+ DEBUG_PRINTLN (" TX:FAIL " );
224+ // setFlag(CAN_prevTxFail);
225+ }
226+ } else {
227+ DEBUG_PRINT (" AR:FAIL:" );
228+ if (alert_result==ESP_ERR_INVALID_ARG){
229+ DEBUG_PRINTLN (" INV_ARG" );
230+ }
231+ if (alert_result==ESP_ERR_INVALID_STATE){
232+ DEBUG_PRINTLN (" INV_STATE" );
233+ }
234+ if (alert_result==ESP_ERR_TIMEOUT){
235+ DEBUG_PRINTLN (" TIMEOUT" );
236+ }
237+ }
238+ }
239+ }
240+
241+ void indexProgrammerTask (void *pvParameters){
242+ DEBUG_PRINTF (" IndexProgrammer: started.\r\n " );
243+ waitForFlag (CAN_dumpingFinished);
244+ waitForFlag (CAN_startProgramming);
245+ vTaskDelay (pdMS_TO_TICKS (1000 ));
246+ #ifdef DISABLE_PROGRAMMING
247+ while (1 ){
248+ DEBUG_PRINTLN (" PROGRAMMING DISABLED!\r\n " );
249+ vTaskDelay (10000 );
250+ }
251+ #endif
252+ while (1 ){
253+ setFlag (CAN_requestedCodeIndexProgramming);
254+ xQueueSend (canTxQueue, &codeIndex01201[0 ], portMAX_DELAY);
255+ waitForFlag (CAN_receivedProgrammingAllow);
256+ for (int i=1 ; i<26 ; i++){
257+ xQueueSend (canTxQueue, &codeIndex01201[i], portMAX_DELAY);
258+ vTaskDelay (pdMS_TO_TICKS (15 ));
259+ }
260+ waitForFlag (CAN_programmingFinished);
261+ DEBUG_PRINTLN (" Programming finished! Now resetting the headunit..." );
262+ vTaskDelay (pdMS_TO_TICKS (1000 ));
263+ xQueueSend (canTxQueue, &opcom_resetEHU, portMAX_DELAY);
264+ DEBUG_PRINTF (" Headunit has been reset! Check whether Aux is available in the audio menu." );
265+ while (1 ){
266+ vTaskDelay (10000 );
267+ }
268+ }
269+ }
270+
271+ void ehu_force_testmode (){
272+ xQueueSend (canTxQueue, &ehu_bong_start, portMAX_DELAY);
273+ xQueueSend (canTxQueue, &ehu_bong_end, portMAX_DELAY);
274+ }
0 commit comments