#include #include #include #include #include "esp_log.h" #include CRGB led[1]; static SemaphoreHandle_t mutex; // Task handles TaskHandle_t xCANSendTaskHandle = NULL; TaskHandle_t xSerialConsoleTaskHandle = NULL; // Task functions void vCANSendTask(void *pvParameters); void vSerialConsoleTask(void *pvParameters); // Define CAN frame IDs and data struct CANFrame { unsigned long id; byte length; byte data[8]; }; CANFrame frames[] = { {0x418, 8, {0x30, 0x07, 0x00, 0x03, 0x28, 0x28, 0xF8, 0xA0}}, {0x46B, 8, {0x01, 0x00, 0xA3, 0x00, 0x01, 0x00, 0x00, 0xC0}}, {0x47C, 8, {0x64, 0x7D, 0x42, 0x01, 0x91, 0x00, 0x70, 0x04}}, {0x47D, 8, {0x04, 0xC1, 0x77, 0x95, 0x60, 0x01, 0x20, 0xC4}}, {0x47F, 8, {0x04, 0x0C, 0xFA, 0x00, 0xEA, 0x72, 0x44, 0x00}}, {0x25B, 6, {0x04, 0x46, 0x00, 0xFF, 0xFF, 0xC0}}, {0x69F, 4, {0x75, 0x01, 0x33, 0x6F}}}; // Easylink anti-theft protecion, last 7 number of VIN in reverse order + last byte F. // Define byte indices for CAN frame modification #define STANDBY_BYTE_INDEX 0 #define MUTE_BYTE_INDEX 2 #define REVERSE_BYTE_INDEX 0 #define BRIGHTNESS_BYTE_INDEX 1 #define BRIGHTNESS_LEVEL_BYTE_INDEX 2 #define VOLUME_BYTE_INDEX 4 // Define GPIO pins #define IGNITION_GPIO_PIN 6 #define REVERSE_GPIO_PIN 7 #define DEBOUNCE_DELAY_IGNITION 1000 #define DEBOUNCE_DELAY_OTHERS 100 #define STANDBY_DURATION 3600000 // 60 minutes in milliseconds // Define initial states for variables bool ignitionState = false; bool reverseState = false; bool brightnessState = false; // false: day, true: night bool muteState = false; bool standbyState = false; bool volumeState = false; // false: normal, true: lowered int brightnessLevel = 50; // Default brightness level (0-100) bool gpioEnabled = false; unsigned long lastIgnitionTime = 0; unsigned long lastReverseTime = 0; bool gpioIgnitionState = false; // Tracks the state of GPIO ignition unsigned long standbyStartTime = 0; // Tracks the start time of standby mode // Internal variables bool serial_init = false; bool can_enabled = false; bool led_status = false; int serial_ping = 0; // Function to initialize CAN bus void initCAN() { while (!CAN.begin(500E3)) { ESP_LOGE("CAN Interface", "Initialization failed, retrying..."); vTaskDelay(100); } ESP_LOGI("CAN Interface", "Initialization succeeded!"); can_enabled = true; } void stopCAN() { CAN.end(); ESP_LOGI("CAN Interface", "Shutdown succeeded!"); can_enabled = false; } // Function to send a CAN message void sendCANFrame(unsigned long canId, const byte *data, int len) { ESP_LOGD("CAN Interface", "TX ID 0x%02X DATA %02X", canId, data); CAN.beginPacket(canId); CAN.write(data, len); CAN.endPacket(); ESP_LOGD("CAN Interface", "TX successful"); } // Function to update CAN frame data based on variables void updateCANFrameData(CANFrame *frames) { frames[4].data[BRIGHTNESS_BYTE_INDEX] = brightnessState ? 0x8C : 0x0C; frames[4].data[VOLUME_BYTE_INDEX] = volumeState ? 0xEE : 0xEA; frames[4].data[BRIGHTNESS_LEVEL_BYTE_INDEX] = map(brightnessLevel, 0, 100, 0x00, 0xFA); frames[5].data[REVERSE_BYTE_INDEX] = reverseState ? 0x08 : 0x04; frames[3].data[MUTE_BYTE_INDEX] = muteState ? 0x00 : 0x77; frames[1].data[STANDBY_BYTE_INDEX] = standbyState ? 0x00 : 0x01; ESP_LOGD("CAN", "Frame update successful"); } // Function to handle GPIO inputs void handleGPIO() { // If GPIO is disabled, return without processing GPIO inputs if (!gpioEnabled) { return; } unsigned long currentMillis = millis(); // Ignition input if (currentMillis - lastIgnitionTime >= DEBOUNCE_DELAY_IGNITION) { bool newIgnitionState = digitalRead(IGNITION_GPIO_PIN); if (newIgnitionState != ignitionState) { if (!newIgnitionState) { ESP_LOGI("GPIO State machine", "Ignition turned off"); standbyState = true; ESP_LOGI("State machine", "Ignition set to standby mode"); } else { ESP_LOGI("GPIO State machine", "Ignition turned on"); ignitionState = true; // Initialize CAN interface if ignition is on if (ignitionState) { initCAN(); } } } } // Handle reverse input if (gpioEnabled && currentMillis - lastReverseTime >= DEBOUNCE_DELAY_OTHERS) { bool newReverseState = digitalRead(REVERSE_GPIO_PIN); if (newReverseState != reverseState) { if (newReverseState) { // Reverse engaged ESP_LOGI("GPIO State machine", "Reversing engaged"); reverseState = true; volumeState = true; } else { // Reverse disengaged ESP_LOGI("GPIO State machine", "Reversing disengaged"); reverseState = false; volumeState = false; } } } } // Function to print summary of all serial variables void printVariableSummary() { Serial.println("---CAN status summary----"); Serial.println("-------------------------"); Serial.print("Ignition: "); Serial.println(ignitionState ? "on" : "off"); Serial.print("Standby: "); Serial.println(standbyState ? "yes" : "no"); Serial.print("Reverse: "); Serial.println(reverseState ? "on" : "off"); Serial.print("Brightness: "); Serial.println(brightnessState ? "night" : "day"); Serial.print("Mute: "); Serial.println(muteState ? "on" : "off"); Serial.print("Volume: "); Serial.println(volumeState ? "lowered" : "normal"); Serial.print("Brightness Level: "); Serial.println(brightnessLevel); Serial.print("GPIO: "); Serial.println(gpioEnabled ? "enable" : "disable"); Serial.println("-------------------------"); } void vCANSendTask(void *pvParameters) { (void)pvParameters; while (1) { handleGPIO(); // Send CAN frames at 10Hz if ignition is on if (ignitionState || gpioIgnitionState) { unsigned long currentMillis = millis(); static unsigned long lastTxTime = 0; if (currentMillis - lastTxTime >= 100) { // Update CAN frame data based on variables updateCANFrameData(frames); // Send all CAN frames int i = 0; if (standbyState) { i = 1; } else { i = 0; } for (i; i < 6; i++) { sendCANFrame(frames[i].id, frames[i].data, frames[i].length); } lastTxTime = currentMillis; } // Send CAN frame at 1Hz static unsigned long lastTxTime69F = 0; if (currentMillis - lastTxTime69F >= 1000) { sendCANFrame(frames[6].id, frames[6].data, frames[6].length); // Send anti-theft frame lastTxTime69F = currentMillis; } } // Check if ignition should go into standby mode if (!gpioIgnitionState && ignitionState && !standbyStartTime && standbyState) { standbyStartTime = millis(); // Start the standby timer ESP_LOGI("State machine", "Start standby timer"); } // Check if standby duration has elapsed and gpio ignition is still false if (standbyStartTime && millis() - standbyStartTime >= STANDBY_DURATION && !gpioIgnitionState) { ignitionState = false; // Set ignition to off after standby duration ESP_LOGI("State machine", "Ignition turned off after standby duration"); standbyState = false; standbyStartTime = 0; // Reset standby timer } // If GPIO ignition is true, reset standby timer if (gpioIgnitionState && standbyState) { standbyStartTime = 0; // Reset standby timer standbyState = false; ESP_LOGI("State machine", "Ignition standby timer disabled"); } vTaskDelay(10); } } void vSerialConsoleTask(void *pvParameters) { (void)pvParameters; while (1) { if (Serial.available() > 0) { String input = Serial.readStringUntil('\n'); input.trim(); if (input.length() == 0) { printVariableSummary(); } else { // Parse input commands if (input.startsWith("ignition")) { if (input.endsWith("on")) { ignitionState = true; ESP_LOGI("State machine", "Ignition turned on"); standbyState = false; if (!can_enabled) { ESP_LOGD("State machine", "Try to enable CAN Interface"); initCAN(); } if (standbyStartTime != 0) { standbyStartTime = 0; // Reset standby timer ESP_LOGI("State machine", " Ignition standby timer disabled"); } } else if (input.endsWith("off")) { ignitionState = false; standbyStartTime = millis(); ESP_LOGI("State machine", "Ignition turned off"); if (can_enabled) { ESP_LOGD("State machine", "Try to disable CAN Interface"); stopCAN(); } } else if (input.endsWith("standby")) { // Print current standby duration if ignition is in standby mode if (standbyStartTime) { unsigned long remainingTime = STANDBY_DURATION - (millis() - standbyStartTime); standbyState = true; ESP_LOGI("State machine", "Ignition in standby mode, remaining time: %d minutes", remainingTime / 60000); } else { standbyStartTime = millis(); ESP_LOGI("State machine", "Ignition set to standby mode, retype command to see duration left"); if (!can_enabled) { ESP_LOGD("State machine", "Try to enable CAN Interface"); initCAN(); } standbyState = true; ignitionState = true; } } } else if (input.startsWith("gpio")) { if (input.endsWith("enable")) { gpioEnabled = true; ESP_LOGI("Variables", "GPIO enabled"); } else if (input.endsWith("disable")) { gpioEnabled = false; ESP_LOGI("Variables", "GPIO disabled"); } } else if (input.startsWith("mute")) { if (input.endsWith("on")) { muteState = true; ESP_LOGI("Variables", "MUTE turned on"); } else if (input.endsWith("off")) { muteState = false; ESP_LOGI("Variables", "MUTE turned off"); } } else if (input.startsWith("reverse")) { if (input.endsWith("on")) { reverseState = true; ESP_LOGI("Variables", "REVERSE turned on"); } else if (input.endsWith("off")) { reverseState = false; ESP_LOGI("Variables", "REVERSE turned off"); } } else if (input.startsWith("brightness")) { if (input.endsWith("day")) { brightnessState = false; ESP_LOGI("Variables", "Brightness set to DAY"); } else if (input.endsWith("night")) { brightnessState = true; ESP_LOGI("Variables", "Brightness set to NIGHT"); } } else if (input.startsWith("volume")) { if (input.endsWith("normal")) { volumeState = false; ESP_LOGI("Variables", "VOLUME set to NORMAL"); } else if (input.endsWith("lowered")) { volumeState = true; ESP_LOGI("Variables", "VOLUME set to LOWERED"); } } else { ESP_LOGE("Variables", "Invalid command received"); } } } vTaskDelay(50); if (millis() - serial_ping >= 10000) { serial_ping += 10000; ESP_LOGI("Serial Console", "Ping, Time %d s", millis() / 1000); } unsigned long previousMillis = 0; long interval = 1000; if (standbyState) { led[0] = CRGB::Yellow; interval = 500; } else if (ignitionState) { led[0] = CRGB::Green; interval = 250; } else if (!ignitionState) { led[0] = CRGB::Red; interval = 1000; } unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; if (led_status) { FastLED.setBrightness(25); led_status = false; } else { FastLED.setBrightness(25); led_status = true; } FastLED.show(); } } } void setup() { Serial.begin(115200); Serial.setTimeout(5000); ESP_LOGI("SYS", "Easylink CAN Waker application starting up..."); //TCAN Enable CAN Interface pinMode(16, OUTPUT); digitalWrite(16, HIGH); pinMode(23, OUTPUT); digitalWrite(23, LOW); CAN.setPins(26, 27); mutex = xSemaphoreCreateMutex(); // Print state of variables during bootup ESP_LOGD("SYS", "Print actual variable states"); printVariableSummary(); FastLED.addLeds(led, 1); ESP_LOGD("SYS", "Init WS2812 LED"); xTaskCreatePinnedToCore(vCANSendTask, "CANSendTask", 8096, NULL, 10, &xCANSendTaskHandle, 0); ESP_LOGD("SYS", "Started CANSendTask"); xTaskCreatePinnedToCore(vSerialConsoleTask, "SerialConsoleTask", 8096, NULL, 10, &xSerialConsoleTaskHandle, 1); ESP_LOGD("SYS", "Started SerialConsoleTask"); } void loop() { // We are using tasks instead }