├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── SDCard └── mypic.jpg ├── enclosure ├── Case.Esp32.zip ├── Va&CobGauge.3mf ├── Va&CobGauge.mtl ├── Va&CobGauge.skb ├── Va&CobGauge.skp ├── Va&CobGauge.stl ├── backcover.stl ├── enclosure.jpg ├── enclosure.zip └── readme.md ├── hardware ├── 12-5v micro usb.jpg ├── Box.png ├── ELM327_bluetooth.png ├── ESP32 lvgl_touch.jpg ├── ESP32-2432S028-LCM001.jpeg ├── ESP32-2432S028-LCM002.jpeg ├── ESP32-WROOM-1 Pin definition.png ├── Parts list.txt ├── push_button.jpg └── speaker.jpg ├── icon ├── about.jpg ├── arc.jpg ├── autooff.jpg ├── car-battery-t.png ├── cpu.jpg ├── engine.jpg ├── image.jpg ├── mypic.jpg ├── nofile.jpg ├── number.jpg ├── obd2gauge.jpg ├── overheat.jpg ├── pid list.xlsx ├── quit.jpg ├── sdcard.png ├── switchoff.jpg ├── switchon.jpg ├── vaandcob.jpg ├── vbar.jpg ├── warning.jpg └── wifi.jpg ├── picture ├── config.jpg ├── cover.jpg ├── fullpart.jpg ├── gauge.jpg └── page.jpg └── sketch ├── VaAndCobOBD2Gauge ├── VaAndCobOBD2Gauge.ino ├── bluetooth.h ├── build │ └── esp32.esp32.esp32 │ │ └── VaAndCobOBD2Gauge.ino.bootloader.bin ├── config.h ├── elm327.h ├── firmware │ ├── README.md │ ├── T5_manifest.json │ ├── T6_manifest.json │ ├── VaAndCobOBD2GaugeT5_nogenuine.bin │ ├── VaAndCobOBD2Gauge_T6_nogenuine.bin │ ├── boot_app0.bin │ ├── bootloader.bin │ ├── offset.png │ └── partitions.bin ├── firmware_update.h ├── image.h ├── meter.h ├── music.h ├── nes_audio.cpp ├── nes_audio.h └── touchscreen.h ├── arduinoSetup.png ├── gauge_factory_init ├── build │ └── esp32.esp32.esp32 │ │ ├── gauge_factory_init.ino.bin │ │ ├── gauge_factory_init.ino.bootloader.bin │ │ ├── gauge_factory_init.ino.elf │ │ ├── gauge_factory_init.ino.map │ │ └── gauge_factory_init.ino.partitions.bin └── gauge_factory_init.ino └── library modified └── TFT_eSPI ├── How to replace file.md └── User_Setup.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | buy_me_a_coffee: vaandcob 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT No Commercial Use License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | Non-Commercial Use Restriction: 22 | The Software and any derivative works thereof must not be used, distributed, 23 | or otherwise exploited for commercial purposes or for any financial benefit. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32-Bluetooth-OBD2-Gauge 2 | 3 | ## This repository has been archived and is no longer maintained. (October 24th, 2025) 4 | 5 | A fully functional code-updated 6 | Last Update: September 27th, 2024 7 | 8 | This is an ESP32 OBD2 Gauge for vehicles that are compatible with OBD2 9 | (Special made for FORD vehicle) 10 | - Prototype https://youtu.be/PkQaUJbzTNM 11 | - Current Model Short Demo https://youtu.be/vvBIeim7XTE 12 | 13 | ![](/picture/page.jpg) 14 | ![](/picture/config.jpg) 15 | ![](enclosure/enclosure.jpg) 16 | 17 | ## Software: 18 | - Arduino IDE 2.3.2 + ESP32 arduino core 2.0.17 19 | - * IMPORTANT * this project won't work with ESP32 arduiino core 3.x.x 20 | - modified TFT_eSPI library 2.5.43 21 | 22 | ## Hardware 23 | - ESP32 TFT2.8 320x240 with "RESISTIVE" touch board. 24 | buy here https://s.click.aliexpress.com/e/_Ew95gMl 25 | - ELM327 Bluetooth adaptor (recommended v1.5 not v2.1) 26 | Highly recommend ELM327 Bluetooth adaptor https://s.click.aliexpress.com/e/_oo3THvG 27 | - A small speaker for mobile phone 1 Watt 8 ohm 9x28 mmm. 28 | - A push button 6x6x5 2 legs 29 | - 3d print enclosure 30 | - 12v to 5v dc micro usb power regulator module 31 | - Gauge magnetic stand 32 | 33 | ## Features: 34 | - Show vehicle data 7 pids 35 | * MAP - manifold air pressure (PSI) 36 | * PCM - pcm voltage (volt) 37 | * ENG LOAD - engine load (%) 38 | * ENG SPD - engine speed (RPM) 39 | * Coolant - coolant temperature (c) 40 | * Oil Temp - engine oil temperature (c) 41 | * TFT - Ford transmission fluid temperature (c) 42 | 43 | - 8 layout page selectable display 44 | - DTC read and clear function (Engine warning light only) 45 | - Warning when parameter reaches setting value 46 | - Adjustable warning value for each PIDs 47 | - Configurable CPU overheat protection 48 | - Configurable gauge Auto turn on/off 49 | - Auto screen brightness 50 | - Change off screen to user screen with micro SDcard 51 | - Firmware updatable (micro SDcard and WiFi) 52 | - VIN Read on the About page. 53 | 54 | 55 | ## [Flash Firmware Online](https://vaandcob.github.io/webpage/src/index.html) 56 | 57 | ------------------------------------------------------- 58 | [![Buy Me a Coffee](https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20coffee&emoji=☕&slug=vaandcob&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff)](https://www.buymeacoffee.com/vaandcob) 59 | 60 | 61 | -------------------------------------------------------------------------------- /SDCard/mypic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/SDCard/mypic.jpg -------------------------------------------------------------------------------- /enclosure/Case.Esp32.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/enclosure/Case.Esp32.zip -------------------------------------------------------------------------------- /enclosure/Va&CobGauge.3mf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/enclosure/Va&CobGauge.3mf -------------------------------------------------------------------------------- /enclosure/Va&CobGauge.mtl: -------------------------------------------------------------------------------- 1 | # 2 | ## Alias OBJ Material File 3 | # Exported from SketchUp, (c) 2000-2012 Trimble Navigation Limited 4 | 5 | newmtl M_0134_DimGray 6 | Ka 0.000000 0.000000 0.000000 7 | Kd 0.411765 0.411765 0.411765 8 | Ks 0.330000 0.330000 0.330000 9 | 10 | newmtl FrontColor 11 | Ka 0.000000 0.000000 0.000000 12 | Kd 1.000000 1.000000 1.000000 13 | Ks 0.330000 0.330000 0.330000 14 | 15 | -------------------------------------------------------------------------------- /enclosure/Va&CobGauge.skb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/enclosure/Va&CobGauge.skb -------------------------------------------------------------------------------- /enclosure/Va&CobGauge.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/enclosure/Va&CobGauge.skp -------------------------------------------------------------------------------- /enclosure/Va&CobGauge.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/enclosure/Va&CobGauge.stl -------------------------------------------------------------------------------- /enclosure/enclosure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/enclosure/enclosure.jpg -------------------------------------------------------------------------------- /enclosure/enclosure.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/enclosure/enclosure.zip -------------------------------------------------------------------------------- /enclosure/readme.md: -------------------------------------------------------------------------------- 1 | 3d print files 2 | 3 | ![](/enclosure/enclosure.jpg) 4 | -------------------------------------------------------------------------------- /hardware/12-5v micro usb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/hardware/12-5v micro usb.jpg -------------------------------------------------------------------------------- /hardware/Box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/hardware/Box.png -------------------------------------------------------------------------------- /hardware/ELM327_bluetooth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/hardware/ELM327_bluetooth.png -------------------------------------------------------------------------------- /hardware/ESP32 lvgl_touch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/hardware/ESP32 lvgl_touch.jpg -------------------------------------------------------------------------------- /hardware/ESP32-2432S028-LCM001.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/hardware/ESP32-2432S028-LCM001.jpeg -------------------------------------------------------------------------------- /hardware/ESP32-2432S028-LCM002.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/hardware/ESP32-2432S028-LCM002.jpeg -------------------------------------------------------------------------------- /hardware/ESP32-WROOM-1 Pin definition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/hardware/ESP32-WROOM-1 Pin definition.png -------------------------------------------------------------------------------- /hardware/Parts list.txt: -------------------------------------------------------------------------------- 1 | ESP32 Arduino LVGL WIFI&Bluetooth Development Board 2.8" 240*320 Smart Display Screen 2.8inch LCD TFT Module With Touch WROOM 2 | https://s.click.aliexpress.com/e/_DB8Ht8N 3 | 4 | Micro USB Output 12V to 5V 3A 5 | https://shopee.co.th/%E2%98%80S%E2%98%80%E0%B8%AD%E0%B8%B8%E0%B8%9B%E0%B8%81%E0%B8%A3%E0%B8%93%E0%B9%8C%E0%B9%81%E0%B8%9B%E0%B8%A5%E0%B8%87%E0%B8%9E%E0%B8%B2%E0%B8%A7%E0%B9%80%E0%B8%A7%E0%B8%AD%E0%B8%A3%E0%B9%8C%E0%B8%8B%E0%B8%B1%E0%B8%9E%E0%B8%9E%E0%B8%A5%E0%B8%B2%E0%B8%A2-Micro-USB-Output-12V-to-5V-3A-%E0%B8%AA%E0%B9%8D%E0%B8%B2%E0%B8%AB%E0%B8%A3%E0%B8%B1%E0%B8%9A-PDA-DV-i.293684420.7945781904?sp_atk=8f247e46-d3d4-4ec1-abec-2bd83ef709de&xptdk=8f247e46-d3d4-4ec1-abec-2bd83ef709de 6 | 7 | ELM327 Bluetooth 8 | https://s.click.aliexpress.com/e/_oo3THvG 9 | 10 | Speaker 1W 8R 2809 8ohm 11 | https://shopee.co.th/(Takashitree)-%E0%B8%A5%E0%B9%8D%E0%B8%B2%E0%B9%82%E0%B8%9E%E0%B8%87%E0%B8%AE%E0%B8%AD%E0%B8%A3%E0%B9%8C%E0%B8%99%E0%B8%84%E0%B8%AD%E0%B8%A1%E0%B8%9E%E0%B8%B4%E0%B8%A7%E0%B9%80%E0%B8%95%E0%B8%AD%E0%B8%A3%E0%B9%8C-%E0%B9%82%E0%B8%99%E0%B9%89%E0%B8%95%E0%B8%9A%E0%B8%B8%E0%B9%8A%E0%B8%81-1W-8R-2809-8-%E0%B9%82%E0%B8%AD%E0%B8%AB%E0%B9%8C%E0%B8%A1-28*9-%E0%B8%A1%E0%B8%A1.-2-%E0%B8%8A%E0%B8%B4%E0%B9%89%E0%B8%99-i.131626048.22227074892 12 | 13 | Push button 14 | Push Bottom Switch 6x6x5 mm -------------------------------------------------------------------------------- /hardware/push_button.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/hardware/push_button.jpg -------------------------------------------------------------------------------- /hardware/speaker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/hardware/speaker.jpg -------------------------------------------------------------------------------- /icon/about.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/about.jpg -------------------------------------------------------------------------------- /icon/arc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/arc.jpg -------------------------------------------------------------------------------- /icon/autooff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/autooff.jpg -------------------------------------------------------------------------------- /icon/car-battery-t.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/car-battery-t.png -------------------------------------------------------------------------------- /icon/cpu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/cpu.jpg -------------------------------------------------------------------------------- /icon/engine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/engine.jpg -------------------------------------------------------------------------------- /icon/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/image.jpg -------------------------------------------------------------------------------- /icon/mypic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/mypic.jpg -------------------------------------------------------------------------------- /icon/nofile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/nofile.jpg -------------------------------------------------------------------------------- /icon/number.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/number.jpg -------------------------------------------------------------------------------- /icon/obd2gauge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/obd2gauge.jpg -------------------------------------------------------------------------------- /icon/overheat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/overheat.jpg -------------------------------------------------------------------------------- /icon/pid list.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/pid list.xlsx -------------------------------------------------------------------------------- /icon/quit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/quit.jpg -------------------------------------------------------------------------------- /icon/sdcard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/sdcard.png -------------------------------------------------------------------------------- /icon/switchoff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/switchoff.jpg -------------------------------------------------------------------------------- /icon/switchon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/switchon.jpg -------------------------------------------------------------------------------- /icon/vaandcob.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/vaandcob.jpg -------------------------------------------------------------------------------- /icon/vbar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/vbar.jpg -------------------------------------------------------------------------------- /icon/warning.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/warning.jpg -------------------------------------------------------------------------------- /icon/wifi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/icon/wifi.jpg -------------------------------------------------------------------------------- /picture/config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/picture/config.jpg -------------------------------------------------------------------------------- /picture/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/picture/cover.jpg -------------------------------------------------------------------------------- /picture/fullpart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/picture/fullpart.jpg -------------------------------------------------------------------------------- /picture/gauge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/picture/gauge.jpg -------------------------------------------------------------------------------- /picture/page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/picture/page.jpg -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/VaAndCobOBD2Gauge.ino: -------------------------------------------------------------------------------- 1 | /*========= Va&Cob OBD2 Gauge ========== 2 | Programmed: by Ratthanin W. 3 | Hardware: 4 | - Hardware 2.8" TFT ESP32 LVGL https://www.youtube.com/watch?v=d2OXlVcRYrU 5 | Configuration: 6 | - Partition scheme 7 | Minimal SPIFFS 1.9MB APP/ 190kB SPIFFS /OTA (for OTA ,must reduce code size) 8 | 9 | Important Note: 10 | - New device must flash "gauge_factory_init.ino" first to get it work by checking 'serialno' in pref. 11 | or you can skip genuine checking by comment out line no. 338 12 | - CPU temp read never work until Bluetooth or Wifi is connected 13 | 14 | About opening logo image and goodbye image. 15 | - you can save welcome page logo into gauge by saving file name "vaandcob.jpg" size 320x240 pixels into littleFS storage. 16 | - you can save goodbye page image into gauge by saving file name "mypic.jpg" size 320z240 pixels into littleFS storage 17 | * if you don't want to use it, you can comment out code line no. 320 - 322 18 | */ 19 | 20 | //--- FLAG SETTING ----- for debugging 21 | //#define TERMINAL //temrinal mode (no gauge) 22 | //#define SERIAL_DEBUG //to show data in serial port 23 | //#define SKIP_CONNECTION //skip elm327 BT connection to view meter 24 | //#define TEST_DTC //test DTC 25 | 26 | //#define FORD_T5 // FOR FORD T5 uncomment this line 27 | 28 | //Intermal temperature sensor function declaration 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | uint8_t temprature_sens_read(); 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | uint8_t temprature_sens_read(); //declare intermal temperature sensor functio 37 | 38 | //Macro 39 | #define array_length(x) (sizeof(x) / sizeof(x[0])) //macro to calculate array length 40 | 41 | //library load 42 | #include //save permanent data 43 | Preferences pref; //create preference 44 | #include 45 | #include //Hardware-specific library 46 | 47 | 48 | //Pin configuration 49 | #define SCA_PIN 21 //not used same pin with TFT_BL 50 | #define SCL_PIN 22 //not used (avaialable) CAN_TX 51 | #define ADC_PIN 35 //not used (available) CAN_RX 52 | #define LED_RED_PIN 4 53 | #define LED_GREEN_PIN 16 54 | #define LED_BLUE_PIN 17 55 | #define LDR_PIN 34 //LDR sensor 56 | #define BUZZER_PIN 26 //speaker 57 | #define SELECTOR_PIN 27 //push button 58 | // setting PWM properties 59 | #define backlightChannel 0 60 | #define buzzerChannel 2 61 | 62 | //DISPLAY 63 | TFT_eSPI tft = TFT_eSPI(); 64 | #define TFT_GREY 0x5AEB 65 | 66 | 67 | //ELM327 init https://www.elmelectronics.com/wp-content/uploads/2016/07/ELM327DS.pdf 68 | const uint8_t elm327InitCount = 8; 69 | const String elm327Init[elm327InitCount] = { "ATZ", "ATBRD23", "ATSP0", "ATAT2", "ATL0", "ATH0", "ATE0", "0100" }; //agressive wait response time from ECU 70 | //const String elm327Init[elm327InitCount] = {"ATZ","ATPP0CSV35","ATPP0CON","ATSP0","ATAT2","ATL0","ATH0","ATE0","0100"};//agressive wait response time from ECU 71 | 72 | /*------ PIDS total 7 now ------------------- 73 | Array data -> { Label , unit, pid, fomula, min, max, ,skip, digit, warn } 74 | label = Pid label show on meter 75 | unit = unit for pid 76 | pid = string pid 0104 77 | formula = formula no to calculate 78 | min = lowest value 79 | max = highest value 80 | skip = delay reading 0 - 3; 3 = max delay read 81 | digit = want to show digit or not 82 | warn = default warning value 83 | */ 84 | const String pidConfig[7][9] = { 85 | //[pid][data] 86 | { "ENG Load", "%", "0104", "2", "0", "100", "0", "0", "80" }, //0 = 0104 87 | { "ECT", "`C", "0105", "1", "0", "120", "3", "0", "99" }, //1 = 0105 88 | { "MAP", "psi", "010B", "0", "0", "40", "0", "1", "35" }, //2 = 010B 89 | { "ENG SPD", "rpm", "010C", "3", "0", "5000", "0", "0", "4000" }, //3 = 010C 90 | { "PCM Volt", "volt", "0142", "4", "0", "16", "1", "1", "15" }, //4 = 0142 91 | #ifdef FORD_T5 92 | { "IAT", "`C", "010F", "1", "0", "120", "3", "0", "99" }, //5 = 015C 93 | #else 94 | { "Oil Temp", "`C", "015C", "1", "0", "120", "3", "0", "99" }, //5 = 015C 95 | #endif 96 | #ifdef FORD_T5 97 | { "TFT", "`C", "221674", "6", "0", "120", "3", "0", "99" } //6 = 221674 for FORD T5 98 | #else 99 | { "Trans Temp", "`C", "221E1C", "5", "0", "120", "3", "1", "99" } //6 = 221E1C for FORD T6+ 100 | #endif 101 | 102 | }; 103 | 104 | //barometric pressure "0133" turbo boost = map - bp; 105 | //hold warning value 106 | String warningValue[7] = { "80", "99", "35", "4000", "15", "99", "99" }; 107 | 108 | /* User configuration here to change display 109 | layout 0 layout 1 layout 2 layout 3 layout 4 layout 5 110 | █ 1 █ █ 7 █ █ 1 █ █ █ █ █ 4 █ █ █ █ █ 7 █ █ █ █ █ █ █ █ █ 111 | █ 2 █ █ 8 █ █ 2 █ 3 4 1 █ 5 █ 4 1 2 █ 8 █ 1 2 3 4 1 2 3 4 112 | █ 3 █ █ 9 █ █ 3 █ █ █ █ █ 6 █ █ █ █ █ 9 █ █ █ █ █ █ █ █ █ 113 | 114 | set up meter here which pid to use on each cell 115 | 0 - engine load 116 | 1 - coolant 117 | 2 - manifold Pressure 118 | 3 - engine Speed 119 | 4 - pcm volt 120 | 5 - oil Temp 121 | 6 - trans Temp 122 | */ 123 | const uint8_t pidInCell[8][7] = { 124 | //[layout][cellNo] 125 | //the last cell must be 3 (engine speed) to check engine off 126 | { 0, 2, 3, 1, 5, 6, 4 }, //layout 0 -> 6 cell {load,map,engspd,coolant,oil,tft,pcmvolt 127 | { 0, 2, 3, 1, 5, 6, 4 }, //layout 1 -> 6 cell {load,map,engspd,coolant,oil,tft,pcmvolt 128 | { 0, 2, 3, 1, 5, 6, 4 }, //layout 2 -> 6 cell {load,map,engspd,coolant,oil,tft,pcmvolt 129 | { 1, 5, 6, 3, 2, 4, 4 }, //layout 3 -> 5 cell {cooland,oil,tft,map,pcmvolt,pcmvolt} 130 | { 2, 1, 5, 6, 4, 0, 4 }, //layout 4 -> 5 cell {MAP,coolant,oiltemp,tft,pcmvolt,load,pcmvolt} 131 | { 2, 0, 1, 5, 6, 4, 4 }, //layout 5 -> 5 cell {MAP,engload,coolant,oiltemp,tft,engload,rpm} 132 | { 1, 5, 6, 4, 0, 3, 4 }, //layout 6 -> 4 cell {coolant,oiltemp,tft,pcmvolt,map,endspd,pcmvolt} 133 | { 3, 2, 0, 4, 1, 5, 4 }, //layout 7 -> 4 cell {engspd,map,engload,pcmvolt,coolant,oil,pcmvolt} 134 | }; 135 | // User configuration here to change display > 136 | 137 | /*---------------------------*/ 138 | //if NO DATA value will set to 0 to skip reading 139 | uint8_t layout = 0; //pidInCelltype 0-4 EEPROM 0x00 140 | const uint8_t max_layout = array_length(pidInCell); //total 5 type of display page 141 | uint8_t pidIndex = 0; //point to PID List 142 | const uint8_t maxpidIndex = array_length(pidConfig); //total 7 pids in picConfig 143 | bool prompt = false; //bt elm ready flag 144 | bool skip = false; //pid skip flag 145 | uint8_t A = 0; //get A 146 | uint8_t B = 0; //get B 147 | 148 | //Global variable 149 | const uint32_t SLEEP_DURATION = 180 * 1000000; //180 sec* us = 3 min 150 | String line[12] = { "", "", "", "", "", "", "", "", "", "", "", "" }; //buffer for each line in terminal function 151 | bool dim = false; //back light dim flag 152 | const uint8_t LIGHT_LEVEL = 40; //backlight control by light level Higher = darker light 153 | long runtime = 0; //to check FPS 154 | long holdtime = 0; //hold time for button pressed check 155 | const String compile_date = __DATE__ " - " __TIME__; //get built date and time 156 | bool press = false; // button press flag 157 | bool showsystem = false; //show fps and temp EEPROM 0x01 158 | uint8_t pidRead = 0; //counter that hold number of pids been read to check pid/sec 159 | String serial_no = ""; //keep serial no. 160 | 161 | //CPU Temp 162 | const uint8_t tempOverheat = 60; //max operating cpu temp 60c 163 | const int8_t factoryTempOffset = -50; //default offset adjustment temp up to each tested esp32 max at -50 164 | float tempRead = 0.0; //current reading cpu temp 165 | int8_t tempOffset = 0; //offset adjustment temp up to each tested esp32 166 | uint8_t temp_read_delay = 0; //hold delay counter to read temp 167 | uint8_t temp_overheat_count = 0; //counter to read continuiosly overheat 168 | 169 | //BLUETOOTH 170 | String bt_message = ""; //bluetooth message buffer 171 | String se_message = ""; //serial port message buffer 172 | String deviceName[8] = { "", "", "", "", "", "", "", "" }; //discoveryBT name 173 | String deviceAddr[8] = { "", "", "", "", "", "", "", "" }; //discover BT addr 174 | uint8_t btDeviceCount = 0; //discovered bluetooth devices counter 175 | #define BT_DISCOVER_TIME 5000 //bluetooth discoery time 176 | esp_bd_addr_t client_addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //obdII mac addr 177 | esp_bd_addr_t recent_client_addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; //keep last btaddr in RTC memory 178 | const String client_name = "OBDII"; //adaptor name to search 179 | esp_spp_sec_t sec_mask = ESP_SPP_SEC_NONE; // or ESP_SPP_SEC_ENCRYPT|ESP_SPP_SEC_AUTHENTICATE to request pincode confirmation 180 | esp_spp_role_t role = ESP_SPP_ROLE_SLAVE; // or ESP_SPP_ROLE_MASTER 181 | bool foundOBD2 = false; 182 | BluetoothSerial BTSerial; //bluetooth serial device 183 | 184 | //---- Include Header File --- 185 | #include "image.h" 186 | #include "touchscreen.h" 187 | 188 | /*----------- global function ---------*/ 189 | 190 | //------------------------------------------------------------------------------------------ 191 | void checkCPUTemp() { //temperature can read only when BT or Wifi Connected 192 | temp_read_delay++; 193 | if ((temp_read_delay >= 50) && (foundOBD2)) { //delay loop 50 then read temp, avoid error eading 194 | temp_read_delay = 0; 195 | uint8_t temperature = temprature_sens_read(); //read internal temp sensor 196 | tempRead = (temperature - 32) / 1.8 + tempOffset; //change unit F to C 197 | #ifdef SERIAL_DEBUG 198 | Serial.print(tempRead, 1); //print Celsius temperature and tab 199 | Serial.println("°c"); 200 | #endif 201 | //read 10 times to confirm it's really overtemp. 202 | if (tempRead >= tempOverheat) temp_overheat_count++; //count overheat time 203 | else temp_overheat_count = 0; //reset 204 | if (temp_overheat_count > 10) { //overheat read more than 10 times. 205 | Serial.printf("CPU Overheat Shutdown at %d°C\n", tempOverheat); 206 | tft.fillRectVGradient(0, 0, 320, 240, TFT_RED, TFT_BLACK); 207 | tft.pushImage(127, 5, 64, 64, overheat, TFT_RED); //show sdcard icon 208 | tft.setTextColor(TFT_WHITE); 209 | tft.drawCentreString("CPU Overheat Shutdown!", 159, 75, 4); 210 | tft.setTextColor(TFT_WHITE, TFT_BLUE); 211 | tft.drawCentreString("Auto-restart within 3 minutes", 159, 140, 4); 212 | tft.drawCentreString("OR", 159, 170, 4); 213 | tft.drawCentreString("Press button to restart", 159, 200, 4); 214 | ledcWriteTone(buzzerChannel, 1500); 215 | delay(5000); 216 | ledcWriteTone(buzzerChannel, 0); 217 | BTSerial.disconnect(); //disconnect bluetooth 218 | tft.fillScreen(TFT_BLACK); 219 | tft.writecommand(0x10); //TFT sleep 220 | //enter deep sleep 221 | esp_sleep_enable_timer_wakeup(SLEEP_DURATION); //sleep timer 3 min 222 | esp_sleep_enable_ext0_wakeup(GPIO_NUM_27, LOW); //wake when button pressed 223 | esp_deep_sleep_start(); //sleep shutdown backlight auto off with esp32 224 | } //if temp_overheat_coun 225 | } //if temp_read_delay 226 | } //check CPU Temp 227 | /*------------------*/ 228 | //TERMINAL terminal for debugging display text 229 | void Terminal(String texts, uint16_t x, uint16_t y, uint16_t w, uint16_t h) { 230 | uint8_t max_line = round((h - y) / 16.0); 231 | tft.fillRect(x, y, x + w, y + h, TFT_BLACK); 232 | tft.setTextColor(TFT_WHITE); 233 | for (uint8_t i = 0; i < max_line; i++) { 234 | if (i <= max_line - 2) { 235 | line[i] = line[i + 1]; 236 | } else { 237 | line[i] = texts; 238 | } 239 | tft.drawString(line[i], x, i * 20 + y, 2); 240 | } //for 241 | bt_message = ""; //reset bt message 242 | } 243 | /*------------------*/ 244 | 245 | void checkGenuine() { //check if genuine obd2 gauge - must flash "gauge_factory_init.ino" first 246 | //security {serialno: "V&C-OBDII-001"} 247 | pref.begin("security", true); //read only 248 | serial_no = pref.getString("serialno", ""); //if no serial no, then empty 249 | Serial.print(F("Serial No: ")); 250 | if (serial_no == "") { 251 | tft.setTextColor(TFT_WHITE, TFT_RED); 252 | tft.drawString("Not genuine Va&Cob Gauge!", 0, 115, 4); 253 | Serial.println(F("Not genuine Va&Cob Gauge!")); 254 | while (true) { //beep 255 | ledcWriteTone(buzzerChannel, 2000); 256 | digitalWrite(LED_RED_PIN, LOW); //red on 257 | delay(500); 258 | ledcWriteTone(buzzerChannel, 0); 259 | digitalWrite(LED_RED_PIN, HIGH); //red off 260 | delay(500); 261 | } 262 | } else { 263 | Serial.println(serial_no); //print serial no 264 | pref.end(); 265 | } 266 | } 267 | //---------------------------------------------- 268 | 269 | //---- Include Header File -------------------- 270 | //#include "image.h" 271 | #include "bluetooth.h" 272 | #include "meter.h" 273 | #include "config.h" 274 | 275 | //----SET UP ----------------------------------- 276 | void setup() { 277 | //pin configuration 278 | analogSetAttenuation(ADC_0db); // 0dB(1.0 ครั้ง) 0~800mV for LDR 279 | pinMode(LDR_PIN, ANALOG); //ldr analog input read brightness 280 | pinMode(BUZZER_PIN, OUTPUT); //speaker 281 | pinMode(SELECTOR_PIN, INPUT_PULLUP); //button 282 | pinMode(LED_RED_PIN, OUTPUT); //red 283 | pinMode(LED_GREEN_PIN, OUTPUT); //green 284 | pinMode(LED_BLUE_PIN, OUTPUT); //blue 285 | digitalWrite(LED_RED_PIN, LOW); //on 286 | digitalWrite(LED_GREEN_PIN, HIGH); //off 287 | digitalWrite(LED_BLUE_PIN, HIGH); //off 288 | //pwm setup 289 | ledcSetup(buzzerChannel, 1500, 10); //buzzer 10 bit 290 | ledcSetup(backlightChannel, 12000, 8); //backlight 8 bit 291 | ledcAttachPin(BUZZER_PIN, buzzerChannel); //attach buzzer 292 | 293 | //init communication 294 | Serial.begin(115200); 295 | //memory check 296 | psramInit(); 297 | Serial.println(F("\n---------------------------------------\n")); 298 | Serial.printf("Free Internal Heap %d/%d\n", ESP.getFreeHeap(), ESP.getHeapSize()); 299 | Serial.printf("Free SPIRam Heap %d/%d\n", ESP.getFreePsram(), ESP.getPsramSize()); 300 | Serial.printf("ChipRevision %d, Cpu Freq %d, SDK Version %s\n", ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion()); 301 | Serial.printf("Flash Size %d, Flash Speed %d\n", ESP.getFlashChipSize(), ESP.getFlashChipSpeed()); 302 | Serial.println(F("\n---------------------------------------\n")); 303 | Serial.println(F("<< Va&Cob OBDII Gauge >>")); 304 | Serial.print(F("by Ratthanin W. BUILD -> ")); 305 | Serial.println(compile_date); 306 | 307 | 308 | 309 | //init TFT display 310 | tft.init(); 311 | tft.setRotation(1); //landcape 312 | // Start the SPI for the touchscreen and init the touchscreen 313 | touchscreenSPI.begin(XPT2046_CLK, XPT2046_MISO, XPT2046_MOSI, XPT2046_CS); 314 | ts.begin(touchscreenSPI); 315 | ts.setRotation(1); 316 | 317 | touch_calibrate(); //hold button at start to calibrate touch 318 | //testTouch();//test touchscreen 319 | 320 | tft.setSwapBytes(true); //to display correct image color 321 | show_spiffs_jpeg_image("/vaandcob.jpg", 0, 0);// display logo image 322 | delay(3000); 323 | 324 | //backlight ledcAttachPin must be set after tft.init() 325 | ledcAttachPin(TFT_BL, backlightChannel); //attach backlight 326 | for (uint8_t i = 255; i > 0; i--) { //fading effect 327 | ledcWrite(backlightChannel, i); //full bright 328 | delay(5); 329 | } 330 | digitalWrite(LED_RED_PIN, HIGH); //red off 331 | tft.fillScreen(TFT_BLACK); //show start page 332 | tft.fillRectVGradient(0, 0, 320, 26, TFT_YELLOW, 0x8400); 333 | tft.setTextColor(TFT_BLACK); 334 | tft.pushImage(0, 0, 320, 25, obd2gauge); //show logo 335 | ledcWrite(backlightChannel, 255); //full bright 336 | tft.setTextColor(TFT_WHITE, TFT_RED); 337 | tft.drawRightString(" Config button ->", 319, 26, 2); 338 | String txt = " BUILD : " + compile_date; 339 | tft.setTextColor(TFT_WHITE, TFT_BLUE); 340 | tft.drawString(txt, 0, 26, 2); 341 | 342 | // checkGenuine();//check guniune 343 | //init variable 344 | pref.begin("setting", false); 345 | /* Create a namespace called "setting" with read/write mode 346 | { pref.get...(key,default) 347 | layout : UShort 348 | showsystem: bool 349 | tempOffset : uint16_t 350 | warning value: uint16_t 351 | recent_client_addr : {0x00,0x00,0x00,0x00,0x00,0x00} array of bytes[6] 352 | }*/ 353 | layout = pref.getUShort("layout", 0); //load layout setting from NVR 354 | if (layout > max_layout) layout = 0; 355 | showsystem = pref.getBool("showsystem", false); //load showsytem 356 | tempOffset = pref.getInt("tempOffset", factoryTempOffset); //load defaultTempoffset 357 | #ifdef SERIAL_DEBUG 358 | Serial.printf("Show System: %d\n", showsystem); 359 | Serial.printf("Temp Offset: %d\n", tempOffset); 360 | #endif 361 | for (uint8_t i = 0; i < maxpidIndex; i++) { //read warning value from pid name if not found load from default 362 | warningValue[i] = pref.getString(pidConfig[i][0].c_str(), pidConfig[i][8]); 363 | } 364 | pref.getBytes("recent_client", recent_client_addr, pref.getBytesLength("recent_client")); //read last bt address 365 | ecu_off_volt = pref.getFloat("ecu_off_volt", factoryECUOff); //read ecu 0ff voltage 366 | 367 | 368 | //start bluetooth 369 | if (!BTSerial.begin("Va&Cob OBDII Gauge", true)) { 370 | Serial.println(F("Bluetooth..error!")); 371 | Terminal("Bluetooth..error!", 0, 48, 320, 191); 372 | abort(); 373 | } else { 374 | Serial.println(F("Bluetooth..OK")); 375 | Terminal("Bluetooth..OK", 0, 48, 320, 191); 376 | } 377 | runtime = millis(); 378 | #ifdef SKIP_CONNECTION 379 | foundOBD2 = true; 380 | #endif 381 | 382 | //Connect to ELM327 383 | connectLastOBDII(); //try connect last BT 384 | while (!foundOBD2) { //not success try scan and connect another OBD2 385 | scanBTdevice(); 386 | autoDim(); //auto backlight handle 387 | //checkCPUTemp(); //check temp never work if BT or WIFI not connected 388 | if (digitalRead(SELECTOR_PIN) == LOW) { //button pressed 389 | if (!press) { 390 | press = true; //set flag 391 | holdtime = millis(); //set timer 392 | } else if (holdtime - millis() > 500) { //press once and longer 393 | configMenu(); //open config menu 394 | press = false; //reset flag 395 | } //else if holdtimer > 30000 396 | delay(200); //delay avoid bounce 397 | } 398 | } 399 | 400 | //Initialize ELM327 401 | Terminal("Initializing...", 0, 48, 320, 191); 402 | bt_message = ""; 403 | for (uint8_t initIndex = 0; initIndex < elm327InitCount; initIndex++) { //send init ELM327 AT command 404 | BTSerial.print(elm327Init[initIndex] + "\r"); 405 | if (initIndex == elm327InitCount - 1) delay(2000); //wait for 0100 Searching..protocal 406 | else delay(100); //must add delay , unless will skip BTSerial.avaialble>0 407 | while (BTSerial.available() > 0) { 408 | char incomingChar = BTSerial.read(); 409 | if (incomingChar == '>') { //check prompt 410 | BTSerial.flush(); //clear buffer 411 | #ifdef SERIAL_DEBUG 412 | Serial.println(bt_message); 413 | #endif 414 | #ifdef TERMINAL 415 | Terminal(bt_message, 0, 48, 320, 191); 416 | #endif 417 | if (initIndex == 1) BTSerial.print("\r"); //ATBRD23 response 418 | bt_message = ""; 419 | } else { 420 | bt_message.concat(incomingChar); //keep elm327 respond 421 | } 422 | } //while 423 | } //for 424 | 425 | initScreen(); 426 | beepbeep(); 427 | prompt = true; 428 | //---------------------- 429 | } //setup 430 | 431 | /*###################################*/ 432 | void loop() { 433 | //SCAN BUTON (button press HOLD to config menu) 434 | if (digitalRead(SELECTOR_PIN) == LOW) { //button pressed 435 | if (!press) { 436 | press = true; //set flag 437 | holdtime = millis(); //set timer 438 | } else if (holdtime - millis() > 3000) { //press once and longer than 3 sec 439 | configMenu(); //open config menu 440 | press = false; //reset flag 441 | if (foundOBD2) initScreen(); //open new layout screen 442 | } //else if holdtimer > 30000 443 | delay(200); //delay avoid bounce 444 | } else { //button release 445 | if (press) { //change layout next page 446 | layout++; //change to next layout page 447 | if (layout == max_layout) layout = 0; 448 | ledcWriteTone(buzzerChannel, 5000); //play click sound 449 | delay(5); 450 | ledcWriteTone(buzzerChannel, 0); 451 | initScreen(); //open meter screen 452 | //reset variable 453 | for (uint8_t i = 0; i < maxpidIndex - 1; i++) old_data[i] = 0.0; 454 | engine_off_count = 0; 455 | BTSerial.flush(); //clear tx 456 | while (BTSerial.available() > 0) { //clear rx buffer 457 | BTSerial.read(); 458 | } 459 | bt_message = ""; 460 | skip = false; 461 | pidIndex = maxpidIndex - 1; //will be +1 to be 0 462 | press = false; //reset flag 463 | prompt = true; //to trig reading elm again 464 | delay(200); //delay avoid bounce 465 | 466 | } //if press 467 | 468 | } //else digitalRead 469 | //---------------------- 470 | //BLUETOOTH read 471 | while (BTSerial.available() > 0) { 472 | char incomingChar = BTSerial.read(); 473 | if (incomingChar == '>') { //check prompt 474 | BTSerial.flush(); //clear tx 475 | #ifdef SERIAL_DEBUG 476 | Serial.println(bt_message); 477 | #endif 478 | #ifdef TERMINAL //for degbugging 479 | getAB(bt_message); 480 | Terminal(bt_message + "->" + String(A) + "," + String(B), 0, 48, 320, 191); 481 | #endif 482 | prompt = true; //pid response ready to calculate & display 483 | } else { 484 | bt_message.concat(incomingChar); //keep elm327 respond 485 | } 486 | } 487 | //---------------------- 488 | //SERIAL PORT read 489 | if (Serial.available() > 0) { 490 | char incomingChar = Serial.read(); 491 | //check CR/NL 492 | if ((incomingChar != '\r') && (incomingChar != '\n')) { 493 | se_message.concat(incomingChar); 494 | //Serial.print(incomingChar,HEX); 495 | } else { 496 | 497 | if (se_message != "") { 498 | #ifdef TERMINAL 499 | Terminal(se_message, 0, 48, 320, 191); //display terminal 500 | #endif 501 | BTSerial.print(se_message + "\r"); 502 | se_message = ""; 503 | Serial.flush(); 504 | } //if se_message 505 | 506 | } //else if incomingChar 507 | } //if Serial.available 508 | 509 | //Send another PID to elm327 510 | if (prompt) { //> ELM327 ready! -> request next PID 511 | prompt = false; //no prompt from ELM327 512 | checkCPUTemp(); //check temperature. 513 | if (!skip) { 514 | updateMeter(pidIndex, bt_message); //update meter screen 515 | pidRead++; //for calculate pid/s 516 | } 517 | pidIndex++; //point to next pid 518 | if (pidIndex >= maxpidIndex) { 519 | pidIndex = 0; //back to pid 0 520 | autoDim(); //auto backlight handle 521 | if (showsystem) { 522 | String status = String(pidRead * 1000.0 / (millis() - runtime), 0) + " p/s " + String(tempRead, 1) + "`c"; 523 | tft.setTextColor(TFT_YELLOW, TFT_BLACK); 524 | tft.drawCentreString(status.c_str(), 159, 0, 2); 525 | runtime = millis(); 526 | } //if showsystem 527 | pidRead = 0; //reset pid read counter 528 | } 529 | if (pidCurrentSkip[pidIndex] <= 0) { //end of skip loop , go next index 530 | pidCurrentSkip[pidIndex] = pidReadSkip[pidIndex]; //read max delay loop to current 531 | skip = false; 532 | BTSerial.print(pidList[pidIndex] + "\r"); //send PID request 533 | } else { //skip sending request 534 | pidCurrentSkip[pidIndex] = pidCurrentSkip[pidIndex] - 1; //decrese loop count 535 | prompt = true; 536 | skip = true; 537 | } //else 538 | 539 | } //if prompt 540 | 541 | /*------------------*/ 542 | } // loop 543 | -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/bluetooth.h: -------------------------------------------------------------------------------- 1 | /* Bluetooth function */ 2 | //---------------------------------- 3 | //convert bt address to text 4 | //{0x00,0x1d,0xa5,0x00,0x12,0x92} -> 00:1d:a5:00:12:92 5 | String ByteArraytoString(esp_bd_addr_t bt_address) { 6 | String txt = ""; 7 | String nib = ""; 8 | for (uint8_t i=0;i(pDevice); 29 | // btDeviceList[pDevice->getAddress()] = * set; 30 | String txt = pDevice->toString().c_str(); 31 | Serial.printf("Found a new device: %s\n", pDevice->toString().c_str()); 32 | deviceName[btDeviceCount] = pDevice->getName().c_str(); 33 | deviceAddr[btDeviceCount] = pDevice->getAddress().toString().c_str(); 34 | btDeviceCount++; 35 | } ) 36 | ) { 37 | delay(BT_DISCOVER_TIME); 38 | BTSerial.discoverAsyncStop(); 39 | #ifdef SERIAL_DEBUG 40 | Serial.println("Discovering stopped"); 41 | #endif 42 | digitalWrite(LED_BLUE_PIN,HIGH);//blue led off 43 | delay(1000);//redsicovery delay 44 | 45 | } else { 46 | #ifdef SERIAL_DEBUG 47 | Serial.println("Error on discovering bluetooth clients."); 48 | #endif 49 | } 50 | 51 | String txt = "Found "+String(btDeviceCount)+" device(s)"; 52 | Terminal(txt,0,48,320,191); 53 | 54 | //matching scan obd2 and config obd2 55 | for (uint8_t i=0;i {0x00,0x1d,0xa5,0x00,0x12,0x92}; 64 | //copy match bt mac address to client_name to connect 65 | String str = deviceAddr[i]; 66 | uint8_t StringCount = 0; 67 | while (str.length() > 0) { 68 | int8_t index = str.indexOf(':'); 69 | if (index == -1) {// No : found 70 | client_addr[StringCount] = strtol(str.c_str(), 0, 16);//convert hex string to byte 71 | str = "";//set length to 0; 72 | //exit loop while 73 | } else { 74 | client_addr[StringCount] = strtol(str.substring(0, index).c_str(),0,16);//convert hex string to byte 75 | StringCount++; 76 | str = str.substring(index+1); 77 | } 78 | }//while mac address copied to client_addr for connect below code 79 | if (ByteArraytoString(client_addr) != ByteArraytoString(recent_client_addr)) { 80 | pref.putBytes("recent_client",client_addr,sizeof(client_addr));//save new bt addr to pref. 81 | Serial.println(F("Save a new BT client address")); 82 | } 83 | } else //not match 84 | foundOBD2 = false; 85 | 86 | }//for loop list device 87 | 88 | 89 | //connect to obd2 90 | if (foundOBD2) { 91 | txt = "Connecting to " + client_name +" - " + ByteArraytoString(client_addr); 92 | Terminal(txt,0,48,320,191); 93 | Serial.println(txt); 94 | BTSerial.connect(client_addr, 0, sec_mask, role);//connect to OBDII adaptor 95 | bool blink = false; 96 | while(!BTSerial.connected(1000)) { 97 | Serial.print(F(".")); 98 | blink =! blink; 99 | digitalWrite(LED_BLUE_PIN,blink);//blue led offset 100 | if (blink) tft.setTextColor(TFT_BLUE,TFT_BLACK); 101 | else tft.setTextColor(TFT_BLACK,TFT_BLACK); 102 | tft.drawRightString("$",319,210,4); 103 | } 104 | 105 | Terminal("Connected Successfully!",0,48,320,191); 106 | Serial.println(F("Connected Successfully!")); 107 | prompt = true; 108 | digitalWrite(LED_GREEN_PIN, LOW);//green led 109 | 110 | } else { 111 | Terminal("OBDII Adaptor not found!",0,48,320,191); 112 | Serial.println(F("OBDII Adaptor not found!")); 113 | } //foundOBD2 114 | }//scanBTDevice 115 | 116 | //--------------------------- 117 | //connect to recent obdII for fast connection, skip scanning 118 | void connectLastOBDII() { 119 | digitalWrite(LED_BLUE_PIN,LOW);//blue led on 120 | String txt = "Connecting to " + client_name +" - " + ByteArraytoString(recent_client_addr); 121 | Terminal(txt,0,48,320,191); 122 | Serial.println(txt); 123 | BTSerial.connect(recent_client_addr, 0, sec_mask, role);//connect to OBDII adaptor 124 | uint8_t try_count = 0; 125 | bool blink = false; 126 | while(!BTSerial.connected(1000) && (try_count <3)) { 127 | Serial.print(F(".")); 128 | blink =! blink; 129 | digitalWrite(LED_BLUE_PIN,blink);//blue led offset 130 | try_count++; 131 | } 132 | if (try_count == 3) {//cannot connect 133 | Terminal("OBDII Adaptor not found!",0,48,320,191); 134 | Serial.println(F("OBDII Adaptor not found!")); 135 | BTSerial.disconnect(); 136 | foundOBD2 = false; 137 | } else { 138 | Terminal("Connected Successfully!",0,48,320,191); 139 | Serial.println(F("Connected Successfully!")); 140 | prompt = true; 141 | digitalWrite(LED_GREEN_PIN, LOW);//green led 142 | foundOBD2 = true; 143 | } 144 | 145 | }//connectLasbtOBDII -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/build/esp32.esp32.esp32/VaAndCobOBD2Gauge.ino.bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/VaAndCobOBD2Gauge/build/esp32.esp32.esp32/VaAndCobOBD2Gauge.ino.bootloader.bin -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/config.h: -------------------------------------------------------------------------------- 1 | /* "config.h" 2 | this header file is for configuration menu 3 | */ 4 | //freeRTOS multi tasking 5 | #include 6 | #include 7 | #include 8 | 9 | #include "music.h" //songs data 10 | #include "nes_audio.h" //nes audio player 11 | #include "firmware_update.h" //firmware updater function 12 | #include "elm327.h"// read VIN function 13 | 14 | #define mypic_filename "/mypic.jpg"//mypic file 15 | #define FORMAT_SPIFFS_IF_FAILED true//format if no spiffs 16 | 17 | 18 | Cartridge player(BUZZER_PIN);//not specified 4 pin , use 1 pin at buzzer pin 19 | 20 | //configuration menu 21 | const String menuList[8] = { 22 | "Show pid/s & CPU Temp",//toggle show system 23 | "Load my pic 320x240 px",//load user own pic 24 | "Update firmware",//update new firmware 25 | "Warning setting",//setting warning parameter (warning data, offset temp) 26 | "DTCs",//dtc engine 27 | "Auto-off",//auto turn off 28 | "About",//about 29 | "Exit" 30 | }; 31 | const uint8_t maxMenu = array_length(menuList);//length of menu list 32 | 33 | //menu list for warning setting 34 | const String parameterList[8][2] = { 35 | {"CPU Overheat Temperature "+String(tempOverheat)+"`c ,offset "," `c "}, 36 | {"- Engine Load"," % "}, 37 | {"- Coolant Temperature"," `c "}, 38 | {"- Manifold Air Pressure"," psi "}, 39 | {"- Engine Speed"," rpm "}, 40 | {"- PCM Voltage"," volt"}, 41 | {"- Engine Oil Temperature"," `c "}, 42 | {"- Transmission Fluid Temperature"," `c "} 43 | }; 44 | 45 | //increasement and decreasement of each PID 46 | const int pidWarnStep[8] = {1,5,1,5,500,1,1,1}; 47 | 48 | //Sprites 49 | TFT_eSprite car = TFT_eSprite(&tft); 50 | TFT_eSprite bk = TFT_eSprite(&tft); 51 | TFT_eSprite pole = TFT_eSprite(&tft); 52 | TFT_eSprite fence = TFT_eSprite(&tft); 53 | 54 | //------ NES Audio play on Task0 55 | TaskHandle_t TaskHandle0 = NULL;//create taskhandle0 56 | 57 | void TaskPlayMusic(void *pvParameters) { 58 | esp_task_wdt_init(62, false);//62 sec wd timer / disable wd 59 | while(1) { 60 | // player.frame_counter_cb(danceLight);//cal back fucntion (not use) 61 | player.play_nes(song , true, 0.25); //play nes music, loop, volume 0.25, 62 | 63 | } 64 | } 65 | //---------------------------- 66 | void animation() {//start field effect 67 | //sprite variable 68 | #define BK_HEIGHT 80 69 | #define BK_WIDTH 235 70 | uint8_t cur_x = 68; 71 | uint8_t cur_y = 10; 72 | uint8_t dash_x = 20; 73 | int16_t tag_x = 235; 74 | int16_t fence_x = 0; 75 | uint8_t ani_speed = 2; 76 | 77 | //create spirtes 78 | bk.createSprite(BK_WIDTH,BK_HEIGHT); 79 | 80 | car.createSprite(CAR_WIDTH,CAR_HEIGHT);//image size 114x50 81 | car.setSwapBytes(true); 82 | car.pushImage(0,0,CAR_WIDTH,CAR_HEIGHT,futurecar);//image size 114x50 83 | 84 | pole.createSprite(POLE_WIDTH,POLE_HEIGHT); 85 | pole.setSwapBytes(true); 86 | pole.pushImage(0,0,POLE_WIDTH,POLE_HEIGHT,pole1); 87 | 88 | fence.createSprite(FENCE_WIDTH,FENCE_HEIGHT); 89 | fence.setSwapBytes(true); 90 | fence.pushImage(0,0,FENCE_WIDTH,FENCE_HEIGHT,fence1); 91 | //------------------------------- 92 | tft.fillScreen(TFT_BLACK);//clear screen 93 | //draw QRCode 94 | tft.drawBitmap(0, 159, qrcode, QRCODE_WIDTH, QRCODE_HEIGHT, TFT_WHITE); 95 | //draw text 96 | tft.setTextColor(TFT_WHITE); 97 | tft.drawString(">MAP/ENG LOAD/ECT/EOT/TFT/ENG SPD/PCM Volt",0,40,2); 98 | tft.drawString(">Warning, Automatic Dim/OnOff/O็verheat Shutdown",0,60,2); 99 | tft.drawString(">Read DTC Code & Clear MIL Status",0,80,2); 100 | tft.drawString("* FW-\"VaandCobOBD2Gauge.bin\" * Image-\"mypic.jpg\"",0,100,2); 101 | tft.drawString("* Facebook : www.facebook.com/vaandcob",0,120,2); 102 | tft.setTextColor(TFT_YELLOW); 103 | tft.drawString(" [ Manual ] ----------------[ Press button to exit ]",0,140,2); 104 | tft.setTextColor(TFT_CYAN); 105 | String txt = "[ "+serial_no+" ] BUILD : "+compile_date; 106 | tft.drawString(txt,0,0,2); 107 | //get VIN 108 | getPID("ATE0");//force echo off 109 | txt = "VIN : " + getVIN(getPID("0902")); 110 | tft.setTextColor(TFT_GREENYELLOW); 111 | tft.drawString(txt,0,20,2); 112 | 113 | bk.setTextColor(TFT_BLACK,TFT_ORANGE); 114 | //loop draw Animation 115 | while (digitalRead(SELECTOR_PIN) == HIGH) {//exit if press button 116 | 117 | //draw road 118 | bk.fillSprite(0x4228);//draw road 119 | bk.fillRect(0,0,235,16,TFT_LIGHTGREY); 120 | bk.fillRect(0,16,235,3,0x8430); 121 | 122 | //draw dash line 123 | for(int16_t i = 0; i < 5; i++) { 124 | //speed //space //posY //length 125 | // bk.drawFastHLine(dash_x +(40 * i), 20,10,TFT_WHITE);//far end dash line 126 | bk.drawFastHLine((dash_x-20)*1.5+(60 * i),45,15,TFT_WHITE);//middle dash line 127 | // bk.drawFastHLine((dash_x-40)*2.0+(80 * i),79,20,TFT_WHITE);//nearest dash line 128 | } 129 | dash_x = dash_x - ani_speed; 130 | if (dash_x <= 0 ) dash_x = 40; 131 | 132 | //draw traffic 133 | tag_x = tag_x - ani_speed; 134 | if (tag_x <= -80) tag_x = 235; 135 | //bk.setTextColor(random(0xffff)); 136 | bk.drawString("< Thank You >",tag_x,0,2);//draw billboard 137 | 138 | //draw car 139 | int8_t move = random(-1,2); 140 | cur_x = cur_x + move; 141 | move = random(-1,2); 142 | cur_y = cur_y + move; 143 | if (cur_x > 235-CAR_WIDTH) cur_x = 235-CAR_WIDTH; 144 | if (cur_x < 1) cur_x = 1; 145 | if (cur_y < 1) cur_y = 1; 146 | if (cur_y > 79-CAR_HEIGHT) cur_y = 79-CAR_HEIGHT; 147 | car.pushToSprite(&bk,cur_x,cur_y,TFT_BLACK);//put car into bk, white color as transparent 148 | 149 | //draw fence 150 | for(int i=0;i<8;i++)//7 sprites 151 | fence.pushToSprite(&bk,fence_x*2+(FENCE_WIDTH*i),80-FENCE_HEIGHT,TFT_BLACK); 152 | fence_x = fence_x - ani_speed; 153 | if (fence_x <= -FENCE_WIDTH + ani_speed) fence_x = 0;//loop back 154 | 155 | //draw pole 156 | pole.pushToSprite(&bk,tag_x*2,80-POLE_HEIGHT,TFT_BLACK);//draw bush 157 | 158 | //show background sprite 159 | bk.pushSprite(85,159); 160 | //led blinking effect 161 | digitalWrite(LED_RED_PIN, random(0,2)); 162 | digitalWrite(LED_GREEN_PIN, random(0,2)); 163 | digitalWrite(LED_BLUE_PIN, random(0,2)); 164 | checkCPUTemp(); 165 | autoDim(); 166 | } 167 | //remove sprites 168 | car.deleteSprite(); 169 | bk.deleteSprite(); 170 | pole.deleteSprite(); 171 | fence.deleteSprite(); 172 | }//task star field 173 | 174 | /*------------------*/ 175 | //highlight and change warning each menu 176 | void setWarning(int8_t index, int8_t change) { 177 | for (uint8_t i = 0;i max) tempOffset = max; 204 | } 205 | String result = String(tempOffset); 206 | switch (result.length()) { 207 | case 1: result = " " + result; break; 208 | case 2: result = " " + result; break; 209 | case 3: result = " " + result; break; 210 | }//switch 211 | tft.drawRightString(result,285,57+(index*16),2);//cpu offset 212 | } else {//other pid 213 | 214 | int min = pidConfig[index-1][4].toInt();//get min 215 | int max = pidConfig[index-1][5].toInt();//get max 216 | if (change == -1) {//decrease 217 | warningValue[index-1] = String(warningValue[index-1].toInt() - pidWarnStep[index]); 218 | if (warningValue[index-1].toInt() < min) warningValue[index-1] = String(min); 219 | } 220 | if (change == 1) {//increase 221 | warningValue[index-1] = String(warningValue[index-1].toInt() + pidWarnStep[index]); 222 | if (warningValue[index-1].toInt() > max) warningValue[index-1] = String(max); 223 | } 224 | String result = warningValue[index-1]; 225 | switch (result.length()) { 226 | case 1: result = " " + result; break; 227 | case 2: result = " " + result; break; 228 | case 3: result = " " + result; break; 229 | }//switch 230 | tft.drawRightString(result,285,57+(index*16),2);//pid warning value 231 | } //if i = 0 232 | delay(300);//touch screen delay 233 | } 234 | /*--------------------*/ 235 | void loadMyPic() { 236 | if(!SD.begin()) {//sdcard not attach 237 | tft.pushImage(129,44,60,60,sdcard);//show sdcard icon 238 | Serial.println(F("Micro SD Card not mounted!")); 239 | tft.setTextColor(TFT_WHITE,TFT_RED); 240 | tft.drawCentreString("Please insert Micro SD Card",159,120,4); 241 | } else {//sd card attached 242 | Serial.println(F("Micro SD Card..OK")); 243 | uint8_t cardType = SD.cardType(); 244 | if (cardType == CARD_NONE) {//bad card 245 | Serial.println(F("Micro SD Card Error!")); 246 | tft.setTextColor(TFT_WHITE,TFT_RED); 247 | tft.drawCentreString("Micro SD Card Error!",159,120,4); 248 | 249 | } else {//good sd card attached 250 | if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) { 251 | Serial.println(F("Error: SPIFFS failed!")); 252 | tft.setTextColor(TFT_WHITE,TFT_RED); 253 | tft.drawCentreString("Error: SPIFFS failed!",159,120,4); 254 | 255 | } else { 256 | tft.setTextColor(TFT_BLACK,TFT_GREEN); 257 | tft.drawCentreString("Copying...\"mypic.jpg\"",159,120,4); 258 | Serial.println(F("Deleting...\"mypic.jpg\"")); 259 | SPIFFS.remove(mypic_filename);//remove old file first 260 | File sourceFile = SD.open(mypic_filename); 261 | if (sourceFile) {//found file 262 | 263 | File destFile = SPIFFS.open(mypic_filename, FILE_WRITE); 264 | Serial.println(F("Copying...\"mypic.jpg\"")); 265 | tft.setTextColor(TFT_BLACK,TFT_GREEN); 266 | static uint8_t buf[512]; 267 | while(sourceFile.read( buf, 512) ) { 268 | destFile.write( buf, 512 );//copy file from sdcard to spiffs 269 | } 270 | sourceFile.close(); 271 | destFile.close(); 272 | //load mypic.jpg from spiffs to show on screen 273 | show_spiffs_jpeg_image(mypic_filename, 0, 0); 274 | 275 | } else { 276 | Serial.println(F("\"mypic.jpg\" not found!")); 277 | tft.pushImage(130,44,60,60,nofile);//show fileicon 278 | tft.setTextColor(TFT_WHITE,TFT_RED); 279 | tft.drawCentreString("\"mypic.jpg\" not found!",159,120,4); 280 | }//if sourcefile 281 | }//if SPIFFS.begin 282 | }//else sccard errror 283 | }//else sdcard attach 284 | SD.end(); 285 | SPIFFS.end(); 286 | beep(); 287 | }//loadmypic 288 | /*----------------*/ 289 | 290 | void listMenu(uint8_t choice) { 291 | //draw icon 292 | if (showsystem) tft.pushImage(0,35,25,25,switchon);//switchon image 293 | else tft.pushImage(0,35,25,25,switchoff);//switchon image 294 | tft.pushImage(0,65,25,25,image);//image 295 | tft.pushImage(0,95,25,25,cpu);//firmware 296 | tft.pushImage(0,125,25,25,warning);//parameter 297 | tft.pushImage(0,155,25,25,engine);//engine 298 | tft.pushImage(159,155,25,25,autooff);//off 299 | tft.pushImage(0,185,25,25,about);//about 300 | tft.pushImage(159,185,25,25,quit);//exit 301 | for (uint8_t i=0;i Press & Release Button",0,211,2); 311 | tft.drawString("Select -> Press & Hold Button",0,227,2); 312 | } 313 | 314 | /*----------------*/ 315 | void configMenu() {//control configuration menu 316 | uint8_t select = 0; 317 | bool pressed = false; 318 | long holdtimer = 0; 319 | bool currentFlag = showsystem;//keep current flag status 320 | beep(); 321 | tft.fillScreen(TFT_BLACK);//clear screen 322 | tft.fillRectVGradient(0, 0, 320, 26, TFT_ORANGE, 0xfc00); 323 | tft.setTextColor(TFT_BLACK); 324 | tft.drawCentreString("[--- Configuration Menu ---]",159,0,4); 325 | listMenu(select); 326 | delay(1000); 327 | while (true) {//loop check button press 328 | 329 | if (digitalRead(SELECTOR_PIN) == LOW) {//button pressed 330 | if (!pressed) { 331 | pressed = true;//set press flag 332 | holdtimer = millis(); 333 | } else if (holdtimer - millis() > 3000) {//hold 3 sec enter config menu 334 | //---------- TOGGLE SHOW SYSTEM STATUS ----------- 335 | if (select == 0) { 336 | beepbeep(); 337 | showsystem = !showsystem;//toggle 338 | tft.setTextColor(TFT_WHITE,TFT_BLUE); 339 | if (showsystem) tft.pushImage(0,35,25,25,switchon);//switchon image 340 | else tft.pushImage(0,35,25,25,switchoff);//switchon image 341 | 342 | } 343 | //--------- LOAD MY PIC ----------- 344 | if (select == 1) { 345 | clickSound(); 346 | tft.fillRect(0,30,320,239,TFT_BLACK); 347 | loadMyPic();//load pic from sdcard to spiffs 348 | tft.setTextColor(TFT_BLACK,TFT_WHITE); 349 | tft.drawCentreString("[- Press button to exit -]",159,215,4); 350 | while (digitalRead(SELECTOR_PIN) == HIGH) { 351 | //wait for button press to exit 352 | checkCPUTemp(); 353 | autoDim(); 354 | }//while 355 | clickSound(); 356 | tft.fillScreen(TFT_BLACK);//clear screen 357 | tft.setTextColor(TFT_BLACK,TFT_ORANGE); 358 | tft.drawCentreString("[--- Configuration Menu ---]",159,0,4); 359 | listMenu(select); 360 | } 361 | 362 | //--------- UPDATE FIRMWARE ----------- 363 | if (select ==2) { 364 | clickSound(); 365 | tft.fillRect(0,30,320,239,TFT_BLACK); 366 | tft.pushImage(56,44,60,60,sdcard);//show sdcard icon 367 | tft.pushImage(203,44,60,60,wifi);//show wifi icon 368 | tft.setTextColor(TFT_CYAN,TFT_BLACK); 369 | tft.drawCentreString("Touch an icon to choose",159,125,4); 370 | tft.drawCentreString("firmware update method.",159,155,4); 371 | tft.setTextColor(TFT_BLACK,TFT_WHITE); 372 | tft.drawCentreString("[- Press button to exit -]",159,215,4); 373 | delay(1000); 374 | while (digitalRead(SELECTOR_PIN) == HIGH) {//wait for button press to exit 375 | autoDim(); 376 | checkCPUTemp(); 377 | //touching 378 | uint16_t t_x = 0, t_y = 0; // To store the touch coordinates 379 | bool touched = getTouch(&t_x, &t_y); 380 | if (touched && (t_y > 44) && (t_y < 104)) { 381 | if ((t_x >= 56) && (t_x <116)) { 382 | clickSound(); 383 | updateFirmwareSD();//update by sd card 384 | } 385 | if ((t_x >= 203) && (t_x <263)) { 386 | clickSound(); 387 | updateFirmwareWIFI();//update by wifi 388 | break; 389 | } 390 | }//if touched 391 | delay(300);//touch screen delay 392 | }//while 393 | 394 | tft.fillRect(0,30,320,210,TFT_BLACK); 395 | listMenu(select); 396 | } 397 | 398 | //------------ WARNING SETTING ----------- 399 | if (select ==3) { 400 | int8_t warningMenuIndex = 0;//index for warning setting 401 | int8_t inc_dec = 0;//-1=decrease,0=no change, 1=increase 402 | clickSound(); 403 | tft.fillRect(0,30,320,210,TFT_BLACK); 404 | tft.setTextColor(TFT_WHITE,TFT_BLACK); 405 | tft.drawCentreString("[ Warning Parameter Setting]",159,30,2); 406 | tft.drawFastHLine(0,50,320,TFT_RED); 407 | tft.setTextColor(TFT_WHITE,TFT_RED); 408 | tft.drawCentreString("[- Press button to Save & Exit -]",159,190,2); 409 | for (uint8_t i = 0;i<5;i++) //draw buttons 410 | tft.fillRoundRect(i*64,211,60,30,5,TFT_NAVY); 411 | tft.setTextColor(TFT_WHITE); 412 | tft.drawCentreString("DEFAULT",31,217,2);//reset 413 | tft.drawCentreString("^ PREV",95,217,2);//prev 414 | tft.drawCentreString("NEXT v",159,217,2);//next 415 | tft.drawCentreString("-",223,206,6);//- 416 | tft.fillRect(286,219,3,16,TFT_WHITE);//+ 417 | tft.fillRect(280,225,16,3,TFT_WHITE);//+ 418 | setWarning(warningMenuIndex,inc_dec);//list menu first 419 | delay(1000); 420 | while (digitalRead(SELECTOR_PIN) == HIGH) {//wait for button press to exit 421 | autoDim(); 422 | checkCPUTemp(); 423 | String status = ""; 424 | if (foundOBD2) {//temperature read not working if not connect to bluetooh or 425 | status = " " +String(tempRead,1)+"`c"; 426 | } else { 427 | status = "--.-`c"; 428 | } 429 | tft.setTextColor(TFT_YELLOW,TFT_BLACK); 430 | tft.drawRightString(status.c_str(),319,30,2); //show cpu temp on top-right corner 431 | //touching 432 | uint16_t t_x = 0, t_y = 0; // To store the touch coordinates 433 | bool touched = getTouch(&t_x, &t_y); 434 | if (touched && (t_y > 211)) { 435 | clickSound(); 436 | #ifdef SERIAL_DEBUG 437 | Serial.printf("%d - %d\n",t_x,t_y); 438 | #endif 439 | if (t_x < 64) {//DEFAULT 440 | tempOffset = factoryTempOffset;//load default 441 | //read default warning value 442 | for (uint8_t i=0;i < maxpidIndex; i++) warningValue[i] = pidConfig[i][8]; 443 | } 444 | if ((t_x >= 64) && (t_x <128)) warningMenuIndex--;//prev 445 | if ((t_x >= 128) && (t_x <192)) warningMenuIndex++;//next 446 | if ((t_x >= 192) && (t_x <256)) inc_dec = -1;//decrease 447 | if ((t_x >= 256) && (t_x <320)) inc_dec = 1;//increase 448 | if (warningMenuIndex == maxpidIndex+1) warningMenuIndex = 0; 449 | if (warningMenuIndex < 0) warningMenuIndex = maxpidIndex; 450 | 451 | setWarning(warningMenuIndex,inc_dec);//update list 452 | inc_dec = 0;//reset 453 | 454 | }//if touched 455 | }//while 456 | //exit and save warning data to preference 457 | pref.putInt("tempOffset",tempOffset);//save tempoffset 458 | for (uint8_t i=0;i < maxpidIndex; i++) {//save all pid 459 | pref.putString(pidConfig[i][0].c_str(),warningValue[i]); 460 | } 461 | tft.setTextColor(TFT_BLACK,TFT_GREEN); 462 | tft.drawCentreString("Parameters Saved.",159,120,4); 463 | beep(); 464 | delay(1000); 465 | tft.fillRect(0,30,320,210,TFT_BLACK); 466 | listMenu(select); 467 | } 468 | 469 | //--------------- DTC ----------- 470 | if (select == 4) {//https://www.elmelectronics.com/wp-content/uploads/2016/07/ELM327DS.pdf 471 | 472 | clickSound(); 473 | tft.fillRect(0,30,320,239,TFT_BLACK); 474 | tft.pushImage(0,28,25,25,engine);//about 475 | tft.setTextColor(TFT_YELLOW,TFT_BLACK); 476 | tft.drawString("Diagnostic Trouble Codes",30,28,4); 477 | delay(500); 478 | getPID("ATE0");//force echo off 479 | //#1 get number of DTC & MIL status 480 | //getAB2("41 01 86 0E 88 88 41 01 81 0C 00 00 ","41","01");//(A = 80+1,80h=MIL ON,no of dtc = A-80h) 481 | uint8_t error_cnt = 0; 482 | uint8_t no_of_dtc = 0; 483 | A = 0xFF;//supoose error read A 484 | while ((A == 0xFF) && (error_cnt <3) ) {//if A == 0xFF repeat reading again (A must be 0x80+) try 3 times 485 | Serial.print(F("MIL Status 0101->")); 486 | getAB2(getPID("0101"),"41","01");//try read MIL status 487 | //Terminal(String(A).c_str(),0,88,320,125); 488 | error_cnt++; 489 | delay(100); 490 | } 491 | #ifdef TEST_DTC 492 | A = 0x86; 493 | #endif 494 | 495 | if (A == 0xFF) {//Error reading MIL status 496 | Serial.println(F("Error Reading MIL Status!")); 497 | tft.setTextColor(TFT_WHITE,TFT_RED); 498 | tft.drawCentreString("* Error Reading MIL Status! *",159,120,4); 499 | tft.setTextColor(TFT_LIGHTGREY,TFT_BLACK); 500 | tft.drawCentreString("[ Please try again ]",159,148,4); 501 | tft.setTextColor(TFT_BLACK,TFT_WHITE); 502 | tft.drawCentreString("[- Press button to exit -]",159,215,4); 503 | beep(); 504 | } else { 505 | if (A == 0x00) {//41 01 00 = Mil is OFF No DTC 506 | tft.setTextColor(TFT_GREEN,TFT_BLACK); 507 | tft.drawCentreString("MIL is OFF - No DTC",159,120,4); 508 | tft.setTextColor(TFT_BLACK,TFT_WHITE); 509 | tft.drawCentreString("[- Press button to exit -]",159,215,4); 510 | } else if (A >= 0x80) {//41 01 8x if A =>80h then MIL status ON else skip to NO MIL below 511 | Serial.print(F("A = "));Serial.println(A,HEX); 512 | // #2 get Raw Diagnostic Troulbe Code from ELM327 513 | //(for 1 dtc)43 06 00 7D 514 | //(more than 1 dtc) 00E 0: 43 06 00 7D C6 93 1: 01 08 C6 0F 02 E9 02 e: E0 CC CC CC CC CC CC 43 01 C4 01 515 | uint8_t error_cnt = 0; 516 | String dtcRead = ""; 517 | 518 | //check first header 43 or 00 519 | while ((dtcRead.substring(0,2) != "43") && (dtcRead.substring(0,2) != "00") && (error_cnt < 5)) { 520 | Serial.print(F("Read DTC 03->")); 521 | dtcRead = getPID("03");//try read DTC code 522 | #ifdef TEST_DTC 523 | dtcRead = "00E 0: 43 06 00 7D C6 93 1: 01 08 C6 0F 02 E9 02 2: E0 CC CC CC CC CC CC 43 01 C4 01"; 524 | #endif 525 | //Terminal(dtcRead,0,88,320,125); 526 | error_cnt++; 527 | delay(100); 528 | } 529 | if (error_cnt >= 10) {//exist by error counter 530 | Serial.println(F("Error Reading DTCs!")); 531 | tft.setTextColor(TFT_WHITE,TFT_RED); 532 | tft.drawCentreString("* Error Reading DTCs! *",159,120,4); 533 | tft.setTextColor(TFT_LIGHTGREY,TFT_BLACK); 534 | tft.drawCentreString("[ Please try again ]",159,148,4); 535 | tft.setTextColor(TFT_BLACK,TFT_WHITE); 536 | tft.drawCentreString("[- Press button to exit -]",159,215,4); 537 | beep(); 538 | } else { 539 | //#3 Interpret get the real DTC code 5 digits 540 | //(for 1 dtc)43 06 00 7D 541 | // 00E 0: <43 06> /00 7D /C6 93 /1: /01 08 /C6 0F /02 E9 /02 2: E0 /CC CC /CC CC /CC CC /<43 01>/C4 01  542 | beepbeep(); 543 | int byteCount = 0;//keep number of data byte 544 | String hexcode[128];//4x16 545 | uint8_t StringCount = 0; 546 | dtcRead.trim(); 547 | while (dtcRead.length() > 0) {//split to hex byte 548 | int index = dtcRead.indexOf(' '); 549 | String getByte = dtcRead.substring(0, index);//get first byte 550 | if (getByte.indexOf(':') == -1 ) {//no : 551 | if (getByte.length() >= 3) { 552 | byteCount = strtol(getByte.c_str(), NULL, 16);//get byte count = 14 byte 553 | Serial.print(byteCount); 554 | Serial.print(" = "); 555 | } else {//skip ':' byte 556 | hexcode[StringCount++] = getByte;//copy strings from 0 to space to hexcode 557 | Serial.print(getByte + ","); 558 | if (StringCount == byteCount) break;//stop when byte count reach, ignore the rest 559 | //4306007DC6930108C60F02E902E0 = 14 byte 560 | } 561 | } 562 | dtcRead = dtcRead.substring(index + 1);//copy the rest behind space to elm_rs 563 | }//while 564 | 565 | Serial.println(); //debug split hex byte 566 | 567 | //interpret DTC 007D=P007D C693=U0693 0108=P0108 C60F=U060F 02E9=P02E9 02E0=P02E0 568 | String dtcList[16] = {};//to keep all DTC in list 569 | no_of_dtc = 0; 570 | for (uint8_t index = 0;index < StringCount; index++) { 571 | if (hexcode[index] == "43") {//check if first nibble is 43 572 | index = index + 2;//skip 2 byte to next pid 573 | } 574 | String dtc_1 = hexcode[index];//C6 575 | uint8_t code = strtol(dtc_1.substring(0,1).c_str(),0,16);//0x0C compare with dtcMap table 576 | dtc_1 = dtcMap[code]+dtc_1.substring(1);//U0+6 = U06 change prefix,remove 1st char get the rest 577 | index++; 578 | String dtc_2 = hexcode[index];//0F 579 | dtcList[no_of_dtc] = dtc_1+dtc_2;//U060F 580 | Serial.print(dtcList[no_of_dtc] + ",");//got real DTC code here 581 | no_of_dtc++;//keep real no of dtc in no_of_dtc 582 | }//for 583 | tft.setTextColor(TFT_RED,TFT_BLACK); 584 | String txt = "MIL is ON - No of DTCs = "+String(no_of_dtc); 585 | tft.drawCentreString(txt,159,58,4); 586 | tft.drawFastHLine(0,85,320,TFT_WHITE); 587 | Serial.printf("\nNo of DTCs = %d\n",no_of_dtc); 588 | 589 | //#4 List all DTC on screen 590 | tft.setTextColor(TFT_WHITE,TFT_BLACK); 591 | for (uint8_t i=0;i0x80 609 | }//else if error_ent>5 610 | 611 | while (digitalRead(SELECTOR_PIN) == HIGH) { //wait for button press to exit 612 | checkCPUTemp(); 613 | autoDim(); 614 | //CLEAR MIL 615 | if (no_of_dtc !=0) {//if there is dtc, touch checking for clear MIL 616 | uint16_t t_x = 0, t_y = 0; // To store the touch coordinates 617 | bool touched = getTouch(&t_x, &t_y); 618 | #ifdef SERIAL_DEBUG 619 | Serial.printf("%d - %d\n",t_x,t_y); 620 | #endif 621 | if (touched && (t_y > 225) && (t_x < 127)) {//touch clear MIL button 622 | clickSound(); 623 | BTSerial.print("04\r");//clear MIL 04->7F 04 78  7F 04 78 624 | tft.setTextColor(TFT_BLACK,TFT_GREEN); 625 | tft.drawCentreString("* Successful Clear MIL *",159,120,4); 626 | Serial.println(F("* Successful Clear MIL *")); 627 | beepbeep(); 628 | delay(3000); 629 | break; 630 | }//if touch 631 | }//if (no_of_dtc) 632 | yield(); 633 | }//while button exit 634 | tft.fillRect(0,28,320,239,TFT_BLACK); 635 | listMenu(select); 636 | }//select = 4 637 | //---------- Auto OFF Voltage ----------- 638 | if (select == 5) { 639 | clickSound(); 640 | tft.fillRect(0,30,320,210,TFT_BLACK); 641 | tft.pushImage(0,30,25,25,autooff);//about 642 | tft.setTextColor(TFT_WHITE,TFT_BLACK); 643 | tft.drawString("Gauge Auto-off Setting",40,30,4); 644 | tft.setTextColor(TFT_WHITE,TFT_BLACK); 645 | tft.drawString("Set detection PCM voltage to turn off gauge.",10,65,2); 646 | tft.drawString("If gauge turn off while the engnine is running",10,85,2); 647 | tft.drawString("Please lower down the voltage",10,105,2); 648 | tft.drawString("Volt",220,135,4); 649 | tft.setTextColor(TFT_BLACK,TFT_WHITE); 650 | tft.drawCentreString("[- Press button to exit -]",159,180,4); 651 | for (uint8_t i = 0;i<5;i++) //draw buttons 652 | tft.fillRoundRect(i*64,211,60,30,5,TFT_NAVY); 653 | tft.setTextColor(TFT_WHITE); 654 | tft.drawCentreString("DEFAULT",31,217,2);//reset 655 | tft.drawCentreString("-",223,206,6);//- 656 | tft.fillRect(286,219,3,16,TFT_WHITE);//+ 657 | tft.fillRect(280,225,16,3,TFT_WHITE);//+ 658 | tft.setTextColor(TFT_YELLOW,TFT_BLACK); 659 | String result = String(ecu_off_volt, 1); 660 | tft.drawCentreString(result.c_str(),159,135,4); 661 | delay(1000); 662 | while (digitalRead(SELECTOR_PIN) == HIGH) {//wait for button press to exit 663 | checkCPUTemp(); 664 | autoDim(); 665 | uint16_t t_x = 0, t_y = 0; // To store the touch coordinates 666 | bool touched = getTouch(&t_x, &t_y); 667 | if (touched && (t_y > 211)) { 668 | clickSound(); 669 | #ifdef SERIAL_DEBUG 670 | Serial.printf("%d - %d\n",t_x,t_y); 671 | #endif 672 | if (t_x < 64) {//DEFAULT 673 | ecu_off_volt = factoryECUOff; 674 | } 675 | 676 | if ((t_x >= 192) && (t_x <256)) {//decrease 677 | ecu_off_volt = ecu_off_volt - 0.1; 678 | if (ecu_off_volt <= 11.0) ecu_off_volt = 11.0; 679 | } 680 | if ((t_x >= 256) && (t_x <320)) {//increase 681 | ecu_off_volt = ecu_off_volt + 0.1; 682 | if (ecu_off_volt >= 15.0) ecu_off_volt = 15.0; 683 | } 684 | String result = String(ecu_off_volt, 1); 685 | tft.drawCentreString(result.c_str(),159,135,4); 686 | delay(300);//touch screen delay 687 | }//if touched 688 | }//while 689 | //exit and save warning data to preference 690 | pref.putFloat("ecu_off_volt",ecu_off_volt);//save ecu_off_volt 691 | 692 | tft.setTextColor(TFT_BLACK,TFT_GREEN); 693 | tft.drawCentreString("Parameters Saved.",159,120,4); 694 | beep(); 695 | delay(1000); 696 | tft.fillRect(0,30,320,210,TFT_BLACK); 697 | listMenu(select); 698 | }//select = 5 699 | 700 | //---------------------ABOUT----------- 701 | if (select == 6) { 702 | clickSound(); 703 | delay(500); 704 | xTaskCreatePinnedToCore( 705 | &TaskPlayMusic 706 | , "play music in core 0" // A name just for humans 707 | , 4096// This stack size can be checked & adjusted by reading the Stack Highwater 708 | , NULL// no variable passting to task 709 | , 3 //Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest. 710 | , &TaskHandle0 711 | , 0);//core0 712 | animation();//play star field animation 713 | vTaskDelete(TaskHandle0);//stop animation and text animation 714 | pinMode(BUZZER_PIN,OUTPUT);//set pin mode again unless speaker stop working 715 | ledcAttachPin(BUZZER_PIN, buzzerChannel);//reattach buzzer pin 716 | clickSound(); 717 | tft.fillScreen(TFT_BLACK);//clear screen 718 | tft.fillRectVGradient(0, 0, 320, 26, TFT_ORANGE, 0xfc00); 719 | tft.setTextColor(TFT_BLACK); 720 | tft.drawCentreString("[--- Configuration Menu ---]",159,0,4); 721 | listMenu(select); 722 | }//if (select ==6) 723 | //---------------------EXIT----------- 724 | if (select == 7) { 725 | clickSound(); 726 | if (showsystem != currentFlag) {//save to eeprom only when flag changed 727 | pref.putBool("showsystem", showsystem);//save to NVR 728 | } 729 | break;//exist main config menu loop while 730 | }//select = 7 731 | pressed = false; 732 | prompt = true;//to trig reading elm again 733 | } //if holdtimer > 3000 734 | delay(250);//delay avoid bounce 735 | //********************** 736 | } else {//button release 737 | if (pressed) {//button release 738 | select++;//next menu 739 | if (select == maxMenu) select = 0; 740 | listMenu(select);//next menu 741 | clickSound(); 742 | pressed = false; 743 | delay(200); 744 | }//pressed 745 | 746 | }//if press&& digitalread 747 | checkCPUTemp(); 748 | autoDim(); 749 | 750 | } //while (true) 751 | tft.fillScreen(TFT_BLACK);//clear screen 752 | }//configMenu 753 | -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/elm327.h: -------------------------------------------------------------------------------- 1 | //elm327 interpreter 2 | 3 | //DTC prefex code mapping 4 | const String dtcMap[16] = {"P0","P1","P2","P3","C0","C1","C2","C3","B0","B1","B2","B3","U0","U1","U2","U3"}; 5 | 6 | //get A B from response return in global A B variable 7 | void getAB2(String elm_rsp,String mode,String para) { //41 05 2A 3C 01> 8 | elm_rsp.trim(); 9 | String hexcode[16]; 10 | uint8_t StringCount = 0; 11 | while (elm_rsp.length() > 0) {//keep reading each char 12 | int index = elm_rsp.indexOf(' ');//check space 13 | if (index == -1) // No space found 14 | { //only data without space is now in hexcode {41,05,aa,bb} 15 | hexcode[StringCount++] = elm_rsp; //last byte 16 | //for (index=0;index0) {//clear rx buffer 32 | BTSerial.read(); 33 | } 34 | String bt_response = ""; 35 | BTSerial.print(pid+"\r");//get no of dtc 36 | delay(500);//wait for response 37 | while (BTSerial.available()>0) {//reading Bluetooth 38 | char incomingChar = BTSerial.read(); 39 | //check CR/NL 40 | if (incomingChar == '>') { 41 | Serial.println(bt_response); 42 | BTSerial.flush(); 43 | } else { 44 | bt_response.concat(incomingChar);//keep elm327 respond 45 | } 46 | } //while BTSerial 47 | return bt_response; 48 | } 49 | /*------------------*/ 50 | /* VIN reading 51 | String resp = "014 52 | 0: 49 02 01 4D 50 42 53 | 1: 41 4D 46 30 36 30 4E 54 | 2: 58 34 33 37 30 39 33 "; */ 55 | //convert ascii code to Charactor 56 | const char asciiTable[0x60]= " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}`"; 57 | char HextoChar(uint8_t asciiCode) { 58 | if ((asciiCode > 0x20) && (asciiCode < 0x80)) { 59 | return(asciiTable[asciiCode-32]); 60 | } 61 | else return '\0';//null-terminator 62 | } 63 | //---------------------------- 64 | //function get VIN Serial.println(getVIN(getPID(0902))); 65 | String getVIN(String elm_rsp) { 66 | String VIN = ""; 67 | uint8_t byteCount = 0; 68 | elm_rsp.trim(); 69 | while (elm_rsp.length() > 0) {//keep reading each char 70 | int index = elm_rsp.indexOf(' ');//check space 71 | String getByte = elm_rsp.substring(0, index);//get first byte 72 | if (index == -1) {//no space found 73 | byte ascii = strtol(getByte.c_str(), NULL, 16);//read ascii 74 | VIN.concat(HextoChar(ascii));//last char 75 | //check correct VIN 76 | if (VIN.length() == byteCount) { 77 | VIN = VIN.substring(3);//remove first 3 string 78 | Serial.println("VIN: " + VIN); 79 | return VIN;//return VIN number 80 | } else { 81 | return "Cannot read VIN"; 82 | } 83 | 84 | } else {//found space 85 | if (getByte.indexOf(':') == -1 ) { 86 | if (getByte.length() >= 3) { 87 | byteCount = strtol(getByte.c_str(), NULL, 16);//get byte count 88 | } else {//skip ':' byte 89 | byte ascii = strtol(getByte.c_str(), NULL, 16);//read ascii 90 | VIN.concat(HextoChar(ascii));//convert to hex charactor 91 | } 92 | } 93 | elm_rsp = elm_rsp.substring(index + 1);//copy the rest behind space to elm_rsp. 94 | }//else 95 | } //while 96 | return "Cannot read VIN"; 97 | }//getVIN 98 | //---------------------------- 99 | 100 | -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/firmware/README.md: -------------------------------------------------------------------------------- 1 | there are 3 compiled firmware version 2 | 1. VaandcobOBD2_T6_2023.bin 3 | - this is an original firmware from that work with ford T6 up with transmission 6R80, 10R80 4 | - work very well before some libraries updated. 5 | 6 | 2. VaandcobOBD2_T5.bin 7 | - this is a new firmware for old ford T5 vehicle with old automatic transmission 8 | 9 | 3. for firmware suffix with _nogenuine.bin are the version that skip genuine board check. 10 | 11 | 12 | ---------------------------------------- 13 | You can flash firmware with web flasher tool -> 14 | https://vaandcob.github.io/webpage/src/index.html 15 | -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/firmware/T5_manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Va&Cob OBDII Gauge", 3 | "version": "Ford T5", 4 | "new_install_prompt_erase": false, 5 | "new_install_improv_wait_time": 0, 6 | "builds": [ 7 | { 8 | "chipFamily": "ESP32", 9 | "parts": [ 10 | { "path": "bootloader.bin", "offset": 4096 }, 11 | { "path": "partitions.bin", "offset": 32768}, 12 | { "path": "boot_app0.bin", "offset": 57344 }, 13 | { "path": "VaAndCobOBD2GaugeT5_nogenuine.bin", "offset": 65536 } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/firmware/T6_manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Va&Cob OBDII Gauge", 3 | "version": "Ford T6", 4 | "new_install_prompt_erase": false, 5 | "new_install_improv_wait_time": 0, 6 | "builds": [ 7 | { 8 | "chipFamily": "ESP32", 9 | "parts": [ 10 | { "path": "bootloader.bin", "offset": 4096 }, 11 | { "path": "partitions.bin", "offset": 32768 }, 12 | { "path": "boot_app0.bin", "offset": 57344 }, 13 | { "path": "VaAndCobOBD2Gauge_T6_nogenuine.bin", "offset": 65536 } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/firmware/VaAndCobOBD2GaugeT5_nogenuine.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/VaAndCobOBD2Gauge/firmware/VaAndCobOBD2GaugeT5_nogenuine.bin -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/firmware/VaAndCobOBD2Gauge_T6_nogenuine.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/VaAndCobOBD2Gauge/firmware/VaAndCobOBD2Gauge_T6_nogenuine.bin -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/firmware/boot_app0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/VaAndCobOBD2Gauge/firmware/boot_app0.bin -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/firmware/bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/VaAndCobOBD2Gauge/firmware/bootloader.bin -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/firmware/offset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/VaAndCobOBD2Gauge/firmware/offset.png -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/firmware/partitions.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/VaAndCobOBD2Gauge/firmware/partitions.bin -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/firmware_update.h: -------------------------------------------------------------------------------- 1 | //Firmware updater library 2 | #include //ota partition boot option 3 | #include // main updater 4 | #include // File System 5 | #include // SD Card ESP32 6 | #include //safe file 7 | 8 | //Wifi updater 9 | #include //wifi 10 | #include 11 | 12 | const char* ssid = "Va&Cob OBD2 Gauge"; 13 | const char* password = "12345678";//default password 14 | IPAddress local_IP(192, 168, 4,1); 15 | IPAddress gateway(192, 168, 4, 1); 16 | IPAddress subnet(255, 255, 255, 0); 17 | WebServer server(80); 18 | 19 | #define firmware_filename "/VaAndCobOBD2Gauge.bin"//firmware file 20 | 21 | 22 | /*------------ SOUND EFFECT ----------*/ 23 | void beepbeep() { //2 shot beep 24 | ledcWriteTone(buzzerChannel,2000); 25 | delay(50); 26 | ledcWriteTone(buzzerChannel,0); 27 | delay(50); 28 | ledcWriteTone(buzzerChannel,2000); 29 | delay(50); 30 | ledcWriteTone(buzzerChannel,0); 31 | } 32 | void beep() { 33 | ledcWriteTone(buzzerChannel,2000); 34 | delay(500); 35 | ledcWriteTone(buzzerChannel,0); 36 | } 37 | void clickSound() { 38 | ledcWriteTone(buzzerChannel,5000); 39 | delay(5); 40 | ledcWriteTone(buzzerChannel,0); 41 | } 42 | /*-----------------------------*/ 43 | //html update file 44 | static const char uploadIndex[] PROGMEM = 45 | R"( 46 | 47 | 48 | 49 | 50 | 51 | 70 |

