espd

ESP32_EMU_BT_V.2.07 - different size labels and padding

May 27th, 2025
24
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 12.82 KB | None | 0 0
  1. #include <Arduino.h>
  2. #define LV_COLOR_16_SWAP 0
  3. #include <lvgl.h>
  4. #include <TFT_eSPI.h>
  5. #include "BluetoothSerial.h"
  6. #include <string>
  7. #include <stdexcept>
  8. using namespace std;
  9.  
  10. //#define USE_NAME
  11. const char *pin = "1234";
  12. String myBtName = "ESP32-BT-Master-1";
  13.  
  14. #if !defined(CONFIG_BT_SPP_ENABLED)
  15. #error Serial Bluetooth not available or not enabled. It is only available for the ESP32 chip.
  16. #endif
  17.  
  18. BluetoothSerial SerialBT;
  19.  
  20. #ifdef USE_NAME
  21. String slaveName = "EMUCANBT_SPP";
  22. #else
  23. uint8_t address[6] = { 0x98, 0xDA, 0x20, 0x02, 0xBE, 0xA4 };
  24. #endif
  25.  
  26. const int backLightPin = 27;
  27. const int buzzerPin = 22;
  28. bool buzzerOn = false;
  29. bool btIconSts = false;
  30. static lv_style_t style_bt;
  31. static bool style_initialized = false;
  32.  
  33. int rpm;
  34. int spd;
  35. float afr;
  36. float mapR;
  37. float boost;
  38. int tps;
  39. int clt;
  40. int ign;
  41. int inj;
  42. float bat;
  43. int cel;
  44.  
  45. unsigned long previousMillis = 0;
  46. const unsigned long reconnectInterval = 5000;
  47.  
  48. LV_FONT_DECLARE(lv_font_montserrat_14);
  49. LV_FONT_DECLARE(lv_font_montserrat_18);
  50. LV_FONT_DECLARE(lv_font_montserrat_20);                    
  51. LV_FONT_DECLARE(lv_font_montserrat_28);
  52.  
  53. lv_obj_t *bt_icon_label;
  54.  
  55. // Display & LVGL setup
  56. TFT_eSPI tft = TFT_eSPI();
  57. static lv_disp_draw_buf_t draw_buf;
  58. static lv_color_t buf[LV_HOR_RES_MAX * 20];
  59. lv_obj_t *table;
  60.  
  61. // LVGL Display Flush Callback
  62. void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
  63.   uint16_t w = area->x2 - area->x1 + 1;
  64.   uint16_t h = area->y2 - area->y1 + 1;
  65.   tft.startWrite();
  66.   tft.setAddrWindow(area->x1, area->y1, w, h);
  67.   tft.pushColors((uint16_t *)&color_p->full, w * h, true);
  68.   tft.endWrite();
  69.   lv_disp_flush_ready(disp);
  70. }
  71.  
  72. // Initialize LVGL Table
  73. void create_table() {
  74.   lv_obj_set_style_bg_color(lv_scr_act(), lv_color_make(30, 30, 30), LV_PART_MAIN);
  75.  
  76.   table = lv_table_create(lv_scr_act());
  77.   lv_obj_align(table, LV_ALIGN_CENTER, -1, 0);
  78.   lv_obj_set_style_text_opa(table, LV_OPA_COVER, 0);
  79.   lv_obj_clear_flag(lv_scr_act(), LV_OBJ_FLAG_SCROLLABLE);
  80.   lv_obj_set_scrollbar_mode(table, LV_SCROLLBAR_MODE_OFF);  
  81.   lv_obj_set_style_text_color(table, lv_color_white(), LV_PART_ITEMS);
  82.   lv_obj_set_style_bg_color(table, lv_color_make(30, 30, 30), LV_PART_MAIN);
  83.   //lv_obj_set_style_text_font(table, &lv_font_montserrat_20, LV_PART_ITEMS);
  84.  
  85.   /*static lv_style_t style_cell0;
  86.   lv_style_init(&style_cell0);
  87.   lv_style_set_pad_top(&style_cell0, 10);
  88.   lv_style_set_pad_bottom(&style_cell0, 10);
  89.   lv_style_set_pad_left(&style_cell0, 2);
  90.   lv_style_set_pad_right(&style_cell0, 2);
  91.   lv_obj_add_style(table, &style_cell0, LV_PART_ITEMS);*/
  92.  
  93.   lv_table_set_col_cnt(table, 4);
  94.   lv_table_set_row_cnt(table, 6);
  95.  
  96.   lv_obj_set_style_border_width(table, 1, LV_PART_ITEMS);
  97.   lv_obj_set_style_border_color(table, lv_color_white(), LV_PART_ITEMS);
  98.   lv_obj_set_style_border_side(table, LV_BORDER_SIDE_FULL, LV_PART_ITEMS);
  99.  
  100.   lv_table_set_col_width(table, 0, 53);
  101.   lv_table_set_col_width(table, 1, 105);
  102.   lv_table_set_col_width(table, 2, 47);
  103.   lv_table_set_col_width(table, 3, 115);
  104.  
  105.   lv_table_add_cell_ctrl(table, 5, 1, LV_TABLE_CELL_CTRL_MERGE_RIGHT);
  106.   lv_table_add_cell_ctrl(table, 5, 2, LV_TABLE_CELL_CTRL_MERGE_RIGHT);
  107.   lv_table_add_cell_ctrl(table, 5, 3, LV_TABLE_CELL_CTRL_MERGE_RIGHT);
  108.  
  109.   lv_table_set_cell_value(table, 0, 0, "RPM");
  110.   lv_table_set_cell_value(table, 0, 2, "SPD");
  111.   lv_table_set_cell_value(table, 1, 0, "AFR");
  112.   lv_table_set_cell_value(table, 1, 2, "CLT");
  113.   lv_table_set_cell_value(table, 2, 0, "TPS");
  114.   lv_table_set_cell_value(table, 2, 2, "BAT");
  115.   lv_table_set_cell_value(table, 3, 0, "MAP");
  116.   lv_table_set_cell_value(table, 3, 2, "BST");
  117.   lv_table_set_cell_value(table, 4, 0, "INJ");
  118.   lv_table_set_cell_value(table, 4, 2, "IGN");
  119.   lv_table_set_cell_value(table, 5, 0, "CEL");
  120.  
  121.   lv_obj_add_event_cb(table, my_table_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
  122.   lv_obj_add_event_cb(table, table_event_cb_bg, LV_EVENT_DRAW_PART_BEGIN, NULL);
  123.  
  124.   create_bt_icon();
  125.  
  126.   lv_timer_handler();
  127. }
  128.  
  129. void setup() {
  130.   tft.init();
  131.   pinMode(backLightPin, OUTPUT);
  132.   digitalWrite(backLightPin, LOW);
  133.   uint16_t darkGray = ((30 & 0xF8) << 8) | ((30 & 0xFC) << 3) | (30 >> 3);
  134.   tft.fillScreen(darkGray);
  135.   tft.setRotation(1);
  136.   Serial.begin(1000000);
  137.  
  138.   pinMode(buzzerPin, OUTPUT);
  139.  
  140.   // Initialize LVGL
  141.   lv_init();
  142.   lv_refr_now(NULL);
  143.   lv_disp_draw_buf_init(&draw_buf, buf, NULL, LV_HOR_RES_MAX * 10);
  144.  
  145.   // Setup LVGL Display Driver
  146.   static lv_disp_drv_t disp_drv;
  147.   lv_disp_drv_init(&disp_drv);
  148.   disp_drv.hor_res = 320;
  149.   disp_drv.ver_res = 240;
  150.   disp_drv.flush_cb = my_disp_flush;
  151.   disp_drv.draw_buf = &draw_buf;
  152.   lv_disp_drv_register(&disp_drv);
  153.  
  154.   SerialBT.begin(myBtName, true);
  155.  
  156.   create_table();
  157.   digitalWrite(backLightPin, HIGH);
  158.   connectToBt();
  159. }
  160.  
  161. void connectToBt() {
  162.   bool connected;
  163.   #ifndef USE_NAME
  164.     SerialBT.setPin(pin);
  165.   #endif
  166.  
  167.   #ifdef USE_NAME
  168.     connected = SerialBT.connect(slaveName);
  169.   #else
  170.     connected = SerialBT.connect(address);
  171.   #endif
  172.  
  173.   if (connected) {
  174.     Serial.println("Connected Successfully!");
  175.   } else {
  176.     Serial.println("Initial connect failed. Will retry in loop...");
  177.   }
  178.   update_bt_icon_color(SerialBT.hasClient(), false);
  179. }
  180.  
  181. void loop() {
  182.   uint8_t frame[5];
  183.   uint8_t channel;
  184.   uint16_t value;
  185.   int chData;
  186.   unsigned long currentMillis = millis();
  187.  
  188.   if (!SerialBT.connected()) {
  189.     // Attempt reconnection every 5 seconds
  190.     if (currentMillis - previousMillis >= reconnectInterval) {
  191.       previousMillis = currentMillis;
  192.       connectToBt();
  193.     }
  194.   }
  195.  
  196.   update_bt_icon_color(SerialBT.hasClient(), false);
  197.  
  198.   // Wait until at least 5 bytes are available
  199.   while (SerialBT.available() >= 5) {
  200.     SerialBT.readBytes(frame, 5);  // Read exactly 5 bytes
  201.     channel = frame[0];
  202.     value = (frame[2] << 8) | frame[3];
  203.     chData = static_cast<int>(channel);
  204.     if (chData == 1) {
  205.       rpm = static_cast<int>(value);
  206.       lv_table_set_cell_value(table, 0, 1, String(rpm).c_str());
  207.     } else if (chData == 28) {
  208.       spd = (static_cast<int>(value) / 2.8);
  209.       lv_table_set_cell_value(table, 0, 3, (String(spd) + " KM/H").c_str());
  210.     } else if (chData == 12) {
  211.       afr = (static_cast<float>(value) / 10);
  212.       lv_table_set_cell_value(table, 1, 1, String(afr).c_str());
  213.     } else if (chData == 2) {
  214.       mapR = (static_cast<float>(value) / 100);
  215.       boost = (mapR - 1.0132f);
  216.       lv_table_set_cell_value(table, 3, 1, (String(mapR) + " BAR").c_str());
  217.       lv_table_set_cell_value(table, 3, 3, (String(boost) + " BAR").c_str());
  218.     } else if (chData == 3) {
  219.       tps = static_cast<int>(value);
  220.       lv_table_set_cell_value(table, 2, 1, (String(tps) + " %").c_str());
  221.     } else if (chData == 24) {
  222.       clt = static_cast<int>(value);
  223.       lv_table_set_cell_value(table, 1, 3, (String(clt) + " °C").c_str());
  224.     } else if (chData == 6) {
  225.       ign = (static_cast<int>(value) / 2);
  226.       lv_table_set_cell_value(table, 4, 3, (String(ign) + " °").c_str());
  227.     } else if (chData == 19) {
  228.       inj = (static_cast<int>(value) / 2);
  229.       lv_table_set_cell_value(table, 4, 1, (String(inj) + " %").c_str());
  230.     } else if (chData == 5) {
  231.       bat = (static_cast<float>(value) / 37);
  232.       lv_table_set_cell_value(table, 2, 3, (String(bat) + " V").c_str());
  233.     } else if (chData == 255) {
  234.       cel = decodeCheckEngine(value);
  235.     }
  236.   }
  237.  
  238.   /*if (cel > 0 || clt > 105 || rpm > 7000 || boost > 1.10 || (bat < 12.00 && bat > 1.00)) {
  239.     digitalWrite(buzzerPin, HIGH);  // Buzzer ON
  240.   } else {
  241.     digitalWrite(buzzerPin, LOW);   // Buzzer OFF
  242.   }*/
  243.  
  244.   buzzerOn = (cel > 0 || clt > 105 || rpm > 7000 || boost > 1.10 || (bat < 12.00 && bat > 1.00));
  245.   digitalWrite(buzzerPin, (millis() % 600 < 300) && buzzerOn);
  246.  
  247.   lv_obj_invalidate(table);
  248.   lv_timer_handler();
  249. }
  250.  
  251. int decodeCheckEngine(uint16_t value) {
  252.   int cel_codes = 0; string cel_names = "";
  253.   if (value == 0) {
  254.     return 0;
  255.   }
  256.   else {
  257.     if (value & (1 << 0)) {
  258.       cel_codes++;  // Bit 0
  259.       cel_names = "CLT ";
  260.     }
  261.     if (value & (1 << 1)) {
  262.       //cel_codes++;  // Bit 1
  263.       //cel_names += "IAT ";
  264.     }
  265.     if (value & (1 << 2)) {
  266.       cel_codes++;  // Bit 2
  267.       cel_names += "MAP ";
  268.     }
  269.     if (value & (1 << 3)) {
  270.       cel_codes++;  // Bit 3
  271.       cel_names += "WBO ";
  272.     }
  273.     if (value & (1 << 8)) {
  274.       cel_codes++;  // Bit 8
  275.       cel_names += "FF SENSOR ";
  276.     }
  277.     if (value & (1 << 9)) {
  278.       cel_codes++;  // Bit 9
  279.       cel_names += "DBW ";
  280.     }
  281.     if (value & (1 << 10)) {
  282.       cel_codes++;  // Bit 10
  283.       cel_names += "FPR ";
  284.     }
  285.  
  286.     lv_table_set_cell_value(table, 5, 1, cel_names.c_str());
  287.     return cel_codes;
  288.   }
  289. }
  290.  
  291. // Cell alignment fix
  292. void my_table_event_cb(lv_event_t * e) {
  293.   lv_obj_t * table = lv_event_get_target(e);
  294.   lv_obj_draw_part_dsc_t * dsc = (lv_obj_draw_part_dsc_t *)lv_event_get_param(e);
  295.  
  296.   if (dsc->part == LV_PART_ITEMS) {
  297.     uint16_t row = dsc->id / lv_table_get_col_cnt(table);
  298.     uint16_t col = dsc->id % lv_table_get_col_cnt(table);
  299.    
  300.     if(col == 0 || col == 2) {
  301.         dsc->label_dsc->font = &lv_font_montserrat_18;
  302.     } else if(col == 1 || col == 3) {
  303.         dsc->label_dsc->font = &lv_font_montserrat_20;
  304.     }  
  305.     dsc->rect_dsc->pad_top = 10;
  306.     dsc->rect_dsc->pad_bottom = 10;
  307.     if (col == 0 || col == 2) {
  308.         dsc->rect_dsc->pad_left = 2;
  309.         dsc->rect_dsc->pad_right = 2;
  310.     } else if(col == 1 || col == 3) {
  311.         dsc->rect_dsc->pad_left = 1;
  312.         dsc->rect_dsc->pad_right = 4;
  313.     }  
  314.     dsc->label_dsc->align = LV_TEXT_ALIGN_CENTER;
  315.     if ((row == 0 && col == 1) || (row == 0 && col == 3) || (row == 1 && col == 1) || (row == 1 && col == 3) || (row == 2 && col == 1) || (row == 2 && col == 3) || (row == 3 && col == 1) || (row == 3 && col == 3) ||
  316.         (row == 4 && col == 1) || (row == 4 && col == 3)) {
  317.       dsc->label_dsc->align = LV_TEXT_ALIGN_RIGHT;
  318.     }
  319.     if (row == 5 && col == 1) {
  320.       dsc->label_dsc->align = LV_TEXT_ALIGN_CENTER;
  321.     }
  322.   }
  323. }
  324.  
  325. static void table_event_cb_bg(lv_event_t *e) {
  326.   lv_obj_t *table = lv_event_get_target(e);
  327.   lv_obj_draw_part_dsc_t *dsc = (lv_obj_draw_part_dsc_t *)lv_event_get_param(e);
  328.  
  329.   // Ensure dsc and rect_dsc are valid
  330.   if (!dsc || !dsc->rect_dsc) return;
  331.  
  332.   // Only modify table cell backgrounds
  333.   if (dsc->part == LV_PART_ITEMS) {
  334.     uint16_t row = dsc->id / lv_table_get_col_cnt(table);
  335.     uint16_t col = dsc->id % lv_table_get_col_cnt(table);
  336.  
  337.     const char *value_str = lv_table_get_cell_value(table, row, col);
  338.  
  339.     // Check if value_str is null or empty before conversion
  340.     float value = 0.0f;  // Default value
  341.     if (value_str != nullptr && value_str[0] != '\0') {
  342.       try {
  343.         value = std::stof(value_str);  // Convert string to float safely
  344.       } catch (...) {
  345.         value = 0.0f;  // Handle invalid conversions
  346.       }
  347.     }
  348.  
  349.     // Default cell color
  350.     lv_color_t bg_color = lv_color_make(30, 30, 30);
  351.     lv_color_t text_color = lv_color_white();
  352.  
  353.     if (row == 0 && col == 1 && value > 7000.00) {
  354.       bg_color = lv_color_make(0, 0, 255);
  355.       text_color = lv_color_white();
  356.     }
  357.     if (row == 1 && col == 3 && value > 100.00) {
  358.       bg_color = lv_color_make(0, 0, 255);
  359.       text_color = lv_color_white();
  360.     }
  361.     if (row == 1 && col == 3 && value < 55.00 && value > 01.00) {
  362.       bg_color = lv_color_make(0, 255, 255);
  363.       text_color = lv_color_black();
  364.     }
  365.     if (row == 2 && col == 3 && value < 12.00 && value > 01.00) {
  366.       bg_color = lv_color_make(0, 0, 255);
  367.       text_color = lv_color_white();
  368.     }
  369.     if (row == 3 && col == 3 && value > 1.10) {
  370.       bg_color = lv_color_make(0, 0, 255);
  371.       text_color = lv_color_white();
  372.     }
  373.     if (row == 5 && col == 1 && value_str != nullptr && value_str[0] != '\0') {
  374.       bg_color = lv_color_make(0, 0, 255);
  375.       text_color = lv_color_white();
  376.     }
  377.  
  378.     // Apply background color to the cell
  379.     dsc->rect_dsc->bg_color = bg_color;
  380.     dsc->rect_dsc->bg_opa = LV_OPA_COVER;
  381.     dsc->label_dsc->color = text_color;
  382.   }
  383. }
  384.  
  385. void update_bt_icon_color(bool is_connected, bool firstTime) {
  386.   if(btIconSts != is_connected || firstTime) {
  387.     if (!style_initialized) {
  388.       lv_style_init(&style_bt);
  389.       style_initialized = true;
  390.     }
  391.     if (is_connected) {
  392.       lv_style_set_text_color(&style_bt, lv_color_make(0, 255, 0)); // Green
  393.     } else {
  394.       lv_style_set_text_color(&style_bt, lv_color_make(0, 0, 255)); // Red
  395.     }
  396.     lv_obj_add_style(bt_icon_label, &style_bt, 0);
  397.     btIconSts = is_connected;
  398.   }
  399. }
  400.  
  401. void create_bt_icon() {
  402.   bt_icon_label = lv_label_create(lv_scr_act());
  403.   lv_label_set_text(bt_icon_label, LV_SYMBOL_BLUETOOTH);
  404.   lv_obj_set_style_text_font(bt_icon_label, &lv_font_montserrat_28, LV_PART_MAIN);
  405.   lv_obj_align(bt_icon_label, LV_ALIGN_BOTTOM_RIGHT, -3, -1);
  406.   update_bt_icon_color(SerialBT.hasClient(), true);
  407. }
Advertisement
Add Comment
Please, Sign In to add comment