#include <Wire.h>
#include <INA226.h>

INA226 ina(Wire);

// #include <Arduino.h>
// #include "misakiUTF16FontData.h"
#include <LovyanGFX.hpp>
// ESP32でLovyanGFXを独自設定で利用する場合の設定例
extern const uint8_t rgb888[];
extern const uint8_t bgr888[];
extern const uint16_t swap565[];
extern const uint16_t rgb565[];
extern const uint8_t rgb332[];

static constexpr int image_width = 33;
static constexpr int image_height = 31;
//----------------------------------------------------------------------------

/// 独自の設定を行うクラスを、LGFX_Deviceから派生して作成します。
class LGFX : public lgfx::LGFX_Device
{
  lgfx::Panel_ST7789 _panel_instance;
  lgfx::Bus_SPI _bus_instance; // SPIバスのインスタンス
  lgfx::Light_PWM _light_instance;

public:
  // コンストラクタを作成し、ここで各種設定を行います。
  // クラス名を変更した場合はコンストラクタも同じ名前を指定してください。
  LGFX(void)
  {
    {                                    // バス制御の設定を行います。
      auto cfg = _bus_instance.config(); // バス設定用の構造体を取得します。

      // SPIバスの設定
      cfg.spi_host = SPI2_HOST; // 使用するSPIを選択  ESP32-S2,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
      // ※ ESP-IDFバージョンアップに伴い、VSPI_HOST , HSPI_HOSTの記述は非推奨になるため、エラーが出る場合は代わりにSPI2_HOST , SPI3_HOSTを使用してください。
      cfg.spi_mode = 3;                  // SPI通信モードを設定 (0 ~ 3)
      cfg.freq_write = 80000000;         // 送信時のSPIクロック (最大80MHz, 80MHzを整数で割った値に丸められます)
      cfg.freq_read = 16000000;          // 受信時のSPIクロック
      cfg.spi_3wire = true;              // 受信をMOSIピンで行う場合はtrueを設定
      cfg.use_lock = true;               // トランザクションロックを使用する場合はtrueを設定
      cfg.dma_channel = SPI_DMA_CH_AUTO; // 使用するDMAチャンネルを設定 (0=DMA不使用 / 1=1ch / 2=ch / SPI_DMA_CH_AUTO=自動設定)
      // ※ ESP-IDFバージョンアップに伴い、DMAチャンネルはSPI_DMA_CH_AUTO(自動設定)が推奨になりました。1ch,2chの指定は非推奨になります。
      cfg.pin_sclk = 4;                       // SPIのSCLKピン番号を設定
      cfg.pin_mosi = 5;                       // SPIのMOSIピン番号を設定
      cfg.pin_miso = -1;                      // SPIのMISOピン番号を設定 (-1 = disable)
      cfg.pin_dc = 6;                         // SPIのD/Cピン番号を設定  (-1 = disable)
                                              // SDカードと共通のSPIバスを使う場合、MISOは省略せず必ず設定してください。
      _bus_instance.config(cfg);              // 設定値をバスに反映します。
      _panel_instance.setBus(&_bus_instance); // バスをパネルにセットします。
    }

    {                                      // 表示パネル制御の設定を行います。
      auto cfg = _panel_instance.config(); // 表示パネル設定用の構造体を取得します。

      cfg.pin_cs = 8;    // CSが接続されているピン番号   (-1 = disable)
      cfg.pin_rst = 7;   // RSTが接続されているピン番号  (-1 = disable)
      cfg.pin_busy = -1; // BUSYが接続されているピン番号 (-1 = disable)
      // ※ 以下の設定値はパネル毎に一般的な初期値が設定されていますので、不明な項目はコメントアウトして試してみてください。
      cfg.panel_width = 172;    // 実際に表示可能な幅
      cfg.panel_height = 320;   // 実際に表示可能な高さ
      cfg.offset_x = 34;        // パネルのX方向オフセット量
      cfg.offset_y = 0;         // パネルのY方向オフセット量
      cfg.offset_rotation = 3;  // 回転方向の値のオフセット 0~7 (4~7は上下反転)
      cfg.dummy_read_pixel = 8; // ピクセル読出し前のダミーリードのビット数
      cfg.dummy_read_bits = 1;  // ピクセル以外のデータ読出し前のダミーリードのビット数
      cfg.readable = false;     // データ読出しが可能な場合 trueに設定
      cfg.invert = true;        // パネルの明暗が反転してしまう場合 trueに設定
      cfg.rgb_order = false;    // パネルの赤と青が入れ替わってしまう場合 trueに設定
      cfg.dlen_16bit = false;   // 16bitパラレルやSPIでデータ長を16bit単位で送信するパネルの場合 trueに設定
      cfg.bus_shared = false;   // SDカードとバスを共有している場合 trueに設定(drawJpgFile等でバス制御を行います)

      _panel_instance.config(cfg);
    }

    {                                      // バックライト制御の設定を行います。(必要なければ削除)
      auto cfg = _light_instance.config(); // バックライト設定用の構造体を取得します。

      cfg.pin_bl = 10;     // バックライトが接続されているピン番号
      cfg.invert = true;   // バックライトの輝度を反転させる場合 true
      cfg.freq = 44100;    // バックライトのPWM周波数
      cfg.pwm_channel = 1; // 使用するPWMのチャンネル番号

      _light_instance.config(cfg);
      _panel_instance.setLight(&_light_instance); // バックライトをパネルにセットします。
    }

    setPanel(&_panel_instance); // 使用するパネルをセットします。
  }
};

