Belajar Elektro

Random post

Belajar Elektro

Powered By Blogger

This is default featured slide 1 title

Go to Blogger edit html and find these sentences.Now replace these sentences with your own descriptions.This theme is Bloggerized by Lasantha Bandara - Premiumbloggertemplates.com.

This is default featured slide 2 title

Go to Blogger edit html and find these sentences.Now replace these sentences with your own descriptions.This theme is Bloggerized by Lasantha Bandara - Premiumbloggertemplates.com.

This is default featured slide 3 title

Go to Blogger edit html and find these sentences.Now replace these sentences with your own descriptions.This theme is Bloggerized by Lasantha Bandara - Premiumbloggertemplates.com.

This is default featured slide 4 title

Go to Blogger edit html and find these sentences.Now replace these sentences with your own descriptions.This theme is Bloggerized by Lasantha Bandara - Premiumbloggertemplates.com.

This is default featured slide 5 title

Go to Blogger edit html and find these sentences.Now replace these sentences with your own descriptions.This theme is Bloggerized by Lasantha Bandara - Premiumbloggertemplates.com.

Friday, 16 October 2026

SWR DAN POWER METER ESP32 C3

 


WIRING :

OLED SDA

GPIO6

OLED SCL

GPIO7

FWD ADC

GPIO0

REF ADC

GPIO1

Buzzer transistor

GPIO4






KODE :

#include <Arduino.h>

#include <Wire.h>

#include <U8g2lib.h>

#include <math.h>

 

// =====================================================

// ESP32-C3 SUPER MINI

// SWR + POWER METER

// FINAL VERSION

// WITH STARTUP INTRO + SOUND

// =====================================================

 

// =====================================================

// PIN

// =====================================================

 

#define FWD_PIN     0

#define REF_PIN     1

#define BUZZER_PIN  4

 

#define OLED_SDA    6

#define OLED_SCL    7

 

// =====================================================

// OLED

// =====================================================

 

U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled(

  U8G2_R0,

  U8X8_PIN_NONE

);

 

// =====================================================

// ADC

// =====================================================

 

#define ADC_MAX   4095.0

#define ADC_VREF  3.3

 

// Detector -> 10k -> ADC -> 15k -> GND

const float DIVIDER_RATIO = 25.0 / 15.0;

 

// =====================================================

// ZERO OFFSET

// =====================================================

 

const float FWD_ZERO_ADC = 6.0;

const float REF_ZERO_ADC = 5.2;

 

// =====================================================

// FWD POWER CALIBRATION

// =====================================================

//

// 1 W  = 1.3001 V  -> HASIL AKTUAL

// 5 W  = 2.5062 V  -> HASIL AKTUAL

//

// Titik di atas 5 W masih estimasi.

// =====================================================

 

#define CAL_POINTS 9

 

float calVoltage[CAL_POINTS] = {

  0.0000,

  1.3001,

  2.5062,

  2.8500,

  3.4000,

  3.6500,

  4.3000,

  4.7000,

  5.0000

};

 

float calPower[CAL_POINTS] = {

  0.0,

  1.0,

  5.0,

  10.0,

  20.0,

  25.0,

  50.0,

  75.0,

  100.0

};

 

// =====================================================

// ADC AVERAGING

// =====================================================

 

#define ADC_SAMPLES 100

 

float readAverageADC(uint8_t pin)

{

  uint32_t total = 0;

 

  for (int i = 0; i < ADC_SAMPLES; i++)

  {

    total += analogRead(pin);

    delayMicroseconds(100);

  }

 

  return (float)total / ADC_SAMPLES;

}

 

// =====================================================

// ADC -> VOLTAGE

// =====================================================

 

float adcToVoltage(float adc)

{

  if (adc < 0)

    adc = 0;

 

  if (adc > ADC_MAX)

    adc = ADC_MAX;

 

  return (adc / ADC_MAX) * ADC_VREF;

}

 

// =====================================================

// ADC -> COUPLER VOLTAGE

// =====================================================

 

float adcToCouplerVoltage(float adc)

{

  if (adc <= 0)

    return 0.0;

 

  return adcToVoltage(adc) * DIVIDER_RATIO;

}

 

// =====================================================

// FWD COUPLER VOLTAGE

// =====================================================

 

float getFwdCouplerVoltage(float rawADC)

{

  float correctedADC =

    rawADC - FWD_ZERO_ADC;

 

  if (correctedADC < 0)

    correctedADC = 0;

 

  return adcToCouplerVoltage(

    correctedADC

  );

}

 

// =====================================================

// RAW REF COUPLER

// =====================================================

 

float getRawRefCouplerVoltage(float rawADC)

{

  float correctedADC =

    rawADC - REF_ZERO_ADC;

 

  if (correctedADC < 0)

    correctedADC = 0;

 

  return adcToCouplerVoltage(

    correctedADC

  );

}

 

// =====================================================

// RESIDUAL REF COMPENSATION

// =====================================================

//

// Berdasarkan pengukuran dummy 50 ohm:

//

// FWD sekitar 1.31 V

// REF sekitar 0.0394 V

//

// FWD sekitar 2.53 V

// REF sekitar 0.0489 V

//

// Kompensasi hanya mengurangi residual

// yang terukur pada sistem.

//

// Tidak memaksa SWR menjadi 1.00.

// =====================================================

 

float calculateResidualRef(float fwdVoltage)

{

  // TX mati

  if (fwdVoltage < 0.20)

    return 0.0;

 

  // Di bawah sekitar 1 W

  if (fwdVoltage <= 1.30)

  {

    return

      (fwdVoltage / 1.30) *

      0.0394;

  }

 

  // Antara 1 W dan 5 W

  if (fwdVoltage <= 2.53)

  {

    float fraction =

      (fwdVoltage - 1.30) /

      (2.53 - 1.30);

 

    return

      0.0394 +

      fraction *

      (0.0489 - 0.0394);

  }

 

  // Di atas 5 W

  float extra =

    fwdVoltage - 2.53;

 

  float residual =

    0.0489 +

    extra * 0.0040;

 

  // Batas kompensasi

  if (residual > 0.070)

    residual = 0.070;

 

  return residual;

}

 

