物料
1. Esp32开发板(带蓝牙)--19元
2. 1.6寸 130*130 半反半透屏 SPI接口(强烈建议半反半透屏,整体效果均衡,阳光下效果也不错) ---- 15元, 现在涨到20+了
3. 万能 PCB板,排针,排针座(如果屏幕直插,注意排针和排针座高度,或者直接用排线),蜂鸣器(可选) ---算10元吧
4. 蓝牙OBD(建议选拔掉壳后,薄薄的哪种,不影响把盖子盖回去) ---10十~40元,基本最便宜的都能满足需求
连线也极其简单,就是屏幕,另外有一个按键,这个按键完全可以不要
/*******************************************************************************
Start of Arduino_GFX setting
Arduino_GFX try to find the settings depends on selected board in Arduino IDE
Or you can define the display dev kit not in the board list
Defalult pin list for non display dev kit:
Arduino Nano, Micro and more: TFT_CS: 9, TFT_DC: 8, TFT_RST: 7, TFT_BL: 6
ESP32 various dev board : TFT_CS: 5, TFT_DC: 27, TFT_RST: 33, TFT_BL: 22
ESP8266 various dev board : TFT_CS: 15, TFT_DC: 4, TFT_RST: 2, TFT_BL: 5
Raspberry Pi Pico dev board : TFT_CS: 17, TFT_DC: 27, TFT_RST: 26, TFT_BL: 28
RTL872x various dev board : TFT_CS: 18, TFT_DC: 17, TFT_RST: 2, TFT_BL: 23
Seeeduino XIAO dev board : TFT_CS: 3, TFT_DC: 2, TFT_RST: 1, TFT_BL: 0
Teensy 4.1 dev board : TFT_CS: 39, TFT_DC: 41, TFT_RST: 40, TFT_BL: 22
******************************************************************************/
#include <Arduino_GFX_Library.h>
#include "FreeMonoBold14pt7b.h"
#include "FreeMonoBold36pt7b.h"
#include "BluetoothSerial.h"
#include "ELMduino.h"
#include "time.h"
#include "esp_system.h"
uint8_t address[6] = {0x34, 0x90, 0x1b, 0x20, 0xa6, 0x92};
String name = "OBDII";
BluetoothSerial SerialBT;
#define ELM_PORT SerialBT
#define DEBUG_PORT Serial
#define MAX_SPEED 512
#define INVALID_SPEED -1
#define BUTTON_PIN 32
#define BUZZER_VCC 18
#define BUZZER_GND 19
struct Button {
const uint8_t PIN;
uint32_t numberKeyPresses;
unsigned long lastInterrupt;
bool pressed;
};
Arduino_DataBus *bus = new Arduino_ESP32SPI(4 /* DC */, 15 /* CS */, 17 /* SCK */, 16 /* MOSI */, -1 /* MISO */, VSPI /* spi_num */);
Arduino_GFX *gfx = new Arduino_SSD1283A(bus, 2 /* RST */, 2 /* rotation */);
Button button1 = {BUTTON_PIN, 0, 0, false};
ELM327 myELM327;
int32_t oriSpeed = 0;
int32_t fixSpeed = 0;
int32_t lastOriSpeed = 0;
unsigned long bootTime = 0 ;
unsigned long runTime = 0;
bool isBuzzerEnable = true;
bool lastStatusOk = false;
void buzzerOn() {
digitalWrite(BUZZER_VCC, HIGH);
}
void buzzerOff() {
digitalWrite(BUZZER_VCC, LOW);
}
void buzzerTime(uint8_t t) {
if (!isBuzzerEnable) {
return;
}
for (uint8_t i = 0 ; i < t; i++) {
buzzerOn();
delay(200 + i * 30);
buzzerOff();
delay(50 + i * 10);
}
}
//void measure(char *s, int16_t x, int16_t y) {
//
// int16_t x1, y1;
// uint16_t w, h;
// gfx->getTextBounds(s, x, y, &x1, &y1, &w, &h);
// // gfx->fillRect(x1, y1, w, h, BLACK);
// Serial.printf("x1 = %d , y1 = %d, w = %d, h = %d\n", x1, y1, w, h);
//}
void initBuzzer() {
pinMode(BUZZER_VCC, OUTPUT); // Buzzer VCC
pinMode(BUZZER_GND, OUTPUT); // Buzzer GND
digitalWrite(BUZZER_VCC, HIGH);
digitalWrite(BUZZER_GND, LOW);
delay(50);
digitalWrite(BUZZER_VCC, LOW);
}
void initTFT() {
gfx->begin();
gfx->fillScreen(BLACK);
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
gfx->setFont(&FreeMonoBold14pt7b);
gfx->fillScreen(BLACK);
}
void showRuntime() {
uint16_t x = 1;
uint16_t y = 20;
gfx->setTextColor(YELLOW, BLACK);
gfx->setCursor(x, y);
gfx->setFont(&FreeMonoBold14pt7b);
uint8_t h = runTime / 3600;
uint8_t m = runTime / 60 % 60;
uint8_t s = runTime % 60;
gfx->printf("%02d:%02d:%02d", h, m, s);
}
void showFixSpeed(int32_t speed) {
uint16_t x = 0;
uint16_t y = 80;
gfx->setTextColor(WHITE, BLACK);
gfx->setCursor(x, y);
gfx->setFont(&FreeMonoBold36pt7b);
gfx->printf("%3d", speed);
}
void showOriSpeed(int32_t speed) {
uint16_t x = 0;
uint16_t y = 120;
gfx->setFont(&FreeMonoBold14pt7b);
gfx->setTextColor(GREEN, BLACK);
gfx->setCursor(1, 120);
gfx->printf("%3d", speed);
gfx->setTextColor(PINK, BLACK);
gfx->setCursor(60, 120);
gfx->printf("km/h");
}
void initOBD() {
int8_t retryCount = 0;
gfx->setFont(&FreeMonoBold14pt7b);
gfx->setCursor(0, 60);
gfx->println("initOBD");
DEBUG_PORT.begin(115200);
// SerialBT.setPin("1234");
ELM_PORT.begin("ArduHUD", true);
while (!ELM_PORT.connect(address)) {
// while (!ELM_PORT.connect("MI9")) {
DEBUG_PORT.println("Couldn't connect to OBD scanner - 1");
gfx->fillScreen(BLACK);
gfx->setCursor(0, 60);
gfx->printf("Connect\n %d", ++retryCount);
if (retryCount > 5) {
gfx->fillScreen(BLACK);
gfx->setCursor(0, 60);
gfx->println("Restart");
delay(2000);
esp_restart();
}
}
if (!myELM327.begin(ELM_PORT, true, 2000)) {
DEBUG_PORT.println("Couldn't connect to OBD scanner - 2");
gfx->fillScreen(BLACK);
gfx->setCursor(0, 60);
gfx->println("No connection");
}
DEBUG_PORT.println("Connected to ELM327");
}
void printSpeed() {
static uint8_t noDataCount = 0;
oriSpeed = myELM327.kph();
if (myELM327.status != ELM_SUCCESS) {
noDataCount++;
if (noDataCount >= 3) {
myELM327.printError();
gfx->fillScreen(BLACK);
gfx->setCursor(0, 60);
gfx->println("no data");
lastStatusOk = false;
}
} else {
if (oriSpeed > MAX_SPEED) {
oriSpeed = INVALID_SPEED;
}
fixSpeed = (int)(oriSpeed * 1.05 );
if (!lastStatusOk) {
gfx->fillScreen(BLACK);
}
showFixSpeed(fixSpeed);
showOriSpeed(oriSpeed);
lastStatusOk = true;
DEBUG_PORT.printf("oriSpeed = %d , lastOriSpeed = %d\n", oriSpeed, lastOriSpeed);
if (oriSpeed > 120 && lastOriSpeed <= 120) {
buzzerTime(3);
} else if ( oriSpeed > 100 && lastOriSpeed <= 100) {
buzzerTime(2);
} else if (oriSpeed > 80 && lastOriSpeed <= 80) {
buzzerTime(1);
}
lastOriSpeed = oriSpeed;
noDataCount = 0;
}
}
void IRAM_ATTR isr() {
unsigned long curT = millis();
DEBUG_PORT.printf("isr, time : %d, lastTime : %d \n", curT, button1.lastInterrupt);
if (curT - button1.lastInterrupt < 10) {
DEBUG_PORT.printf("skip isr, time : %d \n", curT - button1.lastInterrupt);
return;
}
button1.lastInterrupt = curT;
button1.numberKeyPresses += 1;
isBuzzerEnable = !isBuzzerEnable;
button1.pressed = true;
}
void setup(void)
{
Serial.begin(115200);
bootTime = millis();
initBuzzer();
initTFT();
initOBD();
// init Button
pinMode(button1.PIN, INPUT_PULLUP);
attachInterrupt(button1.PIN, isr, RISING);
// attachInterrupt(button1.PIN, isr, FALLING);
}
void loop()
{
static long lastRefresh = 0;
if (millis() - lastRefresh > 500) {
lastRefresh = millis();
runTime = (millis() - bootTime) / 1000;
showRuntime();
printSpeed();
}
if (button1.pressed) {
button1.pressed = false;
gfx->fillScreen(BLACK);
gfx->setCursor(0, 60);
gfx->println(isBuzzerEnable ? "Enable Buzzer" : "Disable Buzzer");
delay(1000);
}
}