M5Stack Gauges for Boats



  • Hello,

    this is my first post in this forum. I am pretty new to this product and not very experienced in Arduino programing. So I need your help in order to finalize my project. I do need several gauges for my boat. So I decided to try the M5Stack which allows me to display 3 different gauges on one display by simply pressing one of the three buttons. As a start I used the example "TFT_Meter_linear". So far the functionality is given. However, I would like to rotate the scale and the pointer. The command "tft.setRotation()" doesn't do it. I would appreciate if someone could give me a hint on which values I have to manipulate in order to get the desired result. I have attached a picture of the current and the required display as well as the code

    Best regards
    0_1618302186104_Rudder.JPG image url)

     
     
     An example analogue meter using a ILI9341 TFT LCD screen
    
     Needs Font 2 (also Font 4 if using large scale label)
    
     Make sure all the display driver and pin comnenctions are correct by
     editting the User_Setup.h file in the TFT_eSPI library folder.
    
     #########################################################################
     ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ######
     #########################################################################
     
    Updated by Bodmer for variable meter size
     */
    
    // Define meter size as 1 for M5.Lcd.rotation(0) or 1.3333 for M5.Lcd.rotation(1)
    #define M_SIZE 1.3333
    
    #include <M5Stack.h>
    
    #define ADC_Calibration_Value1 250.0 // For resistor measure 5 Volt and 180 Ohm equals 100% plus 1K resistor.
    #define ADCpin1 35  // Potentiometer is connected to GPIO 35 (Analog ADC1_CH7) 
    // add a 1k Resistor from pin 35 to +5V and a Diode from GND to pin 35 (Cathode)
    // a 10µf cap from GND to pin 35 may reduce a noisy needle
    
    #define TFT_GREY 0x5AEB
    
    float ltx = 0;    // Saved x coord of bottom of needle
    uint16_t osx = M_SIZE*120, osy = M_SIZE*120; // Saved x & y coords
    uint32_t updateTime = 0;       // time for next update
    
    int old_analog =  -999; // Value last displayed
    
    int value[6] = {0, 0, 0, 0, 0, 0};
    int old_value[6] = { -1, -1, -1, -1, -1, -1};
    int d = 0;
    
    // variable for storing the potentiometer value
    int potValue = 0;
    
    void setup(void) {
      M5.begin();
      M5.Power.begin();
      // M5.Lcd.setRotation(1);
      // Serial.begin(57600); // For debug
      M5.Lcd.fillScreen(TFT_BLACK);
    
      analogMeter(); // Draw analogue meter
    
      updateTime = millis(); // Next update time
    
    }
    
    //*****************************************************************************
    // ReadADC is used to improve the linearity of the ESP32 ADC see: https://github.com/G6EJD/ESP32-ADC-Accuracy-Improvement-function
    double ReadADC(byte pin) {
      double reading = analogRead(pin); // Reference voltage is 3v3 so maximum reading is 3v3 = 4095 in range 0 to 4095
      if (reading < 1 || reading > 4095) return 0;
      // return -0.000000000009824 * pow(reading,3) + 0.000000016557283 * pow(reading,2) + 0.000854596860691 * reading + 0.065440348345433;
      return (-0.000000000000016 * pow(reading, 4) + 0.000000000118171 * pow(reading, 3) - 0.000000301211691 * pow(reading, 2) + 0.001109019271794 * reading + 0.034143524634089) * 1000;
    } 
    // Added an improved polynomial, use either, comment out as required
    
    void loop() {
    
      potValue = ReadADC(ADCpin1) * ADC_Calibration_Value1 / 4096;
    
     Serial.print(ReadADC(ADCpin1));                   // print raw value
     Serial.print(" ");                                // tab
     Serial.println(potValue);                         // print final value 
     
     delay(200); // to smoothen the pointer
    
      
      plotNeedle(potValue, 0);
    
    }
    
    // #########################################################################
    //  Draw the analogue meter on the screen
    // #########################################################################
    void analogMeter()
    {
    
      // Meter outline
      M5.Lcd.fillRect(0, 0, M_SIZE*239, M_SIZE*126, TFT_GREY);
      M5.Lcd.fillRect(5, 3, M_SIZE*230, M_SIZE*119, TFT_WHITE);
    
      M5.Lcd.setTextColor(TFT_BLACK);  // Text colour
    
      // Draw ticks every 5 degrees from -50 to +50 degrees (100 deg. FSD swing)
      for (int i = -50; i < 51; i += 5) {
        // Long scale tick length
        int tl = 15;
    
        // Coodinates of tick to draw
        float sx = cos((i - 90) * 0.0174532925);
        float sy = sin((i - 90) * 0.0174532925);
        uint16_t x0 = sx * (M_SIZE*100 + tl) + M_SIZE*120;
        uint16_t y0 = sy * (M_SIZE*100 + tl) + M_SIZE*140;
        uint16_t x1 = sx * M_SIZE*100 + M_SIZE*120;
        uint16_t y1 = sy * M_SIZE*100 + M_SIZE*140;
    
        // Coordinates of next tick for zone fill
        float sx2 = cos((i + 5 - 90) * 0.0174532925);
        float sy2 = sin((i + 5 - 90) * 0.0174532925);
        int x2 = sx2 * (M_SIZE*100 + tl) + M_SIZE*120;
        int y2 = sy2 * (M_SIZE*100 + tl) + M_SIZE*140;
        int x3 = sx2 * M_SIZE*100 + M_SIZE*120;
        int y3 = sy2 * M_SIZE*100 + M_SIZE*140;
    
        // Green zone limits
        if (i >= -50 && i < 0) {
          M5.Lcd.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREEN);
          M5.Lcd.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREEN);
        }
    
        // Red zone limits
        if (i >= 0 && i <50) {
          M5.Lcd.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_RED);
          M5.Lcd.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_RED);
        }
    
        // Short scale tick length
        if (i % 25 != 0) tl = 8;
    
        // Recalculate coords incase tick lenght changed
        x0 = sx * (M_SIZE*100 + tl) + M_SIZE*120;
        y0 = sy * (M_SIZE*100 + tl) + M_SIZE*140;
        x1 = sx * M_SIZE*100 + M_SIZE*120;
        y1 = sy * M_SIZE*100 + M_SIZE*140;
    
        // Draw tick
        M5.Lcd.drawLine(x0, y0, x1, y1, TFT_BLACK);
    
        // Check if labels should be drawn, with position tweaks
        if (i % 25 == 0) {
          // Calculate label positions
          x0 = sx * (M_SIZE*100 + tl + 10) + M_SIZE*120;
          y0 = sy * (M_SIZE*100 + tl + 10) + M_SIZE*140;
          switch (i / 25) {
            case -2: M5.Lcd.drawCentreString("40", x0, y0 - 12, 2); break;
            case -1: M5.Lcd.drawCentreString("20", x0, y0 - 9, 2); break;
            case 0: M5.Lcd.drawCentreString("CTR", x0, y0 - 7, 2); break;
            case 1: M5.Lcd.drawCentreString("20", x0, y0 - 9, 2); break;
            case 2: M5.Lcd.drawCentreString("40", x0, y0 - 12, 2); break;
          }
        }
    
        // Now draw the arc of the scale
        sx = cos((i + 5 - 90) * 0.0174532925);
        sy = sin((i + 5 - 90) * 0.0174532925);
        x0 = sx * M_SIZE*100 + M_SIZE*120;
        y0 = sy * M_SIZE*100 + M_SIZE*140;
        // Draw scale arc, don't draw the last part
        if (i < 50) M5.Lcd.drawLine(x0, y0, x1, y1, TFT_BLACK);
      }
    
      M5.Lcd.drawString("Port", M_SIZE*(5 + 230 - 40), M_SIZE*(119 - 20), 2); // Label at bottom right
      M5.Lcd.drawString("STBD", M_SIZE*(5 + 60 - 40), M_SIZE*(119 - 20), 2); // Label at bottom left
      M5.Lcd.drawCentreString("Rudder", M_SIZE*120, M_SIZE*70, 4); // Comment out to avoid font 4
      M5.Lcd.drawRect(5, 3, M_SIZE*230, M_SIZE*119, TFT_BLACK); // Draw bezel line
    
      plotNeedle(0, 0); // Put meter needle at 0
    }
    
    // #########################################################################
    // Update needle position
    // This function is blocking while needle moves, time depends on ms_delay
    // 10ms minimises needle flicker if text is drawn within needle sweep area
    // Smaller values OK if text not in sweep area, zero for instant movement but
    // does not look realistic... (note: 100 increments for full scale deflection)
    // #########################################################################
    void plotNeedle(int value, byte ms_delay)
    {
    
      if (value < -10) value = -10; // Limit value to emulate needle end stops
      if (value > 110) value = 110;
    
      // Move the needle until new value reached
      while (!(value == old_analog)) {
        if (old_analog < value) old_analog++;
        else old_analog--;
    
        if (ms_delay == 0) old_analog = value; // Update immediately if delay is 0
    
        float sdeg = map(old_analog, -10, 110, -150, -30); // Map value to angle
        // Calcualte tip of needle coords
        float sx = cos(sdeg * 0.0174532925);
        float sy = sin(sdeg * 0.0174532925);
    
        // Calculate x delta of needle start (does not start at pivot point)
        float tx = tan((sdeg + 90) * 0.0174532925);
    
        // Erase old needle image
        M5.Lcd.drawLine(M_SIZE*(120 + 20 * ltx - 1), M_SIZE*(140 - 20), osx - 1, osy, TFT_WHITE);
        M5.Lcd.drawLine(M_SIZE*(120 + 20 * ltx), M_SIZE*(140 - 20), osx, osy, TFT_WHITE);
        M5.Lcd.drawLine(M_SIZE*(120 + 20 * ltx + 1), M_SIZE*(140 - 20), osx + 1, osy, TFT_WHITE);
        M5.Lcd.drawLine(M_SIZE*(120 + 20 * ltx), M_SIZE*(140 - 20), osx, osy, TFT_WHITE);
        M5.Lcd.drawLine(M_SIZE*(120 + 20 * ltx + 1), M_SIZE*(140 - 20), osx + 1, osy, TFT_WHITE);
    
        // Re-plot text under needle
        M5.Lcd.setTextColor(TFT_BLACK);
        M5.Lcd.drawCentreString("Rudder", M_SIZE*120, M_SIZE*70, 4); // // Comment out to avoid font 4
    
        // Store new needle end coords for next erase
        ltx = tx;
        osx = M_SIZE*(sx * 98 + 120);
        osy = M_SIZE*(sy * 98 + 140);
    
        // Draw the needle in the new postion, magenta makes needle a bit bolder
        // draws 5 lines to thicken needle
        M5.Lcd.drawLine(M_SIZE*(120 + 20 * ltx - 1), M_SIZE*(140 - 20), osx - 1, osy, TFT_RED);
        M5.Lcd.drawLine(M_SIZE*(120 + 20 * ltx), M_SIZE*(140 - 20), osx, osy, TFT_MAGENTA);
        M5.Lcd.drawLine(M_SIZE*(120 + 20 * ltx + 1), M_SIZE*(140 - 20), osx + 1, osy, TFT_RED);
        M5.Lcd.drawLine(M_SIZE*(120 + 20 * ltx), M_SIZE*(140 - 20), osx, osy, TFT_MAGENTA);
        M5.Lcd.drawLine(M_SIZE*(120 + 20 * ltx + 1), M_SIZE*(140 - 20), osx + 1, osy, TFT_RED);
    
        // Slow needle down slightly as it approaches new postion
        if (abs(old_analog - value) < 10) ms_delay += ms_delay / 5;
    
        // Wait before next update
        delay(ms_delay);
      }
    }
    ``


  • Sorry can't help your question. But would like to thank you for your code. Which i have adapted to display temperatures.



  • Add the following to void loop ().

    void loop() {
    M5.update();
    if(M5.BtnA.wasPressed()){M5.Lcd.setRotation(3);M5.Lcd.fillScreen(TFT_BLACK);analogMeter();}
    if(M5.BtnC.wasPressed()){M5.Lcd.setRotation(1);M5.Lcd.fillScreen(TFT_BLACK);analogMeter();}