// =====================================================

// EFFECTIVE REF

// =====================================================

 

float getEffectiveRefVoltage(

  float rawRefVoltage,

  float fwdVoltage

)

{

  if (fwdVoltage < 0.20)

    return 0.0;

 

  float residual =

    calculateResidualRef(

      fwdVoltage

    );

 

  float effective =

    rawRefVoltage - residual;

 

  if (effective < 0)

    effective = 0;

 

  return effective;

}

 

// =====================================================

// VOLTAGE -> POWER

// =====================================================

 

float voltageToPower(float voltage)

{

  if (voltage <= calVoltage[0])

    return 0.0;

 

  if (voltage >=

      calVoltage[CAL_POINTS - 1])

  {

    return calPower[CAL_POINTS - 1];

  }

 

  for (int i = 0;

       i < CAL_POINTS - 1;

       i++)

  {

    if (voltage >= calVoltage[i] &&

        voltage <= calVoltage[i + 1])

    {

      float v1 =

        calVoltage[i];

 

      float v2 =

        calVoltage[i + 1];

 

      float p1 =

        calPower[i];

 

      float p2 =

        calPower[i + 1];

 

      float fraction =

        (voltage - v1) /

        (v2 - v1);

 

      return

        p1 +

        fraction * (p2 - p1);

    }

  }

 

  return 0.0;

}

 

// =====================================================

// SWR

// =====================================================

 

float calculateSWR(

  float vfwd,

  float vref

)

{

  // Tidak ada TX

  if (vfwd < 0.20)

    return 1.0;

 

  if (vref <= 0.00001)

    return 1.0;

 

  float gamma =

    vref / vfwd;

 

  if (gamma < 0)

    gamma = 0;

 

  if (gamma >= 0.999)

    gamma = 0.999;

 

  float swr =

    (1.0 + gamma) /

    (1.0 - gamma);

 

  if (swr < 1.0)

    swr = 1.0;

 

  if (swr > 99.9)

    swr = 99.9;

 

  return swr;

}

 

// =====================================================

// RETURN LOSS

// =====================================================

 

float calculateReturnLoss(

  float vfwd,

  float vref

)

{

  // TX mati

  if (vfwd < 0.20)

    return -1.0;

 

  // REF sangat kecil

  if (vref <= 0.00001)

    return 99.9;

 

  float gamma =

    vref / vfwd;

 

  if (gamma <= 0.000001)

    return 99.9;

 

  if (gamma >= 0.9999)

    gamma = 0.9999;

 

  float rl =

    -20.0 * log10(gamma);

 

  if (rl > 99.9)

    rl = 99.9;

 

  if (rl < 0)

    rl = 0;

 

  return rl;

}

 

// =====================================================

// REFLECTED POWER

// =====================================================

 

float calculateRefPower(

  float fwdPower,

  float vfwd,

  float vref

)

{

  if (fwdPower <= 0.001)

    return 0.0;

 

  if (vfwd < 0.20)

    return 0.0;

 

  if (vref <= 0.00001)

    return 0.0;

 

  float ratio =

    vref / vfwd;

 

  if (ratio < 0)

    ratio = 0;

 

  if (ratio > 0.999)

    ratio = 0.999;

 

  float pref =

    fwdPower *

    ratio *

    ratio;

 

  if (pref < 0)

    pref = 0;

 

  return pref;

}

 

// =====================================================

// STARTUP INTRO

// =====================================================

 

void startupIntro()

{

  // Pastikan buzzer mati

  noTone(BUZZER_PIN);

 

  digitalWrite(

    BUZZER_PIN,

    LOW

  );

 

  // =================================================

  // SCREEN 1

  // =================================================

 

  oled.clearBuffer();

 

  oled.setFont(

    u8g2_font_6x10_tf

  );

 

  oled.drawStr(

    44,

    18,

    "SWR"

  );

 

  oled.drawStr(

    19,

    34,

    "POWER METER"

  );

 

  oled.drawStr(

    35,

    52,

    "START"

  );

 

  oled.sendBuffer();

 

  delay(300);

 

  // =================================================

  // BEEP 1

  // =================================================

 

  tone(

    BUZZER_PIN,

    1000

  );

 

  delay(120);

 

  noTone(

    BUZZER_PIN

  );

 

  delay(100);

 

  // =================================================

  // BEEP 2

  // =================================================

 

  tone(

    BUZZER_PIN,

    1500

  );

 

  delay(120);

 

  noTone(

    BUZZER_PIN

  );

 

  delay(100);

 

  // =================================================

  // BEEP 3

  // =================================================

 

  tone(

    BUZZER_PIN,

    2000

  );

 

  delay(180);

 

  noTone(

    BUZZER_PIN

  );

 

  delay(250);

 

  // =================================================

  // READY SCREEN

  // =================================================

 

  oled.clearBuffer();

 

  oled.setFont(

    u8g2_font_6x10_tf

  );

 

  oled.drawStr(

    43,

    25,

    "READY"

  );

 

  oled.drawStr(

    25,

    42,

    "MEASURING"

  );

 

  oled.sendBuffer();

 

  // =================================================

  // READY SOUND

  // =================================================

 

  tone(

    BUZZER_PIN,

    2200

  );

 

  delay(100);

 

  tone(

    BUZZER_PIN,

    2600

  );

 

  delay(180);

 

  noTone(

    BUZZER_PIN

  );

 

  delay(700);

}

 

