I asked Bing: ''How to get started with Arduino?'' You know, the artificial intelligence search engine from Microsoft.
Bing answered: "Hello, this is Bing. I’m glad you’re interested in Arduino.'' ... And then it went on at length about how to get started with Arduino. 😀
I have no idea what I'm doing, but I somehow managed to get a digital VFO up and running. The Arduino Uno controller board, and the SI-5351 frequency generator board mounted on a small piece of Vero board to keep things tidy. The OLED display and rotary encoder on the stand behind. It's amazing. Magic!

Note the creator/author: Julio Cesar
Original project page: 10kHz to 120MHz VFO / RF Generator with Si5351 and Arduino | Arduino Project Hub
The Arduino need to run some software in order for the thing to work. I really don't know the details of how the software works. BUT - carefully reading the comments in the program code, it is possible to figure out how to add IF offset and frequency coverage to the code.
The original code didn't work 'out of the box' with the rotary encoder, and display, and SI5351 module that I have used (chinese things), so I had to make some changes to the declarations. I'm posting the code below here.
Note: I have some changes. IF offset is set to 0Hz so it works as a signal generator. Frequency coverage is from 600kHz to 120MHz.
So - if anyone wants to try this, please follow the instructions on the original project pages. Try the original code first. Copy/paste the code into the Arduino IDE and upload it to your Arduino board.
But if you've got random chinese boards like me, and it doesn't work, you might give the code below a try.
/********************************************************************************************************
10kHz to 120MHz VFO / RF Generator with Si5351 and Arduino Nano, with Intermediate Frequency (IF)
offset (+ or -). See the schematics for wiring details. By J. CesarSound - ver 1.0 - Dec/2020.
*********************************************************************************************************/
//Libraries
#include <Wire.h> //IDE Standard
#include <Rotary.h> //Ben Buxton https://github.com/brianlow/Rotary
#include <si5351.h> //Etherkit https://github.com/etherkit/Si5351Arduino
#include <Adafruit_GFX.h> //Adafruit GFX https://github.com/adafruit/Adafruit-GFX-Library
//#include <Adafruit_SSD1306.h> //Adafruit SSD1306 https://github.com/adafruit/Adafruit_SSD1306
#include <Adafruit_SSD1306_EMULATOR.h>
//User preferences
//------------------------------------------------------------------------------------------------------------
#define IF 0 //Enter your IF frequency, ex: 455 = 455kHz, 10700 = 10.7MHz, 0 = to direct convert receiver or RF generator, + will add and - will subtract IF offfset.
#define FREQ_INIT 10000000 //Enter your initial frequency at startup, ex: 7000000 = 7MHz, 10000000 = 10MHz, 840000 = 840kHz.
#define XT_CAL_F 33000 //Si5351 calibration factor, adjust to get exatcly 10MHz. Increasing this value will decreases the frequency and vice versa.
#define tunestep A0 //Change the pin used by encoder push button if you want.
//------------------------------------------------------------------------------------------------------------
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define SCREEN_ADDRESS \
0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Rotary r = Rotary(2, 3);
//Adafruit_SSD1306 display = Adafruit_SSD1306(128, 32, &Wire);
Adafruit_SSD1306_EMULATOR display(SCREEN_WIDTH,
SCREEN_HEIGHT);
Si5351 si5351;
unsigned long freq = FREQ_INIT;
unsigned long freqold, fstep;
long interfreq = IF;
long cal = XT_CAL_F;
unsigned long long pll_freq = 90000000000ULL;
byte encoder = 1;
byte stp;
unsigned int period = 100; //millis display active
unsigned long time_now = 0; //millis display active
ISR(PCINT2_vect) {
char result = r.process();
if (result == DIR_CW) set_frequency(1);
else if (result == DIR_CCW) set_frequency(-1);
}
void set_frequency(short dir) {
if (encoder == 1) { //Up/Down frequency
if (dir == 1) freq = freq + fstep;
if (freq >= 120000000) freq = 120000000;
if (dir == -1) freq = freq - fstep;
if (fstep == 1000000 && freq <= 1000000) freq = 1000000;
else if (freq < 600000) freq = 600000;
}
}
void setup() {
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(WHITE);
display.display();
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(tunestep, INPUT_PULLUP);
statup_text();
si5351.init(SI5351_CRYSTAL_LOAD_8PF, 0, cal);
si5351.output_enable(SI5351_CLK0, 1); //1 - Enable / 0 - Disable CLK
si5351.output_enable(SI5351_CLK1, 0);
si5351.output_enable(SI5351_CLK2, 0);
si5351.drive_strength(SI5351_CLK0, SI5351_DRIVE_2MA); //Output current 2MA, 4MA, 6MA or 8MA
PCICR |= (1 << PCIE2);
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
sei();
stp = 3;
setstep();
layout();
displayfreq();
}
void loop() {
if (freqold != freq) {
time_now = millis();
tunegen();
freqold = freq;
}
if (digitalRead(tunestep) == LOW) {
time_now = (millis() + 300);
setstep();
delay(300);
}
if ((time_now + period) > millis()) {
displayfreq();
layout();
}
}
void tunegen() {
si5351.set_freq_manual((freq + (interfreq * 1000ULL)) * 100ULL, pll_freq, SI5351_CLK0);
}
void displayfreq() {
unsigned int m = freq / 1000000;
unsigned int k = (freq % 1000000) / 1000;
unsigned int h = (freq % 1000) / 1;
display.clearDisplay();
display.setTextSize(2);
char buffer[15] = "";
if (m < 1) {
display.setCursor(41, 1); sprintf(buffer, "%003d.%003d", k, h);
}
else if (m < 100) {
display.setCursor(5, 1); sprintf(buffer, "%2d.%003d.%003d", m, k, h);
}
else if (m >= 100) {
unsigned int h = (freq % 1000) / 10;
display.setCursor(5, 1); sprintf(buffer, "%2d.%003d.%02d", m, k, h);
}
display.print(buffer);
}
void setstep() {
switch (stp) {
case 1:
stp = 2;
fstep = 10;
break;
case 2:
stp = 3;
fstep = 100;
break;
case 3:
stp = 4;
fstep = 1000;
break;
case 4:
stp = 5;
fstep = 5000;
break;
case 5:
stp = 6;
fstep = 10000;
break;
case 6:
stp = 1;
fstep = 1000000;
break;
}
}
void layout() {
display.setTextColor(WHITE);
display.drawLine(0, 20, 127, 20, WHITE);
// display.drawLine(0, 43, 127, 43, WHITE);
// display.drawLine(105, 24, 105, 39, WHITE);
display.setTextSize(1);
display.setCursor(2, 25);
display.print("TS= ");
if (stp == 2) display.print("10Hz"); if (stp == 3) display.print("100Hz"); if (stp == 4) display.print("1k");
if (stp == 5) display.print("5k"); if (stp == 6) display.print("10k"); if (stp == 1) display.print("1M");
// display.setCursor(1, 48);
// display.print("IF:");
// display.print(interfreq);
// display.print("k");
display.setTextSize(1);
display.setCursor(110, 23);
if (freq < 1000000) display.print("kHz");
if (freq >= 1000000) display.print("MHz");
// display.setCursor(110, 33);
// if (interfreq == 0) display.print("VFO");
// if (interfreq != 0) display.print("L O");
display.display();
}
void statup_text() {
display.setTextSize(1);
display.setCursor(4, 5);
display.print("Si5351");
display.setCursor(4, 20);
display.print("VFO / RF Generator");
display.setCursor(4, 35);
display.print("Version 1.0");
display.setCursor(4, 50);
display.print(">> JCR RADIO <<");
display.display();
delay(3000);
display.clearDisplay();
}
Good job.
I want to try this - I have acquired all the bits including a Uno and several Nano's.
I fried the mainboard for my 3D printer, and having to adapt new marlin firmware to a new mainboard for my box of random parts 3d printer has really turned out to be ..... tedious.
I'm not a code person. I've already learned lots more about it than I wanted. But still not enough, apparently. I've considered giving up several times and just getting a store bought printer.
Win W5JAG
PS:
I would like to have the SI5351 generate a fixed frequency of 10240kHz on CLK1 to serve as a local oscillator for a double conversion setup with 10,7MHz first IF.
If anyone reading this know how to do that, I'd be very interested to try it out.