// GCA901_Nano_voltage_meter
// 
// grid voltage variation monitor (230V - 250V AC)
// rolling averaged voltage (of 10 readings) is sent to display
// NOTE: here voltage generated with random function
//
// microcontroller: Arduino Nano
// display 240*240 circular SPI 3.3V TFT with GC9A01 controller
// 
// note: random function drives fluctuations of the parameter named 'volt'
// CG9A01    Arduino Nano
//  RST -------- NC
//  CST -------- 10
//  DC ---------  9
//  SDA -------- 11 - green wire
//  SCL -------- 13 - yellow wire
//  
// Floris Wouterlood
// September 1, 2023
// public domain

// made for a 240*240 pixel circular display
// all x-y-coordinates relative to center = x = 120 and y = 120
     
   #include "SPI.h"
   #include "Adafruit_GFX.h"
   #include "Adafruit_GC9A01A.h"

   #define TFT_DC 2
   #define TFT_CS 15
 
   Adafruit_GC9A01A tft (TFT_CS, TFT_DC);
 
   #define BLACK      0x0000                                                               // some extra colors
   #define BLUE       0x001F
   #define RED        0xF800
   #define GREEN      0x07E0
   #define CYAN       0x07FF
   #define MAGENTA    0xF81F
   #define YELLOW     0xFFE0
   #define WHITE      0xFFFF
   #define ORANGE     0xFBE0
   #define GREY       0x84B5
   #define BORDEAUX   0xA000
   #define AFRICA     0xAB21                // 0xce5f  //0xAB21                                                               // current dial color
    const int vlez = 34;
   #define DEG2RAD 0.0174532925 
   //int vlez;
   int sig = 0;
   int multiplier;
   int    frametime = 100; 
   int    x_pos;
   int    y_pos; 
   int    center_x = 120;                                                                  // center x of dial on 240*240 TFT display
   int    center_y = 120;                                                                  // center y of dial on 240*240 TFT display
   float  pivot_x, pivot_y,pivot_x_old, pivot_y_old;
   float  p1_x,p1_y,p2_x,p2_y,p3_x, p3_y, p4_x, p4_y, p5_x, p5_y; 
   float  p1_x_old,p1_y_old, p2_x_old, p2_y_old, p3_x_old, p3_y_old;
   float  p4_x_old, p4_y_old, p5_x_old, p5_y_old;
   float  angleOffset = 3.14;
   float  arc_x;
   float  arc_y;
   int    radius = 120;                                                                    // center y of circular scale                                                   
   float  angle_circle = 0;
   float  needleAngle = 0;
   int    iteration = 0;
   int    j;                                                            
   float  volt = 220;
   int    needle_multiplier = 1;
   float  needle_setter;             
 float currentNeedleValue = 230;  // Start with a base voltage or level
float needleSpeed = 10;         // Speed at which the needle returns to the left
   
                                                     // to start with
   
void setup() {

   //randomSeed (analogRead(0)); 
 pinMode(12, OUTPUT);
 
    
   pinMode(vlez, INPUT);
   tft.begin();    
   Serial.begin (9600); 
   Serial.println (""); 
   Serial.println (""); 
   tft.setRotation (0);  
   
   tft.fillScreen (BLACK);
   tft.drawCircle (center_x, center_y,120, BLACK);             
   pivot_x = center_x;
   pivot_y = center_y+50;

   p1_x_old = center_x; p1_y_old = center_y+50;
   p2_x_old = center_x; p2_y_old = center_y+50;
   p3_x_old = center_x; p3_y_old = center_y+50;
   p4_x_old = center_x; p4_y_old = center_y+50;
   p5_x_old = center_x; p5_y_old = center_y+30;
                                                                          
   create_dial ();
   needle_setter = volt;
   needleAngle = (((needle_setter)*DEG2RAD*1.8)-3.14);
   needle();  
   draw_pivot ();
}


void loop (){

  // Map the analog input (voltage) to the needle range
  float targetNeedleValue = map(analogRead(vlez), 0, 400, 230, 270);
  Serial.println(targetNeedleValue);
  sig = analogRead(vlez);
  if (sig > 280)  {digitalWrite(12, HIGH);} else {digitalWrite(12, LOW);}

  
  // If the target value is greater than the current needle position, move quickly
  if (targetNeedleValue > currentNeedleValue) {
    currentNeedleValue = targetNeedleValue;
  } 
  // If the target value is lower, move more slowly to simulate damping
  else if (targetNeedleValue < currentNeedleValue) {
    currentNeedleValue -= needleSpeed; // Decrease the value gradually
    if (currentNeedleValue < targetNeedleValue) {
      currentNeedleValue = targetNeedleValue;  // Ensure we don't overshoot
    }
  }

  // Update the needle position
  needle_setter = currentNeedleValue;
  needle();
  draw_pivot(); 

  delay(frametime);  // Control the update rate
} 


