Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 150 additions & 99 deletions BoatGauges.ino
Original file line number Diff line number Diff line change
@@ -1,132 +1,183 @@
#include <TFT_eSPI.h>
#include <TFT_eSPI.h>
#include "gauge1.h"
#include "gauge2.h"
#include "gauge3.h"
#include "gauge4.h"
#include "gauge5.h"
#include "gauge6.h"
#include "font.h"
TFT_eSPI tft = TFT_eSPI();
TFT_eSprite img = TFT_eSprite(&tft);
TFT_eSprite ln = TFT_eSprite(&tft);

double rad=0.01745;
int angle;
TFT_eSPI tft = TFT_eSPI(); // invoke tft library
TFT_eSprite img = TFT_eSprite(&tft); // sprite for gauge
//TFT_eSprite ln = TFT_eSprite(&tft); // unused sprite

int sx=120;
int sy=120;
int r=76;
double rad = 0.01745; // constant: number of radian per degree
int angle; // mapped potentiometer value to corresponding angle on a circle

// needle indicator circle constants
int sx = 120; // x coordinate
int sy = 120; // y coordinate
int r = 76; // radius

// array for storing bounding box coordinates for every degree alog the circle
float x[360];
float y[360];
float x2[360];
float y2[360];

// pwm variables which i think supposed control screen brigness
const int pwmFreq = 5000;
const int pwmResolution = 8;
const int pwmLedChannelTFT = 0;

int chosenOne=0;
int minValue[6]={0,20,0,0,0,80};
int maxValue[6]={40,100,60,80,70,160};
int dbounce=0;
// gauge params
int chosenOne = 0; // selected gauge index
int minValue[6] = { 0, 20, 0, 0, 0, 80 }; // min values for different gauges
int maxValue[6] = { 40, 100, 60, 80, 70, 160 }; // max values for different gauges

