Skip to content

Commit

Permalink
EHU32 v0.8
Browse files Browse the repository at this point in the history
Now blocks "Aux" messages with high accuraccy by abusing ISO 15765-2 flow control
Tweaked timings
  • Loading branch information
PNKP237 authored Apr 10, 2024
1 parent 91ca93a commit 091b468
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 74 deletions.
33 changes: 16 additions & 17 deletions src/A2DP.ino
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
BluetoothA2DPSink a2dp_sink;
I2SStream i2s;
BluetoothA2DPSink a2dp_sink(i2s);

// updates the buffers
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
Expand Down Expand Up @@ -40,19 +41,11 @@ void a2dp_audio_state_changed(esp_a2d_audio_state_t state, void *ptr){ // callb

// start A2DP audio service
void a2dp_init(){
const i2s_config_t i2s_config = { // ext dac BLCK=26 WS/LRCK=25 DOUT=22
.mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX),
.sample_rate = 44100,
.bits_per_sample = (i2s_bits_per_sample_t)16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = (i2s_comm_format_t) (I2S_COMM_FORMAT_STAND_I2S),
.intr_alloc_flags = 0,
.dma_buf_count = 8,
.dma_buf_len = 128,
.use_apll = true,
.tx_desc_auto_clear = true
};
a2dp_sink.set_i2s_config(i2s_config);
auto i2s_conf=i2s.defaultConfig();
i2s_conf.pin_bck=26;
i2s_conf.pin_ws=25;
i2s_conf.pin_data=22;
i2s.begin(i2s_conf);
a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);
a2dp_sink.set_avrc_metadata_attribute_mask(ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_ALBUM);
a2dp_sink.set_on_connection_state_changed(a2dp_connection_state_changed);
Expand All @@ -64,11 +57,9 @@ void a2dp_init(){
a2dp_sink.set_auto_reconnect(true);
}

a2dp_sink.start("EHU32");
// setting up bluetooth audio sink
a2dp_sink.start("EHU32"); // setting up bluetooth audio sink
a2dp_started=1;
if(DEBUGGING_ON) Serial.println("A2DP: Started!");

processDataBuffer(1, "EHU32 Started!", "Bluetooth on", "Waiting for connection...");
}

