Skip to content

Commit

Permalink
Add support for I2C OLED Displays (#52)
Browse files Browse the repository at this point in the history
* First refactory to support OLED (tested TFT still working)

* First OLED working version with menu

* Some code clean up
  • Loading branch information
melkati authored Dec 17, 2021
1 parent f099be9 commit 673d222
Show file tree
Hide file tree
Showing 9 changed files with 358 additions and 117 deletions.
22 changes: 17 additions & 5 deletions CO2_Gadget.ino
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
/**/ // #define SUPPORT_OTA // Needs SUPPORT_WIFI - CURRENTLY NOT WORKING AWAITING FIX
/**/ #define SUPPORT_TFT
/**/ #define DEBUG_ARDUINOMENU
#define UNITHOSTNAME "CO2-Gadget"
/**/ #define UNITHOSTNAME "CO2-Gadget"
/**/ // #define ALTERNATIVE_I2C_PINS // For the compact build as shown at https://emariete.com/medidor-co2-display-tft-color-ttgo-t-display-sensirion-scd30/
/**/ #endif
/*****************************************************************************************************/
Expand All @@ -32,6 +32,7 @@ bool debugSensors = false;
bool inMenu = false;
bool bleInitialized = false;
int8_t selectedCO2Sensor = -1;
uint32_t DisplayBrightness = 100;

// Variables to control automatic display off to save power
bool displayOffOnExternalPower = false;
Expand All @@ -44,6 +45,16 @@ uint64_t lastButtonUpTimeStamp = millis(); // Last time button UP was pressed
#endif // ifdef BUILD_GIT
#define BUILD_GIT __DATE__

#undef I2C_SDA
#undef I2C_SCL
#ifdef ALTERNATIVE_I2C_PINS
#define I2C_SDA 22
#define I2C_SCL 21
#else
#define I2C_SDA 21
#define I2C_SCL 22
#endif

#include <Wire.h>
#include "driver/adc.h"
#include "soc/soc.h" // disable brownout problems
Expand Down Expand Up @@ -234,8 +245,8 @@ void displayLoop() {
return;

if (millis() > nextTimeToDisplayOff) {
Serial.println("-->[MAIN] Turning off display to save power");
setTFTBrightness(0); // Turn off the display
Serial.println("-->[MAIN] Turning off display to save power");
turnOffDisplay();
nextTimeToDisplayOff = nextTimeToDisplayOff + (timeToDisplayOff * 1000);
}
}
Expand All @@ -257,9 +268,10 @@ void setup() {
initPreferences();
initBattery();
#if defined SUPPORT_OLED
delay(100);
initDisplayOLED();
delay(1000);
displaySplashScreenOLED();
delay(1000);
#endif
#if defined SUPPORT_TFT
initDisplayTFT();
Expand Down Expand Up @@ -291,5 +303,5 @@ void loop() {
#endif
displayLoop();
buttonsLoop();
nav.poll(); // this device only draws when needed
menuLoop();
}
7 changes: 6 additions & 1 deletion CO2_Gadget_Buttons.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ Button2 btnUp(BTN_UP); // Initialize the up button
Button2 btnDwn(BTN_DWN); // Initialize the down button

void IRAM_ATTR buttonUpISR() {
setTFTBrightness(TFTBrightness); // Turn on the display at TFTBrightness brightness
#ifdef SUPPORT_TFT
setTFTBrightness(DisplayBrightness); // Turn on the display at DisplayBrightness brightness
#endif
#ifdef SUPPORT_OLED
setOLEDBrightness(DisplayBrightness); // Turn on the display at DisplayBrightness brightness
#endif
nextTimeToDisplayOff = millis() + (timeToDisplayOff*1000);
}

Expand Down
114 changes: 93 additions & 21 deletions CO2_Gadget_Menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@
// clang-format on
#include <menu.h>
#include <menuIO/serialIO.h>

#ifdef SUPPORT_TFT
#include <menuIO/TFT_eSPIOut.h>
// #include <menuIO/chainStream.h>
#endif

#ifdef SUPPORT_OLED
#include <menuIO/u8g2Out.h>
#include <menuIO/chainStream.h>
#endif

#include <menuIO/esp8266Out.h> //must include this even if not doing web output...

using namespace Menu;
Expand Down Expand Up @@ -195,12 +203,17 @@ result doSavePreferences(eventMask e, navNode &nav, prompt &item) {

result doSetTFTBrightness(eventMask e, navNode &nav, prompt &item) {
#ifdef DEBUG_ARDUINOMENU
Serial.printf("-->[MENU] Setting TFT brightness at %d", TFTBrightness);
Serial.printf("-->[MENU] Setting TFT brightness at %d", DisplayBrightness);
Serial.print(F("-->[MENU] action1 event:"));
Serial.println(e);
Serial.flush();
#endif
setTFTBrightness(TFTBrightness);
#ifdef SUPPORT_FTF
setTFTBrightness(DisplayBrightness);
#endif
#ifdef SUPPORT_OLED
setOLEDBrightness(DisplayBrightness);
#endif
return proceed;
}

Expand Down Expand Up @@ -507,7 +520,7 @@ TOGGLE(displayOffOnExternalPower, activeDisplayOffMenuOnBattery, "Off on USB: ",
,VALUE("OFF", false, doNothing, noEvent));

MENU(displayConfigMenu, "Display Config", doNothing, noEvent, wrapStyle
,FIELD(TFTBrightness, "Brightness:", "", 10, 255, 10, 10, doSetTFTBrightness, anyEvent, wrapStyle)
,FIELD(DisplayBrightness, "Brightness:", "", 10, 255, 10, 10, doSetTFTBrightness, anyEvent, wrapStyle)
,FIELD(timeToDisplayOff, "Time To Off:", "", 0, 900, 5, 5, doNothing, noEvent, wrapStyle)
,SUBMENU(activeDisplayOffMenuOnBattery)
,EXIT("<Back"));
Expand All @@ -528,8 +541,8 @@ MENU(configMenu, "Configuration", doNothing, noEvent, wrapStyle

MENU(informationMenu, "Information", doNothing, noEvent, wrapStyle
,FIELD(battery_voltage, "Battery", "V", 0, 9, 0, 0, doNothing, noEvent, noStyle)
,OP("Comp" BUILD_GIT, doNothing, noEvent)
,OP("Version" CO2_GADGET_VERSION CO2_GADGET_REV, doNothing, noEvent)
,OP("Comp " BUILD_GIT, doNothing, noEvent)
,OP("Version " CO2_GADGET_VERSION CO2_GADGET_REV, doNothing, noEvent)
,EDIT("IP", tempIPAddress, alphaNum, doNothing, noEvent, wrapStyle)
,EDIT("BLE Dev. Id", tempBLEDeviceId, alphaNum, doNothing, noEvent, wrapStyle)
,EXIT("<Back"));
Expand All @@ -555,8 +568,15 @@ MENU(mainMenu, "CO2 Gadget", doNothing, noEvent, wrapStyle
,SUBMENU(subMenu)
,EXIT("<Exit"));

#define MAX_DEPTH 4

serialIn serial(Serial);

// define serial output device
idx_t serialTops[MAX_DEPTH] = {0};
serialOut outSerial(Serial, serialTops);

#ifdef SUPPORT_TFT
// define menu colors --------------------------------------------------------
#define Black RGB565(0, 0, 0)
#define Red RGB565(255, 0, 0)
Expand Down Expand Up @@ -596,15 +616,6 @@ const colorDef<uint16_t> colors[6] MEMMODE = {
{{(uint16_t)White, (uint16_t)Gray}, {(uint16_t)Black, (uint16_t)Red, (uint16_t)White}}, // cursorColor
{{(uint16_t)White, (uint16_t)Yellow}, {(uint16_t)Black, (uint16_t)DarkerOrange, (uint16_t)Red}}, // titleColor - Menu title color
};
// clang-format on

#define MAX_DEPTH 4

serialIn serial(Serial);

// define serial output device
idx_t serialTops[MAX_DEPTH] = {0};
serialOut outSerial(Serial, serialTops);

#define tft_WIDTH 240
#define tft_HEIGHT 135
Expand All @@ -617,10 +628,49 @@ navNode *nodes[sizeof(panels) /
panelsList pList(panels, nodes, 1); // a list of panels and nodes
idx_t eSpiTops[MAX_DEPTH] = {0};
TFT_eSPIOut eSpiOut(tft, colors, eSpiTops, pList, fontW, fontH + 1);
menuOut *constMEM outputs[] MEMMODE = {&outSerial,
&eSpiOut}; // list of output devices
outputsList out(outputs,
sizeof(outputs) / sizeof(menuOut *)); // outputs list controller
menuOut *constMEM outputs[] MEMMODE = {&outSerial, &eSpiOut}; // list of output devices
outputsList out(outputs, sizeof(outputs) / sizeof(menuOut *)); // outputs list controller
#endif

#ifdef SUPPORT_OLED
// define menu colors --------------------------------------------------------
//each color is in the format:
// {{disabled normal,disabled selected},{enabled normal,enabled selected, enabled editing}}
// this is a monochromatic color table
const colorDef<uint8_t> colors[6] MEMMODE={
{{0,0},{0,1,1}},//bgColor
{{1,1},{1,0,0}},//fgColor
{{1,1},{1,0,0}},//valColor
{{1,1},{1,0,0}},//unitColor
{{0,1},{0,0,1}},//cursorColor
{{1,1},{1,0,0}},//titleColor
};

#define fontX 5
#define fontY 10
// #define MENUFONT u8g2_font_7x13_mf
// #define fontX 7
// #define fontY 16
#define offsetX 1
#define offsetY 2
#define U8_Width 128
#define U8_Height 64
#define USE_HWI2C
#define fontMarginX 2
#define fontMarginY 2

//define output device oled
idx_t gfx_tops[MAX_DEPTH];
PANELS(gfxPanels,{0,0,U8_Width/fontX,U8_Height/fontY});
u8g2Out oledOut(u8g2,colors,gfx_tops,gfxPanels,fontX,fontY,offsetX,offsetY,fontMarginX,fontMarginY);

//define outputs controller
menuOut* outputs[]{&outSerial,&oledOut};//list of output devices
outputsList out(outputs,sizeof(outputs)/sizeof(menuOut*));//outputs list controller

MENU_INPUTS(in,&serial);
#endif
// clang-format on

NAVROOT(nav, mainMenu, MAX_DEPTH, serial, out);

Expand Down Expand Up @@ -733,7 +783,6 @@ void loadTempArraysWithActualValues() {

// when menu is suspended
result idle(menuOut &o, idleEvent e) {
#if defined SUPPORT_TFT
if (e == idleStart) {
#ifdef DEBUG_ARDUINOMENU
Serial.println("-->[MENU] Event idleStart");
Expand All @@ -745,7 +794,12 @@ result idle(menuOut &o, idleEvent e) {
Serial.println("-->[MENU] Event iddling");
Serial.flush();
#endif
#if defined SUPPORT_TFT
showValuesTFT(co2);
#endif
#if defined SUPPORT_OLED
showValuesOLED(co2);
#endif
readBatteryVoltage();
} else if (e == idleEnd) {
#ifdef DEBUG_ARDUINOMENU
Expand All @@ -762,11 +816,29 @@ result idle(menuOut &o, idleEvent e) {
#endif
}
return proceed;
}

void menuLoop() {
#ifdef SUPPORT_TFT
nav.poll(); // this device only draws when needed
#endif

#ifdef SUPPORT_OLED
nav.doInput();
if (nav.sleepTask) {
showValuesOLED(co2);
} else {
if (nav.changed(0)) {
u8g2.firstPage();
do nav.doOutput();
while (u8g2.nextPage());
}
}
#endif
}

void menu_init() {
nav.idleTask = idle; // function to be used when menu is suspended
nav.idleTask = idle; // function to be called when menu is suspended
nav.idleOn(idle);
nav.timeOut = 30;
nav.showTitle = true;
Expand Down
107 changes: 63 additions & 44 deletions CO2_Gadget_OLED.h
Original file line number Diff line number Diff line change
@@ -1,60 +1,79 @@

#ifndef CO2_Gadget_OLED_h
#define CO2_Gadget_OLED_h

#ifdef SUPPORT_OLED

// clang-format off
/*****************************************************************************************************/
/********* *********/
/********* SETUP OLED DISPLAY FUNCTIONALITY *********/
/********* *********/
/*****************************************************************************************************/
// clang-format on
#if defined SUPPORT_OLED
#include <U8x8lib.h>
U8X8_SH1106_128X64_NONAME_HW_I2C u8x8(/* reset=*/U8X8_PIN_NONE);
// #include <U8x8lib.h>
#include <U8g2lib.h>
#include "bootlogo.h"
#include "icons.h"
// U8X8_SH1106_128X64_NONAME_HW_I2C u8g2(/* reset=*/U8X8_PIN_NONE);
// U8G2_SSD1306_128X64_NONAME_HW_I2C u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
// U8G2_SSD1306_128X64_VCOMH0_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, I2C_SCL, I2C_SDA);//allow contrast change
// U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, I2C_SCL, I2C_SDA);
// U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // Frame Buffer: clearBuffer/sendBuffer. More RAM usage, Faster
// U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE); // Page Buffer: firstPage/nextPage. Less RAM usage, Slower

char oled_msg[20];
int displayWidth = 128;
int displayHeight = 64;
// #define MENUFONT u8g2_font_6x10_mf
#define MENUFONT u8g2_font_5x8_mf

void setOLEDBrightness(uint32_t newBrightness) {
Serial.printf("Setting screen brightness value at %d\n (unfunctional. TO DO", newBrightness);
}

#endif
void turnOffDisplay() {
setOLEDBrightness(0); // Turn off the display
}

void displaySplashScreenOLED() {
u8g2.clearDisplay();
u8g2.firstPage();
do {
// u8g2.drawXBMP(30, 0, 59, 20, eMarieteLogo);
// u8g2.drawXBM(7, 23, 46, 36, CO2Logo);
// u8g2.drawXBM(60, 32, 61, 23, GadgetLogo);
u8g2.drawXBM(0, 0, 128, 64, splash);
} while (u8g2.nextPage());
u8g2.setFont(MENUFONT);
}

void initDisplayOLED() {
#if defined SUPPORT_OLED
u8x8.begin();
u8x8.setPowerSave(0);
u8x8.setFont(u8x8_font_chroma48medium8_r);
u8x8.drawString(0, 1, " eMariete.com");
u8x8.drawString(0, 2, " Sensirion");
u8x8.drawString(0, 3, "CO2 Gadget");
u8x8.drawString(0, 4, "Concentration Monitor");
#endif
Serial.printf("-->[OLED] Initialized: \t#%s#\n",
((u8g2.begin()) ? "OK" : "Failed"));
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_ncenB12_tr);
u8g2.drawStr(0, 15, " eMariete.com");
u8g2.drawStr(0, 33, " CO2 Gadget");
u8g2.drawStr(0, 51, " Air Quality");
} while (u8g2.nextPage());
u8g2.setFont(MENUFONT);
}

void showValuesOLED(uint16_t co2) {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_7Segments_26x42_mn);
u8g2.setCursor(0, 44);
u8g2.print(co2);
u8g2.setFont(u8g2_font_5x7_tf);
u8g2.setCursor(110, 51);
u8g2.print("ppm");
} while (u8g2.nextPage());
u8g2.setFont(MENUFONT);
}

void showValuesOLED(String text) {
#if defined SUPPORT_OLED
u8x8.clearLine(2);
u8x8.clearLine(3);
u8x8.setFont(u8x8_font_chroma48medium8_r);
u8x8.drawString(0, 4, "CO2: ");
u8x8.setFont(u8x8_font_courB18_2x3_r);
sprintf(oled_msg, "%4d", co2); // If parameter string then: co2.c_str()
u8x8.drawString(4, 3, oled_msg);
u8x8.setFont(u8x8_font_chroma48medium8_r);
u8x8.drawString(12, 4, "ppm");

u8x8.clearLine(6);
sprintf(oled_msg, "T:%.1fC RH:%.0f%%", temp, hum);
u8x8.drawUTF8(0, 6, oled_msg);

if (activeWIFI) {
if (WiFiMulti.run() != WL_CONNECTED) {
u8x8.clearLine(7);
u8x8.drawUTF8(0, 6, "WiFi unconnected");
} else {
u8x8.clearLine(7);
IPAddress ip = WiFi.localIP();
sprintf(oled_msg, "%s", ip.toString().c_str());
// sprintf("IP:%u.%u.%u.%u\n", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) &
// 0xff, ip >> 24);
u8x8.drawString(0, 7, oled_msg);
}
}
#endif
}
#endif // SUPPORT_OLED
#endif // CO2_Gadget_OLED_h
Loading

0 comments on commit 673d222

Please sign in to comment.