void setup() {
int dbounce = 0; // simple button debounce variable to prevent skipping multiple gauges as a result of button press

ledcSetup(pwmLedChannelTFT, pwmFreq, pwmResolution);
ledcAttachPin(5, pwmLedChannelTFT);
ledcWrite(pwmLedChannelTFT, 90);

pinMode(12,INPUT_PULLUP);

tft.init();
tft.setRotation(0);
tft.setSwapBytes(true);
img.setSwapBytes(true);
tft.fillScreen(TFT_ORANGE);
img.createSprite(240, 240);


tft.setPivot(60,60);
img.setTextDatum(4);
img.setTextColor(TFT_BLACK,0xAD55);
void setup() {
// i think this supposed to control screen brightness via pwm but it doesn't seem to have any effect
ledcSetup(pwmLedChannelTFT, pwmFreq, pwmResolution);
ledcAttachPin(5, pwmLedChannelTFT);
ledcWrite(pwmLedChannelTFT, 90);

pinMode(12, INPUT_PULLUP); // button

// initialise tft library
tft.init();
tft.setRotation(0); // set the display image orientation to 0, 1, 2 or 3
tft.setSwapBytes(true); // swap the byte order for pushImage() and pushPixels() - corrects endianness
img.setSwapBytes(true); // required to load background sprite

// set background color, image
tft.fillScreen(TFT_ORANGE); // required to load background sprite
img.createSprite(240, 240); // create sprite in RAM

// set gauge text position, size, color, font
tft.setPivot(60, 60);
img.setTextDatum(4); // middle centre text allignment
img.setTextColor(TFT_BLACK, 0xAD55);
img.setFreeFont(&Orbitron_Medium_28);

int i=0;
int a=136;

while(a!=44)
{
x[i]=r*cos(rad*a)+sx;
y[i]=r*sin(rad*a)+sy;
x2[i]=(r-20)*cos(rad*a)+sx;
y2[i]=(r-20)*sin(rad*a)+sy;
i++;
a++;
if(a==360)
a=0;
// while loop params
int i = 0; // loop counter
int a = 136; // starting angle in degrees

// while loop sweeps an arc along the circle for every degree to calculate
// positions for bounding boxes that will be used to position red triangle indicator
// sweep starts from 136 degrees and moves to 360 degrees
// then resets (as circle only has 360 degrees) to 0 degrees and continues to 44 degrees
// for a total of 268 degree arc
while (a != 44) {
x[i] = r * cos(rad * a) + sx;
y[i] = r * sin(rad * a) + sy;
x2[i] = (r - 20) * cos(rad * a) + sx;
y2[i] = (r - 20) * sin(rad * a) + sy;

i++;
a++;

// reset "a" on overflow
if (a == 360) {
a = 0;
}
}
}

// debug
//min angle 136 or 137
//max angle 43

int a1,a2;
int result=0;
int a1, a2; // min/max angles for needle indicator bounding box
int result = 0; // mapped potentiometer value to gauge min/max range

void loop() {
// check button state with debounce logic
if (digitalRead(12) == 0) {
if (dbounce == 0) {
dbounce = 1; // set to prevent skipping gauges if button is held
chosenOne++; // select next gauge

// reset gauge index on overflow
if (chosenOne >= 6) {
chosenOne = 0;
}
}
} else {
dbounce = 0;
}

// read potentiometer value and map it for selected gauge min/max range
result = map(analogRead(14), 0, 4095, minValue[chosenOne], maxValue[chosenOne]);

// map obtained result to corresponding angle on a circle
angle = map(result, minValue[chosenOne], maxValue[chosenOne], 0, 267);

// push currently selected gauge image to "img" sprite
if (chosenOne == 0) {
img.pushImage(0, 0, 240, 240, gauge1);
}

if (chosenOne == 1) {
img.pushImage(0, 0, 240, 240, gauge2);
}

if (chosenOne == 2) {
img.pushImage(0, 0, 240, 240, gauge3);
}

if (chosenOne == 3) {
img.pushImage(0, 0, 240, 240, gauge4);
}

if (chosenOne == 4) {
img.pushImage(0, 0, 240, 240, gauge5);
}

if (chosenOne == 5) {
img.pushImage(0, 0, 240, 240, gauge6);
}

// push measured value to "img" sprite
if (chosenOne == 5) {
// battery voltage with 2 decimal places
img.drawFloat(result / 10.00, 2, 120, 114);
} else if (chosenOne == 4) {
// tachometer (with x100 multiplier)
img.drawString(String(result * 100), 120, 114);
} else {
img.drawString(String(result), 120, 114);
}

// debug: push raw potentiometer ADC value to "img" sprite
//img.drawString(String(analogRead(22)), 30, 10, 2);

// set min & max bounding box angles
a1 = angle - 4;
a2 = angle + 4;

// check angle limits (end stops)
if (a1 < 0) {
a1 = angle - 4 + 359; // rotate angle clockwise
}

if (a2 >= 359) {
a2 = angle + 4 - 359; // rotate angle counter clockwise
}

// draw red triangle corresponding to measure value on "img" sprite
if (result <= minValue[chosenOne] + 4) {
// draw triangle on the left end stop
img.fillTriangle(x[angle], y[angle], x2[angle], y2[angle], x2[a2 + 2], y2[a2 + 2], TFT_RED);
} else if (result >= maxValue[chosenOne] - 4) {
// draw triangle on the right end stop
img.fillTriangle(x[angle], y[angle], x2[a1 - 2], y2[a1 - 2], x2[angle], y2[angle], TFT_RED);
} else {
img.fillTriangle(x[angle], y[angle], x2[a1], y2[a1], x2[a2], y2[a2], TFT_RED);
}

if(digitalRead(12)==0)
{
if(dbounce==0)
{
dbounce=1;
chosenOne++;
if(chosenOne>=6)
chosenOne=0;
}
}else dbounce=0;
result=map(analogRead(14),0,4095,minValue[chosenOne],maxValue[chosenOne]);
angle=map(result,minValue[chosenOne],maxValue[chosenOne],0,267);



if(chosenOne==0)
img.pushImage(0,0,240,240,gauge1);
if(chosenOne==1)
img.pushImage(0,0,240,240,gauge2);
if(chosenOne==2)
img.pushImage(0,0,240,240,gauge3);
if(chosenOne==3)
img.pushImage(0,0,240,240,gauge4);
if(chosenOne==4)
img.pushImage(0,0,240,240,gauge5);
if(chosenOne==5)
img.pushImage(0,0,240,240,gauge6);

if(chosenOne==5)
img.drawFloat(result/10.00,2,120,114);
else if(chosenOne==4)
img.drawString(String(result*100),120,114);
else
img.drawString(String(result),120,114);
//img.drawString(String(analogRead(22)), 30,10,2);

a1=angle-4;
a2=angle+4;

if(a1<0)
a1=angle-4+359;
if(a2>=359)
a2=angle+4-359;

if(result<=minValue[chosenOne]+4)
img.fillTriangle(x[angle],y[angle],x2[angle],y2[angle],x2[a2+2],y2[a2+2],TFT_RED);
else if(result>=maxValue[chosenOne]-4)
img.fillTriangle(x[angle],y[angle],x2[a1-2],y2[a1-2],x2[angle],y2[angle],TFT_RED);
else
img.fillTriangle(x[angle],y[angle],x2[a1],y2[a1],x2[a2],y2[a2],TFT_RED);


img.pushSprite(0, 0);
// push resulting gauge sprite (gauge image + measure value + red triangle needle) to display
img.pushSprite(0, 0);
}
64 changes: 64 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# BoatGauges
Example sketch for six different boat gauges namely:
1. Coolant pressure (psi)
2. Coolant temp (degree C)
3. Oil pressure (psi)
4. Speedo (km/h)
5. Tacho (rpm)
6. Battery voltage


For live demonstration visit [https://www.youtube.com/watch?v=y_H7HM0oyoo]

# Arduino IDE Setup
1. Install ESP32 boards library [guide from randomnerdtutorials](https://randomnerdtutorials.com/installing-the-esp32-board-in-arduino-ide-windows-instructions/)
2. Install TFT_eSPI library by Bodmer through Library Manager (Sketch > Include Library > Manage Libraries or Ctrl+Shift+l)

![image](https://user-images.githubusercontent.com/6739564/173196508-e732ded0-ca3b-419f-80a7-bce4eaf675de.png)


# Configure TFT_eSPI library
Open ``..\Documents\Arduino\libraries\TFT_eSPI\User_Setup.h`` and comment out line 44:
```
//#define ILI9341_DRIVER
```
On line 64 uncomment GC9A01 driver:
```
#define GC9A01_DRIVER
```

Open ``..\Documents\Arduino\libraries\TFT_eSPI\User_Setup_Select.h`` and comment out line 24:
```
//#include <User_Setups/Setup1_ILI9341.h>
```
On line 105 uncomment GC9A01 setup:
```
#include <User_Setups/Setup200_GC9A01.h>
```

Open ``..\Documents\Arduino\libraries\TFT_eSPI\User_Setups\Setup200_GC9A01.h`` and update pin assignments:
```
#define TFT_MISO 5
#define TFT_MOSI 2
#define TFT_SCLK 15 // Clock pin
#define TFT_CS 17 // Chip select control pin
#define TFT_DC 16 // Data Command control pin
#define TFT_RST 4 // Reset pin (could connect to Arduino RESET pin)
```

# ESP32 & GC9A01 Connections

![image](https://user-images.githubusercontent.com/6739564/172683255-a640ba47-1d2f-4fc0-b8f3-acbada262f81.png)
> Image is courtesy of WOKWI.com and some MS Paint work

```
ESP32 GC9A01
3V3 VCC
GND GND
D15 SCL
D2 SDA
D4 RES
RX2 DC
TX2 CS
D5 BLK
```