static LGFX lcd;
// static LGFX_Sprite sprite(&lcd);

void drawGradation(void)
{
  // 背景にグラデーションを描画する
  lcd.startWrite();
  lcd.setAddrWindow(0, 0, lcd.width(), lcd.height());
  for (int y = 0; y < lcd.height(); ++y)
  {
    for (int x = 0; x < lcd.width(); ++x)
    {
      lcd.writeColor(lcd.color888(x >> 1, (x + y) >> 2, y >> 1), 1);
    }
  }
  lcd.endWrite();
}

void checkConfig()
{
  Serial.print("Mode:                  ");
  switch (ina.getMode())
  {
  case INA226_MODE_POWER_DOWN:
    Serial.println("Power-Down");
    break;
  case INA226_MODE_SHUNT_TRIG:
    Serial.println("Shunt Voltage, Triggered");
    break;
  case INA226_MODE_BUS_TRIG:
    Serial.println("Bus Voltage, Triggered");
    break;
  case INA226_MODE_SHUNT_BUS_TRIG:
    Serial.println("Shunt and Bus, Triggered");
    break;
  case INA226_MODE_ADC_OFF:
    Serial.println("ADC Off");
    break;
  case INA226_MODE_SHUNT_CONT:
    Serial.println("Shunt Voltage, Continuous");
    break;
  case INA226_MODE_BUS_CONT:
    Serial.println("Bus Voltage, Continuous");
    break;
  case INA226_MODE_SHUNT_BUS_CONT:
    Serial.println("Shunt and Bus, Continuous");
    break;
  default:
    Serial.println("unknown");
  }

  Serial.print("Samples average:       ");
  switch (ina.getAverages())
  {
  case INA226_AVERAGES_1:
    Serial.println("1 sample");
    break;
  case INA226_AVERAGES_4:
    Serial.println("4 samples");
    break;
  case INA226_AVERAGES_16:
    Serial.println("16 samples");
    break;
  case INA226_AVERAGES_64:
    Serial.println("64 samples");
    break;
  case INA226_AVERAGES_128:
    Serial.println("128 samples");
    break;
  case INA226_AVERAGES_256:
    Serial.println("256 samples");
    break;
  case INA226_AVERAGES_512:
    Serial.println("512 samples");
    break;
  case INA226_AVERAGES_1024:
    Serial.println("1024 samples");
    break;
  default:
    Serial.println("unknown");
  }

  Serial.print("Bus conversion time:   ");
  switch (ina.getBusConversionTime())
  {
  case INA226_BUS_CONV_TIME_140US:
    Serial.println("140uS");
    break;
  case INA226_BUS_CONV_TIME_204US:
    Serial.println("204uS");
    break;
  case INA226_BUS_CONV_TIME_332US:
    Serial.println("332uS");
    break;
  case INA226_BUS_CONV_TIME_588US:
    Serial.println("558uS");
    break;
  case INA226_BUS_CONV_TIME_1100US:
    Serial.println("1.100ms");
    break;
  case INA226_BUS_CONV_TIME_2116US:
    Serial.println("2.116ms");
    break;
  case INA226_BUS_CONV_TIME_4156US:
    Serial.println("4.156ms");
    break;
  case INA226_BUS_CONV_TIME_8244US:
    Serial.println("8.244ms");
    break;
  default:
    Serial.println("unknown");
  }

  Serial.print("Shunt conversion time: ");
  switch (ina.getShuntConversionTime())
  {
  case INA226_SHUNT_CONV_TIME_140US:
    Serial.println("140uS");
    break;
  case INA226_SHUNT_CONV_TIME_204US:
    Serial.println("204uS");
    break;
  case INA226_SHUNT_CONV_TIME_332US:
    Serial.println("332uS");
    break;
  case INA226_SHUNT_CONV_TIME_588US:
    Serial.println("558uS");
    break;
  case INA226_SHUNT_CONV_TIME_1100US:
    Serial.println("1.100ms");
    break;
  case INA226_SHUNT_CONV_TIME_2116US:
    Serial.println("2.116ms");
    break;
  case INA226_SHUNT_CONV_TIME_4156US:
    Serial.println("4.156ms");
    break;
  case INA226_SHUNT_CONV_TIME_8244US:
    Serial.println("8.244ms");
    break;
  default:
    Serial.println("unknown");
  }

  Serial.print("Max possible current:  ");
  Serial.print(ina.getMaxPossibleCurrent());
  Serial.println(" A");

  Serial.print("Max current:           ");
  Serial.print(ina.getMaxCurrent());
  Serial.println(" A");

  Serial.print("Max shunt voltage:     ");
  Serial.print(ina.getMaxShuntVoltage());
  Serial.println(" V");

  Serial.print("Max power:             ");
  Serial.print(ina.getMaxPower());
  Serial.println(" W");
}
#include <vector>

