#include <SPI.h>
#include <Wire.h> // I2C
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <math.h> // for sine drawing
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // OLED Reset pin or -1 if sharing Arduino reset pin
#define SCREEN_ADDRESS 0x3C // See datasheet for OLED Address; 0x3C for 128x32 and 128x64
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
/* Adafruit_SSD1306 display(OLED_RESET); */
/* Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); */
/* #include <Fonts/FreeSans12pt7b.h> */
uint16_t screen_redraw; // track screen redraws
// Encoder state structure
struct EncoderState {
int16_t value;
uint8_t increment; // stores fator of increment for wheel
int8_t delta; // stores last change in wheel value
uint8_t button; // gets clicked or double clicked
uint8_t click_cycle; // increases with button presses
uint8_t click_cycle_count; // count of states for button presses
uint8_t d_click_cycle; // increases with double button presses
uint8_t d_click_cycle_count; // count of states for double button presses
};
EncoderState encoder_state = {0, 1, 0, -1, 0, 0};
// 'sq2', 8x6px
const unsigned char sq2_bmp [] PROGMEM = {
0x06, 0x09, 0x09, 0x90, 0x90, 0x60, // 'sine, 8x6px
0x18, 0x18, 0x24, 0x24, 0x42, 0xc3, // 'tr, 8x6px
0x77, 0x55, 0x55, 0x55, 0x55, 0xdd, // 'sq1, 8x6px
0x3c, 0x24, 0x24, 0x24, 0x24, 0xe7// 'sq2, 8x6px
};
const unsigned char wave_bmp [] PROGMEM = {
// '1', 12x12px
0x00, 0xc0, 0x01, 0x20, 0x01, 0x20, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x84, 0x00, 0x84, 0x00,
0x84, 0x00, 0x48, 0x00, 0x48, 0x00, 0x30, 0x00,
// '2', 12x12px
0x3c, 0xf0, 0x24, 0x90, 0x24, 0x90, 0x24, 0x90, 0x24, 0x90, 0x24, 0x90, 0x24, 0x90, 0x24, 0x90,
0x24, 0x90, 0x24, 0x90, 0x24, 0x90, 0xe7, 0x90,
// '3', 12x12px
0x1f, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80,
0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0xf0, 0xf0,
// '4', 12x12px
0x06, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x09, 0x00, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80,
0x10, 0x80, 0x20, 0x40, 0x20, 0x40, 0xe0, 0x70
};
void setup() {
// Display init
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
// Display config
//display.setFont(&FreeSans12pt7b); // Set custom font
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.cp437(true); // Use full 256 char 'Code Page 437' font
Serial.begin(9600);
Serial.println("Setup complete");
//showSplash();
showStatic(); // start showing the initial wave
Serial.println("Back");
display.clearDisplay();
// textNormal("setup end");
display.display();
}
void showSplash() {
// Display welcome message for 2 seconds
display.clearDisplay();
display.setCursor(20, 5);
display.print("Signal Generator");
// Calculate the center and radius of the circle
int center_x = SCREEN_WIDTH / 2;
int center_y = SCREEN_HEIGHT / 2;
int radius = 12; // Half of the wave height
// Draw the circle (optional, for visualization)
display.drawCircle(center_x, center_y, radius+4, SSD1306_WHITE);
float step_size = 2.0 * PI / (2 * radius);
// Draw the sinusoidal wave
for (int x = center_x - radius; x <= center_x + radius; x++) {
int y = center_y + (int)(radius * sin((x - (center_x - radius)) * step_size));
display.drawPixel(x, y, SSD1306_WHITE);
}
display.display();
delay(500);
display.clearDisplay();
display.display();
}
// when changing freq, click changes increment ammount
// double click enters menu
void showStatic() {
encoder_state.value = 0;
showStaticInfo();
encoder_state.click_cycle = 0; // reset click
encoder_state.click_cycle_count = 2; // click has 3 states
encoder_state.d_click_cycle = 0; // reset d_click
encoder_state.d_click_cycle_count = 2; // d_click has 3 states
// stay here until goto menu is selected
while (1) { //encoder_state.button != CheapClickEncoder::DoubleClicked) {
// do a first info redraw when double clicked to show
// the selected menu
/* if (encoder_state.button != CheapClickEncoder::DoubleClicked) {
showStaticInfo();
}
*/
// handle the menu switching from the encoder?
// the wheel value should be blocked from menu to
// menu
// menu
switch (encoder_state.d_click_cycle) {
case 0: // frequency, change using wheel
// check encoder multiplier
switch (1) {
case 0:
encoder_state.increment = 1;
break;
case 1:
encoder_state.increment = 10;
break;
case 2:
encoder_state.increment = 100;
break;
}
// if encoder changes refresh screen and set new freq
if (0) {
showStaticInfo();
}
// Serial.println("asdkljkl");
break;
case 1: // channel, change using wheel
// should disable the button cycle count
// if encoder changes toggle channel and refresh screen
if (encoder_state.delta != 0) {
showStaticInfo();
}
break;
case 2: // wave form, change using wheel
if (encoder_state.delta != 0) {
showStaticInfo();
}
// case 3:
// break;
}
}
Serial.print("screen_redraw ");Serial.println(screen_redraw);
return;
}
// show the static wave screen
void showStaticInfo() {
++screen_redraw; // track screen redraws
display.clearDisplay();
display.setTextSize(1);
// display wave info
display.setCursor(0, 0);
display.print("ch ");
display.print(0);
// display.print(ad.getChannelTxt());
//invertIfSelectedAndPrint(0, 1);
/* display.setCursor(86, 0);
display.print("f+ ");
display.print(100);
*/
invertIconIfSelectedAndDraw(114, 0, wave_bmp, 12, 12);
display.setTextSize(2);
display.setTextSize(2);
display.setCursor(35, 16);
display.print(1440);
display.setCursor(50, 40);
display.print("Hz");
display.setCursor(80, 40);
display.write(0x12); // up down
// display.write(0x17); // up
// display.write(0x17); // down
display.setTextSize(1);
display.setCursor(110, 54);
display.print("100");
// return to menu
display.setCursor(0, 54);
display.write(0x1B);
display.display();
}
// Template function to display "selected" text
template <typename T>
void invertIfSelectedAndPrint(T text, uint8_t menu_id) {
if (encoder_state.d_click_cycle == menu_id) {
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw inverted text
}
display.print(text);
display.setTextColor(SSD1306_WHITE); // Draw white text
}
void invertIconIfSelectedAndDraw(
uint8_t x, uint8_t y, const unsigned char* bmp, uint8_t w, uint8_t h) {
bool isSelected = (encoder_state.d_click_cycle == 1);
// Set background and icon colors based on selection state
uint16_t bgColor = isSelected ? SSD1306_WHITE : SSD1306_BLACK;
uint16_t iconColor = isSelected ? SSD1306_BLACK : SSD1306_WHITE;
// Draw background rectangle
display.fillRect(x - 1, y - 1, w + 1, h + 1, bgColor);
// Draw the icon bitmap
display.drawBitmap(x, y, bmp, w, h, iconColor);
}
void loop() {
}