Here's the "proof of concept" code. You will probably have trouble using it to draw arbitrary angles as there will be a tenancy to leaving small segments that are not correctly redrawn. This is due to rounding errors and a directional asymmetry in the arc drawing algorithm.
I will update the code to make it more suitable for your application and email you a copy.
Edit: There is no need to draw the four "20"'s during refresh, so these can be plotted during setup only. This saves a few milliseconds and then with 2 degree angle changes the UNO can update the screen at an impressive 70 frames per second.
// Demo code for artifical horizon display
// Written for a 160 x 128 TFT display
#include <SPI.h>
// Use ONE of these three libraries, comment out other two!
// For S6D02A1 based TFT displays
//#include <TFT_S6D02A1.h> // Bodmer's graphics and font library for S6D02A1 driver chip
//TFT_S6D02A1 tft = TFT_S6D02A1(); // Invoke library, pins defined in User_Setup.h
// https://github.com/Bodmer/TFT_S6D02A1
// For ST7735 based TFT displays
#include <TFT_ST7735.h> // Bodmer's graphics and font library for ST7735 driver chip
TFT_ST7735 tft = TFT_ST7735(); // Invoke library, pins defined in User_Setup.h
// https://github.com/Bodmer/TFT_ST7735
// For ILI9341 based TFT displays (note sketch is currently setup for a 160 x 128 display)
//#include <TFT_ILI9341.h> // Bodmer's graphics and font library for ILI9341 driver chip
//TFT_ILI9341 tft = TFT_ILI9341(); // Invoke library, pins defined in User_Setup.h
// https://github.com/Bodmer/TFT_ILI9341
#define REDRAW_DELAY 17 // minimum delay in milliseconds between display updates
#define HOR 63 // Horizon circle outside radius
#define HIR 24 // Horizon circle inside radius
#define HAW HOR-HIR // Horizon arc width (outside - inside radius)
#define BROWN 0x5140 //0x5960
#define SKY_BLUE 0x02B5 //0x0318 //0x039B //0x34BF
#define DARK_RED 0x8000
#define DARK_GREY 0x39C7
#define XC 64 // x coord of centre of horizon
#define YC 80 // y coord of centre of horizon
#define ANGLE_INC 2 // Angle increment for arc segments
#define DEG2RAD 0.0174532925
int roll_angle = 0;
int roll_delta = 90;
int demo_delta = ANGLE_INC;
unsigned long redrawTime = 0;
// #########################################################################
// Setup, runs once on boot up
// #########################################################################
void setup(void) {
Serial.begin(115200);
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
// Centre graphics
// Draw the inner white arc (360 degrees = a circle)
fillArc(XC, YC, 0, 360 / ANGLE_INC, HIR, HIR, 2, DARK_GREY);
// Draw the red cross-hairs
tft.fillRect(XC - 21, YC - 1, 42, 3, DARK_RED);
tft.fillRect(XC - 1, YC - 21, 3, 42, DARK_RED);
// Draw the two 180 degree arc segments for sky and ground
fillArc(XC, YC, -90, 180 / ANGLE_INC, HOR, HOR, HAW, SKY_BLUE);
fillArc(XC, YC, 90, 180 / ANGLE_INC, HOR, HOR, HAW, BROWN);
// Draw fixed text
tft.setTextColor(TFT_GREEN);
tft.setCursor(10, 0);
tft.print("SPD LNAV WNAV PTH");
tft.setCursor(10, 150);
tft.print("Lior Z. AHI Display");
}
// #########################################################################
// Main loop, keeps looping around
// #########################################################################
void loop() {
// Refresh the display at regular intervals
if (millis() > redrawTime) {
redrawTime = millis() + REDRAW_DELAY;
unsigned long drawTime = millis(); // Test only
// Change to ramp down the horizon angle for the demo and swap the colours for the direction change
if (roll_angle > (360 - ANGLE_INC)) {
demo_delta = -ANGLE_INC;
roll_delta = -90; // roll_delta is -90 for anticlockwise rotation
//delay(500);
}
// Change to ramp up the horizon angle for the demo and swap the colours for the direction change
if (roll_angle < ANGLE_INC) {
demo_delta = ANGLE_INC;
roll_delta = 90; // roll_delta is +90 for clockwise rotation
//delay(500);
}
// Draw the new parts of the two arc segments 180 degrees apart
// If roll_angle is zero = horizontal
fillArc(XC, YC, roll_angle + roll_delta, 1, HOR, HOR, HAW, SKY_BLUE);
fillArc(XC, YC, roll_angle - roll_delta, 1, HOR, HOR, HAW, BROWN);
drawInfo();
// This is for the demo to produce an angle that changes
roll_angle += demo_delta;
//if (roll_angle == 90) delay(1000);
if (millis() - drawTime > REDRAW_DELAY) Serial.println(millis() - drawTime); // Test only to see how long an update takes
//delay(500);
}
}
// #########################################################################
// Draw a circular or elliptical arc with a defined thickness
// #########################################################################
// x,y == coords of centre of arc
// start_angle = 0 - 359
// seg_count = number of 6 degree segments to draw (60 => 360 degree arc)
// rx = x axis outer radius
// ry = y axis outer radius
// w = width (thickness) of arc in pixels
// colour = 16 bit colour value
// Note if rx and ry are the same then an arc of a circle is drawn
int fillArc(int x, int y, int start_angle, int seg_count, int rx, int ry, int w, unsigned int colour)
{
// Fudge factor adjustment for this sketch (so horizon is horizontal when start anngle is 0)
start_angle--;
byte seg = ANGLE_INC; // Segments are INC degrees wide
byte inc = ANGLE_INC; // Draw segments every INC degrees, increase for segmented ring
// Calculate first pair of coordinates for segment start
float sx = cos((start_angle - 90) * DEG2RAD);
float sy = sin((start_angle - 90) * DEG2RAD);
uint16_t x0 = sx * (rx - w) + x;
uint16_t y0 = sy * (ry - w) + y;
uint16_t x1 = sx * rx + x;
uint16_t y1 = sy * ry + y;
// Draw colour trapezoids every inc degrees
for (int i = start_angle; i < start_angle + seg * seg_count; i += inc) {
// Calculate pair of coordinates for segment end
float sx2 = cos((i + seg - 90) * DEG2RAD);
float sy2 = sin((i + seg - 90) * DEG2RAD);
int x2 = sx2 * (rx - w) + x;
int y2 = sy2 * (ry - w) + y;
int x3 = sx2 * rx + x;
int y3 = sy2 * ry + y;
tft.fillTriangle(x0, y0, x1, y1, x2, y2, colour);
tft.fillTriangle(x1, y1, x2, y2, x3, y3, colour);
// Copy segment end to sgement start for next segment
x0 = x2;
y0 = y2;
x1 = x3;
y1 = y3;
}
}
// #########################################################################
// Draw the information
// #########################################################################
void drawInfo(void)
{
// side vertical lines
tft.fillRect( 15, 30, 5, 100, DARK_GREY);
tft.fillRect(108, 30, 5, 100, DARK_GREY);
tft.setTextColor(TFT_WHITE);
tft.setCursor(2, 30);
tft.print("20");
tft.setCursor(2, 122);
tft.print("20");
tft.setCursor(2, 50);
tft.print("10");
tft.setCursor(2, 102);
tft.print("10");
tft.setCursor(115, 30);
tft.print("20");
tft.setCursor(115, 122);
tft.print("20");
tft.setCursor(115, 50);
tft.print("10");
tft.setCursor(115, 102);
tft.print("10");
}