// =====================================================

// BUZZER ALARM SWR

// =====================================================

 

unsigned long lastBeep = 0;

 

bool buzzerState = false;

 

void updateBuzzer(float swr)

{

  // Alarm SWR >= 1.50

  if (swr >= 1.50)

  {

    if (

      millis() - lastBeep >= 300

    )

    {

      lastBeep = millis();

 

      buzzerState =

        !buzzerState;

 

      digitalWrite(

        BUZZER_PIN,

        buzzerState

      );

    }

  }

  else

  {

    buzzerState = false;

 

    digitalWrite(

      BUZZER_PIN,

      LOW

    );

  }

}

 

// =====================================================

// OLED MAIN DISPLAY

// =====================================================

 

void displayMain(

  float fwdPower,

  float refPower,

  float returnLoss,

  float swr

)

{

  oled.clearBuffer();

 

  // =================================================

  // TITLE

  // =================================================

 

  oled.setFont(

    u8g2_font_6x10_tf

  );

 

  oled.drawStr(

    0,

    9,

    "SWR and Power Meter"

  );

 

  // =================================================

  // FWD

  // =================================================

 

  char line[32];

 

  snprintf(

    line,

    sizeof(line),

    "FWD : %.2f W",

    fwdPower

  );

 

  oled.drawStr(

    0,

    22,

    line

  );

 

  // =================================================

  // REF

  // =================================================

 

  snprintf(

    line,

    sizeof(line),

    "REF : %.3f W",

    refPower

  );

 

  oled.drawStr(

    0,

    34,

    line

  );

 

  // =================================================

  // RETURN LOSS

  // =================================================

 

  if (returnLoss < 0)

  {

    oled.drawStr(

      0,

      46,

      "RL  : ----"

    );

  }

  else

  {

    snprintf(

      line,

      sizeof(line),

      "RL  : %.1f dB",

      returnLoss

    );

 

    oled.drawStr(

      0,

      46,

      line

    );

  }

 

  // =================================================

  // SWR

  // =================================================

 

  snprintf(

    line,

    sizeof(line),

    "SWR:%.2f",

    swr

  );

 

  oled.drawStr(

    0,

    58,

    line

  );

 

  // =================================================

  // SWR BAR

  // =================================================

 

  int barX = 58;

  int barY = 51;

  int barW = 68;

  int barH = 10;

 

  oled.drawFrame(

    barX,

    barY,

    barW,

    barH

  );

 

  // SWR 1.00 = kosong

  // SWR 3.00 = penuh

 

  float maxSWR = 3.0;

 

  float normalized =

    (swr - 1.0) /

    (maxSWR - 1.0);

 

  if (normalized < 0)

    normalized = 0;

 

  if (normalized > 1)

    normalized = 1;

 

  int fillWidth =

    (int)(

      (barW - 2) *

      normalized

    );

 

  if (fillWidth > 0)

  {

    oled.drawBox(

      barX + 1,

      barY + 1,

      fillWidth,

      barH - 2

    );

  }

 

  oled.sendBuffer();

}

 

// =====================================================

// SETUP

// =====================================================

 

void setup()

{

  // =================================================

  // SERIAL

  // =================================================

 

  Serial.begin(115200);

 

  delay(1000);

 

  // =================================================

  // ADC

  // =================================================

 

  analogReadResolution(12);

 

  analogSetPinAttenuation(

    FWD_PIN,

    ADC_11db

  );

 

  analogSetPinAttenuation(

    REF_PIN,

    ADC_11db

  );

 

  // =================================================

  // BUZZER

  // =================================================

 

  pinMode(

    BUZZER_PIN,

    OUTPUT

  );

 

  digitalWrite(

    BUZZER_PIN,

    LOW

  );

 

  // =================================================

  // OLED

  // =================================================

 

  Wire.begin(

    OLED_SDA,

    OLED_SCL

  );

 

  oled.begin();

 

  // =================================================

  // STARTUP INTRO

  // =================================================

 

  startupIntro();

 

  // =================================================

  // SERIAL INFORMATION

  // =================================================

 

  Serial.println();

 

  Serial.println(

    "=========================================="

  );

 

  Serial.println(

    "      ESP32-C3 SWR POWER METER"

  );

 

  Serial.println(

    "      FINAL OPTIMIZED VERSION"

  );

 

  Serial.println(

    "=========================================="

  );

 

  Serial.println();

 

  Serial.print(

    "FWD ZERO ADC : "

  );

 

  Serial.println(

    FWD_ZERO_ADC,

    1

  );

 

  Serial.print(

    "REF ZERO ADC : "

  );

 

  Serial.println(

    REF_ZERO_ADC,

    1

  );

 

  Serial.println();

 

  Serial.println(

    "REF residual compensation : ON"

  );

 

  Serial.println(

    "Startup intro              : ON"

  );

 

  Serial.println(

    "SWR alarm >= 1.50          : ON"

  );

 

  Serial.println();

 

  Serial.println(

    "Meter ready."

  );

 

  Serial.println();

}

 

// =====================================================

// LOOP

// =====================================================

 

void loop()

