DIY SWR & POWER METER VHF 500 WATT 2N
KODE PROGRAM UCAPAN TERIMAKASIH :
#include
<U8g2lib.h>
#include
<Wire.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C
u8g2(U8G2_R0);
const int
buzzerPin = 4;
unsigned
long lastChange = 0;
unsigned
long lastMove = 0;
const
unsigned long moveInterval = 150;
int stage =
0;
int pupilX
= 0;
int
pupilDir = 1;
const int
maxPupilOffset = 6;
unsigned
long stageDurations[] = {3000, 2000, 2000, 5000}; // durasi tiap stage
int planeX
= -40;
int planeY
= 28;
bool
propellerToggle = false; // animasi
baling-baling
// Awan
Layer 1 (jauh)
const int
cloudCount1 = 2;
int
cloudX1[cloudCount1] = {130, 200};
int
cloudY1[cloudCount1] = {10, 18};
// Awan
Layer 2 (dekat)
const int
cloudCount2 = 2;
int
cloudX2[cloudCount2] = {160, 220};
int cloudY2[cloudCount2]
= {25, 32};
void
setup() {
pinMode(buzzerPin, OUTPUT);
u8g2.begin();
u8g2.setFont(u8g2_font_ncenB14_tr);
}
void loop()
{
unsigned long now = millis();
// Update animasi mata saat stage 0
if (stage == 0) {
if (now - lastMove > moveInterval) {
pupilX += pupilDir;
if (pupilX > maxPupilOffset || pupilX
< -maxPupilOffset) {
pupilDir = -pupilDir;
pupilX += pupilDir * 2;
}
lastMove = now;
}
}
// Update animasi pesawat & awan saat
stage 3
if (stage == 3 && now - lastMove >
moveInterval) {
planeX += 2;
if (planeX > 128) planeX = -40;
lastMove = now;
propellerToggle = !propellerToggle;
// Gerak awan layer 1 (lebih lambat)
for (int i = 0; i < cloudCount1; i++) {
cloudX1[i] -= 1;
if (cloudX1[i] < -30) cloudX1[i] = 130
+ random(20);
}
// Gerak awan layer 2 (lebih cepat)
for (int i = 0; i < cloudCount2; i++) {
cloudX2[i] -= 2;
if (cloudX2[i] < -30) cloudX2[i] = 130
+ random(30);
}
}
// Ganti stage tiap interval sesuai durasi
masing-masing
if (now - lastChange >
stageDurations[stage]) {
stage = (stage + 1) % 4;
lastChange = now;
playCallTone(stage);
}
u8g2.clearBuffer();
switch (stage) {
case 0:
drawRobotHeadWithMouthAndEyes(pupilX);
break;
case 1:
drawTextCenteredBold("TERIMAKASIH");
break;
case 2:
drawTextTwoLinesBold("ATAS",
"ORDERANYA");
break;
case 3:
drawPlane(planeX, planeY);
// Gambar awan layer 1 (jauh)
for (int i = 0; i < cloudCount1; i++)
{
drawCloud(cloudX1[i], cloudY1[i]);
}
// Gambar awan layer 2 (dekat)
for (int i = 0; i < cloudCount2; i++)
{
drawCloud(cloudX2[i], cloudY2[i]);
}
break;
}
u8g2.sendBuffer();
}
void
drawRobotHeadWithMouthAndEyes(int pupilOffsetX) {
u8g2.drawRFrame(0, 0, 128, 64, 12);
int eyeY = 25;
int leftEyeX = 40;
int rightEyeX = 88;
int eyeWidth = 40;
int eyeHeight = 28;
u8g2.drawEllipse(leftEyeX, eyeY, eyeWidth /
2, eyeHeight / 2);
u8g2.drawEllipse(rightEyeX, eyeY, eyeWidth /
2, eyeHeight / 2);
int pupilRadius = 8;
u8g2.drawDisc(leftEyeX + pupilOffsetX, eyeY,
pupilRadius);
u8g2.drawDisc(rightEyeX + pupilOffsetX, eyeY,
pupilRadius);
int mouthX = 32;
int mouthY = 48;
int mouthW = 64;
int mouthH = 12;
u8g2.drawRBox(mouthX, mouthY, mouthW, mouthH,
5);
u8g2.drawHLine(mouthX, mouthY + mouthH / 2,
mouthW);
}
void
drawTextCenteredBold(const char* text) {
u8g2.setFont(u8g2_font_ncenB10_tr); // font tebal 10 px
int16_t tw = u8g2.getStrWidth(text);
u8g2.setCursor((128 - tw) / 2, 30);
u8g2.print(text);
}
void
drawTextTwoLinesBold(const char* line1, const char* line2) {
u8g2.setFont(u8g2_font_ncenB10_tr);
int16_t tw1 = u8g2.getStrWidth(line1);
int16_t tw2 = u8g2.getStrWidth(line2);
u8g2.setCursor((128 - tw1) / 2, 20);
u8g2.print(line1);
u8g2.setCursor((128 - tw2) / 2, 44);
u8g2.print(line2);
}
void drawPlane(int
x, int y) {
// Badan utama
u8g2.drawBox(x, y, 24, 6);
// Hidung pesawat
u8g2.drawTriangle(x + 24, y, x + 32, y + 3, x
+ 24, y + 6);
// Sayap
u8g2.drawBox(x + 6, y - 6, 6, 6); // atas
u8g2.drawBox(x + 6, y + 6, 6, 6); // bawah
// Ekor horizontal & vertikal
u8g2.drawBox(x - 4, y + 1, 4, 2); //
horizontal
u8g2.drawBox(x, y - 5, 2, 5); // vertikal
// Baling-baling animasi
static bool propellerToggle = false;
propellerToggle = !propellerToggle;
if (propellerToggle) {
u8g2.drawDisc(x + 31, y + 3, 2); // mode
bulat
} else {
u8g2.drawLine(x + 30, y + 1, x + 32, y +
5); // silang
u8g2.drawLine(x + 32, y + 1, x + 30, y +
5);
}
// Asap knalpot
u8g2.drawTriangle(x - 6, y + 1, x - 4, y - 1,
x - 4, y + 5);
}
void
drawCloud(int x, int y) {
u8g2.drawCircle(x, y, 6, U8G2_DRAW_ALL);
u8g2.drawCircle(x + 6, y - 2, 5,
U8G2_DRAW_ALL);
u8g2.drawCircle(x + 12, y, 6, U8G2_DRAW_ALL);
}
void
playCallTone(int currentStage) {
int melody0[] = {1000, 1200, 1400, 1200,
1000};
int melody1[] = {600, 800, 600};
int melody2[] = {900, 1100, 900};
int melody3[] = {1300, 1500, 1700};
int duration = 150;
int* melody;
int length;
switch (currentStage) {
case 0:
melody = melody0;
length = sizeof(melody0) /
sizeof(melody0[0]);
break;
case 1:
melody = melody1;
length = sizeof(melody1) /
sizeof(melody1[0]);
break;
case 2:
melody = melody2;
length = sizeof(melody2) /
sizeof(melody2[0]);
break;
case
3:
melody = melody3;
length = sizeof(melody3) /
sizeof(melody3[0]);
break;
default:
return;
}
for (int i = 0; i < length; i++) {
tone(buzzerPin, melody[i], duration);
delay(duration + 50);
}
noTone(buzzerPin);
}
KODE SWR & POWER METER 500 WATT GEN2
KODE DENGAN DEBUG :
#include
<Wire.h>
#include
<Adafruit_GFX.h>
#include
<Adafruit_SSD1306.h>
#include
<math.h>
// Pengaturan OLED
#define
SCREEN_WIDTH 128
#define
SCREEN_HEIGHT 64
#define
OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH,
SCREEN_HEIGHT, &Wire,
OLED_RESET);
// Pengaturan pin
#define
FWD_PIN A0
#define
REF_PIN A1
#define
ALARM_PIN 4
// Buzzer pasif
// Variabel untuk alarm
unsigned
long lastAlarmToggle = 0;
bool
alarmState = false;
// Fungsi konversi tegangan ke daya
(dengan rentang terbaru)
float
voltageToPower(float
voltage) {
if
(voltage <= 0.2)
return 1;
else
if (voltage
<= 0.4) return
1 + (voltage
- 0.2) * (4.0
/ 0.2);
// 1–5W
else
if (voltage
<= 0.7) return
5 + (voltage
- 0.4) * (15.0
/ 0.3);
// 5–20W
else
if (voltage
<= 1.1) return
20 + (voltage
- 0.7) * (30.0
/ 0.4);
// 20–50W
else
if (voltage
<= 1.2) return
50 + (voltage
- 1.1) * (30.0
/ 0.1);
// 50–80W
else
if (voltage
<= 1.4) return
80 + (voltage
- 1.2) * (20.0
/ 0.2);
// 80–100W
else
if (voltage
<= 1.8) return
100 + (voltage
- 1.4) * (50.0
/ 0.4);
// 100–150W
else
if (voltage
<= 2.2) return
150 + (voltage
- 1.8) * (50.0
/ 0.4);
// 150–200W
else
if (voltage
<= 2.8) return
200 + (voltage
- 2.2) * (100.0
/ 0.6);
// 200–300W
else
if (voltage
<= 3.8) return
300 + (voltage
- 2.8) * (100.0
/ 1.0);
// 300–400W
else
if (voltage
<= 4.2) return
400 + (voltage
- 3.8) * (100.0
/ 0.4);
// 400–500W
else
return 500;
}
// Fungsi smoothing: rata-rata dari
beberapa pembacaan analog
float
readAveragedAnalog(int
pin, int
samples = 10)
{
long
total = 0;
for
(int
i = 0; i < samples; i++)
{
total += analogRead(pin);
delay(2);
// jeda kecil antar pembacaan
}
return
(float)total
/ samples;
}
void
setup()
{
Serial.begin(9600);
while
(!Serial);
// Tunggu hingga port serial siap
Serial.println(F("Inisialisasi
dimulai"));
pinMode(ALARM_PIN,
OUTPUT);
noTone(ALARM_PIN);
// Pastikan buzzer mati
if
(!display.begin(SSD1306_SWITCHCAPVCC,
0x3C)) {
Serial.println(F("OLED
gagal!"));
while
(1);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(15,
0);
display.println(F("SWR
& POWER METER"));
display.display();
delay(2000);
}
void
loop()
{
float
fwdRaw = readAveragedAnalog(FWD_PIN);
float
refRaw = readAveragedAnalog(REF_PIN);
float
vFwd = fwdRaw * (5.0
/ 1023.0);
float
vRef = refRaw * (5.0
/ 1023.0);
float
pFwd = voltageToPower(vFwd);
float
pRef = voltageToPower(vRef);
float
swr = 1.0;
if
(vFwd > 0.01
&& vRef < vFwd) {
float
ratio = vRef / vFwd;
float
sqrtRatio = sqrt(ratio);
if
(sqrtRatio > 0.99)
sqrtRatio = 0.99;
// batasi supaya denominator tidak nol
swr = (1
+ sqrtRatio) / (1
- sqrtRatio);
if
(swr > 99)
swr = 99;
}
// Kompensasi SWR agar tidak
melonjak drastis pada power besar
if
(pFwd > 20)
{ // threshold power 20 watt
float
faktor = 20.0 / pFwd;
if
(faktor < 0.2)
faktor = 0.2; // batas
minimal faktor
swr = 1.0
+ (swr - 1.0)
* faktor;
}
float
rl = 0;
if
(pFwd > 0.1
&& pRef > 0.01)
{
rl = -10.0
* log10(pRef
/ pFwd);
}
else {
rl = 99.0;
}
// Alarm berbunyi jika SWR
> 1.5
if
(swr > 1.5)
{
unsigned
long currentMillis = millis();
if
(currentMillis - lastAlarmToggle >=
500) {
// Alarm berdenyut
lastAlarmToggle
= currentMillis;
alarmState = !alarmState;
if
(alarmState)
{
tone(ALARM_PIN,
2000);
// Frekuensi 2kHz
}
else {
noTone(ALARM_PIN);
}
}
}
else {
noTone(ALARM_PIN);
alarmState = false;
}
// OLED Tampilkan data
display.clearDisplay();
display.setCursor(20,
0);
display.setTextSize(1);
display.print(F("SWR
& POWER METER"));
display.setCursor(0,
12);
display.setTextSize(1);
display.print(F("PWR
: ")); display.print(pFwd,
1); display.println(F("
W"));
display.setCursor(0,
22);
display.print(F("REF
: ")); display.print(pRef,
1); display.println(F("
W"));
display.setCursor(0,
32);
display.print(F("SWR
: ")); display.println(swr,
2);
display.setCursor(0,
42);
display.print(F("RL
: ")); display.print(rl,
1); display.println(F("
dB"));
// Bar SWR
int
barWidth = map(swr
* 100, 100,
300, 0,
128);
barWidth = constrain(barWidth,
0, 128);
display.fillRect(0,
56, barWidth, 6,
SSD1306_WHITE);
display.drawRect(0,
56, 128,
6, SSD1306_WHITE);
display.display();
delay(500);
}
0 comments:
Post a Comment