// This program is put together with clues from the raduino github listing,
// found was missing code and strange coding too, but listed here is my version
// for a voice ssb for lsb radio operation of the bitx40 radio.
//
// This version of the arduino controlled vfo software does not display the
// callsign of the operator as a label notice for QSO operation.
//
// However the lower display now illustrates a signal strength meter for the Rx signal.
// The blue wire fron the cable connector leading to the vfo tuning control, the
// blue wire is analog port (A6), which is to be now connected to the receivers AGC line. The
// maximum AGC voltage of my bitx40 is around 2Volts. Should the AGC voltage on your
// AGC line be different, then the variable "int scale_division = 15;" numerical
// value may need to be altered to fix in a meter range from "S1" to "9+++" on the
// available lower display space area. However do note that if your AGC voltage is greater
// than 5Volts, a resistor scale divider will need to be used to reduce the measuring AGC
// voltage to a maximum of 5Volts. This regard for a maximum AGC voltage of 5 Volts, the
// "int scale_division = 15;" numerical value should be as shown next "int scale_division = 85;".
//
// coding put together by Alastair GW0AJU
// https://www.hfsignals.com/index.php/bitx40-circuit-description/
// date: 12th June 2018
// updated 18th January 2019
// updated 20th January 2024
// updated 17th April 2025
//
#include <EEPROM.h>
#include <Wire.h>
#include <si5351.h>
Si5351 si5351;
#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9,10,11,12,13);
int address = 100;
int address_knob = 104;
int address_base = 108;
int address_first_boot = 120;
#define ANALOG_TUNING (A7)
#define signal_measurement (A6) // blue wire
//************* signal meter variables *********
int x_start_rx = 0;
int x_rx = 0;
int rx_bar_signal_value = 0;
int plot_rx = 0;
int signal_meter = 0;
int scale_division = 15;
//************ vfo tuning variables *************
long crystal_drift = 50; // bfo crystal is high by the "+" amount
unsigned long baseTune = 7090000; // first off boot up value
unsigned long bfo_freq = (11998000 - crystal_drift); // drift cancelled by the minus calculation
long pwr_up_forty_metre = 7090000; // 40m band SSB QRP Centre of Activity 7,090 kHz int old_knob = 0;
int change_knob = 0;
int knob;
int old_knob;
#define LOWEST_FREQ (7000000)
#define HIGHEST_FREQ (7200000) // UK version
//#define HIGHEST_FREQ (7300000) // American version
long frequency;
long freq_display = 0;
unsigned long newFreq;
unsigned long old_freq = frequency;
long start_freq;
int check;
//This function will write a 4 byte (32bit) long to the eeprom at
//the specified address to address + 3.
void EEPROMWritelong(int address, long value)
{
//Decomposition from a long to 4 bytes by using bitshift.
//One = Most significant -> Four = Least significant byte
byte four = (value & 0xFF);
byte three = ((value >> 8) & 0xFF);
byte two = ((value >> 16) & 0xFF);
byte one = ((value >> 24) & 0xFF);
//Write the 4 bytes into the eeprom memory.
EEPROM.write(address, four);
EEPROM.write(address + 1, three);
EEPROM.write(address + 2, two);
EEPROM.write(address + 3, one);
}
//This function will return a 4 byte (32bit) long from the eeprom
//at the specified address to address + 3.
long EEPROMReadlong(long address)
{
//Read the 4 bytes from the eeprom memory.
long four = EEPROM.read(address);
long three = EEPROM.read(address + 1);
long two = EEPROM.read(address + 2);
long one = EEPROM.read(address + 3);
//Return the recomposed long by using bitshift.
return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}
void setup()
{
// Start serial and initialize the Si5351
analogReference(DEFAULT);
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, 0);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLA);
si5351.set_pll(SI5351_PLL_FIXED, SI5351_PLLB);
si5351.drive_strength(SI5351_CLK2, SI5351_DRIVE_6MA);
si5351.output_enable(SI5351_CLK0, 0);
si5351.output_enable(SI5351_CLK1, 0);
si5351.output_enable(SI5351_CLK2, 1);
si5351.update_status();
delay(10);
check = EEPROM.read(address_first_boot);
if (check == 1)
{
power_up();
}
if (check != 1)
{
first_up();
check = 1;
EEPROM.write(address_first_boot, check);
}
//int address_power_up = 125;
lcd.begin(16, 2);
lcd.setCursor(0,0);
lcd.print("bixt40 40m radio");
lcd.setCursor(0,1);
lcd.print("alastair GW0AJU");
delay(2000); // 2 second delay
lcd.setCursor(0,0);
lcd.print("setting up radio");
lcd.setCursor(0,1);
lcd.print("please waiting ");
delay(3000); // 3 second delay
lcd.clear();
lcd.home();
lcd.setCursor(0,0);
lcd.print("vfo:");
lcd.setCursor(13,0);
lcd.print("KHz");
lcd.setCursor(0,1);
lcd.print("Sig:");
setFrequency(frequency);
updateDisplay();
}
void power_up()
{
start_freq = EEPROMReadlong(address);
frequency = EEPROMReadlong(address);
knob = EEPROMReadlong(address_knob);
baseTune = EEPROMReadlong(address_base);
old_knob = knob;
}
void first_up()
{
EEPROMWritelong(address, pwr_up_forty_metre);
knob = analogRead(ANALOG_TUNING)-10;
baseTune = baseTune + (10 * change_knob);
frequency = baseTune;
old_knob = knob;
frequency = EEPROMReadlong(address);
EEPROMWritelong(address, frequency);
EEPROMWritelong(address_knob, knob);
EEPROMWritelong(address_base, baseTune);
}
void loop()
{
delay(50);
doTuning(); // tune vfo from variable resistance pot
plot_signal_rx(); // plot signal strength meter readings
}
void doTuning()
{
knob = analogRead(ANALOG_TUNING)-10;
// the knob is fully on the low end, move down by 10 Khz and wait for 200 msec
if (knob < 10 && frequency > LOWEST_FREQ)
{
baseTune = baseTune - 1000;
delay(100);
frequency = baseTune;
setFrequency(frequency);
updateDisplay();
EEPROMWritelong(address, frequency);
EEPROMWritelong(address_knob, knob);
EEPROMWritelong(address_base, baseTune);
}
// the knob is full on the high end, move up by 10 Khz and wait for 200 msec
else if (knob > 1010 && frequency < HIGHEST_FREQ)
{
baseTune = baseTune + 1000;
delay(100);
frequency = baseTune;
setFrequency(frequency);
updateDisplay();
EEPROMWritelong(address, frequency);
EEPROMWritelong(address_knob, knob);
EEPROMWritelong(address_base, baseTune);
}
// the tuning knob is at neither extremities, tune the signals as usual
else if (knob != old_knob)
{
change_knob = knob - old_knob; // if change_knob is +, then up band, if -, then down band
baseTune = baseTune + (10 * change_knob); // changed from 50Hz steps to 10Hz steps
frequency = baseTune;
old_knob = knob;
setFrequency(frequency);
updateDisplay();
EEPROMWritelong(address, frequency);
EEPROMWritelong(address_knob, knob);
EEPROMWritelong(address_base, baseTune);
}
}
void setFrequency(unsigned long f)
{
si5351.set_freq((bfo_freq - f) * 100ULL, SI5351_CLK2);
frequency = f;
si5351.update_status();
}
void updateDisplay()
{
freq_display = frequency;
lcd.home();
lcd.setCursor(5,0);
lcd.print(" ");
lcd.setCursor(5,0);
// lcd.print(knob);
lcd.print(freq_display/1E3,2);
}
//********************* Receiver bar graph plot *******************
void plot_signal_rx()
{
signal_meter = 0;
x_start_rx = 0;
rx_bar_signal_value = 0;
plot_rx = 0;
// analog port "0" is used for S meter wire to MC1360 agc lline via resistor voltage step down attenuator
rx_bar_signal_value = analogRead(signal_measurement);
if ( rx_bar_signal_value <= 10) // 10bit ADC setting
{
rx_bar_signal_value = 10.01; // error correction for maths and to overcome radio noise floor
}
signal_meter = (rx_bar_signal_value); // overcome ground signal noise
plot_rx = signal_meter / scale_division; // max analogue port value is 1024
x_start_rx = plot_rx; // to locate square plot x axis ( x_start_x is an integer variable )
plot_way_rx();
}
void plot_way_rx()
{
if ( x_rx > x_start_rx)
{
down_rx();
x_rx = x_rx - 1;
}
if ( x_rx < x_start_rx)
{
x_rx = x_rx + 1;
up_rx();
}
}
void up_rx() // plot block
{
if (x_rx == 1)
{
lcd.setCursor(x_rx + 3,1);
lcd.print("1");
}
else if (x_rx == 3)
{
lcd.setCursor(x_rx + 3,1);
lcd.print("3");
}
else if (x_rx == 5)
{
lcd.setCursor(x_rx + 3,1);
lcd.print("5");
}
else if (x_rx == 7)
{
lcd.setCursor(x_rx + 3,1);
lcd.print("7");
}
else if (x_rx == 9)
{
lcd.setCursor(x_rx + 3,1);
lcd.print("9");
}
else if (x_rx >= 10 && x_rx <= 12)
{
lcd.setCursor(x_rx + 3,1);
lcd.print("+");
}
else if ( (x_rx != 1) && (x_rx != 3) && (x_rx != 5) && (x_rx != 9) | (x_rx > 10) )
{
lcd.setCursor(x_rx + 3,1);
lcd.print("-");
}
}
void down_rx() // plot blank
{
lcd.setCursor(x_rx + 3,1);
lcd.print(" ");
}