{

  // =================================================

  // READ ADC

  // =================================================

 

  float fwdRaw =

    readAverageADC(

      FWD_PIN

    );

 

  float refRaw =

    readAverageADC(

      REF_PIN

    );

 

  // =================================================

  // ADC VOLTAGE

  // =================================================

 

  float fwdADCVoltage =

    adcToVoltage(

      fwdRaw

    );

 

  float refADCVoltage =

    adcToVoltage(

      refRaw

    );

 

  // =================================================

  // FWD COUPLER

  // =================================================

 

  float fwdCoupler =

    getFwdCouplerVoltage(

      fwdRaw

    );

 

  // =================================================

  // RAW REF

  // =================================================

 

  float rawRefCoupler =

    getRawRefCouplerVoltage(

      refRaw

    );

 

  // =================================================

  // RESIDUAL REF

  // =================================================

 

  float residualRef =

    calculateResidualRef(

      fwdCoupler

    );

 

  // =================================================

  // EFFECTIVE REF

  // =================================================

 

  float refCoupler =

    getEffectiveRefVoltage(

      rawRefCoupler,

      fwdCoupler

    );

 

  // =================================================

  // FWD POWER

  // =================================================

 

  float fwdPower =

    voltageToPower(

      fwdCoupler

    );

 

  // =================================================

  // SWR

  // =================================================

 

  float swr =

    calculateSWR(

      fwdCoupler,

      refCoupler

    );

 

  // =================================================

  // RETURN LOSS

  // =================================================

 

  float returnLoss =

    calculateReturnLoss(

      fwdCoupler,

      refCoupler

    );

 

  // =================================================

  // REF POWER

  // =================================================

 

  float refPower =

    calculateRefPower(

      fwdPower,

      fwdCoupler,

      refCoupler

    );

 

  // =================================================

  // BUZZER

  // =================================================

 

  updateBuzzer(

    swr

  );

 

  // =================================================

  // OLED

  // =================================================

 

  displayMain(

    fwdPower,

    refPower,

    returnLoss,

    swr

  );

 

  // =================================================

  // SERIAL MONITOR

  // =================================================

 

  Serial.println();

 

  Serial.println(

    "------------------------------------------"

  );

 

  Serial.print(

    "FWD RAW ADC     : "

  );

 

  Serial.println(

    fwdRaw,

    1

  );

 

  Serial.print(

    "FWD ADC         : "

  );

 

  Serial.print(

    fwdADCVoltage,

    4

  );

 

  Serial.println(

    " V"

  );

 

  Serial.print(

    "FWD COUPLER     : "

  );

 

  Serial.print(

    fwdCoupler,

    4

  );

 

  Serial.println(

    " V"

  );

 

  Serial.print(

    "FWD POWER       : "

  );

 

  Serial.print(

    fwdPower,

    2

  );

 

  Serial.println(

    " W"

  );

 

  Serial.println();

 

  Serial.print(

    "REF RAW ADC     : "

  );

 

  Serial.println(

    refRaw,

    1

  );

 

  Serial.print(

    "REF ADC         : "

  );

 

  Serial.print(

    refADCVoltage,

    4

  );

 

  Serial.println(

    " V"

  );

 

  Serial.print(

    "REF RAW COUPLER : "

  );

 

  Serial.print(

    rawRefCoupler,

    4

  );

 

  Serial.println(

    " V"

  );

 

  Serial.print(

    "REF RESIDUAL    : "

  );

 

  Serial.print(

    residualRef,

    4

  );

 

  Serial.println(

    " V"

  );

 

  Serial.print(

    "REF EFFECTIVE   : "

  );

 

  Serial.print(

    refCoupler,

    4

  );

 

  Serial.println(

    " V"

  );

 

  Serial.print(

    "REF POWER       : "

  );

 

  Serial.print(

    refPower,

    3

  );

 

  Serial.println(

    " W"

  );

 

  Serial.println();

 

  Serial.print(

    "SWR             : "

  );

 

  Serial.println(

    swr,

    2

  );

 

  Serial.print(

    "RETURN LOSS     : "

  );

 

  if (returnLoss < 0)

  {

    Serial.println(

      "----"

    );

  }

  else

  {

    Serial.print(

      returnLoss,

      2

    );

 

    Serial.println(

      " dB"

    );

  }

 

  Serial.println(

    "------------------------------------------"

  );

 

  delay(300);

}

 

 

VERSI 2 :

// =====================================================

// ESP32-C3 SUPER MINI

// SWR + POWER METER

// FINAL ELEGANT VERSION

// - OPENING ANIMASI + NADA BAS

// - 2-LEVEL BUZZER ALARM (PASSIVE)

// - FAST & STABLE

// =====================================================

 

#include <Arduino.h>

#include <Wire.h>

#include <U8g2lib.h>

#include <math.h>

 

// =====================================================

// DEBUG SWITCH

// =====================================================

// 0 = Serial Monitor ringkas (lebih cepat)

// 1 = Serial Monitor lengkap (untuk debug)

// =====================================================

 

#define DEBUG_SERIAL 0

 

// =====================================================

// PIN

// =====================================================

 

#define FWD_PIN     0

#define REF_PIN     1

#define BUZZER_PIN  4

 

#define OLED_SDA    6

#define OLED_SCL    7

 

// =====================================================

// OLED

// =====================================================

 

U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled(

  U8G2_R0,

  U8X8_PIN_NONE

);

 

// =====================================================

// ADC

// =====================================================

 

#define ADC_MAX   4095.0

#define ADC_VREF  3.3

 

// Detector -> 10k -> ADC -> 15k -> GND

const float DIVIDER_RATIO = 25.0 / 15.0;

 

// =====================================================

// ZERO OFFSET

// =====================================================

 

const float FWD_ZERO_ADC = 6.0;

const float REF_ZERO_ADC = 5.2;

 

// =====================================================

// FWD POWER CALIBRATION

// =====================================================

 

#define CAL_POINTS 9

 

float calVoltage[CAL_POINTS] = {

  0.0000,

  1.3001,

  2.5062,

  2.8500,

  3.4000,

  3.6500,

  4.3000,

  4.7000,

  5.0000

};

 

float calPower[CAL_POINTS] = {

  0.0,

  1.0,

  5.0,

  10.0,

  20.0,

  25.0,

  50.0,

  75.0,

  100.0

};

 

// =====================================================

// ADC FILTER: MEDIAN + AVERAGE (FAST)

// =====================================================

 

#define ADC_MEDIAN_N   9

