Skip to content

Commit 1d84de4

Browse files
authored
EHU32 v0.9.2
Increased stability in measurement modes, the messages are now sent less frequently Added a watchdog task, reset by radio messages Extended time before automatic AC macro kicks in after entering the AC menu
1 parent a8c90ec commit 1d84de4

File tree

4 files changed

+107
-45
lines changed

4 files changed

+107
-45
lines changed

src/A2DP.ino

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ volatile bool md_album_recvd=0, md_artist_recvd=0, md_title_recvd=0;
77

88
// updates the buffers
99
void avrc_metadata_callback(uint8_t md_type, const uint8_t *data2) { // fills the song title buffer with data, updates text_lenght with the amount of chars
10+
xSemaphoreTake(CAN_MsgSemaphore, portMAX_DELAY); // take the semaphore as a way to prevent the buffers being accessed elsewhere
1011
switch(md_type){
1112
case 0x1: memset(title_buffer, 0, sizeof(title_buffer));
1213
snprintf(title_buffer, sizeof(title_buffer), "%s", data2);
@@ -25,6 +26,7 @@ void avrc_metadata_callback(uint8_t md_type, const uint8_t *data2) { // fills t
2526
break;
2627
default: break;
2728
}
29+
xSemaphoreGive(CAN_MsgSemaphore);
2830
if(md_title_recvd && md_artist_recvd && md_album_recvd){
2931
DIS_forceUpdate=1; // lets the main loop() know that there's a new song title in the buffer
3032
md_title_recvd=0;
@@ -69,36 +71,40 @@ void a2dp_init(){
6971
a2dp_sink.start("EHU32"); // setting up bluetooth audio sink
7072
a2dp_started=1;
7173
DEBUG_PRINTLN("A2DP: Started!");
72-
disp_mode=0; // set display mode to audio metadata on boot
73-
writeTextToDisplay(1, "EHU32 v0.9.1 started!", "Bluetooth on", "Waiting for connection...");
74+
disp_mode=-1; // set display mode to audio metadata on boot
75+
writeTextToDisplay(1, "EHU32 v0.9.2 started!", "Bluetooth on", "Waiting for connection...");
7476
}
7577

7678
// handles events such as connecion/disconnection and audio play/pause
7779
void A2DP_EventHandler(){
7880
if(ehu_started && !a2dp_started){ // this enables bluetooth A2DP service only after the radio is started
7981
a2dp_init();
8082
}
81-
82-
if(audio_state_changed && bt_connected){ // mute external DAC when not playing; bt_connected ensures no "Connected, paused" is displayed, seems that the audio_state_changed callback comes late
83-
if(bt_audio_playing){
84-
digitalWrite(PCM_MUTE_CTL, HIGH);
85-
DIS_forceUpdate=1; // force reprinting of audio metadata when the music is playing
86-
} else {
87-
digitalWrite(PCM_MUTE_CTL, LOW);
88-
writeTextToDisplay(1, "Bluetooth connected", "", "Paused");
89-
}
90-
audio_state_changed=0;
91-
}
9283

9384
if(bt_state_changed){ // mute external DAC when not playing
9485
if(bt_connected){
9586
a2dp_sink.set_volume(127); // workaround to ensure max volume being applied on successful connection
9687
writeTextToDisplay(1, "Bluetooth connected", "", (char*)a2dp_sink.get_peer_name());
88+
if(disp_mode==-1) disp_mode=0;
9789
} else {
9890
writeTextToDisplay(1, "Bluetooth disconnected", "", "");
91+
if(disp_mode==0) disp_mode=-1;
9992
}
10093
bt_state_changed=0;
10194
}
95+
96+
if(audio_state_changed && bt_connected){ // mute external DAC when not playing; bt_connected ensures no "Connected, paused" is displayed, seems that the audio_state_changed callback comes late
97+
if(bt_audio_playing){
98+
digitalWrite(PCM_MUTE_CTL, HIGH);
99+
DIS_forceUpdate=1; // force reprinting of audio metadata when the music is playing
100+
if(disp_mode==-1) disp_mode=0;
101+
} else {
102+
digitalWrite(PCM_MUTE_CTL, LOW);
103+
writeTextToDisplay(1, "Bluetooth connected", "", "Paused");
104+
if(disp_mode==0) disp_mode=-1;
105+
}
106+
audio_state_changed=0;
107+
}
102108
}
103109

104110
// ID 0x501 DB3 0x18 indicates imminent shutdown of the radio and display; disconnect from source

src/CAN.ino

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ void canReceiveTask(void *pvParameters){
5353
if(disp_mode!=-1){ // don't bother checking the data if there's no need to update the display
5454
if(Recvd_CAN_MSG.data[0]==0x10 && (Recvd_CAN_MSG.data[1]<0x40 || (Recvd_CAN_MSG.data[1]<0x4F && Recvd_CAN_MSG.data[2]==0x50))){ // we check if the total payload of radio's message is small, if yes assume it's an Aux message
5555
DEBUG_PRINTLN("CAN: Received display update, trying to block");
56-
twai_transmit(&Msg_PreventDisplayUpdate, pdMS_TO_TICKS(10)); // radio blocking msg has to be transmitted ASAP, which is why we skip the queue
56+
twai_transmit(&Msg_PreventDisplayUpdate, pdMS_TO_TICKS(30)); // radio blocking msg has to be transmitted ASAP, which is why we skip the queue
5757
twai_read_alerts(&alerts_triggered, pdMS_TO_TICKS(10)); // read stats
5858
if(alerts_triggered & TWAI_ALERT_TX_SUCCESS){
5959
CAN_flowCtlFail=0;
@@ -62,7 +62,7 @@ void canReceiveTask(void *pvParameters){
6262
CAN_flowCtlFail=1;
6363
DEBUG_PRINTLN("CAN: Blocking failed!");
6464
}
65-
if(disp_mode==0) vTaskResume(canDisplayTaskHandle); // only retransmit the msg for audio metadata mode
65+
if(disp_mode==0 || disp_mode==2) vTaskResume(canDisplayTaskHandle); // only retransmit the msg for audio metadata mode and single line coolant, since these don't update frequently
6666
}
6767
}
6868
}
@@ -136,7 +136,7 @@ void canProcessTask(void *pvParameters){
136136
break;
137137
}
138138
case 0x208: { // AC panel button event
139-
if((RxMsg.data[0]==0x0) && (RxMsg.data[1]==0x17) && (RxMsg.data[2]<0x02)){ // FIXME!
139+
if((RxMsg.data[0]==0x0) && (RxMsg.data[1]==0x17) && (RxMsg.data[2]<0x01)){ // FIXME!
140140
vTaskResume(canAirConMacroTaskHandle); // start AC macro
141141
}
142142
break;
@@ -150,13 +150,19 @@ void canProcessTask(void *pvParameters){
150150
break;
151151
}
152152
case 0x546: { // display measurement blocks (used as a fallback)
153-
switch(RxMsg.data[0]){ // measurement block ID -> update data which the message is referencing
153+
if(disp_mode==1 || disp_mode==2) xSemaphoreTake(CAN_MsgSemaphore, portMAX_DELAY);
154+
DEBUG_PRINT("CAN: Got measurements from DIS: ");
155+
switch(RxMsg.data[0]){ // measurement block ID -> update data which the message is referencing, I may implement more cases in the future which is why switch is there
154156
case 0x0B: { // 0x0B references coolant temps
157+
DEBUG_PRINT("coolant\n");
155158
int CAN_data_coolant=RxMsg.data[5]-40;
156159
snprintf(voltage_buffer, sizeof(voltage_buffer), "No additional data available");
157160
snprintf(coolant_buffer, sizeof(coolant_buffer), "Coolant temp: %d%c%cC ", CAN_data_coolant, 0xC2, 0xB0);
158161
//snprintf(speed_buffer, sizeof(speed_buffer), "ECC not present"); // -> speed received as part of the 0x4E8 msg
159162
CAN_coolant_recvd=1;
163+
#ifdef DEBUG
164+
CAN_speed_recvd=1;
165+
#endif
160166
break;
161167
}
162168
default: break;
@@ -166,15 +172,19 @@ void canProcessTask(void *pvParameters){
166172
CAN_coolant_recvd=0;
167173
CAN_new_dataSet_recvd=1;
168174
}
175+
if(disp_mode==1 || disp_mode==2) xSemaphoreGive(CAN_MsgSemaphore); // let the message processing continue
169176
break;
170177
}
171178
case 0x548: { // AC measurement blocks
179+
if(disp_mode==1 || disp_mode==2) xSemaphoreTake(CAN_MsgSemaphore, portMAX_DELAY); // if we're in body data mode, take the semaphore to prevent the buffer being modified while the display message is being compiled
180+
DEBUG_PRINT("CAN: Got measurements from ECC: ");
172181
switch(RxMsg.data[0]){ // measurement block ID -> update data which the message is referencing
173182
case 0x07: { // 0x10 references battery voltage
174183
CAN_data_voltage=RxMsg.data[2];
175184
CAN_data_voltage/=10;
176185
snprintf(voltage_buffer, sizeof(voltage_buffer), "Voltage: %.1f V ", CAN_data_voltage);
177186
CAN_voltage_recvd=1;
187+
DEBUG_PRINT("battery voltage\n");
178188
break;
179189
}
180190
case 0x10: { // 0x10 references coolant temps
@@ -183,13 +193,15 @@ void canProcessTask(void *pvParameters){
183193
CAN_data_coolant/=10;
184194
snprintf(coolant_buffer, sizeof(coolant_buffer), "Coolant temp: %.1f%c%cC ", CAN_data_coolant, 0xC2, 0xB0);
185195
CAN_coolant_recvd=1;
196+
DEBUG_PRINT("coolant\n");
186197
break;
187198
}
188199
case 0x11: { // 0x11 references RPMs and speed
189200
CAN_data_rpm=(RxMsg.data[1]<<8 | RxMsg.data[2]);
190201
CAN_data_speed=RxMsg.data[4];
191202
snprintf(speed_buffer, sizeof(speed_buffer), "%d km/h %d RPM ", CAN_data_speed, CAN_data_rpm);
192203
CAN_speed_recvd=1;
204+
DEBUG_PRINT("speed and RPMs\n");
193205
break;
194206
}
195207
default: break;
@@ -200,14 +212,17 @@ void canProcessTask(void *pvParameters){
200212
CAN_speed_recvd=0;
201213
CAN_new_dataSet_recvd=1;
202214
}
215+
if(disp_mode==1 || disp_mode==2) xSemaphoreGive(CAN_MsgSemaphore); // let the message processing continue
203216
break;
204217
}
205-
case 0x4E8: { // this provides speed and RPMs right from the bus
206-
if(disp_mode==1 && !ECC_present){
218+
case 0x4E8: { // this provides speed and RPMs right from the bus, only for disp_mode==1
219+
if((disp_mode==1) && !ECC_present){
220+
if(disp_mode==1) xSemaphoreTake(CAN_MsgSemaphore, portMAX_DELAY);
207221
CAN_data_rpm=(RxMsg.data[2]<<8 | RxMsg.data[3]);
208222
CAN_data_speed=RxMsg.data[4];
209223
snprintf(speed_buffer, sizeof(speed_buffer), "%d km/h %d RPM ", CAN_data_speed, CAN_data_rpm);
210224
CAN_speed_recvd=1;
225+
if(disp_mode==1) xSemaphoreGive(CAN_MsgSemaphore); // let the message processing continue
211226
}
212227
break;
213228
}
@@ -219,6 +234,7 @@ void canProcessTask(void *pvParameters){
219234
a2dp_sink.reconnect();
220235
ehu_started=1;
221236
}
237+
xTaskNotifyGive(canWatchdogTaskHandle); // reset the watchdog
222238
break;
223239
}
224240
case 0x6C8: { // if any ECC module message is received, assume ECC is available to request measurement data from
@@ -330,9 +346,11 @@ void CANsimTask(void *pvParameters){
330346
}
331347
case 'd': {
332348
Serial.print("CURRENT FLAGS CAN: ");
333-
Serial.printf("CAN_MessageReady=%d CAN_prevTxFail=%d, DIS_forceUpdate=%d, ECC_present=%d \n", CAN_MessageReady, CAN_prevTxFail, DIS_forceUpdate, ECC_present);
349+
Serial.printf("CAN_MessageReady=%d CAN_prevTxFail=%d, DIS_forceUpdate=%d, ECC_present=%d, ehu_started=%d \n", CAN_MessageReady, CAN_prevTxFail, DIS_forceUpdate, ECC_present, ehu_started);
334350
Serial.print("CURRENT FLAGS BODY: ");
335351
Serial.printf("CAN_voltage_recvd=%d CAN_coolant_recvd=%d, CAN_speed_recvd=%d, CAN_new_dataSet_recvd=%d \n", CAN_voltage_recvd, CAN_coolant_recvd, CAN_speed_recvd, CAN_new_dataSet_recvd);
352+
Serial.print("TIME AND STUFF: ");
353+
Serial.printf("last_millis_req=%lu last_millis_disp=%lu, millis=%lu \n", last_millis_req, last_millis_disp, millis());
336354
Serial.printf("CanMsgSemaphore state: %d \n", checkMutexState());
337355
Serial.printf("TxQueue items: %d, RxQueue items: %d \n", uxQueueMessagesWaiting(canTxQueue), uxQueueMessagesWaiting(canRxQueue));
338356
break;
@@ -345,13 +363,13 @@ void CANsimTask(void *pvParameters){
345363
}
346364
#endif
347365

348-
// this task waits for a flow control packet from the display
366+
// this task implements ISO 15765-2 (multi-packet transmission over CAN frames) in a crude, but hopefully robust way in order to send frames to the display
349367
void canDisplayTask(void *pvParameters){
350368
while(1){
351-
if(xSemaphoreTake(CAN_MsgSemaphore, pdMS_TO_TICKS(100))==pdTRUE){ // if the buffer is being accessed, block indefinitely
369+
if(xSemaphoreTake(CAN_MsgSemaphore, portMAX_DELAY)==pdTRUE){ // if the buffer is being accessed, block indefinitely
352370
if(CAN_flowCtlFail){ // failed transmitting flow control before the display resulting in an error frame, wait for a bit before sending it again, skip queue
353371
vTaskDelay(pdMS_TO_TICKS(1));
354-
twai_transmit(&Msg_PreventDisplayUpdate, pdMS_TO_TICKS(10)); // hope for the best and send flow control again
372+
twai_transmit(&Msg_PreventDisplayUpdate, pdMS_TO_TICKS(10)); // hope for the best and send flow control again, skip queue
355373
CAN_flowCtlFail=0;
356374
vTaskDelay(pdMS_TO_TICKS(20));
357375
}
@@ -362,24 +380,24 @@ void canDisplayTask(void *pvParameters){
362380
sendMultiPacket();
363381
}
364382
//xEventGroupWaitBits(CAN_Events, CAN_MessageReady, pdFALSE, pdFALSE, portMAX_DELAY); // this waits until CAN_MessageReady is set by the transmit function (only in case of a successful TX)
365-
if(CAN_MessageReady && !CAN_prevTxFail){ // possibly not needed anymore?
383+
if(CAN_MessageReady && !CAN_prevTxFail){ // possibly not needed anymore? nope, still needed until I'm competent enough to implement EventGroups
366384
DEBUG_PRINTLN("CAN: Now waiting for 2C1...");
367-
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); // waiting for a notification from the canProcessTask once a flow control frame is received
385+
xTaskNotifyWait(0, 0, NULL, pdMS_TO_TICKS(100));
368386
sendMultiPacketData();
369387
xTaskNotifyStateClear(NULL);
370388
} else {
371389
vTaskDelay(10);
372390
}
373391
xSemaphoreGive(CAN_MsgSemaphore); // release the semaphore
374392
}
375-
vTaskSuspend(NULL);
393+
vTaskSuspend(NULL); // have the display task stop itself
376394
}
377395
}
378396

379397
// this task provides asynchronous simulation of button presses on the AC panel, quickly toggling AC
380398
void canAirConMacroTask(void *pvParameters){
381399
while(1){
382-
vTaskDelay(100);
400+
vTaskDelay(500); // initial delay has to be extended, in some cases 100ms was not enough to have the AC menu appear on the screen, resulting in the inputs being dropped and often entering the vent config instead
383401
xQueueSend(canTxQueue, &Msg_ACmacro_down, portMAX_DELAY);
384402
vTaskDelay(100);
385403
xQueueSend(canTxQueue, &Msg_ACmacro_press, portMAX_DELAY);
@@ -451,6 +469,7 @@ void sendMultiPacketData(){ // should only be executed after the display ackno
451469

452470
// function to queue a frame requesting measurement blocks
453471
void requestMeasurementBlocks(){
472+
DEBUG_PRINTLN("CAN: Requesting measurements...");
454473
if(ECC_present){ // request measurement blocks from the climate control module
455474
xQueueSend(canTxQueue, &Msg_MeasurementRequestECC, portMAX_DELAY);
456475
} else {
@@ -464,13 +483,14 @@ void canActionEhuButton0(){ // do not use for CD30! it does not have a "
464483

465484
void canActionEhuButton1(){ // regular audio metadata mode
466485
if(disp_mode!=0){
467-
disp_mode=0;
486+
if(bt_audio_playing) disp_mode=0; // we have to check whether the music is playing, else we the buffered song title just stays there
468487
DIS_forceUpdate=1;
469488
}
470489
}
471490

472491
void canActionEhuButton2(){ // printing speed+rpm, coolant and voltage from measurement blocks
473492
if(disp_mode!=1){
493+
CAN_new_dataSet_recvd=0;
474494
disp_mode=1;
475495
disp_mode_changed=1;
476496
DEBUG_PRINTLN("DISP_MODE: Switching to vehicle data...");
@@ -479,6 +499,7 @@ void canActionEhuButton2(){ // printing speed+rpm, coolant and voltage f
479499

480500
void canActionEhuButton3(){
481501
if(disp_mode!=2){
502+
CAN_new_dataSet_recvd=0;
482503
disp_mode=2;
483504
disp_mode_changed=1;
484505
DEBUG_PRINTLN("DISP_MODE: Switching to 1-line coolant...");

0 commit comments

Comments
 (0)