/*
D9에서 나온 PWM 신호를 받아서
PWM 신호를 읽고 OLED에 x, y 축이 있는 좌표계와
해당 PWM 듀티를 사각파 형태로 반복 표현하는 코드입니다.
*/
#include <U8glib.h>
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); // I2C 인터페이스, SSD1306 128x64
const int pwmPin = 9;     // PWM 신호를 출력할 핀
const int analogPin = A0; // PWM 신호를 읽을 아날로그 핀
// 25%, 50%, 75%, 100%에 해당하는 PWM 값 (0 ~ 255)
int dutyCycles[4] = {64, 128, 191, 255};
int pwmValue = 0; // 현재 PWM 값
// PWM 신호 주기 (1초 간격)
int checkInterval = 1000;
// 1초 간격 체크: 마지막 업데이트 시간과 interval 차이가 난 경우 true 반환
bool isUpdateDue(unsigned long lastUpdate, unsigned long interval)
{
    return (millis() - lastUpdate >= interval);
}
// 마지막 업데이트 시간을 현재 시간으로 갱신하는 함수
void updateLastTime(unsigned long &lastUpdate)
{
    lastUpdate = millis();
}
// 현재 인덱스에 해당하는 duty cycle 값을 반환하는 함수
int getDutyCycle(int index)
{
    return dutyCycles[index % 4];
}
// 인덱스 값을 순환시키는 함수
int advanceIndex(int index)
{
    return (index + 1) % 4;
}
// PWM 값을 하드웨어에 출력하는 함수
void outputPWM(int value)
{
    analogWrite(pwmPin, value);
}
// PWM 신호를 non blocking 방식으로 갱신하는 제어 함수
void pwmSignalControl()
{
    static unsigned long lastUpdate = 0; // 마지막 업데이트 시각 저장
    static int index = 0;
    if (isUpdateDue(lastUpdate, checkInterval))
    { // 1초 간격 확인
        pwmValue = getDutyCycle(index);
        outputPWM(pwmValue);
        index = advanceIndex(index);
        updateLastTime(lastUpdate);
    }
}
// OLED에 x, y 축이 있는 좌표계를 그리고,
// 지정 영역을 여러 주기로 나누어 사각파 형태로 현재 PWM 듀티를 표현하는 함수
void drawGraph()
{
    // 그래프 영역 설정
    int graphX = 5;    // 그래프 좌측 여백
    int graphY = 10;   // 그래프 상단 여백
    int graphW = 118;  // 그래프 너비
    int graphH = 40;   // 그래프 높이
    // 사각파 한 주기의 길이 (픽셀 단위)
    int period = 20;
    // 한 주기 내 'High' 구간의 길이: pwmValue (0~255)를 period (0~20)로 매핑
    int highWidth = map(pwmValue, 0, 255, 0, period);
    u8g.firstPage();
    do
    {
        // 좌표축 그리기
        // y축: (graphX, graphY)부터 (graphX, graphY+graphH)
        u8g.drawLine(graphX, graphY, graphX, graphY + graphH);
        // x축: (graphX, graphY+graphH)부터 (graphX+graphW, graphY+graphH)
        u8g.drawLine(graphX, graphY + graphH, graphX + graphW, graphY + graphH);
        // 사각파형태의 그래프를 반복적으로 그리기 (한 주기씩)
        int numPeriods = graphW / period;
        for (int i = 0; i < numPeriods; i++)
        {
            int startX = graphX + i * period;
            // 그릴 사각파: 왼쪽 끝에서 상승, highWidth 만큼 high 상태, 이후 하강
            // 상승: low (아래, graphY+graphH)에서 high (위, graphY)로
            if (highWidth > 0)
            {
                u8g.drawLine(startX, graphY + graphH, startX, graphY);                   // 상승 에지
                // horizontal high 구간
                u8g.drawLine(startX, graphY, startX + highWidth, graphY);
                // 하강 에지: 고점에서 낮은 값으로
                u8g.drawLine(startX + highWidth, graphY, startX + highWidth, graphY + graphH);
            }
            // 나머지 구간은 low 상태: x축과 동일한 y 위치
            u8g.drawLine(startX + highWidth, graphY + graphH, startX + period, graphY + graphH);
        }
        // PWM 값 텍스트 표시
        u8g.setFont(u8g_font_6x10);
        char buf[12];
        sprintf(buf, "PWM: %d", pwmValue);
        u8g.drawStr(graphX, graphY - 2, buf);
      
    } while (u8g.nextPage());
}
void setup()
{
    // 시리얼 통신은 필요없으므로 제거 가능
    pinMode(pwmPin, OUTPUT);   // PWM 핀을 출력으로 설정
    pinMode(analogPin, INPUT); // 아날로그 핀을 입력으로 설정
}
void loop()
{
    pwmSignalControl();
    drawGraph();
}