#define ADC_AVG_N      3

 

float readFilteredADC(uint8_t pin)

{

  float medianBuffer[ADC_AVG_N];

 

  for (int k = 0; k < ADC_AVG_N; k++)

  {

    uint16_t samples[ADC_MEDIAN_N];

 

    for (int i = 0; i < ADC_MEDIAN_N; i++)

    {

      samples[i] = analogRead(pin);

      delayMicroseconds(80);

    }

 

    // Insertion sort (cepat untuk array kecil)

    for (int i = 1; i < ADC_MEDIAN_N; i++)

    {

      uint16_t key = samples[i];

      int j = i - 1;

 

      while (j >= 0 && samples[j] > key)

      {

        samples[j + 1] = samples[j];

        j--;

      }

      samples[j + 1] = key;

    }

 

    medianBuffer[k] = samples[ADC_MEDIAN_N / 2];

  }

 

  float sum = 0;

  for (int k = 0; k < ADC_AVG_N; k++)

    sum += medianBuffer[k];

 

  return sum / ADC_AVG_N;

}

 

// =====================================================

// IIR LOW-PASS FILTER

// =====================================================

 

float iirFwd = 0.0;

float iirRef = 0.0;

const float IIR_ALPHA = 0.30f;

 

float applyIIR(float newValue, float &state)

{

  if (state == 0.0f)

    state = newValue;

  else

    state = state + IIR_ALPHA * (newValue - state);

 

  return state;

}

 

// =====================================================

// ADC -> VOLTAGE

// =====================================================

 

float adcToVoltage(float adc)

{

  if (adc < 0) adc = 0;

  if (adc > ADC_MAX) adc = ADC_MAX;

 

  return (adc / ADC_MAX) * ADC_VREF;

}

 

// =====================================================

// ADC -> COUPLER VOLTAGE

// =====================================================

 

float adcToCouplerVoltage(float adc)

{

  if (adc <= 0)

    return 0.0;

 

  return adcToVoltage(adc) * DIVIDER_RATIO;

}

 

// =====================================================

// FWD COUPLER VOLTAGE

// =====================================================

 

float getFwdCouplerVoltage(float rawADC)

{

  float correctedADC = rawADC - FWD_ZERO_ADC;

  if (correctedADC < 0) correctedADC = 0;

 

  return adcToCouplerVoltage(correctedADC);

}

 

// =====================================================

// RAW REF COUPLER

// =====================================================

 

float getRawRefCouplerVoltage(float rawADC)

{

  float correctedADC = rawADC - REF_ZERO_ADC;

  if (correctedADC < 0) correctedADC = 0;

 

  return adcToCouplerVoltage(correctedADC);

}

 

// =====================================================

// RESIDUAL REF COMPENSATION

// =====================================================

 

float calculateResidualRef(float fwdVoltage)

{

  if (fwdVoltage < 0.20)

    return 0.0;

 

  if (fwdVoltage <= 1.30)

  {

    return (fwdVoltage / 1.30) * 0.0394;

  }

 

  if (fwdVoltage <= 2.53)

  {

    float fraction = (fwdVoltage - 1.30) / (2.53 - 1.30);

    return 0.0394 + fraction * (0.0489 - 0.0394);

  }

 

  float extra = fwdVoltage - 2.53;

  float residual = 0.0489 + extra * 0.0040;

 

  if (residual > 0.065)

    residual = 0.065 + (residual - 0.065) * 0.3f;

 

  if (residual > 0.070)

    residual = 0.070;

 

  return residual;

}

 

// =====================================================

// EFFECTIVE REF

// =====================================================

 

float getEffectiveRefVoltage(

  float rawRefVoltage,

  float fwdVoltage

)

{

  if (fwdVoltage < 0.20)

    return 0.0;

 

  float residual = calculateResidualRef(fwdVoltage);

 

  float effective = rawRefVoltage - (residual * 0.8f);

 

  if (effective < 0)

    effective = 0;

 

  return effective;

}

 

// =====================================================

// VOLTAGE -> POWER

// =====================================================

 

float voltageToPower(float voltage)

{

  if (voltage <= calVoltage[0])

    return 0.0;

 

  if (voltage >= calVoltage[CAL_POINTS - 1])

    return calPower[CAL_POINTS - 1];

 

  for (int i = 0; i < CAL_POINTS - 1; i++)

  {

    if (voltage >= calVoltage[i] &&

        voltage <= calVoltage[i + 1])

    {

      float v1 = calVoltage[i];

      float v2 = calVoltage[i + 1];

      float p1 = calPower[i];

      float p2 = calPower[i + 1];

 

      float fraction = (voltage - v1) / (v2 - v1);

 

      return p1 + fraction * (p2 - p1);

    }

  }

 

  return 0.0;

}

 

// =====================================================

// SWR

// =====================================================

 

float calculateSWR(float vfwd, float vref)

{

  if (vfwd < 0.20) return 1.0;

  if (vref <= 0.00001) return 1.0;

 

  float gamma = vref / vfwd;

 

  if (gamma < 0) gamma = 0;

  if (gamma >= 0.999) gamma = 0.999;

 

  float swr = (1.0 + gamma) / (1.0 - gamma);

 

  if (swr < 1.0)   swr = 1.0;

  if (swr > 99.9)  swr = 99.9;

 

  return swr;

}

 

// =====================================================

// RETURN LOSS

// =====================================================

 

float calculateReturnLoss(float vfwd, float vref)

{

  if (vfwd < 0.20) return -1.0;

  if (vref <= 0.00001) return 99.9;

 

  float gamma = vref / vfwd;

 

  if (gamma <= 0.000001) return 99.9;

  if (gamma >= 0.9999) gamma = 0.9999;

 

  float rl = -20.0 * log10(gamma);

 

  if (rl > 99.9) rl = 99.9;

  if (rl < 0)    rl = 0;

 

  return rl;

}

 

