422 lines
12 KiB
C++
422 lines
12 KiB
C++
#include <SPI.h>
|
|
#include "CustomFont.h"
|
|
#include "MiniFont.h"
|
|
#include "tahoma16.h"
|
|
#include "protocol.h"
|
|
#include "display.h"
|
|
#include "pins.h"
|
|
#include "BluetoothAbstract.h"
|
|
#include "OLEDconsts.h"
|
|
#include "BTCWifi.h"
|
|
|
|
#define MAXIFONT tahoma_16ptFontInfo
|
|
#define MINIFONT miniFontInfo
|
|
|
|
#define X_FANICON 55
|
|
#define Y_FANICON 39
|
|
#define X_FUELICON 81
|
|
#define Y_FUELICON 39
|
|
#define X_TARGETICON 31
|
|
#define Y_TARGETICON 39
|
|
#define Y_BASELINE 58
|
|
#define X_BATTICON 95
|
|
#define Y_BATTICON 0
|
|
#define X_GLOWICON 97
|
|
#define Y_GLOWICON 38
|
|
#define X_BODYBULB 119
|
|
#define X_BULB 1 // >= 1
|
|
#define Y_BULB 4
|
|
#define X_WIFIICON 22
|
|
#define Y_WIFIICON 0
|
|
#define X_BTICON 12
|
|
#define Y_BTICON 0
|
|
|
|
|
|
#define MINI_BATTLABEL
|
|
#define MINI_TEMPLABEL
|
|
#define MINI_TARGETLABEL
|
|
#define MINI_FANLABEL
|
|
#define MINI_GLOWLABEL
|
|
#define MINI_FUELLABEL
|
|
#define MINI_BODYLABEL
|
|
|
|
//
|
|
// **** NOTE: There are two very lame libaries conspiring to make life difficult ****
|
|
// A/ The ESP32 SPI.cpp library instatiates an instance of SPI, using the VSPI port (and pins)
|
|
// B/ The Adfruit_SH1106 library has hard coded "SPI" as the SPI port instance
|
|
//
|
|
// As an ESP32 has a pin multiplexer, this is very bad form.
|
|
// The design uses the defacto HSPI pins (and port),
|
|
// You **MUST comment out the SPIClass SPI(VSPI);** at the end of the ESP32 SPI library
|
|
// then we declare "SPI" here, which will use HSPI!!!!
|
|
|
|
// 128 x 64 OLED support
|
|
SPIClass SPI; // default constructor opens HSPI on standard pins : MOSI=13,CLK=14,MISO=12(unused)
|
|
//Adafruit_SH1106 display(OLED_DC_pin, -1, OLED_CS_pin);
|
|
CCustomFont display(OLED_DC_pin, -1, OLED_CS_pin);
|
|
|
|
bool animatePump = false;
|
|
bool animateRPM = false;
|
|
bool animateGlow = false;
|
|
|
|
|
|
extern float fFilteredTemperature;
|
|
extern CBluetoothAbstract& getBluetoothClient();
|
|
|
|
void showThermometer(float desired, float actual);
|
|
void showBodyThermometer(int actual);
|
|
void showBTicon();
|
|
void showWifiIcon();
|
|
void showBatteryIcon(float voltage);
|
|
void showGlowPlug(int power);
|
|
void showFan(int RPM);
|
|
void showFuel(float rate);
|
|
void showRunState(int state, int errstate);
|
|
void printRightJustify(const char* str, int yPos, int RHS=128);
|
|
|
|
void initOLED()
|
|
{
|
|
SPI.setFrequency(8000000);
|
|
// SH1106_SWITCHCAPVCC = generate display voltage from 3.3V internally
|
|
display.begin(SH1106_SWITCHCAPVCC, 0, false);
|
|
|
|
// Show initial display buffer contents on the screen --
|
|
// the library initializes this with an Adafruit splash screen.
|
|
display.display();
|
|
}
|
|
|
|
void updateOLED(const CProtocol& CtlFrame, const CProtocol& HtrFrame)
|
|
{
|
|
int runstate = HtrFrame.getRunState();
|
|
int errstate = HtrFrame.getErrState();
|
|
if(errstate) errstate--; // correct for +1 biased return value
|
|
|
|
display.clearDisplay();
|
|
|
|
float desiredT = 0;
|
|
if(runstate && (runstate <= 5)) {
|
|
if(CtlFrame.isThermostat())
|
|
desiredT = CtlFrame.getTemperature_Desired();
|
|
else
|
|
desiredT = -HtrFrame.getPump_Fixed() * 0.1f;
|
|
}
|
|
|
|
showThermometer(desiredT, // read values from most recently sent [BTC] frame
|
|
fFilteredTemperature);
|
|
|
|
if(getBluetoothClient().isConnected())
|
|
showBTicon();
|
|
|
|
if(isWifiConnected()) {
|
|
showWifiIcon();
|
|
display.fillRect(X_WIFIICON + 8, Y_WIFIICON + 5, 10, 7, BLACK);
|
|
display.setFontInfo(&MINIFONT); // select Mini Font
|
|
display.setCursor(X_WIFIICON+9, Y_WIFIICON+6);
|
|
display.print("AP");
|
|
display.setFontInfo(NULL);
|
|
}
|
|
|
|
float voltage = HtrFrame.getVoltage_Supply() * 0.1f;
|
|
showBatteryIcon(voltage);
|
|
|
|
animateRPM = false;
|
|
animatePump = false;
|
|
animateGlow = false;
|
|
|
|
if(runstate) {
|
|
float power = HtrFrame.getGlowPlug_Current() * 0.01 * HtrFrame.getGlowPlug_Voltage() * 0.1;
|
|
if(power > 1) {
|
|
showGlowPlug(int(power));
|
|
}
|
|
|
|
showFan(HtrFrame.getFan_Actual());
|
|
|
|
showFuel(HtrFrame.getPump_Actual() * 0.1f);
|
|
|
|
showBodyThermometer(HtrFrame.getTemperature_HeatExchg());
|
|
}
|
|
else {
|
|
if(isWifiConnected()) {
|
|
printRightJustify(getWifiAddrStr(), 57);
|
|
}
|
|
}
|
|
|
|
showRunState(runstate, errstate);
|
|
|
|
#ifdef DEMO_LARGEFONT
|
|
display.fillRect(20,20, 80,16, BLACK);
|
|
display.setCursor(20,20);
|
|
display.setFontInfo(&MAXIFONT); // Dot Factory Font
|
|
display.print("25.6`");
|
|
display.setFontInfo(NULL); // standard 5x7 font
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
void animateOLED()
|
|
{
|
|
static int fan = 0;
|
|
static int drip = 0;
|
|
static int heat = 0;
|
|
|
|
if(animatePump || animateRPM || animateGlow) {
|
|
|
|
if(animatePump) {
|
|
// erase region of fuel icon
|
|
display.fillRect(X_FUELICON, Y_FUELICON, 7, 16, BLACK);
|
|
display.drawBitmap(X_FUELICON, Y_FUELICON+(drip/2), FuelIcon, 7, 12, WHITE);
|
|
drip++;
|
|
drip &= 0x07;
|
|
}
|
|
|
|
if(animateRPM) {
|
|
// erase region of fuel icon
|
|
display.fillRect(X_FANICON, Y_FANICON, 16, 16, BLACK);
|
|
switch(fan) {
|
|
case 0: display.drawBitmap(X_FANICON, Y_FANICON, FanIcon1, 16, 16, WHITE); break;
|
|
case 1: display.drawBitmap(X_FANICON, Y_FANICON, FanIcon2, 16, 16, WHITE); break;
|
|
case 2: display.drawBitmap(X_FANICON, Y_FANICON, FanIcon3, 16, 16, WHITE); break;
|
|
case 3: display.drawBitmap(X_FANICON, Y_FANICON, FanIcon4, 16, 16, WHITE); break;
|
|
}
|
|
fan++;
|
|
fan &= 0x03;
|
|
}
|
|
|
|
if(animateGlow) {
|
|
display.fillRect(X_GLOWICON, Y_GLOWICON, 17, 10, BLACK);
|
|
display.drawBitmap(X_GLOWICON, Y_GLOWICON, GlowPlugIcon, 16, 9, WHITE);
|
|
display.drawBitmap(X_GLOWICON, Y_GLOWICON + 2 + heat, GlowHeatIcon, 17, 2, WHITE);
|
|
heat -= 2;
|
|
heat &= 0x07;
|
|
}
|
|
}
|
|
display.display();
|
|
}
|
|
|
|
|
|
#define TEMP_YPOS(A) ((((20 - A) * 3) / 2) + 22)
|
|
void showThermometer(float desired, float actual)
|
|
{
|
|
char msg[16];
|
|
display.clearDisplay();
|
|
// draw bulb design
|
|
display.drawBitmap(X_BULB, Y_BULB, thermometerBitmap, 8, 50, WHITE);
|
|
// draw mercury
|
|
int yPos = Y_BULB + TEMP_YPOS(actual);
|
|
display.drawLine(X_BULB + 3, yPos, X_BULB + 3, Y_BULB + 42, WHITE);
|
|
display.drawLine(X_BULB + 4, yPos, X_BULB + 4, Y_BULB + 42, WHITE);
|
|
// print actual temperature
|
|
#ifdef MINI_TEMPLABEL
|
|
sprintf(msg, "%.1f`C", actual);
|
|
display.setCursor(0, Y_BASELINE);
|
|
display.setFontInfo(&MINIFONT); // select Mini Font
|
|
display.print(msg);
|
|
display.setFontInfo(NULL);
|
|
#else
|
|
display.setTextColor(WHITE);
|
|
display.setCursor(0, Y_BASELINE);
|
|
display.print(actual, 1);
|
|
#endif
|
|
|
|
// draw set point
|
|
if(desired) {
|
|
display.drawBitmap(X_TARGETICON, Y_TARGETICON, TargetIcon, 13, 13, WHITE); // set indicator against bulb
|
|
char msg[16];
|
|
if(desired > 0) {
|
|
int yPos = Y_BULB + TEMP_YPOS(desired) - 2;
|
|
display.drawBitmap(X_BULB-1, yPos, thermoPtr, 3, 5, WHITE); // set indicator against bulb
|
|
sprintf(msg, "%.0f`C", desired);
|
|
}
|
|
else {
|
|
sprintf(msg, "%.1fHz", -desired);
|
|
}
|
|
#ifdef MINI_TARGETLABEL
|
|
int xPos = X_TARGETICON + 7 - strlen(msg) * 2; // 2 = 1/2 width mini font
|
|
display.setCursor(xPos, Y_BASELINE);
|
|
display.setFontInfo(&MINIFONT); // select Mini Font
|
|
display.print(msg);
|
|
display.setFontInfo(NULL);
|
|
#else
|
|
int xPos = X_TARGETICON + 6 - strlen(msg) * 3; // 3 = 1/2 width normal font
|
|
display.setCursor(xPos, Y_BASELINE);
|
|
display.print(msg);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#define BODY_YPOS(A) ((((100 - A) * 3) / 16) + 22) // 100degC centre - ticks +- 80C
|
|
void showBodyThermometer(int actual)
|
|
{
|
|
// draw bulb design
|
|
display.drawBitmap(X_BODYBULB, Y_BULB, thermometerBitmap, 8, 50, WHITE);
|
|
// draw mercury
|
|
int yPos = Y_BULB + BODY_YPOS(actual);
|
|
display.drawLine(X_BODYBULB + 3, yPos, X_BODYBULB + 3, Y_BULB + 42, WHITE);
|
|
display.drawLine(X_BODYBULB + 4, yPos, X_BODYBULB + 4, Y_BULB + 42, WHITE);
|
|
// print actual temperature
|
|
display.setTextColor(WHITE);
|
|
char label[16];
|
|
// determine width and position right justified
|
|
#ifdef MINI_BODYLABEL
|
|
sprintf(label, "%d`C", actual);
|
|
int width = strlen(label) * 4;
|
|
display.setCursor(125-width, Y_BASELINE);
|
|
display.setFontInfo(&MINIFONT); // select Mini Font
|
|
display.print(label);
|
|
display.setFontInfo(NULL);
|
|
#else
|
|
sprintf(label, "%d", actual);
|
|
int width = strlen(label) * 6;
|
|
display.setCursor(127-width, Y_BASELINE);
|
|
display.print(label);
|
|
#endif
|
|
}
|
|
|
|
void showBTicon()
|
|
{
|
|
display.drawBitmap(X_BTICON, Y_BTICON, BTicon, W_BTICON, H_BTICON, WHITE);
|
|
}
|
|
|
|
void showWifiIcon()
|
|
{
|
|
display.drawBitmap(X_WIFIICON, Y_WIFIICON, wifiIcon, W_WIFIICON, H_WIFIICON, WHITE);
|
|
}
|
|
|
|
void showBatteryIcon(float voltage)
|
|
{
|
|
display.drawBitmap(X_BATTICON, Y_BATTICON, BatteryIcon, 15, 10, WHITE);
|
|
#ifdef MINI_BATTLABEL
|
|
char msg[16];
|
|
sprintf(msg, "%.1fV", voltage);
|
|
int xPos = X_BATTICON + 7 - strlen(msg) * 2;
|
|
display.setCursor(xPos, Y_BATTICON+12);
|
|
display.setFontInfo(&MINIFONT); // select Mini Font
|
|
display.print(msg);
|
|
display.setFontInfo(NULL);
|
|
#else
|
|
display.setCursor(85, 12);
|
|
display.setTextColor(WHITE);
|
|
display.print(voltage, 1);
|
|
display.print("V");
|
|
#endif
|
|
|
|
// nominal 10.5 -> 13.5V bargraph
|
|
// int Capacity = int((voltage - 11.0) * 4.5);
|
|
// int Capacity = (voltage - 11.4) * 7;
|
|
int Capacity = (voltage - 10.7) * 4;
|
|
if(Capacity < 0) Capacity = 0;
|
|
if(Capacity > 11) Capacity = 11;
|
|
display.fillRect(X_BATTICON+2 + Capacity, Y_BATTICON+2, 11-Capacity, 6, BLACK);
|
|
}
|
|
|
|
void showGlowPlug(int power)
|
|
{
|
|
display.drawBitmap(X_GLOWICON, Y_GLOWICON, GlowPlugIcon, 16, 9, WHITE);
|
|
// animateGlow = true;
|
|
#ifdef MINI_GLOWLABEL
|
|
char msg[16];
|
|
sprintf(msg, "%dW", power);
|
|
int xPos = X_GLOWICON + 9 - strlen(msg) * 2;
|
|
display.setCursor(xPos, Y_GLOWICON+12);
|
|
display.setFontInfo(&MINIFONT); // select Mini Font
|
|
display.print(msg);
|
|
display.setFontInfo(NULL);
|
|
#else
|
|
display.setCursor(X_GLOWICON, Y_GLOWICON+12);
|
|
display.print(power);
|
|
display.print("W");
|
|
#endif
|
|
}
|
|
|
|
void showFan(int RPM)
|
|
{
|
|
// NOTE: fan rotation animation performed in animateOLED
|
|
animateRPM = RPM != 0; // used by animation routine
|
|
|
|
display.setTextColor(WHITE);
|
|
char msg[16];
|
|
sprintf(msg, "%d", RPM);
|
|
#ifdef MINI_FANLABEL
|
|
int xPos = X_FANICON + 8 - strlen(msg) * 2; // 3 = 1/2 width font
|
|
display.setCursor(xPos, Y_BASELINE);
|
|
display.setFontInfo(&MINIFONT); // select Mini Font
|
|
display.print(msg);
|
|
display.setFontInfo(NULL);
|
|
#else
|
|
int xPos = X_FANICON + 8 - ( strlen(msg) * 3); // 3 = 1/2 width font
|
|
display.setCursor(xPos, Y_BASELINE);
|
|
display.print(msg);
|
|
#endif
|
|
}
|
|
|
|
void showFuel(float rate)
|
|
{
|
|
// NOTE: fuel drop animation performed in animateOLED
|
|
animatePump = rate != 0; // used by animation routine
|
|
if(rate) {
|
|
char msg[16];
|
|
sprintf(msg, "%.1f", rate);
|
|
#ifdef MINI_FUELLABEL
|
|
int xPos = X_FUELICON + 3 - strlen(msg) * 2; // 3 = 1/2 width font
|
|
display.setCursor(xPos, Y_BASELINE);
|
|
display.setFontInfo(&MINIFONT); // select Mini Font
|
|
display.print(msg);
|
|
display.setFontInfo(NULL);
|
|
#else
|
|
int xPos = X_FUELICON + 3 - ( strlen(msg) * 3); // 3 = 1/2 width font
|
|
display.setCursor(xPos, Y_BASELINE);
|
|
display.setTextColor(WHITE);
|
|
display.print(msg);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void showRunState(int runstate, int errstate)
|
|
{
|
|
static bool toggle = false;
|
|
const char* toPrint = NULL;
|
|
int yPos = 25;
|
|
display.setTextColor(WHITE, BLACK);
|
|
if(runstate >= 0 && runstate <= 8) {
|
|
if(((runstate == 0) || (runstate > 5)) && errstate) {
|
|
// create an "E-XX" message to display
|
|
char msg[16];
|
|
sprintf(msg, "E-%02d", errstate);
|
|
int xPos = 64 - ((strlen(msg)/2) * 6);
|
|
if(runstate > 5)
|
|
yPos -= 8;
|
|
display.setCursor(xPos, yPos);
|
|
yPos += 8;
|
|
// flash error code
|
|
toggle = !toggle;
|
|
if(toggle)
|
|
display.print(msg);
|
|
else {
|
|
display.print(" ");
|
|
}
|
|
// bounds limit error and gather message
|
|
if(errstate > 10) errstate = 11;
|
|
toPrint = Errstates[errstate-1];
|
|
}
|
|
else {
|
|
toPrint = Runstates[runstate];
|
|
}
|
|
}
|
|
if(toPrint) {
|
|
int width = strlen(toPrint);
|
|
int xPos = 64 - ((width/2) * 6);
|
|
display.setCursor(xPos, yPos);
|
|
display.print(toPrint);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void printRightJustify(const char* str, int yPos, int RHS)
|
|
{
|
|
int xPos = RHS - strlen(str) * 6;
|
|
display.setCursor(xPos, yPos);
|
|
display.print(str);
|
|
}
|
|
|