### Install Dependencies and Run Dev Server Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/Contributing.md Navigate to the docs directory, install dependencies, and start the local development server to preview documentation changes. ```bash cd docs npm i npm run dev ``` -------------------------------- ### Params.json Entry Example Source: https://github.com/meatpihq/wican-fw/blob/main/vehicle_profiles/README.md Example of a parameter entry in the params.json file. This provides a human-readable description and detailed settings, including unit, class, min/max values, and type, for a specific parameter like 'SOC_BMS'. ```json "SOC_BMS": { "description": "State of charge according to the BMS.", "settings": { "unit": "%", "class": "battery", "min": "0", "max": "100" } } ``` -------------------------------- ### Odometer Data Log Example Source: https://github.com/meatpihq/wican-fw/blob/main/vehicle_profiles/volvo/README.md Example log data from an ELM327 showing PID and odometer readings for a Volvo XC40. ```log PID: 22DD011 @ 30733km > 1EC6EE80 0662DD01 00780D ``` ```log PID: 22DD011 @ 30776km > 1EC6EE80 0662DD01 007838 ``` -------------------------------- ### WireGuard Client Configuration File Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/8.VPN/0.WireGuard.md A typical WireGuard client configuration file format. Ensure all sensitive keys and details are correctly filled in. ```ini [Interface] PrivateKey = Address = 10.10.0.10/32 DNS = 10.10.0.1 [Peer] PublicKey = PresharedKey = AllowedIPs = 10.10.0.0/24 Endpoint = vpn.example.com:51820 PersistentKeepalive = 25 ``` -------------------------------- ### Temperature Conversion Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/0.Config/6.Automate/1.Usage.md Convert raw byte data to Celsius using a common offset. Subtract 40 from the first byte to get engine temperature. ```Automate Expression B0 - 40 ``` -------------------------------- ### Percentage Calculation Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/0.Config/6.Automate/1.Usage.md Convert an 8-bit value to a percentage by scaling it relative to the maximum possible value (255). ```Automate Expression B0 * 100 / 255 ``` -------------------------------- ### Scaled 16-bit Value Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/0.Config/6.Automate/1.Usage.md Extract a 16-bit value and apply a scaling factor by dividing it. This example divides by 4. ```Automate Expression [B0:B1] / 4 ``` -------------------------------- ### Scaled 8-bit Value Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/0.Config/6.Automate/1.Usage.md Extract an 8-bit value and apply a scaling factor by multiplying it. This example multiplies by 0.5. ```Automate Expression B0 * 0.5 ``` -------------------------------- ### Initial POST Payload Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/9.HTTPS/0.HTTPS.md This JSON payload is sent once per boot cycle on the first successful POST to an enabled HTTPS destination. It includes configuration, status, and an initial telemetry sample. ```json { "autopid_data": { "TestCustom": 127.5 }, "config": { "0C-EngineRPM": { "class": "speed", "unit": "rpm" }, "0D-VehicleSpeed": { "class": "speed", "unit": "km/h" }, "COOLANT_TMP": { "class": "temperature", "unit": "°C" }, "ENGINE_RPM": { "class": "frequency", "unit": "RPM" }, "FUEL": { "class": "none", "unit": "%" }, "FUEL_PRESSURE": { "class": "pressure", "unit": "kPa" }, "INTAKE_AIR_TMP": { "class": "temperature", "unit": "°C" }, "MAF": { "class": "none", "unit": "g/s" }, "ODOMETER": { "class": "distance", "unit": "km" }, "SPEED": { "class": "speed", "unit": "km/h" }, "STFT": { "class": "none", "unit": "%" }, "THROTTLE": { "class": "none", "unit": "%" }, "TestCustom": { "class": "none", "unit": "none" } }, "status": { "device_id": "device_id", "fw_version": "4.40", "git_version": "v4.40p", "hw_version": "WiCAN-OBD-PRO", "ecu_status": "online", "sta_status": "Connected", "sta_ip": "192.168.0.146", "wifi_mode": "BLEStation" /* many additional fields (network creds, tokens, topics, etc.) omitted for brevity */ } } ``` -------------------------------- ### Receive CAN Frames (RX) Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/7.MQTT/Usage.md Subscribe to this topic to receive CAN frames transmitted by the WiCAN device. A single MQTT message may contain multiple CAN frames. ```json {"bus":0,"type":"rx","ts":34610,"frame":[{"id":123,"dlc":8,"rtr":false,"extd":false,"data":[1,2,3,4,5,6,7,8]},{"id":124,"dlc":8,"rtr":false,"extd":true,"data":[1,2,3,4,5,6,7,8]}]} ``` -------------------------------- ### Setup SocketCAN over Wi-Fi Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/6.SocketCAN/Usage.md Use this snippet to create a virtual serial port over TCP for Wi-Fi connections. Ensure the IP address matches your device's IP on the network. The commands set up socat, slcand, and configure the can0 network interface. ```bash sudo socat pty,link=/dev/netcan0,raw tcp:192.168.80.1:3333 & sudo slcand -o -c -s8 /dev/netcan0 can0 sudo ifconfig can0 txqueuelen 1000 sudo ifconfig can0 up ``` -------------------------------- ### Generic HTTPS POST Request Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/9.HTTPS/0.HTTPS.md This example shows a simplified HTTP POST request to a collector endpoint using a Bearer Token for authentication. Ensure your content type and payload match the server's expectations. ```http POST /iot/telemetry HTTP/1.1 Host: collector.example.com Authorization: Bearer MY_SUPER_TOKEN User-Agent: ESP32 WiCAN/1.0 Content-Type: application/json {"device_id":"...","ts":1730000000,"can":[...],"metrics":{...}} ``` -------------------------------- ### Request CAN Frames (TX) Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/7.MQTT/Usage.md Use this payload to request CAN frames to be transmitted by the WiCAN device. Ensure the topic is correctly formatted with the device ID. ```json { "bus": "0", "type": "tx", "frame": [{ "id": 2015, "dlc": 8, "rtr": false, "extd": false, "data": [2, 1, 70, 170, 170, 170, 170, 170] }] } ``` -------------------------------- ### Initialize WebSocket and Click Default Tab Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage.html Initializes a WebSocket connection and programmatically clicks the element with ID 'defaultOpen'. This is likely used to set the default view or start communication. ```javascript var canData=[];document.getElementById("defaultOpen").click();var ws=null;var cr=String.fromCharCode(13) ``` -------------------------------- ### Monitor Control and WebSocket Connection Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage.html Manages the start and stop of a monitoring session using WebSockets. It configures the WebSocket URL, sends initial commands, and handles connection events. ```javascript var mon_control=(()=>{var a=document.getElementById(`mon_datarate`).selectedIndex;var b=window.location.href;var c=`http://192.168.31.72/`;console.log(a);alert(`Please reboot device after Monitor, otherwise the device may not function as expected`);ws_url=`ws://`+ b.substr(7,b.length)+ `ws`;console.log(ws_url);if(document.getElementById(`mon_button`).value==`Start`){ws=new WebSocket(ws_url);setTimeout(bindEvents,1000);ws.addEventListener(`open`,b=>{var c=`C`+ cr+ `S`+ a+ cr+ `O`+ cr;console.log(`onopen called`);ws.send(c);window.mon_button_en(0)});ws.addEventListener(`close`,a=>{window.mon_button_en(1);console.log(`onclose called`)})}else{ws_close()}}); ``` -------------------------------- ### Setup SocketCAN over USB Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/6.SocketCAN/Usage.md This snippet configures SocketCAN for a direct USB connection. It uses slcand to create the can0 interface from the USB serial device and then brings the interface up with a specified transmit queue length. ```bash sudo slcand -o -s6 -t sw -S 4000000 /dev/ttyACM0 can0 sudo ifconfig can0 txqueuelen 1000 sudo ifconfig can0 up ``` -------------------------------- ### Subsequent Telemetry POST Payload Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/9.HTTPS/0.HTTPS.md This JSON payload is sent periodically or on trigger events after the initial boot payload. It contains only the telemetry data within the 'autopid_data' field. ```json { "autopid_data": { "0C-EngineRPM": 4147, "0D-VehicleSpeed": 0, "COOLANT_TMP": 155, "ENGINE_RPM": 4147, "FUEL": 62.35, "FUEL_PRESSURE": 300, "INTAKE_AIR_TMP": -40, "MAF": 34.81, "SPEED": 0, "STFT": -100, "THROTTLE": 33.73, "TestCustom": 127.5 } } ``` -------------------------------- ### Post Configuration Settings Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage.html Asynchronously posts configuration settings to the server after a delay. It initializes an object `a` and then awaits a promise that resolves after 1 second. The subsequent code for fetching and processing settings is truncated. ```javascript var postConfig=(async()=>{let l=`can_datarate`,t=`batt_alert`,H=`mqtt_port`,A=`batt_alert_port`,z=`batt_alert_url`,G=`mqtt_url`,Q=`mqtt_rx_topic`,m=`can_mode`,q=`sleep_status`,L=`mqtt_tx_topic`,o=`protocol`,F=`mqtt_en`,K=`keep_alive`,M=`ap_auto_disable`,E=`batt_mqtt_pass`,j=`webhook_en`,n=`port_type`,O=`enable`,s=`sleep_time`,v=`batt_alert_pass`,P=`disable`,R=`mqtt_rx_en`,I=`mqtt_user`,x=`batt_alert_protocol`,T=`mqtt_elm327_log`,r=`sleep_volt`,y=`mqtt://`,C=`batt_alert_time`,J=`mqtt_pass`,u=`batt_alert_ssid`,w=`batt_alert_volt`,k=`sta_security`,S=`mqtt_status_topic`,B=`batt_alert_topic`,D=`batt_mqtt_user`,N=`mqtt_tx_en`,p=`ble_status`,i=`wifi_mode`;var a={};await new Promise(a=>setTimeout(a,1000));const b=await ``` -------------------------------- ### Later Telemetry POST Payload Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/9.HTTPS/0.HTTPS.md An example of a subsequent telemetry-only POST, demonstrating that values within 'autopid_data' can change over time or per cycle. ```json { "autopid_data": { "0C-EngineRPM": 4200, "0D-VehicleSpeed": 5, "COOLANT_TMP": 156, "ENGINE_RPM": 4200, "FUEL": 62.10, "FUEL_PRESSURE": 300, "INTAKE_AIR_TMP": -39, "MAF": 35.10, "SPEED": 5, "STFT": -98, "THROTTLE": 34.00, "TestCustom": 127.5 } } ``` -------------------------------- ### Configure MQTT General Settings Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage_full.html Populates general MQTT configuration fields, including enable status, URL, port, username, and password. The URL is sliced to remove 'http://' or 'https://'. ```javascript document.getElementById("mqtt_en").value = obj.mqtt_en; document.getElementById("mqtt_url").value = obj.mqtt_url.slice(7); document.getElementById("mqtt_port").value = obj.mqtt_port; document.getElementById("mqtt_user").value = obj.mqtt_user; document.getElementById("mqtt_pass").value = obj.mqtt_pass; ``` -------------------------------- ### 32-bit Value Extraction Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/0.Config/6.Automate/1.Usage.md Extract a 32-bit unsigned integer value from four consecutive bytes in big-endian format. ```Automate Expression [B0:B3] ``` -------------------------------- ### Hardware Version Selection Source: https://github.com/meatpihq/wican-fw/blob/main/CMakeLists.txt Selects the hardware version for the project. Uncomment the desired version to set it. ```cmake # Uncomment and use one of the following as needed # set(HARDWARE_VER ${WICAN_V210}) set(HARDWARE_VER ${WICAN_V300}) # set(HARDWARE_VER ${WICAN_USB_V100}) # set(HARDWARE_VER ${WICAN_PRO}) add_definitions(-DHARDWARE_VER=${HARDWARE_VER}) ``` -------------------------------- ### Extract Lower Nibble Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/0.Config/6.Automate/1.Usage.md Isolate and extract the lower 4 bits (nibble) of a byte using a bitwise AND operation. ```Automate Expression B0 & 0x0F ``` -------------------------------- ### CAN to JSON Interpreter Output Format Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/7.MQTT/Usage.md Example JSON formats for data processed by the CAN to JSON interpreter. The structure depends on the configured 'Name' parameter. ```json {"VehicleSpeed": 23.000000} ``` ```json {"EngineSpeed": 1165.000000} ``` ```json {"CustomID": 3594.400000} ``` -------------------------------- ### Hardware Preference and Version String Logic Source: https://github.com/meatpihq/wican-fw/blob/main/CMakeLists.txt Sets the hardware preference and version string based on the selected HARDWARE_VER. This logic determines build-time definitions for hardware identification. ```cmake set(PROJECT_PREF "wican-fw") # Set HW_PREF based on HARDWARE_VER if (HARDWARE_VER EQUAL ${WICAN_PRO}) set(HW_PREF "${WICAN_PRO_PREF}") set(HW_VER "${WICAN_PRO_STR}") elseif (HARDWARE_VER EQUAL ${WICAN_USB_V100}) set(HW_PREF "${WICAN_USB_V100_PREF}") set(HW_VER "${WICAN_USB_V100_STR}") elseif (HARDWARE_VER EQUAL ${WICAN_V210}) set(HW_PREF "${WICAN_V210_PREF}") set(HW_VER "${WICAN_V210_STR}") else() set(HW_PREF "${WICAN_V300_PREF}") set(HW_VER "${WICAN_V300_STR}") endif() add_definitions(-DHARDWARE_VERSION="${HW_VER}") ``` -------------------------------- ### Store Configuration - JavaScript Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage_full.html Collects various configuration settings from the DOM, formats them into a JSON object, and sends them to the server via a POST request. Provides user feedback on success or failure. ```javascript async function postConfig() { var obj = {}; await new Promise(resolve => setTimeout(resolve, 1000)); // Add 1 second delay const storeResult = await storeAutoTableData(); if (!storeResult) { return; } obj["wifi_mode"] = document.getElementById("wifi_mode").value; obj["webhook_en"] = document.getElementById("webhook_en").value; obj["ap_ch"] = document.getElementById("ap_ch_value").value; obj["sta_ssid"] = document.getElementById("ssid_value").value; obj["sta_pass"] = document.getElementById("pass_value").value; obj["sta_security"] = document.getElementById("sta_security").value; obj["can_datarate"] = document.getElementById("can_datarate").value; obj["can_mode"] = document.getElementById("can_mode").value; obj["port_type"] = document.getElementById("port_type").value; obj["port"] = document.getElementById("tcp_port_value").value; obj["ap_pass"] = document.getElementById("ap_pass_value").value; obj["protocol"] = document.getElementById("protocol").value; obj["ble_pass"] = document.getElementById("ble_pass_value").value; obj["ble_status"] = document.getElementById("ble_status").value; obj["sleep_status"] = document.getElementById("sleep_status").value; obj["sleep_volt"] = document.getElementById("sleep_volt").value; obj["sleep_time"] = document.getElementById("sleep_time").value; obj["batt_alert"] = document.getElementById("batt_alert").value; obj["batt_alert_ssid"] = document.getElementById("batt_alert_ssid").value; obj["batt_alert_pass"] = document.getElementById("batt_alert_pass").value; obj["batt_alert_volt"] = document.getElementById("batt_alert_volt").value; obj["batt_alert_protocol"] = document.getElementById("batt_alert_protocol").value; let mqtt_txt = "mqtt://"; let mqtt_url_val = mqtt_txt.concat(document.getElementById("batt_alert_url").value); obj["batt_alert_url"] = mqtt_url_val; obj["batt_alert_port"] = document.getElementById("batt_alert_port").value; obj["batt_alert_topic"] = document.getElementById("batt_alert_topic").value; obj["batt_alert_time"] = document.getElementById("batt_alert_time").value; obj["batt_mqtt_user"] = document.getElementById("batt_mqtt_user").value; obj["batt_mqtt_pass"] = document.getElementById("batt_mqtt_pass").value; obj["mqtt_en"] = document.getElementById("mqtt_en").value; let mqtt_txt2 = "mqtt://"; let mqtt_url_val2 = mqtt_txt.concat(document.getElementById("mqtt_url").value); obj["mqtt_url"] = mqtt_url_val2; obj["mqtt_port"] = document.getElementById("mqtt_port").value; obj["mqtt_user"] = document.getElementById("mqtt_user").value; obj["mqtt_pass"] = document.getElementById("mqtt_pass").value; obj["keep_alive"] = document.getElementById("keep_alive").value; obj["mqtt_tx_topic"] = document.getElementById("mqtt_tx_topic").value; obj["ap_auto_disable"] = document.getElementById("ap_auto_disable").value; if(document.getElementById("mqtt_tx_en_checkbox").checked){ obj["mqtt_tx_en"] = "enable"; }else{ obj["mqtt_tx_en"] = "disable"; } obj["mqtt_rx_topic"] = document.getElementById("mqtt_rx_topic").value; if(document.getElementById("mqtt_rx_en_checkbox").checked){ obj["mqtt_rx_en"] = "enable"; }else{ obj["mqtt_rx_en"] = "disable"; } obj["mqtt_status_topic"] = document.getElementById("mqtt_status_topic").value; obj["mqtt_elm327_log"] = document.getElementById("mqtt_elm327_log").value; var configJSON = JSON.stringify(obj); const xhttp = new XMLHttpRequest(); xhttp.onload = function() { showNotification(this.responseText, "green"); document.getElementById("submit_button").disabled = true; }; xhttp.onerror = function() { showNotification("Failed to save configuration", "red"); }; xhttp.open("POST", "/store_config"); xhttp.send(configJSON); } ``` -------------------------------- ### Extract Upper Nibble Example Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/0.Config/6.Automate/1.Usage.md Isolate and extract the upper 4 bits (nibble) of a byte using bitwise AND and right shift operations. ```Automate Expression (B0 & 0xF0) >> 4 ``` -------------------------------- ### Update UI with General Configuration Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage_full.html Populates various form fields with configuration values from the 'obj' object, including protocol, ports, passwords, and sleep settings. This code assumes 'obj' contains all necessary configuration properties. ```javascript txCheckBoxChanged(); rxCheckBoxChanged(); document.getElementById("protocol").value = obj.protocol; document.getElementById("tcp_port_value").value = obj.port; document.getElementById("ap_pass_value").value = obj.ap_pass; document.getElementById("ble_pass_value").value = obj.ble_pass; document.getElementById("sleep_volt").value = obj.sleep_volt; document.getElementById("sleep_volt_value").textContent = obj.sleep_volt; document.getElementById("sleep_time").value = obj.sleep_time; document.getElementById('sleep_time_value').textContent = obj.sleep_time; ``` -------------------------------- ### ABRP API Payload Format Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/9.HTTPS/1.ABRP_API.md This is an example of the data payload expected by the ABRP API, formatted as URL-encoded key-value pairs. Ensure all units are normalized before sending. ```text utc=1730000000&soc=54.1&speed=23.4&lat=40.71278&lon=-74.00611&voltage=398.2¤t=-42.3&power=16.8&is_charging=0&car_model=tesla:model3:2021 ``` -------------------------------- ### Download Configuration Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage.html Fetches configuration data from multiple API endpoints (`/load_config`, `/load_auto_pid_car_data`, etc.) and compiles them into a single JSON file for download. Includes error handling for fetch requests and provides a timestamped filename. ```javascript var downloadCfg=(async()=>{let e=0,f=Error;const a=[`/load_config`,`/load_auto_pid_car_data`,`/load_auto_pid`,`/load_canflt`];const b=500;let c={};let d=!1;try{for(let g=e;gsetTimeout(a,b))}};if(Object.keys(c).length===e){throw new f(`No data was successfully fetched from any endpoint`)}else{const g=JSON.stringify(c,null,2);const h=new Blob([g],{type:`application/json`});const i=window.URL.createObjectURL(h);const j=document.createElement(`a`);j.href=i;j.download=`config_${new Date().toISOString().split(`T`)[e]}.json`;document.body.appendChild(j);j.click();document.body.removeChild(j);window.URL.revokeObjectURL(i);return !0}}catch(a){alert(`Failed to download configuration`);return !1}}) ``` -------------------------------- ### CMake Boilerplate and Version Definitions Source: https://github.com/meatpihq/wican-fw/blob/main/CMakeLists.txt Essential CMake boilerplate and definitions for WiCAN hardware versions and their string/preference representations. These must be in the specified order. ```cmake cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) set(WICAN_V210 1) set(WICAN_V300 2) set(WICAN_USB_V100 3) set(WICAN_PRO 4) set(WICAN_V210_STR "OBD") set(WICAN_V300_STR "OBD") set(WICAN_USB_V100_STR "USB") set(WICAN_PRO_STR "OBD-PRO") # Set these without quotes, as they're string literals set(WICAN_V210_PREF "obd") set(WICAN_V300_PREF "obd") set(WICAN_USB_V100_PREF "usb") set(WICAN_PRO_PREF "obd_pro") add_definitions(-DWICAN_V210=${WICAN_V210}) add_definitions(-DWICAN_V300=${WICAN_V300}) add_definitions(-DWICAN_USB_V100=${WICAN_USB_V100}) add_definitions(-DWICAN_PRO=${WICAN_PRO}) add_definitions(-DWICAN_V210_STR="${WICAN_V210_STR}") add_definitions(-DWICAN_V300_STR="${WICAN_V300_STR}") add_definitions(-DWICAN_USB_V100_STR="${WICAN_USB_V100_STR}") add_definitions(-DWICAN_PRO_STR="${WICAN_PRO_STR}") ``` -------------------------------- ### Load Local Car Models from File Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage_full.html Handles file input for local car model data. Parses the selected JSON file and checks for a single car format. ```javascript function loadLocalCarModels() { const fileInput = document.getElementById("car_data_file"); if (fileInput.files.length == 0) { showNotification("No files selected!", "red"); return; } const file = fileInput.files[0]; const reader = new FileReader(); reader.onload = async function(event) { try { const jsonData = JSON.parse(event.target.result); let data; // Check if it's a single car format (like Zeekr) with car_model property if (jsonData.car_model && jsonData.pids) { showNotification("Single car format detected. ", "red"); } } catch (error) { showNotification(error.message, "red"); } }; reader.readAsText(file); } ``` -------------------------------- ### Handle Input and Send Request Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage.html This snippet checks input values and sends a GET request to '/check_status'. It's typically used for form submissions or status checks. ```javascript if(document.getElementById(l).value==j){document.getElementById(m).style.display=k}};a.open("GET","/check_status");a.send()}); ``` -------------------------------- ### Upload Configuration File Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage.html Handles the upload of a configuration file (JSON format). It reads the file, parses its content, and sends different configuration sections (e.g., 'config', 'auto_pid') to their respective API endpoints via POST requests. Provides user feedback on success or failure. ```javascript var uploadCfg=(async()=>{let e=JSON;const a=document.getElementById(`fileInput`);const b=a.files[0];if(!b)return;const c={'config':`/store_config`,'auto_pid':`/store_auto_data`,'auto_pid_car_data':`/store_car_data`,'canflt':`/store_canflt`};const d=200;try{const f=new FileReader();f.onload=(async(b)=>{try{const f=e.parse(b.target.result);let g=!1;for(const [a,b] of Object.entries(c)){if(f[a]){try{const c=await fetch(b,{method:`POST`,headers:{'Content-Type':`application/json`},body:e.stringify(f[a])});if(!c.ok){g=!0;throw new Error(`HTTP error! status: ${c.status}`)};await new Promise(a=>setTimeout(a,d))}catch(a){g=!0}}};if(g){alert(`Some configurations failed to upload`)}else{alert(`Configuration uploaded successfully, Rebooting...`)};a.value=` `}catch(a){alert(`Failed to parse configuration file`)}});f.onerror=(()=>{alert(`Error reading file`)});f.readAsText(b)}catch(a){alert(`Upload failed`)}}) ``` -------------------------------- ### Configure UI Elements Based on Settings Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage_full.html Adjusts UI element states (dropdowns, visibility) based on current configuration values like battery alert time, protocol, and MQTT settings. It also handles dynamic updates for car models and their parameters. ```javascript else if(obj.batt_alert_time == "6") { document.getElementById("batt_alert_time").selectedIndex = "1"; } else if(obj.batt_alert_time == "12") { document.getElementById("batt_alert_time").selectedIndex = "2"; } else if(obj.batt_alert_time == "24") { document.getElementById("batt_alert_time").selectedIndex = "3"; } if(document.getElementById("protocol").value == "savvycan") { document.getElementById("tcp_port_value").value = "23"; document.getElementById("tcp_port_value").disabled = true; document.getElementById("port_type").selectedIndex = "0"; document.getElementById("port_type").disabled = true; } if(document.getElementById("batt_alert").value == "enable") { document.getElementById("batt_alert_div").style.display = "block"; } else if(document.getElementById("batt_alert").value == "disable") { document.getElementById("batt_alert_div").style.display = "none"; } if(document.getElementById("mqtt_en").value == "enable") { document.getElementById("mqtt_en_div").style.display = "block"; } else if(document.getElementById("mqtt_en").value == "disable") { document.getElementById("mqtt_en_div").style.display = "none"; } loadCANFLT(); loadautoPIDCarData(); loadautoPID(); loadWebhookConfig(); document.getElementById("car_model").addEventListener('change', function() { // Clear existing entries document.querySelector('.specific-pid-entries').innerHTML = ''; const selectedModel = this.value; if (latest_car_models && Array.isArray(latest_car_models.cars)) { const selectedCar = latest_car_models.cars.find(car => car.car_model === selectedModel); const specificInitElement = document.getElementById("specific_init"); specificInitElement.value = selectedCar.init; if (selectedCar && selectedCar.pids) { selectedCar.pids.forEach(pid => { if (pid.parameters) { pid.parameters.forEach(param => { addCarParameter({ ...param, pid: pid.pid, pid_init: pid.pid_init }); }); } }); } } }); document.getElementById("store_canflt_button").disabled = true; document.querySelector(".store").disabled = true; document.getElementById("submit_button").disabled = true; }; checkStatus(); xhttp.open("GET", "/load_config"); xhttp.send(); ``` -------------------------------- ### Update CAN Filter Data Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage.html Parses response text to update CAN filter rows. It checks if the response is an array and iterates through it to restore each row's data. This function is triggered by a GET request to /load_canflt. ```javascript a=JSON.parse(this.responseText);if(this.responseText!=b){if(Array.isArray(a.can_flt)){a.can_flt.forEach(a=>{restoreCANFLTRow(a["CANID"],a["Name"],a["PID"],a["PIDIndex"],a["StartBit"],a["BitLength"],a["Expression"],a["Cycle"])})}}};a.open(`GET`,`/load_canflt`);a.send() ``` -------------------------------- ### WiCAN Initialization Sequence for Ioniq 9 Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/0.Config/6.Automate/3.New_Vehicle_Profiles.md Essential initialization commands for WiCAN to communicate with the Ioniq 9. These commands are derived from logs and are included in the vehicle profile. ```bash >ATSP6 OK >ATSH7E4 OK >ATST96 OK >ATFCSH7E4 OK >ATFCSD300000 OK >ATFCSM0 OK ``` -------------------------------- ### OBDII PID Request - Battery SoC Source: https://github.com/meatpihq/wican-fw/blob/main/docs/content/7.MQTT/Usage.md Example payload for requesting the Battery State of Charge (SoC). Compatibility may vary by EV model. The response payload shows how to calculate the SoC percentage. ```json {"bus":"0","type":"tx","frame":[{"id":2015,"dlc":8,"rtr":false,"extd":false,"data":[2,1,91,170,170,170,170,170]}]} ``` ```json {"bus":"0","type":"rx","ts":51561,"frame":[{"id":2024,"dlc":8,"rtr":false,"extd":false,"data":[3,65,91,170,0,0,0,0]}]} ``` -------------------------------- ### Load and Display Webhook Configuration Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage.html Fetches webhook configuration from the API and updates the UI elements. It handles displaying the URL, interval, success/fail counts, status, last post time, and error details. Includes error handling for API fetch failures and a timer to periodically refresh the configuration. ```javascript var loadWebhookConfig=(()=>{let m=0,a=`webhook_url`,q=`none`,f=`webhook_stat_last_post`,o=``,b=`webhook_interval`,e=`webhook_stat_status`,k=`Not configured`,c=`webhook_stat_success`,j=`webhook_error_details_btn`,p=`No error recorded.`,g=`webhook_last_error`,d=`webhook_stat_fail`,h=`webhook_error_details`,i=`webhook_error_row`,l=Number,n=String;if(!window._webhookStatsTimer){window._webhookStatsTimer=setInterval(loadWebhookConfig,5000)};fetch(`/api/webhook`).then(a=>{if(!a.ok){throw new Error(`Failed to fetch webhook config`)};return a.json()}).then(r=>{const s=document.getElementById(a);const t=document.getElementById(b);const u=document.getElementById(c);const v=document.getElementById(d);const w=document.getElementById(e);const x=document.getElementById(f);const y=document.getElementById(g);const z=document.getElementById(h);const A=document.getElementById(i);const B=document.getElementById(j);if(s){s.value=r.enabled&&r.url?r.url:k};if(t){t.value=r.enabled&&r.interval?r.interval.toString():k};const C=l(r.success_count||m);const D=l(r.fail_count||m);const E=r&&r.status?n(r.status):(r.enabled?`unknown`:`disabled`);const F=r&&r.last_post?n(r.last_post):o;const G=r&&r.last_error?n(r.last_error):o;const H=a=>{const b=n(a||o).trim();return b.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z\s+/,o)};const I=G?H(G):o;window._webhookLastErrorText=I;if(u)u.textContent=`OK: ${C}`;if(v)v.textContent=`Fail: ${D}`;if(w){w.textContent=`Status: ${E||`-`}`;const a=n(E||o).toLowerCase();if(a===`ok`)w.style.color=`var(--success-color)`;else if(a===`failed`||a===`error`)w.style.color=`var(--danger-color)`;else w.style.color=`var(--gray-600)`};if(x)x.textContent=o;if(y){y.textContent=I||o;y.title=I||o};if(z){z.textContent=I||p};const J=!!I;if(A)A.style.display=J?`flex`:q;if(B)B.disabled=!J;if(!J&&z)z.style.display=q}).catch(l=>{console.log(`Webhook config not available:`,l);const m=document.getElementById(a);const n=document.getElementById(b);if(m)m.value=k;if(n)n.value=k;const r=document.getElementById(c);const s=document.getElementById(d);const t=document.getElementById(e);const u=document.getElementById(f);const v=document.getElementById(g);const w=document.getElementById(h);const x=document.getElementById(i);const y=document.getElementById(j);if(r)r.textContent=`OK: 0`;if(s)s.textContent=`Fail: 0`;if(t)t.textContent=`Status: -`;if(u)u.textContent=o;if(v){v.textContent=o;v.title=o};if(w)w.textContent=p;if(x)x.style.display=q;if(y)y.disabled=!0;if(w)w.style.display=q})}); ``` -------------------------------- ### Load Device Configuration Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage.html Fetches device configuration from the server using XMLHttpRequest and dynamically updates UI elements based on the received JSON data. Handles various settings including Wi-Fi mode, AP channel, station credentials, CAN data rate and mode, port type, BLE status, sleep status, and MQTT settings. ```javascript var Load=(()=>{let g=\`ap_auto_disable\`,w=\`mqtt_tx_en_checkbox\`,y=\`mqtt_rx_en_checkbox\`,u=\`none\`,z=\`mqtt_rx_topic\`,m=\`3\`,o=\`can_mode\`,v=\`sleep_status\`,x=\`mqtt_tx_topic\`,A=\`protocol\`,r=\`ble_warning_div\`,D=7,E=\`mqtt_en\`,s=\`block\`,e=\`pass_value\`,l=\`2\`,C=\`batt_alert\`,b=\`wifi_mode\`,k=\`can_datarate\`,G=\`batt_alert_div\`,p=\`port_type\`,i=\`enable\`,t=\`disable\`,H=\`mqtt_en_div\`,h=\`0\`,F=\`batt_alert_time\`,f=\`sta_security\`,d=\`ssid_value\`,c=\`1\`,q=\`ble_status\`,n=\`6\`,B=\`tcp_port_value\`,j=Array;const a=new XMLHttpRequest();a.onload=function(){var a=JSON.parse(this.responseText);if(a.wifi_mode==\`APStation\`){document.getElementById(b).selectedIndex=c;document.getElementById(d).disabled=!1;document.getElementById(e).disabled=!1;document.getElementById(f).disabled=!1;document.getElementById(g).disabled=!1}else if(a.wifi_mode==\`AP\`){document.getElementById(b).selectedIndex=h;document.getElementById(d).disabled=!0;document.getElementById(e).disabled=!0;document.getElementById(g).disabled=!0};if(a.ap_auto_disable==i){document.getElementById(g).selectedIndex=h}else{document.getElementById(g).selectedIndex=c};var I=parseInt(a.ap_ch);I=I- 1;document.getElementById(\`ap_ch_value\`).selectedIndex=I.toString();document.getElementById(d).value=a.sta_ssid;document.getElementById(e).value=a.sta_pass;document.getElementById(f).value=a.sta_security||\`wpa3\`;const J=document.getElementById(\`webhook_en\`);if(J){const b=a.webhook_en||i;const c=j.from(J.options||[]).some(a=>a&&a.value===b);J.value=c?b:i};if(a.can_datarate==\`5K\`){document.getElementById(k).selectedIndex=h}else if(a.can_datarate==\`10K\`){document.getElementById(k).selectedIndex=c}else if(a.can_datarate==\`20K\`){document.getElementById(k).selectedIndex=l}else if(a.can_datarate==\`25K\`){document.getElementById(k).selectedIndex=m}else if(a.can_datarate==\`50K\`){document.getElementById(k).selectedIndex=\`4\`}else if(a.can_datarate==\`100K\`){document.getElementById(k).selectedIndex=\`5\`}else if(a.can_datarate==\`125K\`){document.getElementById(k).selectedIndex=n}else if(a.can_datarate==\`250K\`){document.getElementById(k).selectedIndex=\`7\`}else if(a.can_datarate==\`500K\`){document.getElementById(k).selectedIndex=\`8\`}else if(a.can_datarate==\`800K\`){document.getElementById(k).selectedIndex=\`9\`}else if(a.can_datarate==\`1000K\`){document.getElementById(k).selectedIndex=\`10\`}else if(a.can_datarate==\`auto\`){document.getElementById(k).selectedIndex=\`11\`};if(a.can_mode==\`normal\`){document.getElementById(o).selectedIndex=h}else if(a.can_mode==\`silent\`){document.getElementById(o).selectedIndex=c};if(a.port_type==\`tcp\`){document.getElementById(p).selectedIndex=h}else if(a.port_type==\`udp\`){document.getElementById(p).selectedIndex=c};if(a.ble_status==i){document.getElementById(q).selectedIndex=h;document.getElementById(r).style.display=s}else if(a.ble_status==t){document.getElementById(q).selectedIndex=c;document.getElementById(r).style.display=u};if(a.sleep_status==i){document.getElementById(v).selectedIndex=h}else if(a.sleep_status==t){document.getElementById(v).selectedIndex=c};if(\`mqtt_tx_en\` in a){if(a.mqtt_tx_en===i){document.getElementById(w).checked=!0}else{document.getElementById(w).checked=!1}}else{document.getElementById(w).checked=!1;document.getElementById(x).disabled=!0};if(\`mqtt_rx_en\` in ``` -------------------------------- ### Vehicle Profile JSON Structure Source: https://github.com/meatpihq/wican-fw/blob/main/vehicle_profiles/README.md Example of a vehicle profile configuration in JSON format. This defines the car model, initialization commands for the OBD adapter, and specific Parameter IDs (PIDs) with their associated data parameters. ```json { "car_model": "Hyundai: Ioniq5/Ioniq6/Kona", "init": "ATSH7E4;ATST96;", "pids": [ { "pid": "2201019", "parameters": { "SOC_BMS": "B10/2", "Aux_Batt_Volts": "B38*0.1" } }, { "pid": "2201057", "parameters": { "SOH": "[B34:B35]/10" } } ] } ``` -------------------------------- ### WiCAN CAN to JSON Interrupter Configuration Source: https://github.com/meatpihq/wican-fw/wiki/EV-Battery-SoC-in-Home-Assistant-‐-Example Configure WiCAN to read CAN frames, calculate values using mathematical expressions, and publish them in JSON format. This example specifically calculates Battery SoC from received CAN frame data. ```text ID: 1995 Name: BatterySoC PID: 3 Start bit: 0 (Not used) Bit Length: 1 (Not used) Expression: ((B4*256)+B5)/10 Cycle: 1000 ``` -------------------------------- ### Load System Settings Source: https://github.com/meatpihq/wican-fw/blob/main/main/homepage_full.html Loads system settings via XMLHttpRequest, parses the JSON response, and updates UI elements for Wi-Fi mode, SSID, password, AP channel, and webhook enablement. Adjusts UI element states based on loaded configuration values. ```javascript function Load() { const xhttp = new XMLHttpRequest(); xhttp.onload = function() { var obj = JSON.parse(this.responseText); if(obj.wifi_mode == "APStation") { document.getElementById("wifi_mode").selectedIndex = "1"; document.getElementById("ssid_value").disabled = false; document.getElementById("pass_value").disabled = false; document.getElementById("sta_security").disabled = false; document.getElementById("ap_auto_disable").disabled = false; } else if(obj.wifi_mode == "AP") { document.getElementById("wifi_mode").selectedIndex = "0"; document.getElementById("ssid_value").disabled = true; document.getElementById("pass_value").disabled = true; document.getElementById("ap_auto_disable").disabled = true; } if(obj.ap_auto_disable == "enable") { document.getElementById("ap_auto_disable").selectedIndex = "0"; } else { document.getElementById("ap_auto_disable").selectedIndex = "1"; } var ch = parseInt(obj.ap_ch); ch = ch - 1; document.getElementById("ap_ch_value").selectedIndex = ch.toString(); document.getElementById("ssid_value").value = obj.sta_ssid; document.getElementById("pass_value").value = obj.sta_pass; document.getElementById("sta_security").value = obj.sta_security || "wpa3"; const webhookEnEl = document.getElementById("webhook_en"); if (webhookEnEl) { const webhookModeFromCfg = obj.webhook_en || "enable"; const hasWebhookOption = Array.from(webhookEnEl.options || []).some(o => o && o.value === webhookModeFromCfg); webhookEnEl.value = hasWebhookOption ? webhookModeFromCfg : "enable"; } if(obj.can_datarate == "5K") { document.getElementById("can_datarate").selectedIndex = "0"; } else if(obj.can_datarate == "10K") { ```