Skip to content

Commit 0b05861

Browse files
authored
EHU32 v0.7
Added proper conversion to UTF-16, audio metadata containing accented characters is now supported* Switched from delay to vTaskDelay Extended the RX buffer to catch more events, seems to have no ill effects *various displays may not support some extended latin characters, thus some strings may not be displayed in full - in such cases all the text coming after "unrecognized" letter is not shown at all
1 parent fb6a9cf commit 0b05861

File tree

6 files changed

+85
-46
lines changed

6 files changed

+85
-46
lines changed

src/A2DP.ino

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ void a2dp_init(){
6363
} else {
6464
a2dp_sink.set_auto_reconnect(true);
6565
}
66-
67-
a2dp_sink.start("EHU32"); // setting up bluetooth audio sink
66+
67+
a2dp_sink.start("EHU32");
68+
// setting up bluetooth audio sink
6869
a2dp_started=1;
6970
if(DEBUGGING_ON) Serial.println("A2DP: Started!");
7071

@@ -111,5 +112,6 @@ void a2dp_shutdown(){
111112
a2dp_sink.disconnect();
112113
ehu_started=0; // so it is possible to restart and reconnect the source afterwards in the rare case radio is shutdown but ESP32 is still powered up
113114
a2dp_started=0; // while extremely unlikely to happen in the vehicle, this comes handy for debugging on my desk setup
115+
if(DEBUGGING_ON) Serial.println("CAN: EHU went down! Disconnecting A2DP.");
114116
}
115117
}

