Tesla Wireless Trailer Light Controller, looking for input

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);
  }
}
1 Like

Forget it.
If your intention is to use it on any public road, it becomes a safety issue.
Arduino are not in this catagory.

Putting aside the added complexity for something that's really a very simple task (sorry, had to get that out of my system), the question is why do you want to avoid relays? Because they're "mechanical" would be a good answer if the reason is aesthetics, I guess.

Concerned about reliability? Get better quality relays that actually provide a lifetime spec.
Power draw? Come on, we're talking lights on a car here. Relay power is miniscule.
Water ingress? Seal them better.

OK, so assuming none of those addresses the concern, the next solution is replacing the relays with a transistor of some sort. Here is a 4-channel array of bipolar transistors with 3A max load. I'm guessing a trailer light is around 10W (too lazy to get up and grab one) so that will give you a fair amount of overhead.
One benefit of relays is that they have a very low voltage loss. The transistor array I selected has a 1V saturation voltage so you'll be applying the battery voltage - 1V to the lights. A relay would have a few tenths of a volt max.

Now, looking at arrays of FETs, you'll find much lower saturation voltages at the same currents.
Here's an example providing 5A @ about 0.2V saturation and it can be driven directly from a digital pin.

In your situation, I'd probably either just use the relay board, or remove the relays and replace them with a good quality Omron or similar. But it's your project :slight_smile:

This isn't being driven on public roads. This is purely for testing purposes and to serve as a proof of concept. I didn't say in the initial post but my hope was to build something to add brake lights and signals into my mountainbike carrier that currently semi obstructs the view of my taillights.

Thank you for your insight, I was not worried about the power draw of the relays themselves. I was looking for fast activation, reliable, quiet solutions. I didn't mention noise initially but it was a concern. I can make this work using a mosfet n-channel board but would require I run power and ground from each light to the board. I want to share a common ground between all the lights so I can use the standard quick 4wire trailer connectors. I think going with p-channel mosfets could work but I'm not sure how one would wire that.

MOS FET PWM 3-20V to 3.7-27VDC 10A 4-Channel Driver Module

Are you tapping into the CAN twisted pair to keep from having to connect a wiring harness to the taillights?
Where are you placing the relay module that you can hear it clicking from the driver seat?

1 Like

The term you're searching for is High-Side Drive.

If you're looking for arduino-type modules, I think your best bet might be to find some half-bridge or H-bridge motor drivers. Something like an L298 driver where you can control each half bridge. That lets you keep one side of the lights grounded and switch the +12VDC. Should be able to control 4 lights from each module this way.

Hi,
Google;

automotive high side switch

There are many high side MOSFETs designed for automotive applications that keep the hardware to a minimum.

Tom.... :smiley: :+1: :coffee: :australia:

You also need to look at what is currently used . A CAN type trailer module is usually available for most cars ( prob not fir Tesla) and is also often used to check indicator operation ( and feedback to dash ) and do things like change traction control and stability settings .
For a TESLA it is very unlikely Tesla will support any add on you make and connecting to the bus may throw errors ( Tesla are very protective about who can play with their cars ) .
There are a few things to research here .
BTW quality relays ( not the blue ones …) are very reliable , you will find lots of them in modern cars .

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.