///////////////////////////////////////////////////////////////// /* ESP32 | Walkie-Talkie, Half-duplex communication based on WebSocket Video Tutorial: https://youtu.be/kw30vLdrGE8 Created by Eric Nam (ThatProject) */ ///////////////////////////////////////////////////////////////// #include #include #include #include "Free_Fonts.h" #include "driver/i2s.h" #include "Button2.h"; #include "I2s_Setting.h" #include "Const.h" #define RIGHT_BUTTON_PIN 35 Button2 rButton = Button2(RIGHT_BUTTON_PIN); using namespace websockets; WebsocketsClient client; TFT_eSPI tft = TFT_eSPI(); TFT_eSprite logScreen = TFT_eSprite(&tft); unsigned long timeStampOffset = 0; unsigned long requestTimestamp; TaskHandle_t i2sReadTaskHandler = NULL; void setup() { Serial.begin(115200); display_init(); display_ready_screen(); button_init(); i2s_buff_init(); start_to_connect(); xTaskCreate(ping_task, "ping_task", 2048, NULL, 1, NULL); } void loop() { rButton.loop(); if (client.available()) { client.poll(); } } void onEventsCallback(WebsocketsEvent event, String data) { if(event == WebsocketsEvent::ConnectionClosed) { ESP.restart(); } } void onMessageCallback(WebsocketsMessage message) { int msgLength = message.length(); if(message.type() == MessageType::Binary){ if(states == Listening && msgLength > 0){ i2s_write_data((char*)message.c_str(), msgLength); } }else if(message.type() == MessageType::Text){ unsigned long timeResponse = message.data().toInt(); actionCommand(timeResponse); } } void actionCommand(unsigned long timestamp){ if(timeStampOffset == 0){ unsigned long currentTimestamp = millis(); timeStampOffset = currentTimestamp >= timestamp? currentTimestamp - timestamp : timestamp - currentTimestamp; return; } if(requestTimestamp == timestamp && states == Idle){ states = Speaking; print_log_screen("* Speaking Mode *"); i2s_RX_init(); xTaskCreate(i2s_read_task, "i2s_read_task", 4096, NULL, 1, &i2sReadTaskHandler); }else if(requestTimestamp != timestamp ){ if(states == Idle){ states = Listening; i2s_TX_init(); print_log_screen("* Listening Mode *"); }else if(states == Listening){ states = Idle; i2s_TX_uninst(); print_log_screen("* IDLE Mode *"); } } } void start_to_connect(){ WiFi.begin(ssid, password); print_log_screen("- WiFi Connecting"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } print_log_screen("+ WiFi Connected"); print_log_screen("- Socket Connecting"); client.onMessage(onMessageCallback); client.onEvent(onEventsCallback); while(!client.connect(websockets_server_host, websockets_server_port, "/")){ delay(500); Serial.print("."); } print_log_screen("+ Socket Connected"); } static void i2s_read_task(void *arg){ while (1) { i2s_read_data(); client.sendBinary((const char*)flash_write_buff, I2S_READ_LEN); } } static void ping_task(void *arg){ char dummy_buffer [1]; while (1) { if(states == Idle){ client.sendBinary(dummy_buffer, 1); } vTaskDelay(1000); } } void pressed(Button2& btn) { if(btn == rButton && states == Idle){ client.send(getStrTimestamp()); } } void released(Button2& btn) { if(btn == rButton && states == Speaking){ states = Idle; print_log_screen("* IDLE Mode *"); delay(100); if( i2sReadTaskHandler != NULL ){ vTaskDelete( i2sReadTaskHandler ); i2sReadTaskHandler = NULL; } delay(100); client.send(getStrTimestamp()); i2s_RX_uninst(); } } String getStrTimestamp(){ requestTimestamp = millis() + timeStampOffset; return (String)requestTimestamp; } void button_init(){ rButton.setPressedHandler(pressed); rButton.setReleasedHandler(released); } void display_init(){ tft.begin(); tft.setRotation(0); tft.setTextColor(TFT_WHITE,TFT_BLACK); tft.fillScreen(TFT_BLACK); logScreen.setColorDepth(8); logScreen.createSprite(tft.width(), tft.height()/2); logScreen.fillSprite(TFT_BLUE); logScreen.setScrollRect(0, 0, tft.width(), tft.height()/2, TFT_BLUE); logScreen.setTextColor(TFT_WHITE); logScreen.setTextDatum(TL_DATUM); } void display_ready_screen(){ tft.fillScreen(TFT_BLACK); tft.setTextDatum(TL_DATUM); tft.setFreeFont(FF21); tft.setTextColor(TFT_SKYBLUE, TFT_BLACK); tft.drawString("ESP32-ESP32", 0, 10, GFXFF); tft.setFreeFont(FF23); tft.setTextColor(TFT_SILVER, TFT_BLACK); tft.drawString("Walkie", 0, 35, GFXFF); tft.setTextColor(TFT_GOLD, TFT_BLACK); tft.drawString("Talkie", 30, 60, GFXFF); tft.setTextDatum(MC_DATUM); tft.setFreeFont(FF21); tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.drawString(deviceRole == Server ? "[Server Ready]" : "[Client Ready]", tft.width()/2, 100, GFXFF); } void print_log_screen(String text){ logScreen.drawString(text, 0, -1, 2); logScreen.pushSprite(0, tft.height()/2); logScreen.scroll(0,16); }