src/BusReceive.ino

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// initialize CAN BUS
22
void twai_init(){
33
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_5, GPIO_NUM_4, TWAI_MODE_NORMAL); // CAN bus set up
4-
g_config.rx_queue_len=20;
4+
g_config.rx_queue_len=40;
55
g_config.tx_queue_len=5;
66
g_config.intr_flags=(ESP_INTR_FLAG_LEVEL1 & ESP_INTR_FLAG_IRAM);
77
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
@@ -82,36 +82,36 @@ void canDecodeAC(){
8282
if(DEBUGGING_ON) Serial.println("CAN: Decoding AC buttons");
8383
if((RxMessage.data[0]==0x0) && (RxMessage.data[1]==0x17) && (RxMessage.data[2]<0x03)){ // knob held for short time
8484
int ac_dlc=3, button_delay=100;
85-
delay(button_delay);
85+
vTaskDelay(pdMS_TO_TICKS(button_delay));
8686
//down once
8787
char ac_buffer[8]={0x08, 0x16, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; // todo optimize this garbage
8888
sendPacket(0x208, ac_buffer, ac_dlc);
89-
delay(button_delay);
89+
vTaskDelay(pdMS_TO_TICKS(button_delay));
9090
//push button
9191
ac_buffer[0]=0x01;
9292
ac_buffer[1]=0x17;
9393
ac_buffer[2]=0x00;
9494
sendPacket(0x208, ac_buffer, ac_dlc);
95-
delay(button_delay);
95+
vTaskDelay(pdMS_TO_TICKS(button_delay));
9696
//release button with a time constant
9797
ac_buffer[0]=0x00;
9898
ac_buffer[2]=0x10;
9999
sendPacket(0x208, ac_buffer, ac_dlc);
100-
delay(button_delay);
100+
vTaskDelay(pdMS_TO_TICKS(button_delay));
101101
//turn down twice
102102
ac_buffer[0]=0x08;
103103
ac_buffer[1]=0x16;
104104
ac_buffer[2]=0x01;
105105
sendPacket(0x208, ac_buffer, ac_dlc);
106-
delay(button_delay);
106+
vTaskDelay(pdMS_TO_TICKS(button_delay));
107107
sendPacket(0x208, ac_buffer, ac_dlc);
108-
delay(button_delay);
108+
vTaskDelay(pdMS_TO_TICKS(button_delay));
109109
//push button
110110
ac_buffer[0]=0x01;
111111
ac_buffer[1]=0x17;
112112
ac_buffer[2]=0x00;
113113
sendPacket(0x208, ac_buffer, ac_dlc);
114-
delay(button_delay);
114+
vTaskDelay(pdMS_TO_TICKS(button_delay));
115115
//release button with a time constant
116116
ac_buffer[0]=0x00;
117117
ac_buffer[2]=0x10;
@@ -130,7 +130,7 @@ void canUpdateDisplay(){
130130
if(DIS_autoupdate && disp_mode!=-1){ // don't bother checking the data if there's no need to update the display
131131
if(RxMessage.data[0]==0x10 && RxMessage.data[1]<0x40){ // we check if the total payload of radio's message is small, if yes assume it's an Aux message
132132
//Serial.println("Got 6C1 # 10 XX...");
133-
delay(50);
133+
vTaskDelay(pdMS_TO_TICKS(50));
134134
sendMultiPacket();
135135
}
136136
}
@@ -240,7 +240,7 @@ void canActionEhuButton6(){
240240

241241
void canActionEhuButton7(){
242242
a2dp_sink.disconnect();
243-
delay(1000);
243+
vTaskDelay(pdMS_TO_TICKS(1000));
244244
ESP.restart();
245245
}
246246

src/BusSend.ino

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ void sendPacket(int id, char can_send_buffer[8], int dlc=8){
1818
if(DEBUGGING_ON) Serial.print("TX:OK ");
1919
} else {
2020
if(DEBUGGING_ON) Serial.print("TX:FAIL ");
21-
CAN_prevTxFail=1;
21+
CAN_prevTxFail=1; // the main loop will try to transmit the message again on the next iteration
2222
}
2323
} else {
2424
if(DEBUGGING_ON) Serial.print("AR:FAIL:");
25+
CAN_prevTxFail=1;
2526
if(alert_result==ESP_ERR_INVALID_ARG){
2627
if(DEBUGGING_ON) Serial.print("INV_ARG");
2728
}
@@ -70,7 +71,7 @@ void sendMultiPacketData(){ // should only be executed after the display ackno
7071
for(int i=1;i<64 && (CAN_MsgArray[i][0]!=0x00 && !CAN_prevTxFail);i++){ // this loop will stop sending data once the next packet doesn't contain a label
7172
if(DEBUGGING_ON) sendPacketSerial(0x6C1, CAN_MsgArray[i]);
7273
sendPacket(0x6C1, CAN_MsgArray[i]);
73-
delay(2);
74+
vTaskDelay(pdMS_TO_TICKS(2));
7475
}
7576
if(DEBUGGING_ON) Serial.println();
7677
CAN_MessageReady=0; // new buffers can now be prepared as the message has been sent

src/EHU32.ino

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include "driver/twai.h"
1111
#include <WiFi.h>
1212
#include <WiFiAP.h>
13-
#include <ESPmDNS.h>
1413
#include <WiFiUdp.h>
1514
#include <ArduinoOTA.h>
1615

@@ -30,7 +29,7 @@ float CAN_data_coolant=0, CAN_data_voltage=0;
3029
bool ehu_started=0, a2dp_started=0, bt_connected=0, bt_state_changed=0, bt_audio_playing=0, audio_state_changed=0;
3130
// data buffers
3231
static char utf16buffer[384], utf16_title[128], utf16_artist[128], utf16_album[128], CAN_MsgArray[64][8], title_buffer[64], artist_buffer[64], album_buffer[64], coolant_buffer[32], speed_buffer[32], voltage_buffer[32];
33-
// display mode 0 -> song metadata, 1 -> body data, -1 -> prevent screen updates
32+
// display mode 0 -> song metadata, 1 -> body data, 2 -> MP3 player -1 -> prevent screen updates
3433
int disp_mode=3;
3534
bool disp_mode_changed=0, disp_mode_changed_with_delay=0;
3635
// time to compare against
@@ -41,7 +40,7 @@ void sendMultiPacketData();
4140
void setup() {
4241
pinMode(PCM_MUTE_CTL, OUTPUT);
4342
digitalWrite(PCM_MUTE_CTL, HIGH);
44-
delay(100);
43+
vTaskDelay(pdMS_TO_TICKS(100));
4544
if(DEBUGGING_ON) Serial.begin(921600); // serial comms for debug
4645
twai_init();
4746
}
@@ -50,11 +49,11 @@ void setup() {
5049
void processDataBuffer(bool disp_mode_override=0, char* up_line_text=nullptr, char* mid_line_text=nullptr, char* low_line_text=nullptr){ // disp_mode_override exists as a simple way to print one-off messages (like board status, errors and such)
5150
if(!CAN_MessageReady){ // only prepare new buffers once the previously prepared message has been sent. DIS_forceUpdate is still 1, so this function should be called again next loop
5251
if(!disp_mode_override){
53-
if(disp_mode==0 && (album_buffer[0]!='\0' || title_buffer[0]!='\0' || artist_buffer[0]!='\0')){
52+
if((disp_mode==0 || disp_mode==2) && (album_buffer[0]!='\0' || title_buffer[0]!='\0' || artist_buffer[0]!='\0')){
5453
prepareMultiPacket(utf8_conversion(album_buffer, title_buffer, artist_buffer)); // prepare a 3-line message (audio Title, Album and Artist)
5554
}
5655
if(disp_mode==1){
57-
prepareMultiPacket(utf8_conversion(coolant_buffer, speed_buffer, voltage_buffer)); // prepare a vehicle dat
56+
prepareMultiPacket(utf8_conversion(coolant_buffer, speed_buffer, voltage_buffer)); // vehicle data buffer
5857
}
5958
} else { // overriding buffers, making sure to switch disp_mode_changed_with delay so the message stays there for a while
6059
prepareMultiPacket(utf8_conversion(up_line_text, mid_line_text, low_line_text));
@@ -76,7 +75,7 @@ void loop() {
7675
if(DEBUGGING_ON) Serial.println("CAN: TWAI DRIVER UNINSTALL OK");
7776
} else {
7877
if(DEBUGGING_ON) Serial.println("CAN: TWAI DRIVER UNINSTALL FAIL!!! Rebooting..."); // total fail - just reboot at this point
79-
delay(100);
78+
vTaskDelay(pdMS_TO_TICKS(100));
8079
ESP.restart();
8180
}
8281
twai_init();
@@ -86,12 +85,12 @@ void loop() {
8685
CAN_prevTxFail=0;
8786
}
8887
if(CAN_MessageReady){ // CAN_MessageReady is set after the display has been requested is sent. This waits for the display to reply with an ACK (id 0x2C1)
89-
delay(5); // waits a bit because sometimes stuff happens so fast we get the ACK meant for the radio, we need to wait for the radio to stop talking
88+
vTaskDelay(pdMS_TO_TICKS(5)); // waits a bit because sometimes stuff happens so fast we get the ACK meant for the radio, we need to wait for the radio to stop talking
9089
RxMessage.identifier=0x0; // workaround for debugging when the bus is not on
9190
while(!RxMessage.identifier==0x2C1){
9291
if(DEBUGGING_ON) Serial.println("CAN: Waiting for 0x2C1 ACK...");
9392
while(twai_receive(&RxMessage, pdMS_TO_TICKS(5))!=ESP_OK){ // wait for the desired message
94-
delay(1); // I've honestly tried everything, refreshing status and alerts. This shit just doesn't work properly
93+
vTaskDelay(pdMS_TO_TICKS(1)); // I've honestly tried everything, refreshing status and alerts. This shit just doesn't work properly
9594
}
9695
}
9796
sendMultiPacketData();
@@ -128,6 +127,6 @@ void loop() {
128127
if(DIS_forceUpdate && disp_mode==0){ // handles data processing for A2DP AVRC data events
129128
processDataBuffer();
130129
}
131-
130+
132131
A2DP_EventHandler(); // process bluetooth and audio flags set by interrupt callbacks
133132
}

src/OTA.ino

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ void OTA_start(){
1010
if(DEBUGGING_ON) Serial.println("TWAI: Stopped successfully.");
1111
}
1212
a2dp_sink.end(true);
13-
delay(500);
13+
vTaskDelay(pdMS_TO_TICKS(500));
1414
if (!WiFi.softAP(ssid, password)) {
1515
if(DEBUGGING_ON) Serial.println("FAIL: Soft AP creation failed.");
16-
delay(1000);
16+
vTaskDelay(pdMS_TO_TICKS(1000));
1717
ESP.restart();
1818
} else {
1919
IPAddress myIP = WiFi.softAPIP();
20-
ArduinoOTA.setHostname("EHU32");
2120
ArduinoOTA
21+
.setMdnsEnabled(false)
22+
.setRebootOnSuccess(true)
2223
.onStart([]() {
2324
String type;
2425
if (ArduinoOTA.getCommand() == U_FLASH)
@@ -44,7 +45,7 @@ void OTA_start(){
4445
while(1){
4546
ArduinoOTA.handle();
4647
if(OTA_Finished){
47-
delay(1000);
48+
vTaskDelay(pdMS_TO_TICKS(1000));
4849
ESP.restart();
4950
}
5051
}

src/TextHandler.ino

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,65 @@ void clear_buffer(char* buf_to_clear){
88
}
99
}
1010

11-
// converts EASCII data from arguments to fake UTF-16, then compiles a full display message with formatting; returns total bytes written as part of message payload
12-
int utf8_conversion(char* upper_line_buffer, char* middle_line_buffer, char* lower_line_buffer){ // go through title, artist, album faking UTF-16 since 16-02-24
11+
// process an UTF-8 buffer, returns the amount of chars processed
12+
unsigned int utf8_to_utf16(const char* utf8_buffer, char* utf16_buffer){
13+
unsigned int utf16_bytecount=0;
14+
while (*utf8_buffer!='\0'){
15+
uint32_t charint=0;
16+
if ((*utf8_buffer&0x80)==0x00){
17+
charint=*utf8_buffer&0x7F;
18+
utf8_buffer++;
19+
}
20+
else if ((*utf8_buffer&0xE0)==0xC0){
21+
charint=(*utf8_buffer & 0x1F)<<6;
22+
charint|=(*(utf8_buffer+1)&0x3F);
23+
utf8_buffer+=2;
24+
}
25+
else if ((*utf8_buffer&0xF0)==0xE0){
26+
charint=(*utf8_buffer&0x0F)<<12;
27+
charint|=(*(utf8_buffer+1)&0x3F)<<6;
28+
charint|=(*(utf8_buffer+2)&0x3F);
29+
utf8_buffer += 3;
30+
}
31+
else if ((*utf8_buffer&0xF8)==0xF0){
32+
charint=(*utf8_buffer&0x07)<<18;
33+
charint|=(*(utf8_buffer+1)&0x3F)<<12;
34+
charint|=(*(utf8_buffer+2)&0x3F)<<6;
35+
charint|=(*(utf8_buffer+3)&0x3F);
36+
utf8_buffer+=4;
37+
}
38+
else {
39+
return utf16_bytecount/2;
40+
}
41+
// only process supported chars, latin and extended latin works, cyrillic does not
42+
if ((charint>=0x0000&&charint<=0x024F) || (charint>=0x1E00 && charint<=0x2C6F)){
43+
if (charint>=0x10000) {
44+
charint-=0x10000;
45+
utf16_buffer[utf16_bytecount++]=static_cast<char>((charint>>10)+0xD8);
46+
utf16_buffer[utf16_bytecount++]=static_cast<char>((charint>>2)&0xFF);
47+
utf16_buffer[utf16_bytecount++]=static_cast<char>(0xDC|((charint>>10)&0x03));
48+
utf16_buffer[utf16_bytecount++]=static_cast<char>((charint&0x03)<<6);
49+
}
50+
else {
51+
utf16_buffer[utf16_bytecount++]=static_cast<char>((charint>>8)&0xFF);
52+
utf16_buffer[utf16_bytecount++]=static_cast<char>(charint&0xFF);
53+
}
54+
}
55+
}
56+
return utf16_bytecount/2; // amount of chars processed
57+
}
58+
59+
// converts UTF-8 strings from arguments to real UTF-16, then compiles a full display message with formatting; returns total bytes written as part of message payload
60+
int utf8_conversion(char* upper_line_buffer, char* middle_line_buffer, char* lower_line_buffer){
1361
int upper_line_buffer_lenght=0, middle_line_buffer_lenght=0, lower_line_buffer_lenght=0;
1462
if(upper_line_buffer!=nullptr){ // calculating string lenghts to keep track of processed data
15-
upper_line_buffer_lenght=snprintf(nullptr, 0, upper_line_buffer);
63+
upper_line_buffer_lenght=utf8_to_utf16(upper_line_buffer, utf16_album);
1664
}
1765
if(middle_line_buffer!=nullptr){
18-
middle_line_buffer_lenght=snprintf(nullptr, 0, middle_line_buffer);
66+
middle_line_buffer_lenght=utf8_to_utf16(middle_line_buffer, utf16_title);
1967
}
2068
if(lower_line_buffer!=nullptr){
21-
lower_line_buffer_lenght=snprintf(nullptr, 0, lower_line_buffer);
22-
}
23-
24-
for(int i=0; i<middle_line_buffer_lenght && middle_line_buffer!=nullptr; i++){ // janky conversion to UTF-16
25-
utf16_title[i*2]=0x0;
26-
utf16_title[(i*2)+1]=middle_line_buffer[i];
27-
}
28-
for(int i=0; i<upper_line_buffer_lenght && upper_line_buffer!=nullptr; i++){
29-
utf16_album[i*2]=0x0;
30-
utf16_album[(i*2)+1]=upper_line_buffer[i];
31-
}
32-
for(int i=0; i<lower_line_buffer_lenght && lower_line_buffer!=nullptr; i++){
33-
utf16_artist[i*2]=0x0;
34-
utf16_artist[(i*2)+1]=lower_line_buffer[i];
69+
lower_line_buffer_lenght=utf8_to_utf16(lower_line_buffer, utf16_artist);
3570
}
3671

3772
if(DEBUGGING_ON){ // debug stuff
@@ -141,4 +176,5 @@ int utf8_conversion(char* upper_line_buffer, char* middle_line_buffer, char* low
141176
utf16buffer[0]=(last_byte_written+1); // TOTAL PAYLOAD SIZE based on how many bytes have been written
142177
utf16buffer[3]=utf16buffer[0]-3;
143178
return last_byte_written+1; // return the total message size
144-
}
179+
}
180+

0 commit comments

Comments
 (0)