{"id":15679541,"url":"https://github.com/tobozo/lgfxmeter","last_synced_at":"2025-05-07T10:03:46.349Z","repository":{"id":40244502,"uuid":"487980555","full_name":"tobozo/LGFXMeter","owner":"tobozo","description":"LGFX based Gauge Decoration and Animation library 🧭","archived":false,"fork":false,"pushed_at":"2022-05-18T14:30:04.000Z","size":198,"stargazers_count":18,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-07T10:01:55.321Z","etag":null,"topics":["arduino","esp32","gauge","icsmeter","lovyangfx","m5gfx","m5unified"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tobozo.png","metadata":{"files":{"readme":"ReadMe.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-05-02T20:24:29.000Z","updated_at":"2025-03-26T08:48:56.000Z","dependencies_parsed_at":"2022-09-04T00:41:26.087Z","dependency_job_id":null,"html_url":"https://github.com/tobozo/LGFXMeter","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobozo%2FLGFXMeter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobozo%2FLGFXMeter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobozo%2FLGFXMeter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobozo%2FLGFXMeter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tobozo","download_url":"https://codeload.github.com/tobozo/LGFXMeter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252856550,"owners_count":21814858,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","esp32","gauge","icsmeter","lovyangfx","m5gfx","m5unified"],"created_at":"2024-10-03T16:32:56.357Z","updated_at":"2025-05-07T10:03:46.315Z","avatar_url":"https://github.com/tobozo.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LGFXMeter library\n\n\n![image](https://user-images.githubusercontent.com/1893754/166322961-1f34e81a-ebb4-4c7b-8c05-4857d09a3456.png)\n\n\n\n[![PlatformIO Registry](https://badges.registry.platformio.org/packages/tobozo/library/LGFXMeter.svg)](https://registry.platformio.org/packages/libraries/tobozo/LGFXMeter)\n\nThis library was inspired by [ICSMeter](https://github.com/armel/ICSMeter).\n\n\n## Library requirements:\n\n  - M5Unified\n  - M5GFX\n\n\n## Usage\n\n\n\n\n### Creating a custom gauge.\n\n\n\n```C\n\n  #include \u003cLGFXMeter.h\u003e\n\n\n  {\n    // First describe the gauge.\n\n    // Units and Labels.\n    const ruler_unit_t My_Units[] = {\n    /*{ idx, angle,   label, size, distance,        fontFace, fontSize, textDatum }*/\n      {   0,  0.0f, nullptr,    0,        0,         nullptr,     0.0f,  MC_DATUM },\n      {   1,  2.0f,     \"0\",   -8,      -11,  \u0026FreeSans9pt7b,     0.75f, MC_DATUM },\n      {   2, 45.0f,    \"50\",   -8,      -11,  \u0026FreeSans9pt7b,     0.75f, MC_DATUM },\n      {   3, 90.0f,  \"100%\",   -8,      -11,  \u0026FreeSans9pt7b,     0.75f, MC_DATUM },\n    };\n\n    // Units and Labels\n    const ruler_unit_t My_Other_Units[] = {\n    /*{ idx, angle,   label, size, distance,        fontFace, fontSize, textDatum }*/\n      {   0,  0.0f, nullptr,    0,        0,         nullptr,     0.0f,  MC_DATUM },\n      {   1,  2.0f,   \"+10\",    8,       11,  \u0026FreeSans9pt7b,     0.75f, MC_DATUM },\n      {   2, 45.0f,   \"+50\",    8,       11,  \u0026FreeSans9pt7b,     0.75f, MC_DATUM },\n      {   3, 90.0f, \"+100%\",    8,       11,  \u0026FreeSans9pt7b,     0.75f, MC_DATUM },\n    };\n\n    // Attach units to ruler\n    const ruler_t My_Ruler       = {  0.0f,  90.0f, 150, 1, My_Units,       sizeof(My_Units)/sizeof(ruler_unit_t) };\n\n    // Attach units to ruler\n    const ruler_t My_Other_Ruler = { 45.0f,  90.0f, 160, 1, My_Other_Units, sizeof(My_Other_Units)/sizeof(ruler_unit_t) };\n\n    // group all rulers\n    const ruler_item_t items[] =\n    {\n    /*{ ruler_t,          palette color index }*/\n      { \u0026My_Ruler,        1                   },\n      { \u0026My_Other_Ruler,  2                   }\n    };\n\n    // define a color palette\n    const gauge_palette_t palette =\n    {\n      0xffffffU, /*.transparent_color  */\n      0x222222U, /*.fill_color         */\n      0xff4444U, /*.warn_color         */\n      0x00ff00U, /*.ok_color           */\n      0xff2222U, /*.arrow_color        */\n      0xaaaaaaU, /*.arrow_border_color */\n      0x888888U, /*.arrow_shadow_color */\n    };\n\n    //\n    const gauge_t MY_CUSTOM_GAUGE =\n    {\n      .items       = items,\n      .items_count = sizeof(items)/sizeof(ruler_item_t),\n      .palette     = palette,\n      .start       = -45.0f,\n      .end         =  45.0f\n    };\n\n  }\n\n```\n\n\n\n\n### Integrating the gauge\n\n```C++\n\n#include \u003cM5Unified.h\u003e\n#include \u003cLGFXMeter.h\u003e\n\n\nvoid setup()\n{\n  M5.begin();\n\n  auto cfg = LGFXMeter::config( MY_CUSTOM_GAUGE );\n\n  // attach the gauge to TFT\n  cfg.display = \u0026M5.Lcd;\n\n  // The gauge will create its own canvas unless cfg.dstCanvas is provided.\n  // Use case: background is variable, generated from gradients or vector drawings.\n  // /!\\ Don't use this feature if your device has no PSRam as it comes with a\n  // memory consumption overhead that may get the mask canvas creation to fail.\n  // cfg.dstCanvas = myGaugeSprite;\n\n  // set gauge geometry\n  cfg.clipRect  = {\n    .x         = GaugePosX,\n    .y         = GaugePosY,\n    .w         = GaugeWidth,\n    .h         = GaugeHeight\n  };\n\n  // - Mask zoom level rendering setting affects mask size and antialias\n  // - The antialias trick is to draw gauge rulers and labels in an upscaled, low bit depth, canvas, and\n  //   then downscale it into the final gauge canvas\n  // - The zoom level value is expressed as a fraction [0....1] and is applied to the mask canvas.\n  // - Although the mask is discarded after initial rendering, it will need some available ram.\n  //   A lower cfg.zoomAA will need more ram. Set it to 1.0 to disable Antialias and consume minimal ram.\n  //\n  // e.g.\n  //   - 320*160 gauge with cfg.zoomAA=0.5 will use a 640*320 mask with antialias\n  //   - 320*160 gauge with cfg.zoomAA=1.0 will use a 320*160 mask with NO antialias\n  //\n  // cfg.zoomAA = psramInit() ? 0.5 : 1.0;\n\n  // fill screen with a color from the gauge palette\n  M5.Lcd.fillScreen( cfg.palette-\u003etransparent_color );\n\n  ICSGauge = new Gauge_Class( cfg );\n  ICSGauge-\u003epushGauge(); // render empty gauge (no needle yet)\n}\n\n\nvoid loop()\n{\n\n  // measure some value\n  int mySensorValue = analogRead( mySensorPinNumber );\n\n  // map() it to the gauge angular range [0...90]\n  float my_angle = utils::mapFloat( mySensorValue, 0, 4095, 0.0, 90.0 );\n\n  // Either animate (300ms blocking) ...\n  ICSGauge-\u003eanimateNeedle( my_angle );\n\n  // .. or use eased drawing (300ms non blocking) ...\n  ICSGauge-\u003esetNeedle( my_angle );\n  ICSGauge-\u003eeaseNeedle( 300 );\n  // ICSGauge-\u003eeaseNeedle( 300, easing::easeOutBounce );\n  // /!\\ See lgfxmeter_types.hpp for complete list of available easing function\n  // Function names\n\n  // .. or just render the needle without easing or animation\n  ICSGauge-\u003edrawNeedle( my_angle );\n\n}\n\n\n\n```\n\n\n### Custom needle and background\n\nBackground, needle and needle shadow images can be any of the following formats:\n\n  - PNG: `IMAGE_PNG`\n  - QOI: `IMAGE_QOI`\n  - JPG: `IMAGE_JPG`\n  - BMP: `IMAGE_BMP`\n  - RAW: `IMAGE_RAW` (e.g. Sprite, untested)\n\n\nAs seen in the examples, image data can be stored in byte arrays.\n\n```C++\n\n  const image_t bgImg         = { 16, bg_png,                 bg_png_len,                 IMAGE_PNG, 320, 240 };\n  const image_t vuMeterArrow  = { 16, clock_arrow_png,        clock_arrow_png_len,        IMAGE_PNG, 16, 144 };\n  const image_t vuMeterShadow = { 16, clock_arrow_shadow_png, clock_arrow_shadow_png_len, IMAGE_PNG, 16, 144 };\n\n```\n\nCustom background, needle and shadow can be setup as follows:\n\n\n```C++\n  // Optionally use pre-rendered arrow and custom shadow\n  // - Image can be any size (scale will constrained) but smaller is faster, transparent png works\n  // - Pointy end of the arrow goes on the top\n  // - Shadow image must have same dimensions as arrow image\n  cfg.needleCfg.needleImg = \u0026vuMeterArrow;\n  cfg.needleCfg.shadowImg = \u0026vuMeterShadow;\n  //cfg.needleCfg.scaleX = 0.5; // scaling down a stretched image to produce nicer antialiased result\n  //cfg.needle.axis = { GaugeWidth/2, GaugePosY + GaugeHeight }; // will be automatically positioned with a clunky calculation otherwise\n\n\n  // Optionally share a background image between TFT and the gauge sprite\n  // - Image must be png/jpg/bmp/qoi or byte array\n  // - Image dimensions must be tft.width() * tft.height()\n  cfg.bgImage   = \u0026bgImg;\n\n  // draw the gauge background shared image\n  M5.Lcd.drawPng( bgImg.data, bgImg.len );\n\n```\n\nBackground can eventually be changed after gauge creation, but it will remove any previously drawn rulers.\nHowever if the gauge is built without rulers and uses a simple background image, then custom modes (e.g. dark/light) are possible.\n\n```C++\n\n  // firt create your image entity\n  const image_t alternateBgImage = { 16, my_image_data, my_image_data_len, IMAGE_PNG, GaugeWidth, GaugeWidth };\n\n  // overwrite the gauge background (will also remove the rulers !)\n  utils::drawImage( ICSGauge-\u003egetGaugeSprite(), alternateBgImage, 0, 0 );\n\n  // eventually change the transparency color depending on the saturation\n  needle::cfg.transparent_color = is_background_dark ? 0x000000U : 0xffffffU;\n\n  // or toggle needle shadow in dark mode\n  needle::cfg.drop_shadow       = is_background_dark ? false : true;\n\n\n```\n\n\n\n## Credits:\n\n- [@armel](https://github.com/armel) A.K.A. F4HWN\n- [@lovyan03](https://github.com/lovyan03)\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftobozo%2Flgfxmeter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftobozo%2Flgfxmeter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftobozo%2Flgfxmeter/lists"}