Thursday, 6 June 2019
20m WSPR Beacon
Below is the software. Looks like the formatting has been affected when it was copied over. Auto format in the Arduino IDE works great!
*****************************************************************
#include <DDS.h>
#include <SoftwareSerial.h>
#define RESET 2
#define FQ_UD 3
#define W_CLK 4
#define DATA 5
#define HighPWR_LED 6
#define LowPWR_LED 7
#define TX_LED 8
#define Percent25_LED 9
#define Percent50_LED 10
#define Percent75_LED 11
#define Percent100_LED 12
#define SerialTX 13
#define SerialRX A0
#define Tone0_LED A1
#define Tone1_LED A2
#define Tone2_LED A3
#define Tone3_LED A4
const int GPS_BUFFER_SIZE = 200; // The maximum size allowed for an NMEA sentence.
char sentence[GPS_BUFFER_SIZE] = {0};
volatile int _sentenceIndex = 0;
volatile char character = 0;
volatile int SymbolIndex = 0;
volatile int TX = 0;
volatile int Power = 0;
volatile int _UTCMinutes, _UTCSeconds = 0;
volatile long freq = 0;
const byte LowPWR_WSPR_Data[] = {3, 1, 2, 0, 2, 2, 0, 2, 3, 2, 0, 0, 1, 1, 1, 0, 2, 2, 3, 2, 2, 3, 2, 3, 1, 3, 3, 2, 0, 2, 0, 2,
0, 2, 3, 2, 2, 3, 2, 1, 0, 0, 2, 0, 0, 0, 1, 0, 1, 3, 2, 2, 1, 3, 0, 3, 0, 2, 2, 1, 3, 0, 3, 0,
2, 0, 0, 3, 3, 2, 3, 2, 3, 2, 3, 0, 1, 0, 0, 1, 2, 2, 1, 2, 1, 3, 0, 0, 0, 3, 3, 0, 1, 0, 1, 2,
2, 2, 3, 0, 0, 0, 2, 0, 3, 0, 0, 3, 0, 0, 3, 3, 3, 2, 3, 1, 2, 0, 1, 3, 2, 3, 0, 0, 2, 1, 3, 1,
2, 0, 0, 2, 2, 3, 0, 1, 0, 2, 1, 3, 0, 0, 0, 2, 2, 0, 0, 3, 3, 0, 3, 0, 3, 3, 2, 2, 2, 1, 3, 2,
2, 0
};
const byte HighPWR_WSPR_Data[] = {3, 1, 2, 0, 2, 0, 0, 2, 3, 0, 0, 2, 1, 3, 1, 2, 2, 2, 3, 2, 2, 3, 2, 3, 1, 3, 3, 0, 0, 0, 0, 0,
0, 2, 3, 0, 2, 1, 2, 3, 0, 0, 2, 0, 0, 0, 1, 0, 1, 1, 2, 0, 1, 1, 0, 3, 0, 0, 2, 1, 3, 0, 3, 0,
2, 0, 0, 1, 3, 2, 3, 2, 3, 2, 3, 2, 1, 0, 0, 1, 2, 2, 1, 0, 1, 1, 0, 0, 0, 1, 3, 2, 1, 0, 1, 0,
2, 2, 3, 0, 0, 2, 2, 0, 3, 2, 0, 1, 0, 2, 3, 1, 3, 0, 3, 1, 2, 2, 1, 3, 2, 1, 0, 2, 2, 3, 3, 1,
2, 0, 0, 2, 2, 1, 0, 3, 0, 2, 1, 1, 0, 0, 0, 2, 2, 0, 0, 3, 3, 2, 3, 0, 3, 1, 2, 0, 2, 3, 3, 0,
2, 0
};
// Create the objects
DDS dds(W_CLK, FQ_UD, DATA, RESET);
SoftwareSerial mySerial(SerialRX, SerialTX);
void setup()
{
// Initialize the AD9850
dds.init();
dds.trim(125000000);
dds.setFrequency(0);
// Initialize the Software serial port
mySerial.begin(38400);
pinMode(HighPWR_LED, OUTPUT);
pinMode(LowPWR_LED, OUTPUT);
pinMode(TX_LED, OUTPUT);
pinMode(Percent25_LED, OUTPUT);
pinMode(Percent50_LED, OUTPUT);
pinMode(Percent75_LED, OUTPUT);
pinMode(Percent100_LED, OUTPUT);
pinMode(Tone0_LED, OUTPUT);
pinMode(Tone1_LED, OUTPUT);
pinMode(Tone2_LED, OUTPUT);
pinMode(Tone3_LED, OUTPUT);
digitalWrite(HighPWR_LED, LOW);
digitalWrite(LowPWR_LED, LOW);
digitalWrite(TX_LED, LOW);
digitalWrite(Percent25_LED, LOW);
digitalWrite(Percent50_LED, LOW);
digitalWrite(Percent75_LED, LOW);
digitalWrite(Percent100_LED, LOW);
digitalWrite(Tone0_LED, LOW);
digitalWrite(Tone1_LED, LOW);
digitalWrite(Tone2_LED, LOW);
digitalWrite(Tone3_LED, LOW);
}
void loop()
{
if (TX == 0)
{
if (mySerial.available())
{
character = mySerial.read(); // Get a single character
sentence[_sentenceIndex] = character; // Append the current character to the sentence
_sentenceIndex++;
if (_sentenceIndex == GPS_BUFFER_SIZE) // Check for overrun of the sentence array. If so, restart
_sentenceIndex = 0;
if (character == '\r') // Carriage return. Look for RMC sentence
{
_sentenceIndex = 0; // Reset index counter ready for next timet
if ((sentence[4] == 'R') && (sentence[5] == 'M') && (sentence[6] == 'C'))
{
_UTCSeconds = (10 * (sentence[12] - 48) + (sentence[13] - 48));
_UTCMinutes = (10 * (sentence[10] - 48) + (sentence[11] - 48));
if ((_UTCMinutes % 2 == 0) && ((_UTCSeconds == 0) || (_UTCSeconds == 1) || (_UTCSeconds == 2)))
TX = 1;
}
}
}
}
if (TX == 1)
{
digitalWrite(TX_LED, HIGH); // Turn on the TX LED
freq = random(14097000, 14097200); // Select a random TX frequency
if (Power == 0)
for (SymbolIndex = 0; SymbolIndex <= 161; SymbolIndex++)
{
dds.setFrequency(freq + (LowPWR_WSPR_Data[SymbolIndex] * 1.4648)); // Change DDS freq
ToneLEDs(LowPWR_WSPR_Data[SymbolIndex]); // Update tone LEDs
ProgressLEDs(SymbolIndex); // Update progress LEDs
delay(683);
}
if (Power == 1)
for (SymbolIndex = 0; SymbolIndex <= 161; SymbolIndex++)
{
dds.setFrequency(freq + (HighPWR_WSPR_Data[SymbolIndex] * 1.4648)); // Change DDS freq
ToneLEDs(HighPWR_WSPR_Data[SymbolIndex]); // Update tone LEDs
ProgressLEDs(SymbolIndex); // Update progress LEDs
delay(683);
}
dds.setFrequency(0); // Turn off the DDS
ToneLEDs(4); // Turn off the tone LEDs
ProgressLEDs(0); // Turn off the progress LEDs
digitalWrite(TX_LED, LOW); // Turn off the TX LED
TX = 0;
Power = !Power; // Toggle transmit power
PowerLEDs(Power); // Toggle power level LED
}
}
void ToneLEDs(int x)
{
switch (x)
{
case 4:
digitalWrite(Tone0_LED, LOW);
digitalWrite(Tone1_LED, LOW);
digitalWrite(Tone2_LED, LOW);
digitalWrite(Tone3_LED, LOW);
break;
case 3:
digitalWrite(Tone0_LED, LOW);
digitalWrite(Tone1_LED, LOW);
digitalWrite(Tone2_LED, LOW);
digitalWrite(Tone3_LED, HIGH);
break;
case 2:
digitalWrite(Tone0_LED, LOW);
digitalWrite(Tone1_LED, LOW);
digitalWrite(Tone2_LED, HIGH);
digitalWrite(Tone3_LED, LOW);
break;
case 1:
digitalWrite(Tone0_LED, LOW);
digitalWrite(Tone1_LED, HIGH);
digitalWrite(Tone2_LED, LOW);
digitalWrite(Tone3_LED, LOW);
break;
case 0:
digitalWrite(Tone0_LED, HIGH);
digitalWrite(Tone1_LED, LOW);
digitalWrite(Tone2_LED, LOW);
digitalWrite(Tone3_LED, LOW);
break;
}
}
void ProgressLEDs(int x)
{
if (x <= 30)
{
digitalWrite(Percent25_LED, LOW);
digitalWrite(Percent50_LED, LOW);
digitalWrite(Percent75_LED, LOW);
digitalWrite(Percent100_LED, LOW);
}
if ((x > 30) && (x <= 70))
{
digitalWrite(Percent25_LED, LOW);
digitalWrite(Percent50_LED, LOW);
digitalWrite(Percent75_LED, LOW);
digitalWrite(Percent100_LED, HIGH);
}
if ((x > 70) && (x <= 110))
{
digitalWrite(Percent25_LED, LOW);
digitalWrite(Percent50_LED, LOW);
digitalWrite(Percent75_LED, HIGH);
digitalWrite(Percent100_LED, HIGH);
}
if ((x > 110) && (x <= 150))
{
digitalWrite(Percent25_LED, LOW);
digitalWrite(Percent50_LED, HIGH);
digitalWrite(Percent75_LED, HIGH);
digitalWrite(Percent100_LED, HIGH);
}
if (x > 150)
{
digitalWrite(Percent25_LED, HIGH);
digitalWrite(Percent50_LED, HIGH);
digitalWrite(Percent75_LED, HIGH);
digitalWrite(Percent100_LED, HIGH);
}
}
void PowerLEDs(int x)
{
switch (x)
{
case 0:
digitalWrite(LowPWR_LED, HIGH);
digitalWrite(HighPWR_LED, LOW);
break;
case 1:
digitalWrite(LowPWR_LED, LOW);
digitalWrite(HighPWR_LED, HIGH);
break;
}
}
*************************************************************
The link to the GENWSPR file is: http://www.g4jnt.com/JTModesBcns.htm
Saturday, 20 April 2019
Portable 20m SSB Rig
See YouTube for video log.
BPF
Ant Amplifier
IF Amp
#include <Wire.h>
#include <SPI.h>
#include <TM1637Display.h>
#include <si5351.h>
#include "LowPower.h"
const uint32_t bandStart = 14000000; // start of 20m
const uint32_t bandEnd = 14350000; // end of 20m
const uint32_t bandInit = 14100000; // where to initially set the frequency
volatile long currentfreq = 0;
volatile long oldfreq = 0;
volatile int currentmode = 0;
volatile int oldmode = 0;
volatile uint32_t freq = bandInit ; // this is a variable (changes) - set it to the beginning of the band
volatile uint32_t radix = 1000; // how much to change the frequency by, clicking the rotary encoder will change this.
const uint32_t BFO_freq = 8998450; // 8998450 = high side injection. For low side injection use 9001350;
// Rotary encoder pins and other inputs
static const int rotBPin = 2;
static const int rotAPin = 3;
static const int pushPin = 4;
static const int PTTInput = 8;
static const int brightnessPin = A3;
static const int tunespeedLED = A2;
static const int gnd = 10;
static const int vcc = 11;
static const int DIO = 12;
static const int CLK = 13;
// Rotary encoder variables, used by interrupt routines
volatile int rotState = 0;
volatile int rotAval = 1;
volatile int rotBval = 1;
volatile long remainder = 0;
volatile long OnesHz = 0;
volatile long TensHz = 0;
volatile long HundredsHz = 0;
volatile long OneskHz = 0;
volatile long TenskHz = 0;
volatile long HundredskHz = 0;
volatile long OnesMHz = 0;
volatile long TensMHz = 0;
volatile int Brightness = 3;
volatile int batterySave = 0;
// Instantiate the Objects
TM1637Display display(CLK, DIO); // CLK, DIO
Si5351 si5351;
void setup()
{
// Set up frequency and radix switches
pinMode(rotAPin, INPUT);
pinMode(rotBPin, INPUT);
pinMode(pushPin, INPUT);
pinMode(brightnessPin, INPUT);
pinMode(gnd, OUTPUT);
pinMode(tunespeedLED, OUTPUT);
pinMode(vcc, OUTPUT);
pinMode(PTTInput, INPUT);
// Set up pull-up resistors on inputs
digitalWrite(rotAPin, HIGH);
digitalWrite(rotBPin, HIGH);
digitalWrite(pushPin, HIGH);
digitalWrite(brightnessPin, HIGH);
digitalWrite(gnd, LOW);
digitalWrite(vcc, HIGH);
digitalWrite(tunespeedLED, LOW);
digitalWrite(PTTInput, LOW);
// Set up interrupt pins
attachInterrupt(digitalPinToInterrupt(rotAPin), ISRrotAChange, CHANGE);
attachInterrupt(digitalPinToInterrupt(rotBPin), ISRrotBChange, CHANGE);
// Initialize the display
display.setBrightness(Brightness, true);
UpdateDisplay();
delay(1000);
// Initialize the DDS
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
si5351.set_correction(87000, SI5351_PLL_INPUT_XO); // Set to specific Si5351 calibration number
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_2MA);
si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_2MA);
si5351.set_freq((freq * 100ULL), SI5351_CLK0);
si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK2);
}
void loop()
{
LowPower.idle(SLEEP_60MS, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_ON, SPI_OFF, USART0_OFF, TWI_OFF);
currentmode = digitalRead(PTTInput);
if (currentmode != oldmode)
{
SendFrequency();
oldmode = currentmode;
}
currentfreq = getfreq(); // Interrupt safe method to get the current frequency
if (currentfreq != oldfreq)
{
UpdateDisplay();
SendFrequency();
oldfreq = currentfreq;
}
if (digitalRead(brightnessPin) == LOW)
{
Brightness--;
display.setBrightness(Brightness, true);
if (Brightness == -1)
{
display.setBrightness(0, false);
digitalWrite(tunespeedLED, LOW);
batterySave = 1;
}
if (Brightness == -2)
{
Brightness = 3;
batterySave = 0;
}
UpdateDisplay();
delay(500);
}
if ((radix == 100) && (batterySave == 0))
digitalWrite(tunespeedLED, HIGH);
if (radix == 1000)
digitalWrite(tunespeedLED, LOW);
}
void wakeUp()
{
// Just a handler for the sleep pin interrupt.
}
long getfreq()
{
long temp_freq;
cli();
temp_freq = freq;
sei();
return temp_freq;
}
// Interrupt routines
void ISRrotAChange()
{
if (digitalRead(rotAPin))
{
rotAval = 1;
UpdateRot();
}
else
{
rotAval = 0;
UpdateRot();
}
}
void ISRrotBChange()
{
if (digitalRead(rotBPin))
{
rotBval = 1;
UpdateRot();
}
else
{
rotBval = 0;
UpdateRot();
}
}
void UpdateRot()
{
switch (rotState)
{
case 0: // Idle state, look for direction
if (!rotBval)
rotState = 1; // CW 1
if (!rotAval)
rotState = 11; // CCW 1
break;
case 1: // CW, wait for A low while B is low
if (!rotBval)
{
if (!rotAval)
{
// either increment radixindex or freq
if (digitalRead(pushPin) == LOW)
{
if (radix == 1000)
radix = 100;
else if (radix == 100)
radix = 1000;
}
else
{
freq = (freq + radix);
if (freq > bandEnd)
freq = bandEnd;
}
rotState = 2; // CW 2
}
}
else if (rotAval)
rotState = 0; // It was just a glitch on B, go back to start
break;
case 2: // CW, wait for B high
if (rotBval)
rotState = 3; // CW 3
break;
case 3: // CW, wait for A high
if (rotAval)
rotState = 0; // back to idle (detent) state
break;
case 11: // CCW, wait for B low while A is low
if (!rotAval)
{
if (!rotBval)
{
// either decrement radixindex or freq
if (digitalRead(pushPin) == LOW)
{
if (radix == 100)
radix = 1000;
else if (radix == 1000)
radix = 100;
}
else
{
freq = (freq - radix);
if (freq < bandStart)
freq = bandStart;
}
rotState = 12; // CCW 2
}
}
else if (rotBval)
rotState = 0; // It was just a glitch on A, go back to start
break;
case 12: // CCW, wait for A high
if (rotAval)
rotState = 13; // CCW 3
break;
case 13: // CCW, wait for B high
if (rotBval)
rotState = 0; // back to idle (detent) state
break;
}
}
void UpdateDisplay()
{
TensMHz = freq / 10000000; // TensMHz = 12345678 / 10000000 = 1
remainder = freq - (TensMHz * 10000000); // remainder = 12345678 - 10000000 = 2345678
OnesMHz = remainder / 1000000; // OnesMhz = 2345678 / 1000000 = 2
remainder = remainder - (OnesMHz * 1000000); // remainder = 2345678 - (2 * 1000000) = 345678
HundredskHz = remainder / 100000; // HundredskHz = 345678 / 100000 = 3
remainder = remainder - (HundredskHz * 100000); // remainder = 345678 - (3 * 100000) = 45678
TenskHz = remainder / 10000; // TenskHz = 45678 / 10000 = 4
remainder = remainder - (TenskHz * 10000); // remainder = 45678 - (4 * 10000) = 5678
OneskHz = remainder / 1000; // OneskHz = 5678 / 1000 = 5
remainder = remainder - (OneskHz * 1000); // remainder = 5678 - (5 * 1000) = 678
HundredsHz = remainder / 100; // HundredsHz = 678 / 100 = 6
remainder = remainder - (HundredsHz * 100); // remainder = 678 - (6 * 100) = 78
TensHz = remainder / 10; // TensHz = 78 / 10 = 7
remainder = remainder - (TensHz * 10); // remainder = 78 - (7 * 10) = 8
OnesHz = remainder; // OnesHz = 8
display.showNumberDec(((1000 * HundredskHz) + ( 100 * TenskHz) + (10 * OneskHz) + HundredsHz), true);
}
void SendFrequency()
{
if (currentmode == 1) // Transmit
{
si5351.set_freq(((freq - BFO_freq + 50) * 100ULL), SI5351_CLK2);
si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK0);
}
else // Receive
{
si5351.set_freq(((freq - BFO_freq + 50) * 100ULL), SI5351_CLK0);
si5351.set_freq((BFO_freq * 100ULL), SI5351_CLK2);
}
}
Friday, 15 March 2019
Homebrew RF Power Amplifier
RF Power amplifier based on the W6JL amp. The intent is to use readily available parts in the first instant. These may be swapped out depending on how well they perform.
See YouTube for videos. https://www.youtube.com/channel/UCSNPW3_gzuMJcX_ErBZTv2g
The original circuit (minus the biasing RFCs)
My version of the bias network
The final amp has used a BN-43-202 for T1 (8:4) and a BN-43-3312 for T2 (1:3). I also left off the RFCs for the YouTube video. Adding the RFCs would increase the gain a little.
See YouTube for videos. https://www.youtube.com/channel/UCSNPW3_gzuMJcX_ErBZTv2g
The original circuit (minus the biasing RFCs)
My version of the bias network
The final amp has used a BN-43-202 for T1 (8:4) and a BN-43-3312 for T2 (1:3). I also left off the RFCs for the YouTube video. Adding the RFCs would increase the gain a little.
Friday, 8 March 2019
Homebrew Panadapter
Homebrew panadapter using a Teensy 3.5.
Test Code
#include "SPI.h"
#include "ILI9341_t3.h"
#include <si5351.h> // Si5351Jason library
#include <Audio.h>
//const int myInput = AUDIO_INPUT_MIC;
const int myInput = AUDIO_INPUT_LINEIN;
uint16_t WaterfallData[100][128] = {1};
int Gain = 50;
static const long bandInit = 9008450; // 8800000 8565000 to initially set the frequency. Was 9020000
volatile long freq = bandInit ;
// For optimized ILI9341_t3 library
#define TFT_DC 20
#define TFT_CS 21
#define TFT_RST 255 // 255 = unused, connect to 3.3V
#define TFT_MOSI 7
#define TFT_SCLK 14
#define TFT_MISO 12
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
Si5351 si5351; // Name for the Si5351 DDS
// Setup audio shield
AudioInputI2S audioInput;
AudioMixer4 InputAmp;
AudioAnalyzeFFT256 FFT;
// Setup the audio connections
AudioConnection patchCord1(audioInput, 0, InputAmp, 0);
AudioConnection patchCord2(InputAmp, 0, FFT, 0);
// Instantiate the Audio Shield
AudioControlSGTL5000 audioShield;
void setup()
{
Serial.begin(9600);
// Setup screen
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
tft.drawRect(31, 0, 257, 37, ILI9341_YELLOW);
tft.drawRect(31, 36, 257, 103, ILI9341_YELLOW);
tft.drawRect(31, 138, 257, 102, ILI9341_YELLOW);
// Setup audio shield.
AudioMemory(12);
audioShield.enable();
audioShield.inputSelect(myInput);
InputAmp.gain(0, Gain);
FFT.windowFunction(AudioWindowHanning256);
FFT.averageTogether(30);
// Setup the DDS
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_8MA);
si5351.set_freq(freq * 100ULL, SI5351_CLK1);
}
void loop()
{
if (FFT.available())
UpdateDisplay();
if (Serial.available() > 0)
{
char c = Serial.read();
switch (c)
{
case 'w': Gain = Gain + 10; break;
case 's': Gain = Gain - 10; break;
}
Serial.println(Gain);
InputAmp.gain(0, Gain);
}
}
void UpdateDisplay()
{
int bar = 0;
int xPos = 0;
int low = 0;
// Spectrum
for (int x = 0; x <= 127; x++)
{
WaterfallData[0][x] = abs(FFT.output[x]);
bar = WaterfallData[0][x];
if (bar > 100)
bar = 100;
tft.drawFastVLine(32 + (xPos * 2), 138 - bar, bar, ILI9341_GREEN); //draw green bar
tft.drawFastVLine(32 + (xPos * 2), 38, 100 - bar, ILI9341_BLACK); //finish off with black to the top of the screen
xPos++;
}
// Waterfall
for (int row = 99; row >= 0; row--)
for (int col = 0; col <= 127; col++)
{
WaterfallData[row][col] = WaterfallData[row - 1][col];
if (WaterfallData[row][col] >= low + 75)
tft.drawPixel(32 + (col * 2), 139 + row, ILI9341_RED);
else if ((WaterfallData[row][col] >= low + 50) && (WaterfallData[row][col] < low + 75))
tft.drawPixel(32 + (col * 2), 139 + row, ILI9341_MAGENTA);
else if ((WaterfallData[row][col] >= low + 30) && (WaterfallData[row][col] < low + 50))
tft.drawPixel(32 + (col * 2), 139 + row, ILI9341_YELLOW);
else if ((WaterfallData[row][col] >= low + 20) && (WaterfallData[row][col] < low + 30))
tft.drawPixel(32 + (col * 2), 139 + row, ILI9341_BLUE);
else if (WaterfallData[row][col] < low + 20)
tft.drawPixel(32 + (col * 2), 139 + row, ILI9341_BLACK);
}
}
void SendFrequency()
{
si5351.set_freq(freq * 100ULL, SI5351_CLK1);
}
Test Code
#include "SPI.h"
#include "ILI9341_t3.h"
#include <si5351.h> // Si5351Jason library
#include <Audio.h>
//const int myInput = AUDIO_INPUT_MIC;
const int myInput = AUDIO_INPUT_LINEIN;
uint16_t WaterfallData[100][128] = {1};
int Gain = 50;
static const long bandInit = 9008450; // 8800000 8565000 to initially set the frequency. Was 9020000
volatile long freq = bandInit ;
// For optimized ILI9341_t3 library
#define TFT_DC 20
#define TFT_CS 21
#define TFT_RST 255 // 255 = unused, connect to 3.3V
#define TFT_MOSI 7
#define TFT_SCLK 14
#define TFT_MISO 12
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO);
Si5351 si5351; // Name for the Si5351 DDS
// Setup audio shield
AudioInputI2S audioInput;
AudioMixer4 InputAmp;
AudioAnalyzeFFT256 FFT;
// Setup the audio connections
AudioConnection patchCord1(audioInput, 0, InputAmp, 0);
AudioConnection patchCord2(InputAmp, 0, FFT, 0);
// Instantiate the Audio Shield
AudioControlSGTL5000 audioShield;
void setup()
{
Serial.begin(9600);
// Setup screen
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
tft.drawRect(31, 0, 257, 37, ILI9341_YELLOW);
tft.drawRect(31, 36, 257, 103, ILI9341_YELLOW);
tft.drawRect(31, 138, 257, 102, ILI9341_YELLOW);
// Setup audio shield.
AudioMemory(12);
audioShield.enable();
audioShield.inputSelect(myInput);
InputAmp.gain(0, Gain);
FFT.windowFunction(AudioWindowHanning256);
FFT.averageTogether(30);
// Setup the DDS
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.drive_strength(SI5351_CLK1, SI5351_DRIVE_8MA);
si5351.set_freq(freq * 100ULL, SI5351_CLK1);
}
void loop()
{
if (FFT.available())
UpdateDisplay();
if (Serial.available() > 0)
{
char c = Serial.read();
switch (c)
{
case 'w': Gain = Gain + 10; break;
case 's': Gain = Gain - 10; break;
}
Serial.println(Gain);
InputAmp.gain(0, Gain);
}
}
void UpdateDisplay()
{
int bar = 0;
int xPos = 0;
int low = 0;
// Spectrum
for (int x = 0; x <= 127; x++)
{
WaterfallData[0][x] = abs(FFT.output[x]);
bar = WaterfallData[0][x];
if (bar > 100)
bar = 100;
tft.drawFastVLine(32 + (xPos * 2), 138 - bar, bar, ILI9341_GREEN); //draw green bar
tft.drawFastVLine(32 + (xPos * 2), 38, 100 - bar, ILI9341_BLACK); //finish off with black to the top of the screen
xPos++;
}
// Waterfall
for (int row = 99; row >= 0; row--)
for (int col = 0; col <= 127; col++)
{
WaterfallData[row][col] = WaterfallData[row - 1][col];
if (WaterfallData[row][col] >= low + 75)
tft.drawPixel(32 + (col * 2), 139 + row, ILI9341_RED);
else if ((WaterfallData[row][col] >= low + 50) && (WaterfallData[row][col] < low + 75))
tft.drawPixel(32 + (col * 2), 139 + row, ILI9341_MAGENTA);
else if ((WaterfallData[row][col] >= low + 30) && (WaterfallData[row][col] < low + 50))
tft.drawPixel(32 + (col * 2), 139 + row, ILI9341_YELLOW);
else if ((WaterfallData[row][col] >= low + 20) && (WaterfallData[row][col] < low + 30))
tft.drawPixel(32 + (col * 2), 139 + row, ILI9341_BLUE);
else if (WaterfallData[row][col] < low + 20)
tft.drawPixel(32 + (col * 2), 139 + row, ILI9341_BLACK);
}
}
void SendFrequency()
{
si5351.set_freq(freq * 100ULL, SI5351_CLK1);
}
Saturday, 26 January 2019
Si5351 Quadrature Clock Output down to 3MHz
Credit to Brian Harper M1CEM and Miguel BartiƩ PY2OHH
Step 1. Edit si5351.h file. Change the SI5351_PLL_VCO_MIN to 380000000, i.e.,
#define SI5351_PLL_VCO_MIN 380000000
Step 2. Example code snippet:
volatile long freq = 3500000;
volatile int Even_Divisor = 0;
volatile int oldEven_Divisor = 0;
void EvenDivisor()
{
if (freq < 6850000)
{
Even_Divisor = 126;
}
if ((freq >= 6850000) && (freq < 9500000))
{
Even_Divisor = 88;
}
if ((freq >= 9500000) && (freq < 13600000))
{
Even_Divisor = 64;
}
if ((freq >= 13600000) && (freq < 17500000))
{
Even_Divisor = 44;
}
if ((freq >= 17500000) && (freq < 25000000))
{
Even_Divisor = 34;
}
if ((freq >= 25000000) && (freq < 36000000))
{
Even_Divisor = 24;
}
if ((freq >= 36000000) && (freq < 45000000)) {
Even_Divisor = 18;
}
if ((freq >= 45000000) && (freq < 60000000)) {
Even_Divisor = 14;
}
if ((freq >= 60000000) && (freq < 80000000)) {
Even_Divisor = 10;
}
if ((freq >= 80000000) && (freq < 100000000)) {
Even_Divisor = 8;
}
if ((freq >= 100000000) && (freq < 146600000)) {
Even_Divisor = 6;
}
if ((freq >= 150000000) && (freq < 220000000)) {
Even_Divisor = 4;
}
}
void SendFrequency()
{
EvenDivisor();
si5351.set_freq_manual(freq * SI5351_FREQ_MULT, Even_Divisor * freq * SI5351_FREQ_MULT, SI5351_CLK0);
si5351.set_freq_manual(freq * SI5351_FREQ_MULT, Even_Divisor * freq * SI5351_FREQ_MULT, SI5351_CLK2);
si5351.set_phase(SI5351_CLK0, 0);
si5351.set_phase(SI5351_CLK2, Even_Divisor);
if(Even_Divisor != oldEven_Divisor)
{
si5351.pll_reset(SI5351_PLLA);
oldEven_Divisor = Even_Divisor;
}
}
Step 1. Edit si5351.h file. Change the SI5351_PLL_VCO_MIN to 380000000, i.e.,
#define SI5351_PLL_VCO_MIN 380000000
Step 2. Example code snippet:
volatile long freq = 3500000;
volatile int Even_Divisor = 0;
volatile int oldEven_Divisor = 0;
void EvenDivisor()
{
if (freq < 6850000)
{
Even_Divisor = 126;
}
if ((freq >= 6850000) && (freq < 9500000))
{
Even_Divisor = 88;
}
if ((freq >= 9500000) && (freq < 13600000))
{
Even_Divisor = 64;
}
if ((freq >= 13600000) && (freq < 17500000))
{
Even_Divisor = 44;
}
if ((freq >= 17500000) && (freq < 25000000))
{
Even_Divisor = 34;
}
if ((freq >= 25000000) && (freq < 36000000))
{
Even_Divisor = 24;
}
if ((freq >= 36000000) && (freq < 45000000)) {
Even_Divisor = 18;
}
if ((freq >= 45000000) && (freq < 60000000)) {
Even_Divisor = 14;
}
if ((freq >= 60000000) && (freq < 80000000)) {
Even_Divisor = 10;
}
if ((freq >= 80000000) && (freq < 100000000)) {
Even_Divisor = 8;
}
if ((freq >= 100000000) && (freq < 146600000)) {
Even_Divisor = 6;
}
if ((freq >= 150000000) && (freq < 220000000)) {
Even_Divisor = 4;
}
}
void SendFrequency()
{
EvenDivisor();
si5351.set_freq_manual(freq * SI5351_FREQ_MULT, Even_Divisor * freq * SI5351_FREQ_MULT, SI5351_CLK0);
si5351.set_freq_manual(freq * SI5351_FREQ_MULT, Even_Divisor * freq * SI5351_FREQ_MULT, SI5351_CLK2);
si5351.set_phase(SI5351_CLK0, 0);
si5351.set_phase(SI5351_CLK2, Even_Divisor);
if(Even_Divisor != oldEven_Divisor)
{
si5351.pll_reset(SI5351_PLLA);
oldEven_Divisor = Even_Divisor;
}
}
Monday, 14 January 2019
Homebrew Power/SWR Meter
Please see YouTube for details:
https://www.youtube.com/channel/UCSNPW3_gzuMJcX_ErBZTv2g
This is my first idea for a directional coupler. Please note that this is not suppose to be a competition or commercial grade device. My aim is to keep the costs down, but still have a functional power/SWR meter at the end.
Note also the 51 ohm (below) termination resistor is now 150 ohm.
Final Software:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
volatile float FwdVoltage = 0;
volatile float RevVoltage = 0;
volatile float PWR_Cal = 0;
volatile float SWR_Cal = 0;
volatile float PWR = 0;
volatile float SWR = 0;
volatile float FwdVoltageReadings[10];
volatile float FwdVoltageAverage;
volatile float RevVoltageReadings[10];
volatile float RevVoltageAverage;
volatile int FwdNumberOfLines = 0;
volatile int RevNumberOfLines = 0;
volatile int FwdNumberOfLinesToBlank = 0;
volatile int RevNumberOfLinesToBlank = 0;
volatile int AvCount = 0;
volatile double oldPWR = 0;
volatile double oldSWR = 0;
// Instantiate the Objects
LiquidCrystal_I2C lcd(0x27, 16, 2);
//Custom bar characters
const byte Bar1Array[8] = {B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10000};
const byte Bar2Array[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11000};
const byte Bar3Array[8] = {B11100, B11100, B11100, B11100, B11100, B11100, B11100, B11100};
const byte Bar4Array[8] = {B11110, B11110, B11110, B11110, B11110, B11110, B11110, B11110};
const byte Bar5Array[8] = {B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111};
void setup()
{
pinMode(A0, INPUT); //Fwd power voltage pin
pinMode(A1, INPUT); //Rev power voltage pin
pinMode(A2, INPUT); //PWR cal pin
pinMode(A3, INPUT); //SWR cal pin
//analogReference(EXTERNAL);
Serial.begin(9600);
// Initialize the display
lcd.begin();
lcd.backlight();
lcd.createChar(1, Bar1Array); //Special charaters for the PWR and SWR bar display
lcd.createChar(2, Bar2Array);
lcd.createChar(3, Bar3Array);
lcd.createChar(4, Bar4Array);
lcd.createChar(5, Bar5Array);
UpdateDisplay();
}
void loop()
{
FwdVoltage = analogRead(A0);
RevVoltage = analogRead(A1);
PWR_Cal = analogRead(A2);
SWR_Cal = analogRead(A3);
FwdVoltageReadings[AvCount] = FwdVoltage; // Put fwd power reading into array
RevVoltageReadings[AvCount] = RevVoltage; // Put rev power reading into array
AvCount++;
if (AvCount == 9) // 0 to 9 = 10 bins
{
FwdVoltageAverage = 0;
RevVoltageAverage = 0;
for (int x = 0; x <= 9; x++)
FwdVoltageAverage = FwdVoltageAverage + FwdVoltageReadings[x];
FwdVoltageAverage = FwdVoltageAverage / 10; // Calc average
FwdVoltageAverage = FwdVoltageAverage - 136; // Subtract noise
FwdVoltageAverage = abs(FwdVoltageAverage); // Make absolute (remove any negative)
FwdVoltageAverage = FwdVoltageAverage * 5 / 1024; // covert into voltage
FwdVoltageAverage = FwdVoltageAverage + (PWR_Cal * 5 / 1024); // Cal to scope (no between 0 and 5)
for (int x = 0; x <= 9; x++)
RevVoltageAverage = RevVoltageAverage + RevVoltageReadings[x];
RevVoltageAverage = RevVoltageAverage / 10; // Calc average
RevVoltageAverage = RevVoltageAverage - 166; // Subtract noise
RevVoltageAverage = abs(RevVoltageAverage); // Make absolute (remove any negative)
RevVoltageAverage = RevVoltageAverage * 5 / 1024; // covert into voltage
// Lookup table for forward power
if (FwdVoltageAverage < 1.855)
PWR = 0;
else if ((FwdVoltageAverage >= 1.855) && (FwdVoltageAverage < 1.97))
PWR = 1;
else if ((FwdVoltageAverage >= 1.97) && (FwdVoltageAverage < 2.017))
PWR = 2;
else if ((FwdVoltageAverage >= 2.017) && (FwdVoltageAverage < 2.051))
PWR = 3;
else if ((FwdVoltageAverage >= 2.051) && (FwdVoltageAverage < 2.056))
PWR = 4;
else if ((FwdVoltageAverage >= 2.056) && (FwdVoltageAverage < 2.07))
PWR = 5;
else if ((FwdVoltageAverage >= 2.07) && (FwdVoltageAverage < 2.085))
PWR = 6;
else if ((FwdVoltageAverage >= 2.085) && (FwdVoltageAverage < 2.109))
PWR = 7;
else if ((FwdVoltageAverage >= 2.109) && (FwdVoltageAverage < 2.124))
PWR = 8;
else if ((FwdVoltageAverage >= 2.124) && (FwdVoltageAverage < 2.134))
PWR = 9;
else if ((FwdVoltageAverage >= 2.134) && (FwdVoltageAverage < 2.144))
PWR = 10;
else if ((FwdVoltageAverage >= 2.144) && (FwdVoltageAverage < 2.153))
PWR = 11;
else if ((FwdVoltageAverage >= 2.153) && (FwdVoltageAverage < 2.163))
PWR = 12;
else if ((FwdVoltageAverage >= 2.163) && (FwdVoltageAverage < 2.173))
PWR = 13;
else if ((FwdVoltageAverage >= 2.173) && (FwdVoltageAverage < 2.183))
PWR = 14;
else if ((FwdVoltageAverage >= 2.183) && (FwdVoltageAverage < 2.192))
PWR = 15;
else if ((FwdVoltageAverage >= 2.192) && (FwdVoltageAverage < 2.197))
PWR = 16;
else if ((FwdVoltageAverage >= 2.197) && (FwdVoltageAverage < 2.212))
PWR = 17;
else if ((FwdVoltageAverage >= 2.212) && (FwdVoltageAverage < 2.217))
PWR = 18;
else if ((FwdVoltageAverage >= 2.217) && (FwdVoltageAverage < 2.22))
PWR = 19;
// 20 - 24W
else if ((FwdVoltageAverage >= 2.220) && (FwdVoltageAverage < 2.246))
PWR = ((FwdVoltageAverage - 2.22) * 5 / .026) + 20;
// 25 - 29W
else if ((FwdVoltageAverage >= 2.246) && (FwdVoltageAverage < 2.266))
PWR = ((FwdVoltageAverage - 2.246) * 5 / .02) + 25;
// 30 - 34W
else if ((FwdVoltageAverage >= 2.266) && (FwdVoltageAverage < 2.280))
PWR = ((FwdVoltageAverage - 2.266) * 5 / .014) + 30;
// 35 - 39W
else if ((FwdVoltageAverage >= 2.280) && (FwdVoltageAverage < 2.300))
PWR = ((FwdVoltageAverage - 2.280) * 5 / .02) + 35;
// >= 40W
else if (FwdVoltage >= 2.3)
PWR = ((FwdVoltageAverage - 2.3) * 60 / .07) + 40;
if ((PWR > 0) && (PWR < 3))
FwdNumberOfLines = 1;
else
FwdNumberOfLines = PWR / 3.33; // Power bar. 100W / 30 segments = 3.33W per segment
if (PWR == 0)
SWR = 0;
else
{
SWR = (1 + sqrt(RevVoltageAverage / FwdVoltageAverage)) / (1 - sqrt(RevVoltageAverage / FwdVoltageAverage));
SWR = SWR / (SWR_Cal * 5 / 1024); // Cal to scope (no between 0 and 5)
}
RevNumberOfLines = SWR * 5; // One segment per SWR increment
AvCount = 0;
}
if ((PWR != oldPWR) || (SWR != oldSWR)) // Update display if PWR or SWR has changed
{
//SendTelemetryData();
UpdateDisplay();
oldPWR = PWR;
oldSWR = SWR;
}
}
void SendTelemetryData()
{
Serial.print("Foward Reading Average: ");
Serial.println(FwdVoltageAverage, 3);
Serial.print("Reverse Reading Average: ");
Serial.println(RevVoltageAverage, 3);
Serial.print("Power to Load: ");
Serial.println(PWR);
Serial.print("SWR: ");
Serial.println(SWR);
Serial.println("");
}
void UpdateDisplay()
{
//Display PWR
lcd.setCursor(0, 0);
lcd.print("PWR");
lcd.setCursor(3, 0);
lcd.print(" ");
lcd.setCursor(4, 0);
lcd.print(PWR, 0);
//Display SWR
lcd.setCursor(8, 0);
lcd.print("SWR");
lcd.setCursor(11, 0);
lcd.print(" ");
lcd.setCursor(12, 0);
lcd.print(SWR, 1);
//Draw PWR Bar
FwdNumberOfLinesToBlank = 29 - FwdNumberOfLines;
lcd.setCursor(0, 1);
lcd.print("P");
while (FwdNumberOfLines >= 5)
{
lcd.write(5);
FwdNumberOfLines = FwdNumberOfLines - 5;
}
if (FwdNumberOfLines == 1)
lcd.write(1);
if (FwdNumberOfLines == 2)
lcd.write(2);
if (FwdNumberOfLines == 3)
lcd.write(3);
if (FwdNumberOfLines == 4)
lcd.write(4);
while (FwdNumberOfLinesToBlank >= 5)
{
lcd.print(" ");
FwdNumberOfLinesToBlank = FwdNumberOfLinesToBlank - 5;
}
// Blank sedment before 'S'
lcd.setCursor(7, 1);
lcd.print(" ");
//Draw SWR Bar
RevNumberOfLinesToBlank = 29 - RevNumberOfLines;
lcd.setCursor(8, 1);
lcd.print("S");
while (RevNumberOfLines >= 5)
{
lcd.write(5);
RevNumberOfLines = RevNumberOfLines - 5;
}
if (RevNumberOfLines == 1)
lcd.write(1);
if (RevNumberOfLines == 2)
lcd.write(2);
if (RevNumberOfLines == 3)
lcd.write(3);
if (RevNumberOfLines == 4)
lcd.write(4);
while (RevNumberOfLinesToBlank >= 5)
{
lcd.print(" ");
RevNumberOfLinesToBlank = RevNumberOfLinesToBlank - 5;
}
}
https://www.youtube.com/channel/UCSNPW3_gzuMJcX_ErBZTv2g
This is my first idea for a directional coupler. Please note that this is not suppose to be a competition or commercial grade device. My aim is to keep the costs down, but still have a functional power/SWR meter at the end.
Note also the 51 ohm (below) termination resistor is now 150 ohm.
Tandem coupler experiments. This is looking very promising with a wide frequency and power range.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
volatile float FwdVoltage = 0;
volatile float RevVoltage = 0;
volatile float PWR_Cal = 0;
volatile float SWR_Cal = 0;
volatile float PWR = 0;
volatile float SWR = 0;
volatile float FwdVoltageReadings[10];
volatile float FwdVoltageAverage;
volatile float RevVoltageReadings[10];
volatile float RevVoltageAverage;
volatile int FwdNumberOfLines = 0;
volatile int RevNumberOfLines = 0;
volatile int FwdNumberOfLinesToBlank = 0;
volatile int RevNumberOfLinesToBlank = 0;
volatile int AvCount = 0;
volatile double oldPWR = 0;
volatile double oldSWR = 0;
// Instantiate the Objects
LiquidCrystal_I2C lcd(0x27, 16, 2);
//Custom bar characters
const byte Bar1Array[8] = {B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10000};
const byte Bar2Array[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11000};
const byte Bar3Array[8] = {B11100, B11100, B11100, B11100, B11100, B11100, B11100, B11100};
const byte Bar4Array[8] = {B11110, B11110, B11110, B11110, B11110, B11110, B11110, B11110};
const byte Bar5Array[8] = {B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111};
void setup()
{
pinMode(A0, INPUT); //Fwd power voltage pin
pinMode(A1, INPUT); //Rev power voltage pin
pinMode(A2, INPUT); //PWR cal pin
pinMode(A3, INPUT); //SWR cal pin
//analogReference(EXTERNAL);
Serial.begin(9600);
// Initialize the display
lcd.begin();
lcd.backlight();
lcd.createChar(1, Bar1Array); //Special charaters for the PWR and SWR bar display
lcd.createChar(2, Bar2Array);
lcd.createChar(3, Bar3Array);
lcd.createChar(4, Bar4Array);
lcd.createChar(5, Bar5Array);
UpdateDisplay();
}
void loop()
{
FwdVoltage = analogRead(A0);
RevVoltage = analogRead(A1);
PWR_Cal = analogRead(A2);
SWR_Cal = analogRead(A3);
FwdVoltageReadings[AvCount] = FwdVoltage; // Put fwd power reading into array
RevVoltageReadings[AvCount] = RevVoltage; // Put rev power reading into array
AvCount++;
if (AvCount == 9) // 0 to 9 = 10 bins
{
FwdVoltageAverage = 0;
RevVoltageAverage = 0;
for (int x = 0; x <= 9; x++)
FwdVoltageAverage = FwdVoltageAverage + FwdVoltageReadings[x];
FwdVoltageAverage = FwdVoltageAverage / 10; // Calc average
FwdVoltageAverage = FwdVoltageAverage - 136; // Subtract noise
FwdVoltageAverage = abs(FwdVoltageAverage); // Make absolute (remove any negative)
FwdVoltageAverage = FwdVoltageAverage * 5 / 1024; // covert into voltage
FwdVoltageAverage = FwdVoltageAverage + (PWR_Cal * 5 / 1024); // Cal to scope (no between 0 and 5)
for (int x = 0; x <= 9; x++)
RevVoltageAverage = RevVoltageAverage + RevVoltageReadings[x];
RevVoltageAverage = RevVoltageAverage / 10; // Calc average
RevVoltageAverage = RevVoltageAverage - 166; // Subtract noise
RevVoltageAverage = abs(RevVoltageAverage); // Make absolute (remove any negative)
RevVoltageAverage = RevVoltageAverage * 5 / 1024; // covert into voltage
// Lookup table for forward power
if (FwdVoltageAverage < 1.855)
PWR = 0;
else if ((FwdVoltageAverage >= 1.855) && (FwdVoltageAverage < 1.97))
PWR = 1;
else if ((FwdVoltageAverage >= 1.97) && (FwdVoltageAverage < 2.017))
PWR = 2;
else if ((FwdVoltageAverage >= 2.017) && (FwdVoltageAverage < 2.051))
PWR = 3;
else if ((FwdVoltageAverage >= 2.051) && (FwdVoltageAverage < 2.056))
PWR = 4;
else if ((FwdVoltageAverage >= 2.056) && (FwdVoltageAverage < 2.07))
PWR = 5;
else if ((FwdVoltageAverage >= 2.07) && (FwdVoltageAverage < 2.085))
PWR = 6;
else if ((FwdVoltageAverage >= 2.085) && (FwdVoltageAverage < 2.109))
PWR = 7;
else if ((FwdVoltageAverage >= 2.109) && (FwdVoltageAverage < 2.124))
PWR = 8;
else if ((FwdVoltageAverage >= 2.124) && (FwdVoltageAverage < 2.134))
PWR = 9;
else if ((FwdVoltageAverage >= 2.134) && (FwdVoltageAverage < 2.144))
PWR = 10;
else if ((FwdVoltageAverage >= 2.144) && (FwdVoltageAverage < 2.153))
PWR = 11;
else if ((FwdVoltageAverage >= 2.153) && (FwdVoltageAverage < 2.163))
PWR = 12;
else if ((FwdVoltageAverage >= 2.163) && (FwdVoltageAverage < 2.173))
PWR = 13;
else if ((FwdVoltageAverage >= 2.173) && (FwdVoltageAverage < 2.183))
PWR = 14;
else if ((FwdVoltageAverage >= 2.183) && (FwdVoltageAverage < 2.192))
PWR = 15;
else if ((FwdVoltageAverage >= 2.192) && (FwdVoltageAverage < 2.197))
PWR = 16;
else if ((FwdVoltageAverage >= 2.197) && (FwdVoltageAverage < 2.212))
PWR = 17;
else if ((FwdVoltageAverage >= 2.212) && (FwdVoltageAverage < 2.217))
PWR = 18;
else if ((FwdVoltageAverage >= 2.217) && (FwdVoltageAverage < 2.22))
PWR = 19;
// 20 - 24W
else if ((FwdVoltageAverage >= 2.220) && (FwdVoltageAverage < 2.246))
PWR = ((FwdVoltageAverage - 2.22) * 5 / .026) + 20;
// 25 - 29W
else if ((FwdVoltageAverage >= 2.246) && (FwdVoltageAverage < 2.266))
PWR = ((FwdVoltageAverage - 2.246) * 5 / .02) + 25;
// 30 - 34W
else if ((FwdVoltageAverage >= 2.266) && (FwdVoltageAverage < 2.280))
PWR = ((FwdVoltageAverage - 2.266) * 5 / .014) + 30;
// 35 - 39W
else if ((FwdVoltageAverage >= 2.280) && (FwdVoltageAverage < 2.300))
PWR = ((FwdVoltageAverage - 2.280) * 5 / .02) + 35;
// >= 40W
else if (FwdVoltage >= 2.3)
PWR = ((FwdVoltageAverage - 2.3) * 60 / .07) + 40;
if ((PWR > 0) && (PWR < 3))
FwdNumberOfLines = 1;
else
FwdNumberOfLines = PWR / 3.33; // Power bar. 100W / 30 segments = 3.33W per segment
if (PWR == 0)
SWR = 0;
else
{
SWR = (1 + sqrt(RevVoltageAverage / FwdVoltageAverage)) / (1 - sqrt(RevVoltageAverage / FwdVoltageAverage));
SWR = SWR / (SWR_Cal * 5 / 1024); // Cal to scope (no between 0 and 5)
}
RevNumberOfLines = SWR * 5; // One segment per SWR increment
AvCount = 0;
}
if ((PWR != oldPWR) || (SWR != oldSWR)) // Update display if PWR or SWR has changed
{
//SendTelemetryData();
UpdateDisplay();
oldPWR = PWR;
oldSWR = SWR;
}
}
void SendTelemetryData()
{
Serial.print("Foward Reading Average: ");
Serial.println(FwdVoltageAverage, 3);
Serial.print("Reverse Reading Average: ");
Serial.println(RevVoltageAverage, 3);
Serial.print("Power to Load: ");
Serial.println(PWR);
Serial.print("SWR: ");
Serial.println(SWR);
Serial.println("");
}
void UpdateDisplay()
{
//Display PWR
lcd.setCursor(0, 0);
lcd.print("PWR");
lcd.setCursor(3, 0);
lcd.print(" ");
lcd.setCursor(4, 0);
lcd.print(PWR, 0);
//Display SWR
lcd.setCursor(8, 0);
lcd.print("SWR");
lcd.setCursor(11, 0);
lcd.print(" ");
lcd.setCursor(12, 0);
lcd.print(SWR, 1);
//Draw PWR Bar
FwdNumberOfLinesToBlank = 29 - FwdNumberOfLines;
lcd.setCursor(0, 1);
lcd.print("P");
while (FwdNumberOfLines >= 5)
{
lcd.write(5);
FwdNumberOfLines = FwdNumberOfLines - 5;
}
if (FwdNumberOfLines == 1)
lcd.write(1);
if (FwdNumberOfLines == 2)
lcd.write(2);
if (FwdNumberOfLines == 3)
lcd.write(3);
if (FwdNumberOfLines == 4)
lcd.write(4);
while (FwdNumberOfLinesToBlank >= 5)
{
lcd.print(" ");
FwdNumberOfLinesToBlank = FwdNumberOfLinesToBlank - 5;
}
// Blank sedment before 'S'
lcd.setCursor(7, 1);
lcd.print(" ");
//Draw SWR Bar
RevNumberOfLinesToBlank = 29 - RevNumberOfLines;
lcd.setCursor(8, 1);
lcd.print("S");
while (RevNumberOfLines >= 5)
{
lcd.write(5);
RevNumberOfLines = RevNumberOfLines - 5;
}
if (RevNumberOfLines == 1)
lcd.write(1);
if (RevNumberOfLines == 2)
lcd.write(2);
if (RevNumberOfLines == 3)
lcd.write(3);
if (RevNumberOfLines == 4)
lcd.write(4);
while (RevNumberOfLinesToBlank >= 5)
{
lcd.print(" ");
RevNumberOfLinesToBlank = RevNumberOfLinesToBlank - 5;
}
}
Friday, 11 January 2019
Homebrew 2 Tone Audio Oscillator
2 Tone oscillator. Based on two Wein bridge oscillators using LM358 op amps. See YouTube for detaails:
https://youtu.be/H3f-ex2S4Xw
Subscribe to:
Posts (Atom)