Expand Down Expand Up @@ -110,8 +101,16 @@ void A2DP_EventHandler(){
void a2dp_shutdown(){
if(a2dp_started && RxMessage.data[3]==0x18){
a2dp_sink.disconnect();
a2dp_sink.stop();
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
a2dp_started=0; // while extremely unlikely to happen in the vehicle, this comes handy for debugging on my desk setup
if(DEBUGGING_ON) Serial.println("CAN: EHU went down! Disconnecting A2DP.");
}
}

void a2dp_end(){
a2dp_sink.disconnect();
a2dp_sink.stop();
a2dp_started=0;
if(DEBUGGING_ON) Serial.println("A2DP: Stopped!");
}
44 changes: 19 additions & 25 deletions src/BusReceive.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ void twai_init(){
g_config.tx_queue_len=5;
g_config.intr_flags=(ESP_INTR_FLAG_LEVEL1 & ESP_INTR_FLAG_IRAM);
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
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); // TODO: set up proper filters
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
TxMessage.extd=0; // TxMessage settings - not extended ID CAN packet
TxMessage.rtr=0; // not retransmission packet
TxMessage.ss=0; // don't transmit the packet as a single shot -> less chance of an error this way
Expand Down Expand Up @@ -35,24 +35,22 @@ void sendPacket(int id, char can_send_buffer[8], int dlc);
// process incoming CAN messages until there is none left in the buffer; picks appropriate action based on message identifier
void canReceive(){ // logical filter based on the CAN ID
while(twai_receive(&RxMessage, pdMS_TO_TICKS(10)==ESP_OK && !CAN_MessageReady)){
if(!RxMessage.rtr){
switch (RxMessage.identifier){
case 0x201: canDecodeEhuButtons();
break;
case 0x206: canDecodeWheel();
break;
case 0x208: canDecodeAC();
break;
case 0x501: a2dp_shutdown();
break;
case 0x548: if(disp_mode==1) canUpdateBodyData();
break;
case 0x4ec: if(disp_mode==3) canUpdateCoolant(); // temporarily unused, but should work without ECC module
break;
case 0x6c1: canUpdateDisplay();
break;
default: break;
}
switch (RxMessage.identifier){
case 0x201: canDecodeEhuButtons();
break;
case 0x206: canDecodeWheel();
break;
case 0x208: canDecodeAC();
break;
case 0x501: a2dp_shutdown();
break;
case 0x548: if(disp_mode==1) canUpdateBodyData();
break;
case 0x4ec: if(disp_mode==3) canUpdateCoolant(); // temporarily unused, but should work without ECC module
break;
case 0x6c1: canUpdateDisplay();
break;
default: break;
}
}
}
Expand Down Expand Up @@ -129,8 +127,8 @@ void canUpdateDisplay(){
}
if(DIS_autoupdate && disp_mode!=-1){ // don't bother checking the data if there's no need to update the display
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
//Serial.println("Got 6C1 # 10 XX...");
vTaskDelay(pdMS_TO_TICKS(50));
preventDisplayUpdate(); // this hack prevents radio from transmitting the entirety of an Aux message
vTaskDelay(pdMS_TO_TICKS(10));
sendMultiPacket();
}
}
Expand Down Expand Up @@ -208,7 +206,6 @@ void canDecodeEhuButtons(){
}

void canActionEhuButton0(){

}

void canActionEhuButton1(){ // regular audio metadata mode
Expand Down Expand Up @@ -239,9 +236,6 @@ void canActionEhuButton6(){
}

void canActionEhuButton7(){
a2dp_sink.disconnect();
vTaskDelay(pdMS_TO_TICKS(1000));
ESP.restart();
}

void canActionEhuButton8(){
Expand Down
8 changes: 8 additions & 0 deletions src/BusSend.ino
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
char flowCtlMessage[8]={0x30,0,0x7F,0,0,0,0,0}; //

// transmits a CAN message with specified payload; sets CAN_prevTxFail in case there was a failure at any point
void sendPacket(int id, char can_send_buffer[8], int dlc=8){
TxMessage.identifier=id;
Expand Down Expand Up @@ -88,4 +90,10 @@ void requestMeasurementBlocks(){ // request from 0x248, 0x548 respond
if(DEBUGGING_ON) Serial.println("CAN: Requesting measurement blocks from 0x246");
char measurement_payload[8]={0x06, 0xAA, 0x01, 0x01, 0x07, 0x10, 0x11};
sendPacket(0x248, measurement_payload, 7);
}

// this abuses ISO 15765-2 flow control, if sent fast enough will prevent the radio from writing to the display in time and as such will not blank the display; makes the experience more seamless
void preventDisplayUpdate(){
sendPacket(0x2C1, flowCtlMessage, 8);
if(DEBUGGING_ON) Serial.println("CAN: Flow control - preventing display update...");
}
11 changes: 5 additions & 6 deletions src/EHU32.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@
Arduino runs: on core 1
Partition scheme: Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)
*/

#include "AudioTools.h"
#include "BluetoothA2DPSink.h"
#include "driver/twai.h"
#include <WiFi.h>
#include <WiFiAP.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

bool DEBUGGING_ON=0;
// pin definitions
const int PCM_MUTE_CTL=23; // this pin controls PCM5102s soft-mute function
const int PCM_MUTE_CTL=23, SD_CS_PIN=32; // this pin controls PCM5102s soft-mute function
// CAN buffers
uint32_t alerts_triggered;
static twai_message_t RxMessage, TxMessage;
Expand All @@ -28,8 +27,8 @@ float CAN_data_coolant=0, CAN_data_voltage=0;
// global bluetooth flags
bool ehu_started=0, a2dp_started=0, bt_connected=0, bt_state_changed=0, bt_audio_playing=0, audio_state_changed=0;
// data buffers
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];
// display mode 0 -> song metadata, 1 -> body data, 2 -> MP3 player -1 -> prevent screen updates
static char utf16buffer[512], 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];
// display mode 0 -> song metadata, 1 -> body data, -1 -> prevent screen updates
int disp_mode=3;
bool disp_mode_changed=0, disp_mode_changed_with_delay=0;
// time to compare against
Expand Down Expand Up @@ -95,7 +94,7 @@ void loop() {
}
sendMultiPacketData();
} else { // this could use a rewrite
if(DEBUGGING_ON && status_info.msgs_to_rx!=0){Serial.printf("CAN: Got messages %d messages in RX queue!\n", status_info.msgs_to_rx);}
if(DEBUGGING_ON && status_info.msgs_to_rx!=0){Serial.printf("CAN: Got messages %d messages in RX queue!\r\n\b", status_info.msgs_to_rx);}
canReceive(); // read data from RX buffer
}