// =====================================================

// REFLECTED POWER

// =====================================================

 

float calculateRefPower(

  float fwdPower,

  float vfwd,

  float vref

)

{

  if (fwdPower <= 0.001) return 0.0;

  if (vfwd < 0.20)       return 0.0;

  if (vref <= 0.00001)   return 0.0;

 

  float ratio = vref / vfwd;

 

  if (ratio < 0) ratio = 0;

  if (ratio > 0.999) ratio = 0.999;

 

  float pref = fwdPower * ratio * ratio;

 

  if (pref < 0) pref = 0;

 

  return pref;

}

 

// =====================================================

// STARTUP INTRO - ELEGANT + NADA BAS

// =====================================================

 

void startupIntro()

{

  noTone(BUZZER_PIN);

  digitalWrite(BUZZER_PIN, LOW);

 

  // =================================================

  // FASE 1: Garis dekorasi dari tengah

  // =================================================

 

  oled.clearBuffer();

 

  for (int i = 0; i <= 64; i += 4)

  {

    oled.clearBuffer();

    oled.drawHLine(64 - i, 32, i * 2);

    oled.drawHLine(64 - i, 30, i * 2);

    oled.sendBuffer();

    delay(10);

  }

 

  tone(BUZZER_PIN, 150);

  delay(180);

  noTone(BUZZER_PIN);

  delay(80);

 

  // =================================================

  // FASE 2: Logo "SWR" + Frame

  // =================================================

 

  oled.clearBuffer();

  oled.setFont(u8g2_font_7x13B_tr);

 

  // Frame muncul

  for (int w = 0; w <= 70; w += 5)

  {

    oled.clearBuffer();

    oled.drawFrame(64 - w / 2, 8, w, 22);

    oled.sendBuffer();

    delay(8);

  }

 

  // Huruf "SWR"

  const char *logo = "SWR";

  for (int i = 1; i <= 3; i++)

  {

    oled.clearBuffer();

    oled.drawFrame(29, 8, 70, 22);

 

    char buf[4];

    strncpy(buf, logo, i);

    buf[i] = '\0';

 

    int w = oled.getStrWidth(buf);

    oled.drawStr((128 - w) / 2, 25, buf);

    oled.sendBuffer();

 

    tone(BUZZER_PIN, 200 + i * 50);

    delay(50);

    noTone(BUZZER_PIN);

    delay(80);

  }

 

  tone(BUZZER_PIN, 220);

  delay(80);

  tone(BUZZER_PIN, 280);

  delay(80);

  noTone(BUZZER_PIN);

  delay(120);

 

  // =================================================

  // FASE 3: Tagline "POWER METER" typing

  // =================================================

 

  const char *tagline = "POWER METER";

 

  for (int i = 1; i <= 11; i++)

  {

    oled.clearBuffer();

    oled.setFont(u8g2_font_7x13B_tr);

 

    oled.drawFrame(29, 8, 70, 22);

    int w1 = oled.getStrWidth(logo);

    oled.drawStr((128 - w1) / 2, 25, logo);

 

    char buf[12];

    strncpy(buf, tagline, i);

    buf[i] = '\0';

 

    int w2 = oled.getStrWidth(buf);

    oled.drawStr((128 - w2) / 2, 48, buf);

    oled.sendBuffer();

 

    tone(BUZZER_PIN, 350);

    delay(15);

    noTone(BUZZER_PIN);

    delay(60);

  }

 

  delay(150);

 

  tone(BUZZER_PIN, 160);

  delay(400);

  noTone(BUZZER_PIN);

  delay(150);

 

  // =================================================

  // FASE 4: Bar loading

  // =================================================

 

  oled.clearBuffer();

  oled.setFont(u8g2_font_7x13B_tr);

 

  int w1 = oled.getStrWidth(logo);

  int w2 = oled.getStrWidth(tagline);

 

  int barX = 14;

  int barY = 48;

  int barW = 100;

  int barH = 8;

 

  for (int fill = 0; fill <= barW - 2; fill += 4)

  {

    oled.clearBuffer();

 

    oled.drawFrame(29, 4, 70, 20);

    oled.drawStr((128 - w1) / 2, 19, logo);

    oled.drawStr((128 - w2) / 2, 36, tagline);

 

    oled.drawFrame(barX, barY, barW, barH);

    oled.drawBox(barX + 1, barY + 1, fill, barH - 2);

    oled.sendBuffer();

 

    tone(BUZZER_PIN, 400 + fill * 2);

    delay(8);

    noTone(BUZZER_PIN);

  }

 

  tone(BUZZER_PIN, 500);

  delay(100);

  noTone(BUZZER_PIN);

  delay(100);

 

  // =================================================

  // FASE 5: READY + Bass sweep + DOR

  // =================================================

 

  oled.clearBuffer();

  oled.setFont(u8g2_font_7x13B_tr);

 

  oled.drawFrame(29, 4, 70, 20);

  oled.drawStr((128 - w1) / 2, 19, logo);

 

  oled.drawFrame(barX, barY, barW, barH);

  oled.drawBox(barX + 1, barY + 1, barW - 2, barH - 2);

 

  oled.setFont(u8g2_font_6x10_tf);

  int w3 = oled.getStrWidth("READY");

  oled.drawStr((128 - w3) / 2, 38, "READY");

 

  oled.sendBuffer();

 

  // Bass sweep naik

  for (int f = 150; f <= 600; f += 50)

  {

    tone(BUZZER_PIN, f);

    delay(30);

  }

  noTone(BUZZER_PIN);

  delay(100);

 

  // DOR final

  tone(BUZZER_PIN, 100);

  delay(250);

  noTone(BUZZER_PIN);

  delay(400);

}

 

