40 Commits
Author SHA1 Message Date
esilva 3437ea473e made the walkietalkie more robust 2026-09-18 23:10:57 +01:00
esilva 33334455cb type fix 2026-06-09 16:38:35 +01:00
esilva 0893b8a0c5 added the moza R9 case 2026-06-09 16:38:26 +01:00
esilva 5523b18da6 turn the screen around so the thinest bezzel becomes the top 2026-05-28 12:06:48 +01:00
esilva 809e99469d update some of the logs 2026-03-15 00:19:52 +00:00
esilva 53d8938cd2 small bug fix 2026-03-14 22:22:18 +00:00
esilva 1e6d5e4c45 user can now created more than a string window 2026-03-13 17:21:16 +00:00
esilva 97d710c7bb Quick implementation of passing data to the windows
it works, its nice seeing this finally working, but i think I have to
go via the composition route for the windows
2026-03-11 23:36:45 +00:00
esilva 84cfa82be3 parse the newly added window ID 2026-03-11 21:20:42 +00:00
esilva 503fb7e7b5 Implementing the data parsing approach
I'm receiving data in a very specific format for each data type.
I parse the bytes correctly and hopefully everything works, yo.
2026-03-10 23:09:03 +00:00
esilva 73291b65ac not really a change, doesn't matter 2026-03-06 00:05:02 +00:00
esilva 9322729294 update to the g29 mount 2026-03-06 00:04:28 +00:00
esilva 0544703dc6 adding the data transfer commands 2026-03-06 00:04:17 +00:00
esilva 91402af140 Added UIOptions
still need to make it work with bitflags instead tho
2026-02-20 21:52:28 +00:00
esilva 8ccb066d1f Added the window update packet and command 2026-02-13 23:39:22 +00:00
esilva 0f41e348c2 Added the window update command
- some other small misc stuff
2026-02-12 23:04:28 +00:00
esilva 61a310516b new window ID is returned 2026-02-09 23:24:09 +00:00
esilva c4288d4542 Should I manually copy bytes? 2026-01-01 23:46:00 +00:00
esilva a267911cc3 Added the window destruction command 2025-12-31 17:03:19 +00:00
esilva e6f05a18dd Fixed teh window create method 2025-12-31 16:42:28 +00:00
esilva 7a4d543561 Added the create window command 2025-12-31 15:56:29 +00:00
esilva 5b00cd578b Moved the logger into its own project 2025-12-28 01:21:26 +00:00
esilva c7250d51ae Replacing Serial2 calls with my logger 2025-12-27 18:41:02 +00:00
esilva 726b6fd4fd Added a logger thingy 2025-12-27 18:32:10 +00:00
esilva ed08cf855a slight refactoring to the state machine 2025-12-27 18:00:43 +00:00
esilva dc23bbaf5b Reworking the communcation 2025-12-23 23:36:51 +00:00
esilva ce15563aee Mainly testing the new eslabsCurses API 2025-12-06 22:26:46 +00:00
esilva 7245e09cd7 Continuing developing the new lib for the UI 2025-11-11 22:31:07 +00:00
esilva 89da044474 organizing the communication code a bit better 2025-10-30 00:32:28 +00:00
esilva 6efdc2771d Simply added an ID to the device 2025-10-29 22:57:35 +00:00
esilva 041fbcd724 Trying to get a good architecture to work with this 2025-10-29 22:04:50 +00:00
esilva 43056411dd Naive discovery procedure, need to make it work after connection loss 2025-10-22 23:36:45 +01:00
esilva b5ee91e44c Setting up the environment to develop the new communication method 2025-10-19 23:21:05 +01:00
esilva 7eac2726ad Moved the cursesAtHome lib to its own repo eslabsCurses 2025-10-19 23:01:14 +01:00
esilva 50b8dc2980 Added a magnetic mount
Its still pretty shit:
- the screw holes on the shell don't line up
- the shell bows due to the screw disalignment stress
- the back of the shell hits the G29 wheel base, needs some coercion
2025-05-29 22:36:02 +01:00
esilva c076998cec cube magnets mount 2025-05-26 15:30:16 +01:00
esilva 2fa7ceb099 small_magnets_case 2025-05-26 14:13:25 +01:00
esilva b5a0907b49 Another useless commit 2025-05-23 21:40:23 +01:00
esilva c901d304d7 Small readme change 2025-05-23 19:22:07 +01:00
esilva 246f051b0e Adding this on GH 2025-05-23 09:21:10 +01:00
46 changed files with 1047 additions and 1068 deletions
+2
View File
@@ -0,0 +1,2 @@
*.FCBak
*.stl
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2
View File
@@ -20,3 +20,5 @@ uploadfs:
update:
pio -f -c vim update
cleanESlabs:
rm -rf .pio/libdeps/esp32-s3-devkitc-1/eslabsCurses
+22 -8
View File
@@ -8,10 +8,16 @@ Currently I have a way to mount it on a G29 because its what I have.
To drive it I one of my other projects: [ESDI](https://github.com/ESilva15/ESDI)
## Many thanks
- to [mgaman/ESP32-4827S043-LVGL](https://github.com/mgaman/ESP32-4827S043-LVGL):
don't remember exactly how but it helped me understand how to set up this ESP32
for development.
![example of the display](./images/example_of_display.jpg)
## Assembly
It requires a very simple modification to the G29. The display will seet between
It requires a very simple modification to the G29. The display will seat between
the base and the steering wheel and for the stock G29 paddles to remain functional,
the steering wheel must be spaced.
@@ -21,13 +27,25 @@ go on the screws that screw the wheel to the base:
![example of the display](./images/spacer_in_g29.jpg "spacer in the g29")
# Roadmap
- [ ] Simhub support
- [ ] Better layout tooling
- [ ] Dynamic layout/data
### Debugging wires
RX 18 -> yellow -> green
TX 19 -> blue -> white
### Development
#### Debugging wires
| CYD | Serial Cable |
| -------------- | ------------ |
| Yellow [RX 18] | Green |
| Blue [TX 19] | White |
The `RX18` and `TX19` pins are the `IO18` and `IO19` pins on the board and
probably are labeled as `UART1` and `USB`.
This is the `Serial2` we define on `main.cpp` currently.
Then we listen to it with something like ``.
### Cool fonts I like:
- u8g2_font_bubble_tr (really cool for splash screens)
@@ -46,7 +64,3 @@ int ascent = u8g2.getAscent();
// will probably vary from font to font tho, right now I'm using
// monospace and that's enough
```
### Shameless begging
Hey, doesn't hurt to try, its free either way:
[Buy me a coffee!](buymeacoffee.com/ESilva_15)
-24
View File
@@ -1,24 +0,0 @@
{
"name": "ili9488CursesUI",
"version": "0.1.1",
"description": "Simple UI for creating interfaces with the ili9488 and arduinos",
"keywords": "ili9488, pro micro, curses",
"repository":
{
"type": "git",
"url": "https://github.com/ESilva15/ili9488CursesUI"
},
"authors":
[
{
"name": "Eduardo Silva",
"maintainer": true
}
],
"dependencies": {
"Adafruit_GFX_Library": "~1.11.3",
"Adafruit_BusIO": "1.16.1"
},
"frameworks": "*",
"platforms": "*"
}
View File
-104
View File
@@ -1,104 +0,0 @@
#include "UIBar.h"
#include "UIDrawing.h"
UIBar::UIBar(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor,
char *title)
: UIElement(d, dims, decor, title) {}
void UIBar::Update(char *v) {
unsigned long time = millis();
if ((time - this->lastUpdate) <= this->refreshRate) {
return;
}
// Conver the string to an integer
uint32_t newVal = atoi(v);
if (this->value == newVal) {
// If the value hasn't changed we do not need to re-render
return;
}
this->DrawBar(&this->value, &newVal);
this->value = newVal;
}
void UIBar::DrawBar(uint32_t *oldValue, uint32_t *newValue) {
// We can help identifying cur RPM visually by using different
// colors on the bar, for example, at a given distance of the ideal shift
// we can change the color gradually
// If the new value is larger than the old one
// we just paint a new rectangle
// If the new value is smaller than the old one
// we have to delete a chunk of the old rectangle
int16_t startX = 0;
int16_t len = 0;
uint16_t colour = WHITE;
if (*newValue > *oldValue) { // Increment the bar
int32_t newV =
(*newValue * (uint32_t)(this->dims.width - 4)) / (this->range * 1000);
int32_t oldV =
(this->value * (uint32_t)(this->dims.width - 4)) / (this->range * 1000);
startX = oldV;
len = newV - oldV;
} else { // Decrement the bar
int32_t newV =
(*newValue * (uint32_t)(this->dims.width - 4)) / (this->range * 1000);
int32_t oldV =
(this->value * (uint32_t)(this->dims.width - 4)) / (this->range * 1000);
startX = newV;
len = oldV - newV;
colour = BLACK;
}
this->display->fillRect(this->dims.x + 2 + startX, this->dims.y + 2, len,
this->dims.height - 4, colour);
this->value = *newValue;
}
void UIBar::renderBlank() {
this->display->setCursor(this->dims.x, this->dims.y);
this->display->print("!RANGE");
}
void UIBar::Box() {
// If the range was not set, we cannot draw our ruler
if (this->range <= 0) {
this->renderBlank();
return;
}
// Bounding box for the bar
this->display->drawRect(this->dims.x, this->dims.y, this->dims.width,
this->dims.height, RED);
// Now we can draw the scale (this should be easy to modify if needed)
// dividirs will be the range of the tach - 8 = 8000rpm
// Calculate step size as a float for accurate positioning
float step = (float)this->dims.width / this->range;
for (int k = 0; k <= this->range; k++) {
uint16_t x = round(k * step) + this->dims.x;
// To ensure it doesn't go beyond the bounding box
if (x >= this->dims.x + this->dims.width) {
x = this->dims.x + this->dims.width - 1;
}
this->display->setTextColor(RED);
this->display->drawFastVLine(x, this->dims.y + this->dims.height, 7, RED);
char legend[5];
sprintf(legend, "%d", k);
this->display->setCursor(x - (CHR_WIDTH(this->decor->textSize) / 2),
this->dims.y + this->dims.height + 7 + 2);
this->display->setTextColor(WHITE);
this->display->setTextSize(this->decor->textSize);
this->display->print(legend);
}
}
-20
View File
@@ -1,20 +0,0 @@
#ifndef _UI_BAR
#define _UI_BAR
#include "UIComponent.h"
struct UIBar : UIElement {
// This might be the only when where its more efficient to have the data
// as integers instead
uint32_t value = 0; // current tach value
uint8_t range = 0; // define the tach range, ie: 8 for 8000rpm
UIBar(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor, char *title);
void Update(char *val);
void DrawBar(uint32_t *oldValue, uint32_t *newValue);
void renderBlank();
void Box();
};
#endif
-108
View File
@@ -1,108 +0,0 @@
#include "UIComponent.h"
// #include "u8g2.h"
#include "Arduino.h"
#include "HardwareSerial.h"
#include "UIDrawing.h"
// #include <U8g2lib.h>
#include <cstdint>
#define DEBUG 1
UIElement::UIElement(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor,
char *title)
: display(d), dims(dims), decor(decor), title(title) {}
UIDimensions::UIDimensions(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
: x(x), y(y), width(w), height(h) {}
int16_t UIElement::getContentAreaX0() {
if (this->decor->hasBorder) {
return this->dims.x + DEFAULT_BORDER_THICKNESS + DEFAULT_MARGIN;
}
return this->dims.x;
}
int16_t UIElement::getContentAreaY0() {
if (this->decor->hasBorder) {
return this->dims.y + this->getTitleAreaHeight() +
DEFAULT_TITLE_CONTENT_SPACING;
}
return this->dims.y;
}
uint16_t UIElement::getContentAreaHeight() {
return this->dims.height - (DEFAULT_MARGIN * 2) - DEFAULT_BORDER_THICKNESS -
this->getTitleAreaHeight();
}
uint16_t UIElement::getContentAreaWidth() {
return this->dims.width - ((DEFAULT_BORDER_THICKNESS + DEFAULT_MARGIN) * 2);
}
int16_t UIElement::getTitleAreaX0() {
return this->dims.x + DEFAULT_BORDER_THICKNESS + DEFAULT_MARGIN;
}
int16_t UIElement::getTitleAreaY0() { return this->dims.y; }
uint16_t UIElement::getTitleAreaHeight() {
int16_t x = 0, y = 0;
uint16_t w = 0, h = 0;
this->display->setTextSize(this->decor->titleSize);
this->display->getTextBounds((const char *)this->title, 0, 0, &x, &y, &w, &h);
return h;
}
uint16_t UIElement::getTitleAreaWidth() {
int16_t x = 0, y = 0;
uint16_t w = 0, h = 0;
this->display->setTextSize(this->decor->titleSize);
this->display->getTextBounds((const char *)this->title, 0, 0, &x, &y, &w, &h);
return w;
}
void UIElement::replaceString(char *oldVal, char *newVal) {
size_t newValLen = strlen(newVal);
size_t oldValLen = strlen(oldVal);
// It crashs somewhere around here
int16_t x0 = this->getContentAreaX0();
int16_t y0 = this->getContentAreaY0(); // <- Crashes here
// Before here
this->display->setTextSize(this->decor->textSize);
// If the new value is shorter, delete the extra in advance
if (oldValLen > newValLen) {
// If the old value is longer than the new value, we have to delete the
// extra of the old value, which is whatever starts at the new value len
for (size_t start = newValLen; start < oldValLen; start++) {
this->display->setCursor(x0 + start * (CHR_WIDTH(this->decor->textSize)),
y0);
this->display->setTextColor(this->decor->bgColor);
this->display->print(oldVal[start]);
}
}
for (size_t k = 0; k < newValLen; k++) {
if (k <= oldValLen) {
if (oldVal[k] != newVal[k]) {
this->display->setCursor(x0 + k * (CHR_WIDTH(this->decor->textSize)),
y0 + 0);
this->display->setTextColor(this->decor->bgColor);
this->display->print(oldVal[k]);
}
}
this->display->setCursor(x0 + k * (CHR_WIDTH(this->decor->textSize)),
y0 + 0);
this->display->setTextColor(this->decor->fgColor);
this->display->print(newVal[k]);
}
}
-55
View File
@@ -1,55 +0,0 @@
#ifndef __UI_COMPONENT
#define __UI_COMPONENT
#include "Arduino_GFX.h"
#include "UIDecorations.h"
#include <stdint.h>
enum ComponentType {
STRING,
TABLE,
};
struct UIDimensions {
uint16_t x = 0, y = 0;
uint16_t width = 0, height = 0;
UIDimensions(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
};
struct UIElement {
Arduino_GFX *display;
UIDimensions dims;
UIDecorations *decor;
char *title;
uint64_t lastUpdate = 0;
uint16_t refreshRate = 0;
ComponentType type;
UIElement(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor,
char *Title);
// Getters
int16_t getContentAreaX0();
int16_t getContentAreaY0();
uint16_t getContentAreaHeight();
uint16_t getContentAreaWidth();
int16_t getTitleAreaX0();
int16_t getTitleAreaY0();
uint16_t getTitleAreaHeight();
uint16_t getTitleAreaWidth();
// Positioning
void horizontalCenter(UIElement *reference);
void verticalCenter(UIElement *reference);
void placeBelow(UIElement *reference);
void placeRight(UIElement *reference);
void placeLeft(UIElement *reference);
// Drawing
void drawBox();
void noBox();
void replaceString(char *oldVal, char *newVal);
};
#endif
-6
View File
@@ -1,6 +0,0 @@
#include "UIDecorations.h"
UIDecorations::UIDecorations() {}
UIDecorations::UIDecorations(uint16_t bg, uint16_t fg, uint16_t title,
uint16_t border, uint8_t titleSize,
uint8_t textSize) {}
-18
View File
@@ -1,18 +0,0 @@
#ifndef __UI_DECORATIONS
#define __UI_DECORATIONS
#include "UIDrawing.h"
#include <stdint.h>
struct UIDecorations {
bool hasBorder = true;
uint16_t bgColor = MAIN_BG_COLOR, fgColor = MAIN_FG_COLOR;
uint16_t titleColor = MAIN_FG_COLOR, borderColor = RED;
uint8_t titleSize = 2, textSize = 4;
UIDecorations();
UIDecorations(uint16_t bg, uint16_t fg, uint16_t title, uint16_t border,
uint8_t titleSize, uint8_t textSize);
};
#endif
-107
View File
@@ -1,107 +0,0 @@
#include "UIDrawing.h"
#include "Arduino_GFX.h"
#include "HardwareSerial.h"
#include "UIComponent.h"
#include <cstdint>
/* TODO:
* On the placement functions, change 5 to a variable or something
*/
// #define DEBUG
uint16_t calculateHeight(int16_t titleSize, int16_t textSize, int16_t nLines) {
return CHR_HEIGHT(titleSize) + CHR_HEIGHT(textSize) +
DEFAULT_TITLE_CONTENT_SPACING +
(2 * (DEFAULT_BORDER_THICKNESS + DEFAULT_MARGIN));
}
uint16_t calculateWidth(int16_t textSize, int16_t nChars) {
return CHR_WIDTH(textSize) * nChars +
(2 * (DEFAULT_MARGIN + DEFAULT_BORDER_THICKNESS));
}
void UIElement::horizontalCenter(UIElement *ref) {
uint16_t middlePoint = 0;
if (ref != nullptr) {
Serial2.println("We are here indeed!");
middlePoint = ref->dims.x + (ref->dims.width / 2);
} else {
middlePoint = this->display->width() / 2;
}
this->dims.x = middlePoint - (this->dims.width / 2);
}
// We can add a mode of alignment here, center, left, right, whatever
void UIElement::placeBelow(UIElement *ref) {
// For now the default alignment will be left until I need some other
this->dims.x = ref->dims.x;
this->dims.y = ref->dims.y + ref->dims.height + 5;
}
void UIElement::placeRight(UIElement *ref) {
this->dims.x = ref->dims.x + ref->dims.width + 5;
}
void UIElement::placeLeft(UIElement *ref) {
this->dims.x = ref->dims.x - this->dims.width - 5;
}
void UIElement::drawBox() {
// This is the border rectangle
if (this->decor->hasBorder) {
for (int k = 0; k < DEFAULT_BORDER_THICKNESS; k++) {
this->display->drawRect(
this->dims.x + k, this->dims.y + k, this->dims.width - (k * 2),
this->dims.height - (k * 2), this->decor->borderColor);
}
}
// For debugging purpouses
// title area
int16_t x = 0, y = 0;
uint16_t w = 0, h = 0;
// U8G2 u8g2; // No display pin setup needed since we're only using font data
// u8g2.setFont(u8g2_font_9x18_tf);
// int ascent = u8g2.getAscent();
h = this->getTitleAreaHeight();
uint16_t titleAreaWidth = this->getTitleAreaWidth();
int16_t titleTopLeftX = this->getTitleAreaX0();
#ifdef DEBUG
// Title area bounds
this->display->drawRect(titleTopLeftX, this->getTitleAreaY0(), titleAreaWidth,
h, GREEN);
#endif
// content area
x = this->getContentAreaX0();
y = this->getContentAreaY0();
w = this->getContentAreaWidth();
h = this->getContentAreaHeight();
#ifdef DEBUG
// Content area bounds
this->display->drawRect(x, y, w, h, BLUE);
#endif
if (this->decor->hasBorder) {
// Remove the border line behind the title
this->display->fillRect(titleTopLeftX - DEFAULT_MARGIN, this->dims.y,
titleAreaWidth + DEFAULT_MARGIN,
DEFAULT_BORDER_THICKNESS, MAIN_BG_COLOR);
// Render the title
this->display->setCursor(titleTopLeftX, this->dims.y);
this->display->print(this->title);
}
#ifdef DEBUG
// Element bounds
this->display->drawRect(this->dims.x, this->dims.y, this->dims.width,
this->dims.height, PURPLE);
#endif
}
-25
View File
@@ -1,25 +0,0 @@
#ifndef __CURSES_UI_VISUALS
#define __CURSES_UI_VISUALS
#include <Arduino_GFX.h>
#define RIGHT 0
#define INVERTED 1
#define LEFT 2
#define NORMAL 3
#define DEFAULT_BORDER_THICKNESS 3
#define DEFAULT_TITLE_CONTENT_SPACING 3
#define DEFAULT_MARGIN 3
#define MAIN_BG_COLOR RGB565(20, 10, 10)
#define MAIN_FG_COLOR RGB565(255, 255, 255)
#define CHR_WIDTH(size) (6 * size)
#define CHR_HEIGHT(size) (8 * size)
#define CHR_SPACE(size) (size)
uint16_t calculateHeight(int16_t titleSize, int16_t textSize, int16_t nLines);
uint16_t calculateWidth(int16_t textSize, int16_t nChars);
#endif
-48
View File
@@ -1,48 +0,0 @@
#include "UIString.h"
#include "HardwareSerial.h"
#include "UIComponent.h"
#include <Arduino_GFX.h>
UIString::UIString() : UIElement(nullptr, {0, 0, 0, 0}, nullptr, (char *)"") {
this->type = STRING;
memset(this->value, 0, this->bufferSize);
}
UIString::UIString(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor,
char *title)
: UIElement(d, dims, decor, title) {
this->type = STRING;
memset(this->value, 0, this->bufferSize);
}
void UIString::Update(const char *v) {
uint64_t time = millis();
if ((time - this->lastUpdate) <= this->refreshRate) {
return;
}
// If the value is larger than our buffer - should add a way of knowing we
// got an error here
if (strlen(v) >= this->bufferSize) {
return;
}
// If the value hasn't changed we do not need to re-render
if (strcmp(this->value, v) == 0) {
return;
}
char newVal[this->bufferSize];
memset(newVal, 0, this->bufferSize);
// strncpy(newVal, v, this->bufferSize - 1);
sprintf(newVal, "%s", v);
char oldVal[this->bufferSize];
memset(oldVal, 0, this->bufferSize);
// strncpy(oldVal, v, this->bufferSize - 1);
sprintf(oldVal, "%s", this->value);
// Figure out the x0 and y0 for the text
replaceString(oldVal, newVal);
strncpy(this->value, v, this->bufferSize);
}
-17
View File
@@ -1,17 +0,0 @@
#ifndef __UI_STRING
#define __UI_STRING
#include "UIComponent.h"
// Representation of single line strings
struct UIString : UIElement {
static const size_t bufferSize = 64;
char value[bufferSize];
UIString();
UIString(Arduino_GFX *d, UIDimensions dims, UIDecorations *decor,
char *title);
void Update(const char *value);
};
#endif
-82
View File
@@ -1,82 +0,0 @@
#include "UITable.h"
#include "HardwareSerial.h"
#include "UIDecorations.h"
#include "UIDrawing.h"
#include <cstdint>
#define DEBUG
UITable::UITable(Arduino_GFX *gfx, UIDimensions dims, UIDecorations *decor,
int nRows, int nCols, int *colWidths, char *title)
: UIElement(gfx, dims, decor, title) {
this->type = TABLE;
this->nRows = nRows;
this->nColumns = nCols;
this->tableData = new UIString *[this->nRows * this->nColumns];
this->colWidths = colWidths;
// Default decoration for every cell
this->decor->textSize = 2;
for (int k = 0; k < ROWS * COLUMNS; k++) {
UIDecorations *cellDecor = new UIDecorations();
cellDecor->textSize = this->decor->textSize;
UIDimensions dims = {0, 0, 0, 0};
this->tableData[k] = new UIString(display, dims, cellDecor, (char *)"");
}
int cellH = CHR_HEIGHT(this->decor->textSize) + CELL_MARGIN;
// Initialize the table dimensions
//// Calculate the height
this->dims.height = ROWS * cellH + this->getTitleAreaHeight() +
DEFAULT_MARGIN + DEFAULT_BORDER_THICKNESS;
//// Calculate the width
for (int c = 0; c < this->nColumns; c++) {
this->dims.width +=
(CHR_WIDTH(this->decor->textSize) + CELL_MARGIN) * this->colWidths[c];
}
this->dims.width += (DEFAULT_BORDER_THICKNESS + DEFAULT_MARGIN) * 2;
}
uint16_t UITable::getContentAreaHeight() {
return this->dims.height - (DEFAULT_MARGIN * 2) - DEFAULT_BORDER_THICKNESS -
this->getTitleAreaHeight();
}
/*
* This was hardcoded for the relative and had a warning for that but
* I recon its dynamic enough for now
*/
void UITable::setup() {
int cellH = CHR_HEIGHT(this->decor->textSize) + CELL_MARGIN;
// Initialize the cells
uint16_t yOffset = this->getContentAreaY0();
for (int r = 0; r < ROWS; r++) {
int16_t xOffset = this->getContentAreaX0();
for (int c = 0; c < COLUMNS; c++) {
this->tableData[r * COLUMNS + c]->refreshRate = this->refreshRate;
this->tableData[r * COLUMNS + c]->decor->textSize = this->decor->textSize;
this->tableData[r * COLUMNS + c]->decor->hasBorder = false;
this->tableData[r * COLUMNS + c]->dims.x = xOffset + CELL_MARGIN;
this->tableData[r * COLUMNS + c]->dims.y = (cellH * r) + yOffset;
this->tableData[r * COLUMNS + c]->dims.height = cellH;
this->tableData[r * COLUMNS + c]->dims.width =
this->colWidths[c] * (CHR_WIDTH(this->decor->textSize) + CELL_MARGIN);
memset(this->tableData[r * COLUMNS + c]->value, 0,
this->tableData[r * COLUMNS + c]->bufferSize);
xOffset +=
this->colWidths[c] * (CHR_WIDTH(this->decor->textSize) + CELL_MARGIN);
#ifdef DEBUG
this->tableData[r * COLUMNS + c]->drawBox();
this->tableData[r * COLUMNS + c]->Update("---");
#endif
}
}
}
-31
View File
@@ -1,31 +0,0 @@
#ifndef __UI_TABLE
#define __UI_TABLE
#include "Arduino_GFX.h"
#include "UIComponent.h"
#include "UIString.h"
#define COLUMNS 3
#define ROWS 5
#define CELL_MARGIN 2
/* Very shitty table element, only works for the relative as of now
*/
struct UITable : UIElement {
// will hold rows * columns UIStrings to fill the table
int nRows = 0;
int nColumns = 0;
int *colWidths;
UIString **tableData;
UITable(Arduino_GFX *gfx, UIDimensions dims, UIDecorations *decor, int nR,
int nC, int *cWidth, char *t);
// void Update(StandingLine standings[5]);
void setup();
// Getters
uint16_t getContentAreaHeight();
};
#endif
+9
View File
@@ -17,6 +17,15 @@ build_flags = -Iinclude/
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
-std=gnu++17
-DLOG_LEVEL=LOG_LEVEL_INFO
build_unflags = -std=gnu++11
# -Ilib/u8g2/csrc
# -Ilib/u8g2/cppsrc
board_build.arduino.memory_type = qio_opi
lib_deps =
../lib/embeslogger/
../lib/eslabsCurses/
lib_extra_dirs = ../lib
+20
View File
@@ -0,0 +1,20 @@
#include "commands.h"
char* CommandToStr(Command id) {
switch(id) {
case CmdRequestID:
return (char*)"CmdRequestID";
case CmdAckID:
return (char*)"CmdAckID";
case CmdCreateWindow:
return (char*)"CmdCreateWindow";
case CmdDestroyWindow:
return (char*)"CmdDestroyWindow";
case CmdUpdateWinDims:
return (char*)"CmdUpdateWinDims";
case CMDData:
return (char*)"CMDData";
default:
return (char*)"CmdUnknown";
}
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef __COMMANDS__
#define __COMMANDS__
#include <stdint.h>
typedef enum : uint8_t {
CmdUnknown = 0,
CmdRequestID = 1,
CmdAckID = 2,
CmdCreateWindow = 3,
CmdDestroyWindow = 4,
CmdUpdateWinDims = 5, // NOTE: change this to a move cmd instead
CmdUpdateWin = 6,
CMDData = 7,
} Command;
char* CommandToStr(Command id);
#endif
+10
View File
@@ -0,0 +1,10 @@
#include "communication.h"
uint8_t CRC8(const uint8_t *data, size_t len) {
uint8_t crc = 0x00;
while (len--) {
crc = pgm_read_byte(&crc8_table[crc ^ *data++]);
}
return crc;
}
+48
View File
@@ -0,0 +1,48 @@
#ifndef __COMMUNICATION__
#define __COMMUNICATION__
#include "pgmspace.h"
#include <Arduino.h>
#include <cstdint>
const byte STX = 0x02;
const byte ETX = 0x03;
const byte ACK = 0x06;
static const uint8_t crc8_table[256] PROGMEM = {
0x00,0x07,0x0E,0x09,0x1C,0x1B,0x12,0x15,
0x38,0x3F,0x36,0x31,0x24,0x23,0x2A,0x2D,
0x70,0x77,0x7E,0x79,0x6C,0x6B,0x62,0x65,
0x48,0x4F,0x46,0x41,0x54,0x53,0x5A,0x5D,
0xE0,0xE7,0xEE,0xE9,0xFC,0xFB,0xF2,0xF5,
0xD8,0xDF,0xD6,0xD1,0xC4,0xC3,0xCA,0xCD,
0x90,0x97,0x9E,0x99,0x8C,0x8B,0x82,0x85,
0xA8,0xAF,0xA6,0xA1,0xB4,0xB3,0xBA,0xBD,
0xC7,0xC0,0xC9,0xCE,0xDB,0xDC,0xD5,0xD2,
0xFF,0xF8,0xF1,0xF6,0xE3,0xE4,0xED,0xEA,
0xB7,0xB0,0xB9,0xBE,0xAB,0xAC,0xA5,0xA2,
0x8F,0x88,0x81,0x86,0x93,0x94,0x9D,0x9A,
0x27,0x20,0x29,0x2E,0x3B,0x3C,0x35,0x32,
0x1F,0x18,0x11,0x16,0x03,0x04,0x0D,0x0A,
0x57,0x50,0x59,0x5E,0x4B,0x4C,0x45,0x42,
0x6F,0x68,0x61,0x66,0x73,0x74,0x7D,0x7A,
0x89,0x8E,0x87,0x80,0x95,0x92,0x9B,0x9C,
0xB1,0xB6,0xBF,0xB8,0xAD,0xAA,0xA3,0xA4,
0xF9,0xFE,0xF7,0xF0,0xE5,0xE2,0xEB,0xEC,
0xC1,0xC6,0xCF,0xC8,0xDD,0xDA,0xD3,0xD4,
0x69,0x6E,0x67,0x60,0x75,0x72,0x7B,0x7C,
0x51,0x56,0x5F,0x58,0x4D,0x4A,0x43,0x44,
0x19,0x1E,0x17,0x10,0x05,0x02,0x0B,0x0C,
0x21,0x26,0x2F,0x28,0x3D,0x3A,0x33,0x34,
0x4E,0x49,0x40,0x47,0x52,0x55,0x5C,0x5B,
0x76,0x71,0x78,0x7F,0x6A,0x6D,0x64,0x63,
0x3E,0x39,0x30,0x37,0x22,0x25,0x2C,0x2B,
0x06,0x01,0x08,0x0F,0x1A,0x1D,0x14,0x13,
0xAE,0xA9,0xA0,0xA7,0xB2,0xB5,0xBC,0xBB,
0x96,0x91,0x98,0x9F,0x8A,0x8D,0x84,0x83,
0xDE,0xD9,0xD0,0xD7,0xC2,0xC5,0xCC,0xCB,
0xE6,0xE1,0xE8,0xEF,0xFA,0xFD,0xF4,0xF3
};
uint8_t CRC8(const uint8_t *data, size_t len);
#endif
+26
View File
@@ -0,0 +1,26 @@
#include "packets.h"
#include <Arduino.h>
#include <cstdint>
#include <cstring>
void initIdentificationPacket(IdentificationPacket* packet, const char* name,
uint8_t id) {
memset(packet, 0, sizeof(*packet));
packet->StartMarker = 0x02; // Start of text
packet->EndMarker = 0x03; // End of text
packet->DeviceID = id;
packet->PktType = identificationPacket;
// snprintf(packet->deviceName, sizeof(packet->deviceName), "%s", name);
strncpy(packet->deviceName, name, strlen(name));
packet->deviceName[strlen(name)] = '\0';
}
void initWindowIDReply(WindowIDReply* pkt, int16_t id) {
memset(pkt, 0, sizeof(*pkt));
pkt->StarMarker = 0x02;
pkt->EndMarker = 0x03;
pkt->wID = id;
}
+39
View File
@@ -0,0 +1,39 @@
#ifndef __PACKETS__
#define __PACKETS__
#include "UIDimensions.h"
#include <stdint.h>
typedef uint8_t PacketType;
const uint8_t identificationPacket = 0;
struct __attribute__((packed)) IdentificationPacket {
char StartMarker;
uint8_t DeviceID;
PacketType PktType;
char deviceName[32];
char EndMarker;
};
void initIdentificationPacket(IdentificationPacket* packet, const char* name,
uint8_t id);
struct __attribute__((packed)) NewWindowBody {
char StartMarker;
uint16_t x0;
uint16_t y0;
uint16_t width;
uint16_t height;
char title[32];
char EndMarker;
};
struct __attribute__((packed)) WindowIDReply {
char StarMarker;
int16_t wID;
char EndMarker;
};
void initWindowIDReply(WindowIDReply* pkt, int16_t id);
#endif
+143
View File
@@ -0,0 +1,143 @@
#include "walkieTalkie.h"
#include "HardwareSerial.h"
#include "logger.h"
#include "communication/commands.h"
#include "communication/communication.h"
#include <cstdint>
#include <stdint.h>
#include <Arduino.h>
namespace WalkieTalkie {
static inline void WaitingSTXRoutine(byte *b) {
LOG_TRACE(F("First byte is: %d\n"), *b);
if (*b == STX) {
state = RecvCMD;
resetBuffer();
}
}
static inline void ResetStateMachine() {
state = WaitingSTX;
resetBuffer();
}
static inline void RecvCMDRoutine(byte *b) {
LOG_TRACE(F("Reading the command byte\n"));
uint8_t cmdVal = Command(*b);
if (cmdVal == 0 || cmdVal > CMDData) {
LOG_TRACE(F("Invalid CMD byte: 0x%02x. Resetting parser.\n"), cmdVal);
ResetStateMachine();
return;
}
LOG_TRACE(F(" Command: %d\n"), *b);
tempCommand = (Command)cmdVal;
state = RecvLen;
resetBuffer();
}
static inline void RecvLenRoutine(byte *b) {
LOG_TRACE(F("Reading the len\n"));
buffer[bufferIndex++] = *b;
// we are currently using a int16_t for the len so: 2 bytes
if (bufferIndex > 1) {
len = buffer[0] | (buffer[1] << 8);
if (len > 2056 || len == 0) {
LOG_WARN(F("Corrupt length received: %d. Resetting parser.\n"), len);
ResetStateMachine();
return;
}
LOG_TRACE(F("Len: %d\n"), len);
state = RecvPayload;
resetBuffer();
}
}
static inline void RecvPayloadRoutine(uint8_t *payload, byte *b) {
LOG_TRACE(F("Reading payload: %-.3d %s 0x%02x\n"), bufferIndex, " - ", *b);
buffer[bufferIndex++] = *b;
if (bufferIndex == len) {
// put the buffer data somewhere
memcpy(payload, buffer, len);
state = RecvCRC;
resetBuffer();
}
}
static inline int RecvCRCRoutine(byte *b) {
crc = *b;
LOG_TRACE(F("Reacing CRC: %d\n"), crc);
state = RecvETX;
resetBuffer();
return 0;
}
static inline int RecvETXRoutine(Command *command, uint8_t *payload, byte *b) {
LOG_TRACE(F("READING ETX\n"));
state = WaitingSTX;
uint8_t computedCRC = CRC8(payload, len);
if (*b != ETX) {
LOG_TRACE(F("Failure, last byte was: %d\n"), *b);
return -1;
} else if (crc != computedCRC) {
LOG_TRACE(F("CRC don't match. Expected: %d, got: %d\n"), crc, computedCRC);
return -2;
} else {
*command = tempCommand;
return len;
}
}
int16_t RecvStream(Command *command, uint8_t *payload, uint16_t payloadMax) {
if (state != WaitingSTX && (millis() - lastByteTime > SERIAL_TIMEOUT_MS)) {
state = WaitingSTX;
resetBuffer();
}
while (Serial.available() > 0) {
lastByteTime = millis();
uint8_t byte = Serial.read();
switch(state) {
case WaitingSTX: {
WaitingSTXRoutine(&byte);
break;
}
case RecvCMD: {
RecvCMDRoutine(&byte);
break;
}
case RecvLen: {
RecvLenRoutine(&byte);
break;
}
case RecvPayload: {
RecvPayloadRoutine(payload, &byte);
break;
}
case RecvCRC: {
RecvCRCRoutine(&byte);
break;
}
case RecvETX: {
return RecvETXRoutine(command, payload, &byte);
}
}
}
return 0;
}
};
+48
View File
@@ -0,0 +1,48 @@
#ifndef __WALKIE_TALKIE__
#define __WALKIE_TALKIE__
#include "communication/commands.h"
#include <cstdint>
#include <Arduino.h>
namespace WalkieTalkie {
typedef enum {
WaitingSTX = 0,
RecvCMD = 1,
RecvLen = 2,
RecvPayload = 3,
RecvCRC = 4,
RecvETX = 5,
} RecvState;
static uint8_t tempLenBuffer[2];
inline uint8_t buffer[4096];
inline int16_t bufferIndex = 0;
inline RecvState state = WaitingSTX;
inline int16_t len = 0;
inline uint8_t crc = 0;
inline Command tempCommand = CmdUnknown;
// Timing control
inline int64_t lastByteTime = 0;
inline int64_t SERIAL_TIMEOUT_MS = 15;
inline static void resetBuffer() {
bufferIndex = 0;
}
struct ACKPacket {
uint8_t StartMarker;
uint8_t ACK;
uint8_t EndMarker;
};
template<typename PacketType>
void SendData(PacketType *packet) {
Serial.write((uint8_t*)packet, sizeof(*packet));
Serial.flush();
}
int16_t RecvStream(Command *command, uint8_t *payload, uint16_t payloadMax);
};
#endif
-84
View File
@@ -1,84 +0,0 @@
#include "data.h"
#include <Arduino.h>
#include <cstring>
uint8_t packetBuffer[sizeof(DataPacket)] = {0};
int bufferIndex = 0;
bool isReceiving = false;
void SendDataRequest() {
DataReq req = {5};
Serial.write((uint8_t *)&req, sizeof(req));
Serial.flush();
}
int RecvDataPacket(DataPacket *p) {
while (Serial.available() > 0) {
uint8_t byte = Serial.read();
if (!isReceiving) {
if (byte == 0x02) {
isReceiving = true;
bufferIndex = 0;
}
}
packetBuffer[bufferIndex++] = byte;
if (bufferIndex >= sizeof(DataPacket)) {
Serial.flush();
bufferIndex = 0;
isReceiving = false;
memcpy(p, packetBuffer, sizeof(DataPacket));
if (p->EndMarker != 0x03) {
// Serial2.println(F("\r////////////// DATA IS MALFORMED
// //////////////"));
return 1;
}
return 0;
// for(int k = 0; k < sizeof(DataPacket); k++) {
// if (k % 8 == 0) {
// Serial2.printf("\r\n");
// }
// Serial2.printf(" 0x%02x", packetBuffer[k]);
// }
// Serial2.printf("\r\n");
// Serial2.printf("%d / %d [%2d] || %d / %d [%2d]\n\n\r",
// ESP.getFreeHeap(),
// ESP.getHeapSize(),
// (int)(((float)(ESP.getHeapSize() - ESP.getFreeHeap()) /
// ESP.getHeapSize()) *
// 100),
// ESP.getFreePsram(), ESP.getPsramSize(),
// (int)(((float)(ESP.getPsramSize() - ESP.getFreePsram())
// /
// ESP.getPsramSize()) *
// 100));
// debugDataPacket(&packet);
}
}
return 1;
}
void DebugDataPacket(DataPacket *p) {
Serial2.printf("\rSize: %zu\n", sizeof(struct DataPacket));
Serial2.printf("\rStartMarker: 0x%02x\n", p->StartMarker);
Serial2.printf("\rSpeed: %s\n", p->speed);
Serial2.printf("\rGear: %s\n", p->gear);
Serial2.printf("\rRPM: %s\n", p->rpm);
Serial2.printf("\rLapNumber: %s\n", p->LapNumber);
Serial2.printf("\rcurrLapTime: %s\n", p->currLapTime);
Serial2.printf("\rlastLapTime: %s\n", p->lastLapTime);
Serial2.printf("\rfuelEst: %s\n", p->FuelEst);
Serial2.printf("\rStandings:\n");
for (int k = 0; k < 5; k++) {
Serial2.printf("\r Lap: %s\n", p->standings[k].Lap);
Serial2.printf("\r DriverName: %s\n", p->standings[k].DriverName);
Serial2.printf("\r TimeBehind: %s\n", p->standings[k].TimeBehindString);
}
Serial2.printf("\rEndMarker: 0x%02x\n\n", p->EndMarker);
}
-59
View File
@@ -1,59 +0,0 @@
#ifndef __DASH_DISPLAY_DATA
#define __DASH_DISPLAY_DATA
#include <stdint.h>
struct DataReq {
uint8_t type;
};
// Car data
const int speedLen = 5;
const int gearLen = 3;
const int rpmLen = 6;
const int brakeBiasLen = 6;
// Lap data
const int lapNumberLen = 5;
const int DeltaToBestLapLen = 6;
const int bestLapTimeLen = 10;
const int currLapTimeLen = 10;
const int lastLapTimeLen = 10;
// Fuel data
const int FuelTankLen = 15;
const int FuelEstLen = 15;
const int LapStringLen = 4;
const int DriverNameLen = 24;
const int TimeBehindStringLen = 8;
#pragma pack(push, 1)
struct StandingLine {
char Lap[LapStringLen];
char DriverName[DriverNameLen];
char TimeBehindString[TimeBehindStringLen];
};
struct DataPacket {
uint8_t StartMarker;
char speed[speedLen];
char gear[gearLen];
char rpm[rpmLen];
char brakeBias[brakeBiasLen];
char LapNumber[lapNumberLen];
char DeltaToBestLap[DeltaToBestLapLen];
char bestLapTime[bestLapTimeLen];
char currLapTime[currLapTimeLen];
char lastLapTime[lastLapTimeLen];
char FuelEst[FuelEstLen];
StandingLine standings[5];
uint8_t EndMarker;
};
#pragma pack(pop)
void DebugDataPacket(DataPacket *p);
int RecvDataPacket(DataPacket *p);
void SendDataRequest();
#endif
+1 -1
View File
@@ -10,7 +10,7 @@
#define PWM_FREQUENCY 5000
#define PWM_RESOLUTION 12
#define SCREEN_ORIENTATION 2
#define SCREEN_ORIENTATION 4
#define SCREEN_LANDSCAPE
/* More display class:
+173 -216
View File
@@ -9,14 +9,24 @@ to have more ways to run analytics
#include "Arduino_GFX.h"
#include "HardwareSerial.h"
#include "UIComponent.h"
#include "UIDecorations.h"
#include "UIDimensions.h"
#include "UIDrawing.h"
#include "UIScreen.h"
#include "communication/commands.h"
#include "communication/packets.h"
#include "communication/walkieTalkie.h"
#include "logger.h"
#include "values.h"
#include "windowPool.h"
#include "methods/methods.h"
// #include "UIDrawing.h"
#include "UIString.h"
#include "UITable.h"
#include "data.h"
// #include "UITable.h"
#include "displaySetup.h"
#include "esp32-hal-psram.h"
#include "ui.h"
// #include "ui.h"
#include <Arduino.h>
#include <Arduino_GFX_Library.h>
#include <cstdint>
@@ -25,6 +35,9 @@ to have more ways to run analytics
// #include <nvs.h>
// #include <nvs_flash.h>
#define ESLABS_DEVICE_ID 0x01
#define ESLABS_DEVICE_NAME "ESLabs CDashDisplay"
#define UART1_TX 19
#define UART1_RX 18
@@ -43,238 +56,182 @@ Arduino_ESP32RGBPanel *panel = new Arduino_ESP32RGBPanel(
//
Arduino_GFX *gfx = new Arduino_RGB_Display(TFT_HOR_RES, TFT_VER_RES, panel, 16);
// HardwareSerial debugSerial(1);
Curses::Screen mainScreen(gfx, UIDimensions(0, 0, TFT_HOR_RES, TFT_VER_RES),
UIDecorations());
// UIelements
DashUI *ui = new DashUI();
UIDecorations *rpmBarDecor = new UIDecorations();
UIDecorations *rpmTextDecor = new UIDecorations();
UIDecorations *speedTextDecor = new UIDecorations();
UIDecorations *gearTextDecor = new UIDecorations();
UIDecorations *lapDeltaDecor = new UIDecorations();
UIDecorations *bestLapDecor = new UIDecorations();
UIDecorations *lastLapDecor = new UIDecorations();
UIDecorations *curLapDecor = new UIDecorations();
UIDecorations *fuelTankDecor = new UIDecorations();
UIDecorations *fuelConsumptionDecor = new UIDecorations();
UIDecorations *brakeBiasDecor = new UIDecorations();
UIDecorations *relativeDecor = new UIDecorations();
UIBar rpmBar(gfx, UIDimensions(0, 0, 0, 0), rpmBarDecor, (char *)"Tach");
UIString rpmText(gfx, UIDimensions(0, 0, 0, 0), rpmTextDecor, (char *)"RPM");
UIString speedText(gfx, UIDimensions(0, 0, 0, 0), speedTextDecor,
(char *)"Speed");
UIString gearText(gfx, UIDimensions(0, 0, 0, 0), rpmTextDecor, (char *)"Gear");
UIString lapDelta(gfx, UIDimensions(0, 0, 0, 0), lapDeltaDecor,
(char *)"Delta");
UIString bestLap(gfx, UIDimensions(0, 0, 0, 0), bestLapDecor,
(char *)"Best Lap");
UIString lastLap(gfx, UIDimensions(0, 0, 0, 0), lastLapDecor,
(char *)"Last Lap");
UIString curLap(gfx, UIDimensions(0, 0, 0, 0), curLapDecor, (char *)"Cur. Lap");
UIString fuelTank(gfx, UIDimensions(0, 0, 0, 0), fuelTankDecor,
(char *)"Fuel Tank");
UIString fuelConsumption(gfx, UIDimensions(0, 0, 0, 0), fuelConsumptionDecor,
(char *)"Fuel Consumption");
UIString brakeBias(gfx, UIDimensions(0, 0, 0, 0), brakeBiasDecor,
(char *)"Brakes");
// UIDecorations *gearTextDecor = new UIDecorations();
// UIString gearText(gfx, UIDimensions(0, 0, 0, 0), gearTextDecor, (char
// *)"Gear");
//
// UIElement mainWindow(
// gfx,
// UIDimensions(0, 0, TFT_HOR_RES, TFT_VER_RES),
// gearTextDecor,
// (char*)"MAIN WINDOW"
// );
void setup() {
psramInit();
Serial.setRxBufferSize(8192);
Serial.begin(115200);
// Initialize the debug serial object
Serial2.begin(115200, SERIAL_8N1, UART1_RX, UART1_TX);
Serial2.flush();
Logger::Initialize(UART1_RX, UART1_TX);
Serial2.print(F("\r=== ESP32 SimRacing DashDisplay ===\r\n"));
LOG_INFO(F("=== ESP32 SimRacing DashDisplay ===\r\n"));
LOG_INFO(F("* Initiating display\r\n"));
Serial2.print(F("* Initiating display\r\n"));
initialDisplaySetup(gfx);
// Setup the relative
// Define the width of the columns in number of chars
int *colW = (int *)malloc(sizeof(int) * 3);
colW[0] = 3;
colW[1] = 18;
colW[2] = 9;
UITable *relative =
new UITable(gfx, UIDimensions(250, 250, 0, 0), relativeDecor, 5, 3, colW,
(char *)"Relative");
relativeDecor->textSize = 2;
relative->dims.x = gfx->width() - relative->dims.width;
relative->dims.y = gfx->height() - relative->dims.height;
relative->refreshRate = 750;
relative->setup();
relative->drawBox();
ui->relative = relative;
// Only setup the main screen after initializing the Serial2
mainScreen.Setup("Main Window");
UIElement *mainWindow = mainScreen.mainWindowHandle;
mainWindow->drawBox();
// Setup the rpmBar box
rpmBarDecor->textSize = 3;
rpmBar.dims.height = 50;
rpmBar.dims.width = 450;
rpmBar.horizontalCenter(nullptr);
rpmBar.dims.x += 10;
rpmBar.range = 8;
rpmBar.refreshRate = 50;
ui->barTacho = &rpmBar;
ui->barTacho->Box();
ui->barTacho->Update((char*)"");
// Setup the rpmText box
rpmTextDecor->textSize = 7;
rpmText.dims.height =
calculateHeight(rpmTextDecor->titleSize, rpmTextDecor->textSize, 1);
rpmText.dims.width = calculateWidth(rpmTextDecor->textSize, 5);
rpmText.placeBelow(&rpmBar);
rpmText.horizontalCenter(&rpmBar);
rpmText.dims.y += 40; // We gotta fix how the bar declares its height instead
// of doing this here
rpmText.refreshRate = 100;
ui->digiTacho = &rpmText;
ui->digiTacho->drawBox();
ui->digiTacho->Update("-----");
// Setup the speedText box
speedTextDecor->textSize = 4;
speedText.dims.height =
calculateHeight(speedTextDecor->titleSize, speedTextDecor->textSize, 1);
speedText.dims.width = calculateWidth(speedTextDecor->textSize, 4);
speedText.placeBelow(&rpmBar);
speedText.placeRight(&rpmText);
speedText.dims.y += 40; // We gotta fix how the bar declares its height
// instead of doing this here
ui->digiSpeedo = &speedText;
ui->digiSpeedo->drawBox();
ui->digiSpeedo->Update("---");
// Setup the gear text box
gearTextDecor->textSize = 7;
gearText.dims.height =
calculateHeight(gearTextDecor->titleSize, gearTextDecor->textSize, 1);
gearText.dims.width = calculateWidth(gearTextDecor->textSize, 2);
gearText.dims.y = rpmText.dims.height + 5;
gearText.placeLeft(&rpmText);
ui->digiGear = &gearText;
ui->digiGear->drawBox();
ui->digiGear->Update("-");
// Setup the lap delta box
lapDeltaDecor->textSize = 3;
lapDelta.dims.height =
calculateHeight(lapDeltaDecor->titleSize, lapDeltaDecor->textSize, 1);
lapDelta.dims.width = calculateWidth(lapDeltaDecor->textSize, 6);
lapDelta.placeBelow(&rpmText);
lapDelta.horizontalCenter(&rpmText);
ui->lapDelta = &lapDelta;
ui->lapDelta->drawBox();
ui->lapDelta->Update("--.-");
// Setup the best lap box
bestLapDecor->textSize = 3;
bestLap.dims.height =
calculateHeight(bestLapDecor->titleSize, bestLapDecor->textSize, 1);
bestLap.dims.width = calculateWidth(bestLapDecor->textSize, 8);
ui->bestLap = &bestLap;
ui->bestLap->drawBox();
ui->bestLap->Update("--:--.--");
// Setup the last lap box
lastLapDecor->textSize = 3;
lastLap.dims.height =
calculateHeight(lastLapDecor->titleSize, lastLapDecor->textSize, 1);
lastLap.dims.width = calculateWidth(lastLapDecor->textSize, 8);
lastLap.placeBelow(&bestLap);
ui->lastLap = &lastLap;
ui->lastLap->drawBox();
ui->lastLap->Update("--:--.--");
// Setup the cur lap box
curLapDecor->textSize = 3;
curLap.dims.height =
calculateHeight(curLapDecor->titleSize, curLapDecor->textSize, 1);
curLap.dims.width = calculateWidth(curLapDecor->textSize, 8);
curLap.placeBelow(&lastLap);
ui->curLap = &curLap;
ui->curLap->drawBox();
ui->curLap->Update("--:--.--");
// Fuel tank
fuelTankDecor->textSize = 3;
fuelTank.dims.height =
calculateHeight(fuelTankDecor->titleSize, fuelTankDecor->textSize, 1);
fuelTank.dims.width = calculateWidth(fuelTankDecor->textSize, 12);
fuelTank.placeBelow(&curLap);
ui->fuelTank = &fuelTank;
ui->fuelTank->drawBox();
ui->fuelTank->Update("--- / ---");
// Fuel consumption
fuelConsumptionDecor->textSize = 3;
fuelConsumption.dims.height = calculateHeight(
fuelConsumptionDecor->titleSize, fuelConsumptionDecor->textSize, 1);
fuelConsumption.dims.width =
calculateWidth(fuelConsumptionDecor->textSize, 12);
fuelConsumption.placeBelow(&fuelTank);
ui->fuelConsumption = &fuelConsumption;
ui->fuelConsumption->drawBox();
ui->fuelConsumption->Update("--.- | --.-");
// Brake bias
brakeBiasDecor->textSize = 3;
brakeBias.dims.height =
calculateHeight(brakeBiasDecor->titleSize, brakeBiasDecor->textSize, 1);
brakeBias.dims.width = calculateWidth(brakeBiasDecor->textSize, 5);
brakeBias.placeBelow(&fuelConsumption);
ui->brakeBias = &brakeBias;
ui->brakeBias->drawBox();
ui->brakeBias->Update("--.-");
// const char *words[] = {"hel", "wor", "why", "is", "thi", "hap", "to",
// "me"};
//
// int wIndex = 0;
// while (1) {
// for (int row = 0; row < ROWS; row++) {
// for (int col = 0; col < COLUMNS; col++) {
// ui->relative->tableData[row * COLUMNS +
// col]->Update(words[wIndex++]); if (wIndex >= 8) {
// wIndex = 0;
// }
// }
// }
// }
// nvs_handle_t handle;
// nvs_open("storage", NVS_READWRITE, &handle);
// nvs_set_i32(handle, "counter", 42);
// nvs_commit(handle);
// nvs_close(handle);
Serial2.println("* Ready for loop");
delay(500);
LOG_INFO(F("* Ready for loop"));
}
const uint16_t PayloadMax = 2056;
uint8_t payload[PayloadMax] = {0};
char lineBuffer[64]; // Enough for "XX XX XX XX XX XX XX XX "
//
void print_memory_stats() {
multi_heap_info_t info;
// MALLOC_CAP_8BIT ensures we are looking at memory capable of
// storing data (Internal RAM + PSRAM if available)
heap_caps_get_info(&info, MALLOC_CAP_8BIT);
size_t total_free = info.total_free_bytes;
size_t total_allocated = info.total_allocated_bytes;
size_t total_size = total_free + total_allocated;
size_t min_free = info.minimum_free_bytes; // "Low water mark"
LOG_INFO(F("Memory Stats:\r\n"));
LOG_INFO(F(" Total: %u bytes\r\n"), total_size);
LOG_INFO(F(" Used: %u bytes\r\n"), total_allocated);
LOG_INFO(F(" Free: %u bytes\r\n"), total_free);
LOG_INFO(F(" Min Free: %u bytes (Historic peak usage)\r\n"), min_free);
}
uint64_t lastDataRead = 0;
void loop(void) {
// Request data here
SendDataRequest();
uint64_t cur = millis();
// Receive data
DataPacket telemetryPacket;
int res = RecvDataPacket(&telemetryPacket);
Command cmd = CmdUnknown;
if (res == 0) {
ui->Update(&telemetryPacket);
if (Serial.available() > 0) {
int16_t resp = WalkieTalkie::RecvStream(&cmd, payload, PayloadMax);
if (resp < 0) {
LOG_WARN(F("Failed to receive data from serial"));
return;
} else if (resp == 0) {
return;
}
lastDataRead = millis();
}
LOG_DEBUG(F("Command: %s\n"), CommandToStr(cmd));
LOG_DEBUG(F("Response Payload Len: %d\n"), resp);
// If no data is received for 5 seconds, reset the display
if (millis() - lastDataRead >= 5000) {
// ui.speedometer->Update(0);
// ui.numberTach->Update(0);
// ui.gear->Update(0);
// ui.barTach->Update(0);
// print_memory_stats();
switch(cmd) {
case CmdRequestID:
IdentificationPacket papers;
initIdentificationPacket(&papers, ESLABS_DEVICE_NAME, ESLABS_DEVICE_ID);
WalkieTalkie::SendData(&papers);
break;
case CmdCreateWindow: {
int8_t newWindowID = Window::Create(payload, &mainScreen);
if (newWindowID < 0) {
LOG_WARN(F("Failed to add new window!\n"));
break;
}
LOG_INFO(F("Successfully added a new window: %d\n"), newWindowID);
// Now we return the ID of this new window - esdi should expect it
WindowIDReply wID;
initWindowIDReply(&wID, newWindowID);
WalkieTalkie::SendData(&wID);
break;
}
case CmdUpdateWinDims: {
LOG_INFO(F("Updating Win DIMS\n"));
bool res = Window::UpdateDims(payload, &mainScreen);
if (!res) {
LOG_WARN(F("Failed to update window dims\n"));
}
break;
};
case CmdUpdateWin: {
LOG_INFO(F("UPDATEING WINDOW\n"));
bool res = Window::UpdateWindow(payload, &mainScreen);
if (!res) {
LOG_WARN(F("Failed to update window\n"));
}
break;
}
case CmdDestroyWindow: {
uint8_t destroyedWinID = Window::Destroy(payload, &mainScreen);
if (destroyedWinID < 0) {
LOG_WARN(F("Failed to destroy window\n"));
break;
}
LOG_INFO(F("Succesfully destroyed window: %d\n"), destroyedWinID);
break;
}
case CMDData: {
LOG_DEBUG(F("Data Received: %d bytes\r\n"), resp);
// int pos = 0;
// for (int k = 0; k < resp; k++) {
// // Write 2 hex digits and a space to our local buffer
// // %02X ensures leading zeros (e.g., 0A instead of A)
// pos += sprintf(lineBuffer + pos, "%02X ", payload[k]);
//
// // Every 8 bytes OR if it's the very last byte in the payload
// if ((k + 1) % 8 == 0 || k == resp - 1) {
// // Send the completed line to the logger
// // We use LOG_DEBUG or similar so we don't spam [WARN] on every line
// LOG_DEBUG(F("%s\r\n"), lineBuffer);
//
// // Reset buffer position for the next line
// pos = 0;
// memset(lineBuffer, 0, sizeof(lineBuffer));
// }
// }
// LOG_DEBUG(F("\r\n"));
Data::Parse(payload, resp, &mainScreen);
break;
}
default:
LOG_ERROR(F("Command: %s\n"), CommandToStr(cmd));
LOG_ERROR(F("Unknown Command: `%02X` has not been implemented yet.\n"), cmd);
int pos = 0;
for (int k = 0; k < resp; k++) {
// Write 2 hex digits and a space to our local buffer
// %02X ensures leading zeros (e.g., 0A instead of A)
pos += sprintf(lineBuffer + pos, "%02X ", payload[k]);
// Every 8 bytes OR if it's the very last byte in the payload
if ((k + 1) % 8 == 0 || k == resp - 1) {
// Send the completed line to the logger
// We use LOG_DEBUG or similar so we don't spam [WARN] on every line
LOG_WARN(F("%s\r\n"), lineBuffer);
// Reset buffer position for the next line
pos = 0;
memset(lineBuffer, 0, sizeof(lineBuffer));
}
}
LOG_WARN(F("\r\n"));
break;
}
}
}
+342
View File
@@ -0,0 +1,342 @@
#include "methods.h"
#include "Arduino.h"
#include "UIBar.h"
#include "UIDecorations.h"
#include "UIDrawing.h"
#include "communication/packets.h"
#include "logger.h"
#include "UIComponent.h"
#include "UIScreen.h"
#include "values.h"
#include <cstdint>
#include <cstdio>
#include <string.h>
namespace Window {
// Window creation
void printUIDimsPacket(UIDimensions *dims) {
LOG_INFO(F(" Dimensions:\n"));
LOG_INFO(F(" X0 : %d\n"), dims->x);
LOG_INFO(F(" Y0 : %d\n"), dims->y);
LOG_INFO(F(" Width : %d\n"), dims->width);
LOG_INFO(F(" Height: %d\n"), dims->height);
}
void printUIDecorPacket(UIDecorations *decor) {
LOG_INFO(F(" Decorations:\n"));
LOG_INFO(F(" HasBorder : %d\n"), decor->hasBorder);
LOG_INFO(F(" BGColour : %d\n"), decor->bgColor);
LOG_INFO(F(" FGColour : %d\n"), decor->fgColor);
LOG_INFO(F(" TitleColour : %d\n"), decor->titleColor);
LOG_INFO(F(" BorderColour: %d\n"), decor->borderColor);
LOG_INFO(F(" TitleSize : %d\n"), decor->titleSize);
LOG_INFO(F(" TextSize : %d\n"), decor->textSize);
}
void printUIOptionsPacket(UIWindowOpts* opts) {
LOG_INFO(F(" Options:\n"));
LOG_INFO(F(" ShowID : %d\n"), opts->ShowID);
LOG_INFO(F(" Type : %d\n"), opts->WinType);
LOG_INFO(F(" PreviewValue: %s\n"), opts->PreviewValue);
}
void printUIWindowPacket(UICreateWindowPacket *win) {
LOG_INFO(F("New window:\n"));
printUIDimsPacket(&win->dims);
printUIDecorPacket(&win->decor);
printUIOptionsPacket(&win->opts);
LOG_INFO(F(" Title: %s\n"), win->title);
}
void printUpdateDimsPacket(UpdateDimsPacket *pkt) {
LOG_INFO(F("NEW DIMENSIONS:\n"));
LOG_INFO(F(" TARGET WINDOW: %d\n"), pkt->wID);
printUIDimsPacket(&pkt->dims);
}
UICreateWindowPacket::UICreateWindowPacket() {}
void setTitleWithOpts(UICreateWindowPacket* win, UIElement* elem, int16_t ID) {
if (win->opts.ShowID == ShowIDTrue) {
elem->SetTitle((char *)"%s [%2d]", (const char *)win->title, ID);
} else {
elem->SetTitle((char *)"%s", (const char *)win->title);
}
}
int8_t Create(uint8_t *payload, Curses::Screen *mainScreen) {
// Load the payload into a struct
UICreateWindowPacket win;
// Maybe find a better way to copy this - if we had metadata to the payload
// for example
memcpy((void*)&win, payload, sizeof(UICreateWindowPacket));
printUIWindowPacket(&win);
UIElement *mainWindow = mainScreen->mainWindowHandle;
int16_t childID = mainWindow->AddChild((ComponentType)win.opts.WinType);
if (childID < 0) {
LOG_DEBUG(F("COULD NOT ADD CHILD\r\n"));
return -1;
}
UIElement *childComponent = mainWindow->GetChild(childID);
setTitleWithOpts(&win, childComponent, childID);
// This are universal things
childComponent->SetUIDecorations(win.decor);
childComponent->SetUIDimensions(UIDimensions(win.dims.x, win.dims.y,
win.dims.width, win.dims.height));
childComponent->SetDisplay(mainScreen->display);
// NOTE: if I move to a composition style thing I guess I can do all this
// setup at start up time.
// I can also just have a base void* chunk of data with options I send to
// each UI type and the UI type handles it somehow, we'll see
// Handle each type of window
switch ((ComponentType)win.opts.WinType) {
case STRING:
LOG_DEBUG(F("Setting up the STRING type UIWindow\r\n"));
childComponent->drawBox();
break;
case BAR: {
LOG_DEBUG(F("Setting up the BAR type UIWindow\r\n"));
UIBar* bar = (UIBar*)childComponent;
bar->range = 9;
bar->drawBox();
break;
}
}
childComponent->Update(win.opts.PreviewValue, true);
return childID;
}
bool UpdateDims(uint8_t *payload, Curses::Screen *mainScreen) {
UpdateDimsPacket pkt;
memcpy((void*)&pkt, payload, sizeof(UpdateDimsPacket));
printUpdateDimsPacket(&pkt);
UIElement *mainWindow = mainScreen->mainWindowHandle;
UIElement* win = mainWindow->GetChild(pkt.wID);
if (win == NULL) {
return false;
}
// Delete the old window
fillRect(win->dims.x, win->dims.y, win->dims.width,
win->dims.height, win->decor.bgColor, win->display);
// Update the windows dimensions
win->dims = pkt.dims;
LOG_DEBUG(F("Win Type is: %d\r\n"), win->type);
// Redraw the window
switch ((ComponentType)win->type) {
case STRING:
win->drawBox();
break;
case BAR:
{
LOG_DEBUG(F("Updating UIBar dimensions\r\n"));
UIBar* bar = (UIBar*)win;
bar->drawBox();
break;
}
}
win->Redraw();
return true;
}
bool UpdateWindow(uint8_t *payload, Curses::Screen *mainScreen) {
UIUpdateWindowPacket pkt;
memcpy((void*)&pkt, payload, sizeof(UICreateWindowPacket));
printUIWindowPacket(&pkt.data);
UIElement* mainWindow = mainScreen->mainWindowHandle;
UIElement* win = mainWindow->GetChild(pkt.WinID);
if (win == NULL) {
return false;
}
// Delete the old window
fillRect(win->dims.x, win->dims.y, win->dims.width,
win->dims.height, win->decor.bgColor, win->display);
// Update the windows dimensons, decor and title
win->dims = pkt.data.dims;
win->decor = pkt.data.decor;
setTitleWithOpts(&pkt.data, win, pkt.WinID);
switch ((ComponentType)win->type) {
case STRING:
win->drawBox();
break;
case BAR:
{
LOG_DEBUG(F("Updating UIBar\r\n"));
UIBar* bar = (UIBar*)win;
bar->drawBox();
break;
}
}
win->Update(pkt.data.opts.PreviewValue, true);
return true;
}
// Window destruction
void printUIDestroyWindowPacket(UIDestroyWindowPacket *win) {
LOG_INFO(F("Destroy window:\n"));
LOG_INFO(F(" WinID: %d\n"), win->WinID);
}
int8_t Destroy(uint8_t *payload, Curses::Screen *mainScreen) {
UIDestroyWindowPacket win;
memcpy(&win, payload, sizeof(UIDestroyWindowPacket));
printUIDestroyWindowPacket(&win);
UIElement *mainWindow = mainScreen->mainWindowHandle;
mainWindow->RemoveChild(win.WinID);
return win.WinID;
}
};
namespace Data {
char buffer[128];
char strValue[128];
uint8_t Parse(uint8_t *payload, size_t payloadSize, Curses::Screen* mainScreen) {
uint16_t curPos = 0;
for (; curPos < payloadSize;) {
// Get the data type from the current position
int16_t wID;
memcpy(&wID, payload + curPos, 2);
curPos += 2;
uint8_t type = payload[curPos];
switch (type) {
case DataTypeUINT8: {
uint8_t value = payload[curPos + 1];
LOG_DEBUG(F("[0x%02X] RECEIVED [%2d]: %d\r\n"), type, wID, value);
sprintf(buffer, "%d", value);
curPos += 2;
break;
}
case DataTypeINT8: {
int8_t value = (int8_t)payload[curPos + 1];
LOG_DEBUG(F("[0x%02X] RECEIVED [%2d]: %d\r\n"), type, wID, value);
sprintf(buffer, "%d", value);
curPos += 2;
break;
}
case DataTypeUINT16: {
uint16_t value;
memcpy(&value, payload + curPos + 1, 2);
LOG_DEBUG(F("[0x%02X] RECEIVED [%2d]: %d\r\n"), type, wID, value);
sprintf(buffer, "%d", value);
curPos += 3; // 1 (Type) + 2 (Value)
break;
}
case DataTypeINT16: {
int16_t value;
memcpy(&value, payload + curPos + 1, 2);
LOG_DEBUG(F("[0x%02X] RECEIVED [%2d]: %d\r\n"), type, wID, value);
sprintf(buffer, "%d", value);
curPos += 3;
break;
}
case DataTypeUINT32: {
uint32_t value;
memcpy(&value, payload + curPos + 1, 4);
LOG_DEBUG(F("[0x%02X] RECEIVED [%2d]: %d\r\n"), type, wID, value);
sprintf(buffer, "%d", value);
curPos += 5; // 1 (Type) + 4 (Value)
break;
}
case DataTypeINT32: {
int32_t value;
memcpy(&value, payload + curPos + 1, 4);
LOG_DEBUG(F("[0x%02X] RECEIVED [%2d]: %d\r\n"), type, wID, value);
sprintf(buffer, "%d", value);
curPos += 5;
break;
}
case DataTypeUINT64: {
uint64_t value;
memcpy(&value, payload + curPos + 1, 8);
LOG_DEBUG(F("[0x%02X] RECEIVED [%2d]: %d\r\n"), type, wID, value);
sprintf(buffer, "%ld", value);
curPos += 9; // 1 (Type) + 8 (Value)
break;
}
case DataTypeINT64: {
int64_t value;
memcpy(&value, payload + curPos + 1, 8);
LOG_DEBUG(F("[0x%02X] RECEIVED [%2d]: %d\r\n"), type, wID, value);
sprintf(buffer, "%ld", value);
curPos += 9;
break;
}
case DataTypeCHAR: {
uint8_t value = payload[curPos + 1];
LOG_DEBUG(F("[0x%02X] RECEIVED [%2d]: %c\r\n"), type, wID, value);
sprintf(buffer, "%c", value);
curPos += 2;
break;
}
case DataTypeSTRING: {
// Strings have: Type (1), Len (1), Data (Len)
uint8_t len = payload[curPos + 1];
memcpy(strValue, payload + curPos + 2, len);
strValue[len] = '\0'; // Null terminator
//
LOG_DEBUG(F("[0x%02X] RECEIVED [%2d]: %s\r\n"), type, wID, strValue);
sprintf(buffer, "%s", strValue);
curPos += (2 + len);
break;
}
default:
// If we hit an unknown type, we are desynced.
// Better to stop than to read garbage.
LOG_ERROR(F("Unknown Type 0x%02X at pos %d\r\n"), type, curPos);
continue;
}
// Update the window value here
UIElement* mainWindow = mainScreen->mainWindowHandle;
UIElement* win = mainWindow->GetChild(wID);
if (win == NULL) {
continue;
}
win->Update(buffer, false);
}
return 0;
}
}
+89
View File
@@ -0,0 +1,89 @@
#ifndef __METHODS__
#define __METHODS__
#include <cstdint>
#include <stdint.h>
#include "UIScreen.h"
#include "UIDimensions.h"
#include "UIDecorations.h"
namespace Window {
const uint8_t ShowIDFalse = 0;
const uint8_t ShowIDTrue = 1;
struct UIWindowOpts {
uint8_t ShowID;
uint8_t WinType;
// NOTE: I recon we need to change this yo
char PreviewValue[32];
};
// Window creation
struct UICreateWindowPacket {
UIDimensions dims;
UIDecorations decor;
UIWindowOpts opts;
char title[32]; // Note: we optimize this type of data transfer if necessary
// we are sending 32 - len(title) extra bytes everytime
UICreateWindowPacket();
};
void printUIWindowPacket(UICreateWindowPacket *win);
int8_t Create(uint8_t *payload, Curses::Screen *mainScreen);
// Window destruction
struct UIDestroyWindowPacket {
int16_t WinID;
};
void printUIDestroyWindowPacket(UIDestroyWindowPacket *win);
int8_t Destroy(uint8_t *payload, Curses::Screen *mainScreen);
// NOTE: if I make the window update send these fields with the current
// values I can reduce the code for this to a single struct instead I reckon
// Window Dimensions Updates
struct UpdateDimsPacket {
int16_t wID;
UIDimensions dims;
};
void printUpdateDimsPacket(UpdateDimsPacket *pkt);
bool UpdateDims(uint8_t *payload, Curses::Screen *mainScreen);
struct UIUpdateWindowPacket {
int16_t WinID;
UICreateWindowPacket data;
};
bool UpdateWindow(uint8_t *payload, Curses::Screen *mainScreen);
};
namespace Screen {
struct UICreateScreenPacket{
uint16_t x0;
uint16_t y0;
uint16_t width;
uint16_t height;
char title[32]; // Note: we optimize this type of data transfer if necessary
// we are sending 32 - len(title) extra bytes everytime
};
}
namespace Data {
typedef uint8_t DataType;
const DataType DataTypeUINT8 = 0;
const DataType DataTypeINT8 = 1;
const DataType DataTypeUINT16 = 2;
const DataType DataTypeINT16 = 3;
const DataType DataTypeUINT32 = 4;
const DataType DataTypeINT32 = 5;
const DataType DataTypeUINT64 = 6;
const DataType DataTypeINT64 = 7;
const DataType DataTypeSTRING = 8;
const DataType DataTypeCHAR = 9;
uint8_t Parse(uint8_t *payload, size_t payloadSize, Curses::Screen* mainScreen);
}
#endif
+26 -27
View File
@@ -1,27 +1,26 @@
#include "ui.h"
#include "HardwareSerial.h"
#include "data.h"
#include <cstdint>
DashUI::DashUI() {}
void DashUI::Update(DataPacket *p) {
this->barTacho->Update(p->rpm);
this->digiTacho->Update(p->rpm);
this->digiSpeedo->Update(p->speed);
this->digiGear->Update(p->gear);
this->lapDelta->Update(p->DeltaToBestLap);
this->bestLap->Update(p->bestLapTime);
this->curLap->Update(p->currLapTime);
this->lastLap->Update(p->lastLapTime);
this->fuelConsumption->Update(p->FuelEst);
this->brakeBias->Update(p->brakeBias);
for (int row = 0; row < ROWS; row++) {
this->relative->tableData[row * COLUMNS + 0]->Update(p->standings[row].Lap);
this->relative->tableData[row * COLUMNS + 1]->Update(
p->standings[row].DriverName);
this->relative->tableData[row * COLUMNS + 2]->Update(
p->standings[row].TimeBehindString);
}
}
// #include "ui.h"
// #include "HardwareSerial.h"
// #include <cstdint>
//
// DashUI::DashUI() {}
//
// void DashUI::Update(DataPacket *p) {
// this->barTacho->Update(p->rpm);
// this->digiTacho->Update(p->rpm);
// this->digiSpeedo->Update(p->speed);
// this->digiGear->Update(p->gear);
// this->lapDelta->Update(p->DeltaToBestLap);
// this->bestLap->Update(p->bestLapTime);
// this->curLap->Update(p->currLapTime);
// this->lastLap->Update(p->lastLapTime);
// this->fuelConsumption->Update(p->FuelEst);
// this->brakeBias->Update(p->brakeBias);
//
// for (int row = 0; row < ROWS; row++) {
// this->relative->tableData[row * COLUMNS + 0]->Update(p->standings[row].Lap);
// this->relative->tableData[row * COLUMNS + 1]->Update(
// p->standings[row].DriverName);
// this->relative->tableData[row * COLUMNS + 2]->Update(
// p->standings[row].TimeBehindString);
// }
// }
+28 -28
View File
@@ -1,28 +1,28 @@
#ifndef __DASH_DISPLAY_UI
#define __DASH_DISPLAY_UI
#include "Arduino_GFX.h"
#include "UIString.h"
#include "UITable.h"
#include "UIBar.h"
#include "data.h"
struct DashUI {
UIBar *barTacho;
UIString *digiTacho;
UIString *digiSpeedo;
UIString *digiGear;
UIString *lapDelta;
UIString *bestLap;
UIString *lastLap;
UIString *curLap;
UIString *fuelTank;
UIString *fuelConsumption;
UIString *brakeBias;
UITable *relative;
DashUI();
void Update(DataPacket *p);
};
#endif
// #ifndef __DASH_DISPLAY_UI
// #define __DASH_DISPLAY_UI
//
// #include "Arduino_GFX.h"
// #include "UIString.h"
// #include "UITable.h"
// #include "UIBar.h"
// #include "data.h"
//
// struct DashUI {
// UIBar *barTacho;
// UIString *digiTacho;
// UIString *digiSpeedo;
// UIString *digiGear;
// UIString *lapDelta;
// UIString *bestLap;
// UIString *lastLap;
// UIString *curLap;
// UIString *fuelTank;
// UIString *fuelConsumption;
// UIString *brakeBias;
// UITable *relative;
//
// DashUI();
// void Update(DataPacket *p);
// };
//
// #endif