/*
 * PAC1710で10ミリ秒毎に1000回、10秒、電流値を測定し、SDカードに書く
 */
#include <M5Stack.h>
#include <Wire.h>
#include "PAC1710.hpp"
#include "menu.h"

void beep(int freq, int duration, uint8_t volume);

#define DEVID PAC1710::ADDR::OPEN // Resistor OPEN (N.C.) at ADDR_SEL pin
const int VSHUNT_mOHM = 10;

int sampling  = 10;  // サンプリング間隔(ミリ秒)
#define NSAMPLES 1000     // 10ms x 1000 = 10秒
float startthreshold = 20.0;  // 記録を開始する電流値(ミリA)

void datasend(int id,int reg,int *data,int datasize) {
    Wire.beginTransmission(id);
    Wire.write(reg);
    for(int i=0;i<datasize;i++) {
        Wire.write(data[i]);
    }
    Wire.endTransmission();
}

int dataread(int id,int reg,int *data,int datasize) {
    Wire.beginTransmission(id);
    Wire.write(reg);
    Wire.endTransmission(false);
    Wire.requestFrom(id, datasize);
    int i=0;
    while((i<datasize) && Wire.available()) {
        data[i] = Wire.read();
        i++;
    }
    return Wire.endTransmission(true);
}

uint16_t getID() {
    int id[2] = {0};
    dataread(DEVID, PAC1710::REG::PID, id, 2);
    return (id[0] << 8) | id[1];
}

#define TIMER0 0
hw_timer_t * samplingTimer = NULL;

float ampbuf[NSAMPLES];
float voltbuf[NSAMPLES];

Menu menu;

volatile int t0flag;

void IRAM_ATTR onTimer0() {
    t0flag = 1;
}

#define X0 10
#define Y0 220

void drawData(float maxamp) {
    M5.Lcd.fillRect(0, 0, 320, 220, BLACK);
    for (int i = 0; i < 299; i++) {
        int y0 = map((int)ampbuf[i * 3], 0, (int)maxamp, Y0, 0);
        int y1 = map((int)ampbuf[(i + 1) * 3], 0, (int)maxamp, Y0, 0);
        M5.Lcd.drawLine(i + X0, y0, i + 1 + X0, y1, WHITE);
    }
    M5.Lcd.drawLine(X0, Y0, 310, Y0, WHITE);
    M5.Lcd.drawLine(X0, 0, X0, Y0, WHITE);
}

void setup() {
    M5.begin();
    pinMode(21, INPUT_PULLUP);  // SDAをプルアップする
    pinMode(22, INPUT_PULLUP);  // SCLをプルアップする
    Wire.begin();

    M5.Lcd.setTextSize(2);
    M5.Lcd.fillScreen(BLACK);

    if (getID() != 0x585D) {
        M5.Lcd.setCursor(20, 100);
        M5.Lcd.print("Can not find PAC1710");
    }
    int c1cnf[] = {0B00110000};  // Sample time: 20ms, Range: -10mV to 10mV
    datasend(DEVID, PAC1710::REG::C1_VSAMP_CFG, c1cnf, 1);

    menu.setMenu("start", "", "");
    M5.Lcd.setCursor(20, 100);
    M5.Lcd.print("Press A button");
    M5.Lcd.setCursor(40, 120);
    M5.Lcd.print("to start sampling");

    while (true) {
        M5.update();
        if (M5.BtnA.wasPressed()) break;
    }

    M5.Lcd.fillScreen(BLACK);
    beep(2000, 100, 2);

    samplingTimer = timerBegin(TIMER0, 80, true);  // 1マイクロ秒のタイマーを初期設定する
    timerAttachInterrupt(samplingTimer, &onTimer0, true);  // 割り込み処理関数を設定する
    timerAlarmWrite(samplingTimer, sampling * 1000, true);  // samplingミリ秒のタイマー値を設定する

    timerAlarmEnable(samplingTimer);  // タイマーを起動する

    bool started = false;
    int indx = 0;
    float maxamp = 0;

    M5.Lcd.fillRect(50, 100, 200, 10, BLACK);
    while (true) {
        t0flag = 0;
        while (t0flag == 0) {  // タイマー割り込みを待つ
            delay(0);
        }

        int ch1Vsense[2] = {0};
        int ch1Vsource[2] = {0};

        dataread(DEVID, PAC1710::REG::C1_SVRES_H, ch1Vsense, 2); // CHANNEL 1 VSENSE RESULT REGISTER 
        dataread(DEVID, PAC1710::REG::C1_VVRES_H, ch1Vsource, 2); // CHANNEL 1 VSOURCE RESULT REGISTER 

        float amp = ( (int16_t(ch1Vsense[0] << 8 | (ch1Vsense[1])) >>4) * (1000.0 / 2047));
        float volt = (int16_t((ch1Vsource[0] << 3) | (ch1Vsource[1] >> 5) ) * 19.531); 

        if (!started) {
            // 電流値がしきい値(startthreshold)未満だったら、測定を始めない
            if (amp > -startthreshold && amp < startthreshold) {
                continue;
            }
            started = true;  // 電流値がしきい値を超えたら測定開始
        }
        ampbuf[indx] = amp;  // 電流値をメモリーに記録する
        voltbuf[indx] = volt;  // 電圧値をメモリーに記録する
        maxamp = max(amp, maxamp);
        M5.Lcd.setCursor(100, 100);
        M5.Lcd.print(indx * 100 / NSAMPLES); M5.Lcd.print(" %");
        if (++indx >= NSAMPLES) {  // データー数がサンプル数を超えたら、周期処理を終わる
            break;
        }
    }
    timerAlarmDisable(samplingTimer);  // タイマーを停止する

    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setTextSize(2);
    M5.Lcd.setCursor(20, 100);
    beep(2000, 400, 2);

    char fname[20];
    sprintf(fname, "/curLog.csv");
    for (int i = 0; SD.exists(fname); i++) {
        sprintf(fname, "/curLog(%d).csv", i + 1);
    }
    File f = SD.open(fname, FILE_WRITE);
    if (f) {
        f.println("time, current(mA), volt(mV)");
        for (int i = 0; i < NSAMPLES; i++) {
            f.printf("%d, %.2f, %.2f\r\n", sampling * i, ampbuf[i], voltbuf[i]);
        }
        f.close();
        M5.Lcd.print("Data written to");
        M5.Lcd.setCursor(40, 120);
        M5.Lcd.print(fname);
    } else {
        M5.Lcd.printf("open error %s", fname);
    }

    menu.setMenu("view", "", "");
    M5.Lcd.setCursor(40, 160);
    M5.Lcd.print("Press A button");
    M5.Lcd.setCursor(40, 180);
    M5.Lcd.print("to view data");

    while (true) {
        M5.update();
        if (M5.BtnA.wasPressed()) {
            drawData(maxamp);
        }
    }
}

void loop() {
}