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