// =====================================================

// BUZZER ALARM SWR - 2 LEVEL + HYSTERESIS

// =====================================================

 

unsigned long lastBeep = 0;

bool buzzerState = false;

int alarmLevel = 0;

 

void updateBuzzer(float swr)

{

  // ---- Level dengan hysteresis ----

  if (alarmLevel == 0 && swr >= 1.20)      alarmLevel = 1;

  else if (alarmLevel == 1 && swr >= 1.50) alarmLevel = 2;

  else if (alarmLevel == 1 && swr <  1.15) alarmLevel = 0;

  else if (alarmLevel == 2 && swr <  1.40) alarmLevel = 1;

  else if (alarmLevel == 2 && swr <  1.15) alarmLevel = 0;

 

  // ---- Level 2 : cepat, 800 Hz ----

  if (alarmLevel == 2)

  {

    if (millis() - lastBeep >= 150)

    {

      lastBeep = millis();

      buzzerState = !buzzerState;

 

      if (buzzerState) tone(BUZZER_PIN, 800);

      else             noTone(BUZZER_PIN);

    }

    return;

  }

 

  // ---- Level 1 : lambat, 400 Hz ----

  if (alarmLevel == 1)

  {

    if (millis() - lastBeep >= 500)

    {

      lastBeep = millis();

      buzzerState = !buzzerState;

 

      if (buzzerState) tone(BUZZER_PIN, 400);

      else             noTone(BUZZER_PIN);

    }

    return;

  }

 

  // ---- Aman ----

  if (buzzerState)

  {

    noTone(BUZZER_PIN);

    buzzerState = false;

  }

}

 

// =====================================================

// OLED MAIN DISPLAY

// =====================================================

 

void displayMain(

  float fwdPower,

  float refPower,

  float returnLoss,

  float swr

)

{

  oled.clearBuffer();

 

  oled.setFont(u8g2_font_6x10_tf);

  oled.drawStr(0, 9, "SWR & POWER METER");

 

  char line[32];

 

  snprintf(line, sizeof(line), "FWD : %.2f W", fwdPower);

  oled.drawStr(0, 22, line);

 

  snprintf(line, sizeof(line), "REF : %.3f W", refPower);

  oled.drawStr(0, 34, line);

 

  if (returnLoss < 0)

  {

    oled.drawStr(0, 46, "RL  : ----");

  }

  else

  {

    snprintf(line, sizeof(line), "RL  : %.1f dB", returnLoss);

    oled.drawStr(0, 46, line);

  }

 

  snprintf(line, sizeof(line), "SWR:%.2f", swr);

  oled.drawStr(0, 58, line);

 

  // ---- SWR BAR ----

  int barX = 58;

  int barY = 51;

  int barW = 68;

  int barH = 10;

 

  oled.drawFrame(barX, barY, barW, barH);

 

  float maxSWR = 3.0;

  float normalized = (swr - 1.0) / (maxSWR - 1.0);

 

  if (normalized < 0) normalized = 0;

  if (normalized > 1) normalized = 1;

 

  int fillWidth = (int)((barW - 2) * normalized);

 

  if (fillWidth > 0)

  {

    oled.drawBox(barX + 1, barY + 1, fillWidth, barH - 2);

  }

 

  oled.sendBuffer();

}

 

// =====================================================

// DISPLAY HYSTERESIS

// =====================================================

 

float dispFwdPower   = -1.0;

float dispRefPower   = -1.0;

float dispSwr        = -1.0;

float dispReturnLoss = -999.0;

 

bool needUpdate(float oldV, float newV, float threshold)

{

  if (oldV < 0) return true;

  return fabs(newV - oldV) >= threshold;

}

 

bool powerChanged(float oldV, float newV)

{

  if (oldV < 0) return true;

  float thr = (newV > 5.0f) ? (newV * 0.01f) : 0.05f;

  return fabs(newV - oldV) >= thr;

}

 

// =====================================================

// SETUP

// =====================================================

 

void setup()

{

  Serial.begin(115200);

  delay(1000);

 

  // ---- ADC ----

  analogReadResolution(12);

 

#if defined(ADC_ATTEN_DB_11)

  analogSetPinAttenuation(FWD_PIN, ADC_ATTEN_DB_11);

  analogSetPinAttenuation(REF_PIN, ADC_ATTEN_DB_11);

  Serial.println("ADC atten: ADC_ATTEN_DB_11 (core v3.x)");

#else

  analogSetPinAttenuation(FWD_PIN, ADC_11db);

  analogSetPinAttenuation(REF_PIN, ADC_11db);

  Serial.println("ADC atten: ADC_11db (core v2.x)");

#endif

 

  // ---- BUZZER ----

  pinMode(BUZZER_PIN, OUTPUT);

  noTone(BUZZER_PIN);

  digitalWrite(BUZZER_PIN, LOW);

 

  // ---- OLED ----

  Wire.begin(OLED_SDA, OLED_SCL);

  oled.begin();

 

  // ---- STARTUP INTRO ----

  startupIntro();

 

  // ---- SERIAL INFO ----

  Serial.println();

  Serial.println("==========================================");

  Serial.println("      ESP32-C3 SWR POWER METER");

  Serial.println("      FINAL ELEGANT VERSION");

  Serial.println("==========================================");

  Serial.println();

 

  Serial.print("FWD ZERO ADC : ");

  Serial.println(FWD_ZERO_ADC, 1);

 

  Serial.print("REF ZERO ADC : ");

  Serial.println(REF_ZERO_ADC, 1);

  Serial.println();

 

  Serial.println("Median+Avg filter (9x3)    : ON");

  Serial.println("IIR alpha                  : 0.30");

  Serial.println("Display hysteresis         : ON");

  Serial.println("REF residual compensation  : ON");

  Serial.println("Startup intro (elegant)    : ON");

  Serial.println("SWR alarm 1.20 (400Hz)     : ON");

  Serial.println("SWR alarm 1.50 (800Hz)     : ON");

  Serial.println();

  Serial.println("Meter ready.");

  Serial.println();

}

 