Expand Down
52 changes: 26 additions & 26 deletions src/TextHandler.ino
Original file line number Diff line number Diff line change
Expand Up @@ -58,43 +58,43 @@ unsigned int utf8_to_utf16(const char* utf8_buffer, char* utf16_buffer){

// 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
int utf8_conversion(char* upper_line_buffer, char* middle_line_buffer, char* lower_line_buffer){
int upper_line_buffer_lenght=0, middle_line_buffer_lenght=0, lower_line_buffer_lenght=0;
if(upper_line_buffer!=nullptr){ // calculating string lenghts to keep track of processed data
upper_line_buffer_lenght=utf8_to_utf16(upper_line_buffer, utf16_album);
int upper_line_buffer_length=0, middle_line_buffer_length=0, lower_line_buffer_length=0;
if(upper_line_buffer!=nullptr){ // calculating string lengths to keep track of processed data
upper_line_buffer_length=utf8_to_utf16(upper_line_buffer, utf16_album);
}
if(middle_line_buffer!=nullptr){
middle_line_buffer_lenght=utf8_to_utf16(middle_line_buffer, utf16_title);
middle_line_buffer_length=utf8_to_utf16(middle_line_buffer, utf16_title);
}
if(lower_line_buffer!=nullptr){
lower_line_buffer_lenght=utf8_to_utf16(lower_line_buffer, utf16_artist);
lower_line_buffer_length=utf8_to_utf16(lower_line_buffer, utf16_artist);
}

if(DEBUGGING_ON){ // debug stuff
Serial.printf("\nTitle lenght: %d", middle_line_buffer_lenght);
Serial.printf("\nAlbum lenght: %d", upper_line_buffer_lenght);
Serial.printf("\nArtist lenght: %d", lower_line_buffer_lenght);
Serial.printf("\nTitle length: %d", middle_line_buffer_length);
Serial.printf("\nAlbum length: %d", upper_line_buffer_length);
Serial.printf("\nArtist length: %d", lower_line_buffer_length);
Serial.println("\nTitle buffer in UTF-8:");
for(int i=0;i<middle_line_buffer_lenght;i++){
for(int i=0;i<middle_line_buffer_length;i++){
Serial.printf(" %02X", middle_line_buffer[i]);
}
Serial.println("\nTitle buffer in UTF-16:");
for(int i=0;i<(middle_line_buffer_lenght*2);i++){
for(int i=0;i<(middle_line_buffer_length*2);i++){
Serial.printf(" %02X", utf16_title[i]);
}
Serial.println("\nAlbum buffer in UTF-8:");
for(int i=0;i<upper_line_buffer_lenght;i++){
for(int i=0;i<upper_line_buffer_length;i++){
Serial.printf(" %02X", upper_line_buffer[i]);
}
Serial.println("\nAlbum buffer in UTF-16:");
for(int i=0;i<(upper_line_buffer_lenght*2);i++){
for(int i=0;i<(upper_line_buffer_length*2);i++){
Serial.printf(" %02X", utf16_album[i]);
}
Serial.println("\nArtist buffer in UTF-8:");
for(int i=0;i<lower_line_buffer_lenght;i++){
for(int i=0;i<lower_line_buffer_length;i++){
Serial.printf(" %02X", lower_line_buffer[i]);
}
Serial.println("\nArtist buffer in UTF-16:");
for(int i=0;i<(lower_line_buffer_lenght*2);i++){
for(int i=0;i<(lower_line_buffer_length*2);i++){
Serial.printf(" %02X", utf16_artist[i]);
}
}
Expand All @@ -114,56 +114,56 @@ int utf8_conversion(char* upper_line_buffer, char* middle_line_buffer, char* low
last_byte_written++;
utf16buffer[last_byte_written]=0x10;
last_byte_written++; // we skip utf16buffer[6], its filled in the end (char count for id 0x10)
if(middle_line_buffer_lenght>1){ // if the upper line data is just a space, don't apply formatting - saves 2 frames of data
if(middle_line_buffer_length>1){ // if the upper line data is just a space, don't apply formatting - saves 2 frames of data
for(int i=1;i<=sizeof(DIS_leftadjusted);i++){ // write left-justified formatting string
utf16buffer[last_byte_written+i]=DIS_leftadjusted[i-1];
}
last_byte_written+=sizeof(DIS_leftadjusted);
utf16buffer[6]=sizeof(DIS_leftadjusted)/2;
}
for(int i=1;i<=(middle_line_buffer_lenght*2);i++){
for(int i=1;i<=(middle_line_buffer_length*2);i++){
utf16buffer[last_byte_written+i]=utf16_title[i-1];
}
last_byte_written+=(middle_line_buffer_lenght*2);
utf16buffer[6]+=middle_line_buffer_lenght; // this is static, char count = title+(formatting/2)
last_byte_written+=(middle_line_buffer_length*2);
utf16buffer[6]+=middle_line_buffer_length; // this is static, char count = title+(formatting/2)

int album_count_pos=10;
// ALBUM FIELD
last_byte_written++;
utf16buffer[last_byte_written]=0x11;
last_byte_written++;
album_count_pos=last_byte_written;
if(upper_line_buffer_lenght>=1){ // if the upper line data is just a space, don't apply formatting - saves 2 frames of data
if(upper_line_buffer_length>=1){ // if the upper line data is just a space, don't apply formatting - saves 2 frames of data
for(int i=1;i<=sizeof(DIS_smallfont);i++){ // formatting - small text
utf16buffer[last_byte_written+i]=DIS_smallfont[i-1];
}
last_byte_written+=sizeof(DIS_smallfont);
utf16buffer[album_count_pos]=sizeof(DIS_smallfont)/2;
}
for(int i=1;i<=(upper_line_buffer_lenght*2);i++){
for(int i=1;i<=(upper_line_buffer_length*2);i++){
utf16buffer[last_byte_written+i]=utf16_album[i-1];
}
last_byte_written+=(upper_line_buffer_lenght*2);
utf16buffer[album_count_pos]+=upper_line_buffer_lenght;
last_byte_written+=(upper_line_buffer_length*2);
utf16buffer[album_count_pos]+=upper_line_buffer_length;

int artist_count_pos=album_count_pos;
// ARTIST FIELD
last_byte_written++;
utf16buffer[last_byte_written]=0x12;
last_byte_written++;
artist_count_pos=last_byte_written;
if(lower_line_buffer_lenght>=1){ // if the upper line data is just a space, don't apply formatting - saves 2 frames of data
if(lower_line_buffer_length>=1){ // if the upper line data is just a space, don't apply formatting - saves 2 frames of data
for(int i=1;i<=sizeof(DIS_smallfont);i++){ // formatting - small text
utf16buffer[last_byte_written+i]=DIS_smallfont[i-1];
}
last_byte_written+=sizeof(DIS_smallfont);
utf16buffer[artist_count_pos]=sizeof(DIS_smallfont)/2;
}
for(int i=1;i<=(lower_line_buffer_lenght*2);i++){
for(int i=1;i<=(lower_line_buffer_length*2);i++){
utf16buffer[last_byte_written+i]=utf16_artist[i-1];
}
last_byte_written+=(lower_line_buffer_lenght*2);
utf16buffer[artist_count_pos]+=lower_line_buffer_lenght;
last_byte_written+=(lower_line_buffer_length*2);
utf16buffer[artist_count_pos]+=lower_line_buffer_length;

if((last_byte_written+1)%7==0){ // if the amount of bytes were to result in a full packet (ie no unused bytes), add a char to overflow into the next packet
utf16buffer[artist_count_pos]+=1; // workaround because if the packets are full the display would ignore the message
Expand Down

0 comments on commit 091b468

Please sign in to comment.