/**
 * BL0940 SPI Mode + SSD1306 OLED Display
 * (Calibrated Version)
 *
 * Calibration Values Applied:
 *   Voltage Div = 15988.6
 *   Current Div = 568427.9
 *   Power Div   = 1421.1
 *
 * OLED: SSD1306 128x64 I2C
 *
 * Wiring (Arduino Uno/Nano):
 * --------------------------------
 * BL0940:
 *   SCLK   -> D13
 *   RX/SDI -> D11
 *   TX/SDO -> D12
 *   SEL    -> 3.3V
 *   VDD    -> 3.3V
 *   GND    -> GND
 *
 * OLED SSD1306:
 *   VCC -> 5V
 *   GND -> GND
 *   SDA -> A4
 *   SCL -> A5
 */

#include <SPI.h>
#include <Wire.h>
#include <BL0940.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// OLED settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1
#define SCREEN_ADDRESS 0x3C

Adafruit_SSD1306 display(
    SCREEN_WIDTH,
    SCREEN_HEIGHT,
    &Wire,
    OLED_RESET
);

// BL0940 SPI object
BL0940 meter(-1, 500000);

void setup()
{
    Serial.begin(115200);

    // OLED Init
    if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {

        Serial.println("SSD1306 allocation failed");

        while (1);
    }

    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);

    display.setTextSize(1);
    display.setCursor(0, 0);
    display.println("BL0940 Energy");
    display.println("Initializing...");
    display.display();

    // BL0940 Init
    if (!meter.begin()) {

        Serial.println("BL0940 not detected!");

        display.clearDisplay();
        display.setCursor(0, 0);
        display.println("BL0940 ERROR!");
        display.println("Check SPI");
        display.println("SEL HIGH");
        display.display();

        while (1) {
            delay(1000);
        }
    }

    // ===== Apply Calibration =====
    meter.setVoltageDiv(15988.6);
    meter.setCurrentDiv(568427.9);
    meter.setPowerDiv(1421.1);
    // =============================

    Serial.println("BL0940 initialized.");
    Serial.println("Calibration Applied.");

    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("Calibration");
    display.println("Applied!");
    display.display();

    delay(1500);
}

void loop()
{
    float voltage = meter.getVoltage();
    float current = meter.getCurrent();
    float power   = meter.getPower();
    float energy  = meter.getEnergy();
    float temp    = meter.getInternalTemp();

    if (meter.lastReadOk()) {

        // Serial Output
        Serial.print("V=");
        Serial.print(voltage, 1);

        Serial.print("V  I=");
        Serial.print(current, 3);

        Serial.print("A  P=");
        Serial.print(power, 1);

        Serial.print("W  E=");
        Serial.print(energy, 4);

        Serial.print("kWh  T=");
        Serial.print(temp, 1);
        Serial.println("C");

        // OLED Display
        display.clearDisplay();

        // Large Voltage Display
        display.setTextSize(2);
        display.setCursor(0, 0);

        display.print("V:");
        display.print(voltage, 1);
        display.println("V");

        // Small Text
        display.setTextSize(1);

        display.setCursor(0, 22);
        display.print("I: ");
        display.print(current, 3);
        display.println(" A");

        display.setCursor(0, 34);
        display.print("P: ");
        display.print(power, 1);
        display.println(" W");

        display.setCursor(0, 46);
        display.print("E: ");
        display.print(energy, 4);
        display.println("kWh");

        display.setCursor(0, 56);
        display.print("T:");
        display.print(temp, 1);
        display.print("C");

        display.display();

    } else {

        Serial.println("Read error!");

        display.clearDisplay();
        display.setCursor(0, 0);
        display.println("Read Error!");
        display.display();
    }

    delay(1000);
}
