├── .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 | 
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 | 
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 |
--------------------------------------------------------------------------------