/*
oledTurbo.ino
Emulates a 386/486 era Turbo 7 segment display on an OLED display
Andrew Stephen, 2024
*/
#include <Wire.h>
#include <Pushbutton.h>
#include <U8g2lib.h>
#include "cpulogos.h"
#include "dseg7.h"
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// AB-PB4 motherboard manual
//
// Turbo button 3 pin heaader JW4
// ------------------------------
// 1: Turbo signal
// 2: Turbo signal
// 3: GND
//
// 1-2 Turbo mode
// 2-3 Slow mode
//
// Connect pin1 and pin2 to the chassis' turbo button cable
//
// Turbo LED 2 pin header JW3
// --------------------------
// 1: LED Anode
// 2: LED Cathode
//
// LED will light up when system is in Turbo speed mode, otherwise LED will be off
//
// Reset button 2 pin header JW5
// -----------------------------
// 1: Reset input
// 2: GND
//
// Connect to chassis reset button cable.
// Press and hold the reset button for at least 1 second to reset system
#define TURBO_LED 3 // Pin to sense turbo LED state
#define RESET_SW 2 // Pin to sense reset switch
#define RESET_PIN 8 // Pin to control motherboard reset
#define NTC_PIN A0 // NTC temperature sensor
#define VSENSE1 A1
#define VSENSE2 A2
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
Pushbutton button(RESET_SW);
const char speed[2][4] = { "Slo", "160" };
const float BETA = 3950;
// These are just made up for the Wokwi demo
//
const float v1adj = 0.25; // voltage divider factor ( R2 / R1+R2 )
const float v2adj = 0.25; // 10K, 3333.3333k
byte mode = 0;
bool modeChanged = false;
unsigned long buttonTimer = 0;
byte resetTriggered = 0;
void showVolts() {
int volts1value = analogRead(VSENSE1);
int volts2value = analogRead(VSENSE2);
static float prevV1 = 0.0;
static float prevV2 = 0.0;
float temp = 0.0;
float volts = 0.0;
char buf[6] = "";
// Serial.print(F("Volts: "));
// if (modeChanged || ) {
u8g2.clearBuffer();
u8g2.setDrawColor(1);
u8g2.setCursor(0,31);
temp = (volts1value * 5.0) / 1024;
volts = temp / v1adj;
if (volts < 0.01) {
volts=0.0;
}
prevV1 = volts;
dtostrf(volts,5,2,buf);
// Serial.print(volts,2);
// Serial.print(F(", "));
u8g2.setFont(u8g2_dseg7_26x31_mn);
u8g2.print(buf);
u8g2.drawXBMP(110, 8, V_width, V_height, V_bits);
temp = (volts2value * 5.0) / 1024;
volts = temp / v2adj;
if (volts < 0.01) {
volts=0.0;
}
prevV2 = volts;
dtostrf(volts,5,2,buf);
// Serial.println(volts,2);
u8g2.setFont(u8g2_dseg7_26x31_mn);
u8g2.setCursor(0,64);
u8g2.print(buf);
u8g2.drawXBMP(110, 40, V_width, V_height, V_bits);
u8g2.sendBuffer();
// }
}
void showTemp() {
static int prevTemp = 0;
// Only redraw if the temperature has changed
int analogValue = analogRead(A0);
float celsius = 1 / (log(1 / (1023. / analogValue - 1)) / BETA + 1.0 / 298.15) - 273.15;
int temp = round(celsius);
if (modeChanged || temp == prevTemp) {
// Serial.print(F("Temperature: "));
// Serial.println(celsius);
u8g2.setFont(u8g2_dseg7_32x40_mn);
u8g2.clearBuffer();
u8g2.setDrawColor(1);
u8g2.setCursor(0,52);
u8g2.print(temp);
u8g2.print(F("^C"));
u8g2.sendBuffer();
}
prevTemp = temp;
}
void showTurbo() {
char turbo[4];
bool turboState = LOW;
static bool prevState;
turboState = digitalRead(TURBO_LED);
// Only redraw if the turbo state has changed
if (modeChanged || prevState != turboState) {
if (turboState) {
strcpy(turbo,speed[1]);
}
else {
strcpy(turbo,speed[0]);
}
Serial.print(F("Turbo: "));
Serial.println(turbo);
u8g2.setFont(u8g2_dseg7_42x64_mn);
u8g2.clearBuffer();
u8g2.setDrawColor(1);
u8g2.setCursor(0,64);
u8g2.print(turbo);
u8g2.sendBuffer();
}
prevState = turboState;
}
void showLogo() {
if (modeChanged) {
u8g2.clearBuffer();
u8g2.drawXBMP(38, 0, am5x86_width, am5x86_height, am5x86_bits);
u8g2.sendBuffer();
}
}
void showPowerOnLogo() {
u8g2.clearBuffer();
u8g2.setDrawColor(0);
u8g2.drawXBMP(76, 0, am5x86_width, am5x86_height, am5x86_bits);
u8g2.sendBuffer();
u8g2.sendF("caaaaaac", 0x027, 0, 0, 0, 7, 0, 255, 0x2f); // Init and start scroll
delay(2400);
u8g2.sendF("c", 0x02e); //Stop scrolling
delay(2000);
u8g2.clearBuffer();
u8g2.sendBuffer();
}
void TriggerReset(const byte stage) {
// Change the display to say "Resetting..."
// Short the reset pins
// delay for some amount of time
// Release reset pins
// do something for a reset period of time
// Is therea way to tell if the MB has reset?
switch (stage) {
case 0: // start the reset process
digitalWrite(RESET_PIN, HIGH);
Serial.println(F("Resetting..."));
u8g2.clearBuffer();
u8g2.setDrawColor(1);
u8g2.drawDisc(64, 32, 28, U8G2_DRAW_UPPER_RIGHT);
u8g2.setDrawColor(0);
u8g2.drawDisc(64, 32, 20, U8G2_DRAW_UPPER_RIGHT);
u8g2.setFont(u8g2_font_UnnamedDOSFontIV_tr);
u8g2.setFontMode(1);
u8g2.setDrawColor(2);
u8g2.setCursor(18,38);
u8g2.print(F("Resetting"));
u8g2.sendBuffer();
break;
case 1:
u8g2.clearBuffer();
u8g2.setDrawColor(1);
u8g2.drawDisc(64, 32, 28, U8G2_DRAW_UPPER_RIGHT|U8G2_DRAW_LOWER_RIGHT);
u8g2.setDrawColor(0);
u8g2.drawDisc(64, 32, 20, U8G2_DRAW_UPPER_RIGHT|U8G2_DRAW_LOWER_RIGHT);
u8g2.setFont(u8g2_font_UnnamedDOSFontIV_tr);
u8g2.setFontMode(1);
u8g2.setDrawColor(2);
u8g2.setCursor(18,38);
u8g2.print(F("Resetting"));
u8g2.sendBuffer();
break;
case 2:
u8g2.clearBuffer();
u8g2.setDrawColor(1);
u8g2.drawDisc(64, 32, 28, U8G2_DRAW_UPPER_RIGHT|U8G2_DRAW_LOWER_RIGHT|U8G2_DRAW_LOWER_LEFT);
u8g2.setDrawColor(0);
u8g2.drawDisc(64, 32, 20, U8G2_DRAW_UPPER_RIGHT|U8G2_DRAW_LOWER_RIGHT|U8G2_DRAW_LOWER_LEFT);
u8g2.setFont(u8g2_font_UnnamedDOSFontIV_tr);
u8g2.setFontMode(1);
u8g2.setDrawColor(2);
u8g2.setCursor(18,38);
u8g2.print(F("Resetting"));
u8g2.sendBuffer();
break;
case 50:
Serial.println(F("Reset committed"));
u8g2.clearBuffer();
u8g2.setDrawColor(1);
u8g2.drawDisc(64, 32, 28, U8G2_DRAW_ALL);
u8g2.setDrawColor(0);
u8g2.drawDisc(64, 32, 20, U8G2_DRAW_ALL);
u8g2.setFont(u8g2_font_UnnamedDOSFontIV_tr);
u8g2.setFontMode(1);
u8g2.setDrawColor(2);
u8g2.setCursor(14,38);
u8g2.print(F("Rebooting..."));
u8g2.sendBuffer();
delay(1000);
digitalWrite(RESET_PIN, LOW);
delay(3000);
showPowerOnLogo();
break;
default: // abort the reset process
digitalWrite(RESET_PIN, LOW);
Serial.println(F("Cancelling reset"));
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_UnnamedDOSFontIV_tr);
u8g2.setFontMode(1);
u8g2.setDrawColor(2);
u8g2.setCursor(18,38);
u8g2.print(F("Cancelled"));
u8g2.sendBuffer();
delay(1500);
break;
}
}
void setup() {
pinMode(TURBO_LED, INPUT);
pinMode(RESET_PIN, OUTPUT);
digitalWrite(RESET_PIN, LOW);
pinMode(VSENSE1, INPUT);
pinMode(VSENSE2, INPUT);
Serial.begin(115200);
// initialize OLED I2C
u8g2.begin();
u8g2.clearBuffer();
u8g2.setDrawColor(0);
showPowerOnLogo();
modeChanged = true;
}
void loop() {
unsigned long nowTimer = 0;
if (button.getSingleDebouncedPress()) {
// The button was pressed, so start a timer
buttonTimer = millis();
Serial.print(F("Button pressed at "));
Serial.println(buttonTimer);
}
if (buttonTimer > 0) {
nowTimer = millis();
nowTimer -= buttonTimer;
if (nowTimer > 1000 && resetTriggered == 0) {
Serial.println(F("Reset triggered"));
TriggerReset(0);
resetTriggered = 1;
}
else if (resetTriggered == 1 && nowTimer > 1750 && nowTimer <= 2500) {
TriggerReset(1);
resetTriggered = 2;
}
else if (resetTriggered == 2 && nowTimer > 2500 && nowTimer <= 3250) {
TriggerReset(2);
resetTriggered = 3;
}
else if (resetTriggered == 3 && nowTimer > 3250) {
Serial.println(F("Reset complete")); // else if the timer is longer than 3 sec then force reset
TriggerReset(50);
modeChanged = true;
buttonTimer = 0;
resetTriggered = 0;
}
}
if (button.getSingleDebouncedRelease()) {
// The button was released. Test for short or long press
nowTimer = millis();
nowTimer -= buttonTimer;
Serial.print(F("Button released after "));
Serial.println(nowTimer);
if (nowTimer <= 1000) { // If the timer is less than 1 sec then change mode
mode++;
Serial.print(F("Mode: "));
Serial.println(mode);
modeChanged = true;
buttonTimer = 0;
}
else if ((nowTimer > 1000 && nowTimer < 3000) && resetTriggered > 0) {
Serial.println(F("Cancelling reset")); // if the button is released befvore 3 sec then stop reset
TriggerReset(99); // Send cancel signal
modeChanged = true;
buttonTimer = 0;
resetTriggered = 0;
}
}
if (resetTriggered == 0) {
switch (mode) {
case 0:
showTurbo();
break;
case 1:
showVolts();
break;
case 2:
showTemp();
break;
case 3:
showLogo();
break;
default:
showTurbo();
mode = 0;
break;
}
modeChanged = false;
}
}