Please choose file 'vaandcobobd2gauge.bin'

71 |
72 | 73 |
74 |

75 | 76 |

0%
77 |

78 | 110 | 111 | 112 | )"; 113 | /*----- OTA Web Server ------*/ 114 | void StartOTAserver() { 115 | //open upload page http://192.168.4.1 116 | server.on("/", HTTP_GET, []() { 117 | Serial.println(F("Request upload firmware...")); 118 | server.sendHeader("Connection", "close"); 119 | server.send(200, "text/html", uploadIndex); 120 | }); 121 | 122 | //handling uploading firmware file http://192.168.4.1/update 123 | server.on("/update", HTTP_POST, []() { 124 | server.sendHeader("Connection", "close"); 125 | server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); 126 | if (Update.hasError()) {//error 127 | tft.setTextColor(TFT_WHITE,TFT_RED); 128 | tft.drawCentreString("Error: bad firmware file!",159,170,4); 129 | beep(); 130 | } 131 | else { 132 | esp_ota_mark_app_valid_cancel_rollback();//prevent from boot old firmware 133 | delay(3000); 134 | ESP.restart();//successful upload restart esp 135 | } 136 | }, []() { 137 | 138 | HTTPUpload& upload = server.upload(); 139 | if (upload.status == UPLOAD_FILE_START) { 140 | beepbeep(); 141 | Serial.printf("Firmware: %s\n", upload.filename.c_str()); 142 | tft.setTextColor(TFT_BLACK,TFT_GREEN); 143 | tft.drawCentreString("Updating... please wait",159,170,4); 144 | if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size 145 | Update.printError(Serial); 146 | } 147 | } else if (upload.status == UPLOAD_FILE_WRITE) { 148 | // flashing firmware to ESP 149 | if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { 150 | Update.printError(Serial); 151 | } 152 | } else if (upload.status == UPLOAD_FILE_END) { 153 | if (Update.end(true)) { //true to set the size to the current progress 154 | Serial.printf("Update Successful... Restart"); 155 | tft.setTextColor(TFT_BLACK,TFT_GREEN); 156 | tft.drawCentreString("Update Successful... Restart",159,170,4); 157 | beepbeep(); 158 | 159 | } else { 160 | Update.printError(Serial); 161 | } 162 | } 163 | }); 164 | server.begin(); 165 | }//startOTAserver 166 | /*-------------- WiFi Updater----------------*/ 167 | void updateFirmwareWIFI() { 168 | WiFi.mode(WIFI_AP);//set wifi mode soft access point 169 | String pwd = String(random(99999999));//random new password at least 8 digit long to get it work 170 | for (uint8_t i=pwd.length();i<8;i++) { 171 | pwd = "0"+pwd;//add 0 prefix to set 8 char length password 172 | } 173 | password = pwd.c_str(); 174 | WiFi.softAP(ssid, password);//start ACCESS Point 175 | if (!WiFi.softAPConfig(local_IP, gateway, subnet)) {//error start soft AP 176 | Serial.println(F("STA Failed to configure")); 177 | tft.setTextColor(TFT_WHITE,TFT_RED); 178 | tft.drawCentreString("Failed to start WiFi!",159,120,4); 179 | beep(); 180 | delay(3000); 181 | } else {//softAP config OK 182 | StartOTAserver();//start server 183 | //show WiFi softAP information 184 | tft.fillRect(0,30,320,239,TFT_BLACK); 185 | tft.setTextColor(TFT_CYAN,TFT_BLACK); 186 | tft.drawCentreString("> Connect to gauge Wi-Fi <",159,40,4); 187 | tft.setTextColor(TFT_WHITE,TFT_BLACK); 188 | String txt = "SSID: "+String(ssid); 189 | Serial.println(txt); 190 | tft.drawString(txt,0,80,4); 191 | txt = "Password: "+String(password); 192 | Serial.println(txt); 193 | tft.drawString(txt,0,110,4); 194 | txt = "IP Address: "+ WiFi.softAPIP().toString(); 195 | Serial.println(txt); 196 | tft.drawString(txt,0,140,4); 197 | tft.setTextColor(TFT_BLACK,TFT_WHITE); 198 | tft.drawCentreString("[- Press button to exit -]",159,215,4); 199 | while (digitalRead(SELECTOR_PIN) == HIGH) {//wait for button press to exit 200 | autoDim(); 201 | checkCPUTemp(); 202 | server.handleClient();//update server handle 203 | delay(300);//delay button press 204 | } //exit wifi update 205 | clickSound(); 206 | server.stop(); 207 | WiFi.mode(WIFI_MODE_STA); 208 | WiFi.softAPdisconnect();//turn off WiFi 209 | }//else WiFi.softapconfig 210 | } //updateFirmwareWiFI 211 | /*----------- SD Card Updater --------------*/ 212 | // perform the actual update from a given stream 213 | void performUpdate(Stream &updateSource, size_t updateSize) { 214 | if (Update.begin(updateSize, U_FLASH, LED_RED_PIN, LOW)) { 215 | //add progressbar 216 | tft.fillRoundRect(50,61, 218, 28, 14, TFT_LIGHTGREY); 217 | tft.fillRect(59,67,200,16,TFT_BLACK);//0% progress bar 218 | //update progress 219 | Serial.print(F("Progress: ")); 220 | //progress callback 221 | Update.onProgress([&](size_t written, size_t total) { 222 | uint8_t progress = written * 100 / total; 223 | tft.fillRectHGradient(59,67,progress*2,16,TFT_RED,TFT_GREEN);//progressbar 224 | if (progress%10 == 0) Serial.print(F("*")); 225 | ledcWriteTone(buzzerChannel,random(4000)+8000);//acceleraet sound effect 226 | }); 227 | 228 | //write firmware from SDCard here 229 | size_t written = Update.writeStream(updateSource); 230 | Serial.printf("%d bytes written",written); 231 | 232 | if (Update.end()) {//updaet is done 233 | if (Update.isFinished()) { 234 | Serial.println(F("Update Successful... Restart")); 235 | tft.setTextColor(TFT_BLACK,TFT_GREEN); 236 | tft.drawCentreString("Update Successful... Restart",159,120,4); 237 | beepbeep(); 238 | esp_ota_mark_app_valid_cancel_rollback();//prevent from boot old firmware 239 | delay(1000); 240 | ESP.restart();//restart 241 | } 242 | else { 243 | Serial.println(F("Error! Update not finished")); 244 | tft.setTextColor(TFT_WHITE,TFT_RED); 245 | tft.drawCentreString("Error! Update not finished",159,120,4); 246 | } 247 | } else {//update stop during updating 248 | Serial.println("Error Occurred. Error #: " + String(Update.getError())); 249 | } 250 | 251 | }//if update.begin 252 | else 253 | { 254 | Serial.println(F("Not enough space to load firmware")); 255 | tft.setTextColor(TFT_WHITE,TFT_RED); 256 | tft.drawCentreString("Not enough space to upload!",159,120,4); 257 | } 258 | } 259 | /*------------------*/ 260 | // check given FS for valid update.bin and perform update if available 261 | void updateFromFS(fs::FS &fs) { 262 | Serial.println(F("Loading firmware...")); 263 | tft.setTextColor(TFT_WHITE,TFT_BLUE); 264 | tft.drawCentreString("Loading firmware...",159,120,4); 265 | File updateBin = fs.open(firmware_filename); 266 | if (updateBin) { 267 | 268 | if(updateBin.isDirectory()){ 269 | Serial.println(F("\"VaandCobOBD2Gauge.bin\" is not a file")); 270 | tft.setTextColor(TFT_WHITE,TFT_RED); 271 | tft.drawCentreString("\"VaandCobOBD2Gauge.bin\" is not a file",159,120,4); 272 | updateBin.close(); 273 | return; 274 | } 275 | 276 | size_t updateSize = updateBin.size(); 277 | 278 | if (updateSize > 0) { 279 | Serial.println(F("Updating... please wait")); 280 | tft.setTextColor(TFT_WHITE,TFT_BLUE); 281 | tft.drawCentreString("Updating... please wait",159,120,4); 282 | performUpdate(updateBin, updateSize); 283 | } 284 | else { 285 | Serial.println(F("Error! file is empty")); 286 | tft.setTextColor(TFT_WHITE,TFT_RED); 287 | tft.drawCentreString("Error! file is empty",159,120,4); 288 | } 289 | updateBin.close(); 290 | // whe finished remove the binary from sd card to indicate end of the process 291 | //fs.remove(firmware_filename); 292 | 293 | } else { 294 | Serial.println(F("Firmware not found!")); 295 | tft.pushImage(130,44,60,60,nofile);//show fileicon 296 | tft.setTextColor(TFT_WHITE,TFT_RED); 297 | tft.drawCentreString("Firmware not found!",159,120,4); 298 | } 299 | 300 | } 301 | /*------------------*/ 302 | void updateFirmwareSD() { 303 | tft.fillRect(0,30,320,239,TFT_BLACK); 304 | tft.setTextColor(TFT_BLACK,TFT_WHITE); 305 | tft.drawCentreString("[- Press button to exit -]",159,215,4); 306 | if(!SD.begin()) {//sdcard not attach 307 | Serial.println(F("Micro SD Card not mounted!")); 308 | tft.pushImage(129,44,60,60,sdcard);//show sdcard icon 309 | tft.setTextColor(TFT_WHITE,TFT_RED); 310 | tft.drawCentreString("Please insert Micro SD Card",159,120,4); 311 | } else {//sd card attached 312 | Serial.println(F("Micro SD Card..OK")); 313 | uint8_t cardType = SD.cardType(); 314 | if (cardType == CARD_NONE) {//bad card 315 | Serial.println(F("Micro SD Card Error!")); 316 | tft.setTextColor(TFT_WHITE,TFT_RED); 317 | tft.drawCentreString("Micro SD Card Error!",159,120,4); 318 | } else {//good sd card attached 319 | updateFromFS(SD);//update firmware from sd card 320 | 321 | }//else 322 | }//else 323 | SD.end(); 324 | beep(); 325 | }//updatefirmware 326 | /*----------------*/ 327 | -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/meter.h: -------------------------------------------------------------------------------- 1 | // All Meters display function 2 | 3 | const uint16_t screenWidth = tft.height();//landscape 4 | const uint16_t screenHeight = tft.width(); 5 | /*---- METER ------_* 6 | Type 0:numericMeter 7 | Type 1:arcMeters 8 | Type 2:vBarMeter 9 | coordinate for each cell (numieric meter & arc meter) 10 | 1 , 4 , 7 11 | 2 , 5 , 8 12 | 3 , 6 , 9 */ 13 | const uint16_t cell1_x[9] = { 0, 0, 0, 80, 80, 80, 160, 160, 160 }; //cell type 1 x origin cooridate 14 | const uint16_t cell1_y[9] = { 0, 80, 160, 0, 80, 160, 0, 80, 160 }; //cell type 1 y origin coordiate 15 | /*coordinate for each cell type 2 (Vertical bar meter) 16 | 1 , 2 , 3 , 4 */ 17 | const uint16_t cell2_x[4] = { 0, 80, 160, 240 }; //vertical bar cell x origin coordinate 18 | bool blinkCell[9] = {false,false,false,false,false,false,false,false,false};//flag for blinking 19 | 20 | String pidList[maxpidIndex] = {}; //{"0104","0105","010C","010B","010C","0142","015C"} 21 | uint8_t pidReadSkip[maxpidIndex] = {}; //skip reading 0 - 3; 3 = max delay read 22 | uint8_t pidCurrentSkip[maxpidIndex] = {}; //current skip counter 23 | uint8_t engine_off_count = 0;//counter to indicate engine is off then sleep 24 | float old_data[maxpidIndex] = {};//keep old data for each gauge to improve speed for pidIndex 0-6 25 | 26 | //backlight variable average counter 27 | uint8_t low_count = 0; 28 | uint8_t high_count = 0; 29 | 30 | const float factoryECUOff = 12.5;//default off voltage 31 | float ecu_off_volt = factoryECUOff;//turn engine off if voltage lower than 12.4 32 | 33 | /* #################################### 34 | draw digital numeric meter 160x80 px 35 | #################################### 36 | cell 37 | 1 , 2 , 3 38 | 4 , 5 , 6 39 | 7 , 8 , 9 */ 40 | void numericMeter(uint8_t cell, uint8_t pid) { 41 | int w = screenWidth / 2; 42 | int h = screenHeight / 3; 43 | int x = cell1_x[cell - 1]; 44 | int y = cell1_y[cell - 1]; 45 | // Array data -> { Label , unit, pid, fomula, min, max, ,skip, digit } 46 | String label = pidConfig[pid][0]; //get PID to label 47 | tft.drawRect(x, y, w, screenHeight, TFT_GREY); 48 | tft.fillRect(x + 2, y + 2, w - 5, h - 5, TFT_BLACK); 49 | tft.fillRectVGradient(x + 2, y + 2, w - 5, h * 0.33,TFT_BLUE ,0x0011); 50 | tft.setTextColor(TFT_WHITE); 51 | tft.drawCentreString(label, x + w / 2, y + 2, 4); 52 | tft.setTextColor(TFT_GREEN, TFT_BLACK); 53 | tft.drawRightString("---", x + w - 5, y + (h * 0.25) + 11, 6); 54 | 55 | } //numericMeter 56 | /*---------------------------*/ 57 | //plot number on digital Meter 58 | void plotNumeric(uint8_t cell, float data, int warn, bool digit) { 59 | int compare = warn/data;//compare data and warn 60 | if (compare == 0) {//warning 61 | int w = screenWidth / 2 - 1; 62 | int h = screenHeight / 3 - 1; 63 | int x = cell1_x[cell - 1]; 64 | int y = cell1_y[cell - 1]; 65 | if (blinkCell[cell]) {//show 66 | String result = String(data, digit); 67 | switch (result.length()) { 68 | case 2: result = " " + result; break; 69 | case 3: result = " " + result; break; 70 | case 4: result = " " + result; break; 71 | }//switch 72 | old_data[pidIndex] = data;//update old data 73 | tft.setTextColor(TFT_RED, TFT_BLACK);//red color font 74 | tft.drawRightString(result.c_str(), x + w - 5, y + (h * 0.25) + 11, 6); 75 | 76 | } else {//hiding make blinking effect 77 | tft.fillRect(x + 2, y + 28, w - 5, 50, TFT_BLACK);//delete old number 78 | } 79 | blinkCell[cell] = !blinkCell[cell]; 80 | 81 | } else {//no warning 82 | 83 | if (data != old_data[pidIndex]) {//if value change then redraw new data 84 | int w = screenWidth / 2 - 1; 85 | int h = screenHeight / 3 - 1; 86 | int x = cell1_x[cell - 1]; 87 | int y = cell1_y[cell - 1]; 88 | 89 | String result = String(data, digit); 90 | switch (result.length()) { 91 | case 2: result = " " + result; break; 92 | case 3: result = " " + result; break; 93 | case 4: result = " " + result; break; 94 | }//if data 95 | tft.setTextColor(TFT_GREEN, TFT_BLACK);//green color font 96 | tft.drawRightString(result.c_str(), x + w - 5, y + (h * 0.25) + 11, 6); 97 | old_data[pidIndex] = data;//update old data 98 | } //if nothing changed then skip 99 | }//if warn/data == 0 100 | } 101 | 102 | /* ########################## 103 | draw analog meter 160x80 px 104 | ########################## 105 | cell 106 | 1 , 2 , 3 107 | 4 , 5 , 6 108 | 7 , 8 , 9 */ 109 | void arcMeter(uint8_t cell, uint8_t pid) { 110 | int w = screenWidth / 2; 111 | int h = screenHeight / 3; 112 | int x = cell1_x[cell - 1]; 113 | int y = cell1_y[cell - 1]; 114 | // Array data -> { Label , unit, pid, fomula, min, max, ,skip, digit } 115 | String label = pidConfig[pid][0]; //get PID to label 116 | String unit = pidConfig[pid][1]; //get PID to unit 117 | int min = pidConfig[pid][4].toInt(); //get PID to min 118 | int max = pidConfig[pid][5].toInt(); //get PID to max 119 | // Meter outline 120 | tft.fillRect(x, y, w, h, TFT_GREY); 121 | tft.fillRect(x + 2, y + 2, 156, 76, TFT_BLACK); 122 | tft.setTextColor(TFT_WHITE); // Text colour 123 | tft.drawCentreString(String(min).c_str(), x + 16, y + 10, 2); 124 | tft.drawCentreString(String(max).c_str(), x + w - 16, y + 10, 2); 125 | tft.drawRightString(unit.c_str(), x + 150, y + 60, 2); // Units at bottom right 126 | tft.drawCentreString(label.c_str(), x + 80, y + 58, 2); // Comment out to avoid font 4 127 | tft.setTextColor(TFT_GREEN); 128 | tft.drawString("---", x + 10, y + 60, 2); // data 129 | //draw arc animation 130 | for (int angle = 135; angle < 225; angle++) { 131 | tft.drawSmoothArc(x + 80, y + 90, 80, 60, 134, angle, TFT_GREEN, TFT_DARKGREY, false); 132 | } 133 | for (int angle = 224; angle > 135; angle--) { 134 | tft.drawSmoothArc(x + 80, y + 90, 80, 60, angle + 1, 226, TFT_WHITE, TFT_DARKGREY, false); 135 | } 136 | } 137 | // ######################################################################### 138 | void plotArc(uint8_t cell, String label, float data, int warn, int min, int max, bool digit) { 139 | if (data != old_data[pidIndex]) { 140 | //int w = screenWidth / 2 - 1; 141 | //int h = screenHeight / 3 - 1; 142 | int x = cell1_x[cell - 1]; 143 | int y = cell1_y[cell - 1]; 144 | 145 | String result = String(data, digit); 146 | switch (result.length()) { 147 | case 2: result = result+" "; break; 148 | case 3: result = result+" "; break; 149 | case 4: result = result+" "; break; 150 | } 151 | 152 | if (data < min) data = min; 153 | if (data > max) data = max; 154 | 155 | int arcColor = TFT_GREEN; 156 | int compare = warn/data;//compare data and warn 157 | if (compare == 0) //warning 158 | arcColor = TFT_RED;//red font red arc 159 | else // no warn 160 | arcColor = TFT_GREEN;//gren font green arc 161 | tft.setTextColor(arcColor, TFT_BLACK); 162 | tft.drawString(result.c_str(), x + 10, y + 60, 2); // data 163 | int angle = map(data, min, max, 135, 225); 164 | if (data >= old_data[pidIndex])//new data > old data -> draw only green arc 165 | tft.drawSmoothArc(x + 80, y + 90, 80, 60, 134, angle, arcColor, TFT_DARKGREY, false); 166 | else //new data < old data -> draw only white arc 167 | tft.drawSmoothArc(x + 80, y + 90, 80, 60, angle + 1, 226, TFT_WHITE, TFT_DARKGREY, false); 168 | old_data[pidIndex] = data;//update old data 169 | }//if 170 | 171 | } 172 | /* ###################################### 173 | draw linear vertical meter 80x240 px 174 | ###################################### 175 | cell 1 , 2 , 3 , 4 */ 176 | //vertical meter cell 1 to 4 full screen height 177 | void vBarMeter(uint8_t cell, uint8_t pid) { 178 | int x = cell2_x[cell - 1]; //start origin 179 | int w = screenWidth / 4; 180 | // Array data -> { Label , unit, pid, fomula, min, max, ,skip, digit } 181 | String label = pidConfig[pid][0]; //get PID to label 182 | String unit = pidConfig[pid][1]; //get PID to unit 183 | //int min = pidConfig[pid][4].toInt(); //get PID to min 184 | int max = pidConfig[pid][5].toInt(); //get PID to max 185 | tft.drawRect(x, 0, w, screenHeight, TFT_DARKGREY); 186 | tft.fillRectVGradient(x + 2, 2, w - 3, screenHeight * 0.12 - 10, TFT_BLUE, 0x0011); 187 | tft.fillRect(x + 2, screenHeight * 0.12, w - 3, screenHeight - screenHeight * 0.24, TFT_BLACK); 188 | tft.setTextColor(TFT_WHITE); 189 | tft.drawCentreString(label, x + w / 2, 2, 2); 190 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 191 | tft.drawString(unit,x + 3, screenHeight * 0.12-8, 1); 192 | tft.setTextColor(TFT_GREEN, TFT_BLACK); 193 | tft.drawCentreString("---", x + w / 2, screenHeight - (screenHeight * 0.12) + 1, 4); 194 | float smalltick = screenHeight * 0.75 / 50; 195 | //float bigtick = screenHeight*0.75/10; 196 | //draw number 197 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 198 | // tft.drawRigscreenHeighttString("120",x+w-2,y+screenHeight*0.12+2,1); 199 | for (int i = 0; i < 51; i++) { //draw tick 200 | tft.drawFastHLine(x + w / 3, screenHeight * 0.12 + smalltick * i+3, 10, TFT_WHITE); 201 | if (i % 5 == 0) { 202 | tft.drawFastHLine(x + w / 3, screenHeight * 0.12 + smalltick * i+3, 20, TFT_WHITE); 203 | if (i % 10 == 0) { 204 | int mark = max - (max * i * 2 / 100); 205 | tft.drawRightString(String(mark), x + w - 3, screenHeight * 0.12 + smalltick * i - 7, 2); 206 | } //i%10 207 | } //i%5 208 | } //for i 209 | //animation 210 | for (int barH = 0; barH < 183; barH++) { //draw bar 211 | tft.fillRect(x + 5, 212 - barH, 20, barH, TFT_RED);//lower 212 | } 213 | for (int barH = 182; barH >= 0; barH--) { //draw bar 214 | tft.fillRect(x + 5, 29, 20, 183 - barH, TFT_BLACK); //upper 215 | } 216 | } //drawLinerVmeter 217 | /*-------------------*/ 218 | //vertical meter cell 1 to 4 full screen height 219 | void plotVBar(uint8_t cell, float data, int warn, int min, int max, bool digit) { //cell 1-4 220 | if (data != old_data[pidIndex]) {//if value change then redraw new data 221 | int x = cell2_x[cell - 1]; 222 | int w = screenWidth / 4; 223 | if (data < min) data = min; 224 | if (data > max) data = max; 225 | int barColor = TFT_GREEN; 226 | int compare = warn/data;//compare data and warn 227 | if (compare == 0) //warning 228 | barColor = TFT_RED;//red bar red font 229 | else // no warn 230 | barColor = TFT_GREEN;//green bar green font 231 | //draw new bar 232 | int barH = map(data, min, max, 0, 182); 233 | if (data > old_data[pidIndex]) //new data > old data -> draw red bar 234 | tft.fillRectVGradient(x + 5, 212 - barH, 20, barH, barColor, 0x8000);//lower 235 | else //new data < old data -> draw black bar 236 | tft.fillRect(x + 5, 29, 20, 183 - barH, TFT_BLACK); //upper 237 | 238 | tft.setTextColor(barColor, TFT_BLACK);//draw text 239 | String result = String(data, digit); 240 | switch (result.length()) { 241 | case 2: result = " " + result + " "; break; 242 | case 3: result = " " + result + " "; break; 243 | case 4: result = " " + result + " "; break; 244 | } 245 | tft.drawCentreString(result.c_str(), x + w / 2, screenHeight - (screenHeight * 0.12) + 1, 4); 246 | old_data[pidIndex] = data;//update old data 247 | }//if 248 | } 249 | /*-------------------*/ 250 | //initialize dislay by layout choose 251 | void initScreen() { 252 | //String pidList[6] = {"0105","0142","015C","010B","221E1C","010C"}; 253 | // Array data -> { Label , unit, pid, fomula, min, max, ,skip, digit } 254 | #ifdef SERIAL_DEBUG 255 | Serial.printf("Display layout -> %d\n",layout); 256 | #endif 257 | for (int i = 0; i < 7; i++) { 258 | pidList[i] = pidConfig[pidInCell[layout][i]][2]; //get PID to list 259 | pidReadSkip[i] = pidConfig[pidInCell[layout][i]][6].toInt(); //get read skip 260 | pidCurrentSkip[i] = pidReadSkip[i]; //skip for skip reading pid 261 | old_data[i] = 0;//clear old data 262 | } 263 | tft.fillScreen(TFT_BLACK); //clear screen 264 | switch (layout) { 265 | case 0: 266 | { //6 meter 267 | /*page 0 268 | █ 1 █ █ 7 █ 269 | █ 2 █ █ 8 █ 270 | █ 3 █ █ 9 █ */ 271 | arcMeter(1, pidInCell[0][0]); //cell,pid05 272 | arcMeter(2, pidInCell[0][1]); 273 | arcMeter(3, pidInCell[0][2]); 274 | numericMeter(7, pidInCell[0][3]); 275 | numericMeter(8, pidInCell[0][4]); 276 | numericMeter(9, pidInCell[0][5]); 277 | } 278 | break; 279 | case 1: 280 | { //6 meter 281 | /*page 1 282 | █ 1 █ █ 7 █ 283 | █ 2 █ █ 8 █ 284 | █ 3 █ █ 9 █ */ 285 | arcMeter(1, pidInCell[1][0]); //cell,pid05 286 | arcMeter(2, pidInCell[1][1]); 287 | arcMeter(3, pidInCell[1][2]); 288 | arcMeter(7, pidInCell[1][3]); //cell,pid05 289 | arcMeter(8, pidInCell[1][4]); 290 | arcMeter(9, pidInCell[1][5]); 291 | } 292 | break; 293 | case 2: 294 | { //6 meter 295 | /*page 2 296 | █ 1 █ █ 7 █ 297 | █ 2 █ █ 8 █ 298 | █ 3 █ █ 9 █ */ 299 | numericMeter(1, pidInCell[2][0]); 300 | numericMeter(2, pidInCell[2][1]); 301 | numericMeter(3, pidInCell[2][2]); 302 | numericMeter(7, pidInCell[2][3]); 303 | numericMeter(8, pidInCell[2][4]); 304 | numericMeter(9, pidInCell[2][5]); 305 | } 306 | break; 307 | case 3: 308 | { //5 meter 309 | /*page 3 310 | █ 1 █ █ █ 311 | █ 2 █ 3 4 312 | █ 3 █ █ █ */ 313 | numericMeter(1, pidInCell[3][0]); 314 | numericMeter(2, pidInCell[3][1]); 315 | numericMeter(3, pidInCell[3][2]); 316 | vBarMeter(3, pidInCell[3][3]); 317 | vBarMeter(4, pidInCell[3][4]); 318 | } 319 | break; 320 | case 4: 321 | { //5 meter 322 | /*page 4 323 | █ █ 4 █ █ 324 | 1 █ 5 █ 4 325 | █ █ 6 █ █ */ 326 | vBarMeter(1, pidInCell[4][0]); 327 | numericMeter(4, pidInCell[4][1]); 328 | numericMeter(5, pidInCell[4][2]); 329 | numericMeter(6, pidInCell[4][3]); 330 | vBarMeter(4, pidInCell[4][4]); 331 | } 332 | break; 333 | case 5: 334 | { //5 meter 335 | /*page 3 336 | █ █ █ 7 █ 337 | 1 2 █ 8 █ 338 | █ █ █ 9 █ */ 339 | 340 | vBarMeter(1, pidInCell[5][0]); 341 | vBarMeter(2, pidInCell[5][1]); 342 | numericMeter(7, pidInCell[5][2]); 343 | numericMeter(8, pidInCell[5][3]); 344 | numericMeter(9, pidInCell[5][4]); 345 | } 346 | break; 347 | case 6: 348 | { //4 meter 349 | /* page 4 350 | █ █ █ █ 351 | 1 2 3 4 352 | █ █ █ █ */ 353 | vBarMeter(1, pidInCell[6][0]); 354 | vBarMeter(2, pidInCell[6][1]); 355 | vBarMeter(3, pidInCell[6][2]); 356 | vBarMeter(4, pidInCell[6][3]); 357 | } 358 | break; 359 | case 7: 360 | { //5 meter 361 | /* page 5 362 | █ █ █ █ 363 | 1 2 3 4 364 | █ █ █ █ */ 365 | vBarMeter(1, pidInCell[7][0]); 366 | vBarMeter(2, pidInCell[7][1]); 367 | vBarMeter(3, pidInCell[7][2]); 368 | vBarMeter(4, pidInCell[7][3]); 369 | } 370 | break; 371 | } //switch page 372 | 373 | } //initscreen 374 | /*-------------------------------*/ 375 | 376 | //get A B from response return in global A B variable 377 | void getAB(String elm_rsp) { //41 05 2A 3C 01> 378 | if (elm_rsp == "NO DATA\r\r>") {//pid no data 379 | A = 0; 380 | B = 0; 381 | 382 | } else { 383 | //check if pid is 2 or 3 command length. 384 | uint8_t charcnt = pidList[pidIndex].length() / 2; //0105->2,221E1C->3 385 | String strs[8]; 386 | uint8_t StringCount = 0; 387 | while (elm_rsp.length() > 0) {//keep reading each char 388 | int index = elm_rsp.indexOf(' ');//check space 389 | if (index == -1) // No space found 390 | { //only data without space is now in strs {41,05,aa,bb} 391 | strs[StringCount++] = elm_rsp; //last byte 392 | A = strtol(strs[charcnt].c_str(),NULL,16); //byte 3 save to A 393 | B = strtol(strs[charcnt + 1].c_str(),NULL,16); //byte 4 save to B 394 | break; 395 | } else {//found space 396 | strs[StringCount++] = elm_rsp.substring(0, index);//copy strings from 0 to space to strs 397 | elm_rsp = elm_rsp.substring(index + 1);//copy the rest behind space to elm_rsp 398 | } 399 | } //while 400 | } //else 401 | }//getAB 402 | /*-------------------------------*/ 403 | //check engine status if off then turn off gauge 404 | void engine_onoff(float data, uint8_t pid) { //use pcm volt 405 | //check if pid is 010C eng speed and value = 0 406 | //String t = pidList[pid] + " - " + (String)value; 407 | if (pidList[pid] == "0142") { //pis = 010C engine speed RPM 408 | 409 | int compare = ecu_off_volt/data; 410 | if (compare > 0) //check lower voltage 411 | engine_off_count++; 412 | else 413 | engine_off_count = 0; //reset 414 | if (engine_off_count > 20) { //engine is stop 415 | if(pref.getUShort("layout",false) != layout) {//if current layout not NVR load layout 416 | pref.putUShort("layout", layout);//save current layout to NVR 417 | } 418 | show_spiffs_jpeg_image("/mypic.jpg", 0, 0);//show mypic 419 | ledcWriteTone(buzzerChannel,3136); 420 | delay(100); 421 | ledcWriteTone(buzzerChannel,2637); 422 | delay(100); 423 | ledcWriteTone(buzzerChannel,2093); 424 | delay(100); 425 | ledcWriteTone(buzzerChannel,0); 426 | for (int i=255;i>0;i--) {//fading effect 427 | ledcWrite(backlightChannel, i);//full bright 428 | delay(10); 429 | }//turn off backlight 430 | BTSerial.print("ATLP\r");//set ELM327 to Low Power 431 | BTSerial.disconnect();//disconnect bluetooth 432 | tft.fillScreen(TFT_BLACK); 433 | tft.writecommand(0x10); //TFT sleep 434 | esp_sleep_enable_ext0_wakeup(GPIO_NUM_27,LOW); //wake when button pressed 435 | esp_deep_sleep_start();//sleep shutdown backlight auto off with esp32 436 | 437 | }//if engine_off_count>20 438 | }//if PID = "010C" 439 | } 440 | /*-------------------------------*/ 441 | //coolant volt oiltemp vaporpressure TFT Load 442 | // {"01051","01421","015C1","01321","221E1C1","01041"}; 443 | void updateMeter(uint8_t pidNo, String response) { //update parameter on screen 444 | // Array data -> { Label , unit, pid, fomula, min, max, ,skip, digit } 445 | String label = pidConfig[pidInCell[layout][pidNo]][0]; 446 | //String unit = pidConfig[pidInCell[layout][pidNo]][1]; 447 | uint8_t formula = pidConfig[pidInCell[layout][pidNo]][3].toInt(); 448 | int min = pidConfig[pidInCell[layout][pidNo]][4].toInt(); 449 | int max = pidConfig[pidInCell[layout][pidNo]][5].toInt(); 450 | bool digit = pidConfig[pidInCell[layout][pidNo]][7].toInt(); 451 | int warn = warningValue[pidInCell[layout][pidNo]].toInt(); 452 | 453 | getAB(response); //get AB 454 | float data = 0.0; 455 | switch (formula) { //choose fomula 456 | case 0: data = A * 0.145; break; //psi 457 | case 1: data = A - 40; break; //temp 458 | case 2: data = A * 100.0 / 255; break; 459 | case 3: data = (256 * A + B) / 4.0; break; 460 | case 4: data = (256 * A + B) / 1000.0; break; 461 | case 5: 462 | data = (256 * A + B) / 16.0; 463 | if (data >150 || data <0) data = old_data[pidIndex];//check if data is outbound use old data 464 | break; 465 | 466 | case 6: 467 | data = (A*256+B)*5/72-18; 468 | if (data >150 || data <0) data = old_data[pidIndex];//check if data is outbound use old data 469 | break; 470 | 471 | //more formula 472 | } //switch fomula 473 | 474 | 475 | switch (layout) { 476 | case 0: 477 | { //6 meter 478 | /*page 0 479 | █ 1 █ █ 7 █ 480 | █ 2 █ █ 8 █ 481 | █ 3 █ █ 9 █ */ 482 | switch (pidNo) { 483 | case 0: plotArc(1, label, data, warn, min, max, digit); break; 484 | case 1: plotArc(2, label, data, warn, min, max, digit); break; 485 | case 2: plotArc(3, label, data, warn, min, max, digit); break; 486 | case 3: plotNumeric(7, data, warn, digit); break; 487 | case 4: plotNumeric(8, data, warn, digit); break; 488 | case 5: plotNumeric(9, data, warn, digit); break; 489 | case 6: engine_onoff(data, pidNo); break; 490 | } 491 | } //case 0 492 | break; 493 | case 1: 494 | { //6 meter 495 | /*page 1 496 | █ 1 █ █ 7 █ 497 | █ 2 █ █ 8 █ 498 | █ 3 █ █ 9 █ */ 499 | switch (pidNo) { 500 | case 0: plotArc(1, label, data, warn, min, max, digit); break; 501 | case 1: plotArc(2, label, data, warn, min, max, digit); break; 502 | case 2: plotArc(3, label, data, warn, min, max, digit); break; 503 | case 3: plotArc(7, label, data, warn, min, max, digit); break; 504 | case 4: plotArc(8, label, data, warn, min, max, digit); break; 505 | case 5: plotArc(9, label, data, warn, min, max, digit); break; 506 | case 6: engine_onoff(data, pidNo); break; 507 | } 508 | } //case 1 509 | break; 510 | case 2: 511 | { //6 meter 512 | /*page 2 513 | █ 1 █ █ 7 █ 514 | █ 2 █ █ 8 █ 515 | █ 3 █ █ 9 █ */ 516 | switch (pidNo) { 517 | case 0: plotNumeric(1, data, warn, digit); break; 518 | case 1: plotNumeric(2, data, warn, digit); break; 519 | case 2: plotNumeric(3, data, warn, digit); break; 520 | case 3: plotNumeric(7, data, warn, digit); break; 521 | case 4: plotNumeric(8, data, warn, digit); break; 522 | case 5: plotNumeric(9, data, warn, digit); break; 523 | case 6: engine_onoff(data, pidNo); break; 524 | } 525 | } //case 2 526 | break; 527 | case 3: 528 | { //5 meter 529 | /*page 3 530 | █ 1 █ █ █ 531 | █ 2 █ 3 4 532 | █ 3 █ █ █ */ 533 | switch (pidNo) { 534 | case 0: plotNumeric(1, data, warn, digit); break; 535 | case 1: plotNumeric(2, data, warn, digit); break; 536 | case 2: plotNumeric(3, data, warn, digit); break; 537 | case 3: plotVBar(3, data, warn, min, max, digit); break; 538 | case 4: plotVBar(4, data, warn, min, max, digit); break; 539 | case 5: engine_onoff(data, pidNo); break; 540 | case 6: engine_onoff(data, pidNo); break; 541 | } 542 | } //case 3 543 | break; 544 | case 4: 545 | { //5 meter 546 | /*page 4 547 | █ █ 4 █ █ 548 | 1 █ 5 █ 4 549 | █ █ 6 █ █ */ 550 | switch (pidNo) { 551 | case 0: plotVBar(1, data, warn, min, max, digit); break; 552 | case 1: plotNumeric(4, data, warn, digit); break; 553 | case 2: plotNumeric(5, data, warn, digit); break; 554 | case 3: plotNumeric(6, data, warn, digit); break; 555 | case 4: plotVBar(4, data, warn, min, max, digit); break; 556 | case 5: engine_onoff(data, pidNo); break; 557 | case 6: engine_onoff(data, pidNo); break; 558 | } 559 | } //case 4 560 | break; 561 | case 5: 562 | { //5 meter 563 | /*page 5 564 | █ █ █ 7 █ 565 | 1 2 █ 8 █ 566 | █ █ █ 9 █ */ 567 | switch (pidNo) { 568 | case 0: plotVBar(1, data, warn, min, max, digit); break; 569 | case 1: plotVBar(2, data, warn, min, max, digit); break; 570 | case 2: plotNumeric(7, data, warn, digit); break; 571 | case 3: plotNumeric(8, data, warn, digit); break; 572 | case 4: plotNumeric(9, data, warn, digit); break; 573 | case 5: engine_onoff(data, pidNo); break; 574 | case 6: engine_onoff(data, pidNo); break; 575 | } 576 | } //case 5 577 | break; 578 | case 6: 579 | { //4 meter 580 | /* page 6 581 | █ █ █ █ 582 | 1 2 3 4 583 | █ █ █ █ */ 584 | switch (pidNo) { 585 | case 0: plotVBar(1, data, warn, min, max, digit); break; 586 | case 1: plotVBar(2, data, warn, min, max, digit); break; 587 | case 2: plotVBar(3, data, warn, min, max, digit); break; 588 | case 3: plotVBar(4, data, warn, min, max, digit); break; 589 | case 4: engine_onoff(data, pidNo); break; 590 | case 5: engine_onoff(data, pidNo); break; 591 | case 6: engine_onoff(data, pidNo); break; 592 | } 593 | } //case 6 594 | break; 595 | case 7: 596 | { //5 meter 597 | /* page 7 598 | █ █ █ █ 599 | 1 2 3 4 600 | █ █ █ █ */ 601 | switch (pidNo) { 602 | case 0: plotVBar(1, data, warn, min, max, digit); break; 603 | case 1: plotVBar(2, data, warn, min, max, digit); break; 604 | case 2: plotVBar(3, data, warn, min, max, digit); break; 605 | case 3: plotVBar(4, data, warn, min, max, digit); break; 606 | case 4: engine_onoff(data, pidNo); break; 607 | case 5: engine_onoff(data, pidNo); break; 608 | case 6: engine_onoff(data, pidNo); break; 609 | } 610 | } //case 7 611 | break; 612 | } //switch page 613 | bt_message = ""; //reset 614 | } //updatemeter 615 | /*-------------------------------*/ 616 | 617 | //auto dim backlight function 618 | void autoDim() { 619 | int light = analogRead(LDR_PIN);//read light 620 | if (light > LIGHT_LEVEL) {//low light 621 | low_count++; 622 | if (low_count > 10) { 623 | low_count = 10;//low amb light 10 times count 624 | if (!dim) { 625 | ledcWrite(backlightChannel, 50);//dim light 626 | dim = true; 627 | #ifdef SERIAL_DEBUG 628 | Serial.println(F("Backlight -> dim")); 629 | #endif 630 | }//if !dim 631 | }//if low_count 632 | high_count = 0; 633 | 634 | } else { 635 | high_count++; 636 | if (high_count >10) { 637 | high_count = 10;//high amb light 10 times count 638 | if (dim) { 639 | ledcWrite(backlightChannel, 255);//brightest 640 | dim = false; 641 | #ifdef SERIAL_DEBUG 642 | Serial.println(F("Backlight -> bright")); 643 | #endif 644 | }//if dim 645 | }//if high_count 646 | low_count = 0; 647 | }//if light > lightlevel 648 | }//autodim 649 | //--------------------------- 650 | /*############################################*/ 651 | -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/nes_audio.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "nes_audio.h" 3 | 4 | static void (*fc_callback)(); 5 | 6 | Cartridge::Cartridge(uint8_t audio_pin) { 7 | single_output = audio_pin;//only single 1 pin use 8 | 9 | } 10 | 11 | void Cartridge::frame_counter_cb(void (*action)()){ 12 | fc_callback = action; 13 | } 14 | 15 | void Cartridge::init(){//call for 4 channel use only 16 | 17 | sigmaDeltaSetup(p1_pin, p1_channel, 88200); 18 | sigmaDeltaWrite(p1_channel, 0); 19 | 20 | sigmaDeltaSetup(p2_pin, p2_channel, 88200); 21 | sigmaDeltaWrite(p2_channel, 0); 22 | 23 | sigmaDeltaSetup(n_pin, n_channel, 88200); 24 | sigmaDeltaWrite(n_channel, 0); 25 | 26 | sigmaDeltaSetup(t_pin, t_channel, 88200); 27 | sigmaDeltaWrite(t_channel, 0); 28 | 29 | } 30 | 31 | void Cartridge::play_nes(const uint8_t* music, bool looping, float volume){ 32 | NES_PLAYING = true;//variable to control playback 33 | read_vgm_header(music); 34 | 35 | next_cycle = ESP.getCycleCount(); 36 | next_audio = ESP.getCycleCount(); 37 | while (NES_PLAYING) { 38 | uint32_t t_now = ESP.getCycleCount(); 39 | if (t_last > t_now) { 40 | next_cycle = t_now; 41 | next_audio = t_now; 42 | } 43 | 44 | if (t_now >= next_cycle) { 45 | next_cycle += cycle_period; 46 | clock_apu(); 47 | } 48 | 49 | if (t_now >= next_audio) { 50 | next_audio += audio_period; 51 | parse_vgm(music, looping); 52 | 53 | audio_counter++; 54 | if (audio_counter >= audio_divisor) { 55 | audio_counter = 0; 56 | sample_audio(); 57 | render_audio(volume); 58 | } 59 | } 60 | t_last = t_now; 61 | 62 | // if (digitalRead(27) == LOW) NES_PLAYING = false;//button press then stop 63 | }//while nes playing 64 | } 65 | 66 | void Cartridge::parse_vgm(const uint8_t* music, bool looping){ 67 | uint8_t extra = 0; 68 | if (vgm_wait == 0) { 69 | uint8_t command = music[vgm_index]; 70 | 71 | if (command == 0x61) { // Wait nL nH samples 72 | uint16_t result = music[vgm_index + 2]; 73 | result = result << 8; 74 | result += music[vgm_index + 1]; 75 | vgm_wait = result; 76 | extra = 2; 77 | } 78 | else if (command == 0x62) { // Wait 735 samples 79 | vgm_wait = 735; 80 | } 81 | else if (command == 0x63) { // Wait 882 samples 82 | vgm_wait = 882; 83 | } 84 | else if (command == 0x67 && music[vgm_index + 1] == 0x66) { // Start of data block 85 | uint32_t block_size = (music[vgm_index + 3] ) + 86 | (music[vgm_index + 4] << 8) + 87 | (music[vgm_index + 5] << 16) + 88 | (music[vgm_index + 6] << 24); 89 | vgm_index += block_size - 1; // Basically ignore all data block contents 90 | } 91 | else if (command == 0x66 || vgm_index == VGM_EOF_OFFSET){ 92 | Serial.println("END"); 93 | if(looping) { 94 | if(VGM_LOOP_OFFSET == 0) { // Loop offset is not built into file 95 | vgm_index = VGM_DATA_OFFSET - 1; // Restart the song entirely 96 | } 97 | else { 98 | vgm_index = VGM_LOOP_OFFSET - 1; // Music loops, reset to looping byte 99 | } 100 | } 101 | else { 102 | reset_nes(); // Reset if loop opt-out 103 | } 104 | } 105 | else if (command >= 0x70 && command < 0x80) { // Wait (0x7)n+1 samples 106 | uint8_t result = command; 107 | result = result << 4; 108 | result = result >> 4; 109 | vgm_wait = result + 1; 110 | } 111 | else if (command == 0xB4) { // NES APU write dd to aa 112 | uint8_t value = music[vgm_index + 2]; 113 | uint8_t reg = music[vgm_index + 1]; 114 | 115 | write_reg(reg, value); 116 | extra = 2; 117 | } 118 | 119 | vgm_index += 1; 120 | vgm_index += extra; 121 | } 122 | else { 123 | vgm_wait--; 124 | } 125 | } 126 | 127 | void Cartridge::read_vgm_header(const uint8_t* music){ 128 | VGM_EOF_OFFSET = get_32_bit(music, 0x04) + 0x04; 129 | VGM_TOTAL_NUM_SAMPLES = get_32_bit(music, 0x18); 130 | VGM_RATE = get_32_bit(music, 0x24); 131 | VGM_DATA_OFFSET = get_32_bit(music, 0x34) + 0x34; 132 | VGM_NES_APU_CLOCK = get_32_bit(music, 0x84); 133 | VGM_LOOP_OFFSET = get_32_bit(music, 0x1C) + 0x1C; 134 | 135 | vgm_index = VGM_DATA_OFFSET; 136 | } 137 | 138 | uint32_t Cartridge::get_32_bit(const uint8_t* music, uint32_t index){ 139 | uint32_t result = music[index + 3]; 140 | result = result << 8; 141 | result += music[index + 2]; 142 | result = result << 8; 143 | result += music[index + 1]; 144 | result = result << 8; 145 | result += music[index]; 146 | 147 | return result; 148 | } 149 | 150 | void Cartridge::reset_nes(){ 151 | for (uint8_t i = 0; i < 24; i++) { 152 | NES_REG[i] = B00000000; 153 | } 154 | single_output = 0; 155 | // Pulse 1 Variables 156 | p1_output = 0; 157 | p1_11_bit_timer = 0; 158 | p1_wave_index = 0; 159 | p1_length_counter = 0; 160 | p1_envelope_divider = 0; 161 | p1_decay_counter = 0; 162 | p1_volume = 0; 163 | p1_channel = 0; 164 | 165 | // Pulse 2 Variables 166 | p2_output = 0; 167 | p2_11_bit_timer = 0; 168 | p2_wave_index = 0; 169 | p2_length_counter = 0; 170 | p2_envelope_divider = 0; 171 | p2_decay_counter = 0; 172 | p2_volume = 0; 173 | p2_channel = 1; 174 | 175 | // Noise Variables 176 | n_output = 0; 177 | n_timer = 0; 178 | n_length_counter = 0; 179 | n_envelope_divider = 0; 180 | n_decay_counter = 0; 181 | n_volume = 0; 182 | n_xor = 0; 183 | n_lsfr = 1; 184 | n_channel = 2; 185 | 186 | // Triangle Variables 187 | t_output = 0; 188 | t_11_bit_timer = 0; 189 | t_wave_index = 0; 190 | t_length_counter = 0; 191 | t_linear_counter = 0; 192 | t_channel = 3; 193 | 194 | vgm_index = 0; 195 | vgm_wait = 0; 196 | NES_PLAYING = false; 197 | 198 | next_audio = 0; 199 | next_cycle = 0; 200 | cpu_cycles = 0; 201 | apu_cycles = 0; 202 | 203 | t_last = 0; 204 | 205 | audio_counter = 0; 206 | } 207 | 208 | void Cartridge::sample_audio(){ 209 | // Pulse 1 210 | if (p1_length_counter > 0) { 211 | if (p1_11_bit_timer >= 8) { 212 | p1_output = p1_volume * duty_table[get_duty(0x00)][p1_wave_index]; 213 | } 214 | } 215 | else { 216 | p1_output = 0; 217 | } 218 | 219 | // Pulse 2 220 | if (p2_length_counter > 0) { 221 | if (p2_11_bit_timer >= 8) { 222 | p2_output = p2_volume * duty_table[get_duty(0x04)][p2_wave_index]; 223 | } 224 | } 225 | else { 226 | p2_output = 0; 227 | } 228 | 229 | // Noise 230 | if (n_length_counter > 0) { 231 | n_output = n_volume * n_xor; 232 | } 233 | else { 234 | n_output = 0; 235 | } 236 | 237 | // Triangle 238 | if (t_length_counter > 0) { 239 | t_output = tri_table[t_wave_index]; 240 | } 241 | else { 242 | t_output = 0; 243 | } 244 | } 245 | 246 | void Cartridge::render_audio(float gain){ 247 | // four channel 248 | /* 249 | sigmaDeltaWrite(p1_channel, p1_output * 12); 250 | sigmaDeltaWrite(p2_channel, p2_output * 12); 251 | sigmaDeltaWrite(n_channel, n_output * 6); 252 | sigmaDeltaWrite(t_channel, t_output * 16); 253 | */ 254 | // single channel 255 | 256 | uint8_t sum = 0; 257 | sum+=p1_output*0.8; 258 | sum+=p2_output*0.8; 259 | sum+=n_output*0.7; 260 | sum+=t_output; 261 | //sigmaDeltaWrite(t_pin,sum); 262 | dacWrite(single_output,sum * gain);//gain volume to 0.5 or can be to 1.5 263 | 264 | 265 | } 266 | 267 | void Cartridge::clock_apu(){ 268 | clock_frame_counter(); 269 | decrement_timers(); 270 | apu_cycles += 2; 271 | } 272 | 273 | void Cartridge::decrement_timers(){ 274 | // Pulse 1 275 | if (p1_11_bit_timer <= 0) { 276 | p1_11_bit_timer = get_11_bit_timer(0x02, 0x03); 277 | if (p1_wave_index == 0) { 278 | p1_wave_index = 7; 279 | } 280 | else { 281 | p1_wave_index--; 282 | } 283 | } 284 | else { 285 | p1_11_bit_timer -= 2; 286 | } 287 | 288 | // Pulse 2 289 | if (p2_11_bit_timer <= 0) { 290 | p2_11_bit_timer = get_11_bit_timer(0x06, 0x07); 291 | if (p2_wave_index == 0) { 292 | p2_wave_index = 7; 293 | } 294 | else { 295 | p2_wave_index--; 296 | } 297 | } 298 | else { 299 | p2_11_bit_timer -= 2; 300 | } 301 | 302 | // Noise 303 | if (n_timer <= 0) { 304 | uint8_t index = get_reg(0x0E); 305 | index = index << 4; 306 | index = index >> 4; 307 | n_timer = noise_table[index]; 308 | 309 | clock_lsfr(); 310 | } 311 | else { 312 | n_timer -= 2; 313 | } 314 | 315 | // Triangle 316 | if (t_11_bit_timer <= 0) { 317 | t_11_bit_timer = get_11_bit_timer(0x0A, 0x0B) + 1; 318 | 319 | if (t_length_counter > 0 && t_linear_counter > 0) { 320 | if (t_wave_index == 0) { 321 | t_wave_index = 31; 322 | } 323 | else { 324 | t_wave_index--; 325 | } 326 | } 327 | } 328 | else { 329 | t_11_bit_timer -= 2; 330 | t_11_bit_timer -= 2; // Triangle timer clocked on CPU not APU (2x speed) 331 | } 332 | } 333 | 334 | void Cartridge::clock_lsfr(){ 335 | uint8_t bit1 = 0; 336 | uint8_t bit2 = 1; 337 | if (get_bit(0x0E, 7) == 1) { 338 | bit2 = 6; 339 | } 340 | uint8_t val1 = bitRead(n_lsfr, bit1); 341 | uint8_t val2 = bitRead(n_lsfr, bit2); 342 | n_xor = val1 ^ val2; 343 | 344 | n_lsfr = n_lsfr >> 1; 345 | bitWrite(n_lsfr, 14, n_xor); 346 | } 347 | 348 | void Cartridge::clock_frame_counter(){ 349 | if (frame_counter_mode() == 0) { // 4-step sequence 350 | if (apu_cycles == 3728) { 351 | clock_envelopes(); 352 | clock_linear_counter(); 353 | 354 | if (fc_callback) 355 | fc_callback(); 356 | } 357 | else if (apu_cycles == 7456) { 358 | clock_envelopes(); 359 | clock_linear_counter(); 360 | clock_length_counters(); 361 | clock_sweep_units(); 362 | } 363 | else if (apu_cycles == 11186) { 364 | clock_envelopes(); 365 | clock_linear_counter(); 366 | } 367 | else if (apu_cycles == 14914) { 368 | clock_envelopes(); 369 | clock_linear_counter(); 370 | clock_length_counters(); 371 | clock_sweep_units(); 372 | apu_cycles = 0; 373 | } 374 | } 375 | else if (frame_counter_mode() == 1) { // 5-step sequence 376 | if (apu_cycles == 3728) { 377 | clock_envelopes(); 378 | clock_linear_counter(); 379 | 380 | if (fc_callback) 381 | fc_callback(); 382 | } 383 | else if (apu_cycles == 7456) { 384 | clock_envelopes(); 385 | clock_linear_counter(); 386 | clock_length_counters(); 387 | clock_sweep_units(); 388 | } 389 | else if (apu_cycles == 11186) { 390 | clock_envelopes(); 391 | clock_linear_counter(); 392 | } 393 | else if (apu_cycles == 18640) { 394 | clock_envelopes(); 395 | clock_linear_counter(); 396 | clock_length_counters(); 397 | clock_sweep_units(); 398 | apu_cycles = 0; 399 | } 400 | } 401 | } 402 | 403 | uint8_t Cartridge::frame_counter_mode(){ 404 | return get_bit(0x17, 7); 405 | } 406 | 407 | uint8_t Cartridge::get_bit(uint8_t reg, uint8_t b){ 408 | return bitRead(get_reg(reg), b); 409 | } 410 | 411 | void Cartridge::write_bit(uint8_t reg, uint8_t b, uint8_t val){ 412 | uint8_t r = get_reg(reg); 413 | bitWrite(r, b, val); 414 | write_reg(reg, r); // used to make sure conditions defined in write_reg() are used here also 415 | } 416 | 417 | uint8_t Cartridge::get_reg(uint8_t reg){ 418 | return NES_REG[reg]; 419 | } 420 | 421 | void Cartridge::write_reg(uint8_t reg, uint8_t val){ 422 | // PULSE 1 ---------------------------------------- 423 | if (reg == 0x03) { 424 | // Pulse 1 load length counter from table address 425 | uint8_t level = bitRead(val, 3); 426 | uint8_t index = val >> 4; 427 | p1_length_counter = length_table[level][index]; 428 | p1_wave_index = 0; 429 | 430 | p1_decay_counter = 15; 431 | p1_envelope_divider = 0; 432 | 433 | } 434 | else if (reg == 0x15) { 435 | // Pulse 1 436 | if (bitRead(val, 0) == 0) { // if p1 enabled bit clear, empty length counter 437 | write_length_counter(0x03, 0); 438 | } 439 | 440 | //clear DMC interrupt flag 441 | } 442 | 443 | // PULSE 2 ---------------------------------------- 444 | if (reg == 0x07) { 445 | // Pulse 2 load length counter from table address 446 | uint8_t level = bitRead(val, 3); 447 | uint8_t index = val >> 4; 448 | p2_length_counter = length_table[level][index]; 449 | p2_wave_index = 0; 450 | 451 | p2_decay_counter = 15; 452 | p2_envelope_divider = 0; 453 | 454 | } 455 | else if (reg == 0x15) { 456 | // Pulse 2 457 | if (bitRead(val, 1) == 0) { // if p2 enabled bit clear, empty length counter 458 | write_length_counter(0x07, 0); 459 | } 460 | 461 | //clear DMC interrupt flag 462 | } 463 | 464 | // NOISE ---------------------------------------- 465 | if (reg == 0x0F) { 466 | // Noise load length counter from table address 467 | uint8_t level = bitRead(val, 3); 468 | uint8_t index = val >> 4; 469 | n_length_counter = length_table[level][index]; 470 | 471 | n_decay_counter = 15; 472 | n_envelope_divider = 0; 473 | 474 | } 475 | else if (reg == 0x15) { 476 | // Noise 477 | if (bitRead(val, 3) == 0) { // if noise enabled bit clear, empty length counter 478 | write_length_counter(0x0F, 0); 479 | } 480 | 481 | //clear DMC interrupt flag 482 | } 483 | 484 | // TRIANGLE ---------------------------------------- 485 | if (reg == 0x0B) { 486 | // Triangle load length counter from table address 487 | uint8_t level = bitRead(val, 3); 488 | uint8_t index = val >> 4; 489 | t_length_counter = length_table[level][index]; 490 | 491 | // Triangle load linear counter from register 492 | uint8_t linc = get_reg(0x08); 493 | linc = linc << 1; 494 | linc = linc >> 1; 495 | t_linear_counter = linc; 496 | 497 | write_bit(0x08, 7, 1); 498 | } 499 | else if (reg == 0x15) { 500 | 501 | //clear DMC interrupt flag 502 | } 503 | 504 | NES_REG[reg] = val; 505 | } 506 | 507 | void Cartridge::clock_envelopes(){ 508 | // Pulse 1 509 | uint8_t p1_envelope_disable = bitRead(get_reg(0x00), 4); 510 | if (p1_envelope_disable) { 511 | uint8_t v = get_reg(0x00); 512 | v = v << 4; 513 | v = v >> 4; 514 | p1_volume = v; 515 | } 516 | else { 517 | uint8_t p1_envelope_loop = bitRead(get_reg(0x00), 5); 518 | 519 | if (p1_envelope_divider > 0) { 520 | p1_envelope_divider--; 521 | } 522 | else { 523 | uint8_t d = get_reg(0x00); 524 | d = d << 4; 525 | d = d >> 4; 526 | p1_envelope_divider = d; 527 | 528 | //clock decay counter 529 | if (p1_decay_counter > 0) { 530 | p1_decay_counter--; 531 | } 532 | else { 533 | if (p1_envelope_loop == 1) { 534 | p1_decay_counter = 15; 535 | } 536 | } 537 | } 538 | 539 | p1_volume = p1_decay_counter; 540 | } 541 | 542 | // Pulse 2 543 | uint8_t p2_envelope_disable = bitRead(get_reg(0x04), 4); 544 | if (p2_envelope_disable) { 545 | uint8_t v = get_reg(0x04); 546 | v = v << 4; 547 | v = v >> 4; 548 | p2_volume = v; 549 | } 550 | else { 551 | uint8_t p2_envelope_loop = bitRead(get_reg(0x04), 5); 552 | 553 | if (p2_envelope_divider > 0) { 554 | p2_envelope_divider--; 555 | } 556 | else { 557 | uint8_t d = get_reg(0x04); 558 | d = d << 4; 559 | d = d >> 4; 560 | p2_envelope_divider = d; 561 | 562 | //clock decay counter 563 | if (p2_decay_counter > 0) { 564 | p2_decay_counter--; 565 | } 566 | else { 567 | if (p2_envelope_loop == 1) { 568 | p2_decay_counter = 15; 569 | } 570 | } 571 | } 572 | 573 | p2_volume = p2_decay_counter; 574 | } 575 | 576 | // Noise 577 | uint8_t n_envelope_disable = bitRead(get_reg(0x0C), 4); 578 | if (n_envelope_disable) { 579 | uint8_t v = get_reg(0x0C); 580 | v = v << 4; 581 | v = v >> 4; 582 | n_volume = v; 583 | } 584 | else { 585 | uint8_t n_envelope_loop = bitRead(get_reg(0x0C), 5); 586 | 587 | if (n_envelope_divider > 0) { 588 | n_envelope_divider--; 589 | } 590 | else { 591 | uint8_t d = get_reg(0x0C); 592 | d = d << 4; 593 | d = d >> 4; 594 | n_envelope_divider = d; 595 | 596 | //clock decay counter 597 | if (n_decay_counter > 0) { 598 | n_decay_counter--; 599 | } 600 | else { 601 | if (n_envelope_loop == 1) { 602 | n_decay_counter = 15; 603 | } 604 | } 605 | } 606 | 607 | n_volume = n_decay_counter; 608 | } 609 | } 610 | 611 | void Cartridge::clock_linear_counter(){ 612 | if (get_bit(0x08, 7) == 1) { 613 | if (t_linear_counter > 0) { 614 | t_linear_counter--; 615 | } 616 | } 617 | } 618 | 619 | void Cartridge::clock_length_counters(){ 620 | uint8_t length_counter_halt_flag = 0; 621 | 622 | // Pulse 1 623 | length_counter_halt_flag = get_bit(0x00, 5); 624 | if (length_counter_halt_flag == 0) { // Pulse 1 halt flag not set? decrement length counter 625 | if (p1_length_counter > 0) { 626 | p1_length_counter--; 627 | } 628 | } 629 | 630 | // Pulse 2 631 | length_counter_halt_flag = get_bit(0x04, 5); 632 | if (length_counter_halt_flag == 0) { // Pulse 2 halt flag not set? decrement length counter 633 | if (p2_length_counter > 0) { 634 | p2_length_counter--; 635 | } 636 | } 637 | 638 | // Noise 639 | length_counter_halt_flag = get_bit(0x0C, 5); 640 | if (length_counter_halt_flag == 0) { // Noise halt flag not set? decrement length counter 641 | if (n_length_counter > 0) { 642 | n_length_counter--; 643 | } 644 | } 645 | 646 | // Triangle 647 | length_counter_halt_flag = get_bit(0x08, 7); 648 | if (length_counter_halt_flag == 0) { // Triangle halt flag not set? decrement length counter 649 | if (t_length_counter > 0) { 650 | t_length_counter--; 651 | } 652 | } 653 | } 654 | 655 | uint8_t Cartridge::get_duty(uint8_t reg){ 656 | uint8_t r = get_reg(reg); 657 | r = r >> 6; 658 | return r; 659 | } 660 | 661 | uint16_t Cartridge::get_11_bit_timer(uint8_t reg_low, uint8_t reg_high){ 662 | uint8_t rlow = get_reg(reg_low); 663 | uint8_t rhigh = get_reg(reg_high); 664 | rhigh = rhigh << 5; 665 | rhigh = rhigh >> 5; 666 | uint16_t r16 = rhigh; 667 | r16 = r16 << 8; 668 | r16 = r16 + rlow; 669 | return r16; 670 | } 671 | 672 | void Cartridge::set_11_bit_timer(uint8_t reg_low, uint8_t reg_high, uint16_t val){ 673 | uint16_t out_low = val; 674 | out_low = out_low << 8; 675 | out_low = out_low >> 8; 676 | uint8_t ol = out_low; 677 | 678 | uint16_t out_high = val; 679 | out_high = out_high >> 8; 680 | uint8_t oh = out_high; 681 | 682 | uint8_t r_high = get_reg(reg_high); 683 | 684 | uint16_t val_high = r_high; 685 | val_high = val_high >> 3; 686 | val_high = val_high << 3; 687 | val_high = val_high + oh; 688 | 689 | write_reg(reg_low, ol); 690 | write_reg(reg_high, val_high); 691 | } 692 | 693 | uint8_t Cartridge::get_length_counter(uint8_t reg){ 694 | uint8_t r = get_reg(reg); 695 | r = r >> 3; 696 | return r; 697 | } 698 | 699 | uint8_t Cartridge::write_length_counter(uint8_t reg, uint8_t val){ 700 | uint8_t r = get_reg(reg); 701 | r = r << 5; 702 | r = r >> 5; 703 | val = val << 3; 704 | uint8_t result = r + val; 705 | write_reg(reg, result); 706 | return result; 707 | } 708 | 709 | void Cartridge::clock_sweep_units(){ 710 | } 711 | -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/nes_audio.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | https://github.com/connornishijima/Cartridge 4 | Cartridge.cpp - Library for parsing VGM into an NES APU emulation! 5 | Created by Connor Nishijima, October 14th 2018. 6 | Released under the GPLv3 license. 7 | */ 8 | 9 | #ifndef cartridge_h 10 | #define cartridge_h 11 | 12 | #include "Arduino.h" 13 | 14 | class Cartridge 15 | { 16 | public: 17 | Cartridge(uint8_t audio_pin); 18 | 19 | void init(); 20 | void play_nes(const uint8_t* music, bool looping = false, float volume = 1.0); 21 | void frame_counter_cb(void (*action)()); 22 | 23 | private: 24 | void parse_vgm(const uint8_t* music, bool looping); 25 | void read_vgm_header(const uint8_t* music); 26 | uint32_t get_32_bit(const uint8_t* music, uint32_t index); 27 | void reset_nes(); 28 | void sample_audio(); 29 | void render_audio(float gain); 30 | void clock_apu(); 31 | void decrement_timers(); 32 | void clock_lsfr(); 33 | void clock_frame_counter(); 34 | uint8_t frame_counter_mode(); 35 | uint8_t get_bit(uint8_t reg, uint8_t b); 36 | void write_bit(uint8_t reg, uint8_t b, uint8_t val); 37 | uint8_t get_reg(uint8_t reg); 38 | void write_reg(uint8_t reg, uint8_t val); 39 | void clock_envelopes(); 40 | void clock_linear_counter(); 41 | void clock_length_counters(); 42 | uint8_t get_duty(uint8_t reg); 43 | uint16_t get_11_bit_timer(uint8_t reg_low, uint8_t reg_high); 44 | void set_11_bit_timer(uint8_t reg_low, uint8_t reg_high, uint16_t val); 45 | uint8_t get_length_counter(uint8_t reg); 46 | uint8_t write_length_counter(uint8_t reg, uint8_t val); 47 | void clock_sweep_units(); 48 | 49 | uint8_t NES_REG[24] = { 50 | // PULSE 1 51 | B00000000, // $4000 52 | B00000000, // $4001 53 | B00000000, // $4002 54 | B00000000, // $4003 55 | 56 | // PULSE 2 57 | B00000000, // $4004 58 | B00000000, // $4005 59 | B00000000, // $4006 60 | B00000000, // $4007 61 | 62 | // TRIANGLE 63 | B00000000, // $4008 64 | 65 | B00000000, // $4009 (UNUSED) 66 | 67 | B00000000, // $400A 68 | B00000000, // $400B 69 | 70 | // NOISE 71 | B00000000, // $400C 72 | B00000000, // $400E 73 | B00000000, // $400F 74 | 75 | // DMC 76 | B00000000, // $4010 77 | B00000000, // $4011 78 | B00000000, // $4012 79 | B00000000, // $4013 80 | 81 | B00000000, // $4014 (UNUSED) 82 | 83 | // Control/Status 84 | B00000000, // $4015 85 | 86 | B00000000, // $4016 (UNUSED) 87 | 88 | // Frame Counter 89 | B00000000, // $4017 90 | }; 91 | 92 | const uint32_t NES_APU_FREQ = 1789773 / 2 / 2; // APU is half speed of NES CPU, and we are running half the resolution of that to stay light. 93 | const uint32_t cycle_period = F_CPU / NES_APU_FREQ; 94 | 95 | const uint16_t audio_rate = 44100; 96 | const uint32_t audio_period = F_CPU / audio_rate; 97 | uint32_t next_audio = 0; 98 | 99 | uint32_t next_cycle = 0; 100 | uint32_t cpu_cycles = 0; 101 | uint32_t apu_cycles = 0; 102 | 103 | uint32_t t_last = 0; 104 | uint32_t cycles_delta = 0; 105 | uint32_t cycles_so_far = 0; 106 | 107 | const uint8_t audio_divisor = 2; 108 | uint8_t audio_counter = 0; 109 | 110 | const uint8_t length_table[2][16] = { 111 | {0x0A, 0x14, 0x28, 0x50, 0xA0, 0x3C, 0x0E, 0x1A, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x48, 0x10, 0x20}, 112 | {0xFE, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E} 113 | }; 114 | 115 | const uint8_t duty_table[4][8] = { 116 | {0, 0, 0, 0, 0, 0, 0, 1}, // 12.5% 117 | {0, 0, 0, 0, 0, 0, 1, 1}, // 25% 118 | {0, 0, 0, 0, 1, 1, 1, 1}, // 50% 119 | {1, 1, 1, 1, 1, 1, 0, 0}, // 25% (inv.) 120 | }; 121 | 122 | const uint16_t noise_table[16] = { 123 | 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 124 | }; 125 | 126 | const uint8_t tri_table[32] = { 127 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 128 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 129 | }; 130 | uint8_t single_output = 0; 131 | // Pulse 1 Variables 132 | uint8_t p1_output = 0; 133 | int16_t p1_11_bit_timer = 0; 134 | uint8_t p1_wave_index = 0; 135 | uint16_t p1_length_counter = 0; 136 | uint8_t p1_envelope_divider = 0; 137 | uint8_t p1_decay_counter = 0; 138 | uint8_t p1_volume = 0; 139 | uint8_t p1_channel = 0; 140 | uint8_t p1_pin = 12; 141 | 142 | // Pulse 2 Variables 143 | uint8_t p2_output = 0; 144 | int16_t p2_11_bit_timer = 0; 145 | uint8_t p2_wave_index = 0; 146 | uint16_t p2_length_counter = 0; 147 | uint8_t p2_envelope_divider = 0; 148 | uint8_t p2_decay_counter = 0; 149 | uint8_t p2_volume = 0; 150 | uint8_t p2_channel = 1; 151 | uint8_t p2_pin = 14; 152 | 153 | // Noise Variables 154 | uint8_t n_output = 0; 155 | int16_t n_timer = 0; 156 | uint16_t n_length_counter = 0; 157 | uint8_t n_envelope_divider = 0; 158 | uint8_t n_decay_counter = 0; 159 | uint8_t n_volume = 0; 160 | uint8_t n_xor = 0; 161 | uint16_t n_lsfr = 1; 162 | uint8_t n_channel = 2; 163 | uint8_t n_pin = 27; 164 | 165 | // Triangle Variables 166 | uint8_t t_output = 0; 167 | int16_t t_11_bit_timer = 0; 168 | uint8_t t_wave_index = 0; 169 | uint16_t t_length_counter = 0; 170 | uint16_t t_linear_counter = 0; 171 | uint8_t t_channel = 3; 172 | uint8_t t_pin = 26; 173 | 174 | uint32_t vgm_index = 0; 175 | uint16_t vgm_wait = 0; 176 | bool NES_PLAYING = false; 177 | 178 | uint32_t VGM_EOF_OFFSET = 0; 179 | uint32_t VGM_TOTAL_NUM_SAMPLES = 0; 180 | uint32_t VGM_RATE = 0; 181 | uint32_t VGM_DATA_OFFSET = 0; 182 | uint32_t VGM_NES_APU_CLOCK = 0; 183 | uint32_t VGM_LOOP_OFFSET = 0; 184 | 185 | }; 186 | 187 | #endif 188 | -------------------------------------------------------------------------------- /sketch/VaAndCobOBD2Gauge/touchscreen.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | 5 | // Touchscreen pins 6 | #define XPT2046_IRQ 36 // T_IRQ 7 | #define XPT2046_MOSI 32 // T_DIN 8 | #define XPT2046_MISO 39 // T_OUT 9 | #define XPT2046_CLK 25 // T_CLK 10 | #define XPT2046_CS 33 // T_CS 11 | 12 | SPIClass touchscreenSPI = SPIClass(VSPI); 13 | XPT2046_Touchscreen ts(XPT2046_CS, XPT2046_IRQ); 14 | 15 | #define TFT_GREY 0x5AEB 16 | 17 | #define _height 240 18 | #define _width 320 19 | #define CALIBRATION_FILE "/TouchCalData1"//save calibrate file 20 | // Initialise with example calibration values so processor does not crash if setTouch() not called in setup() 21 | uint16_t touchCalibration_x0 = 300, touchCalibration_x1 = 3600, touchCalibration_y0 = 300, touchCalibration_y1 = 3600; 22 | uint8_t touchCalibration_rotate = 1, touchCalibration_invert_x = 2, touchCalibration_invert_y = 0; 23 | long _pressTime; 24 | 25 | //------------------------------------------------------------------------------------------ 26 | //load calibration data 27 | void setTouch(uint16_t *parameters){ 28 | touchCalibration_x0 = parameters[0]; 29 | touchCalibration_x1 = parameters[1]; 30 | touchCalibration_y0 = parameters[2]; 31 | touchCalibration_y1 = parameters[3]; 32 | 33 | if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; 34 | if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; 35 | if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; 36 | if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; 37 | 38 | touchCalibration_rotate = parameters[4] & 0x01; 39 | touchCalibration_invert_x = parameters[4] & 0x02; 40 | touchCalibration_invert_y = parameters[4] & 0x04; 41 | } 42 | //------------------------------------- 43 | //calibrate touch screen within windows 0,0,320,240 44 | void calibrateTouch(uint16_t *parameters, uint32_t color_fg, uint32_t color_bg, uint8_t size){ 45 | int16_t values[] = {0,0,0,0,0,0,0,0}; 46 | 47 | for(uint8_t i = 0; i<4; i++){ 48 | tft.fillRect(0, 0, size+1, size+1, color_bg); 49 | tft.fillRect(0, _height-size-1, size+1, size+1, color_bg); 50 | tft.fillRect(_width-size-1, 0, size+1, size+1, color_bg); 51 | tft.fillRect(_width-size-1, _height-size-1, size+1, size+1, color_bg); 52 | 53 | if (i == 5) break; // used to clear the arrows 54 | 55 | switch (i) { 56 | case 0: // up left 57 | tft.drawLine(0, 0, 0, size, color_fg); 58 | tft.drawLine(0, 0, size, 0, color_fg); 59 | tft.drawLine(0, 0, size , size, color_fg); 60 | break; 61 | case 1: // bot left 62 | tft.drawLine(0, _height-size-1, 0, _height-1, color_fg); 63 | tft.drawLine(0, _height-1, size, _height-1, color_fg); 64 | tft.drawLine(size, _height-size-1, 0, _height-1 , color_fg); 65 | break; 66 | case 2: // up right 67 | tft.drawLine(_width-size-1, 0, _width-1, 0, color_fg); 68 | tft.drawLine(_width-size-1, size, _width-1, 0, color_fg); 69 | tft.drawLine(_width-1, size, _width-1, 0, color_fg); 70 | break; 71 | case 3: // bot right 72 | tft.drawLine(_width-size-1, _height-size-1, _width-1, _height-1, color_fg); 73 | tft.drawLine(_width-1, _height-1-size, _width-1, _height-1, color_fg); 74 | tft.drawLine(_width-1-size, _height-1, _width-1, _height-1, color_fg); 75 | break; 76 | } 77 | 78 | // user has to get the chance to release 79 | if(i>0) delay(1000); 80 | 81 | for(uint8_t j= 0; j<8; j++){ 82 | // Use a lower detect threshold as corners tend to be less sensitive 83 | while(!ts.touched()); 84 | TS_Point p = ts.getPoint(); 85 | values[i*2 ] += p.x; 86 | values[i*2+1] += p.y; 87 | } 88 | values[i*2 ] /= 8; 89 | values[i*2+1] /= 8; 90 | } 91 | 92 | // from case 0 to case 1, the y value changed. 93 | // If the measured delta of the touch x axis is bigger than the delta of the y axis, the touch and TFT axes are switched. 94 | touchCalibration_rotate = false; 95 | if(abs(values[0]-values[2]) > abs(values[1]-values[3])){ 96 | touchCalibration_rotate = true; 97 | touchCalibration_x0 = (values[1] + values[3])/2; // calc min x 98 | touchCalibration_x1 = (values[5] + values[7])/2; // calc max x 99 | touchCalibration_y0 = (values[0] + values[4])/2; // calc min y 100 | touchCalibration_y1 = (values[2] + values[6])/2; // calc max y 101 | } else { 102 | touchCalibration_x0 = (values[0] + values[2])/2; // calc min x 103 | touchCalibration_x1 = (values[4] + values[6])/2; // calc max x 104 | touchCalibration_y0 = (values[1] + values[5])/2; // calc min y 105 | touchCalibration_y1 = (values[3] + values[7])/2; // calc max y 106 | } 107 | 108 | // in addition, the touch screen axis could be in the opposite direction of the TFT axis 109 | touchCalibration_invert_x = false; 110 | if(touchCalibration_x0 > touchCalibration_x1){ 111 | values[0]=touchCalibration_x0; 112 | touchCalibration_x0 = touchCalibration_x1; 113 | touchCalibration_x1 = values[0]; 114 | touchCalibration_invert_x = true; 115 | } 116 | touchCalibration_invert_y = false; 117 | if(touchCalibration_y0 > touchCalibration_y1){ 118 | values[0]=touchCalibration_y0; 119 | touchCalibration_y0 = touchCalibration_y1; 120 | touchCalibration_y1 = values[0]; 121 | touchCalibration_invert_y = true; 122 | } 123 | 124 | // pre calculate 125 | touchCalibration_x1 -= touchCalibration_x0; 126 | touchCalibration_y1 -= touchCalibration_y0; 127 | 128 | if(touchCalibration_x0 == 0) touchCalibration_x0 = 1; 129 | if(touchCalibration_x1 == 0) touchCalibration_x1 = 1; 130 | if(touchCalibration_y0 == 0) touchCalibration_y0 = 1; 131 | if(touchCalibration_y1 == 0) touchCalibration_y1 = 1; 132 | 133 | // export parameters, if pointer valid 134 | if(parameters != NULL){ 135 | parameters[0] = touchCalibration_x0; 136 | parameters[1] = touchCalibration_x1; 137 | parameters[2] = touchCalibration_y0; 138 | parameters[3] = touchCalibration_y1; 139 | parameters[4] = touchCalibration_rotate | (touchCalibration_invert_x <<1) | (touchCalibration_invert_y <<2); 140 | /* 141 | Serial.println("x0 = " + String(touchCalibration_x0)); 142 | Serial.println("x1 = " + String(touchCalibration_x1)); 143 | Serial.println("y0 = " + String(touchCalibration_y0)); 144 | Serial.println("y1 = " + String(touchCalibration_y1)); 145 | */ 146 | } 147 | } 148 | //------------------------------------- 149 | void touch_calibrate() {//calibrate touch screen 150 | uint16_t calData[5]; 151 | uint8_t calDataOK = 0; 152 | // check file system exists 153 | if (!SPIFFS.begin()) { 154 | Serial.println(F("Formating file system")); 155 | SPIFFS.format(); 156 | SPIFFS.begin(); 157 | } 158 | // check if calibration file exists and size is correct 159 | if (SPIFFS.exists(CALIBRATION_FILE)) { 160 | File f = SPIFFS.open(CALIBRATION_FILE, "r"); 161 | if (f) { 162 | if (f.readBytes((char *)calData, 14) == 14) 163 | calDataOK = 1; 164 | f.close(); 165 | }//if f 166 | } 167 | //if can read calibrate data from spiffs file AND button not push 168 | if (calDataOK && (digitalRead(SELECTOR_PIN) == HIGH)) { 169 | setTouch(calData); // calibration data valid 170 | } else { 171 | // data not valid or press button during start 172 | Serial.println(F("Touch screen calibration...")); 173 | tft.fillScreen(TFT_BLACK); 174 | tft.setTextColor(TFT_BLACK, TFT_CYAN); 175 | tft.drawCentreString("Touch screen calibration",tft.width()/2,tft.height()/2-10,2); 176 | tft.setCursor(20, 0); 177 | tft.setTextFont(2); 178 | tft.setTextSize(1); 179 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 180 | tft.println(F("Touch corners as indicated")); 181 | tft.setTextFont(1); 182 | tft.println(); 183 | calibrateTouch(calData, TFT_MAGENTA, TFT_BLACK, 15); 184 | tft.setTextColor(TFT_GREEN, TFT_BLACK); 185 | tft.println(F("Calibration complete!")); 186 | SPIFFS.remove(CALIBRATION_FILE);//Delete if we want to re-calibrate 187 | File f = SPIFFS.open(CALIBRATION_FILE, "w"); // store data 188 | if (f) { 189 | f.write((const unsigned char *)calData, 14); 190 | f.close(); 191 | }//if f 192 | }//else calDataOK 193 | }//touch calibrate 194 | 195 | 196 | //-------------------------- 197 | //get touch point 198 | bool getTouch(uint16_t *x, uint16_t *y) { 199 | int threshold; 200 | 201 | if (ts.touched()) { 202 | TS_Point p = ts.getPoint(); 203 | uint16_t x_tmp = p.x, y_tmp = p.y, xx, yy; 204 | 205 | if (_pressTime > millis()) threshold=20; 206 | uint8_t n = 5; 207 | uint8_t valid = 0; 208 | while (n--) 209 | { 210 | if (ts.touched()) valid++;; 211 | } 212 | if (valid<1) { _pressTime = 0; return false; } 213 | _pressTime = millis() + 50; 214 | 215 | //compensate 216 | if(!touchCalibration_rotate){ 217 | xx=(x_tmp-touchCalibration_x0)*_width/touchCalibration_x1; 218 | yy=(y_tmp-touchCalibration_y0)*_height/touchCalibration_y1; 219 | if(touchCalibration_invert_x) 220 | xx = _width - xx; 221 | if(touchCalibration_invert_y) 222 | yy = _height - yy; 223 | } else { 224 | xx=(y_tmp-touchCalibration_x0)*_width/touchCalibration_x1; 225 | yy=(x_tmp-touchCalibration_y0)*_height/touchCalibration_y1; 226 | if(touchCalibration_invert_x) 227 | xx = _width - xx; 228 | if(touchCalibration_invert_y) 229 | yy = _height - yy; 230 | } 231 | 232 | if (xx >= _width || yy >= _height) return false;//out of window 233 | *x = xx; 234 | *y = yy; 235 | return true; 236 | } else { 237 | return false; 238 | } 239 | } 240 | //-------------------------- 241 | void testTouch() {//for test touch screen 242 | uint16_t t_x = 0, t_y = 0; // To store the touch coordinates 243 | tft.fillScreen(TFT_BLACK); 244 | while (true) { 245 | if(getTouch(&t_x, &t_y)) { 246 | Serial.printf("%d , %d\n",t_x,t_y); 247 | tft.drawPixel(t_x,t_y,TFT_WHITE); 248 | delay(300); 249 | } 250 | } 251 | } -------------------------------------------------------------------------------- /sketch/arduinoSetup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/arduinoSetup.png -------------------------------------------------------------------------------- /sketch/gauge_factory_init/build/esp32.esp32.esp32/gauge_factory_init.ino.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/gauge_factory_init/build/esp32.esp32.esp32/gauge_factory_init.ino.bin -------------------------------------------------------------------------------- /sketch/gauge_factory_init/build/esp32.esp32.esp32/gauge_factory_init.ino.bootloader.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/gauge_factory_init/build/esp32.esp32.esp32/gauge_factory_init.ino.bootloader.bin -------------------------------------------------------------------------------- /sketch/gauge_factory_init/build/esp32.esp32.esp32/gauge_factory_init.ino.elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/gauge_factory_init/build/esp32.esp32.esp32/gauge_factory_init.ino.elf -------------------------------------------------------------------------------- /sketch/gauge_factory_init/build/esp32.esp32.esp32/gauge_factory_init.ino.partitions.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VaAndCob/ESP32-Blutooth-OBD2-Gauge/d75b147ba5c306557b0c7477509e40ff5346471a/sketch/gauge_factory_init/build/esp32.esp32.esp32/gauge_factory_init.ino.partitions.bin -------------------------------------------------------------------------------- /sketch/gauge_factory_init/gauge_factory_init.ino: -------------------------------------------------------------------------------- 1 | /* This is the first sketch to upload for OBDII gauge 2 | - add serial no. by set in #define serial_no 3 | - micro sdcard firmware upload 4 | 5 | 6 | */ 7 | 8 | #include // 9 | #include // File System 10 | #include // SD Card ESP32 11 | #include //save permanent data 12 | #include //Hardware-specific library 13 | 14 | const String serial_to_write = "V&C-001";//010 15 | 16 | 17 | //Pin configuration 18 | #define SCA_PIN 21//not used same pin with TFT_BL 19 | #define SCL_PIN 22//not used (avaialable) CAN_TX 20 | #define ADC_PIN 35//not used (available) CAN_RX 21 | #define LED_RED_PIN 4 22 | #define LED_GREEN_PIN 16 23 | #define LED_BLUE_PIN 17 24 | #define LDR_PIN 34 //LDR sensor 25 | #define BUZZER_PIN 26//speaker 26 | #define SELECTOR_PIN 27//push button 27 | // setting PWM properties 28 | #define backlightChannel 0 29 | #define buzzerChannel 2 30 | 31 | String line[12] = {"","","","","","","","","","","",""};//buffer for each line in terminal function 32 | Preferences pref;//create preference 33 | 34 | TFT_eSPI tft = TFT_eSPI(); 35 | 36 | //TERMINAL terminal for debugging display text 37 | void Terminal(String texts,uint16_t x,uint16_t y,uint16_t w,uint16_t h) { 38 | uint8_t max_line = round((h-y)/16.0); 39 | tft.fillRect(x, y,x+ w,y+ h, TFT_BLACK); 40 | tft.setTextColor(TFT_WHITE); 41 | for (uint8_t i=0;i 0) { 110 | Serial.println("Try to start update"); 111 | performUpdate(updateBin, updateSize); 112 | } 113 | else { 114 | Serial.println("Error, file is empty"); 115 | } 116 | 117 | updateBin.close(); 118 | 119 | // whe finished remove the binary from sd card to indicate end of the process 120 | 121 | } 122 | else { 123 | Serial.println("Could not load update.bin from sd root"); 124 | } 125 | } 126 | //--------------------------- 127 | void setup() { 128 | pinMode(SELECTOR_PIN,INPUT_PULLUP);//button 129 | //pwm setup 130 | ledcSetup(buzzerChannel, 1500, 10);//buzzer 10 bit 131 | ledcSetup(backlightChannel, 12000, 8);//backlight 8 bit 132 | ledcAttachPin(BUZZER_PIN, buzzerChannel);//attach buzzer 133 | 134 | Serial.begin(115200); 135 | delay(100); 136 | //init TFT display 137 | tft.init(); 138 | tft.setRotation(1);//landcape 139 | ledcAttachPin(TFT_BL, backlightChannel);//attach backlight 140 | ledcWrite(backlightChannel, 255);//full bright 141 | tft.fillScreen(TFT_BLACK);//show start page 142 | tft.drawString("Va&Cob OBDII Gauge Factory Initialize",0,0,2); 143 | Serial.println("Va&Cob OBDII Gauge Factory Initialize"); 144 | Serial.println("-------------------------------------"); 145 | Serial.print("Read Serial No: "); 146 | Terminal("Read Serial No: ",0,48,320,191); 147 | //read current serial 148 | pref.begin("security", false);//write /read 149 | String serial_no = pref.getString("serialno",""); 150 | if (serial_no == "") {//serial no not found 151 | Serial.println("XXX No Serial XXX"); 152 | Terminal("XXX No Serial XXX",0,48,320,191); 153 | } else { 154 | Serial.println(serial_no); 155 | Terminal(serial_no,0,48,320,191); 156 | } 157 | //write a new serial no 158 | Serial.print("Write Serial No: "); 159 | Serial.println(serial_to_write); 160 | Terminal("Write Serial No: ",0,48,320,191); 161 | Terminal(serial_to_write,0,48,320,191); 162 | pref.putString("serialno",serial_to_write); 163 | pref.end();//close pref 164 | 165 | Serial.println("-------------------------------------"); 166 | Serial.println("Insert micro sdcard with firmware \"VaAndCobOBD2Gauge.ini\""); 167 | Terminal("-------------------------------------",0,48,320,191); 168 | Terminal("Insert micro sdcard with firmware",0,48,320,191); 169 | Terminal("\"VaAndCobOBD2Gauge.ini\"",0,48,320,191); 170 | 171 | Serial.println("-------------------------------------"); 172 | Serial.print("Press BUTTON to flash firmware"); 173 | Terminal("-------------------------------------",0,48,320,191); 174 | Terminal("Press BUTTON to flash firmware",0,48,320,191); 175 | 176 | while (digitalRead(SELECTOR_PIN) == HIGH) { 177 | delay(10); 178 | } 179 | 180 | //first init and check SD card 181 | if (!SD.begin()) { 182 | Serial.println("Card Mount Failed"); 183 | } 184 | 185 | uint8_t cardType = SD.cardType(); 186 | 187 | if (cardType == CARD_NONE) { 188 | Serial.println("No SD_MMC card attached"); 189 | ESP.restart(); 190 | }else{ 191 | updateFromFS(SD); 192 | } 193 | } 194 | 195 | //--------------------------- 196 | void loop() { 197 | yield(); 198 | } -------------------------------------------------------------------------------- /sketch/library modified/TFT_eSPI/How to replace file.md: -------------------------------------------------------------------------------- 1 | TFT_eSPI release 2.5.43 2 | 3 | Explore to 4 | - C:\...\Arduino\Sketch\libraries\TFT_eSPI 5 | - backup User_Setup.h file by rename to User_Setup_ORG.h 6 | - copy new User_Setup.h to TFT_eSPI folder 7 | -------------------------------------------------------------------------------- /sketch/library modified/TFT_eSPI/User_Setup.h: -------------------------------------------------------------------------------- 1 | /* 2 | Rui Santos & Sara Santos - Random Nerd Tutorials 3 | 4 | Install the "TFT_eSPI" lbirary by Bodmer to interface with the TFT Display - https://github.com/Bodmer/TFT_eSPI 5 | *** IMPORTANT: User_Setup.h available on the internet will probably NOT work with the examples available at Random Nerd Tutorials *** 6 | *** YOU MUST USE THIS User_Setup.h FILE, CHECK FOR THE LATEST VERSION IN THE LINK BELOW IN ORDER TO USE THE EXAMPLES FROM RANDOM NERD TUTORIALS *** 7 | https://RandomNerdTutorials.com/cyd/ 8 | https://RandomNerdTutorials.com/esp32-tft/ 9 | FULL INSTRUCTIONS AVAILABLE ON HOW CONFIGURE THE LIBRARY: https://RandomNerdTutorials.com/cyd/ or https://RandomNerdTutorials.com/esp32-tft/ 10 | */ 11 | 12 | // USER DEFINED SETTINGS 13 | // Set driver type, fonts to be loaded, pins used and SPI control method etc 14 | // 15 | // See the User_Setup_Select.h file if you wish to be able to define multiple 16 | // setups and then easily select which setup file is used by the compiler. 17 | // 18 | // If this file is edited correctly then all the library example sketches should 19 | // run without the need to make any more changes for a particular hardware setup! 20 | // Note that some sketches are designed for a particular TFT pixel width/height 21 | 22 | // User defined information reported by "Read_User_Setup" test & diagnostics example 23 | #define USER_SETUP_INFO "User_Setup" 24 | 25 | // Define to disable all #warnings in library (can be put in User_Setup_Select.h) 26 | //#define DISABLE_ALL_LIBRARY_WARNINGS 27 | 28 | // ################################################################################## 29 | // 30 | // Section 1. Call up the right driver file and any options for it 31 | // 32 | // ################################################################################## 33 | 34 | // Define STM32 to invoke optimised processor support (only for STM32) 35 | //#define STM32 36 | 37 | // Defining the STM32 board allows the library to optimise the performance 38 | // for UNO compatible "MCUfriend" style shields 39 | //#define NUCLEO_64_TFT 40 | //#define NUCLEO_144_TFT 41 | 42 | // STM32 8 bit parallel only: 43 | // If STN32 Port A or B pins 0-7 are used for 8 bit parallel data bus bits 0-7 44 | // then this will improve rendering performance by a factor of ~8x 45 | //#define STM_PORTA_DATA_BUS 46 | //#define STM_PORTB_DATA_BUS 47 | 48 | // Tell the library to use parallel mode (otherwise SPI is assumed) 49 | //#define TFT_PARALLEL_8_BIT 50 | //#defined TFT_PARALLEL_16_BIT // **** 16 bit parallel ONLY for RP2040 processor **** 51 | 52 | // Display type - only define if RPi display 53 | //#define RPI_DISPLAY_TYPE // 20MHz maximum SPI 54 | 55 | // Only define one driver, the other ones must be commented out 56 | //#define ILI9341_DRIVER // Generic driver for common displays 57 | #define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172 58 | //#define ST7735_DRIVER // Define additional parameters below for this display 59 | //#define ILI9163_DRIVER // Define additional parameters below for this display 60 | //#define S6D02A1_DRIVER 61 | //#define RPI_ILI9486_DRIVER // 20MHz maximum SPI 62 | //#define HX8357D_DRIVER 63 | //#define ILI9481_DRIVER 64 | //#define ILI9486_DRIVER 65 | //#define ILI9488_DRIVER // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high) 66 | //#define ST7789_DRIVER // Full configuration option, define additional parameters below for this display 67 | //#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display 68 | //#define R61581_DRIVER 69 | //#define RM68140_DRIVER 70 | //#define ST7796_DRIVER 71 | //#define SSD1351_DRIVER 72 | //#define SSD1963_480_DRIVER 73 | //#define SSD1963_800_DRIVER 74 | //#define SSD1963_800ALT_DRIVER 75 | //#define ILI9225_DRIVER 76 | //#define GC9A01_DRIVER 77 | 78 | // Some displays support SPI reads via the MISO pin, other displays have a single 79 | // bi-directional SDA pin and the library will try to read this via the MOSI line. 80 | // To use the SDA line for reading data from the TFT uncomment the following line: 81 | 82 | // #define TFT_SDA_READ // This option is for ESP32 ONLY, tested with ST7789 and GC9A01 display only 83 | 84 | // For ST7735, ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display 85 | // Try ONE option at a time to find the correct colour order for your display 86 | 87 | // #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue 88 | // #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red 89 | 90 | // For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below 91 | 92 | // #define M5STACK 93 | 94 | // For ST7789, ST7735, ILI9163 and GC9A01 ONLY, define the pixel width and height in portrait orientation 95 | // #define TFT_WIDTH 80 96 | // #define TFT_WIDTH 128 97 | // #define TFT_WIDTH 172 // ST7789 172 x 320 98 | #define TFT_WIDTH 240 // ST7789 240 x 240 and 240 x 320 99 | // #define TFT_HEIGHT 160 100 | // #define TFT_HEIGHT 128 101 | // #define TFT_HEIGHT 240 // ST7789 240 x 240 102 | #define TFT_HEIGHT 320 // ST7789 240 x 320 103 | // #define TFT_HEIGHT 240 // GC9A01 240 x 240 104 | 105 | // For ST7735 ONLY, define the type of display, originally this was based on the 106 | // colour of the tab on the screen protector film but this is not always true, so try 107 | // out the different options below if the screen does not display graphics correctly, 108 | // e.g. colours wrong, mirror images, or stray pixels at the edges. 109 | // Comment out ALL BUT ONE of these options for a ST7735 display driver, save this 110 | // this User_Setup file, then rebuild and upload the sketch to the board again: 111 | 112 | // #define ST7735_INITB 113 | // #define ST7735_GREENTAB 114 | // #define ST7735_GREENTAB2 115 | // #define ST7735_GREENTAB3 116 | // #define ST7735_GREENTAB128 // For 128 x 128 display 117 | // #define ST7735_GREENTAB160x80 // For 160 x 80 display (BGR, inverted, 26 offset) 118 | // #define ST7735_ROBOTLCD // For some RobotLCD arduino shields (128x160, BGR, https://docs.arduino.cc/retired/getting-started-guides/TFT) 119 | // #define ST7735_REDTAB 120 | // #define ST7735_BLACKTAB 121 | // #define ST7735_REDTAB160x80 // For 160 x 80 display with 24 pixel offset 122 | 123 | // If colours are inverted (white shows as black) then uncomment one of the next 124 | // 2 lines try both options, one of the options should correct the inversion. 125 | 126 | // #define TFT_INVERSION_ON 127 | // #define TFT_INVERSION_OFF 128 | 129 | 130 | // ################################################################################## 131 | // 132 | // Section 2. Define the pins that are used to interface with the display here 133 | // 134 | // ################################################################################## 135 | 136 | // If a backlight control signal is available then define the TFT_BL pin in Section 2 137 | // below. The backlight will be turned ON when tft.begin() is called, but the library 138 | // needs to know if the LEDs are ON with the pin HIGH or LOW. If the LEDs are to be 139 | // driven with a PWM signal or turned OFF/ON then this must be handled by the user 140 | // sketch. e.g. with digitalWrite(TFT_BL, LOW); 141 | 142 | #define TFT_BL 21 // LED back-light control pin 143 | #define TFT_BACKLIGHT_ON HIGH // Level to turn ON back-light (HIGH or LOW) 144 | 145 | 146 | 147 | // We must use hardware SPI, a minimum of 3 GPIO pins is needed. 148 | // Typical setup for ESP8266 NodeMCU ESP-12 is : 149 | // 150 | // Display SDO/MISO to NodeMCU pin D6 (or leave disconnected if not reading TFT) 151 | // Display LED to NodeMCU pin VIN (or 5V, see below) 152 | // Display SCK to NodeMCU pin D5 153 | // Display SDI/MOSI to NodeMCU pin D7 154 | // Display DC (RS/AO)to NodeMCU pin D3 155 | // Display RESET to NodeMCU pin D4 (or RST, see below) 156 | // Display CS to NodeMCU pin D8 (or GND, see below) 157 | // Display GND to NodeMCU pin GND (0V) 158 | // Display VCC to NodeMCU 5V or 3.3V 159 | // 160 | // The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin 161 | // 162 | // The DC (Data Command) pin may be labelled AO or RS (Register Select) 163 | // 164 | // With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more 165 | // SPI devices (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS 166 | // line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin 167 | // to be toggled during setup, so in these cases the TFT_CS line must be defined and connected. 168 | // 169 | // The NodeMCU D0 pin can be used for RST 170 | // 171 | // 172 | // Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin 173 | // If 5V is not available at a pin you can use 3.3V but backlight brightness 174 | // will be lower. 175 | 176 | 177 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ###### 178 | 179 | // For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation 180 | //#define TFT_CS PIN_D8 // Chip select control pin D8 181 | //#define TFT_DC PIN_D3 // Data Command control pin 182 | //#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 183 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 184 | 185 | //#define TFT_BL PIN_D1 // LED back-light (only for ST7789 with backlight control pin) 186 | 187 | //#define TOUCH_CS PIN_D2 // Chip select pin (T_CS) of touch screen 188 | 189 | //#define TFT_WR PIN_D2 // Write strobe for modified Raspberry Pi TFT only 190 | 191 | 192 | // ###### FOR ESP8266 OVERLAP MODE EDIT THE PIN NUMBERS IN THE FOLLOWING LINES ###### 193 | 194 | // Overlap mode shares the ESP8266 FLASH SPI bus with the TFT so has a performance impact 195 | // but saves pins for other functions. It is best not to connect MISO as some displays 196 | // do not tristate that line when chip select is high! 197 | // Note: Only one SPI device can share the FLASH SPI lines, so a SPI touch controller 198 | // cannot be connected as well to the same SPI signals. 199 | // On NodeMCU 1.0 SD0=MISO, SD1=MOSI, CLK=SCLK to connect to TFT in overlap mode 200 | // On NodeMCU V3 S0 =MISO, S1 =MOSI, S2 =SCLK 201 | // In ESP8266 overlap mode the following must be defined 202 | 203 | //#define TFT_SPI_OVERLAP 204 | 205 | // In ESP8266 overlap mode the TFT chip select MUST connect to pin D3 206 | //#define TFT_CS PIN_D3 207 | //#define TFT_DC PIN_D5 // Data Command control pin 208 | //#define TFT_RST PIN_D4 // Reset pin (could connect to NodeMCU RST, see next line) 209 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V 210 | 211 | 212 | // ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP ###### 213 | 214 | // For ESP32 Dev board (only tested with ILI9341 display) 215 | // The hardware SPI can be mapped to any pins 216 | 217 | #define TFT_MISO 12 218 | #define TFT_MOSI 13 219 | #define TFT_SCLK 14 220 | #define TFT_CS 15 // Chip select control pin 221 | #define TFT_DC 2 // Data Command control pin 222 | //#define TFT_RST 4 // Reset pin (could connect to RST pin) 223 | #define TFT_RST -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST 224 | 225 | #define TOUCH_CS 33 // Chip select pin (T_CS) of touch screen 226 | 227 | // For ESP32 Dev board (only tested with GC9A01 display) 228 | // The hardware SPI can be mapped to any pins 229 | 230 | //#define TFT_MOSI 15 // In some display driver board, it might be written as "SDA" and so on. 231 | //#define TFT_SCLK 14 232 | //#define TFT_CS 5 // Chip select control pin 233 | //#define TFT_DC 27 // Data Command control pin 234 | //#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) 235 | //#define TFT_BL 22 // LED back-light 236 | 237 | //#define TOUCH_CS 21 // Chip select pin (T_CS) of touch screen 238 | 239 | //#define TFT_WR 22 // Write strobe for modified Raspberry Pi TFT only 240 | 241 | // For the M5Stack module use these #define lines 242 | //#define TFT_MISO 19 243 | //#define TFT_MOSI 23 244 | //#define TFT_SCLK 18 245 | //#define TFT_CS 14 // Chip select control pin 246 | //#define TFT_DC 27 // Data Command control pin 247 | //#define TFT_RST 33 // Reset pin (could connect to Arduino RESET pin) 248 | //#define TFT_BL 32 // LED back-light (required for M5Stack) 249 | 250 | // ###### EDIT THE PINs BELOW TO SUIT YOUR ESP32 PARALLEL TFT SETUP ###### 251 | 252 | // The library supports 8 bit parallel TFTs with the ESP32, the pin 253 | // selection below is compatible with ESP32 boards in UNO format. 254 | // Wemos D32 boards need to be modified, see diagram in Tools folder. 255 | // Only ILI9481 and ILI9341 based displays have been tested! 256 | 257 | // Parallel bus is only supported for the STM32 and ESP32 258 | // Example below is for ESP32 Parallel interface with UNO displays 259 | 260 | // Tell the library to use 8 bit parallel mode (otherwise SPI is assumed) 261 | //#define TFT_PARALLEL_8_BIT 262 | 263 | // The ESP32 and TFT the pins used for testing are: 264 | //#define TFT_CS 33 // Chip select control pin (library pulls permanently low 265 | //#define TFT_DC 15 // Data Command control pin - must use a pin in the range 0-31 266 | //#define TFT_RST 32 // Reset pin, toggles on startup 267 | 268 | //#define TFT_WR 4 // Write strobe control pin - must use a pin in the range 0-31 269 | //#define TFT_RD 2 // Read strobe control pin 270 | 271 | //#define TFT_D0 12 // Must use pins in the range 0-31 for the data bus 272 | //#define TFT_D1 13 // so a single register write sets/clears all bits. 273 | //#define TFT_D2 26 // Pins can be randomly assigned, this does not affect 274 | //#define TFT_D3 25 // TFT screen update performance. 275 | //#define TFT_D4 17 276 | //#define TFT_D5 16 277 | //#define TFT_D6 27 278 | //#define TFT_D7 14 279 | 280 | // ###### EDIT THE PINs BELOW TO SUIT YOUR STM32 SPI TFT SETUP ###### 281 | 282 | // The TFT can be connected to SPI port 1 or 2 283 | //#define TFT_SPI_PORT 1 // SPI port 1 maximum clock rate is 55MHz 284 | //#define TFT_MOSI PA7 285 | //#define TFT_MISO PA6 286 | //#define TFT_SCLK PA5 287 | 288 | //#define TFT_SPI_PORT 2 // SPI port 2 maximum clock rate is 27MHz 289 | //#define TFT_MOSI PB15 290 | //#define TFT_MISO PB14 291 | //#define TFT_SCLK PB13 292 | 293 | // Can use Ardiuno pin references, arbitrary allocation, TFT_eSPI controls chip select 294 | //#define TFT_CS D5 // Chip select control pin to TFT CS 295 | //#define TFT_DC D6 // Data Command control pin to TFT DC (may be labelled RS = Register Select) 296 | //#define TFT_RST D7 // Reset pin to TFT RST (or RESET) 297 | // OR alternatively, we can use STM32 port reference names PXnn 298 | //#define TFT_CS PE11 // Nucleo-F767ZI equivalent of D5 299 | //#define TFT_DC PE9 // Nucleo-F767ZI equivalent of D6 300 | //#define TFT_RST PF13 // Nucleo-F767ZI equivalent of D7 301 | 302 | //#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to processor reset 303 | // Use an Arduino pin for initial testing as connecting to processor reset 304 | // may not work (pulse too short at power up?) 305 | 306 | // ################################################################################## 307 | // 308 | // Section 3. Define the fonts that are to be used here 309 | // 310 | // ################################################################################## 311 | 312 | // Comment out the #defines below with // to stop that font being loaded 313 | // The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not 314 | // normally necessary. If all fonts are loaded the extra FLASH space required is 315 | // about 17Kbytes. To save FLASH space only enable the fonts you need! 316 | 317 | #define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH 318 | #define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters 319 | #define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters 320 | #define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm 321 | #define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-. 322 | #define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-. 323 | //#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT 324 | #define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts 325 | 326 | // Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded 327 | // this will save ~20kbytes of FLASH 328 | #define SMOOTH_FONT 329 | 330 | 331 | // ################################################################################## 332 | // 333 | // Section 4. Other options 334 | // 335 | // ################################################################################## 336 | 337 | // For RP2040 processor and SPI displays, uncomment the following line to use the PIO interface. 338 | //#define RP2040_PIO_SPI // Leave commented out to use standard RP2040 SPI port interface 339 | 340 | // For RP2040 processor and 8 or 16 bit parallel displays: 341 | // The parallel interface write cycle period is derived from a division of the CPU clock 342 | // speed so scales with the processor clock. This means that the divider ratio may need 343 | // to be increased when overclocking. I may also need to be adjusted dependant on the 344 | // display controller type (ILI94341, HX8357C etc). If RP2040_PIO_CLK_DIV is not defined 345 | // the library will set default values which may not suit your display. 346 | // The display controller data sheet will specify the minimum write cycle period. The 347 | // controllers often work reliably for shorter periods, however if the period is too short 348 | // the display may not initialise or graphics will become corrupted. 349 | // PIO write cycle frequency = (CPU clock/(4 * RP2040_PIO_CLK_DIV)) 350 | //#define RP2040_PIO_CLK_DIV 1 // 32ns write cycle at 125MHz CPU clock 351 | //#define RP2040_PIO_CLK_DIV 2 // 64ns write cycle at 125MHz CPU clock 352 | //#define RP2040_PIO_CLK_DIV 3 // 96ns write cycle at 125MHz CPU clock 353 | 354 | // For the RP2040 processor define the SPI port channel used (default 0 if undefined) 355 | //#define TFT_SPI_PORT 1 // Set to 0 if SPI0 pins are used, or 1 if spi1 pins used 356 | 357 | // For the STM32 processor define the SPI port channel used (default 1 if undefined) 358 | //#define TFT_SPI_PORT 2 // Set to 1 for SPI port 1, or 2 for SPI port 2 359 | 360 | // Define the SPI clock frequency, this affects the graphics rendering speed. Too 361 | // fast and the TFT driver will not keep up and display corruption appears. 362 | // With an ILI9341 display 40MHz works OK, 80MHz sometimes fails 363 | // With a ST7735 display more than 27MHz may not work (spurious pixels and lines) 364 | // With an ILI9163 display 27 MHz works OK. 365 | 366 | // #define SPI_FREQUENCY 1000000 367 | // #define SPI_FREQUENCY 5000000 368 | // #define SPI_FREQUENCY 10000000 369 | // #define SPI_FREQUENCY 20000000 370 | //#define SPI_FREQUENCY 27000000 371 | // #define SPI_FREQUENCY 40000000 372 | #define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz) 373 | // #define SPI_FREQUENCY 80000000 374 | 375 | // Optional reduced SPI frequency for reading TFT 376 | #define SPI_READ_FREQUENCY 20000000 377 | 378 | // The XPT2046 requires a lower SPI clock rate of 2.5MHz so we define that here: 379 | #define SPI_TOUCH_FREQUENCY 2500000 380 | 381 | // The ESP32 has 2 free SPI ports i.e. VSPI and HSPI, the VSPI is the default. 382 | // If the VSPI port is in use and pins are not accessible (e.g. TTGO T-Beam) 383 | // then uncomment the following line: 384 | #define USE_HSPI_PORT 385 | 386 | // Comment out the following #define if "SPI Transactions" do not need to be 387 | // supported. When commented out the code size will be smaller and sketches will 388 | // run slightly faster, so leave it commented out unless you need it! 389 | 390 | // Transaction support is needed to work with SD library but not needed with TFT_SdFat 391 | // Transaction support is required if other SPI devices are connected. 392 | 393 | // Transactions are automatically enabled by the library for an ESP32 (to use HAL mutex) 394 | // so changing it here has no effect 395 | 396 | // #define SUPPORT_TRANSACTIONS --------------------------------------------------------------------------------