Hey everyone, I've been building a prototype wireless taillight controller for my Tesla. The goal is to feed CAN data from the vehicle into an ESP32(CANServer) in the car and then a secondary ESP32 would be installed onto the trailer and communicate over Wi-Fi using websockets to trigger the outputs. (Brake, Turn, Running lights). See the attached picture of current wiring.
Currently this works in practice but I want to tidy up the electrical wiring and could use extra eyes on my code, I would like to move away from relays as they are mechanical. I am asking for advice on best practices to make something fast acting and reliable.
Disclaimer: this is purely for educational purposes and is not used for actual hauling. (Off-road use only)
Client Code:
#include <WiFi.h>
#include <WebSocketsClient.h>
#include <ArduinoJson.h>
const char* ssid = "Server";
const char* password = "RandomPassword";
const int LEFT_OUTPUT_PIN = 27;
const int LEFT_BLINK_INTERVAL_MS = 380;
const int RIGHT_OUTPUT_PIN = 26;
const int RIGHT_BLINK_INTERVAL_MS = 380;
const int RUNNING_OUTPUT_PIN = 25;
unsigned long _LeftLastToggledTimestampMs = 0;
unsigned long _RightLastToggledTimestampMs = 0;
int _LeftState = LOW;
int _RightState = LOW;
StaticJsonDocument<200> doc;
int leftIndicatorStatus = 0;
int rightIndicatorStatus = 0;
int brakeLightStatus = 0;
int runningLightStatus = 0;
WebSocketsClient webSocket;
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch (type) {
case WStype_DISCONNECTED:
Serial.printf("Disconnected from WebSocket\n");
break;
case WStype_CONNECTED:
Serial.printf("Connected to WebSocket\n");
webSocket.sendTXT("{\"t\":\"sub\",\"p\":{\"i\":\"VCFRONT_indicatorLef\"}}");
webSocket.sendTXT("{\"t\":\"sub\",\"p\":{\"i\":\"VCFRONT_indicatorRig\"}}");
webSocket.sendTXT("{\"t\":\"sub\",\"p\":{\"i\":\"ID3E2VCLEFT_lightSta\"}}");
webSocket.sendTXT("{\"t\":\"sub\",\"p\":{\"i\":\"VCRIGHT_tailLightSta\"}}");
digitalWrite(25, HIGH); // Visual warning that the ESP32 is connected to ws
digitalWrite(26, HIGH);
digitalWrite(27, HIGH);
delay(250);
digitalWrite(25, LOW);
digitalWrite(26, LOW);
digitalWrite(27, LOW);
delay(250);
digitalWrite(25, HIGH);
digitalWrite(26, HIGH);
digitalWrite(27, HIGH);
delay(250);
digitalWrite(25, LOW);
digitalWrite(26, LOW);
digitalWrite(27, LOW);
break;
case WStype_TEXT:
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
const char* p_i = doc["p"]["i"]; // "<AnalysisItemName>"
int p_v = doc["p"]["v"]; // <value>
Serial.print(p_i);
Serial.print(" ");
Serial.println(p_v);
if (String(p_i) == "VCFRONT_indicatorLef") {
leftIndicatorStatus = p_v;
}
if (String(p_i) == "VCFRONT_indicatorRig") {
rightIndicatorStatus = p_v;
}
if (String(p_i) == "ID3E2VCLEFT_lightSta") {
brakeLightStatus = p_v;
}
if (String(p_i) == "VCRIGHT_tailLightSta") {
runningLightStatus = p_v;
}
break;
}
}
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println();
pinMode(LEFT_OUTPUT_PIN, OUTPUT);
pinMode(RIGHT_OUTPUT_PIN, OUTPUT);
pinMode(RUNNING_OUTPUT_PIN, OUTPUT);
for (uint8_t t = 3; t > 0; t--) {
Serial.printf("BOOTING %d...\n", t);
Serial.flush();
delay(500);
}
WiFi.begin(ssid, password);
while ( WiFi.status() != WL_CONNECTED ) {
delay(250);
}
Serial.println("Wifi Connected");
Serial.println(WiFi.localIP());
webSocket.begin("192.168.4.1", 80, "/ws");
webSocket.onEvent(webSocketEvent);
webSocket.setReconnectInterval(500);
}
void loop() {
webSocket.loop();
unsigned long currentTimestampMs = millis();
bool trailerLeftLightOutput = false;
bool trailerRightLightOutput = false;
bool brakeLeftLightOutput = false;
bool brakeRightLightOutput = false;
if (leftIndicatorStatus != 0)
{
trailerLeftLightOutput = true;
}
if (rightIndicatorStatus != 0)
{
trailerRightLightOutput = true;
}
if (brakeLightStatus == 1)
{
if (leftIndicatorStatus == 0)
{
brakeLeftLightOutput = true;
}
if (rightIndicatorStatus == 0)
{
brakeRightLightOutput = true;
}
}
if (trailerLeftLightOutput)
{
if (currentTimestampMs - _LeftLastToggledTimestampMs >= LEFT_BLINK_INTERVAL_MS) {
_LeftLastToggledTimestampMs = millis();
_LeftState = !_LeftState;
digitalWrite(LEFT_OUTPUT_PIN, _LeftState);
}
}
else
{
digitalWrite(LEFT_OUTPUT_PIN, LOW);
}
if (trailerRightLightOutput)
{
if (currentTimestampMs - _RightLastToggledTimestampMs >= RIGHT_BLINK_INTERVAL_MS) {
_RightLastToggledTimestampMs = millis();
_RightState = !_RightState;
digitalWrite(RIGHT_OUTPUT_PIN, _RightState);
}
}
else
{
digitalWrite(RIGHT_OUTPUT_PIN, LOW);
}
if (brakeLeftLightOutput)
{
digitalWrite(LEFT_OUTPUT_PIN, HIGH);
}
else if (leftIndicatorStatus != 0) {
// do nothing
}
else
{
digitalWrite(LEFT_OUTPUT_PIN, LOW);
}
if (brakeRightLightOutput)
{
digitalWrite(RIGHT_OUTPUT_PIN, HIGH);
}
else if (rightIndicatorStatus != 0) {
// do nothing
}
else
{
digitalWrite(RIGHT_OUTPUT_PIN, LOW);
}
if (runningLightStatus == 1)
{
digitalWrite(RUNNING_OUTPUT_PIN, HIGH);
}
else
{
digitalWrite(RUNNING_OUTPUT_PIN, LOW);
}
}