void needle (){                                                                            // dynamic needle management

   tft.drawLine (pivot_x, pivot_y, p1_x_old, p1_y_old, AFRICA);                            // remove old needle  
   tft.fillTriangle (p1_x_old, p1_y_old, p2_x_old, p2_y_old, p3_x_old, p3_y_old, AFRICA);  // remove old arrow head
   tft.fillTriangle (pivot_x, pivot_y, p4_x_old, p4_y_old, p5_x_old, p5_y_old, AFRICA);    // remove old arrow head
    
   needleAngle = (((needle_setter)*0.01745331*1.8)-3.14);
   p1_x = (pivot_x + ((radius)*cos(needleAngle)));                                         // needle tip
   p1_y = (pivot_y + ((radius)*sin(needleAngle))); 

   p2_x = (pivot_x + ((radius-15)*cos(needleAngle-0.05)));                                 // needle triange left
   p2_y = (pivot_y + ((radius-15)*sin(needleAngle-0.05))); 

   p3_x = (pivot_x + ((radius-15)*cos(needleAngle+0.05)));                                 // needle triange right
   p3_y = (pivot_y + ((radius-15)*sin(needleAngle+0.05))); 

   p4_x = (pivot_x + ((radius-90)*cos(angleOffset+(needleAngle-0.2))));                    // needle triange left
   p4_y = (pivot_y + ((radius-90)*sin(angleOffset+(needleAngle-0.2)))); 

   p5_x = (pivot_x + ((radius-90)*cos(angleOffset+(needleAngle+0.2))));                    // needle triange right
   p5_y = (pivot_y + ((radius-90)*sin(angleOffset+(needleAngle+0.2)))); 
  
   p1_x_old = p1_x; p1_y_old = p1_y;                                                       // remember previous needle position
   p2_x_old = p2_x; p2_y_old = p2_y;                                                                         
   p3_x_old = p3_x; p3_y_old = p3_y;                                                                      

   p4_x_old = p4_x; p4_y_old = p4_y;                                                       // remember previous needle counterweight position
   p5_x_old = p5_x; p5_y_old = p5_y;                                                                      

   tft.drawLine (pivot_x, pivot_y, p1_x, p1_y, BLACK);                                     // create needle 
   tft.fillTriangle (p1_x, p1_y, p2_x, p2_y, p3_x, p3_y, BLACK);                           // create needle tip pointer
  // tft.drawLine (center_x-80, center_y+70, center_x+80,center_y+70, BLACK);                // repair floor 
   tft.fillTriangle (pivot_x, pivot_y, p4_x, p4_y, p5_x, p5_y, BLACK);                     // create needle counterweight
}


void create_dial (){

   tft.fillCircle (center_x, center_y,120, AFRICA);                                        // general dial field
   tft.drawCircle (center_x, center_y,118,GREY);  
   tft.drawCircle (center_x, center_y,117,BLACK);
   tft.drawCircle (center_x, center_y,116,BLACK);  
   tft.drawCircle (center_x, center_y,115,GREY);

   for (j= 30; j<60    ; j+=5)
       {
        needleAngle = ((j*DEG2RAD*1.8)-3.14);
        arc_x = (pivot_x + ((radius+15)*cos(needleAngle)));                                // needle tip
        arc_y = (pivot_y + ((radius+15)*sin(needleAngle))); 
        tft.drawPixel  (arc_x,arc_y, BLACK);
        tft.fillCircle (arc_x,arc_y,2, BLACK);
        }

   for (j= 60; j<75    ; j+=5)
       {
        needleAngle = ((j*DEG2RAD*1.8)-3.14);
        arc_x = (pivot_x + ((radius+15)*cos(needleAngle)));                                // needle tip
        arc_y = (pivot_y + ((radius+15)*sin(needleAngle))); 
        tft.drawPixel  (arc_x,arc_y, RED);
        tft.fillCircle (arc_x,arc_y,2, RED);
        }        

   tft.setTextColor (BLACK,AFRICA);    
   tft.setTextSize (4);
   tft.setCursor (center_x+40, center_y+40);
   tft.print ("VU");  
      tft.setTextSize (4);
   tft.setCursor (center_x-60, center_y+40);
  tft.print ("R");   
                                                                                                                                                                           
  // tft.drawLine (center_x-80, center_y+70, center_x+80,center_y+70, WHITE);                // create floor   
}


void draw_pivot (){
 
   tft.fillCircle (pivot_x, pivot_y,8,RED);               
   tft.drawCircle (pivot_x, pivot_y,8,BLACK);            
   tft.drawCircle (pivot_x, pivot_y,3,BLACK);      
}
