├── .gitignore ├── README.md ├── examples ├── WIFI-Kit-8-DEMO │ └── WIFI-Kit-8-DEMO.ino ├── WIFI-Kit-8-TTY-DEMO │ └── WIFI-Kit-8-TTY-DEMO.ino ├── WIFI-Kit-8-WiFiScan │ └── WIFI-Kit-8-WiFiScan.ino └── arduino-onebutton-menu-DEMO │ ├── DDigitalButton.cpp │ ├── DDigitalButton.h │ ├── DMenu.cpp │ ├── DMenu.h │ └── arduino-onebutton-menu-DEMO.ino ├── keywords.txt ├── library.properties └── src ├── oled.cpp └── oled.h /.gitignore: -------------------------------------------------------------------------------- 1 | *Doxyfile* 2 | html/ 3 | help/ 4 | bin/ 5 | obj/ 6 | *.sh 7 | *.cmd 8 | *.html 9 | TODO -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Driver for OLED displays with SSD1306 or SH1106 2 | 3 | 4 | *** 5 | ### This driver is distributed without license. You may use it for free. 6 | Author: Stefan Frings, 2017 7 | 8 | ### Update by: 9 | Author: Fabio Durigon, 2018, 2022\ 10 | email: develop@dury.it 11 | *** 12 | 13 | ## Features: 14 | * Supported display sizes: 196x16, 28x32 and 128x64 pixels. 15 | * The I2C communication is done by software bit-banging the configurable I/O pins. 16 | * Supports all print() and write() calls as the internal Serial lib of Arduino core. 17 | * Supports also standard C/C++ printf() function. 18 | * Added printf(x,y,...) to print directly at x,y coordinate with one call. 19 | * Some special characters are handled like in unix mode: 20 | '\n' or '\r' -> The cursor is moved to the begin of next line. 21 | '\r' and '\n' consecutive (also inverted) acts as a single one so -> The cursor is moved to the begin of next line. 22 | '\f' like line-feed scroll up entire page. 23 | * TTY mode: 24 | * Display can be used like a terminal window (without positioning the cursor before print). 25 | * Any call to setCursor() has no effect. 26 | * When the cursor position reach the bottom of the screen, the page is scrolled up by one line. 27 | * Added some wrappers for U8g2 library API compatibily. Now, for writing strings, You can use same U8g2 APIs (see [How to use](#how-to-use)): 28 | * drawString(Column,Row,String) -> draw string at specific ROW and COLUMN. 29 | * inverse() -> enable inverted font print. 30 | * noInverse() -> disable inverted font print. 31 | 32 | ## Limitations: 33 | * This driver supports only displays with internal charge pump and I2C interface. 34 | * Communication errors are not handled. 35 | * Only one 6x8 font is supported. 36 | 37 | ## How to use: 38 | 39 | ### 1) Declaration 40 | This constructor is DEPRECATED: 41 | 42 | ```C++ 43 | OLED(uint8_t sda_pin, // sda pin for I2C comunication 44 | uint8_t scl_pin, // scl pin for I2C comunication 45 | uint8_t reset_pin=NO_RESET_PIN, // Reset pin (default: none) 46 | uint8_t i2c_address=0x3C, // I2C address (default: 0x3C) 47 | uint_fast8_t width=128, // Pixel width 48 | uint_fast8_t height=32, // Pixel Height 49 | bool isSH1106=false // Display type: true for SSD1306, false for SH1106 (default: false) 50 | ); 51 | ``` 52 | Please Use this one: 53 | ```C++ 54 | OLED(uint8_t sda_pin, // sda pin for I2C comunication 55 | uint8_t scl_pin, // scl pin for I2C comunication 56 | uint8_t reset_pin=NO_RESET_PIN, // Reset pin (default: none) 57 | tWidth width=W_128, // Display width, must be one of enum: W_96 or W_128 (default: W_128). 58 | tHeight height=H_32, // Display height, must be one of enum: H_16, H_32 or OLED::H_64 (default: H_32). 59 | tDisplayCtrl displayCtrl=CTRL_SSD1306, //Display controller chip, must be one of enum: CTRL_SH1106 or CTRL_SSD1306 (default: CTRL_SSD1306). 60 | uint8_t i2c_address=0x3C // I2C address (default: 0x3C) 61 | ); 62 | ``` 63 | Now, minimal instance can be: 64 | 65 | ```C++ 66 | OLED Display(4,5,16); 67 | ``` 68 | 69 | that means: 70 | 71 | ```C++ 72 | // Contructor used for OLED display directly mounted on NodeMCU WiFi_KIT_8 model from Heltec (http://www.heltec.cn/project/wifi-kit-8/?lang=en). 73 | OLED Display(4,5,16,OLED::W_128,OLED::H_32,OLED::CTRL_SSD1306,0x3C); 74 | ``` 75 |   76 | ### 2) Initialize display lib like Serial artuino lib: 77 | 78 | ```C++ 79 | display.begin(); 80 | ``` 81 |   82 | ### 3) Write a string in 5 different ways. All these methods will produce the same output: 83 | 84 | ```C++ 85 | // Method 1: pixel position 86 | display.draw_string(6,8,"Hello World"); 87 | 88 | // Method 2: pixel position 89 | display.setCursor(6,8); 90 | display.print("Hello World"); 91 | 92 | // Method 3: pixel position 93 | display.printf(6,8,"Hello World"); 94 | 95 | // Method 4: pixel position 96 | display.setCursor(6,8); 97 | display.printf("Hello World"); 98 | 99 | // Method 5: ROW and COLUMN position (U8g2 library API wrapper) 100 | display.drawString(1,1,"Hello World"); 101 | ``` 102 |   103 | ### 4) Show on display 104 | Previous function calls only put data in display memory. When You want di show it up, You need to call: 105 | ```C++ 106 | display.display(); 107 | ``` 108 | This call tell to display driver to refresh the view with memory content. 109 |   110 | ### U8g2 wrappers: 111 | Setup U8g2: 112 | ```C++ 113 | #include 114 | U8X8_SSD1306_128X64_NONAME_HW_I2C display(U8X8_PIN_NONE,19,18); 115 | display.begin(); 116 | display.setFont(u8x8_font_chroma48medium8_r); 117 | ``` 118 | Setup this lib (no need to set font): 119 | 120 | ```C++ 121 | #include 122 | OLED display(18,19,NO_RESET_PIN,OLED::W_128,OLED::H_64); 123 | display.begin(); 124 | ``` 125 | Common usage: 126 | ```C++ 127 | display.inverse(); 128 | display.drawString(0,0,"Inverted Hello World"); // first line 129 | display.noInverse(); 130 | display.drawString(0,1,"Normal Hello World"); // second line 131 | ``` 132 | The only difference is that this lib use only one 6x8 font, so, total columns are 21 insead of 16 of U8g2 8x8 font. -------------------------------------------------------------------------------- /examples/WIFI-Kit-8-DEMO/WIFI-Kit-8-DEMO.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // There are 2 different versions of the board 5 | // OLED display=OLED(2,14,4); 6 | OLED display=OLED(4,5,16); 7 | 8 | static const uint8_t bitmap[] = 9 | { 10 | 1,2,4,8,16,32,64,128,128,128,192,192,128,128,128,64,32,16,8,4,2,1, // first page (8 vertical bits, 22 columns) 11 | 255,255,255,255,15,15,15,15,15,15,15,15,15,15,15,15,15,15,255,255,255,255 // second page (8 vertical bits, 22 columns) 12 | }; 13 | 14 | void setup() 15 | { 16 | delay(1000); 17 | display.begin(); 18 | } 19 | 20 | 21 | void loop() 22 | { 23 | // Draw pixels in the outer edges 24 | display.draw_pixel(0,0); 25 | display.draw_pixel(127,0); 26 | display.draw_pixel(127,31); 27 | display.draw_pixel(0,31); 28 | display.display(); 29 | 30 | // Draw hollow circles 31 | for (uint_least8_t radius=3; radius<62; radius+=3) 32 | { 33 | delay(50); 34 | display.draw_circle(64,16,radius); 35 | if (radius>15) 36 | { 37 | display.draw_circle(64,16,radius-15,OLED::SOLID,OLED::BLACK); 38 | } 39 | display.display(); 40 | } 41 | 42 | // Draw solid circles 43 | delay(500); 44 | display.draw_circle(36,16,14,OLED::SOLID); 45 | display.display(); 46 | delay(500); 47 | display.draw_circle(36,16,7,OLED::SOLID,OLED::BLACK); 48 | display.display(); 49 | 50 | // Draw rectangles 51 | delay(500); 52 | display.draw_rectangle(64,0,98,31); 53 | display.display(); 54 | delay(500); 55 | display.draw_rectangle(69,5,93,26,OLED::SOLID); 56 | display.display(); 57 | 58 | // scroll up 59 | delay(1000); 60 | display.scroll_up(32,20); 61 | 62 | // Draw text with normal size 63 | display.draw_string(4,2,"Hello"); 64 | display.display(); 65 | 66 | // Draw a line 67 | delay(1000); 68 | display.draw_line(4,10,34,10); 69 | display.display(); 70 | 71 | // Draw text from program memory with double size 72 | delay(1000); 73 | display.draw_string_P(16,15,PSTR("World!"),OLED::DOUBLE_SIZE); 74 | display.display(); 75 | 76 | // Draw a cross 77 | delay(1000); 78 | display.draw_line(16,31,88,15); 79 | display.draw_line(16,15,88,31); 80 | display.display(); 81 | 82 | // Draw a raw bitmap 83 | delay(1000); 84 | display.draw_bitmap_P(100,8,22,16,bitmap); 85 | display.display(); 86 | 87 | // Demonstrate scrolling 88 | delay(1000); 89 | display.set_scrolling(OLED::HORIZONTAL_RIGHT); 90 | delay(3000); 91 | display.set_scrolling(OLED::HORIZONTAL_LEFT); 92 | delay(3000); 93 | display.set_scrolling(OLED::NO_SCROLLING); 94 | 95 | // Redraw after scrolling to get the original picture 96 | display.display(); 97 | 98 | // Flash the display 99 | delay(1000); 100 | for (int i=0; i<10; i++) 101 | { 102 | display.set_invert(true); 103 | delay(200); 104 | display.set_invert(false); 105 | delay(200); 106 | } 107 | 108 | // Show contrast values 109 | contrast(128); 110 | contrast(64); 111 | contrast(32); 112 | contrast(16); 113 | contrast(8); 114 | contrast(4); 115 | contrast(2); 116 | contrast(1); 117 | contrast(2); 118 | contrast(4); 119 | contrast(8); 120 | contrast(16); 121 | contrast(32); 122 | contrast(64); 123 | contrast(128); 124 | contrast(255); 125 | contrast(128); 126 | delay(3000); 127 | 128 | display.clear(); 129 | } 130 | 131 | 132 | void contrast(int value) 133 | { 134 | char buffer[4]; 135 | display.clear(); 136 | display.draw_string(0,0,"Contrast:"); 137 | itoa(value,buffer,10); 138 | display.draw_string(64,0,buffer); 139 | display.draw_rectangle(0,20,value/2,31,OLED::SOLID); 140 | display.display(); 141 | display.set_contrast(value); 142 | delay(500); 143 | } -------------------------------------------------------------------------------- /examples/WIFI-Kit-8-TTY-DEMO/WIFI-Kit-8-TTY-DEMO.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /** 4 | * This example dimonstrate use of TTY mode of OLED lib 5 | * https://github.com/durydevelop/arduino-lib-oled.git 6 | * 7 | * TTY mode allows You: 8 | * _to call some print function without refresh display 9 | * _to insert a '\r' or '\n' or call println() writeln() functions to automatically position cursor at begin of next line 10 | * 11 | */ 12 | 13 | // There are 2 different versions of the board 14 | // OLED display=OLED(2,14,4); 15 | OLED display=OLED(4,5,16); 16 | int lineNr=0; 17 | 18 | void setup() 19 | { 20 | delay(1000); 21 | display.begin(); 22 | display.setTTYMode(true); 23 | display.printf("TTY:>Press button"); 24 | } 25 | 26 | 27 | void loop() 28 | { 29 | if (digitalRead(0) == LOW) { 30 | lineNr++; 31 | 32 | /** 33 | * First Method: same as Serial.print() 34 | * 3 lines 35 | * display.print('\n'); 36 | * display.print("TTY:>Button press "); 37 | * display.print(lineNr); 38 | */ 39 | 40 | /** 41 | * Second Method: use printf() 42 | * ...only une line 43 | */ 44 | display.printf("\nTTY:>Button press %d",lineNr); 45 | 46 | 47 | if (lineNr == 65535) lineNr=0; 48 | delay(200); 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /examples/WIFI-Kit-8-WiFiScan/WIFI-Kit-8-WiFiScan.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sketch is a part of examples code of ESP9266WiFi lib made by Ivan Grokhotkov 3 | * 4 | * To compile this sketch You need to add this library https://github.com/esp8266/Arduino 5 | * 6 | * This sketch demonstrates how to scan WiFi networks. 7 | * The API is almost the same as with the WiFi Shield library, 8 | * 9 | * OLED class "Display" replace the "Serial" api calls of arduino core. 10 | */ 11 | #include "ESP8266WiFi.h" 12 | #include 13 | 14 | // There are 2 different versions of the board 15 | // OLED Display=OLED(2,14,4); 16 | OLED Display=OLED(4,5,16); 17 | 18 | void setup() { 19 | Display.begin(); // Instead of Serial.begin(9600) 20 | Display.setTTYMode(true); // This set the TTY mode 21 | 22 | // Set WiFi to station mode and disconnect from an AP if it was previously connected 23 | WiFi.mode(WIFI_STA); 24 | WiFi.disconnect(); 25 | delay(100); 26 | 27 | Display.print("Setup done"); 28 | } 29 | 30 | void loop() { 31 | Display.println(); 32 | Display.print("Scan start"); 33 | 34 | // WiFi.scanNetworks will return the number of networks found 35 | int n = WiFi.scanNetworks(); 36 | Display.println("\r\nScan done"); 37 | if (n == 0) 38 | Display.printf("No networks found"); 39 | else 40 | { 41 | Display.printf("%d networks found",n); 42 | for (int i = 0; i < n; ++i) 43 | { 44 | Display.println(); 45 | // Print SSID and RSSI for each network found 46 | Display.print(i + 1); 47 | Display.print(": "); 48 | Display.print(WiFi.SSID(i)); 49 | Display.print(" ("); 50 | Display.print(WiFi.RSSI(i)); 51 | Display.print(")"); 52 | delay(10); 53 | } 54 | } 55 | 56 | // Wait a bit before scanning again 57 | delay(5000); 58 | } 59 | -------------------------------------------------------------------------------- /examples/arduino-onebutton-menu-DEMO/DDigitalButton.cpp: -------------------------------------------------------------------------------- 1 | /*! \mainpage 2 | * Arduino and Raspberry Pi library for handle button press. 3 | * \section intro_sec Introduction 4 | * This library can be used with Arduino or Raspberry Pi, hardware specific api are performed by pre-processor defines. 5 | * 6 | * Futures: 7 | * -Arduino compliant. 8 | * -Raspberry Pi compliant. Both WiringPi and Pigpio library are supported (see includes comments in DDigitalButton.h) 9 | * -In polling mode, all 8 states and triggers can be read and handled: 10 | * -In event mode, 4 triggers fires callback. 11 | * 12 | * State and triggers: 13 | RELEASE STATE: button is not in StatePressed state. 14 | PRESS STATE: button is in StatePressed state. 15 | PRESSED TRIGGER: press-release after PressedMillis time. 16 | LONG_PRESSED TRIGGER: button is kept pressed for longer than LongPressedMillis. 17 | LONG_PRESSING STATE: after LONG_PRESSED, if button still pressed, the state change in LONG_PRESSING until button is released. 18 | DBL_PRESSED TRIGGER: press-release-press within DblPressSpeedMillis time. 19 | DBL_PRESSING STATE: after DBL_PRESSED, if button still pressed, the state change in DBL_PRESSING until button is released. 20 | RELEASED TRIGGER: release after PRESSED, LONG_PRESSED, DBL_PRESSED. 21 | * 22 | * \section usage_sec Usage 23 | * 24 | * @code 25 | * 26 | * @endcode 27 | */ 28 | 29 | #include "DDigitalButton.h" 30 | 31 | /** 32 | * @brief Constructor 33 | * @param DigitalPin -> Pin number where button is connected. 34 | * @param PressedState -> Can be HIGH or LOW and it is the pin level for which the button is considered to be pressed. 35 | * @param PullUp -> If true the internal pull-up resistor is connected. 36 | * @param PressedMillis -> Time in milliseconds after which "press and release" is triggered as PRESSED. 37 | * @param LongPressedMillis -> Time in milliseconds after which a "keeping press" is triggered as LONG_PRESSED. 38 | * @param DblPressSpeedMillis -> Time in milliseconds before which "press, release, press" is triggered as DBL_PRESSED. 39 | */ 40 | DDigitalButton::DDigitalButton(int DigitalPin, uint8_t PressedState, bool PullUp, unsigned int PressedMillis, unsigned int LongPressedMillis, unsigned int DblPressSpeedMillis) 41 | { 42 | // User settings 43 | Pin=DigitalPin; 44 | StatePressed=PressedState; 45 | PressedDuration=PressedMillis; 46 | LongPressedDuration=LongPressedMillis; 47 | DblPressSpeedDuration=DblPressSpeedMillis; 48 | 49 | // Init timers 50 | ReleaseMs=NowMillis(); 51 | PressMs=ReleaseMs; 52 | PressedMs=ReleaseMs; 53 | 54 | // Trigger Callback 55 | Callback=NULL; 56 | 57 | // Pull-up 58 | if (PullUp) { 59 | SetPinMode(INPUT_PULLUP); 60 | } 61 | else { 62 | SetPinMode(INPUT); 63 | } 64 | 65 | // Read current input state 66 | Read(); 67 | } 68 | 69 | /** 70 | * Enable event mode: set a callback that is called on each TRIGGER state. 71 | * @param EventCallback -> Function to call. 72 | * Callback function must be used in this way: 73 | * @code 74 | * void EventCallback(DDigitalButton::State trigger) { 75 | * if (trigger == DDigitalButton::PRESSED) { 76 | * // Do something 77 | * } 78 | * } 79 | */ 80 | void DDigitalButton::SetEventCallback(DCallback EventCallback) 81 | { 82 | Callback=EventCallback; 83 | } 84 | 85 | //! @return the current button State. 86 | DDigitalButton::State DDigitalButton::Read(void) 87 | { 88 | bool Trig=false; 89 | 90 | if (PrevState == RELEASED) { 91 | CurrState=RELEASE; 92 | } 93 | if (PrevState == PRESSED) { 94 | CurrState=PRESS; 95 | } 96 | 97 | if (ReadPin() == StatePressed) { 98 | PressMs=NowMillis(); // Press time 99 | if (PrevState == RELEASE) { 100 | if ((PressMs-ReleaseMs) > 50) { 101 | CurrState=PRESS; 102 | } 103 | } 104 | else if (PrevState == PRESS) { 105 | if ((PressMs-PressedMs) < DblPressSpeedDuration) { 106 | CurrState=DBL_PRESSED; 107 | Trig=true; 108 | } 109 | if ((PressMs-ReleaseMs) > LongPressedDuration) { 110 | CurrState=LONG_PRESSED; 111 | Trig=true; 112 | } 113 | } 114 | else if (PrevState == LONG_PRESSED) { 115 | CurrState=LONG_PRESSING; 116 | } 117 | else if (PrevState == DBL_PRESSED) { 118 | CurrState=DBL_PRESSING; 119 | } 120 | } 121 | else { 122 | ReleaseMs=NowMillis(); // Release time 123 | if (PrevState == PRESS) { 124 | if ((ReleaseMs-PressMs) > PressedDuration) { 125 | CurrState=PRESSED; 126 | Trig=true; 127 | PressMs=ReleaseMs; 128 | } 129 | else if ((ReleaseMs-PressMs) > 50) { 130 | PressedMs=NowMillis(); 131 | } 132 | } 133 | else if (PrevState == PRESSED || PrevState == LONG_PRESSING || PrevState == LONG_PRESSED) { 134 | CurrState=RELEASED; 135 | Trig=true; 136 | } 137 | else { 138 | CurrState=RELEASE; 139 | } 140 | } 141 | 142 | PrevState=CurrState; 143 | if (Trig) { 144 | if (Callback != NULL) { 145 | //Serial.println("Callback"); 146 | Callback(CurrState); 147 | } 148 | } 149 | 150 | return (CurrState); 151 | } 152 | 153 | //! Overload operator == 154 | DDigitalButton::operator DDigitalButton::State() 155 | { 156 | return Read(); 157 | } 158 | 159 | //! Wrapper function to set mode for pin (INPUT or INPUT_PULLUP) 160 | void DDigitalButton::SetPinMode(uint8_t Mode) 161 | { 162 | #ifdef ARDUINO 163 | pinMode(Pin,Mode); 164 | #else 165 | #ifdef PIGPIO_VERSION 166 | gpioSetMode(Pin,Mode); 167 | #else 168 | pinMode(Pin,Mode); 169 | #endif 170 | #endif 171 | } 172 | 173 | //! Wrapper function to read direct pin level. 174 | /** 175 | * @return pin level (HIGH or LOW) 176 | */ 177 | uint8_t DDigitalButton::ReadPin() { 178 | #ifdef ARDUINO 179 | return(digitalRead(Pin)); 180 | #else 181 | #ifdef PIGPIO_VERSION 182 | return(gpioRead(Pin)); 183 | #else 184 | return(digitalRead(Pin)); 185 | #endif 186 | #endif 187 | } 188 | 189 | //! Wrapper function for getting arduino "millis()" function value 190 | unsigned long DDigitalButton::NowMillis(void) 191 | { 192 | #ifdef ARDUINO 193 | return(millis()); 194 | #else 195 | #ifdef PIGPIO_VERSION 196 | // c++ equivalent of arduino millis() 197 | //extern unsigned int millis (void) ; 198 | return(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); 199 | #else 200 | return(millis()); 201 | #endif 202 | #endif 203 | } 204 | 205 | /* 206 | long millis(){ 207 | struct timespec _t; 208 | clock_gettime(CLOCK_REALTIME, &_t); 209 | return _t.tv_sec*1000 + lround(_t.tv_nsec/1.0e6); 210 | } 211 | */ 212 | -------------------------------------------------------------------------------- /examples/arduino-onebutton-menu-DEMO/DDigitalButton.h: -------------------------------------------------------------------------------- 1 | #ifndef DDigitalButtonH 2 | #define DDigitalButtonH 3 | 4 | #ifdef ARDUINO 5 | // Arduino inlcudes 6 | #if ARDUINO >= 100 7 | #include "Arduino.h" 8 | #else 9 | #include "wiring.h" 10 | #include "WProgram.h" 11 | #endif 12 | #else 13 | // RPi includes 14 | #include 15 | // include lib which want to use (if both are included, pigpio is used) 16 | #include 17 | //#include 18 | 19 | #ifndef __WIRING_PI_H__ 20 | // for NowMillis() 21 | #include 22 | #endif 23 | #endif 24 | 25 | // Set arduino compatibily state defines 26 | #ifndef HIGH 27 | #define HIGH 1 28 | #endif 29 | 30 | #ifndef LOW 31 | #define LOW 0 32 | #endif 33 | 34 | #ifndef INPUT 35 | #define INPUT 0 36 | #endif 37 | 38 | #ifndef OUTPUT 39 | #define OUTPUT 1 40 | #endif 41 | 42 | #ifndef INPUT_PULLUP 43 | #define INPUT_PULLUP 2 44 | #endif 45 | 46 | class DDigitalButton{ 47 | 48 | public: 49 | enum State {RELEASE, //! Release STATE: button is not in StatePressed state. 50 | PRESS, //! Press STATE: button is in StatePressed state. 51 | PRESSED, //! Pressed TRIGGER: press-release after PressedMillis time. 52 | LONG_PRESSED, //! Long pressed TRIGGER: button is kept pressed for longer than LongPressedMillis. 53 | LONG_PRESSING, //! Long Pressing STATE: after LONG_PRESSED, if button still pressed, the state change in LONG_PRESSING until button is released. 54 | DBL_PRESSED, //! Double pressed TRIGGER: press-release-press within DblPressSpeedMillis time. 55 | DBL_PRESSING, //! Double pressing STATE: after DBL_PRESSED, if button still pressed, the state change in DBL_PRESSING until button is released. 56 | RELEASED //! Released TRIGGER: release after PRESSED, LONG_PRESSED, DBL_PRESSED. 57 | }; 58 | 59 | typedef void (*DCallback)(State); 60 | 61 | DDigitalButton(int DigitalPin, uint8_t PressedState = LOW, bool PullUp = true, unsigned int PressedMillis = 100, unsigned int LongPressedMillis = 1000, unsigned int DblPressSpeedMillis=100); 62 | void SetEventCallback(DCallback EventCallback); 63 | DDigitalButton::State Read(void); 64 | operator DDigitalButton::State(); 65 | 66 | private: 67 | void SetPinMode(uint8_t pinMode); 68 | uint8_t ReadPin(void); 69 | unsigned long NowMillis(void); 70 | uint8_t Pin; 71 | State CurrState; 72 | State PrevState; 73 | uint8_t StatePressed; 74 | 75 | unsigned long ReleaseMs; 76 | unsigned long PressMs; 77 | unsigned long PressedMs; 78 | 79 | unsigned long PressedDuration; 80 | unsigned long DblPressSpeedDuration; 81 | unsigned long LongPressedDuration; 82 | DCallback Callback; 83 | }; 84 | #endif 85 | -------------------------------------------------------------------------------- /examples/arduino-onebutton-menu-DEMO/DMenu.cpp: -------------------------------------------------------------------------------- 1 | #include "DMenu.h" 2 | 3 | /** 4 | * @brief Construct a new DMenu::DMenu object 5 | * 6 | * @param MenuItemName -> name of new menu item. 7 | * @param MenuID -> ID of this menu. You can use it to handle it by id instead of by name. Can be a value from 0 to 254. 8 | * @param ParentItem -> pointer to the parent item of the new one. If parameter is NULL the menu item is the root one and Back() does't fire the Callback. 9 | * @param CallbackFunc -> pointer to the DMenuListener function that will be called on Select() or Back() call. 10 | */ 11 | DMenu::DMenu(const char MenuItemName[], uint8_t MenuID, DMenu *ParentItem, DMenuListener CallbackFunc) 12 | { 13 | Init(MenuItemName,MenuID,ParentItem,CallbackFunc); 14 | } 15 | 16 | /** 17 | * @brief 18 | * 19 | * @param MenuItemName -> name of new menu item. 20 | * @param MenuID -> ID of this menu. You can use it to handle it by id instead of by name. Can be a value from 0 to 254. 21 | * @param ParentItem -> pointer to the parent item of the new one. If parameter is NULL the menu item is the root one and Back() does't fire the Callback. 22 | * @param CallbackFunc -> pointer to the DMenuListener function that will be called on Select() or Back() call. 23 | */ 24 | void DMenu::Init(const char MenuItemName[], uint8_t MenuID, DMenu *ParentItem, DMenuListener CallbackFunc) 25 | { 26 | Callback=CallbackFunc; 27 | 28 | if (ParentItem == NULL) { 29 | Parent=this; 30 | } 31 | else { 32 | Parent=ParentItem; 33 | } 34 | 35 | //ID=rand() % DMENU_MAX_ITEMS + 1; 36 | MenuID=ID; 37 | ItemIndex=-1; 38 | ItemsCount=0; 39 | Loop=false; 40 | MaxItemTextLen=DMENU_DEFAULT_MAX_ITEM_TEXT_LEN; 41 | Name=NULL; 42 | SetName(MenuItemName); 43 | } 44 | 45 | //! Destructor 46 | DMenu::~DMenu() 47 | { 48 | // free all created Items 49 | if (ItemsCount > 0) { 50 | for (short int ixI=0;ixI MaxItemTextLen) { 68 | len=MaxItemTextLen; 69 | } 70 | if (Name == NULL) { 71 | Name=new char[len+1]; 72 | } 73 | strncpy(Name,ItemName,len); 74 | Name[len]='\0'; 75 | } 76 | 77 | /** 78 | * @brief Add new menu item. 79 | * 80 | * @param ItemName -> Name of Item. 81 | * @param MenuID -> ID of new sub menu. You can use it to handle it by id instead of by name. Can be a value from 0 to 254. 82 | * @param SetCurrent -> if true set new Item as current one. 83 | * 84 | * @return true of the created item or false if any error occours. 85 | */ 86 | bool DMenu::AddItem(const char ItemName[], uint8_t ItemID, bool SetCurrent) 87 | { 88 | if (ItemsCount == 0) { 89 | // No Items yet 90 | // Allocate first DMenu pointer 91 | Items=(DMenu **) malloc(sizeof(DMenu*)); 92 | if (Items == NULL) { 93 | return 0; 94 | } 95 | } 96 | else { 97 | // Items alreay present 98 | // try to allocate an other DMenu pointer 99 | DMenu **tmp=(DMenu **) realloc(Items,sizeof(DMenu*)*(ItemsCount+1)); 100 | if (tmp == NULL) { 101 | return 0; 102 | } 103 | Items=tmp; 104 | } 105 | 106 | // Create new DMenu and assign it to last array pointer 107 | DMenu *NewItem=new DMenu(ItemName,ItemID,this,Callback); 108 | if (NewItem == NULL) { 109 | return 0; 110 | } 111 | 112 | Items[ItemsCount]=NewItem; 113 | // Inc items counter 114 | ItemsCount++; 115 | 116 | if (SetCurrent || ItemsCount == 1) { 117 | // Set as current item (also if it is the first one) 118 | ItemIndex=ItemsCount-1; 119 | } 120 | 121 | return(NewItem->GetID()); 122 | } 123 | 124 | //! @return Item at Index position 125 | DMenu* DMenu::GetItem(short int Index) 126 | { 127 | if (ItemsCount == 0) { 128 | return(this); 129 | } 130 | 131 | return(Items[Index]); 132 | } 133 | 134 | //! @return current selected Item inside this Menu 135 | DMenu* DMenu::GetCurrItem(void) 136 | { 137 | if (ItemIndex == -1) { 138 | return(this); 139 | } 140 | return(Items[ItemIndex]); 141 | } 142 | 143 | //! @return parent Item 144 | /** 145 | * N.B. If this menu has no Parent the function return this 146 | **/ 147 | DMenu *DMenu::GetParent(void) 148 | { 149 | return(Parent); 150 | } 151 | 152 | bool DMenu::HasParent(void) 153 | { 154 | return (Parent != NULL); 155 | } 156 | 157 | //! @return numbers of DMenu Items in this Menu 158 | uint8_t DMenu::GetItemsCount(void) 159 | { 160 | return(ItemsCount); 161 | } 162 | 163 | //! Change current selected Item to previous one 164 | /** 165 | * @return -> a pointer to new current selected Item 166 | */ 167 | DMenu* DMenu::Up(void) 168 | { 169 | if (ItemsCount == 0) { 170 | return(this); 171 | } 172 | 173 | if (ItemIndex == -1) { 174 | ItemIndex=0; 175 | } 176 | else if (ItemIndex == 0) { 177 | if (Loop) { 178 | ItemIndex=ItemsCount-1; 179 | } 180 | } 181 | else if (ItemIndex > 0) { 182 | ItemIndex--; 183 | } 184 | 185 | if (Callback != NULL) { 186 | Callback(Items[ItemIndex],DMENU_ACTION_UP); 187 | } 188 | 189 | return(Items[ItemIndex]); 190 | } 191 | 192 | //! Change current selected Item to next one 193 | /** 194 | * @return -> a pointer to new current selected Item 195 | */ 196 | DMenu* DMenu::Down(void) 197 | { 198 | if (ItemsCount == 0) { 199 | return(this); 200 | } 201 | 202 | if (ItemIndex == -1) { 203 | ItemIndex=ItemsCount-1; 204 | } 205 | else if (ItemIndex == ItemsCount-1) { 206 | if (Loop) { 207 | ItemIndex=0; 208 | } 209 | } 210 | else if (ItemIndex < ItemsCount-1) { 211 | ItemIndex++; 212 | } 213 | 214 | if (Callback != NULL) { 215 | Callback(Items[ItemIndex],DMENU_ACTION_DOWN); 216 | } 217 | 218 | return(Items[ItemIndex]); 219 | } 220 | 221 | //! @return the Parent Item of this 222 | /** 223 | * If this have a Parent Item, Calling Back() will also fire a Callback event passing the Parent Item as argument, otherwise return NULL. 224 | */ 225 | DMenu* DMenu::Back(void) 226 | { 227 | if (Callback != NULL) { 228 | Callback(Parent,DMENU_ACTION_BACK); 229 | } 230 | return(Parent); 231 | } 232 | 233 | //! Fire a Callback event sending the current selected Item as argument 234 | /** 235 | * N.B. Callback will be called only if there is a selected Item. 236 | **/ 237 | DMenu* DMenu::Select(void) 238 | { 239 | if (ItemIndex == -1) { 240 | return(this); 241 | } 242 | 243 | if (Callback != NULL) { 244 | Callback(Items[ItemIndex],DMENU_ACTION_SELECT); 245 | } 246 | 247 | return (Items[ItemIndex]); 248 | } 249 | 250 | //! @return name string of this Item 251 | const char* DMenu::GetName(void) 252 | { 253 | return(Name); 254 | } 255 | 256 | bool DMenu::NameEquals(const char MenuName[]) 257 | { 258 | return (strcmp(MenuName,Name) == 0); 259 | } 260 | 261 | //! @return ID of this Item 262 | uint8_t DMenu::GetID(void) 263 | { 264 | return(ID); 265 | } 266 | 267 | //! @return zero based index of current selected item 268 | /** 269 | * if -1 is returned, no item is selected 270 | **/ 271 | 272 | short int DMenu::GetCurrItemIndex(void) 273 | { 274 | return(ItemIndex); 275 | } 276 | -------------------------------------------------------------------------------- /examples/arduino-onebutton-menu-DEMO/DMenu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Class: DMenu 3 | * Created on: 2018 4 | * Author: dury 5 | * Version: 1.0 6 | */ 7 | 8 | #ifndef DMENU_H 9 | #define DMENU_H 10 | 11 | //#include "mbed.h" 12 | 13 | #if defined(ARDUINO) 14 | #if ARDUINO >= 100 15 | #include "Arduino.h" 16 | #else 17 | #include "wiring.h" 18 | #include "WProgram.h" 19 | #endif 20 | #else 21 | #include 22 | #include 23 | using namespace std; 24 | #endif // defined 25 | 26 | // DMenu defines 27 | #define DMENU_DEFAULT_MAX_ITEM_TEXT_LEN 21 28 | #define DMENU_MAX_ITEMS 255 29 | #define DMENU_ACTION_UP 1 30 | #define DMENU_ACTION_DOWN 2 31 | #define DMENU_ACTION_BACK 3 32 | #define DMENU_ACTION_SELECT 4 33 | 34 | class DMenu { 35 | public: 36 | typedef void (*DMenuListener) (DMenu *MenuItem, uint8_t Action); 37 | DMenu(const char MenuItemName[], uint8_t MenuID=0, DMenu *Parent=NULL, DMenuListener CallbackFunc=NULL); 38 | ~DMenu(); 39 | void Init(const char MenuItemName[], uint8_t MenuID, DMenu *ParentItem, DMenuListener CallbackFunc); 40 | void SetName(const char ItemName[]); 41 | bool AddItem(const char Name[], uint8_t ItemID=0, bool SetCurrent=false); 42 | DMenu* Up(void); 43 | DMenu* Down(void); 44 | DMenu* Back(void); 45 | DMenu* Select(void); 46 | 47 | DMenu* GetItem(short int Index); 48 | DMenu* GetCurrItem(void); 49 | DMenu* GetParent(void); 50 | bool HasParent(void); 51 | uint8_t GetItemsCount(void); 52 | const char* GetName(void); 53 | bool NameEquals(const char MenuName[]); 54 | uint8_t GetID(void); 55 | short int GetCurrItemIndex(void); 56 | bool Loop; 57 | 58 | private: 59 | DMenuListener Callback; 60 | char *Name; 61 | DMenu *Parent; 62 | short int ItemIndex; //! Index used to handle the current selected Item 63 | uint8_t ItemsCount; 64 | uint8_t ID; 65 | uint8_t MaxItemTextLen; 66 | DMenu** Items; 67 | 68 | }; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /examples/arduino-onebutton-menu-DEMO/arduino-onebutton-menu-DEMO.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "DMenu.h" 3 | #include "DDigitalButton.h" 4 | 5 | #include // **** OLED **** 6 | //#include // **** U8g2 **** 7 | 8 | // For compatibility with U8g2 font 9 | #define COLS 16 10 | #define ROWS 8 11 | 12 | DMenu* CreateMainMenu(void); 13 | DMenu* CreateSubMenu1(void); 14 | DMenu* CreateSubMenu2(void); 15 | void PrintMenu(DMenu *Menu, uint8_t X = 0, uint8_t Y = 0); 16 | void Debug(const String& msg); 17 | 18 | OLED display(18,19,NO_RESET_PIN,OLED::W_128,OLED::H_64); // **** OLED **** 19 | //U8X8_SSD1306_128X64_NONAME_HW_I2C display(U8X8_PIN_NONE,19,18); // **** U8g2 **** 20 | 21 | // Button 22 | DDigitalButton *Button=NULL; 23 | // Menu 24 | DMenu *CurrMenu=NULL; 25 | 26 | void setup() 27 | { 28 | // Begin display 29 | display.begin(); 30 | //display.setFont(u8x8_font_chroma48medium8_r); // **** only for U8g2 **** 31 | 32 | // Begin serial debug 33 | Serial.begin(9600); 34 | Debug("Start"); 35 | 36 | // Create Button 37 | Button=new DDigitalButton(6); 38 | Debug("Button on input pin 6"); 39 | 40 | // Create MainMenu 41 | CurrMenu=CreateMainMenu(); 42 | PrintMenu(CurrMenu); 43 | } 44 | 45 | // Poll Button and handle read status 46 | void loop() 47 | { 48 | DMenu *SelItem; 49 | 50 | switch (Button->Read()) { 51 | case DDigitalButton::PRESSED: 52 | Debug(""); 53 | CurrMenu->Down(); 54 | PrintMenu(CurrMenu); 55 | break; 56 | case DDigitalButton::LONG_PRESSED: { 57 | Debug(""); 58 | SelItem=CurrMenu->Select(); 59 | String ItemName=SelItem->GetName(); 60 | Debug("Item "+ItemName); 61 | if (ItemName == "SubMenu 1") { 62 | CurrMenu=CreateSubMenu1(); 63 | PrintMenu(CurrMenu); 64 | } 65 | else if (ItemName == "SubMenu 2") { 66 | CurrMenu=CreateSubMenu2(); 67 | PrintMenu(CurrMenu); 68 | } 69 | else if (ItemName == "Back") { 70 | if (String(CurrMenu->GetName()) == "SubMenu 1" || String(CurrMenu->GetName()) == "SubMenu 2") { 71 | CurrMenu=CreateMainMenu(); 72 | } 73 | PrintMenu(CurrMenu); 74 | } 75 | break; 76 | } 77 | default: 78 | break; 79 | } 80 | } 81 | 82 | DMenu* CreateMainMenu(void) 83 | { 84 | if (CurrMenu != NULL) { 85 | // Only one menu must be in memory 86 | delete CurrMenu; 87 | } 88 | 89 | CurrMenu=new DMenu("Main Menu"); 90 | CurrMenu->Loop=true; 91 | 92 | CurrMenu->AddItem("SubMenu 1",true); 93 | CurrMenu->AddItem("SubMenu 2"); 94 | 95 | return(CurrMenu); 96 | } 97 | 98 | DMenu* CreateSubMenu1(void) 99 | { 100 | if (CurrMenu != NULL) { 101 | // Only one menu must be in memory 102 | delete CurrMenu; 103 | } 104 | 105 | CurrMenu=new DMenu("SubMenu 1"); 106 | CurrMenu->Loop=true; 107 | 108 | CurrMenu->AddItem("Execute",true); 109 | CurrMenu->AddItem("Back"); 110 | 111 | return(CurrMenu); 112 | } 113 | 114 | DMenu* CreateSubMenu2(void) 115 | { 116 | if (CurrMenu != NULL) { 117 | // Only one menu must be in memory 118 | delete CurrMenu; 119 | } 120 | 121 | CurrMenu=new DMenu("SubMenu 2"); 122 | CurrMenu->Loop=true; 123 | 124 | CurrMenu->AddItem("Execute",true); 125 | CurrMenu->AddItem("Back"); 126 | 127 | return(CurrMenu); 128 | } 129 | 130 | void PrintMenu(DMenu* Menu, uint8_t X, uint8_t Y) 131 | { 132 | display.clear(); 133 | // Line buffer 134 | char Line[COLS+1]; 135 | memset(Line,' ',COLS+1); 136 | 137 | // Create centered menu title line 138 | uint8_t nameLen=strlen(Menu->GetName()); 139 | uint8_t startCol=(COLS-nameLen)/2; 140 | memcpy(&Line[startCol],Menu->GetName(),nameLen); 141 | Line[COLS]='\0'; 142 | 143 | // Print menu title 144 | display.drawString(X,Y,Line); 145 | 146 | // Starts from second line 147 | for (uint8_t iY=Y; iYGetItemsCount();iY++) { 148 | DMenu* Item=CurrMenu->GetItem(iY); 149 | // Starts with item name 150 | uint8_t itemLen=strlen(Item->GetName()); 151 | memcpy(Line,Item->GetName(),itemLen); 152 | // Fill with spaces 153 | memset(&Line[itemLen],' ',COLS-itemLen); 154 | // Add NULL 155 | Line[COLS]='\0'; 156 | if (iY == CurrMenu->GetCurrItemIndex()) { 157 | // Draw selected 158 | display.inverse(); 159 | //display.drawString(X,iY+2,">"); 160 | display.drawString(X,iY+2,Line); 161 | display.noInverse(); 162 | } 163 | else { 164 | // Draw not selected 165 | //display.drawString(X,iY+2," "); 166 | display.drawString(X,iY+2,Line); 167 | } 168 | //display.drawString(X+2,iY+2,Line); 169 | } 170 | display.display(); // **** only for OLED **** 171 | } 172 | 173 | void Debug(const String& msg) 174 | { 175 | Serial.println(msg); 176 | } 177 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map OLED 3 | ####################################### 4 | 5 | ####################################### 6 | # Class (KEYWORD1) 7 | ####################################### 8 | 9 | OLED KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | begin KEYWORD2 15 | set_power KEYWORD2 16 | set_invert KEYWORD2 17 | set_contrast KEYWORD2 18 | set_scrolling KEYWORD2 19 | scroll_up KEYWORD2 20 | display KEYWORD2 21 | clear KEYWORD2 22 | draw_bitmap KEYWORD2 23 | draw_bitmap_P KEYWORD2 24 | draw_character KEYWORD2 25 | draw_string KEYWORD2 26 | draw_string_P KEYWORD2 27 | draw_pixel KEYWORD2 28 | draw_line KEYWORD2 29 | draw_circle KEYWORD2 30 | draw_rectangle KEYWORD2 31 | setTTYMode KEYWORD2 32 | 33 | ####################################### 34 | # Constants (LITERAL1) 35 | ####################################### 36 | sda_pin LITERAL1 37 | scl_pin LITERAL1 38 | reset_pin LITERAL1 39 | width LITERAL1 40 | height LITERAL1 41 | i2c_address LITERAL1 42 | isSH1106 LITERAL1 43 | pages LITERAL1 44 | pages LITERAL1 45 | 46 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=OLED SSD1306 - SH1106 2 | version=1.0.7 3 | author=Fabio Durigon - Stefan Frings 4 | maintainer=Fabio Durigon (develop@dury.it) 5 | sentence=Supported OLED display chip: SSD1306 or SH1106. Supported Interface: I2C (internal driven) 6 | paragraph=This library supports all print() and write() calls as the internal Serial lib of Arduino core. Added also printf() std function call. TTY mode: Display can be used like a terminal window (without positioning the cursor before print), it will scroll up automatically when print function contains a '\r' '\n' character in last screen text line. 7 | category=Display 8 | url=https://github.com/durydevelop/arduino-lib-oled 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/oled.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "oled.h" 3 | 4 | static const uint8_t oled_font6x8 [] PROGMEM = { 5 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp 6 | 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // ! 7 | 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // " 8 | 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // # 9 | 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $ 10 | 0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // % 11 | 0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // & 12 | 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // ' 13 | 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // ( 14 | 0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // ) 15 | 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // * 16 | 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // + 17 | 0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // , 18 | 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // - 19 | 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // . 20 | 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // / 21 | 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 22 | 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 23 | 0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2 24 | 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3 25 | 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 26 | 0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5 27 | 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 28 | 0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7 29 | 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8 30 | 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 31 | 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // : 32 | 0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ; 33 | 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // < 34 | 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // = 35 | 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // > 36 | 0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ? 37 | 0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @ 38 | 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A 39 | 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B 40 | 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C 41 | 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D 42 | 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E 43 | 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F 44 | 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G 45 | 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H 46 | 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I 47 | 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J 48 | 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K 49 | 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L 50 | 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M 51 | 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N 52 | 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O 53 | 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P 54 | 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q 55 | 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R 56 | 0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S 57 | 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T 58 | 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U 59 | 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V 60 | 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W 61 | 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X 62 | 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y 63 | 0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z 64 | 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [ 65 | 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, // backslash 66 | 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ] 67 | 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^ 68 | 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _ 69 | 0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // ' 70 | 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a 71 | 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b 72 | 0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c 73 | 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d 74 | 0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e 75 | 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f 76 | 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g 77 | 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h 78 | 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i 79 | 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j 80 | 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k 81 | 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l 82 | 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m 83 | 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n 84 | 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o 85 | 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p 86 | 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q 87 | 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r 88 | 0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s 89 | 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t 90 | 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u 91 | 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v 92 | 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w 93 | 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x 94 | 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y 95 | 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, // z 96 | 0x00, 0x00, 0x08, 0x77, 0x41, 0x00, // { 97 | 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, // ¦ 98 | 0x00, 0x00, 0x41, 0x77, 0x08, 0x00, // } 99 | 0x00, 0x08, 0x04, 0x08, 0x08, 0x04, // ~ 100 | 0x00, 0x3D, 0x40, 0x40, 0x20, 0x7D, // ü 101 | 0x00, 0x3D, 0x40, 0x40, 0x40, 0x3D, // Ü 102 | 0x00, 0x21, 0x54, 0x54, 0x54, 0x79, // ä 103 | 0x00, 0x7D, 0x12, 0x11, 0x12, 0x7D, // Ä 104 | 0x00, 0x39, 0x44, 0x44, 0x44, 0x39, // ö 105 | 0x00, 0x3D, 0x42, 0x42, 0x42, 0x3D, // Ö 106 | 0x00, 0x02, 0x05, 0x02, 0x00, 0x00, // ° 107 | 0x00, 0x7E, 0x01, 0x49, 0x55, 0x73, // ß 108 | }; 109 | 110 | OLED::OLED(uint8_t sda_pin, uint8_t scl_pin, uint8_t reset_pin, uint8_t i2c_address, uint_fast8_t width, uint_fast8_t height, bool isSH1106) : 111 | sda_pin(sda_pin), 112 | scl_pin(scl_pin), 113 | reset_pin(reset_pin), 114 | width(width), 115 | height(height), 116 | i2c_address(i2c_address), 117 | pages((height + 7) / 8), 118 | bufsize(static_cast (pages*width)) 119 | { 120 | this->buffer = (uint8_t *) malloc(bufsize); // two dimensional array of n pages each of n columns. 121 | X=0; 122 | Y=0; 123 | ttyMode=OLED_DEFAULT_TTY_MODE; 124 | if (isSH1106) { 125 | displayController=CTRL_SH1106; 126 | } 127 | else { 128 | displayController=CTRL_SSD1306; 129 | } 130 | fontInverted=false; 131 | usingOffset=false; 132 | } 133 | 134 | OLED::OLED(uint8_t sda_pin, uint8_t scl_pin, uint8_t reset_pin, tWidth width, tHeight height, tDisplayCtrl displayCtrl, uint8_t i2c_address) : 135 | sda_pin(sda_pin), 136 | scl_pin(scl_pin), 137 | reset_pin(reset_pin), 138 | width(width), 139 | height(height), 140 | i2c_address(i2c_address), 141 | displayController(displayCtrl), 142 | pages((height + 7) / 8), 143 | bufsize(static_cast (pages*width)) 144 | { 145 | this->buffer = (uint8_t *) malloc(bufsize); // two dimensional array of n pages each of n columns. 146 | X=0; 147 | Y=0; 148 | ttyMode=OLED_DEFAULT_TTY_MODE; 149 | fontInverted=false; 150 | usingOffset=false; 151 | } 152 | 153 | OLED::~OLED() 154 | { 155 | free(buffer); 156 | } 157 | 158 | void OLED::i2c_start() 159 | { 160 | while (!digitalRead(sda_pin) || !digitalRead(scl_pin)); 161 | digitalWrite(sda_pin, LOW); 162 | #if !defined OUTPUT_OPEN_DRAIN 163 | pinMode(sda_pin, OUTPUT); 164 | #endif 165 | OLED_I2C_DELAY; 166 | digitalWrite(scl_pin, LOW); 167 | #if !defined OUTPUT_OPEN_DRAIN 168 | pinMode(scl_pin, OUTPUT); 169 | #endif 170 | OLED_I2C_DELAY; 171 | } 172 | 173 | void OLED::i2c_stop() 174 | { 175 | #if !defined OUTPUT_OPEN_DRAIN 176 | pinMode(scl_pin, INPUT); 177 | #endif 178 | digitalWrite(scl_pin, HIGH); 179 | OLED_I2C_DELAY; 180 | #if !defined OUTPUT_OPEN_DRAIN 181 | pinMode(sda_pin, INPUT); 182 | #endif 183 | digitalWrite(sda_pin, HIGH); 184 | OLED_I2C_DELAY; 185 | while (!digitalRead(sda_pin) || !digitalRead(scl_pin)); 186 | } 187 | 188 | bool OLED::i2c_send(uint8_t byte) 189 | { 190 | for (int_fast8_t bit = 7; bit >= 0; bit--) 191 | { 192 | if (byte & 128) 193 | { 194 | #if !defined OUTPUT_OPEN_DRAIN 195 | pinMode(sda_pin, INPUT); 196 | #endif 197 | digitalWrite(sda_pin, HIGH); 198 | } 199 | else 200 | { 201 | digitalWrite(sda_pin, LOW); 202 | #if !defined OUTPUT_OPEN_DRAIN 203 | pinMode(sda_pin, OUTPUT); 204 | #endif 205 | } 206 | OLED_I2C_DELAY; 207 | #if !defined OUTPUT_OPEN_DRAIN 208 | pinMode(scl_pin, INPUT); 209 | #endif 210 | digitalWrite(scl_pin, HIGH); 211 | OLED_I2C_DELAY; 212 | while (!digitalRead(scl_pin)); 213 | digitalWrite(scl_pin, LOW); 214 | #if !defined OUTPUT_OPEN_DRAIN 215 | pinMode(scl_pin, OUTPUT); 216 | #endif 217 | OLED_I2C_DELAY; 218 | byte = byte << 1; 219 | } 220 | // Receive ACK 221 | #if !defined OUTPUT_OPEN_DRAIN 222 | pinMode(sda_pin, INPUT); 223 | #endif 224 | digitalWrite(sda_pin, HIGH); 225 | #if !defined OUTPUT_OPEN_DRAIN 226 | pinMode(scl_pin, INPUT); 227 | #endif 228 | digitalWrite(scl_pin, HIGH); 229 | OLED_I2C_DELAY; 230 | while (!digitalRead(scl_pin)); 231 | bool ack = digitalRead(sda_pin) == 0; 232 | digitalWrite(scl_pin, LOW); 233 | #if !defined OUTPUT_OPEN_DRAIN 234 | pinMode(scl_pin, OUTPUT); 235 | #endif 236 | OLED_I2C_DELAY; 237 | return ack; 238 | } 239 | 240 | void OLED::begin() 241 | { 242 | #if defined OUTPUT_OPEN_DRAIN 243 | pinMode(sda_pin, OUTPUT_OPEN_DRAIN); 244 | digitalWrite(sda_pin, HIGH); 245 | pinMode(scl_pin, OUTPUT_OPEN_DRAIN); 246 | digitalWrite(scl_pin, HIGH); 247 | #else 248 | pinMode(sda_pin, INPUT); 249 | pinMode(scl_pin, INPUT); 250 | #endif 251 | if (reset_pin != NO_RESET_PIN) 252 | { 253 | pinMode(reset_pin, OUTPUT); 254 | digitalWrite(reset_pin, LOW); 255 | delay(10); 256 | digitalWrite(reset_pin, HIGH); 257 | } 258 | delay(100); 259 | i2c_start(); 260 | i2c_send(i2c_address << 1); // address + write 261 | i2c_send(0x00); // command 262 | i2c_send(0xAE); // display off 263 | i2c_send(0xD5); // clock divider 264 | i2c_send(0x80); 265 | i2c_send(0xA8); // multiplex ratio 266 | i2c_send(height - 1); 267 | i2c_send(0xD3); // no display offset 268 | i2c_send(0x00); 269 | i2c_send(0x40); // start line address=0 270 | i2c_send(0x8D); // enable charge pump 271 | i2c_send(0x14); 272 | i2c_send(0x20); // memory adressing mode=horizontal 273 | i2c_send(0x00); 274 | i2c_send(0xA1); // segment remapping mode 275 | i2c_send(0xC8); // COM output scan direction 276 | if (width == 128 && height == 32) 277 | { 278 | i2c_send(0xDA); // com pins hardware configuration 279 | i2c_send(0x02); 280 | } 281 | else if (width == 128 && height == 64) 282 | { 283 | i2c_send(0xDA); // com pins hardware configuration 284 | i2c_send(0x12); 285 | } 286 | else if (width == 96 && height == 16) 287 | { 288 | i2c_send(0xDA); // com pins hardware configuration 289 | i2c_send(0x02); 290 | } 291 | i2c_send(0x81); // contrast control 292 | i2c_send(0x80); 293 | i2c_send(0xD9); // pre-charge period 294 | i2c_send(0x22); 295 | i2c_send(0xDB); // set vcomh deselect level 296 | i2c_send(0x20); 297 | i2c_send(0xA4); // output RAM to display 298 | i2c_send(0xA6); // display mode A6=normal, A7=inverse 299 | i2c_send(0x2E); // stop scrolling 300 | i2c_stop(); 301 | delay(100); 302 | 303 | // Clear and send buffer 304 | clear(); 305 | display(); 306 | 307 | // Switch display on 308 | set_power(true); 309 | } 310 | 311 | void OLED::set_power(bool enable) 312 | { 313 | i2c_start(); 314 | i2c_send(i2c_address << 1); // address + write 315 | i2c_send(0x00); // command 316 | if (enable) 317 | { 318 | i2c_send(0x8D); // enable charge pump 319 | i2c_send(0x14); 320 | i2c_send(0xAF); // display on 321 | } 322 | else 323 | { 324 | i2c_send(0xAE); // display off 325 | i2c_send(0x8D); // disable charge pump 326 | i2c_send(0x10); 327 | } 328 | i2c_stop(); 329 | } 330 | 331 | void OLED::set_invert(bool enable) 332 | { 333 | i2c_start(); 334 | i2c_send(i2c_address << 1); // address + write 335 | i2c_send(0x00); // command 336 | if (enable) 337 | { 338 | i2c_send(0xA7); // invert display 339 | } 340 | else 341 | { 342 | i2c_send(0xA6); // normal display 343 | } 344 | i2c_stop(); 345 | } 346 | 347 | void OLED::set_scrolling(tScrollEffect scroll_type, uint_fast8_t first_page, uint_fast8_t last_page) 348 | { 349 | i2c_start(); 350 | i2c_send(i2c_address << 1); // address + write 351 | i2c_send(0x00); // command 352 | i2c_send(0x2E); // deativate scroll 353 | if (scroll_type == DIAGONAL_LEFT || scroll_type == DIAGONAL_RIGHT) 354 | { 355 | i2c_send(0xA3); // vertical scroll area 356 | i2c_send(0x00); 357 | i2c_send(height - 1); 358 | } 359 | if (scroll_type != NO_SCROLLING) 360 | { 361 | i2c_send(scroll_type); 362 | i2c_send(0x00); // dummy byte 363 | i2c_send(first_page); 364 | i2c_send(0x00); // time interval 365 | i2c_send(last_page); 366 | if (scroll_type == DIAGONAL_LEFT || scroll_type == DIAGONAL_RIGHT) 367 | { 368 | i2c_send(0x01); // vertical scrolling speed 369 | } 370 | else 371 | { 372 | i2c_send(0x00); // dummy byte 373 | i2c_send(0xFF); // dummy byte 374 | } 375 | i2c_send(0x2F); // ativate scroll 376 | } 377 | i2c_stop(); 378 | } 379 | 380 | void OLED::set_contrast(uint8_t contrast) 381 | { 382 | i2c_start(); 383 | i2c_send(i2c_address << 1); // address + write 384 | i2c_send(0x00); // command 385 | i2c_send(0x81); // deativate scrol 386 | i2c_send(contrast); 387 | i2c_stop(); 388 | } 389 | 390 | void OLED::clear(tColor color) 391 | { 392 | if (color == WHITE) 393 | { 394 | memset(buffer, 0xFF, bufsize); 395 | } 396 | else 397 | { 398 | memset(buffer, 0x00, bufsize); 399 | } 400 | X=0; 401 | Y=0; 402 | } 403 | 404 | void OLED::display() 405 | { 406 | uint16_t index = 0; 407 | for (uint_fast8_t page = 0; page < pages; page++) 408 | { 409 | // Set memory address to fill 410 | i2c_start(); 411 | i2c_send(i2c_address << 1); // address + write 412 | i2c_send(0x00); // command 413 | if (displayController == CTRL_SH1106) 414 | { 415 | i2c_send(0xB0 + page); // set page 416 | i2c_send(0x00); // lower columns address =0 417 | i2c_send(0x10); // upper columns address =0 418 | } 419 | else 420 | { 421 | i2c_send(0xB0 + page); // set page 422 | i2c_send(0x21); // column address 423 | i2c_send(0x00); // first column =0 424 | i2c_send(width - 1); // last column 425 | } 426 | i2c_stop(); 427 | 428 | // send one page of buffer to the display 429 | i2c_start(); 430 | i2c_send(i2c_address << 1); // address + write 431 | i2c_send(0x40); // data 432 | if(usingOffset){ 433 | i2c_send(0); 434 | i2c_send(0); 435 | } 436 | for (uint_fast8_t column = 0; column < width; column++) 437 | { 438 | i2c_send(buffer[index++]); 439 | } 440 | i2c_stop(); 441 | yield(); // to avoid that the watchdog triggers 442 | } 443 | } 444 | 445 | void OLED::draw_byte(uint_fast8_t x, uint_fast8_t y, uint8_t b, tColor color) 446 | { 447 | // Invalid position 448 | if (x >= width || y >= height) 449 | { 450 | return; 451 | } 452 | 453 | uint_fast16_t buffer_index = y / 8 * width + x; 454 | 455 | if (fontInverted) { 456 | b^=255; 457 | } 458 | 459 | if (color == WHITE) 460 | { 461 | // If the y position matches a page, then it goes quicker 462 | if (y % 8 == 0) 463 | { 464 | if (buffer_index < bufsize) 465 | { 466 | buffer[buffer_index] |= b; 467 | } 468 | } 469 | else 470 | { 471 | uint16_t w = (uint16_t) b << (y % 8); 472 | if (buffer_index < bufsize) 473 | { 474 | buffer[buffer_index] |= (w & 0xFF); 475 | } 476 | uint16_t buffer_index2 = buffer_index + width; 477 | if (buffer_index2 < bufsize) 478 | { 479 | buffer[buffer_index2] |= (w >> 8); 480 | } 481 | } 482 | } 483 | else 484 | { 485 | // If the y position matches a page, then it goes quicker 486 | if (y % 8 == 0) 487 | { 488 | if (buffer_index < bufsize) 489 | { 490 | buffer[buffer_index] &= ~b; 491 | } 492 | } 493 | else 494 | { 495 | uint16_t w = (uint16_t) b << (y % 8); 496 | if (buffer_index < bufsize) 497 | { 498 | buffer[buffer_index] &= ~(w & 0xFF); 499 | } 500 | uint16_t buffer_index2 = buffer_index + width; 501 | if (buffer_index2 < bufsize) 502 | { 503 | buffer[buffer_index2] &= ~(w >> 8); 504 | } 505 | } 506 | } 507 | return; 508 | } 509 | 510 | void OLED::draw_bytes(uint_fast8_t x, uint_fast8_t y, const uint8_t* data, uint_fast8_t size, tFontScaling scaling, tColor color, bool useProgmem) 511 | { 512 | for (uint_fast8_t column = 0; column < size; column++) 513 | { 514 | uint8_t b; 515 | if (useProgmem) 516 | { 517 | b = pgm_read_byte(data); 518 | } 519 | else 520 | { 521 | b = *data; 522 | } 523 | data++; 524 | if (scaling == DOUBLE_SIZE) 525 | { 526 | // Stretch vertically 527 | uint16_t w = 0; 528 | for (uint_fast8_t bit = 0; bit < 7; bit++) 529 | { 530 | if (b & (1 << bit)) 531 | { 532 | uint_fast8_t pos = bit << 1; 533 | w |= ((1 << pos) | (1 << (pos + 1))); 534 | } 535 | } 536 | 537 | // Output 2 times to strech hozizontally 538 | draw_byte(x, y, w & 0xFF, color); 539 | draw_byte(x, y + 8, (w >> 8), color); 540 | x++; 541 | draw_byte(x, y, w & 0xFF, color); 542 | draw_byte(x, y + 8, (w >> 8), color); 543 | x++; 544 | } 545 | else // NORMAL_SIZE 546 | { 547 | draw_byte(x++, y, b, color); 548 | } 549 | } 550 | } 551 | 552 | size_t OLED::draw_character(uint_fast8_t x, uint_fast8_t y, char c, tFontScaling scaling, tColor color) 553 | { 554 | // Invalid position 555 | if (x >= width || y >= height || c < 32) 556 | { 557 | return 0; 558 | } 559 | 560 | // Remap extended Latin1 character codes 561 | switch ((unsigned char) c) 562 | { 563 | case 252 /* u umlaut */: 564 | c = 127; 565 | break; 566 | case 220 /* U umlaut */: 567 | c = 128; 568 | break; 569 | case 228 /* a umlaut */: 570 | c = 129; 571 | break; 572 | case 196 /* A umlaut */: 573 | c = 130; 574 | break; 575 | case 246 /* o umlaut */: 576 | c = 131; 577 | break; 578 | case 214 /* O umlaut */: 579 | c = 132; 580 | break; 581 | case 176 /* degree */: 582 | c = 133; 583 | break; 584 | case 223 /* szlig */: 585 | c = 134; 586 | break; 587 | } 588 | 589 | uint16_t font_index = (c - 32)*6; 590 | 591 | // Invalid character code/font index 592 | if (font_index >= sizeof (oled_font6x8)) 593 | { 594 | return 0; 595 | } 596 | 597 | draw_bytes(x, y, &oled_font6x8[font_index], 6, scaling, color, true); 598 | return 1; 599 | } 600 | 601 | void OLED::draw_string(uint_fast8_t x, uint_fast8_t y, const char* s, tFontScaling scaling, tColor color) 602 | { 603 | while (*s) 604 | { 605 | draw_character(x, y, *s, scaling, color); 606 | if (scaling == DOUBLE_SIZE) 607 | { 608 | x += 12; 609 | } 610 | else // NORMAL_SIZE 611 | { 612 | x += 6; 613 | } 614 | s++; 615 | } 616 | } 617 | 618 | void OLED::draw_string_P(uint_fast8_t x, uint_fast8_t y, const char* s, tFontScaling scaling, tColor color) 619 | { 620 | char c; 621 | while ((c = pgm_read_byte(s))) 622 | { 623 | draw_character(x, y, c, scaling, color); 624 | if (scaling == DOUBLE_SIZE) 625 | { 626 | x += 12; 627 | } 628 | else // NORMAL_SIZE 629 | { 630 | x += 6; 631 | } 632 | s++; 633 | } 634 | } 635 | 636 | void OLED::draw_bitmap(uint_fast8_t x, uint_fast8_t y, uint_fast8_t bitmap_width, uint_fast8_t bitmap_height, const uint8_t* data, tColor color) 637 | { 638 | uint_fast8_t num_pages = (bitmap_height + 7) / 8; 639 | for (uint_fast8_t page = 0; page < num_pages; page++) 640 | { 641 | draw_bytes(x, y, data, bitmap_width, NORMAL_SIZE, color, false); 642 | data += bitmap_width; 643 | y += 8; 644 | } 645 | } 646 | 647 | void OLED::draw_bitmap_P(uint_fast8_t x, uint_fast8_t y, uint_fast8_t bitmap_width, uint_fast8_t bitmap_height, const uint8_t* data, tColor color) 648 | { 649 | uint_fast8_t num_pages = (bitmap_height + 7) / 8; 650 | for (uint_fast8_t page = 0; page < num_pages; page++) 651 | { 652 | draw_bytes(x, y, data, bitmap_width, NORMAL_SIZE, color, true); 653 | data += bitmap_width; 654 | y += 8; 655 | } 656 | } 657 | 658 | void OLED::draw_pixel(uint_fast8_t x, uint_fast8_t y, tColor color) 659 | { 660 | if (x >= width || y >= height) 661 | { 662 | return; 663 | } 664 | if (color == WHITE) 665 | { 666 | buffer[x + (y / 8) * width] |= (1 << (y & 7)); // set bit 667 | } 668 | else 669 | { 670 | buffer[x + (y / 8) * width] &= ~(1 << (y & 7)); // clear bit 671 | } 672 | } 673 | 674 | void OLED::draw_line(uint_fast8_t x0, uint_fast8_t y0, uint_fast8_t x1, uint_fast8_t y1, tColor color) 675 | { 676 | // Algorithm copied from Wikipedia 677 | int_fast16_t dx = abs(static_cast(x1) - static_cast(x0)); 678 | int_fast16_t sx = x0 < x1 ? 1 : -1; 679 | int_fast16_t dy = -abs(static_cast(y1) - static_cast(y0)); 680 | int_fast16_t sy = y0 < y1 ? 1 : -1; 681 | int_fast16_t err = dx + dy; 682 | int_fast16_t e2; 683 | 684 | while (1) 685 | { 686 | draw_pixel(x0, y0, color); 687 | if (x0 == x1 && y0 == y1) 688 | { 689 | break; 690 | } 691 | e2 = 2 * err; 692 | if (e2 > dy) 693 | { 694 | err += dy; 695 | x0 += sx; 696 | } 697 | if (e2 < dx) 698 | { 699 | err += dx; 700 | y0 += sy; 701 | } 702 | } 703 | } 704 | 705 | void OLED::draw_circle(uint_fast8_t x0, uint_fast8_t y0, uint_fast8_t radius, tFillmode fillMode, tColor color) 706 | { 707 | // Algorithm copied from Wikipedia 708 | int_fast16_t f = 1 - radius; 709 | int_fast16_t ddF_x = 0; 710 | int_fast16_t ddF_y = -2 * radius; 711 | int_fast16_t x = 0; 712 | int_fast16_t y = radius; 713 | 714 | if (fillMode == SOLID) 715 | { 716 | draw_pixel(x0, y0 + radius, color); 717 | draw_pixel(x0, y0 - radius, color); 718 | draw_line(x0 - radius, y0, x0 + radius, y0, color); 719 | } 720 | else 721 | { 722 | draw_pixel(x0, y0 + radius, color); 723 | draw_pixel(x0, y0 - radius, color); 724 | draw_pixel(x0 + radius, y0, color); 725 | draw_pixel(x0 - radius, y0, color); 726 | } 727 | 728 | while (x < y) 729 | { 730 | if (f >= 0) 731 | { 732 | y--; 733 | ddF_y += 2; 734 | f += ddF_y; 735 | } 736 | x++; 737 | ddF_x += 2; 738 | f += ddF_x + 1; 739 | 740 | if (fillMode == SOLID) 741 | { 742 | draw_line(x0 - x, y0 + y, x0 + x, y0 + y, color); 743 | draw_line(x0 - x, y0 - y, x0 + x, y0 - y, color); 744 | draw_line(x0 - y, y0 + x, x0 + y, y0 + x, color); 745 | draw_line(x0 - y, y0 - x, x0 + y, y0 - x, color); 746 | } 747 | else 748 | { 749 | draw_pixel(x0 + x, y0 + y, color); 750 | draw_pixel(x0 - x, y0 + y, color); 751 | draw_pixel(x0 + x, y0 - y, color); 752 | draw_pixel(x0 - x, y0 - y, color); 753 | draw_pixel(x0 + y, y0 + x, color); 754 | draw_pixel(x0 - y, y0 + x, color); 755 | draw_pixel(x0 + y, y0 - x, color); 756 | draw_pixel(x0 - y, y0 - x, color); 757 | } 758 | } 759 | } 760 | 761 | void OLED::draw_rectangle(uint_fast8_t x0, uint_fast8_t y0, uint_fast8_t x1, uint_fast8_t y1, tFillmode fillMode, tColor color) 762 | { 763 | // Swap x0 and x1 if in wrong order 764 | if (x0 > x1) 765 | { 766 | uint_fast8_t tmp = x0; 767 | x0 = x1; 768 | x1 = tmp; 769 | } 770 | // Swap y0 and y1 if in wrong order 771 | if (y0 > y1) 772 | { 773 | uint_fast8_t tmp = y0; 774 | y0 = y1; 775 | y1 = tmp; 776 | } 777 | if (fillMode == SOLID) 778 | { 779 | for (uint_fast8_t y = y0; y <= y1; y++) 780 | { 781 | draw_line(x0, y, x1, y, color); 782 | } 783 | } 784 | else 785 | { 786 | draw_line(x0, y0, x1, y0, color); 787 | draw_line(x0, y1, x1, y1, color); 788 | draw_line(x0, y0, x0, y1, color); 789 | draw_line(x1, y0, x1, y1, color); 790 | } 791 | } 792 | 793 | void OLED::scroll_up(uint_fast8_t num_lines, uint_fast8_t delay_ms) 794 | { 795 | if (delay_ms == 0) 796 | { 797 | // Scroll full pages, fast algorithm 798 | uint_fast8_t scroll_pages = num_lines / 8; 799 | for (uint_fast8_t i = 0; i < pages; i++) 800 | { 801 | for (uint_fast8_t x = 0; x < width; x++) 802 | { 803 | uint16_t index = i * width + x; 804 | uint16_t index2 = (i + scroll_pages) * width + x; 805 | if (index2 < bufsize) 806 | { 807 | buffer[index] = buffer[index2]; 808 | } 809 | else 810 | { 811 | buffer[index] = 0; 812 | } 813 | } 814 | } 815 | num_lines -= scroll_pages * 8; 816 | } 817 | 818 | // Scroll the remainder line by line 819 | bool need_refresh=true; 820 | if (num_lines > 0) 821 | { 822 | uint16_t start=millis() & 0xFFFF; 823 | uint16_t target_time=0; 824 | 825 | for (uint_fast8_t i = 0; i < num_lines; i++) 826 | { 827 | 828 | // Scroll everything 1 line up 829 | for (uint_fast8_t j = 0; j < pages; j++) 830 | { 831 | uint16_t index = j*width; 832 | uint16_t index2 = index + width; 833 | for (uint_fast8_t x = 0; x < width; x++) 834 | { 835 | uint_fast8_t carry = 0; 836 | if (index2 < bufsize) 837 | { 838 | if (buffer[index2] & 1) 839 | { 840 | carry = 128; 841 | } 842 | } 843 | buffer[index] = (buffer[index] >> 1) | carry; 844 | index++; 845 | index2++; 846 | } 847 | } 848 | need_refresh=true; 849 | target_time+=delay_ms; 850 | 851 | // Refresh the display only if we have some time 852 | uint16_t now=millis() & 0xFFFF; 853 | if (now-start < target_time) 854 | { 855 | display(); 856 | need_refresh=false; 857 | } 858 | 859 | // If we have still more time, then yield a while 860 | while((millis() & 0xFFFF)-start < target_time) 861 | { 862 | yield(); 863 | } 864 | } 865 | } 866 | 867 | if (need_refresh) 868 | { 869 | display(); 870 | } 871 | } 872 | 873 | size_t OLED::write(uint8_t c) 874 | { 875 | int n=1; 876 | n=draw_character(X,Y,c); 877 | X+=OLED_FONT_WIDTH; 878 | return n; 879 | } 880 | 881 | void OLED::setCursor(uint_fast8_t x, uint_fast8_t y) 882 | { 883 | if (ttyMode) return; // in TTY mode position the cursor has no effect 884 | X=x; 885 | Y=y; 886 | } 887 | 888 | size_t OLED::printf(uint_fast8_t x, uint_fast8_t y, const char *format, ...) { 889 | va_list arg; 890 | va_start(arg, format); 891 | char temp[64]; 892 | char* buffer = temp; 893 | size_t len = vsnprintf(temp, sizeof(temp), format, arg); 894 | va_end(arg); 895 | if (len > sizeof(temp) - 1) { 896 | buffer = new char[len + 1]; 897 | if (!buffer) { 898 | return 0; 899 | } 900 | va_start(arg, format); 901 | vsnprintf(buffer, len + 1, format, arg); 902 | va_end(arg); 903 | } 904 | X=x; 905 | Y=y; 906 | len = write((const uint8_t*) buffer, len); 907 | if (buffer != temp) { 908 | delete[] buffer; 909 | } 910 | 911 | return len; 912 | } 913 | 914 | size_t OLED::printf(const char *format, ...) { 915 | va_list arg; 916 | va_start(arg, format); 917 | char temp[64]; 918 | char* buffer = temp; 919 | size_t len = vsnprintf(temp, sizeof(temp), format, arg); 920 | va_end(arg); 921 | if (len > sizeof(temp) - 1) { 922 | buffer = new char[len + 1]; 923 | if (!buffer) { 924 | return 0; 925 | } 926 | va_start(arg, format); 927 | vsnprintf(buffer, len + 1, format, arg); 928 | va_end(arg); 929 | } 930 | len = write((const uint8_t*) buffer, len); 931 | if (buffer != temp) { 932 | delete[] buffer; 933 | } 934 | 935 | return len; 936 | } 937 | 938 | size_t OLED::write(const uint8_t *buffer, size_t len) 939 | { 940 | //size_t n = 0; 941 | for (size_t ix=0;ix= height) 978 | { 979 | scroll_up(OLED_FONT_HEIGHT); 980 | Y=height-OLED_FONT_HEIGHT; 981 | } 982 | } 983 | } 984 | if (ttyMode) display(); 985 | return len; 986 | } 987 | 988 | void OLED::setTTYMode(bool enabled) 989 | { 990 | ttyMode=enabled; 991 | } 992 | 993 | void OLED::useOffset(bool enabled) 994 | { 995 | if(displayController == CTRL_SH1106){ 996 | usingOffset=enabled; 997 | } 998 | } 999 | 1000 | // ************************ U8g2 wrappers ************************ 1001 | void OLED::drawString(uint_fast8_t col, uint_fast8_t row, const char* s, tFontScaling scaling, tColor color) 1002 | { 1003 | draw_string(ToX(col),ToY(row),s,scaling,color); 1004 | } 1005 | 1006 | void OLED::inverse(void) 1007 | { 1008 | set_font_inverted(true); 1009 | } 1010 | 1011 | void OLED::noInverse(void) 1012 | { 1013 | set_font_inverted(false); 1014 | } 1015 | 1016 | // ************************ Tools func ************************ 1017 | 1018 | void OLED::set_font_inverted(bool enabled) 1019 | { 1020 | fontInverted=enabled; 1021 | } 1022 | 1023 | uint_fast8_t OLED::ToCol(uint_fast8_t x) 1024 | { 1025 | return(x/OLED_FONT_WIDTH); 1026 | } 1027 | 1028 | uint_fast8_t OLED::ToRow(uint_fast8_t y) 1029 | { 1030 | return(y/OLED_FONT_HEIGHT); 1031 | } 1032 | 1033 | uint_fast8_t OLED::ToX(uint_fast8_t col) 1034 | { 1035 | return(col*OLED_FONT_WIDTH); 1036 | } 1037 | 1038 | uint_fast8_t OLED::ToY(uint_fast8_t row) 1039 | { 1040 | return(row*OLED_FONT_HEIGHT); 1041 | } -------------------------------------------------------------------------------- /src/oled.h: -------------------------------------------------------------------------------- 1 | #ifndef OLED_H 2 | #define OLED_H 3 | 4 | #include 5 | #include "Print.h" 6 | 7 | // You may change the communication speed here 8 | //#define OLED_I2C_DELAY delayMicroseconds(3) // 68kHz@80Mhz or 83kHz@160Mhz 9 | //#define OLED_I2C_DELAY delayMicroseconds(2) // 85kHz@80Mhz or 110kHz@160Mhz 10 | #define OLED_I2C_DELAY delayMicroseconds(1) // 110kHz@80Mhz or 170kHz@160Mhz 11 | //#define OLED_I2C_DELAY delayMicroseconds(0) // 150kHz@80Mhz or 300kHz@160Mhz 12 | //#define OLED_I2C_DELAY // 250kHz@80Mhz or 500kHz@160Mhz 13 | 14 | /** 15 | * Font pixel size 16 | */ 17 | #define OLED_FONT_HEIGHT 8 18 | #define OLED_FONT_WIDTH 6 19 | 20 | /** 21 | * tty mode default 22 | */ 23 | #define OLED_DEFAULT_TTY_MODE false; 24 | 25 | /** 26 | * Use as pin number if the reset line is not connected to an I/O pin. 27 | * In this case, the reset signal must be generated by some other hardware. 28 | */ 29 | #define NO_RESET_PIN 255 30 | 31 | /** 32 | * Driver for OLED displays with SSD1306 or SH1106 controller with write(), print(), println(), printf() support. 33 | * 34 | * Supported display sizes: 196x16, 28x32 and 128x64 pixels. 35 | * This driver supports only displays with internal charge pump and I2C interface. 36 | * The I2C communication is done by software bit-banging the configurable I/O pins. 37 | * Communication errors are not handled. 38 | * 39 | * The driver allocates 192, 512 or 1024 bytes memory on the heap for buffering. 40 | * 41 | * The display is technically organized into "pages" which is a set of 8 horizontal lines. 42 | * For a 128x34 display the memory contains 4 pages each of 128 bytes. 43 | * Each byte represents 8 pixels. Bit 0 is the top pixel, bit 7 is the bottom pixel. 44 | * 45 | * Bitmap graphics have the same format with the exception that they can have any width. The 46 | * height is always a multiple of 8. 47 | * 48 | * Text and bitmaps can be drawn to any position, but vertical positions at the 49 | * page boundaries (y=0, 8, 16, 24, 32, 40, 48, 56) provide better performance. 50 | * 51 | * For all drawing functions, the coordinate 0,0 is the top left corner of the display. 52 | * After drawing into the buffer, you have to call the display() method to send the data to 53 | * the display controller. Otherwise you won't see anything. 54 | * 55 | * This driver is distributed without license. You may use it for free. 56 | * Author: Stefan Frings, 2017 57 | * 58 | * Update by: 59 | * Author: Fabio Durigon, 2018 60 | * email: develop@dury.it 61 | * 62 | */ 63 | class OLED : public Print 64 | { 65 | public: 66 | 67 | /** Possible colors for drawing */ 68 | enum tColor { BLACK, WHITE }; 69 | 70 | /** Filling mode */ 71 | enum tFillmode { HOLLOW, SOLID }; 72 | 73 | /** Supported text sizes. Normal=6x8 pixels, Double=12x16 pixels */ 74 | enum tFontScaling { NORMAL_SIZE, DOUBLE_SIZE }; 75 | 76 | /** Scroll effects supported by the display controller, note that there is no plain vertical scrolling */ 77 | enum tScrollEffect { NO_SCROLLING=0, HORIZONTAL_RIGHT=0x26, HORIZONTAL_LEFT=0x27, DIAGONAL_RIGHT=0x29, DIAGONAL_LEFT=0x2A }; 78 | 79 | /** Possible display width values **/ 80 | enum tWidth { W_96=96, W_128=128 }; 81 | 82 | /** Possible display height values **/ 83 | enum tHeight { H_16=16, H_32=32, H_64=64 }; 84 | 85 | /** Possible display controller **/ 86 | enum tDisplayCtrl { CTRL_SSD1306, CTRL_SH1106 }; 87 | 88 | /** 89 | * Constructor of the OLED class (!! DEPRECATED !!). 90 | * @param sda_pin Pin number of the SDA line (can be any Arduino I/O pin) 91 | * @param sda_pin Pin number of the SCL line (can be any Arduino I/O pin) 92 | * @param reset_pin Pin number for the /RST line, or use NO_RESET_PIN if the reset signal is generated somewhere else 93 | * @param i2c_address The I²C address is usually 0x3c or 0x3D 94 | * @param width With of the display in pixels: 96 or 128 95 | * @param height Height of the display in pixels: 16, 32 or 64 96 | * @param isSH1106 Must be true=SH1106 chip, false=SSD1306 chip 97 | */ 98 | __attribute__((deprecated("Deprecated constructor"))) 99 | OLED(uint8_t sda_pin, uint8_t scl_pin, uint8_t reset_pin, uint8_t i2c_address, uint_fast8_t width, uint_fast8_t height, bool isSH1106=false); 100 | 101 | /** 102 | * @brief Construct a new OLED object 103 | * 104 | * @param sda_pin Pin number of the SDA line (can be any Arduino I/O pin). 105 | * @param sda_pin Pin number of the SCL line (can be any Arduino I/O pin). 106 | * @param reset_pin Pin number for the /RST line, or use NO_RESET_PIN if the reset signal is generated somewhere else. 107 | * @param width Width of the display in pixels: can be W_96 or W_128 (for 96 or 128 pixel width). 108 | * @param height Height of the display in pixels: can be H_16, H_32 or H_64 (for 16,32 or 64 pixel height). 109 | * @param dispayCtrl Display controller chip: can be CTRL_SH1106 or CTRL_SSD1306. 110 | * @param i2c_address The I²C address is usually 0x3c or 0x3D. 111 | */ 112 | OLED(uint8_t sda_pin, uint8_t scl_pin, uint8_t reset_pin=NO_RESET_PIN, tWidth width=W_128, tHeight height=H_32, tDisplayCtrl displayCtrl=CTRL_SSD1306, uint8_t i2c_address=0x3C); 113 | 114 | /** 115 | * Destructor. Frees the buffer memory and leaves the display in the previous state. 116 | */ 117 | virtual ~OLED(); 118 | 119 | /** 120 | * Initialize the display controller, clean memory and switch output on. 121 | */ 122 | void begin(); 123 | 124 | /** 125 | * Will use offset for wired cases when controller uses SSH1106 132x64 but display is 128x64 126 | */ 127 | void useOffset(bool enabled=true); 128 | 129 | /** 130 | * Enable or disable the charge pump and display output. May be used to save power. 131 | * This command is executed by the display controller itself, hence it does not affect the buffer memory. 132 | * @param enable Whether to enable the display output. 133 | */ 134 | void set_power(bool enable); 135 | 136 | /** 137 | * Enable display inverting. If enabled, then WHITE and BLACK are swapped. 138 | * This command is executed by the display controller itself, so it works very fast and does not affect 139 | * the buffer memory. 140 | * @param enable Whether to enable inverse output 141 | */ 142 | void set_invert(bool enable); 143 | 144 | /** 145 | * Set the contrast (brightness) of the display. 146 | * The value affects the electrical current throught the OLED segments in linear ratio. 147 | * But the eye reacts logarithmically. A medium brightnes of 128 looks almost as bright as the 148 | * maximum setting but increases the lifetime of the display a lot. 149 | * @param contrast Contrast value, default is 0x80. 150 | */ 151 | void set_contrast(uint8_t contrast); 152 | 153 | /** 154 | * Enable continuous scrolling. This is performed by the display controller itself without needing continuous 155 | * communication and it does not affect the buffer memory. 156 | * For horizontal scrolling, you can decide whether the whole display or only a part of it shall scroll. 157 | * In case of diagonal scrolling, the vertical part affects always the whole display. 158 | * @param scroll_type Select the scroll effect, or NO_SCROLLING to disable it 159 | * @param first_page Defined which pages are affected by the horizontal scrolling (0-7). Each page contains 8 lines 160 | * @param last_page Defined which pages are affected by the horizontal scrolling (0-7). Each page contains 8 lines 161 | */ 162 | void set_scrolling(tScrollEffect scroll_type, uint_fast8_t first_page=0, uint_fast8_t last_page=7); 163 | 164 | /** 165 | * Scroll the display up, which leaves a number of black pixel lines at the bottom. 166 | * An optional delay time can be used to produce a smooth scrolling effect. 167 | * This is done by software, requiring continuous communication. 168 | * @param num_lines Number of pixel lines to scroll the display content up. 169 | * @param delay_ms Delay time between each step, recommended values are either 0 or 20..60. 170 | */ 171 | void scroll_up(uint_fast8_t num_lines=OLED_FONT_HEIGHT, uint_fast8_t delay_ms=0); 172 | 173 | /** 174 | * Transfer the buffer memory data to the display controller. 175 | * You have to call this method after any drawing action into the buffer to make the change visible. 176 | * A call to this method takes several milliseconds. 177 | */ 178 | void display(); 179 | 180 | /** 181 | * Fill the whole buffer memory with white or black color. 182 | * @param color The color to be used 183 | */ 184 | void clear(tColor color=BLACK); 185 | 186 | /** 187 | * Draw a bitmap from RAM. The raw data format is explained in the description of this class. 188 | * @param x Pixel position of the upper left corner 189 | * @param y Pixel position of the upper left corner 190 | * @param width Width of the bitmap in pixels 191 | * @param height Height of the bitmap in pixels. Must be a multiple of 8 192 | * @param data Raw data, number of bytes must be width*height/8 193 | * @param color Color to draw with 194 | */ 195 | void draw_bitmap(uint_fast8_t x, uint_fast8_t y, uint_fast8_t width, uint_fast8_t height, const uint8_t* data, tColor color=WHITE); 196 | 197 | /** 198 | * Draw a bitmap from program memory (aka FLASH). The raw data format is explained in the description of this class. 199 | * @param x Pixel position of the upper left corner 200 | * @param y Pixel position of the upper left corner 201 | * @param width Width of the bitmap in pixels 202 | * @param height Height of the bitmap in pixels. Must be a multiple of 8 203 | * @param data Raw data, number of bytes must be width*height/8 204 | * @param color Color to draw with 205 | */ 206 | void draw_bitmap_P(uint_fast8_t x, uint_fast8_t y, uint_fast8_t width, uint_fast8_t height, const uint8_t* data, tColor color=WHITE); 207 | 208 | /** 209 | * Draw a character. 210 | * @param x Pixel position of the upper left corner 211 | * @param y Pixel position of the upper left corner 212 | * @param c The character code. Supports US-ASCII characters and german umlauts. See source code of oled.cpp 213 | * @param scaling Scaling factor. Can be used to double the size of the output. The normal font size is 6x8 214 | * @param color Color to draw with 215 | */ 216 | size_t draw_character(uint_fast8_t x, uint_fast8_t y, char c, tFontScaling scaling=NORMAL_SIZE, tColor color=WHITE); 217 | 218 | /** 219 | * Draw a C string from RAM, which is a NULL terminated array of characters. 220 | * @param x Pixel position of the upper left corner 221 | * @param y Pixel position of the upper left corner 222 | * @param s The string to draw. Supports US-ASCII characters and german umlauts. See source code of oled.cpp 223 | * @param scaling Scaling factor. Can be used to double the size of the output. The normal font size is 6x8 224 | * @param color Color to draw with 225 | */ 226 | void draw_string(uint_fast8_t x, uint_fast8_t y, const char* s, tFontScaling scaling=NORMAL_SIZE, tColor color=WHITE); 227 | 228 | /** 229 | * Draw a C string from program memory (aka FLASH), which is a NULL terminated array of characters. 230 | * @param x Pixel position of the upper left corner 231 | * @param y Pixel position of the upper left corner 232 | * @param s The string to draw. Supports US-ASCII characters and german umlauts. See source code of oled.cpp 233 | * @param scaling Scaling factor. Can be used to double the size of the output. The normal font size is 6x8 234 | * @param color Color to draw with 235 | */ 236 | void draw_string_P(uint_fast8_t x, uint_fast8_t y, const char* s, tFontScaling scaling=NORMAL_SIZE, tColor color=WHITE); 237 | 238 | /** 239 | * Draw a single pixel. 240 | * @param x,y Position of the pixel 241 | * @param color Color to draw with 242 | */ 243 | void draw_pixel(uint_fast8_t x, uint_fast8_t y, tColor color=WHITE); 244 | 245 | /** 246 | * Draw a line. 247 | * @param x0,y0 Start point of the line 248 | * @param x1,y1 End point of the line 249 | * @param color Color to draw with 250 | */ 251 | void draw_line(uint_fast8_t x0, uint_fast8_t y0, uint_fast8_t x1, uint_fast8_t y1, tColor color=WHITE); 252 | 253 | /** 254 | * Draw a circle. 255 | * @param x,y Center position of the circle 256 | * @param radius Radius of the circle 257 | * @param fillMode Whether the circle is filled with the color 258 | * @param color Color to draw with 259 | */ 260 | void draw_circle(uint_fast8_t x, uint_fast8_t y, uint_fast8_t radius, tFillmode fillMode=HOLLOW, tColor color=WHITE); 261 | 262 | /** 263 | * Draw a rectangle. 264 | * @param x0,y0 Upper left corner 265 | * @param x1,y1 Lower right corner 266 | * @param fillMode Whether the rectangle is filled with the color 267 | * @param color Color to draw with 268 | */ 269 | void draw_rectangle(uint_fast8_t x0, uint_fast8_t y0, uint_fast8_t x1, uint_fast8_t y1, tFillmode fillMode=HOLLOW, tColor color=WHITE); 270 | 271 | /** 272 | * Write a character at current X,Y coordinates. 273 | * X coordinate will be moved by OLED_FONT_WIDTH value 274 | * * If the string contains a \r or \n and ttyMode is true, Y coordinate will be incremented 275 | * by OLED_FONT_HEIGHT value 276 | * This function overrides write(...) in Print.cpp of Arduino core 277 | * @param c character to write 278 | * @return 1 on succeed, otherwise 0. 279 | */ 280 | size_t write(uint8_t c) override; 281 | 282 | /** 283 | * These functions are copied from HardwareSeral.h of Arduino core. 284 | * I implemented them to be fully compatible witgh Serial calls 285 | */ 286 | inline size_t write(unsigned long n) 287 | { 288 | return write((uint8_t) n); 289 | } 290 | inline size_t write(long n) 291 | { 292 | return write((uint8_t) n); 293 | } 294 | inline size_t write(unsigned int n) 295 | { 296 | return write((uint8_t) n); 297 | } 298 | inline size_t write(int n) 299 | { 300 | return write((uint8_t) n); 301 | } 302 | 303 | /** 304 | * Write a sting at current X,Y coordinates. 305 | * X coordinate will be moved by (size*OLED_FONT_WIDTH) pixels. 306 | * If ttyMode is true display() will be called to redraw screen. 307 | * This function overrides write(...) in Print.cpp of Arduino core 308 | * @param buffer The string to be printed 309 | * @param size The length of the string 310 | * @return Nr of characters written. 311 | */ 312 | size_t write(const uint8_t *buffer, size_t len) override; 313 | 314 | /** Use also Print::write(const uint8_t *buffer, size_t size)*/ 315 | using Print::write; 316 | 317 | /** 318 | * Set cursor position 319 | */ 320 | void setCursor(uint_fast8_t x, uint_fast8_t y); 321 | 322 | /** 323 | * Same as printf width x and y coordinate set 324 | * @param x Pixel position of the upper left corner 325 | * @param y Pixel position of the upper left corner 326 | * @param format Format string same in printf 327 | * @param ... Args for formatted string 328 | * @return Len of written string 329 | */ 330 | size_t printf(uint_fast8_t x, uint_fast8_t y, const char *format, ...); 331 | size_t printf(const char *format, ...); 332 | 333 | /** Use also Print::printf(const char *format, ...) */ 334 | //using Print::printf; 335 | 336 | /** 337 | * Set terminal mode on/off 338 | * @param Enabled If true, driver automatically scroll-up to one text line (height of font) when 339 | * a printing function (that doesn't use coordinate) reach the bottom of the screen. 340 | * Es. 341 | * When the cursor Y position is at the last text line and you use println() or writeln(), 342 | * the text on the the screen scrolls up and first line of text disappears 343 | */ 344 | void setTTYMode(bool enabled); 345 | 346 | // ************************ U8g2 wrappers ************************ 347 | 348 | /** 349 | * @brief Draw a C, NULL terminated string from RAM, using column and row positioning. 350 | * Wrapper for U8g2 library API compatibility. 351 | * 352 | * @param col column position where start to write (zero based). 353 | * @param row row position where start to write (zero based). 354 | * @param s The string to draw. Supports US-ASCII characters and german umlauts. See source code of oled.cpp 355 | * @param scaling Scaling factor. Can be used to double the size of the output. The normal font size is 6x8 356 | * @param color Color to draw with 357 | */ 358 | void drawString(uint_fast8_t col, uint_fast8_t row, const char* s, tFontScaling scaling=NORMAL_SIZE, tColor color=WHITE); 359 | 360 | /** 361 | * @brief Set inverted font write. 362 | * Wrapper for U8g2 library API compatibility. 363 | */ 364 | void inverse(void); 365 | 366 | /** 367 | * @brief Set normal (not inverted) font wite. 368 | * Wrapper for U8g2 library API compatibility. 369 | */ 370 | void noInverse(void); 371 | 372 | private: 373 | /** Pin for the SDA line */ 374 | const uint8_t sda_pin; 375 | 376 | /** Pin for the SCL line */ 377 | const uint8_t scl_pin; 378 | 379 | /** Pin for the /RST line (value=NO_RESET_PIN, is disabled) */ 380 | const uint8_t reset_pin; 381 | 382 | /** Horziontal display size in pixels */ 383 | const uint_fast8_t width; 384 | 385 | /** Vertical display size in pixels */ 386 | const uint_fast8_t height; 387 | 388 | /** I2C address of the display controller */ 389 | const uint8_t i2c_address; 390 | 391 | /** Display controller type SH1106 or SD1306 */ 392 | tDisplayCtrl displayController; 393 | 394 | /** Offset for SH1106 132x64 controller with 128x64 matrix */ 395 | bool usingOffset; 396 | 397 | /** Number of pages in the display and buffer */ 398 | const uint_fast8_t pages; 399 | 400 | /** Size of the buffer in bytes */ 401 | const uint_fast16_t bufsize; 402 | 403 | /** Pointer to the buffer memory */ 404 | uint8_t *buffer; 405 | 406 | /** Current X position */ 407 | uint_fast8_t X; 408 | 409 | /** Current Y position */ 410 | uint_fast8_t Y; 411 | 412 | /** tty mode */ 413 | bool ttyMode; 414 | 415 | /** Font inversion flag */ 416 | bool fontInverted; 417 | 418 | /** Send an I2C start signal */ 419 | void i2c_start(); 420 | 421 | /** Send an I²C stop signal */ 422 | void i2c_stop(); 423 | 424 | /** Send a byte via I2C and return the ack */ 425 | bool i2c_send(uint8_t byte); 426 | 427 | /** Draw a byte into the buffer */ 428 | void draw_byte(uint_fast8_t x, uint_fast8_t y, uint8_t b, tColor color); 429 | 430 | /** Draw multiple bytes into the buffer */ 431 | void draw_bytes(uint_fast8_t x, uint_fast8_t y, const uint8_t* data, uint_fast8_t size, tFontScaling scaling, tColor color, bool useProgmem); 432 | 433 | /** 434 | * @brief Enable/disable inverted print. 435 | * 436 | * @param enabled If true font are printed in inverted background mode. 437 | */ 438 | void set_font_inverted(bool enabled); 439 | 440 | /** 441 | * @brief Convert X coordinate to column index. 442 | * 443 | * @param x Pixel coordinate of horizontal position. 444 | * @return Column index (zero based). 445 | */ 446 | uint_fast8_t ToCol(uint_fast8_t x); 447 | 448 | /** 449 | * @brief Convert Y coordinate to row index. 450 | * 451 | * @param y Pixel coordinate of vertical position. 452 | * @return Row index (zero based). 453 | */ 454 | uint_fast8_t ToRow(uint_fast8_t y); 455 | 456 | /** 457 | * @brief Convert column index to X coordinate. 458 | * 459 | * @param col Index of column (zero based). 460 | * @return X position coordinate. 461 | */ 462 | uint_fast8_t ToX(uint_fast8_t col); 463 | 464 | /** 465 | * @brief Convert row index to Y coordinate. 466 | * 467 | * @param row Index of row (zero based). 468 | * @return Y position coordinate. 469 | */ 470 | uint_fast8_t ToY(uint_fast8_t row); 471 | }; 472 | 473 | #endif 474 | 475 | --------------------------------------------------------------------------------