#include <Keypad.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {10, 9, 8, 7}; // Connect to Arduino pins 9,8,7,6
byte colPins[COLS] = {6, 5, 4, 3}; // Connect to Arduino pins 5,4,3,2
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
bool astable = true;
String params[3] = {"", "", ""}; // 0: R1 (or R in mono), 1: R2 (astable only), 2: C
int units[3] = {1, 1, 0}; // R: 0=Ω,1=kΩ,2=MΩ; C: 0=pF,1=nF,2=µF,3=code
String r_unit_strs[3] = {" ", "k", "M"};
String c_unit_strs[4] = {"pF", "nF", "uF", "code"};
int current_param = 0;
bool show_results = false;
unsigned long blink_timer = 0;
bool show_arrow = true;
void setup() {
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.display();
}
void loop() {
char key = keypad.getKey();
if (key) {
handle_key(key);
}
if (!show_results && (millis() - blink_timer > 500)) {
blink_timer = millis();
show_arrow = !show_arrow;
}
display.clearDisplay();
if (show_results) {
draw_results();
} else {
draw_edit_screen();
}
display.display();
}
void handle_key(char key) {
if (show_results) {
if (key == 'A' || key == 'B') {
show_results = false;
}
return;
}
// Edit screen handling
int max_param = astable ? 2 : 1;
if (key == 'A') { // Up
if (current_param > 0) {
current_param--;
} else {
current_param = max_param; // wrap around
}
}else if (key == 'B') { // Down
if (current_param < max_param) {
current_param++; // just move to next parameter
} else {
// Already at last field → show results
show_results = true;
}
} else if (key == 'C') { // Toggle units
if (current_param == 2) { // C
units[2] = (units[2] + 1) % 4;
} else {
units[current_param] = (units[current_param] + 1) % 3;
}
} else if (key == 'D') { // Toggle mode
astable = !astable;
current_param = 0; // Reset selection
} else if (key >= '0' && key <= '9') {
params[current_param] += key;
} else if (key == '*') {
if ((current_param != 2 || units[2] != 3) && params[current_param].indexOf('.') == -1) {
params[current_param] += '.';
}
} else if (key == '#') {
params[current_param] = "";
}
}
void draw_edit_screen() {
display.setCursor(0, 0);
display.println("555 calculator");
display.print(astable ? "Astable mode " : "Monostable mode");
display.println();
int y = 16; // Approximate, assuming text size 1 (~8px per line, plus spacing)
// R1 or R
display.setCursor(0, y);
display.print((current_param == 0 && show_arrow) ? ">" : " ");
display.print(astable ? "R1: " : "R: ");
display.print(params[0]);
display.print(" ");
display.println(r_unit_strs[units[0]]);
y += 8;
if (astable) {
// R2
display.setCursor(0, y);
display.print((current_param == 1 && show_arrow) ? ">" : " ");
display.print("R2: ");
display.print(params[1]);
display.print(" ");
display.println(r_unit_strs[units[1]]);
y += 8;
}
// C
display.setCursor(0, y);
display.print((current_param == (astable ? 2 : 1) && show_arrow) ? ">" : " ");
display.print("C: ");
display.print(params[2]);
if (units[2] != 3) {
display.print(" ");
display.println(c_unit_strs[units[2]]);
} else {
display.println(); // No unit for code
}
}
double parse_resistor(int idx) {
if (params[idx].length() == 0) return 0.0;
double val = atof(params[idx].c_str());
double mult = (units[idx] == 0) ? 1.0 : (units[idx] == 1) ? 1000.0 : 1000000.0;
return val * mult;
}
double parse_capacitor() {
if (params[2].length() == 0) return 0.0;
if (units[2] == 3) { // Code mode
long num = atol(params[2].c_str());
size_t digits = params[2].length();
if (digits <= 2) {
return num * 1e-12;
} else {
int exp = num % 10;
long mant = num / 10;
return mant * pow(10, exp) * 1e-12;
}
} else {
double val = atof(params[2].c_str());
double mult = (units[2] == 0) ? 1e-12 : (units[2] == 1) ? 1e-9 : 1e-6;
return val * mult;
}
}
void draw_results() {
double r1 = parse_resistor(0);
double r2 = astable ? parse_resistor(1) : 0.0;
double c = parse_capacitor();
if (c == 0.0 || (astable && (r1 + r2 == 0.0)) || (!astable && r1 == 0.0)) {
display.setCursor(0, 0);
display.println("Invalid values");
return;
}
display.setCursor(0, 0);
display.println("Results:");
if (astable) {
double freq = 1.44 / ((r1 + 2 * r2) * c);
double duty = (r1 + r2) / (r1 + 2 * r2) * 100.0;
double period = 1.0 / freq;
double t_high = 0.693 * (r1 + r2) * c;
double t_low = 0.693 * r2 * c;
display.print("Freq: ");
display.print(freq, 2);
display.println(" Hz");
display.print("Duty: ");
display.print(duty, 2);
display.println(" %");
display.print("Period: ");
display.print(period, 6);
display.println(" s");
display.print("T High: ");
display.print(t_high, 6);
display.println(" s");
display.print("T Low: ");
display.print(t_low, 6);
display.println(" s");
} else {
double pulse = 1.1 * r1 * c;
display.print("Pulse: ");
display.print(pulse, 6);
display.println(" s");
}
}