// =====================================================

// LOOP

// =====================================================

 

unsigned long lastDisplay = 0;

unsigned long lastSerial  = 0;

 

const unsigned long DISPLAY_INTERVAL = 150;

const unsigned long SERIAL_INTERVAL  = 500;

const unsigned long LOOP_DELAY       = 20;

 

void loop()

{

  // ---- READ ADC ----

  float fwdRaw = readFilteredADC(FWD_PIN);

  float refRaw = readFilteredADC(REF_PIN);

 

  // ---- IIR ----

  fwdRaw = applyIIR(fwdRaw, iirFwd);

  refRaw = applyIIR(refRaw, iirRef);

 

  // ---- ADC VOLTAGE ----

  float fwdADCVoltage = adcToVoltage(fwdRaw);

  float refADCVoltage = adcToVoltage(refRaw);

 

  // ---- FWD COUPLER ----

  float fwdCoupler = getFwdCouplerVoltage(fwdRaw);

 

  // ---- RAW REF ----

  float rawRefCoupler = getRawRefCouplerVoltage(refRaw);

 

  // ---- RESIDUAL REF ----

  float residualRef = calculateResidualRef(fwdCoupler);

 

  // ---- EFFECTIVE REF ----

  float refCoupler = getEffectiveRefVoltage(

    rawRefCoupler,

    fwdCoupler

  );

 

  // ---- FWD POWER ----

  float fwdPower = voltageToPower(fwdCoupler);

 

  // ---- SWR ----

  float swr = calculateSWR(fwdCoupler, refCoupler);

 

  // ---- RETURN LOSS ----

  float returnLoss = calculateReturnLoss(fwdCoupler, refCoupler);

 

  // ---- REF POWER ----

  float refPower = calculateRefPower(

    fwdPower,

    fwdCoupler,

    refCoupler

  );

 

  // ---- BUZZER ----

  updateBuzzer(swr);

 

  // ---- DISPLAY HYSTERESIS ----

  bool changed = false;

 

  if (powerChanged(dispFwdPower, fwdPower))

  {

    dispFwdPower = fwdPower;

    changed = true;

  }

  if (powerChanged(dispRefPower, refPower))

  {

    dispRefPower = refPower;

    changed = true;

  }

  if (needUpdate(dispSwr, swr, 0.02f))

  {

    dispSwr = swr;

    changed = true;

  }

  if (needUpdate(dispReturnLoss, returnLoss, 0.2f))

  {

    dispReturnLoss = returnLoss;

    changed = true;

  }

 

  // ---- UPDATE OLED ----

  if (changed && millis() - lastDisplay >= DISPLAY_INTERVAL)

  {

    displayMain(

      dispFwdPower,

      dispRefPower,

      dispReturnLoss,

      dispSwr

    );

    lastDisplay = millis();

  }

 

  // ---- SERIAL MONITOR ----

#if DEBUG_SERIAL

  if (millis() - lastSerial >= SERIAL_INTERVAL)

  {

    lastSerial = millis();

 

    Serial.println();

    Serial.println("------------------------------------------");

 

    Serial.print("FWD RAW ADC     : ");

    Serial.println(fwdRaw, 1);

 

    Serial.print("FWD ADC         : ");

    Serial.print(fwdADCVoltage, 4);

    Serial.println(" V");

 

    Serial.print("FWD COUPLER     : ");

    Serial.print(fwdCoupler, 4);

    Serial.println(" V");

 

    Serial.print("FWD POWER       : ");

    Serial.print(fwdPower, 2);

    Serial.println(" W");

 

    Serial.println();

 

    Serial.print("REF RAW ADC     : ");

    Serial.println(refRaw, 1);

 

    Serial.print("REF ADC         : ");

    Serial.print(refADCVoltage, 4);

    Serial.println(" V");

 

    Serial.print("REF RAW COUPLER : ");

    Serial.print(rawRefCoupler, 4);

    Serial.println(" V");

 

    Serial.print("REF RESIDUAL    : ");

    Serial.print(residualRef, 4);

    Serial.println(" V");

 

    Serial.print("REF EFFECTIVE   : ");

    Serial.print(refCoupler, 4);

    Serial.println(" V");

 

    Serial.print("REF POWER       : ");

    Serial.print(refPower, 3);

    Serial.println(" W");

 

    Serial.println();

 

    Serial.print("SWR             : ");

    Serial.println(swr, 2);

 

    Serial.print("RETURN LOSS     : ");

    if (returnLoss < 0)

      Serial.println("----");

    else

    {

      Serial.print(returnLoss, 2);

      Serial.println(" dB");

    }

 

    Serial.print("ALARM LEVEL     : ");

    Serial.println(alarmLevel);

 

    Serial.println("------------------------------------------");

  }

#else

  if (millis() - lastSerial >= SERIAL_INTERVAL)

  {

    lastSerial = millis();

 

    Serial.print("FWD: ");

    Serial.print(fwdPower, 2);

    Serial.print(" W | REF: ");

    Serial.print(refPower, 3);

    Serial.print(" W | SWR: ");

    Serial.print(swr, 2);

    Serial.print(" | RL: ");

    if (returnLoss < 0)

      Serial.print("----");

    else

    {

      Serial.print(returnLoss, 1);

      Serial.print(" dB");

    }

    Serial.print(" | ALARM: ");

    Serial.println(alarmLevel);

  }

#endif

 

  // ---- LOOP DELAY ----

  delay(LOOP_DELAY);

}


Untuk pemesanan SWR :

WA : 085874064828