├── .gitignore ├── README.md ├── examples ├── Basic │ └── I2C_Scanner │ │ └── I2C_Scanner.ino ├── Factory_Test │ └── WiFi_Kit_8_FactoryTest │ │ └── WiFi_Kit_8_FactoryTest.ino ├── NTP_Example │ └── NTP_Example.ino ├── OLED │ ├── HelloWorld │ │ └── HelloWorld.ino │ ├── SSD1306DrawingDemo │ │ └── SSD1306DrawingDemo.ino │ ├── SSD1306SimpleDemo │ │ ├── SSD1306SimpleDemo.ino │ │ └── images.h │ └── SSD1306UiDemo │ │ ├── SSD1306UiDemo.ino │ │ └── images.h └── WiFiKit8_RSSI │ └── WiFiKit8_RSSI.ino ├── img ├── location.png ├── location_8266.png └── location_cn.png ├── keywords.txt ├── library.json ├── library.properties ├── license └── src ├── heltec.cpp ├── heltec.h └── oled ├── OLEDDisplay.cpp ├── OLEDDisplay.h ├── OLEDDisplayFonts.h ├── OLEDDisplayUi.cpp ├── OLEDDisplayUi.h ├── SSD1306.h └── SSD1306Wire.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | .vscode -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Heltec_ESP8266 Library 2 | 3 | English | [简体中文](#简体中文) 4 | 5 | **This library must work with [Heltec ESP8266 develop framework](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series)! A detail document about how to install Heltec ESP8266 development framework and this library available here:** 6 | 7 | **[http://docs.heltec.cn/#/en/user_manual/how_to_install_esp8266_Arduino](http://docs.heltec.cn/#/en/user_manual/how_to_install_esp8266_Arduino)** 8 | 9 | ## CONTENT 10 | 11 | 1. [How to install this library](#how-to-install-this-library) 12 | 13 | - [Use Arduino Library Manager](#use-arduino-library-manager) 14 | 15 | - [Use Git](#use-git) 16 | 17 | 2. [How to use this library](#how-to-use-this-library) 18 | 19 | 3. [API Reference](#api-reference) 20 | 21 | 4. [Hardware Reference](#hardware-reference) 22 | 23 | - [PinoutDiagram](#pinoutdiagram) 24 | 25 | - [Schematic Diagram](#schematic-diagram) 26 | 27 | 28 | *** 29 | 30 | 31 | ## How to install this library 32 | *`We recommedn use Arduino library manager, it's the simplest way`* 33 | 34 | ### Use Arduino Library Manager 35 | Open Arduino IDE, then Select `Sketch`->`Include Library`->`Manage Libraries...` 36 | Search `Heltec ESP8266` and install it. 37 | 38 | 39 | 40 | ### Use Git 41 | 42 | *Firstly, makse sure `Git` and `Arduino IDE` has installed first. If not, please refer [How to install Git and Arduino](http://docs.heltec.cn/#/en/user_manual/how_to_install_git_and_arduino). For correctly installed Arduino, you will find a folder in "Username/Documents/Arduino/Library". **this library must in such a path!*** 43 | 44 | Open "Git bash" in path "Username/Documents/Arduino/Library", and input: 45 | 46 | git clone https://github.com/HelTecAutomation/Heltec_ESP8266.git 47 | 48 | You will see such a new folder in your library path, install done. 49 | 50 | ![image](img/location_8266.png) 51 | 52 | 53 | ## How to use this library 54 | 55 | **`This library must work with [Heltec ESP8266 develop framework](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series)!`** 56 | 57 | A detail steps to execute some examples available here: 58 | 59 | [http://docs.heltec.cn/#/en/user_manual/how_to_install_esp8266_Arduino?id=step2-download-the-heltec-esp8266-library](http://docs.heltec.cn/#/en/user_manual/how_to_install_esp8266_Arduino?id=step2-download-the-heltec-esp8266-library) 60 | 61 | ## API Reference 62 | [OLED API](https://github.com/HelTecAutomation/Heltec_ESP8266/blob/master/src/oled/OLEDDisplay.h) 63 | 64 | 65 | ## Hardware Reference 66 | 67 | ### PinoutDiagram 68 | [https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/PinoutDiagram](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/PinoutDiagram) 69 | ### Schematic Diagram 70 | [https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/SchematicDiagram](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/SchematicDiagram) 71 | 72 | 73 | #### Note: 74 | * ESP series chips are faster to download, please make sure to use the high-quality Micro USB cable, it will be easy to download. 75 | 76 | [Summary of common problems](http://www.heltec.cn/summary-of-common-problems-in-wifi-kit-series-continuous-update/?lang=en) 77 | 78 |   79 | *** 80 | *** 81 |   82 | 83 | ## 简体中文 84 | 85 | **这个Arduino库必须配合[Heltec ESP8266编译环境](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series)一起使用!完整的“编译环境 + 库”的的教程可以参考这里:** 86 | 87 | **[http://docs.heltec.cn/#/en/user_manual/how_to_install_esp8266_Arduino](http://docs.heltec.cn/#/en/user_manual/how_to_install_esp8266_Arduino)** 88 | 89 | *** 90 | 91 | ## 目录 92 | 93 | 1. [安装方法](#安装方法) 94 | 95 | - [通过Arduino库管理器安装](#通过Arduino库管理器安装) 96 | 97 | - [通过Git进行安装](#use-git) 98 | 99 | 2. [怎样使用这个库](#怎样使用这个库) 100 | 101 | 3. [API参考](#API参考) 102 | 103 | 4. [硬件设计参考](#硬件设计参考) 104 | 105 | - [引脚图](#引脚图) 106 | 107 | - [原理图](#原理图) 108 | 109 | 110 | *** 111 | 112 | 113 | ## 安装方法 114 | *`强烈推荐使用Arduino自带的“库管理器”进行安装!`* 115 | 116 | ### 通过Arduino库管理器安装 117 | 打开Arduino IDE, 选择`项目`->`加载库`->`管理库...`,打开“库管理器” 118 | 搜索`Heltec ESP8266`并安装. 119 | 120 | 121 | 122 | ### 通过Git进行安装 123 | 124 | *首先,请确保`Git`和`Arduino IDE`都已经正确安装。如果没有,请参考这里的安装方法[How to install Git and Arduino](http://docs.heltec.cn/#/en/user_manual/how_to_install_git_and_arduino)。 * 125 | 126 | **强调一下:这个库的路径必须位于操作系统的“文档/Arduino/libraries”文件夹内!!!文档文件夹是操作系统自带的,必须!必须!必须!** 127 | 128 | 在“文档/Arduino/libraries”路径下打开"Git bash",输入: 129 | 130 | git clone https://github.com/HelTecAutomation/Heltec_ESP8266.git 131 | 132 | 如果一切正常,应该是这样的 133 | 134 | ![image](img/location_8266.png) 135 | 136 | 137 | ## 怎样使用这个库 138 | 139 | 如何运行一个例程?详细的使用指南:: 140 | 141 | [http://docs.heltec.cn/#/en/user_manual/how_to_install_esp8266_Arduino?id=step2-download-the-heltec-esp8266-library](http://docs.heltec.cn/#/en/user_manual/how_to_install_esp8266_Arduino?id=step2-download-the-heltec-esp8266-library) 142 | 143 | ## API参考 144 | [OLED API](https://github.com/HelTecAutomation/Heltec_ESP8266/blob/master/src/oled/OLEDDisplay.h) 145 | 146 | 147 | ## 硬件设计参考 148 | 149 | ### 引脚图 150 | [https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/PinoutDiagram](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/PinoutDiagram) 151 | ### 原理图 152 | [https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/SchematicDiagram](https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series/tree/master/SchematicDiagram) 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /examples/Basic/I2C_Scanner/I2C_Scanner.ino: -------------------------------------------------------------------------------- 1 | /* Heltec Automation I2C scanner example (also it's a basic example how to use I2C1) 2 | * 3 | * ESP32 have two I2C (I2C0 and I2C1) bus 4 | * 5 | * OLED is connected to I2C0, so if scan with Wire (I2C0), the return address should be 0x3C. 6 | * 7 | * If you need scan other device address in I2C1... 8 | * - Comment all Wire.***() codes; 9 | * - Uncomment all Wire1.***() codes; 10 | * 11 | * I2C scan example and I2C0 12 | * 13 | * HelTec AutoMation, Chengdu, China 14 | * 成都惠利特自动化科技有限公司 15 | * www.heltec.org 16 | * 17 | * this project also realess in GitHub: 18 | * https://github.com/HelTecAutomation/Heltec_ESP32 19 | * */ 20 | 21 | #include "Arduino.h" 22 | #include "heltec.h" 23 | 24 | void setup() 25 | { 26 | Heltec.begin(true, true); 27 | Wire.begin(SDA, SCL); //Scan OLED's I2C address via I2C0 28 | } 29 | 30 | void loop() 31 | { 32 | byte error, address; 33 | int nDevices; 34 | 35 | Serial.println("Scanning..."); 36 | 37 | nDevices = 0; 38 | for(address = 1; address < 127; address++ ) 39 | { 40 | Wire.beginTransmission(address); 41 | error = Wire.endTransmission(); 42 | 43 | // Wire1.beginTransmission(address); 44 | // error = Wire1.endTransmission(); 45 | 46 | if (error == 0) 47 | { 48 | Serial.print("I2C device found at address 0x"); 49 | if (address<16) 50 | Serial.print("0"); 51 | Serial.print(address,HEX); 52 | Serial.println(" !"); 53 | 54 | nDevices++; 55 | } 56 | else if (error==4) 57 | { 58 | Serial.print("Unknown error at address 0x"); 59 | if (address<16) 60 | Serial.print("0"); 61 | Serial.println(address,HEX); 62 | } 63 | } 64 | if (nDevices == 0) 65 | Serial.println("No I2C devices found\n"); 66 | else 67 | Serial.println("done\n"); 68 | 69 | delay(5000); 70 | } 71 | -------------------------------------------------------------------------------- /examples/Factory_Test/WiFi_Kit_8_FactoryTest/WiFi_Kit_8_FactoryTest.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * HelTec Automation(TM) wifi kit 8 factory test code, witch include 3 | * follow functions: 4 | * 5 | * - Basic OLED function test; 6 | * 7 | * - Basic serial port test(in baud rate 115200); 8 | * 9 | * - LED blink test; 10 | * 11 | * - WIFI join and scan test; 12 | * 13 | * - Timer test and some other Arduino basic functions. 14 | * 15 | * by Aaron.Lee from HelTec AutoMation, ChengDu, China 16 | * 成都惠利特自动化科技有限公司 17 | * www.heltec.cn 18 | * 19 | * this project also realess in GitHub: 20 | * https://github.com/HelTecAutomation/Heltec_ESP8266 21 | */ 22 | #include "heltec.h" 23 | #include "ESP8266WiFi.h" 24 | 25 | 26 | //unsigned int counter = 0; 27 | 28 | 29 | void WIFISetUp(void) 30 | { 31 | // Set WiFi to station mode and disconnect from an AP if it was previously connected 32 | WiFi.disconnect(true); 33 | delay(100); 34 | WiFi.mode(WIFI_STA); 35 | WiFi.setAutoConnect(true); 36 | WiFi.begin("Your WiFi SSID","Your Password");//fill in "Your WiFi SSID","Your Password" 37 | delay(100); 38 | Heltec.display->clear(); 39 | 40 | byte count = 0; 41 | while(WiFi.status() != WL_CONNECTED && count < 10) 42 | { 43 | count ++; 44 | delay(500); 45 | Heltec.display->drawString(0, 0, "Connecting..."); 46 | Heltec.display->display(); 47 | } 48 | //Heltec.display->clear(); 49 | if(WiFi.status() == WL_CONNECTED) 50 | { 51 | //Heltec.display->drawString(35, 38, "WIFI SETUP"); 52 | Heltec.display->drawString(0, 9, "OK"); 53 | Heltec.display->display(); 54 | delay(1000); 55 | Heltec.display->clear(); 56 | } 57 | else 58 | { 59 | //Heltec.display->clear(); 60 | Heltec.display->drawString(0, 9, "Failed"); 61 | Heltec.display->display(); 62 | delay(1000); 63 | Heltec.display->clear(); 64 | } 65 | } 66 | 67 | void WIFIScan(unsigned int value) 68 | { 69 | unsigned int i; 70 | WiFi.mode(WIFI_STA); 71 | for(i=0;idrawString(0, 0, "Scan start..."); 74 | Heltec.display->display(); 75 | 76 | int n = WiFi.scanNetworks(); 77 | Heltec.display->drawString(0, 9, "Scan done"); 78 | Heltec.display->display(); 79 | delay(500); 80 | Heltec.display->clear(); 81 | 82 | if (n == 0) 83 | { 84 | Heltec.display->clear(); 85 | Heltec.display->drawString(0, 18, "no network found"); 86 | Heltec.display->display(); 87 | while(1); 88 | } 89 | else 90 | { 91 | Heltec.display->drawString(0, 18, (String)n + " nets found"); 92 | Heltec.display->display(); 93 | delay(2000); 94 | Heltec.display->clear(); 95 | } 96 | } 97 | } 98 | 99 | void setup() 100 | { 101 | //pinMode(LED,OUTPUT); 102 | Heltec.begin(true /*DisplayEnable Enable*/, true /*Serial Enable*/); 103 | 104 | WIFISetUp(); 105 | WIFIScan(1); 106 | } 107 | 108 | void loop() 109 | { 110 | 111 | } 112 | -------------------------------------------------------------------------------- /examples/NTP_Example/NTP_Example.ino: -------------------------------------------------------------------------------- 1 | // WiFiKit8_NTP2 2 | // 23 May 2020 3 | // The Grand Poohbah 4 | 5 | // NTP1 uses UDP to access NTP servers directly to get the time. 6 | // NTP2 uses the NTPClient to get the time. 7 | 8 | // Adapted to WiFi Kit 8 from NetworkTimeProtocol.ino 9 | // From: https://www.hackster.io/hammadiqbal12/internet-clock-ntp-58ffd8 (17 May 2019) 10 | 11 | // Dependent libraries (must install): 12 | // NTP library: https://hacksterio.s3.amazonaws.com/uploads/attachments/893468/ntpclient_MSBZvSJiRa.rar 13 | // Time Library: https://hacksterio.s3.amazonaws.com/uploads/attachments/893471/time_ltyS0W3HlF.rar 14 | 15 | // Board: WiFi Kit 8 16 | // Port: /dev/cu.SLAB_USBtoUART 17 | 18 | // Network Time Protocol (NTP) is a protocol used to synchronize computer clock times in a network. 19 | // It belongs to and is one of the oldest parts of the TCP/IP protocol suite. 20 | // The term NTP applies to both the protocol and the client-server programs that run on computers. 21 | 22 | // NTP, which was developed by David Mills at the University of Delaware in 1981, 23 | // is designed to be highly fault-tolerant and scalable. 24 | 25 | // How does NTP work? 26 | // The NTP client initiates a time-request exchange with the NTP server. 27 | // As a result of this exchange, the client is able to calculate the link delay 28 | // and its local offset, and adjust its local clock to match the clock at the server's computer. 29 | // As a rule, six exchanges over a period of about 5 to 10 minutes are required 30 | // to initially set the clock. 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | const char *ssid = "WiFi SSID"; 40 | const char *pass = "WiFi Password"; 41 | 42 | // PDT 43 | const long kTimeZone = 8; 44 | int gDay, gMonth, gYear, gSeconds, gMinutes, gHours; 45 | int gDayLast, gMonthLast, gYearLast, gSecondsLast, gMinutesLast, gHoursLast; 46 | bool gAMlast; 47 | long int gTimeNow; 48 | const char *myMonthStr[] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 49 | const char *myDowStr[] = {"", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; 50 | 51 | // NTP 52 | WiFiUDP ntpUDP; 53 | NTPClient timeClient(ntpUDP); 54 | 55 | // Query interval 56 | #define kNtpInterval 1000 57 | // Set the timer to trigger immediately. 58 | unsigned long gNtpTimer = 0; 59 | 60 | void setup() { 61 | Serial.begin(115200); 62 | gDayLast = gMonthLast = gYearLast = gSecondsLast = gMinutesLast = gHoursLast = 0; 63 | gAMlast = true; 64 | // Initialize OLED 65 | Heltec.begin(true /*DisplayEnable Enable*/, true /*Serial Enable*/); 66 | // We start by connecting to a WiFi network 67 | setupWiFi(); 68 | drawStatus(); 69 | // The timeClient does all the NTP work. 70 | timeClient.begin(); 71 | } 72 | 73 | void loop() { 74 | // Check time 75 | if(millis() >= gNtpTimer) { 76 | gNtpTimer = millis() + kNtpInterval; 77 | timeClient.update(); 78 | // Get epoch time and adjust it according to the local time zone. 79 | gTimeNow = timeClient.getEpochTime() + (kTimeZone * 3600); 80 | gDay = day(gTimeNow); 81 | gMonth = month(gTimeNow); 82 | gYear = year(gTimeNow); 83 | gHours = hourFormat12(gTimeNow); 84 | gMinutes = minute(gTimeNow); 85 | gSeconds = second(gTimeNow); 86 | drawDate(); 87 | drawTime(); 88 | /* 89 | Serial.print(gTimeNow); 90 | Serial.print(" "); 91 | Serial.print(gDay); 92 | Serial.print("/"); 93 | Serial.print(gMonth); 94 | Serial.print("/"); 95 | Serial.print(gYear); 96 | Serial.print(" "); 97 | Serial.print(gHours); 98 | Serial.print(":"); 99 | Serial.print(gMinutes); 100 | Serial.print(":"); 101 | Serial.print(gSeconds); 102 | Serial.println(); 103 | */ 104 | } 105 | } 106 | 107 | // OLED Methods 108 | 109 | void drawStatus() { 110 | Heltec.display->clear(); 111 | Heltec.display->drawString(0, 0, "Connected to " + String(ssid)); 112 | Heltec.display->drawString(0, 10, "IP Address: " + WiFi.localIP().toString()); 113 | Heltec.display->display(); 114 | delay(5000); 115 | Heltec.display->clear(); 116 | } 117 | 118 | void drawDate() { 119 | // Assemble date string. 120 | String dateStr = String(myDowStr[weekday(gTimeNow)]) + " " + String(gDay) + " " + String(myMonthStr[gMonth]); 121 | // Small font for date display 122 | Heltec.display->setFont(ArialMT_Plain_10); 123 | if(gDay != gDayLast) { 124 | // New day. Clear the entire display. 125 | gDayLast = gDay; 126 | Heltec.display->clear(); 127 | } 128 | Heltec.display->drawString(45, 0, dateStr); 129 | Heltec.display->display(); 130 | } 131 | 132 | void drawTime() { 133 | // Large font for time display 134 | Heltec.display->setFont(ArialMT_Plain_24); 135 | // Erase last value if needed and set new last value. 136 | gHoursLast = eraseOldValueIfNeeded(5, gHoursLast, gHours); 137 | gMinutesLast = eraseOldValueIfNeeded(39, gMinutesLast, gMinutes); 138 | gSecondsLast = eraseOldValueIfNeeded(74, gSecondsLast, gSeconds); 139 | // Draw new time 140 | Heltec.display->setColor(WHITE); 141 | Heltec.display->drawString(5, 10, makeStringWithLeadingZeroIfNeeded(gHours)); 142 | Heltec.display->drawString(31, 10, ":"); 143 | Heltec.display->drawString(39, 10, makeStringWithLeadingZeroIfNeeded(gMinutes)); 144 | Heltec.display->drawString(67, 10, ":"); 145 | Heltec.display->drawString(74, 10, makeStringWithLeadingZeroIfNeeded(gSeconds)); 146 | addAMorPM(); 147 | Heltec.display->display(); 148 | } 149 | 150 | void addAMorPM() { 151 | // Small font for AM/PM 152 | Heltec.display->setFont(ArialMT_Plain_10); 153 | if (isPM(gTimeNow)) { 154 | // PM 155 | if(gAMlast) { 156 | // Change from AM to PM, erase AM 157 | gAMlast = false; 158 | Heltec.display->setColor(BLACK); 159 | Heltec.display->drawString(105, 22, "AM"); 160 | Heltec.display->display(); 161 | Heltec.display->setColor(WHITE); 162 | } 163 | Heltec.display->drawString(105, 22, "PM"); 164 | } 165 | else { 166 | // AM 167 | if(!gAMlast) { 168 | // Change from PM to AM, erase PM 169 | gAMlast = true; 170 | Heltec.display->setColor(BLACK); 171 | Heltec.display->drawString(105, 22, "PM"); 172 | Heltec.display->display(); 173 | Heltec.display->setColor(WHITE); 174 | } 175 | Heltec.display->drawString(105, 22, "AM"); 176 | } 177 | } 178 | 179 | // Add a leading zero if the time is one digit. 180 | String makeStringWithLeadingZeroIfNeeded(int theTime) { 181 | String timeStr; 182 | if (theTime < 10) 183 | timeStr = "0" + String(theTime); 184 | else 185 | timeStr = String(theTime); 186 | return timeStr; 187 | } 188 | 189 | // Draw the old value in black to erase it. 190 | // This avoids the flicker of the entire screen caused by clear. 191 | int eraseOldValueIfNeeded(int xLoc, int oldValue, int newValue) { 192 | if(newValue != oldValue) { 193 | // Value changed, erase old value by redrawing it in black. 194 | String oldValueStr = makeStringWithLeadingZeroIfNeeded(oldValue); 195 | Heltec.display->setColor(BLACK); 196 | Heltec.display->drawString(xLoc, 10, oldValueStr); 197 | Heltec.display->display(); 198 | } 199 | return newValue; 200 | } 201 | 202 | void setupWiFi(void) 203 | { 204 | // Set WiFi to station mode and disconnect from an AP if it was previously connected 205 | WiFi.disconnect(true); 206 | delay(100); 207 | WiFi.mode(WIFI_STA); 208 | WiFi.setAutoConnect(true); 209 | WiFi.begin(ssid, pass); 210 | delay(100); 211 | Heltec.display->clear(); 212 | 213 | byte count = 0; 214 | String connectingStr = "Connecting"; 215 | while(WiFi.status() != WL_CONNECTED && count < 100) 216 | { 217 | count ++; 218 | delay(500); 219 | Heltec.display->drawString(0, 0, connectingStr); 220 | Heltec.display->display(); 221 | // Display a dot for each attempt. 222 | connectingStr += "."; 223 | } 224 | //Heltec.display->clear(); 225 | if(WiFi.status() != WL_CONNECTED) 226 | { 227 | Heltec.display->drawString(0, 9, "Failed"); 228 | Heltec.display->display(); 229 | delay(1000); 230 | Heltec.display->clear(); 231 | } 232 | } -------------------------------------------------------------------------------- /examples/OLED/HelloWorld/HelloWorld.ino: -------------------------------------------------------------------------------- 1 | // This example just provide basic function test; 2 | // For more informations, please vist www.heltec.cn or mail to support@heltec.cn 3 | 4 | #include // Only needed for Arduino 1.6.5 and earlier 5 | #include "heltec.h" // alias for `#include "SSD1306Wire.h"` 6 | 7 | 8 | 9 | void setup() { 10 | Heltec.begin(true /*DisplayEnable Enable*/, true /*Serial Enable*/); 11 | Heltec.display->init(); 12 | Heltec.display->flipScreenVertically(); 13 | Heltec.display->setFont(ArialMT_Plain_10); 14 | 15 | Heltec.display->drawString(0,0,"Hello World!"); 16 | Heltec.display->display(); 17 | } 18 | 19 | void loop() { } -------------------------------------------------------------------------------- /examples/OLED/SSD1306DrawingDemo/SSD1306DrawingDemo.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * HelTec Automation(TM) ESP32 Series Dev boards OLED Drawing Function test code 3 | * 4 | * - Some OLED Drawing Function function test; 5 | * 6 | * by lxyzn from HelTec AutoMation, ChengDu, China 7 | * 成都惠利特自动化科技有限公司 8 | * www.heltec.cn 9 | * 10 | * this project also realess in GitHub: 11 | * https://github.com/HelTecAutomation/Heltec_ESP32 12 | */ 13 | 14 | #include "heltec.h" 15 | 16 | // Adapted from Adafruit_SSD1306 17 | void drawLines() { 18 | for (int16_t i=0; idrawLine(0, 0, i, DISPLAY_HEIGHT-1); 20 | Heltec.display->display(); 21 | delay(10); 22 | } 23 | for (int16_t i=0; idrawLine(0, 0, DISPLAY_WIDTH-1, i); 25 | Heltec.display->display(); 26 | delay(10); 27 | } 28 | delay(250); 29 | 30 | Heltec.display->clear(); 31 | for (int16_t i=0; idrawLine(0, DISPLAY_HEIGHT-1, i, 0); 33 | Heltec.display->display(); 34 | delay(10); 35 | } 36 | for (int16_t i=DISPLAY_HEIGHT-1; i>=0; i-=4) { 37 | Heltec.display->drawLine(0, DISPLAY_HEIGHT-1, DISPLAY_WIDTH-1, i); 38 | Heltec.display->display(); 39 | delay(10); 40 | } 41 | delay(250); 42 | 43 | Heltec.display->clear(); 44 | for (int16_t i=DISPLAY_WIDTH-1; i>=0; i-=4) { 45 | Heltec.display->drawLine(DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, i, 0); 46 | Heltec.display->display(); 47 | delay(10); 48 | } 49 | for (int16_t i=DISPLAY_HEIGHT-1; i>=0; i-=4) { 50 | Heltec.display->drawLine(DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, 0, i); 51 | Heltec.display->display(); 52 | delay(10); 53 | } 54 | delay(250); 55 | Heltec.display->clear(); 56 | for (int16_t i=0; idrawLine(DISPLAY_WIDTH-1, 0, 0, i); 58 | Heltec.display->display(); 59 | delay(10); 60 | } 61 | for (int16_t i=0; idrawLine(DISPLAY_WIDTH-1, 0, i, DISPLAY_HEIGHT-1); 63 | Heltec.display->display(); 64 | delay(10); 65 | } 66 | delay(250); 67 | } 68 | 69 | // Adapted from Adafruit_SSD1306 70 | void drawRect(void) { 71 | for (int16_t i=0; idrawRect(i, i, DISPLAY_WIDTH-2*i, DISPLAY_HEIGHT-2*i); 73 | Heltec.display->display(); 74 | delay(10); 75 | } 76 | } 77 | 78 | // Adapted from Adafruit_SSD1306 79 | void fillRect(void) { 80 | uint8_t color = 1; 81 | for (int16_t i=0; isetColor((color % 2 == 0) ? BLACK : WHITE); // alternate colors 83 | Heltec.display->fillRect(i, i, DISPLAY_WIDTH - i*2, DISPLAY_HEIGHT - i*2); 84 | Heltec.display->display(); 85 | delay(10); 86 | color++; 87 | } 88 | // Reset back to WHITE 89 | Heltec.display->setColor(WHITE); 90 | } 91 | 92 | // Adapted from Adafruit_SSD1306 93 | void drawCircle(void) { 94 | for (int16_t i=0; idrawCircle(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, i); 96 | Heltec.display->display(); 97 | delay(10); 98 | } 99 | delay(1000); 100 | Heltec.display->clear(); 101 | 102 | // This will draw the part of the circel in quadrant 1 103 | // Quadrants are numberd like this: 104 | // 0010 | 0001 105 | // ------|----- 106 | // 0100 | 1000 107 | // 108 | Heltec.display->drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000001); 109 | Heltec.display->display(); 110 | delay(200); 111 | Heltec.display->drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000011); 112 | Heltec.display->display(); 113 | delay(200); 114 | Heltec.display->drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000111); 115 | Heltec.display->display(); 116 | delay(200); 117 | Heltec.display->drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00001111); 118 | Heltec.display->display(); 119 | } 120 | 121 | void printBuffer(void) { 122 | // Initialize the log buffer 123 | // allocate memory to store 8 lines of text and 30 chars per line. 124 | Heltec.display->setLogBuffer(2, 30); 125 | 126 | // Some test data 127 | const char* test[] = { 128 | "Hello", 129 | "World" , 130 | "----", 131 | "Show off", 132 | "how", 133 | "the log buffer", 134 | "is", 135 | "working.", 136 | "Even", 137 | "scrolling is", 138 | "working" 139 | }; 140 | 141 | for (uint8_t i = 0; i < 11; i++) { 142 | Heltec.display->clear(); 143 | // Print to the screen 144 | Heltec.display->println(test[i]); 145 | // Draw it to the internal screen buffer 146 | Heltec.display->drawLogBuffer(0, 0); 147 | // Display it on the screen 148 | Heltec.display->display(); 149 | delay(500); 150 | } 151 | } 152 | 153 | void setup() { 154 | Heltec.begin(true /*DisplayEnable Enable*/, true /*Serial Enable*/); 155 | 156 | Heltec.display->setContrast(255); 157 | 158 | drawLines(); 159 | delay(1000); 160 | Heltec.display->clear(); 161 | 162 | drawRect(); 163 | delay(1000); 164 | Heltec.display->clear(); 165 | 166 | fillRect(); 167 | delay(1000); 168 | Heltec.display->clear(); 169 | 170 | drawCircle(); 171 | delay(1000); 172 | Heltec.display->clear(); 173 | 174 | printBuffer(); 175 | delay(1000); 176 | Heltec.display->clear(); 177 | } 178 | 179 | void loop() { } -------------------------------------------------------------------------------- /examples/OLED/SSD1306SimpleDemo/SSD1306SimpleDemo.ino: -------------------------------------------------------------------------------- 1 | // This example just provide basic function test; 2 | // For more informations, please vist www.heltec.cn or mail to support@heltec.cn 3 | 4 | #include // Only needed for Arduino 1.6.5 and earlier 5 | #include "heltec.h" // alias for `#include "SSD1306Wire.h"` 6 | #include "images.h" 7 | 8 | 9 | #define DEMO_DURATION 3000 10 | typedef void (*Demo)(void); 11 | 12 | int demoMode = 0; 13 | int counter = 1; 14 | 15 | void setup() { 16 | Heltec.begin(true /*DisplayEnable Enable*/, true /*Serial Enable*/); 17 | 18 | Heltec.display->flipScreenVertically(); 19 | Heltec.display->setFont(ArialMT_Plain_10); 20 | 21 | } 22 | 23 | void drawFontFaceDemo() { 24 | // Font Demo1 25 | // create more fonts at http://oledHeltec.display->squix.ch/ 26 | Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); 27 | Heltec.display->setFont(ArialMT_Plain_10); 28 | Heltec.display->drawString(0, 0, "Hello world"); 29 | Heltec.display->setFont(ArialMT_Plain_16); 30 | Heltec.display->drawString(0, 10, "Hello world"); 31 | 32 | } 33 | 34 | void drawTextFlowDemo() { 35 | Heltec.display->setFont(ArialMT_Plain_10); 36 | Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); 37 | Heltec.display->drawStringMaxWidth(0, 0, 128, 38 | "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr" ); 39 | } 40 | 41 | void drawTextAlignmentDemo() { 42 | // Text alignment demo 43 | Heltec.display->setFont(ArialMT_Plain_10); 44 | 45 | // The coordinates define the left starting point of the text 46 | Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT); 47 | Heltec.display->drawString(0, 0, "Left aligned (0,10)"); 48 | 49 | // The coordinates define the center of the text 50 | Heltec.display->setTextAlignment(TEXT_ALIGN_CENTER); 51 | Heltec.display->drawString(64, 10, "Center aligned (64,10)"); 52 | 53 | // The coordinates define the right end of the text 54 | Heltec.display->setTextAlignment(TEXT_ALIGN_RIGHT); 55 | Heltec.display->drawString(128, 20, "Right aligned (128,20)"); 56 | } 57 | 58 | void drawRectDemo() { 59 | // Draw a pixel at given position 60 | for (int i = 0; i < 10; i++) { 61 | Heltec.display->setPixel(i, i); 62 | Heltec.display->setPixel(10 - i, i); 63 | } 64 | Heltec.display->drawRect(12, 12, 20, 20); 65 | 66 | // Fill the rectangle 67 | Heltec.display->fillRect(14, 14, 17, 17); 68 | 69 | // Draw a line horizontally 70 | Heltec.display->drawHorizontalLine(0, 40, 20); 71 | 72 | // Draw a line horizontally 73 | Heltec.display->drawVerticalLine(40, 0, 20); 74 | } 75 | 76 | void drawCircleDemo() { 77 | for (int i=1; i < 8; i++) { 78 | Heltec.display->setColor(WHITE); 79 | Heltec.display->drawCircle(32, 16, i*2); 80 | if (i % 2 == 0) { 81 | Heltec.display->setColor(BLACK); 82 | } 83 | Heltec.display->fillCircle(96, 16, 32 - i* 2); 84 | } 85 | } 86 | 87 | void drawProgressBarDemo() { 88 | int progress = (counter / 5) % 100; 89 | // draw the progress bar 90 | Heltec.display->drawProgressBar(0, 10, 120, 10, progress); 91 | 92 | // draw the percentage as String 93 | Heltec.display->setTextAlignment(TEXT_ALIGN_CENTER); 94 | Heltec.display->drawString(64, 0, String(progress) + "%"); 95 | } 96 | 97 | void drawImageDemo() { 98 | // see http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html 99 | // on how to create xbm files 100 | Heltec.display->drawXbm(34, 0, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits); 101 | } 102 | 103 | Demo demos[] = {drawFontFaceDemo, drawTextFlowDemo, drawTextAlignmentDemo, drawRectDemo, drawCircleDemo, drawProgressBarDemo, drawImageDemo}; 104 | int demoLength = (sizeof(demos) / sizeof(Demo)); 105 | long timeSinceLastModeSwitch = 0; 106 | 107 | void loop() { 108 | // clear the display 109 | Heltec.display->clear(); 110 | // draw the current demo method 111 | demos[demoMode](); 112 | 113 | Heltec.display->setTextAlignment(TEXT_ALIGN_RIGHT); 114 | Heltec.display->drawString(10, 128, String(millis())); 115 | // write the buffer to the display 116 | Heltec.display->display(); 117 | 118 | if (millis() - timeSinceLastModeSwitch > DEMO_DURATION) { 119 | demoMode = (demoMode + 1) % demoLength; 120 | timeSinceLastModeSwitch = millis(); 121 | } 122 | counter++; 123 | delay(10); 124 | } -------------------------------------------------------------------------------- /examples/OLED/SSD1306SimpleDemo/images.h: -------------------------------------------------------------------------------- 1 | #define WiFi_Logo_width 60 2 | #define WiFi_Logo_height 36 3 | const unsigned char WiFi_Logo_bits[] PROGMEM = { 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 5 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 6 | 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 7 | 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 8 | 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 9 | 0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 10 | 0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF, 11 | 0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00, 12 | 0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C, 13 | 0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00, 14 | 0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C, 15 | 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00, 16 | 0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 17 | 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 18 | 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C, 19 | 0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00, 20 | 0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F, 21 | 0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00, 22 | 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 23 | 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 24 | 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF, 25 | 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 26 | 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 28 | }; 29 | 30 | //Added by Sloeber 31 | #pragma once 32 | 33 | -------------------------------------------------------------------------------- /examples/OLED/SSD1306UiDemo/SSD1306UiDemo.ino: -------------------------------------------------------------------------------- 1 | #include // Only needed for Arduino 1.6.5 and earlier 2 | #include "heltec.h" // alias for `#include "SSD1306Wire.h" 3 | #include "oled/OLEDDisplayUi.h" 4 | #include "images.h" 5 | 6 | #define DEMO_DURATION 3000 7 | typedef void (*Demo)(void); 8 | 9 | OLEDDisplayUi ui ( Heltec.display ); 10 | 11 | void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) { 12 | display->setTextAlignment(TEXT_ALIGN_RIGHT); 13 | display->setFont(ArialMT_Plain_10); 14 | display->drawString(128, 0, String(millis())); 15 | } 16 | 17 | void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { 18 | display->drawXbm(x, y, BT_width, BT_height, BT_bits); 19 | display->drawXbm(x + 12 + 1, y, WIFI_width, WIFI_height, WIFI_bits); 20 | display->drawXbm(x + 108, y, BAT_width, BAT_height, BAT_bits); 21 | display->setFont(ArialMT_Plain_24); 22 | display->drawString(x + 28, y + 5, "HelTec"); 23 | } 24 | 25 | void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { 26 | //display->drawXbm(x, y, BT_width, BT_height, BT_bits); 27 | //display->drawXbm(x + 12 + 1, y, WIFI_width, WIFI_height, WIFI_bits); 28 | //display->drawXbm(x + 108, y, BAT_width, BAT_height, BAT_bits); 29 | display->drawString(x + 10, y + 5, "WIFI KIT 8"); 30 | } 31 | 32 | void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { 33 | display->drawXbm(x + 25, y, HelTec_LOGO_width, HelTec_LOGO_height, HelTec_LOGO_bits); 34 | } 35 | 36 | void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) { 37 | display->setTextAlignment(TEXT_ALIGN_LEFT); 38 | //display->setFont(ArialMT_Plain_16); 39 | //display->drawString(x, y, "HelTec"); 40 | display->setFont(ArialMT_Plain_10); 41 | display->drawString(x, y , "HelTec AutoMation"); 42 | display->drawString(x, y + 10, "www.heltec.cn"); 43 | } 44 | 45 | FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4 }; 46 | 47 | int frameCount = 4; 48 | 49 | //OverlayCallback overlays[] = { msOverlay }; 50 | //int overlaysCount = 1; 51 | 52 | void setup() { 53 | 54 | Heltec.begin(true /*DisplayEnable Enable*/, true /*Serial Enable*/); 55 | 56 | ui.setTargetFPS(30); 57 | 58 | // Customize the active and inactive symbol 59 | ui.setActiveSymbol(activeSymbol); 60 | ui.setInactiveSymbol(inactiveSymbol); 61 | 62 | // You can change this to 63 | // TOP, LEFT, BOTTOM, RIGHT 64 | ui.setIndicatorPosition(BOTTOM); 65 | 66 | // Defines where the first frame is located in the bar. 67 | ui.setIndicatorDirection(LEFT_RIGHT); 68 | 69 | // You can change the transition that is used 70 | // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN 71 | ui.setFrameAnimation(SLIDE_LEFT); 72 | 73 | // Add frames 74 | ui.setFrames(frames, frameCount); 75 | 76 | // Add overlays 77 | // ui.setOverlays(overlays, overlaysCount); 78 | 79 | // Initialising the UI will init the display too. 80 | ui.init(); 81 | 82 | Heltec.display->flipScreenVertically(); 83 | } 84 | 85 | void loop() { 86 | int remainingTimeBudget = ui.update(); 87 | 88 | if (remainingTimeBudget > 0) { 89 | // You can do some work here 90 | // Don't do stuff if you are below your 91 | // time budget. 92 | delay(remainingTimeBudget); 93 | } 94 | } -------------------------------------------------------------------------------- /examples/OLED/SSD1306UiDemo/images.h: -------------------------------------------------------------------------------- 1 | #define BAT_width 20 2 | #define BAT_height 9 3 | 4 | #define WIFI_width 14 5 | #define WIFI_height 8 6 | 7 | #define BT_width 8 8 | #define BT_height 10 9 | 10 | #define HelTec_LOGO_width 80 11 | #define HelTec_LOGO_height 32 12 | 13 | 14 | const unsigned char HelTec_LOGO_bits[] = { 15 | 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0xF0, 16 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xF0, 0x01, 0x00, 17 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF0, 0x03, 0x00, 0x00, 0x00, 18 | 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 19 | 0x00, 0x00, 0xF8, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0xF8, 0xF9, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xF8, 21 | 0x83, 0xFF, 0x7C, 0xF8, 0x7F, 0xFE, 0x03, 0xFF, 0xFC, 0xF8, 0x83, 0xFF, 22 | 0x7C, 0xF8, 0x3F, 0xFF, 0x83, 0xFF, 0xFC, 0xFF, 0xC3, 0xFF, 0x7C, 0xF8, 23 | 0x3F, 0xFF, 0xE3, 0xFF, 0xFE, 0xFF, 0xC3, 0xFF, 0x3C, 0xF8, 0x3F, 0xFF, 24 | 0xE1, 0xFF, 0xFE, 0xFF, 0xC1, 0x07, 0x3E, 0xC0, 0x07, 0x1F, 0xF0, 0xF7, 25 | 0xFE, 0xFF, 0xC1, 0x03, 0x3E, 0xE0, 0x83, 0x0F, 0xF8, 0x41, 0xFE, 0xFF, 26 | 0xE1, 0x7F, 0x3E, 0xE0, 0x83, 0xFF, 0xF9, 0x00, 0x3F, 0xFE, 0xE0, 0x3F, 27 | 0x1F, 0xE0, 0x83, 0xFF, 0x7C, 0x00, 0x3F, 0xFE, 0xE0, 0x3F, 0x1F, 0xF0, 28 | 0x81, 0xFF, 0x7C, 0x00, 0x3F, 0xFE, 0xF0, 0x3F, 0x1F, 0xF0, 0xC1, 0xFF, 29 | 0x7C, 0x20, 0x1F, 0xFF, 0xF0, 0x01, 0x1F, 0xF0, 0xC1, 0x07, 0xFC, 0x30, 30 | 0x1F, 0x7F, 0xF0, 0x9F, 0xFF, 0xF1, 0xC1, 0x7F, 0xFC, 0x3F, 0x1F, 0x7F, 31 | 0xF0, 0x9F, 0xFF, 0xF9, 0xE0, 0x7F, 0xF8, 0x1F, 0x1E, 0x7F, 0xF8, 0x9F, 32 | 0xFF, 0xF8, 0xE0, 0x7F, 0xF8, 0x1F, 0x9C, 0x7F, 0xF8, 0x9F, 0xFF, 0xF8, 33 | 0xE0, 0x3F, 0xF0, 0x1F, 0xB0, 0x3F, 0xF8, 0xCF, 0xFF, 0x78, 0xE0, 0x3F, 34 | 0xC0, 0x0F, 0x80, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 35 | 0x80, 0x3F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x80, 0x00, 0x00, 0xC0, 0x3F, 36 | 0x70, 0xFF, 0xFF, 0x7E, 0xF6, 0xEF, 0xFD, 0x00, 0xC0, 0x1F, 0x78, 0xFB, 37 | 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0xC0, 0x1F, 0x78, 0x9B, 0xDD, 0xFF, 38 | 0xEF, 0xB6, 0x7F, 0x00, 0xC0, 0x1F, 0x7C, 0xDF, 0xFD, 0xBF, 0x6F, 0xFE, 39 | 0x7F, 0x00, 0xC0, 0x0F, 0x7E, 0xCF, 0x79, 0xFF, 0x7F, 0xF7, 0x7E, 0x00, 40 | 0x80, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 41 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; 42 | 43 | const unsigned char BAT_bits[] PROGMEM = { 44 | 0xFC, 0xFF, 0x0F, 0x04, 0x00, 0x08, 0xF7, 0xDE, 0x0B, 0xF1, 0xDE, 0x0B, 45 | 0xF1, 0xDE, 0x0B, 0xF1, 0xDE, 0x0B, 0xF7, 0xDE, 0x0B, 0x04, 0x00, 0x08, 46 | 0xFC, 0xFF, 0x0F, 47 | }; 48 | 49 | const unsigned char WIFI_bits[] PROGMEM = { 50 | 0xF0, 0x03, 0x04, 0x08, 0xF2, 0x13, 0x09, 0x24, 0xE4, 0x09, 0x10, 0x02, 51 | 0xC0, 0x00, 0xC0, 0x00, 52 | }; 53 | 54 | const unsigned char BT_bits[] PROGMEM = { 55 | 0x18, 0x28, 0x4A, 0x2C, 0x18, 0x2C, 0x4A, 0x28, 0x18, 0x00, 56 | }; 57 | 58 | 59 | 60 | //屏幕下方的小圆点 61 | const unsigned char activeSymbol[] PROGMEM = { 62 | B00000000, 63 | B00000000, 64 | B00011000, 65 | B00100100, 66 | B01000010, 67 | B01000010, 68 | B00100100, 69 | B00011000 70 | }; 71 | 72 | const unsigned char inactiveSymbol[] PROGMEM = { 73 | B00000000, 74 | B00000000, 75 | B00000000, 76 | B00000000, 77 | B00011000, 78 | B00011000, 79 | B00000000, 80 | B00000000 81 | }; 82 | 83 | //Added by Sloeber 84 | #pragma once 85 | 86 | 87 | //Added by Sloeber 88 | #pragma once 89 | 90 | -------------------------------------------------------------------------------- /examples/WiFiKit8_RSSI/WiFiKit8_RSSI.ino: -------------------------------------------------------------------------------- 1 | // WiFiKit8_RSSI 2 | // 20 Jun 2020 3 | // The Grand Poohbah 4 | 5 | // Display the signal strength of every SSID on the network. 6 | 7 | // Acceptable Signal Strengths 8 | // RSSI stands for Received Signal Strength Indicator 9 | // Signal Strength TL;DR Required for 10 | // -30 dBm Amazing Max achievable signal strength. The client can only be a few feet from the AP to achieve this. Not typical or desirable in the real world. N/A 11 | // -67 dBm Very Good Minimum signal strength for applications that require very reliable, timely delivery of data packets. VoIP/VoWiFi, streaming video 12 | // -70 dBm Okay Minimum signal strength for reliable packet delivery. Email, web 13 | // -80 dBm Not Good Minimum signal strength for basic connectivity. Packet delivery may be unreliable. N/A 14 | // -90 dBm Unusable Approaching or drowning in the noise floor. Any functionality is highly unlikely. N/A 15 | // From https://www.metageek.com/training/resources/understanding-rssi.html 16 | 17 | #include "ESP8266WiFi.h" 18 | #include 19 | 20 | #ifndef DISPLAY_WIDTH 21 | #define DISPLAY_WIDTH 128 22 | #endif 23 | 24 | // The RSSI range is -90 to -10 25 | const int kMinAbsRSSI = 10; 26 | const int kMaxAbsRSSI = 90; 27 | 28 | // Timer 29 | unsigned long gTimer = 0; 30 | unsigned long kTimeInterval = 2000; // 2 seconds 31 | 32 | void setup() { 33 | Serial.begin(115200); 34 | // Initialize OLED 35 | Heltec.begin(true /*DisplayEnable Enable*/, false /*Serial Enable*/); 36 | // Set WiFi to station mode 37 | // and disconnect from an AP if it was previously connected 38 | WiFi.mode(WIFI_STA); 39 | WiFi.disconnect(); 40 | delay(100); 41 | Serial.println(); 42 | Serial.println("Scan Networks"); 43 | drawStatus("Scan Networks"); 44 | // Start right away 45 | gTimer = 0; 46 | } 47 | 48 | void loop() { 49 | if(millis() >= gTimer) { 50 | // Time for an update. Reset timer. 51 | gTimer = millis() + kTimeInterval; 52 | int n = WiFi.scanNetworks(); 53 | for(int i=0; iclear(); 71 | // Draw new bar 72 | Heltec.display->setColor(WHITE); 73 | // Make sure rssi is within our bounds. 74 | int absRSSI = abs(rssi); 75 | if(absRSSI < kMinAbsRSSI) { absRSSI = kMinAbsRSSI; } 76 | else if(absRSSI > kMaxAbsRSSI) { absRSSI = kMaxAbsRSSI; } 77 | // Scale the rssi to the width of the display. 78 | // A large negative value is weaker, a small negative value is stronger. 79 | // Reverse the values so that smaller values have longer bars. 80 | int barWidth = map(absRSSI, kMinAbsRSSI, kMaxAbsRSSI, DISPLAY_WIDTH, 0); 81 | Heltec.display->fillRect(0, 0, barWidth, 8); 82 | Heltec.display->display(); 83 | // Draw segment markers every 20 units. 84 | Heltec.display->setColor(BLACK); 85 | for(int x=20; xdrawLine(x, 0, x, 8); 87 | Heltec.display->display(); 88 | } 89 | // Restore white for text 90 | Heltec.display->setColor(WHITE); 91 | } 92 | 93 | // Draw theStatus centered on the top line 94 | void drawStatus(String theStatus) { 95 | // Clear display 96 | Heltec.display->clear(); 97 | // Small font for status display 98 | Heltec.display->setFont(ArialMT_Plain_10); 99 | // Scroll left and leave the RSSI. 100 | int stringWidth = Heltec.display->getStringWidth(theStatus); 101 | int x = (DISPLAY_WIDTH - stringWidth) / 2; 102 | Heltec.display->drawString(x, 0, theStatus); 103 | Heltec.display->display(); 104 | } 105 | 106 | // Scroll theSSID and theRSSI from right to left on the bottom line 107 | void scrollLeft(String theSSID, int theRSSI) { 108 | String theMessage = theSSID + " " + String(theRSSI); 109 | // Large font 110 | Heltec.display->setFont(ArialMT_Plain_24); 111 | // Scroll far enough to leave the RSSI showing at the end of the scroll. 112 | int xEnd = -Heltec.display->getStringWidth(theSSID); 113 | for(int x=DISPLAY_WIDTH; x>=xEnd; x--){ 114 | Heltec.display->drawString(x, 5, theMessage); 115 | Heltec.display->display(); 116 | delay(10); 117 | yield(); 118 | // Erase the text that we just drew. 119 | Heltec.display->setColor(BLACK); 120 | Heltec.display->drawString(x, 5, theMessage); 121 | Heltec.display->setColor(WHITE); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /img/location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelTecAutomation/Heltec_ESP8266/29bc0c76ccbb787e35d9ec42b8f29f90a3778ce9/img/location.png -------------------------------------------------------------------------------- /img/location_8266.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelTecAutomation/Heltec_ESP8266/29bc0c76ccbb787e35d9ec42b8f29f90a3778ce9/img/location_8266.png -------------------------------------------------------------------------------- /img/location_cn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelTecAutomation/Heltec_ESP8266/29bc0c76ccbb787e35d9ec42b8f29f90a3778ce9/img/location_cn.png -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Heltec_ESP32 3 | ####################################### 4 | 5 | ####################################### 6 | # Library (KEYWORD3) 7 | ####################################### 8 | 9 | Heltec KEYWORD3 10 | 11 | ####################################### 12 | # Datatypes (KEYWORD1) 13 | ####################################### 14 | 15 | LoRa KEYWORD1 16 | display KEYWORD1 17 | 18 | ####################################### 19 | # Methods and Functions (KEYWORD2) 20 | ####################################### 21 | 22 | begin KEYWORD2 23 | end KEYWORD2 24 | write KEYWORD2 25 | sleep KEYWORD2 26 | 27 | beginPacket KEYWORD2 28 | endPacket KEYWORD2 29 | parsePacket KEYWORD2 30 | packetRssi KEYWORD2 31 | packetSnr KEYWORD2 32 | available KEYWORD2 33 | peek KEYWORD2 34 | flush KEYWORD2 35 | onReceive KEYWORD2 36 | receive KEYWORD2 37 | read KEYWORD2 38 | idle KEYWORD2 39 | setTxPower KEYWORD2 40 | setTxPowerMax KEYWORD2 41 | setFrequency KEYWORD2 42 | setSpreadingFactor KEYWORD2 43 | setSignalBandwidth KEYWORD2 44 | setCodingRate4 KEYWORD2 45 | setPreambleLength KEYWORD2 46 | setSyncWord KEYWORD2 47 | enableCrc KEYWORD2 48 | disableCrc KEYWORD2 49 | crc KEYWORD2 50 | noCrc KEYWORD2 51 | random KEYWORD2 52 | setpins KEYWORD2 53 | setSPIFrequency KEYWORD2 54 | dumpRegisters KEYWORD2 55 | 56 | drawString KEYWORD2 57 | display KEYWORD2 58 | wakeup KEYWORD2 59 | resetDisplay KEYWORD2 60 | setColor KEYWORD2 61 | getColor KEYWORD2 62 | setPixel KEYWORD2 63 | drawLine KEYWORD2 64 | drawRect KEYWORD2 65 | fillRect KEYWORD2 66 | drawCircle KEYWORD2 67 | drawCircleQuads KEYWORD2 68 | fillCircle KEYWORD2 69 | drawHorizontalLine KEYWORD2 70 | drawVerticalLine KEYWORD2 71 | drawProgressBar KEYWORD2 72 | drawFastImage KEYWORD2 73 | drawXbm KEYWORD2 74 | drawStringMaxWidth KEYWORD2 75 | getStringWidth KEYWORD2 76 | getStringWidth KEYWORD2 77 | setTextAlignment KEYWORD2 78 | setFont KEYWORD2 79 | setFontTableLookupFunction KEYWORD2 80 | displayOn KEYWORD2 81 | displayOff KEYWORD2 82 | invertDisplay KEYWORD2 83 | normalDisplay KEYWORD2 84 | setContrast KEYWORD2 85 | setBrightness KEYWORD2 86 | resetOrientation KEYWORD2 87 | flipScreenVertically KEYWORD2 88 | mirrorScreen KEYWORD2 89 | clear KEYWORD2 90 | setLogBuffer KEYWORD2 91 | drawLogBuffer KEYWORD2 92 | getWidth KEYWORD2 93 | getHeight KEYWORD2 94 | 95 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Heltec_ESP8266", 3 | "keywords": "heltec, esp8266, wifi kit, oled", 4 | "description": "A library for Heltec ESP8266 based board", 5 | "repository":{ 6 | "type": "git", 7 | "url": "https://github.com/HelTecAutomation/Heltec_ESP8266.git" 8 | }, 9 | "authors":{ 10 | "name": "Heltec Automation(TM)", 11 | "email": "support@heltec.cn" 12 | }, 13 | "version": "1.0.3", 14 | "frameworks": "arduino", 15 | "platforms": "espressif8266" 16 | } 17 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Heltec ESP8266 Dev-Boards 2 | version=1.0.3 3 | author= Heltec Automation 4 | maintainer=HelTec 5 | sentence=Library for Heltec ESP8266 based boards 6 | paragraph=Include, WiFi Kit 8, see more on http://heltec.cn 7 | category=Device Control 8 | url=https://github.com/HelTecAutomation/Heltec_ESP32.git 9 | architectures=esp8266 10 | includes=heltec.h 11 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Heltec Automation(TM) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | See more at http://www.heltec.cn 24 | -------------------------------------------------------------------------------- /src/heltec.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Heltec Automation. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | #include "heltec.h" 5 | 6 | Heltec_ESP8266::Heltec_ESP8266(){ 7 | 8 | #if defined( WIFI_Kit_8 ) 9 | display = new SSD1306Wire(0x3c, SDA, SCL, OLED_RST, GEOMETRY_128_32); 10 | #endif 11 | } 12 | 13 | Heltec_ESP8266::~Heltec_ESP8266(){ 14 | delete display; 15 | } 16 | 17 | void Heltec_ESP8266::begin(bool DisplayEnable, bool SerialEnable) { 18 | 19 | //#if defined( WIFI_LoRa_32_V2 ) || defined( Wireless_Stick ) 20 | 21 | // VextON(); 22 | //#endif 23 | 24 | // UART 25 | if (SerialEnable) { 26 | Serial.begin(115200); 27 | Serial.flush(); 28 | delay(50); 29 | Serial.print("Serial initial done\r\n"); 30 | } 31 | 32 | // OLED 33 | if (DisplayEnable){ 34 | display->init(); 35 | display->flipScreenVertically(); 36 | display->setFont(ArialMT_Plain_10); 37 | display->drawString(0, 0, "OLED initial done!"); 38 | display->display(); 39 | 40 | if (SerialEnable){ 41 | Serial.print("you can see OLED printed OLED initial done!\r\n"); 42 | } 43 | } 44 | } 45 | 46 | //void Heltec_ESP32::VextON(void) 47 | //{ 48 | // pinMode(Vext,OUTPUT); 49 | // digitalWrite(Vext, LOW); 50 | //} 51 | // 52 | //void Heltec_ESP32::VextOFF(void) //Vext default OFF 53 | //{ 54 | // pinMode(Vext,OUTPUT); 55 | // digitalWrite(Vext, HIGH); 56 | //} 57 | 58 | Heltec_ESP8266 Heltec; 59 | 60 | -------------------------------------------------------------------------------- /src/heltec.h: -------------------------------------------------------------------------------- 1 | #ifndef _HELTEC_H_ 2 | #define _HELTEC_H_ 3 | 4 | #if defined(ESP8266) 5 | 6 | #include 7 | 8 | #if defined( WIFI_Kit_8 ) 9 | #include 10 | #include "oled/SSD1306Wire.h" 11 | 12 | #define DISPLAY_WIDTH 128 13 | #define DISPLAY_HEIGHT 32 14 | #define OLED_RST 16 //add this line for OLED_RST undefined error when compiling heltec.cpp in platformIO 15 | 16 | #endif 17 | 18 | class Heltec_ESP8266 { 19 | 20 | public: 21 | Heltec_ESP8266(); 22 | ~Heltec_ESP8266(); 23 | 24 | void begin(bool DisplayEnable=true, bool SerialEnable=true); 25 | SSD1306Wire *display; 26 | }; 27 | 28 | extern Heltec_ESP8266 Heltec; 29 | 30 | #else 31 | #error "This library only supports boards with ESP8266 processor." 32 | #endif 33 | 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/oled/OLEDDisplay.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn 5 | * Copyright (c) 2018 by Fabrice Weinberg 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | * 25 | * ThingPulse invests considerable time and money to develop these open source libraries. 26 | * Please support us by buying our products (and not the clones) from 27 | * https://thingpulse.com 28 | * 29 | */ 30 | 31 | #include "OLEDDisplay.h" 32 | 33 | OLEDDisplay::~OLEDDisplay() { 34 | end(); 35 | } 36 | 37 | bool OLEDDisplay::init() { 38 | if (!this->connect()) { 39 | //DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n"); 40 | Serial.print("Can't establish connection to display\n"); 41 | return false; 42 | } 43 | 44 | if(this->buffer==NULL) { 45 | this->buffer = (uint8_t*) malloc(sizeof(uint8_t) * displayBufferSize); 46 | 47 | if(!this->buffer) { 48 | //DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n"); 49 | Serial.print("Not enough memory to create display\n"); 50 | return false; 51 | } 52 | } 53 | 54 | #ifdef OLEDDISPLAY_DOUBLE_BUFFER 55 | if(this->buffer_back==NULL) { 56 | this->buffer_back = (uint8_t*) malloc(sizeof(uint8_t) * displayBufferSize); 57 | 58 | if(!this->buffer_back) { 59 | //DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n"); 60 | Serial.print("Not enough memory to create back buffer\n"); 61 | free(this->buffer); 62 | return false; 63 | } 64 | } 65 | #endif 66 | 67 | resetDisplay(16); 68 | sendInitCommands(); 69 | 70 | clear(); 71 | #ifdef OLEDDISPLAY_DOUBLE_BUFFER 72 | memset(buffer_back, 1, displayBufferSize); 73 | #endif 74 | display(); 75 | return true; 76 | } 77 | 78 | void OLEDDisplay::end() { 79 | if (this->buffer) { free(this->buffer); this->buffer = NULL; } 80 | #ifdef OLEDDISPLAY_DOUBLE_BUFFER 81 | if (this->buffer_back) { free(this->buffer_back); this->buffer_back = NULL; } 82 | #endif 83 | if (this->logBuffer != NULL) { free(this->logBuffer); this->logBuffer = NULL; } 84 | } 85 | 86 | void OLEDDisplay::sleep() { 87 | sendCommand(0x8D); 88 | sendCommand(0x10); 89 | sendCommand(0xAE); 90 | } 91 | 92 | void OLEDDisplay::wakeup() { 93 | sendCommand(0x8D); 94 | sendCommand(0x14); 95 | sendCommand(0xAF); 96 | } 97 | 98 | void OLEDDisplay::resetDisplay(uint8_t rstPin) { 99 | pinMode(rstPin, OUTPUT); 100 | digitalWrite(rstPin,LOW); 101 | delay(100); 102 | digitalWrite(rstPin,HIGH); 103 | } 104 | 105 | void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) { 106 | this->color = color; 107 | } 108 | 109 | OLEDDISPLAY_COLOR OLEDDisplay::getColor() { 110 | return this->color; 111 | } 112 | 113 | void OLEDDisplay::setPixel(int16_t x, int16_t y) { 114 | if (x >= 0 && x < this->width() && y >= 0 && y < this->height()) { 115 | switch (color) { 116 | case WHITE: buffer[x + (y / 8) * this->width()] |= (1 << (y & 7)); break; 117 | case BLACK: buffer[x + (y / 8) * this->width()] &= ~(1 << (y & 7)); break; 118 | case INVERSE: buffer[x + (y / 8) * this->width()] ^= (1 << (y & 7)); break; 119 | } 120 | } 121 | } 122 | 123 | // Bresenham's algorithm - thx wikipedia and Adafruit_GFX 124 | void OLEDDisplay::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) { 125 | int16_t steep = abs(y1 - y0) > abs(x1 - x0); 126 | if (steep) { 127 | _swap_int16_t(x0, y0); 128 | _swap_int16_t(x1, y1); 129 | } 130 | 131 | if (x0 > x1) { 132 | _swap_int16_t(x0, x1); 133 | _swap_int16_t(y0, y1); 134 | } 135 | 136 | int16_t dx, dy; 137 | dx = x1 - x0; 138 | dy = abs(y1 - y0); 139 | 140 | int16_t err = dx / 2; 141 | int16_t ystep; 142 | 143 | if (y0 < y1) { 144 | ystep = 1; 145 | } else { 146 | ystep = -1; 147 | } 148 | 149 | for (; x0<=x1; x0++) { 150 | if (steep) { 151 | setPixel(y0, x0); 152 | } else { 153 | setPixel(x0, y0); 154 | } 155 | err -= dy; 156 | if (err < 0) { 157 | y0 += ystep; 158 | err += dx; 159 | } 160 | } 161 | } 162 | 163 | void OLEDDisplay::drawRect(int16_t x, int16_t y, int16_t width, int16_t height) { 164 | drawHorizontalLine(x, y, width); 165 | drawVerticalLine(x, y, height); 166 | drawVerticalLine(x + width - 1, y, height); 167 | drawHorizontalLine(x, y + height - 1, width); 168 | } 169 | 170 | void OLEDDisplay::fillRect(int16_t xMove, int16_t yMove, int16_t width, int16_t height) { 171 | for (int16_t x = xMove; x < xMove + width; x++) { 172 | drawVerticalLine(x, yMove, height); 173 | } 174 | } 175 | 176 | void OLEDDisplay::drawCircle(int16_t x0, int16_t y0, int16_t radius) { 177 | int16_t x = 0, y = radius; 178 | int16_t dp = 1 - radius; 179 | do { 180 | if (dp < 0) 181 | dp = dp + 2 * (++x) + 3; 182 | else 183 | dp = dp + 2 * (++x) - 2 * (--y) + 5; 184 | 185 | setPixel(x0 + x, y0 + y); //For the 8 octants 186 | setPixel(x0 - x, y0 + y); 187 | setPixel(x0 + x, y0 - y); 188 | setPixel(x0 - x, y0 - y); 189 | setPixel(x0 + y, y0 + x); 190 | setPixel(x0 - y, y0 + x); 191 | setPixel(x0 + y, y0 - x); 192 | setPixel(x0 - y, y0 - x); 193 | 194 | } while (x < y); 195 | 196 | setPixel(x0 + radius, y0); 197 | setPixel(x0, y0 + radius); 198 | setPixel(x0 - radius, y0); 199 | setPixel(x0, y0 - radius); 200 | } 201 | 202 | void OLEDDisplay::drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads) { 203 | int16_t x = 0, y = radius; 204 | int16_t dp = 1 - radius; 205 | while (x < y) { 206 | if (dp < 0) 207 | dp = dp + 2 * (++x) + 3; 208 | else 209 | dp = dp + 2 * (++x) - 2 * (--y) + 5; 210 | if (quads & 0x1) { 211 | setPixel(x0 + x, y0 - y); 212 | setPixel(x0 + y, y0 - x); 213 | } 214 | if (quads & 0x2) { 215 | setPixel(x0 - y, y0 - x); 216 | setPixel(x0 - x, y0 - y); 217 | } 218 | if (quads & 0x4) { 219 | setPixel(x0 - y, y0 + x); 220 | setPixel(x0 - x, y0 + y); 221 | } 222 | if (quads & 0x8) { 223 | setPixel(x0 + x, y0 + y); 224 | setPixel(x0 + y, y0 + x); 225 | } 226 | } 227 | if (quads & 0x1 && quads & 0x8) { 228 | setPixel(x0 + radius, y0); 229 | } 230 | if (quads & 0x4 && quads & 0x8) { 231 | setPixel(x0, y0 + radius); 232 | } 233 | if (quads & 0x2 && quads & 0x4) { 234 | setPixel(x0 - radius, y0); 235 | } 236 | if (quads & 0x1 && quads & 0x2) { 237 | setPixel(x0, y0 - radius); 238 | } 239 | } 240 | 241 | 242 | void OLEDDisplay::fillCircle(int16_t x0, int16_t y0, int16_t radius) { 243 | int16_t x = 0, y = radius; 244 | int16_t dp = 1 - radius; 245 | do { 246 | if (dp < 0) 247 | dp = dp + 2 * (++x) + 3; 248 | else 249 | dp = dp + 2 * (++x) - 2 * (--y) + 5; 250 | 251 | drawHorizontalLine(x0 - x, y0 - y, 2*x); 252 | drawHorizontalLine(x0 - x, y0 + y, 2*x); 253 | drawHorizontalLine(x0 - y, y0 - x, 2*y); 254 | drawHorizontalLine(x0 - y, y0 + x, 2*y); 255 | 256 | 257 | } while (x < y); 258 | drawHorizontalLine(x0 - radius, y0, 2 * radius); 259 | 260 | } 261 | 262 | void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) { 263 | if (y < 0 || y >= this->height()) { return; } 264 | 265 | if (x < 0) { 266 | length += x; 267 | x = 0; 268 | } 269 | 270 | if ( (x + length) > this->width()) { 271 | length = (this->width() - x); 272 | } 273 | 274 | if (length <= 0) { return; } 275 | 276 | uint8_t * bufferPtr = buffer; 277 | bufferPtr += (y >> 3) * this->width(); 278 | bufferPtr += x; 279 | 280 | uint8_t drawBit = 1 << (y & 7); 281 | 282 | switch (color) { 283 | case WHITE: while (length--) { 284 | *bufferPtr++ |= drawBit; 285 | }; break; 286 | case BLACK: drawBit = ~drawBit; while (length--) { 287 | *bufferPtr++ &= drawBit; 288 | }; break; 289 | case INVERSE: while (length--) { 290 | *bufferPtr++ ^= drawBit; 291 | }; break; 292 | } 293 | } 294 | 295 | void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) { 296 | if (x < 0 || x >= this->width()) return; 297 | 298 | if (y < 0) { 299 | length += y; 300 | y = 0; 301 | } 302 | 303 | if ( (y + length) > this->height()) { 304 | length = (this->height() - y); 305 | } 306 | 307 | if (length <= 0) return; 308 | 309 | 310 | uint8_t yOffset = y & 7; 311 | uint8_t drawBit; 312 | uint8_t *bufferPtr = buffer; 313 | 314 | bufferPtr += (y >> 3) * this->width(); 315 | bufferPtr += x; 316 | 317 | if (yOffset) { 318 | yOffset = 8 - yOffset; 319 | drawBit = ~(0xFF >> (yOffset)); 320 | 321 | if (length < yOffset) { 322 | drawBit &= (0xFF >> (yOffset - length)); 323 | } 324 | 325 | switch (color) { 326 | case WHITE: *bufferPtr |= drawBit; break; 327 | case BLACK: *bufferPtr &= ~drawBit; break; 328 | case INVERSE: *bufferPtr ^= drawBit; break; 329 | } 330 | 331 | if (length < yOffset) return; 332 | 333 | length -= yOffset; 334 | bufferPtr += this->width(); 335 | } 336 | 337 | if (length >= 8) { 338 | switch (color) { 339 | case WHITE: 340 | case BLACK: 341 | drawBit = (color == WHITE) ? 0xFF : 0x00; 342 | do { 343 | *bufferPtr = drawBit; 344 | bufferPtr += this->width(); 345 | length -= 8; 346 | } while (length >= 8); 347 | break; 348 | case INVERSE: 349 | do { 350 | *bufferPtr = ~(*bufferPtr); 351 | bufferPtr += this->width(); 352 | length -= 8; 353 | } while (length >= 8); 354 | break; 355 | } 356 | } 357 | 358 | if (length > 0) { 359 | drawBit = (1 << (length & 7)) - 1; 360 | switch (color) { 361 | case WHITE: *bufferPtr |= drawBit; break; 362 | case BLACK: *bufferPtr &= ~drawBit; break; 363 | case INVERSE: *bufferPtr ^= drawBit; break; 364 | } 365 | } 366 | } 367 | 368 | void OLEDDisplay::drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress) { 369 | uint16_t radius = height / 2; 370 | uint16_t xRadius = x + radius; 371 | uint16_t yRadius = y + radius; 372 | uint16_t doubleRadius = 2 * radius; 373 | uint16_t innerRadius = radius - 2; 374 | 375 | setColor(WHITE); 376 | drawCircleQuads(xRadius, yRadius, radius, 0b00000110); 377 | drawHorizontalLine(xRadius, y, width - doubleRadius + 1); 378 | drawHorizontalLine(xRadius, y + height, width - doubleRadius + 1); 379 | drawCircleQuads(x + width - radius, yRadius, radius, 0b00001001); 380 | 381 | uint16_t maxProgressWidth = (width - doubleRadius + 1) * progress / 100; 382 | 383 | fillCircle(xRadius, yRadius, innerRadius); 384 | fillRect(xRadius + 1, y + 2, maxProgressWidth, height - 3); 385 | fillCircle(xRadius + maxProgressWidth, yRadius, innerRadius); 386 | } 387 | 388 | void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *image) { 389 | drawInternal(xMove, yMove, width, height, image, 0, 0); 390 | } 391 | 392 | void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *xbm) { 393 | int16_t widthInXbm = (width + 7) / 8; 394 | uint8_t data = 0; 395 | 396 | for(int16_t y = 0; y < height; y++) { 397 | for(int16_t x = 0; x < width; x++ ) { 398 | if (x & 7) { 399 | data >>= 1; // Move a bit 400 | } else { // Read new data every 8 bit 401 | data = pgm_read_byte(xbm + (x / 8) + y * widthInXbm); 402 | } 403 | // if there is a bit draw it 404 | if (data & 0x01) { 405 | setPixel(xMove + x, yMove + y); 406 | } 407 | } 408 | } 409 | } 410 | 411 | void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth) { 412 | uint8_t textHeight = pgm_read_byte(fontData + HEIGHT_POS); 413 | uint8_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); 414 | uint16_t sizeOfJumpTable = pgm_read_byte(fontData + CHAR_NUM_POS) * JUMPTABLE_BYTES; 415 | 416 | uint8_t cursorX = 0; 417 | uint8_t cursorY = 0; 418 | 419 | switch (textAlignment) { 420 | case TEXT_ALIGN_CENTER_BOTH: 421 | yMove -= textHeight >> 1; 422 | // Fallthrough 423 | case TEXT_ALIGN_CENTER: 424 | xMove -= textWidth >> 1; // divide by 2 425 | break; 426 | case TEXT_ALIGN_RIGHT: 427 | xMove -= textWidth; 428 | break; 429 | case TEXT_ALIGN_LEFT: 430 | break; 431 | } 432 | 433 | // Don't draw anything if it is not on the screen. 434 | if (xMove + textWidth < 0 || xMove > this->width() ) {return;} 435 | if (yMove + textHeight < 0 || yMove > this->width() ) {return;} 436 | 437 | for (uint16_t j = 0; j < textLength; j++) { 438 | int16_t xPos = xMove + cursorX; 439 | int16_t yPos = yMove + cursorY; 440 | 441 | byte code = text[j]; 442 | if (code >= firstChar) { 443 | byte charCode = code - firstChar; 444 | 445 | // 4 Bytes per char code 446 | byte msbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES ); // MSB \ JumpAddress 447 | byte lsbJumpToChar = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB); // LSB / 448 | byte charByteSize = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE); // Size 449 | byte currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); // Width 450 | 451 | // Test if the char is drawable 452 | if (!(msbJumpToChar == 255 && lsbJumpToChar == 255)) { 453 | // Get the position of the char data 454 | uint16_t charDataPosition = JUMPTABLE_START + sizeOfJumpTable + ((msbJumpToChar << 8) + lsbJumpToChar); 455 | drawInternal(xPos, yPos, currentCharWidth, textHeight, fontData, charDataPosition, charByteSize); 456 | } 457 | 458 | cursorX += currentCharWidth; 459 | } 460 | } 461 | } 462 | 463 | 464 | void OLEDDisplay::drawString(int16_t xMove, int16_t yMove, String strUser) { 465 | uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); 466 | 467 | // char* text must be freed! 468 | char* text = utf8ascii(strUser); 469 | 470 | uint16_t yOffset = 0; 471 | // If the string should be centered vertically too 472 | // we need to now how heigh the string is. 473 | if (textAlignment == TEXT_ALIGN_CENTER_BOTH) { 474 | uint16_t lb = 0; 475 | // Find number of linebreaks in text 476 | for (uint16_t i=0;text[i] != 0; i++) { 477 | lb += (text[i] == 10); 478 | } 479 | // Calculate center 480 | yOffset = (lb * lineHeight) / 2; 481 | } 482 | 483 | uint16_t line = 0; 484 | char* textPart = strtok(text,"\n"); 485 | while (textPart != NULL) { 486 | uint16_t length = strlen(textPart); 487 | drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length)); 488 | textPart = strtok(NULL, "\n"); 489 | } 490 | free(text); 491 | } 492 | //void OLEDDisplay::drawdata(int16_t xMove, int16_t yMove, int16_t Num) { 493 | // uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); 494 | // unsigned char c = 0,i = 0,j = 0,ch[3]; 495 | // String strUser; 496 | // 497 | // ch[0] = Num/100 + 48;//锟斤拷锟斤拷十锟斤拷锟狡碉拷48锟斤拷为锟剿革拷Num锟斤拷锟斤拷ASCLL锟斤拷母锟�4位0011 0000锟斤拷 498 | // ch[1] = Num%100/10 + 48; 499 | // ch[2] = Num%10 + 48; 500 | // 501 | // if(ch[0] == 48) //锟斤拷锟节帮拷锟斤拷锟斤拷每位为"0"时锟斤拷锟斤拷煽崭瘢锟斤拷锟斤拷锟绞撅拷锟� 502 | // { 503 | // ch[0] = 32; 504 | // if(ch[1] == 48) 505 | // { 506 | // ch[1] = 32; 507 | // if(ch[2] == 48) 508 | // { 509 | // ch[2] = 32; 510 | // } 511 | // else{ch[2] = Num%10 + 48;} 512 | // } 513 | // else{ch[1] = Num%100/10 + 48;} 514 | // } 515 | // else {ch[0] = Num/100 + 48;} 516 | // 517 | // // char* text must be freed! 518 | // char* text = utf8ascii(strUser); 519 | // 520 | // uint16_t yOffset = 0; 521 | // // If the string should be centered vertically too 522 | // // we need to now how heigh the string is. 523 | // if (textAlignment == TEXT_ALIGN_CENTER_BOTH) { 524 | // uint16_t lb = 0; 525 | // // Find number of linebreaks in text 526 | // for (uint16_t i=0;text[i] != 0; i++) { 527 | // lb += (text[i] == 10); 528 | // } 529 | // // Calculate center 530 | // yOffset = (lb * lineHeight) / 2; 531 | // } 532 | // 533 | // uint16_t line = 0; 534 | // char* textPart = strtok(text,"\n"); 535 | // while (textPart != NULL) { 536 | // uint16_t length = strlen(textPart); 537 | // drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length)); 538 | // textPart = strtok(NULL, "\n"); 539 | // } 540 | // free(text); 541 | //} 542 | 543 | void OLEDDisplay::drawStringMaxWidth(int16_t xMove, int16_t yMove, uint16_t maxLineWidth, String strUser) { 544 | uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); 545 | uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); 546 | 547 | char* text = utf8ascii(strUser); 548 | 549 | uint16_t length = strlen(text); 550 | uint16_t lastDrawnPos = 0; 551 | uint16_t lineNumber = 0; 552 | uint16_t strWidth = 0; 553 | 554 | uint16_t preferredBreakpoint = 0; 555 | uint16_t widthAtBreakpoint = 0; 556 | 557 | for (uint16_t i = 0; i < length; i++) { 558 | strWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[i] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); 559 | 560 | // Always try to break on a space or dash 561 | if (text[i] == ' ' || text[i]== '-') { 562 | preferredBreakpoint = i; 563 | widthAtBreakpoint = strWidth; 564 | } 565 | 566 | if (strWidth >= maxLineWidth) { 567 | if (preferredBreakpoint == 0) { 568 | preferredBreakpoint = i; 569 | widthAtBreakpoint = strWidth; 570 | } 571 | drawStringInternal(xMove, yMove + (lineNumber++) * lineHeight , &text[lastDrawnPos], preferredBreakpoint - lastDrawnPos, widthAtBreakpoint); 572 | lastDrawnPos = preferredBreakpoint + 1; 573 | // It is possible that we did not draw all letters to i so we need 574 | // to account for the width of the chars from `i - preferredBreakpoint` 575 | // by calculating the width we did not draw yet. 576 | strWidth = strWidth - widthAtBreakpoint; 577 | preferredBreakpoint = 0; 578 | } 579 | } 580 | 581 | // Draw last part if needed 582 | if (lastDrawnPos < length) { 583 | drawStringInternal(xMove, yMove + lineNumber * lineHeight , &text[lastDrawnPos], length - lastDrawnPos, getStringWidth(&text[lastDrawnPos], length - lastDrawnPos)); 584 | } 585 | 586 | free(text); 587 | } 588 | 589 | uint16_t OLEDDisplay::getStringWidth(const char* text, uint16_t length) { 590 | uint16_t firstChar = pgm_read_byte(fontData + FIRST_CHAR_POS); 591 | 592 | uint16_t stringWidth = 0; 593 | uint16_t maxWidth = 0; 594 | 595 | while (length--) { 596 | stringWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[length] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); 597 | if (text[length] == 10) { 598 | maxWidth = max(maxWidth, stringWidth); 599 | stringWidth = 0; 600 | } 601 | } 602 | 603 | return max(maxWidth, stringWidth); 604 | } 605 | 606 | uint16_t OLEDDisplay::getStringWidth(String strUser) { 607 | char* text = utf8ascii(strUser); 608 | uint16_t length = strlen(text); 609 | uint16_t width = getStringWidth(text, length); 610 | free(text); 611 | return width; 612 | } 613 | 614 | void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) { 615 | this->textAlignment = textAlignment; 616 | } 617 | 618 | void OLEDDisplay::setFont(const uint8_t *fontData) { 619 | this->fontData = fontData; 620 | } 621 | 622 | void OLEDDisplay::displayOn(void) { 623 | sendCommand(DISPLAYON); 624 | } 625 | 626 | void OLEDDisplay::displayOff(void) { 627 | sendCommand(DISPLAYOFF); 628 | } 629 | 630 | void OLEDDisplay::invertDisplay(void) { 631 | sendCommand(INVERTDISPLAY); 632 | } 633 | 634 | void OLEDDisplay::normalDisplay(void) { 635 | sendCommand(NORMALDISPLAY); 636 | } 637 | 638 | void OLEDDisplay::setContrast(uint8_t contrast, uint8_t precharge, uint8_t comdetect) { 639 | sendCommand(SETPRECHARGE); //0xD9 640 | sendCommand(precharge); //0xF1 default, to lower the contrast, put 1-1F 641 | sendCommand(SETCONTRAST); 642 | sendCommand(contrast); // 0-255 643 | sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast) 644 | sendCommand(comdetect); //0x40 default, to lower the contrast, put 0 645 | sendCommand(DISPLAYALLON_RESUME); 646 | sendCommand(NORMALDISPLAY); 647 | sendCommand(DISPLAYON); 648 | } 649 | 650 | void OLEDDisplay::setBrightness(uint8_t brightness) { 651 | uint8_t contrast = brightness; 652 | if (brightness < 128) { 653 | // Magic values to get a smooth/ step-free transition 654 | contrast = brightness * 1.171; 655 | } else { 656 | contrast = brightness * 1.171 - 43; 657 | } 658 | 659 | uint8_t precharge = 241; 660 | if (brightness == 0) { 661 | precharge = 0; 662 | } 663 | uint8_t comdetect = brightness / 8; 664 | 665 | setContrast(contrast, precharge, comdetect); 666 | } 667 | 668 | void OLEDDisplay::resetOrientation() { 669 | sendCommand(SEGREMAP); 670 | sendCommand(COMSCANINC); //Reset screen rotation or mirroring 671 | } 672 | 673 | void OLEDDisplay::flipScreenVertically() { 674 | sendCommand(SEGREMAP | 0x01); 675 | sendCommand(COMSCANDEC); //Rotate screen 180 Deg 676 | } 677 | 678 | void OLEDDisplay::mirrorScreen() { 679 | sendCommand(SEGREMAP); 680 | sendCommand(COMSCANDEC); //Mirror screen 681 | } 682 | 683 | void OLEDDisplay::clear(void) { 684 | memset(buffer, 0, displayBufferSize); 685 | } 686 | 687 | void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) { 688 | uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS); 689 | // Always align left 690 | setTextAlignment(TEXT_ALIGN_LEFT); 691 | 692 | // State values 693 | uint16_t length = 0; 694 | uint16_t line = 0; 695 | uint16_t lastPos = 0; 696 | 697 | for (uint16_t i=0;ilogBufferFilled;i++){ 698 | // Everytime we have a \n print 699 | if (this->logBuffer[i] == 10) { 700 | length++; 701 | // Draw string on line `line` from lastPos to length 702 | // Passing 0 as the lenght because we are in TEXT_ALIGN_LEFT 703 | drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0); 704 | // Remember last pos 705 | lastPos = i; 706 | // Reset length 707 | length = 0; 708 | } else { 709 | // Count chars until next linebreak 710 | length++; 711 | } 712 | } 713 | // Draw the remaining string 714 | if (length > 0) { 715 | drawStringInternal(xMove, yMove + line * lineHeight, &this->logBuffer[lastPos], length, 0); 716 | } 717 | } 718 | 719 | uint16_t OLEDDisplay::getWidth(void) { 720 | return displayWidth; 721 | } 722 | 723 | uint16_t OLEDDisplay::getHeight(void) { 724 | return displayHeight; 725 | } 726 | 727 | bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){ 728 | if (logBuffer != NULL) free(logBuffer); 729 | uint16_t size = lines * chars; 730 | if (size > 0) { 731 | this->logBufferLine = 0; // Lines printed 732 | this->logBufferFilled = 0; // Nothing stored yet 733 | this->logBufferMaxLines = lines; // Lines max printable 734 | this->logBufferSize = size; // Total number of characters the buffer can hold 735 | this->logBuffer = (char *) malloc(size * sizeof(uint8_t)); 736 | if(!this->logBuffer) { 737 | DEBUG_OLEDDISPLAY("[OLEDDISPLAY][setLogBuffer] Not enough memory to create log buffer\n"); 738 | return false; 739 | } 740 | } 741 | return true; 742 | } 743 | 744 | size_t OLEDDisplay::write(uint8_t c) { 745 | if (this->logBufferSize > 0) { 746 | // Don't waste space on \r\n line endings, dropping \r 747 | if (c == 13) return 1; 748 | 749 | // convert UTF-8 character to font table index 750 | c = (this->fontTableLookupFunction)(c); 751 | // drop unknown character 752 | if (c == 0) return 1; 753 | 754 | bool maxLineNotReached = this->logBufferLine < this->logBufferMaxLines; 755 | bool bufferNotFull = this->logBufferFilled < this->logBufferSize; 756 | 757 | // Can we write to the buffer? 758 | if (bufferNotFull && maxLineNotReached) { 759 | this->logBuffer[logBufferFilled] = c; 760 | this->logBufferFilled++; 761 | // Keep track of lines written 762 | if (c == 10) this->logBufferLine++; 763 | } else { 764 | // Max line number is reached 765 | if (!maxLineNotReached) this->logBufferLine--; 766 | 767 | // Find the end of the first line 768 | uint16_t firstLineEnd = 0; 769 | for (uint16_t i=0;ilogBufferFilled;i++) { 770 | if (this->logBuffer[i] == 10){ 771 | // Include last char too 772 | firstLineEnd = i + 1; 773 | break; 774 | } 775 | } 776 | // If there was a line ending 777 | if (firstLineEnd > 0) { 778 | // Calculate the new logBufferFilled value 779 | this->logBufferFilled = logBufferFilled - firstLineEnd; 780 | // Now we move the lines infront of the buffer 781 | memcpy(this->logBuffer, &this->logBuffer[firstLineEnd], logBufferFilled); 782 | } else { 783 | // Let's reuse the buffer if it was full 784 | if (!bufferNotFull) { 785 | this->logBufferFilled = 0; 786 | }// else { 787 | // Nothing to do here 788 | //} 789 | } 790 | write(c); 791 | } 792 | } 793 | // We are always writing all uint8_t to the buffer 794 | return 1; 795 | } 796 | 797 | size_t OLEDDisplay::write(const char* str) { 798 | if (str == NULL) return 0; 799 | size_t length = strlen(str); 800 | for (size_t i = 0; i < length; i++) { 801 | write(str[i]); 802 | } 803 | return length; 804 | } 805 | 806 | // Private functions 807 | void OLEDDisplay::setGeometry(OLEDDISPLAY_GEOMETRY g) { 808 | this->geometry = g; 809 | if (g == GEOMETRY_128_64) { 810 | this->displayWidth = 128; 811 | this->displayHeight = 64; 812 | } else if (g == GEOMETRY_128_32) { 813 | this->displayWidth = 128; 814 | this->displayHeight = 32; 815 | } else if (g == GEOMETRY_64_32) { 816 | this->displayWidth = 64; 817 | this->displayHeight = 32; 818 | } 819 | this->displayBufferSize = displayWidth*displayHeight/8; 820 | } 821 | 822 | void OLEDDisplay::sendInitCommands(void) { 823 | sendCommand(DISPLAYOFF); 824 | sendCommand(SETDISPLAYCLOCKDIV); 825 | sendCommand(0xF0); // Increase speed of the display max ~96Hz 826 | sendCommand(SETMULTIPLEX); 827 | sendCommand(this->height() - 1); 828 | sendCommand(SETDISPLAYOFFSET); 829 | sendCommand(0x00); 830 | sendCommand(SETSTARTLINE); 831 | sendCommand(CHARGEPUMP); 832 | sendCommand(0x14); 833 | sendCommand(MEMORYMODE); 834 | sendCommand(0x00); 835 | sendCommand(SEGREMAP); 836 | sendCommand(COMSCANINC); 837 | sendCommand(SETCOMPINS); 838 | 839 | if ((geometry == GEOMETRY_128_64) || (geometry == GEOMETRY_64_32)) { 840 | sendCommand(0x12); 841 | } else if (geometry == GEOMETRY_128_32) { 842 | sendCommand(0x02); 843 | } 844 | 845 | sendCommand(SETCONTRAST); 846 | 847 | if ((geometry == GEOMETRY_128_64) || (geometry == GEOMETRY_64_32)) { 848 | sendCommand(0xCF); 849 | } else if (geometry == GEOMETRY_128_32) { 850 | sendCommand(0x8F); 851 | } 852 | 853 | sendCommand(SETPRECHARGE); 854 | sendCommand(0xF1); 855 | sendCommand(SETVCOMDETECT); //0xDB, (additionally needed to lower the contrast) 856 | sendCommand(0x40); //0x40 default, to lower the contrast, put 0 857 | sendCommand(DISPLAYALLON_RESUME); 858 | sendCommand(NORMALDISPLAY); 859 | sendCommand(0x2e); // stop scroll 860 | sendCommand(DISPLAYON); 861 | } 862 | 863 | void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) { 864 | if (width < 0 || height < 0) return; 865 | if (yMove + height < 0 || yMove > this->height()) return; 866 | if (xMove + width < 0 || xMove > this->width()) return; 867 | 868 | uint8_t rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0) 869 | int8_t yOffset = yMove & 7; 870 | 871 | bytesInData = bytesInData == 0 ? width * rasterHeight : bytesInData; 872 | 873 | int16_t initYMove = yMove; 874 | int8_t initYOffset = yOffset; 875 | 876 | 877 | for (uint16_t i = 0; i < bytesInData; i++) { 878 | 879 | // Reset if next horizontal drawing phase is started. 880 | if ( i % rasterHeight == 0) { 881 | yMove = initYMove; 882 | yOffset = initYOffset; 883 | } 884 | 885 | byte currentByte = pgm_read_byte(data + offset + i); 886 | 887 | int16_t xPos = xMove + (i / rasterHeight); 888 | int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * this->width(); 889 | 890 | // int16_t yScreenPos = yMove + yOffset; 891 | int16_t dataPos = xPos + yPos; 892 | 893 | if (dataPos >= 0 && dataPos < displayBufferSize && 894 | xPos >= 0 && xPos < this->width() ) { 895 | 896 | if (yOffset >= 0) { 897 | switch (this->color) { 898 | case WHITE: buffer[dataPos] |= currentByte << yOffset; break; 899 | case BLACK: buffer[dataPos] &= ~(currentByte << yOffset); break; 900 | case INVERSE: buffer[dataPos] ^= currentByte << yOffset; break; 901 | } 902 | 903 | if (dataPos < (displayBufferSize - this->width())) { 904 | switch (this->color) { 905 | case WHITE: buffer[dataPos + this->width()] |= currentByte >> (8 - yOffset); break; 906 | case BLACK: buffer[dataPos + this->width()] &= ~(currentByte >> (8 - yOffset)); break; 907 | case INVERSE: buffer[dataPos + this->width()] ^= currentByte >> (8 - yOffset); break; 908 | } 909 | } 910 | } else { 911 | // Make new offset position 912 | yOffset = -yOffset; 913 | 914 | switch (this->color) { 915 | case WHITE: buffer[dataPos] |= currentByte >> yOffset; break; 916 | case BLACK: buffer[dataPos] &= ~(currentByte >> yOffset); break; 917 | case INVERSE: buffer[dataPos] ^= currentByte >> yOffset; break; 918 | } 919 | 920 | // Prepare for next iteration by moving one block up 921 | yMove -= 8; 922 | 923 | // and setting the new yOffset 924 | yOffset = 8 - yOffset; 925 | } 926 | 927 | yield(); 928 | } 929 | } 930 | } 931 | 932 | // You need to free the char! 933 | char* OLEDDisplay::utf8ascii(String str) { 934 | uint16_t k = 0; 935 | uint16_t length = str.length() + 1; 936 | 937 | // Copy the string into a char array 938 | char* s = (char*) malloc(length * sizeof(char)); 939 | if(!s) { 940 | DEBUG_OLEDDISPLAY("[OLEDDISPLAY][utf8ascii] Can't allocate another char array. Drop support for UTF-8.\n"); 941 | return (char*) str.c_str(); 942 | } 943 | str.toCharArray(s, length); 944 | 945 | length--; 946 | 947 | for (uint16_t i=0; i < length; i++) { 948 | char c = (this->fontTableLookupFunction)(s[i]); 949 | if (c!=0) { 950 | s[k++]=c; 951 | } 952 | } 953 | 954 | s[k]=0; 955 | 956 | // This will leak 's' be sure to free it in the calling function. 957 | return s; 958 | } 959 | 960 | void OLEDDisplay::setFontTableLookupFunction(FontTableLookupFunction function) { 961 | this->fontTableLookupFunction = function; 962 | } 963 | 964 | 965 | -------------------------------------------------------------------------------- /src/oled/OLEDDisplay.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn 5 | * Copyright (c) 2018 by Fabrice Weinberg 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | * 25 | * ThingPulse invests considerable time and money to develop these open source libraries. 26 | * Please support us by buying our products (and not the clones) from 27 | * https://thingpulse.com 28 | * 29 | */ 30 | 31 | #ifndef OLEDDISPLAY_h 32 | #define OLEDDISPLAY_h 33 | 34 | #include 35 | #include "OLEDDisplayFonts.h" 36 | 37 | //#define DEBUG_OLEDDISPLAY(...) Serial.printf( __VA_ARGS__ ) 38 | 39 | #ifndef DEBUG_OLEDDISPLAY 40 | #define DEBUG_OLEDDISPLAY(...) 41 | #endif 42 | 43 | // Use DOUBLE BUFFERING by default 44 | #ifndef OLEDDISPLAY_REDUCE_MEMORY 45 | #define OLEDDISPLAY_DOUBLE_BUFFER 46 | #endif 47 | 48 | // Header Values 49 | #define JUMPTABLE_BYTES 4 50 | 51 | #define JUMPTABLE_LSB 1 52 | #define JUMPTABLE_SIZE 2 53 | #define JUMPTABLE_WIDTH 3 54 | #define JUMPTABLE_START 4 55 | 56 | #define WIDTH_POS 0 57 | #define HEIGHT_POS 1 58 | #define FIRST_CHAR_POS 2 59 | #define CHAR_NUM_POS 3 60 | 61 | 62 | // Display commands 63 | #define CHARGEPUMP 0x8D 64 | #define COLUMNADDR 0x21 65 | #define COMSCANDEC 0xC8 66 | #define COMSCANINC 0xC0 67 | #define DISPLAYALLON 0xA5 68 | #define DISPLAYALLON_RESUME 0xA4 69 | #define DISPLAYOFF 0xAE 70 | #define DISPLAYON 0xAF 71 | #define EXTERNALVCC 0x1 72 | #define INVERTDISPLAY 0xA7 73 | #define MEMORYMODE 0x20 74 | #define NORMALDISPLAY 0xA6 75 | #define PAGEADDR 0x22 76 | #define SEGREMAP 0xA0 77 | #define SETCOMPINS 0xDA 78 | #define SETCONTRAST 0x81 79 | #define SETDISPLAYCLOCKDIV 0xD5 80 | #define SETDISPLAYOFFSET 0xD3 81 | #define SETHIGHCOLUMN 0x10 82 | #define SETLOWCOLUMN 0x00 83 | #define SETMULTIPLEX 0xA8 84 | #define SETPRECHARGE 0xD9 85 | #define SETSEGMENTREMAP 0xA1 86 | #define SETSTARTLINE 0x40 87 | #define SETVCOMDETECT 0xDB 88 | #define SWITCHCAPVCC 0x2 89 | 90 | #ifndef _swap_int16_t 91 | #define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; } 92 | #endif 93 | 94 | enum OLEDDISPLAY_COLOR { 95 | BLACK = 0, 96 | WHITE = 1, 97 | INVERSE = 2 98 | }; 99 | 100 | enum OLEDDISPLAY_TEXT_ALIGNMENT { 101 | TEXT_ALIGN_LEFT = 0, 102 | TEXT_ALIGN_RIGHT = 1, 103 | TEXT_ALIGN_CENTER = 2, 104 | TEXT_ALIGN_CENTER_BOTH = 3 105 | }; 106 | 107 | 108 | enum OLEDDISPLAY_GEOMETRY { 109 | GEOMETRY_128_64 = 0, 110 | GEOMETRY_128_32 = 1, 111 | GEOMETRY_64_32 = 2 //Wireless Stick 112 | }; 113 | 114 | typedef byte (*FontTableLookupFunction)(const byte ch); 115 | 116 | 117 | class OLEDDisplay : public Print { 118 | 119 | public: 120 | virtual ~OLEDDisplay(); 121 | 122 | uint16_t width(void) const { return displayWidth; }; 123 | uint16_t height(void) const { return displayHeight; }; 124 | 125 | // Initialize the display 126 | bool init(); 127 | 128 | // Free the memory used by the display 129 | void end(); 130 | 131 | void sleep(); 132 | 133 | void wakeup(); 134 | 135 | // Cycle through the initialization 136 | void resetDisplay(uint8_t rstPin); 137 | 138 | /* Drawing functions */ 139 | // Sets the color of all pixel operations 140 | void setColor(OLEDDISPLAY_COLOR color); 141 | 142 | // Returns the current color. 143 | OLEDDISPLAY_COLOR getColor(); 144 | 145 | // Draw a pixel at given position 146 | void setPixel(int16_t x, int16_t y); 147 | 148 | // Draw a line from position 0 to position 1 149 | void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1); 150 | 151 | // Draw the border of a rectangle at the given location 152 | void drawRect(int16_t x, int16_t y, int16_t width, int16_t height); 153 | 154 | // Fill the rectangle 155 | void fillRect(int16_t x, int16_t y, int16_t width, int16_t height); 156 | 157 | // Draw the border of a circle 158 | void drawCircle(int16_t x, int16_t y, int16_t radius); 159 | 160 | // Draw all Quadrants specified in the quads bit mask 161 | void drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads); 162 | 163 | // Fill circle 164 | void fillCircle(int16_t x, int16_t y, int16_t radius); 165 | 166 | // Draw a line horizontally 167 | void drawHorizontalLine(int16_t x, int16_t y, int16_t length); 168 | 169 | // Draw a line vertically 170 | void drawVerticalLine(int16_t x, int16_t y, int16_t length); 171 | 172 | // Draws a rounded progress bar with the outer dimensions given by width and height. Progress is 173 | // a unsigned byte value between 0 and 100 174 | void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress); 175 | 176 | // Draw a bitmap in the internal image format 177 | void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image); 178 | 179 | // Draw a XBM 180 | void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *xbm); 181 | 182 | /* Text functions */ 183 | 184 | // Draws a string at the given location 185 | void drawString(int16_t x, int16_t y, String text); 186 | 187 | // Draws a String with a maximum width at the given location. 188 | // If the given String is wider than the specified width 189 | // The text will be wrapped to the next line at a space or dash 190 | void drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, String text); 191 | 192 | // Returns the width of the const char* with the current 193 | // font settings 194 | uint16_t getStringWidth(const char* text, uint16_t length); 195 | 196 | // Convencience method for the const char version 197 | uint16_t getStringWidth(String text); 198 | 199 | // Specifies relative to which anchor point 200 | // the text is rendered. Available constants: 201 | // TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH 202 | void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment); 203 | 204 | // Sets the current font. Available default fonts 205 | // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24 206 | void setFont(const uint8_t *fontData); 207 | 208 | // Set the function that will convert utf-8 to font table index 209 | void setFontTableLookupFunction(FontTableLookupFunction function); 210 | 211 | /* Display functions */ 212 | 213 | // Turn the display on 214 | void displayOn(void); 215 | 216 | // Turn the display offs 217 | void displayOff(void); 218 | 219 | // Inverted display mode 220 | void invertDisplay(void); 221 | 222 | // Normal display mode 223 | void normalDisplay(void); 224 | 225 | // Set display contrast 226 | // really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0 227 | // normal brightness & contrast: contrast = 100 228 | void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64); 229 | 230 | // Convenience method to access 231 | void setBrightness(uint8_t); 232 | 233 | // Reset display rotation or mirroring 234 | void resetOrientation(); 235 | 236 | // Turn the display upside down 237 | void flipScreenVertically(); 238 | 239 | // Mirror the display (to be used in a mirror or as a projector) 240 | void mirrorScreen(); 241 | 242 | // Write the buffer to the display memory 243 | virtual void display(void) = 0; 244 | 245 | // Clear the local pixel buffer 246 | void clear(void); 247 | 248 | // Log buffer implementation 249 | 250 | // This will define the lines and characters you can 251 | // print to the screen. When you exeed the buffer size (lines * chars) 252 | // the output may be truncated due to the size constraint. 253 | bool setLogBuffer(uint16_t lines, uint16_t chars); 254 | 255 | // Draw the log buffer at position (x, y) 256 | void drawLogBuffer(uint16_t x, uint16_t y); 257 | 258 | // Get screen geometry 259 | uint16_t getWidth(void); 260 | uint16_t getHeight(void); 261 | 262 | // Implement needed function to be compatible with Print class 263 | size_t write(uint8_t c); 264 | size_t write(const char* s); 265 | 266 | uint8_t *buffer = NULL; 267 | 268 | #ifdef OLEDDISPLAY_DOUBLE_BUFFER 269 | uint8_t *buffer_back = NULL; 270 | #endif 271 | 272 | protected: 273 | 274 | OLEDDISPLAY_GEOMETRY geometry = GEOMETRY_128_32; 275 | 276 | uint16_t displayWidth = 128; 277 | uint16_t displayHeight = 32; 278 | uint16_t displayBufferSize = 1024; 279 | 280 | // Set the correct height, width and buffer for the geometry 281 | void setGeometry(OLEDDISPLAY_GEOMETRY g); 282 | 283 | OLEDDISPLAY_TEXT_ALIGNMENT textAlignment = TEXT_ALIGN_LEFT; 284 | OLEDDISPLAY_COLOR color = WHITE; 285 | 286 | const uint8_t *fontData = ArialMT_Plain_10; 287 | 288 | // State values for logBuffer 289 | uint16_t logBufferSize = 0; 290 | uint16_t logBufferFilled = 0; 291 | uint16_t logBufferLine = 0; 292 | uint16_t logBufferMaxLines = 0; 293 | char *logBuffer = NULL; 294 | 295 | // Send a command to the display (low level function) 296 | virtual void sendCommand(uint8_t com) {(void)com;}; 297 | 298 | // Connect to the display 299 | virtual bool connect() { return false; }; 300 | 301 | // Send all the init commands 302 | void sendInitCommands(); 303 | 304 | // converts utf8 characters to extended ascii 305 | char* utf8ascii(String s); 306 | 307 | void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const uint8_t *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline)); 308 | 309 | void drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth); 310 | 311 | // UTF-8 to font table index converter 312 | // Code form http://playground.arduino.cc/Main/Utf8ascii 313 | FontTableLookupFunction fontTableLookupFunction = [](const byte ch) { 314 | static uint8_t LASTCHAR; 315 | 316 | if (ch < 128) { // Standard ASCII-set 0..0x7F handling 317 | LASTCHAR = 0; 318 | return ch; 319 | } 320 | 321 | uint8_t last = LASTCHAR; // get last char 322 | LASTCHAR = ch; 323 | 324 | switch (last) { // conversion depnding on first UTF8-character 325 | case 0xC2: return (uint8_t) ch; 326 | case 0xC3: return (uint8_t) (ch | 0xC0); 327 | case 0x82: if (ch == 0xAC) return (uint8_t) 0x80; // special case Euro-symbol 328 | } 329 | 330 | return (uint8_t) 0; // otherwise: return zero, if character has to be ignored 331 | }; 332 | }; 333 | 334 | #endif 335 | 336 | -------------------------------------------------------------------------------- /src/oled/OLEDDisplayUi.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn 5 | * Copyright (c) 2018 by Fabrice Weinberg 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | * 25 | * ThingPulse invests considerable time and money to develop these open source libraries. 26 | * Please support us by buying our products (and not the clones) from 27 | * https://thingpulse.com 28 | * 29 | */ 30 | 31 | #include "OLEDDisplayUi.h" 32 | 33 | OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) { 34 | this->display = display; 35 | } 36 | 37 | void OLEDDisplayUi::init() { 38 | this->display->init(); 39 | } 40 | 41 | void OLEDDisplayUi::setTargetFPS(uint8_t fps){ 42 | float oldInterval = this->updateInterval; 43 | this->updateInterval = ((float) 1.0 / (float) fps) * 1000; 44 | 45 | // Calculate new ticksPerFrame 46 | float changeRatio = oldInterval / (float) this->updateInterval; 47 | this->ticksPerFrame *= changeRatio; 48 | this->ticksPerTransition *= changeRatio; 49 | } 50 | 51 | // -/------ Automatic controll ------\- 52 | 53 | void OLEDDisplayUi::enableAutoTransition(){ 54 | this->autoTransition = true; 55 | } 56 | void OLEDDisplayUi::disableAutoTransition(){ 57 | this->autoTransition = false; 58 | } 59 | void OLEDDisplayUi::setAutoTransitionForwards(){ 60 | this->state.frameTransitionDirection = 1; 61 | this->lastTransitionDirection = 1; 62 | } 63 | void OLEDDisplayUi::setAutoTransitionBackwards(){ 64 | this->state.frameTransitionDirection = -1; 65 | this->lastTransitionDirection = -1; 66 | } 67 | void OLEDDisplayUi::setTimePerFrame(uint16_t time){ 68 | this->ticksPerFrame = (uint16_t) ( (float) time / (float) updateInterval); 69 | } 70 | void OLEDDisplayUi::setTimePerTransition(uint16_t time){ 71 | this->ticksPerTransition = (uint16_t) ( (float) time / (float) updateInterval); 72 | } 73 | 74 | // -/------ Customize indicator position and style -------\- 75 | void OLEDDisplayUi::enableIndicator(){ 76 | this->state.isIndicatorDrawen = true; 77 | } 78 | 79 | void OLEDDisplayUi::disableIndicator(){ 80 | this->state.isIndicatorDrawen = false; 81 | } 82 | 83 | void OLEDDisplayUi::enableAllIndicators(){ 84 | this->shouldDrawIndicators = true; 85 | } 86 | 87 | void OLEDDisplayUi::disableAllIndicators(){ 88 | this->shouldDrawIndicators = false; 89 | } 90 | 91 | void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) { 92 | this->indicatorPosition = pos; 93 | } 94 | void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) { 95 | this->indicatorDirection = dir; 96 | } 97 | void OLEDDisplayUi::setActiveSymbol(const uint8_t* symbol) { 98 | this->activeSymbol = symbol; 99 | } 100 | void OLEDDisplayUi::setInactiveSymbol(const uint8_t* symbol) { 101 | this->inactiveSymbol = symbol; 102 | } 103 | 104 | 105 | // -/----- Frame settings -----\- 106 | void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) { 107 | this->frameAnimationDirection = dir; 108 | } 109 | void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) { 110 | this->frameFunctions = frameFunctions; 111 | this->frameCount = frameCount; 112 | this->resetState(); 113 | } 114 | 115 | // -/----- Overlays ------\- 116 | void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){ 117 | this->overlayFunctions = overlayFunctions; 118 | this->overlayCount = overlayCount; 119 | } 120 | 121 | // -/----- Loading Process -----\- 122 | 123 | void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) { 124 | this->loadingDrawFunction = loadingDrawFunction; 125 | } 126 | 127 | void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) { 128 | uint8_t progress = 0; 129 | uint8_t increment = 100 / stagesCount; 130 | 131 | for (uint8_t i = 0; i < stagesCount; i++) { 132 | display->clear(); 133 | this->loadingDrawFunction(this->display, &stages[i], progress); 134 | display->display(); 135 | 136 | stages[i].callback(); 137 | 138 | progress += increment; 139 | yield(); 140 | } 141 | 142 | display->clear(); 143 | this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress); 144 | display->display(); 145 | 146 | delay(150); 147 | } 148 | 149 | // -/----- Manuel control -----\- 150 | void OLEDDisplayUi::nextFrame() { 151 | if (this->state.frameState != IN_TRANSITION) { 152 | this->state.manuelControll = true; 153 | this->state.frameState = IN_TRANSITION; 154 | this->state.ticksSinceLastStateSwitch = 0; 155 | this->lastTransitionDirection = this->state.frameTransitionDirection; 156 | this->state.frameTransitionDirection = 1; 157 | } 158 | } 159 | void OLEDDisplayUi::previousFrame() { 160 | if (this->state.frameState != IN_TRANSITION) { 161 | this->state.manuelControll = true; 162 | this->state.frameState = IN_TRANSITION; 163 | this->state.ticksSinceLastStateSwitch = 0; 164 | this->lastTransitionDirection = this->state.frameTransitionDirection; 165 | this->state.frameTransitionDirection = -1; 166 | } 167 | } 168 | 169 | void OLEDDisplayUi::switchToFrame(uint8_t frame) { 170 | if (frame >= this->frameCount) return; 171 | this->state.ticksSinceLastStateSwitch = 0; 172 | if (frame == this->state.currentFrame) return; 173 | this->state.frameState = FIXED; 174 | this->state.currentFrame = frame; 175 | this->state.isIndicatorDrawen = true; 176 | } 177 | 178 | void OLEDDisplayUi::transitionToFrame(uint8_t frame) { 179 | if (frame >= this->frameCount) return; 180 | this->state.ticksSinceLastStateSwitch = 0; 181 | if (frame == this->state.currentFrame) return; 182 | this->nextFrameNumber = frame; 183 | this->lastTransitionDirection = this->state.frameTransitionDirection; 184 | this->state.manuelControll = true; 185 | this->state.frameState = IN_TRANSITION; 186 | this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1; 187 | } 188 | 189 | 190 | // -/----- State information -----\- 191 | OLEDDisplayUiState* OLEDDisplayUi::getUiState(){ 192 | return &this->state; 193 | } 194 | 195 | 196 | int8_t OLEDDisplayUi::update(){ 197 | unsigned long frameStart = millis(); 198 | int8_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate); 199 | if ( timeBudget <= 0) { 200 | // Implement frame skipping to ensure time budget is keept 201 | if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil(-timeBudget / this->updateInterval); 202 | 203 | this->state.lastUpdate = frameStart; 204 | this->tick(); 205 | } 206 | return this->updateInterval - (millis() - frameStart); 207 | } 208 | 209 | 210 | void OLEDDisplayUi::tick() { 211 | this->state.ticksSinceLastStateSwitch++; 212 | 213 | switch (this->state.frameState) { 214 | case IN_TRANSITION: 215 | if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){ 216 | this->state.frameState = FIXED; 217 | this->state.currentFrame = getNextFrameNumber(); 218 | this->state.ticksSinceLastStateSwitch = 0; 219 | this->nextFrameNumber = -1; 220 | } 221 | break; 222 | case FIXED: 223 | // Revert manuelControll 224 | if (this->state.manuelControll) { 225 | this->state.frameTransitionDirection = this->lastTransitionDirection; 226 | this->state.manuelControll = false; 227 | } 228 | if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){ 229 | if (this->autoTransition){ 230 | this->state.frameState = IN_TRANSITION; 231 | } 232 | this->state.ticksSinceLastStateSwitch = 0; 233 | } 234 | break; 235 | } 236 | 237 | this->display->clear(); 238 | this->drawFrame(); 239 | if (shouldDrawIndicators) { 240 | this->drawIndicator(); 241 | } 242 | this->drawOverlays(); 243 | this->display->display(); 244 | } 245 | 246 | void OLEDDisplayUi::resetState() { 247 | this->state.lastUpdate = 0; 248 | this->state.ticksSinceLastStateSwitch = 0; 249 | this->state.frameState = FIXED; 250 | this->state.currentFrame = 0; 251 | this->state.isIndicatorDrawen = true; 252 | } 253 | 254 | void OLEDDisplayUi::drawFrame(){ 255 | switch (this->state.frameState){ 256 | case IN_TRANSITION: { 257 | float progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition; 258 | int16_t x = 0, y = 0, x1 = 0, y1 = 0; 259 | switch(this->frameAnimationDirection){ 260 | case SLIDE_LEFT: 261 | x = -this->display->width() * progress; 262 | y = 0; 263 | x1 = x + this->display->width(); 264 | y1 = 0; 265 | break; 266 | case SLIDE_RIGHT: 267 | x = this->display->width() * progress; 268 | y = 0; 269 | x1 = x - this->display->width(); 270 | y1 = 0; 271 | break; 272 | case SLIDE_UP: 273 | x = 0; 274 | y = -this->display->height() * progress; 275 | x1 = 0; 276 | y1 = y + this->display->height(); 277 | break; 278 | case SLIDE_DOWN: 279 | default: 280 | x = 0; 281 | y = this->display->height() * progress; 282 | x1 = 0; 283 | y1 = y - this->display->height(); 284 | break; 285 | } 286 | 287 | // Invert animation if direction is reversed. 288 | int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1; 289 | x *= dir; y *= dir; x1 *= dir; y1 *= dir; 290 | 291 | bool drawenCurrentFrame; 292 | 293 | 294 | // Prope each frameFunction for the indicator Drawen state 295 | this->enableIndicator(); 296 | (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y); 297 | drawenCurrentFrame = this->state.isIndicatorDrawen; 298 | 299 | this->enableIndicator(); 300 | (this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1); 301 | 302 | // Build up the indicatorDrawState 303 | if (drawenCurrentFrame && !this->state.isIndicatorDrawen) { 304 | // Drawen now but not next 305 | this->indicatorDrawState = 2; 306 | } else if (!drawenCurrentFrame && this->state.isIndicatorDrawen) { 307 | // Not drawen now but next 308 | this->indicatorDrawState = 1; 309 | } else if (!drawenCurrentFrame && !this->state.isIndicatorDrawen) { 310 | // Not drawen in both frames 311 | this->indicatorDrawState = 3; 312 | } 313 | 314 | // If the indicator isn't draw in the current frame 315 | // reflect it in state.isIndicatorDrawen 316 | if (!drawenCurrentFrame) this->state.isIndicatorDrawen = false; 317 | 318 | break; 319 | } 320 | case FIXED: 321 | // Always assume that the indicator is drawn! 322 | // And set indicatorDrawState to "not known yet" 323 | this->indicatorDrawState = 0; 324 | this->enableIndicator(); 325 | (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0); 326 | break; 327 | } 328 | } 329 | 330 | void OLEDDisplayUi::drawIndicator() { 331 | 332 | // Only draw if the indicator is invisible 333 | // for both frames or 334 | // the indiactor is shown and we are IN_TRANSITION 335 | if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawen && this->state.frameState != IN_TRANSITION)) { 336 | return; 337 | } 338 | 339 | uint8_t posOfHighlightFrame = 0; 340 | float indicatorFadeProgress = 0; 341 | 342 | // if the indicator needs to be slided in we want to 343 | // highlight the next frame in the transition 344 | uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame; 345 | 346 | // Calculate the frame that needs to be highlighted 347 | // based on the Direction the indiactor is drawn 348 | switch (this->indicatorDirection){ 349 | case LEFT_RIGHT: 350 | posOfHighlightFrame = frameToHighlight; 351 | break; 352 | case RIGHT_LEFT: 353 | default: 354 | posOfHighlightFrame = this->frameCount - frameToHighlight; 355 | break; 356 | } 357 | 358 | switch (this->indicatorDrawState) { 359 | case 1: // Indicator was not drawn in this frame but will be in next 360 | // Slide IN 361 | indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); 362 | break; 363 | case 2: // Indicator was drawn in this frame but not in next 364 | // Slide OUT 365 | indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition); 366 | break; 367 | } 368 | 369 | //Space between indicators - reduce for small screen sizes 370 | uint16_t indicatorSpacing = 12; 371 | if (this->display->getHeight() < 64 && (this->indicatorPosition == RIGHT || this->indicatorPosition == LEFT)) { 372 | indicatorSpacing = 6; 373 | } 374 | 375 | uint16_t frameStartPos = (indicatorSpacing * frameCount / 2); 376 | const uint8_t *image; 377 | 378 | uint16_t x = 0,y = 0; 379 | 380 | 381 | for (byte i = 0; i < this->frameCount; i++) { 382 | 383 | switch (this->indicatorPosition){ 384 | case TOP: 385 | y = 0 - (8 * indicatorFadeProgress); 386 | x = (this->display->width() / 2) - frameStartPos + 12 * i; 387 | break; 388 | case BOTTOM: 389 | y = (this->display->height() - 8) + (8 * indicatorFadeProgress); 390 | x = (this->display->width() / 2) - frameStartPos + 12 * i; 391 | break; 392 | case RIGHT: 393 | x = (this->display->width() - 8) + (8 * indicatorFadeProgress); 394 | y = (this->display->height() / 2) - frameStartPos + 2 + 12 * i; 395 | break; 396 | case LEFT: 397 | default: 398 | x = 0 - (8 * indicatorFadeProgress); 399 | y = (this->display->height() / 2) - frameStartPos + 2 + indicatorSpacing * i; 400 | break; 401 | } 402 | 403 | if (posOfHighlightFrame == i) { 404 | image = this->activeSymbol; 405 | } else { 406 | image = this->inactiveSymbol; 407 | } 408 | 409 | this->display->drawFastImage(x, y, 8, 8, image); 410 | } 411 | } 412 | 413 | void OLEDDisplayUi::drawOverlays() { 414 | for (uint8_t i=0;ioverlayCount;i++){ 415 | (this->overlayFunctions[i])(this->display, &this->state); 416 | } 417 | } 418 | 419 | uint8_t OLEDDisplayUi::getNextFrameNumber(){ 420 | if (this->nextFrameNumber != -1) return this->nextFrameNumber; 421 | return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount; 422 | } 423 | -------------------------------------------------------------------------------- /src/oled/OLEDDisplayUi.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn 5 | * Copyright (c) 2018 by Fabrice Weinberg 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | * 25 | * ThingPulse invests considerable time and money to develop these open source libraries. 26 | * Please support us by buying our products (and not the clones) from 27 | * https://thingpulse.com 28 | * 29 | */ 30 | 31 | #ifndef OLEDDISPLAYUI_h 32 | #define OLEDDISPLAYUI_h 33 | 34 | #include 35 | #include "OLEDDisplay.h" 36 | 37 | //#define DEBUG_OLEDDISPLAYUI(...) Serial.printf( __VA_ARGS__ ) 38 | 39 | #ifndef DEBUG_OLEDDISPLAYUI 40 | #define DEBUG_OLEDDISPLAYUI(...) 41 | #endif 42 | 43 | enum AnimationDirection { 44 | SLIDE_UP, 45 | SLIDE_DOWN, 46 | SLIDE_LEFT, 47 | SLIDE_RIGHT 48 | }; 49 | 50 | enum IndicatorPosition { 51 | TOP, 52 | RIGHT, 53 | BOTTOM, 54 | LEFT 55 | }; 56 | 57 | enum IndicatorDirection { 58 | LEFT_RIGHT, 59 | RIGHT_LEFT 60 | }; 61 | 62 | enum FrameState { 63 | IN_TRANSITION, 64 | FIXED 65 | }; 66 | 67 | 68 | const uint8_t ANIMATION_activeSymbol[] PROGMEM = { 69 | 0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00 70 | }; 71 | 72 | const uint8_t ANIMATION_inactiveSymbol[] PROGMEM = { 73 | 0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00 74 | }; 75 | 76 | 77 | // Structure of the UiState 78 | struct OLEDDisplayUiState { 79 | uint64_t lastUpdate = 0; 80 | uint16_t ticksSinceLastStateSwitch = 0; 81 | 82 | FrameState frameState = FIXED; 83 | uint8_t currentFrame = 0; 84 | 85 | bool isIndicatorDrawen = true; 86 | 87 | // Normal = 1, Inverse = -1; 88 | int8_t frameTransitionDirection = 1; 89 | 90 | bool manuelControll = false; 91 | 92 | // Custom data that can be used by the user 93 | void* userData = NULL; 94 | }; 95 | 96 | struct LoadingStage { 97 | const char* process; 98 | void (*callback)(); 99 | }; 100 | 101 | typedef void (*FrameCallback)(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y); 102 | typedef void (*OverlayCallback)(OLEDDisplay *display, OLEDDisplayUiState* state); 103 | typedef void (*LoadingDrawFunction)(OLEDDisplay *display, LoadingStage* stage, uint8_t progress); 104 | 105 | class OLEDDisplayUi { 106 | private: 107 | OLEDDisplay *display; 108 | 109 | // Symbols for the Indicator 110 | IndicatorPosition indicatorPosition = BOTTOM; 111 | IndicatorDirection indicatorDirection = LEFT_RIGHT; 112 | 113 | const uint8_t* activeSymbol = ANIMATION_activeSymbol; 114 | const uint8_t* inactiveSymbol = ANIMATION_inactiveSymbol; 115 | 116 | bool shouldDrawIndicators = true; 117 | 118 | // Values for the Frames 119 | AnimationDirection frameAnimationDirection = SLIDE_RIGHT; 120 | 121 | int8_t lastTransitionDirection = 1; 122 | 123 | uint16_t ticksPerFrame = 151; // ~ 5000ms at 30 FPS 124 | uint16_t ticksPerTransition = 15; // ~ 500ms at 30 FPS 125 | 126 | bool autoTransition = true; 127 | 128 | FrameCallback* frameFunctions; 129 | uint8_t frameCount = 0; 130 | 131 | // Internally used to transition to a specific frame 132 | int8_t nextFrameNumber = -1; 133 | 134 | // Values for Overlays 135 | OverlayCallback* overlayFunctions; 136 | uint8_t overlayCount = 0; 137 | 138 | // Will the Indicator be drawen 139 | // 3 Not drawn in both frames 140 | // 2 Drawn this frame but not next 141 | // 1 Not drown this frame but next 142 | // 0 Not known yet 143 | uint8_t indicatorDrawState = 1; 144 | 145 | // Loading screen 146 | LoadingDrawFunction loadingDrawFunction = [](OLEDDisplay *display, LoadingStage* stage, uint8_t progress) { 147 | display->setTextAlignment(TEXT_ALIGN_CENTER); 148 | display->setFont(ArialMT_Plain_10); 149 | display->drawString(64, 18, stage->process); 150 | display->drawProgressBar(4, 32, 120, 8, progress); 151 | }; 152 | 153 | // UI State 154 | OLEDDisplayUiState state; 155 | 156 | // Bookeeping for update 157 | uint8_t updateInterval = 33; 158 | 159 | uint8_t getNextFrameNumber(); 160 | void drawIndicator(); 161 | void drawFrame(); 162 | void drawOverlays(); 163 | void tick(); 164 | void resetState(); 165 | 166 | public: 167 | 168 | OLEDDisplayUi(OLEDDisplay *display); 169 | 170 | /** 171 | * Initialise the display 172 | */ 173 | void init(); 174 | 175 | /** 176 | * Configure the internal used target FPS 177 | */ 178 | void setTargetFPS(uint8_t fps); 179 | 180 | // Automatic Controll 181 | /** 182 | * Enable automatic transition to next frame after the some time can be configured with `setTimePerFrame` and `setTimePerTransition`. 183 | */ 184 | void enableAutoTransition(); 185 | 186 | /** 187 | * Disable automatic transition to next frame. 188 | */ 189 | void disableAutoTransition(); 190 | 191 | /** 192 | * Set the direction if the automatic transitioning 193 | */ 194 | void setAutoTransitionForwards(); 195 | void setAutoTransitionBackwards(); 196 | 197 | /** 198 | * Set the approx. time a frame is displayed 199 | */ 200 | void setTimePerFrame(uint16_t time); 201 | 202 | /** 203 | * Set the approx. time a transition will take 204 | */ 205 | void setTimePerTransition(uint16_t time); 206 | 207 | // Customize indicator position and style 208 | 209 | /** 210 | * Draw the indicator. 211 | * This is the defaut state for all frames if 212 | * the indicator was hidden on the previous frame 213 | * it will be slided in. 214 | */ 215 | void enableIndicator(); 216 | 217 | /** 218 | * Don't draw the indicator. 219 | * This will slide out the indicator 220 | * when transitioning to the next frame. 221 | */ 222 | void disableIndicator(); 223 | 224 | /** 225 | * Enable drawing of indicators 226 | */ 227 | void enableAllIndicators(); 228 | 229 | /** 230 | * Disable draw of indicators. 231 | */ 232 | void disableAllIndicators(); 233 | 234 | /** 235 | * Set the position of the indicator bar. 236 | */ 237 | void setIndicatorPosition(IndicatorPosition pos); 238 | 239 | /** 240 | * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING 241 | */ 242 | void setIndicatorDirection(IndicatorDirection dir); 243 | 244 | /** 245 | * Set the symbol to indicate an active frame in the indicator bar. 246 | */ 247 | void setActiveSymbol(const uint8_t* symbol); 248 | 249 | /** 250 | * Set the symbol to indicate an inactive frame in the indicator bar. 251 | */ 252 | void setInactiveSymbol(const uint8_t* symbol); 253 | 254 | 255 | // Frame settings 256 | 257 | /** 258 | * Configure what animation is used to transition from one frame to another 259 | */ 260 | void setFrameAnimation(AnimationDirection dir); 261 | 262 | /** 263 | * Add frame drawing functions 264 | */ 265 | void setFrames(FrameCallback* frameFunctions, uint8_t frameCount); 266 | 267 | // Overlay 268 | 269 | /** 270 | * Add overlays drawing functions that are draw independent of the Frames 271 | */ 272 | void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount); 273 | 274 | 275 | // Loading animation 276 | /** 277 | * Set the function that will draw each step 278 | * in the loading animation 279 | */ 280 | void setLoadingDrawFunction(LoadingDrawFunction loadingFunction); 281 | 282 | 283 | /** 284 | * Run the loading process 285 | */ 286 | void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount); 287 | 288 | 289 | // Manual Control 290 | void nextFrame(); 291 | void previousFrame(); 292 | 293 | /** 294 | * Switch without transition to frame `frame`. 295 | */ 296 | void switchToFrame(uint8_t frame); 297 | 298 | /** 299 | * Transition to frame `frame`, when the `frame` number is bigger than the current 300 | * frame the forward animation will be used, otherwise the backwards animation is used. 301 | */ 302 | void transitionToFrame(uint8_t frame); 303 | 304 | // State Info 305 | OLEDDisplayUiState* getUiState(); 306 | 307 | int8_t update(); 308 | }; 309 | #endif 310 | -------------------------------------------------------------------------------- /src/oled/SSD1306.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn 5 | * Copyright (c) 2018 by Fabrice Weinberg 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | * 25 | * ThingPulse invests considerable time and money to develop these open source libraries. 26 | * Please support us by buying our products (and not the clones) from 27 | * https://thingpulse.com 28 | * 29 | */ 30 | 31 | #ifndef SSD1306_h 32 | #define SSD1306_h 33 | #include "SSD1306Wire.h" 34 | 35 | // For legacy support make SSD1306 an alias for SSD1306 36 | typedef SSD1306Wire SSD1306; 37 | 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/oled/SSD1306Wire.h: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn 5 | * Copyright (c) 2018 by Fabrice Weinberg 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | * 25 | * ThingPulse invests considerable time and money to develop these open source libraries. 26 | * Please support us by buying our products (and not the clones) from 27 | * https://thingpulse.com 28 | * 29 | */ 30 | 31 | #ifndef SSD1306Wire_h 32 | #define SSD1306Wire_h 33 | 34 | #include "OLEDDisplay.h" 35 | #include 36 | 37 | 38 | class SSD1306Wire : public OLEDDisplay { 39 | private: 40 | uint8_t _address; 41 | uint8_t _sda; 42 | uint8_t _scl; 43 | uint8_t _rst; 44 | bool _doI2cAutoInit = false; 45 | 46 | public: 47 | SSD1306Wire(uint8_t _address, uint8_t _sda, uint8_t _scl, uint8_t _rst, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_32) { 48 | setGeometry(g); 49 | 50 | this->_address = _address; 51 | this->_sda = _sda; 52 | this->_scl = _scl; 53 | this->_rst = _rst; 54 | } 55 | 56 | 57 | bool connect() { 58 | pinMode(_rst,OUTPUT); 59 | digitalWrite(_rst, LOW); 60 | delay(50); 61 | digitalWrite(_rst, HIGH); 62 | 63 | Wire.begin(this->_sda, this->_scl); 64 | // Let's use ~700khz if ESP8266 is in 160Mhz mode 65 | // this will be limited to ~400khz if the ESP8266 in 80Mhz mode. 66 | Wire.setClock(700000); 67 | return true; 68 | } 69 | 70 | void display(void) { 71 | initI2cIfNeccesary(); 72 | const int x_offset = (128 - this->width()) / 2; 73 | #ifdef OLEDDISPLAY_DOUBLE_BUFFER 74 | uint8_t minBoundY = UINT8_MAX; 75 | uint8_t maxBoundY = 0; 76 | 77 | uint8_t minBoundX = UINT8_MAX; 78 | uint8_t maxBoundX = 0; 79 | uint8_t x, y; 80 | 81 | // Calculate the Y bounding box of changes 82 | // and copy buffer[pos] to buffer_back[pos]; 83 | for (y = 0; y < (this->height() / 8); y++) { 84 | for (x = 0; x < this->width(); x++) { 85 | uint16_t pos = x + y * this->width(); 86 | if (buffer[pos] != buffer_back[pos]) { 87 | minBoundY = _min(minBoundY, y); 88 | maxBoundY = _max(maxBoundY, y); 89 | minBoundX = _min(minBoundX, x); 90 | maxBoundX = _max(maxBoundX, x); 91 | } 92 | buffer_back[pos] = buffer[pos]; 93 | } 94 | yield(); 95 | } 96 | 97 | // If the minBoundY wasn't updated 98 | // we can savely assume that buffer_back[pos] == buffer[pos] 99 | // holdes true for all values of pos 100 | 101 | if (minBoundY == UINT8_MAX) return; 102 | 103 | sendCommand(COLUMNADDR); 104 | sendCommand(x_offset + minBoundX); 105 | sendCommand(x_offset + maxBoundX); 106 | 107 | sendCommand(PAGEADDR); 108 | sendCommand(minBoundY); 109 | sendCommand(maxBoundY); 110 | 111 | byte k = 0; 112 | for (y = minBoundY; y <= maxBoundY; y++) { 113 | for (x = minBoundX; x <= maxBoundX; x++) { 114 | if (k == 0) { 115 | Wire.beginTransmission(_address); 116 | Wire.write(0x40); 117 | } 118 | 119 | Wire.write(buffer[x + y * this->width()]); 120 | k++; 121 | if (k == 16) { 122 | Wire.endTransmission(); 123 | k = 0; 124 | } 125 | } 126 | yield(); 127 | } 128 | 129 | if (k != 0) { 130 | Wire.endTransmission(); 131 | } 132 | #else 133 | 134 | sendCommand(COLUMNADDR); 135 | sendCommand(x_offset); 136 | sendCommand(x_offset + (this->width() - 1)); 137 | 138 | sendCommand(PAGEADDR); 139 | sendCommand(0x0); 140 | sendCommand((this->height() / 8) - 1); 141 | 142 | if (geometry == GEOMETRY_128_64) { 143 | sendCommand(0x7); 144 | } else if (geometry == GEOMETRY_128_32) { 145 | sendCommand(0x3); 146 | } 147 | 148 | for (uint16_t i=0; i < displayBufferSize; i++) { 149 | Wire.beginTransmission(this->_address); 150 | Wire.write(0x40); 151 | for (uint8_t x = 0; x < 16; x++) { 152 | Wire.write(buffer[i]); 153 | i++; 154 | } 155 | i--; 156 | Wire.endTransmission(); 157 | } 158 | #endif 159 | } 160 | 161 | void setI2cAutoInit(bool doI2cAutoInit) { 162 | _doI2cAutoInit = doI2cAutoInit; 163 | } 164 | 165 | private: 166 | inline void sendCommand(uint8_t command) __attribute__((always_inline)){ 167 | initI2cIfNeccesary(); 168 | Wire.beginTransmission(_address); 169 | Wire.write(0x80); 170 | Wire.write(command); 171 | Wire.endTransmission(); 172 | } 173 | 174 | void initI2cIfNeccesary() { 175 | if (_doI2cAutoInit) { 176 | Wire.begin(this->_sda, this->_scl); 177 | } 178 | } 179 | 180 | }; 181 | 182 | //SSD1306Wire display; 183 | 184 | #endif 185 | --------------------------------------------------------------------------------