#define LINE_COUNT 6
static std::vector<int> points[LINE_COUNT];
static int colors[] = {TFT_RED, TFT_GREEN, TFT_BLUE, TFT_CYAN, TFT_MAGENTA, TFT_YELLOW};
static int xoffset, yoffset, point_count;

int getBaseColor(int x, int y)
{
  return ((x ^ y) & 3 || ((x - xoffset) & 31 && y & 31) ? TFT_BLACK : ((!y || x == xoffset) ? TFT_WHITE : TFT_DARKGREEN));
}

void setup(void)
{
  lcd.init();
  // lcd.startWrite();
  lcd.setBrightness(100);

  Wire.begin(1, 0, 100000);
  // Default INA226 address is 0x40
  bool success = ina.begin();
  // Check if the connection was successful, stop if not
  if (!success)
  {
    Serial.println("Connection error");
    while (1)
      ;
  }
  // Configure INA226
  ina.configure(INA226_AVERAGES_1, INA226_BUS_CONV_TIME_1100US, INA226_SHUNT_CONV_TIME_1100US, INA226_MODE_SHUNT_BUS_CONT);
  // Calibrate INA226. Rshunt = 0.001 ohm, Max excepted current = 4A
  ina.calibrate(0.001, 40);
  // Display configuration
  checkConfig();

  if (lcd.width() < lcd.height())
    lcd.setRotation(lcd.getRotation() ^ 1);

  yoffset = lcd.height() >> 1;
  xoffset = lcd.width() >> 1;
  point_count = lcd.width() + 1;

  for (int i = 0; i < LINE_COUNT; i++)
  {
    points[i].resize(point_count);
  }

  lcd.startWrite();
  lcd.setAddrWindow(0, 0, lcd.width(), lcd.height());
  for (int y = 0; y < lcd.height(); y++)
  {
    for (int x = 0; x < lcd.width(); x++)
    {
      lcd.writeColor(getBaseColor(x, y - yoffset), 1);
    }
  }
  lcd.endWrite();
}

void loop(void)
{
  static int prev_sec;
  static int fps;
  ++fps;
  int sec = millis() / 1000;
  if (prev_sec != sec)
  {
    prev_sec = sec;
    lcd.setCursor(0, 0);
    lcd.printf("fps:%03d", fps);
    fps = 0;
  }

  static int count;

  for (int i = 0; i < LINE_COUNT; i++)
  {
    points[i][count % point_count] = (sinf((float)count / (10 + 30 * i)) + sinf((float)count / (13 + 37 * i))) * (lcd.height() >> 2);
  }

  ++count;

  lcd.startWrite();
  int index1 = count % point_count;
  for (int x = 0; x < point_count - 1; x++)
  {
    int index0 = index1;
    index1 = (index0 + 1) % point_count;
    for (int i = 0; i < LINE_COUNT; i++)
    {
      int y = points[i][index0];
      if (y != points[i][index1])
      {
        lcd.writePixel(x, y + yoffset, getBaseColor(x, y));
      }
    }

    for (int i = 0; i < LINE_COUNT; i++)
    {
      int y = points[i][index1];
      lcd.writePixel(x, y + yoffset, colors[i]);
    }
  }
  lcd.endWrite();
}