Prvni odevzdani

This commit is contained in:
Pavel Brychta 2020-10-21 09:24:36 +02:00
parent b4ecc8a817
commit 894973a9e7
38 changed files with 7129 additions and 2 deletions

406
README.md
View File

@ -1,3 +1,405 @@
# OLED_SSD # Knihovna OLED_SSD (ESP8266/ESP32/SAMD21)
Arduino knihovna pro obsluhu malých OLED displejů Jedná se o modifikovanou knihovnu pro obsluhu malých OLED displejů, připojených přes I2C, nebo SPI rozhraní. Modifikace spočívají zejména v doplnění programového kódu, umožňujícího dithering (šrafování).
Následuje mírně upravený obsah originálního README.md od původních autorů
# ThingPulse OLED SSD1306 (ESP8266/ESP32)
This is a driver for SSD1306 128x64 and 128x32 OLED displays running on the Arduino/ESP8266 & ESP32 <del>and mbed-os</del> platforms.
Can be used with either the I2C or SPI version of the display.
You can either download this library as a zip file and unpack it to your Arduino/libraries folder or find it in the Arduino library manager under "ESP8266 and ESP32 Oled Driver for SSD1306 display". For mbed-os a copy of the files are available as an mbed-os library.
## Credits
This library has initially been written by Daniel Eichhorn ([@squix78](https://github.com/squix78)). Many thanks go to Fabrice Weinberg ([@FWeinb](https://github.com/FWeinb)) for optimizing and refactoring many aspects of the library. Also many thanks to the many committers who helped to add new features and who fixed many bugs. Mbed-OS support and other improvements were contributed by Helmut Tschemernjak ([@helmut64](https://github.com/helmut64)).
The init sequence for the SSD1306 was inspired by Adafruit's library for the same display.
## Usage
Check out the examples folder for a few comprehensive demonstrations how to use the library. Also check out the [ESP8266 Weather Station](https://github.com/ThingPulse/esp8266-weather-station) library which uses the OLED library to display beautiful weather information.
## Features
* Draw pixels at given coordinates
* Draw lines from given coordinates to given coordinates
* Draw or fill a rectangle with given dimensions
* Draw Text at given coordinates:
* Define Alignment: Left, Right and Center
* Set the Fontface you want to use (see section Fonts below)
* Limit the width of the text by an amount of pixels. Before this widths will be reached, the renderer will wrap the text to a new line if possible
* Display content in automatically side scrolling carousel
* Define transition cycles
* Define how long one frame will be displayed
* Draw the different frames in callback methods
* One indicator per frame will be automatically displayed. The active frame will be displayed from inactive once
## Fonts
Fonts are defined in a proprietary but open format. You can create new font files by choosing from a given list
of open sourced Fonts from this web app: http://oleddisplay.squix.ch
Choose the font family, style and size, check the preview image and if you like what you see click the "Create" button. This will create the font array in a text area form where you can copy and paste it into a new or existing header file.
![FontTool](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/FontTool.png)
## Hardware Abstraction
The library supports different protocols to access the OLED display. Currently there is support for I2C using the built in Wire.h library, I2C by using the much faster [BRZO I2C library](https://github.com/pasko-zh/brzo_i2c) written in assembler and it also supports displays which come with the SPI interface.
### I2C with Wire.h
```C++
#include <Wire.h>
#include "SSD1306Wire.h"
// for 128x64 displays:
SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
// for 128x32 displays:
// SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32); // ADDRESS, SDA, SCL, GEOMETRY_128_32 (or 128_64)
// for using 2nd Hardware I2C (if available)
// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_TWO); //default value is I2C_ONE if not mentioned
// By default SD1306Wire set I2C frequency to 700000, you can use set either another frequency or skip setting the frequency by providing -1 value
// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, 400000); //set I2C frequency to 400kHz
// SSD1306Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, -1); //skip setting the I2C bus frequency
```
for a SH1106:
```C++
#include <Wire.h>
#include "SH1106Wire.h"
SH1106Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
// By default SH1106Wire set I2C frequency to 700000, you can use set either another frequency or skip setting the frequency by providing -1 value
// SH1106Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, 400000); //set I2C frequency to 400kHz
// SH1106Wire(0x3c, SDA, SCL, GEOMETRY_128_64, I2C_ONE, -1); //skip setting the I2C bus frequency
```
### I2C with brzo_i2c
```C++
#include <brzo_i2c.h>
#include "SSD1306Brzo.h"
SSD1306Brzo display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
```
or for the SH1106:
```C++
#include <brzo_i2c.h>
#include "SH1106Brzo.h"
SH1106Brzo display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
```
### SPI
```C++
#include <SPI.h>
#include "SSD1306Spi.h"
SSD1306Spi display(D0, D2, D8); // RES, DC, CS
```
or for the SH1106:
```C++
#include <SPI.h>
#include "SH1106Spi.h"
SH1106Spi display(D0, D2); // RES, DC
```
## API
### Display Control
```C++
// Initialize the display
void init();
// Free the memory used by the display
void end();
// Cycle through the initialization
void resetDisplay(void);
// Connect again to the display through I2C
void reconnect(void);
// Turn the display on
void displayOn(void);
// Turn the display offs
void displayOff(void);
// Clear the local pixel buffer
void clear(void);
// Write the buffer to the display memory
void display(void);
// Inverted display mode
void invertDisplay(void);
// Normal display mode
void normalDisplay(void);
// Set display contrast
// really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0
// normal brightness & contrast: contrast = 100
void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64);
// Convenience method to access
void setBrightness(uint8_t);
// Turn the display upside down
void flipScreenVertically();
// Draw the screen mirrored
void mirrorScreen();
```
## Pixel drawing
```C++
/* Drawing functions */
// Sets the color of all pixel operations
// color : BLACK, WHITE, INVERSE
void setColor(OLEDDISPLAY_COLOR color);
// Draw a pixel at given position
void setPixel(int16_t x, int16_t y);
// Draw a line from position 0 to position 1
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1);
// Draw the border of a rectangle at the given location
void drawRect(int16_t x, int16_t y, int16_t width, int16_t height);
// Fill the rectangle
void fillRect(int16_t x, int16_t y, int16_t width, int16_t height);
// Draw the border of a circle
void drawCircle(int16_t x, int16_t y, int16_t radius);
// Fill circle
void fillCircle(int16_t x, int16_t y, int16_t radius);
// Draw a line horizontally
void drawHorizontalLine(int16_t x, int16_t y, int16_t length);
// Draw a lin vertically
void drawVerticalLine(int16_t x, int16_t y, int16_t length);
// Draws a rounded progress bar with the outer dimensions given by width and height. Progress is
// a unsigned byte value between 0 and 100
void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress);
// Draw a bitmap in the internal image format
void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image);
// Draw a XBM
void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const char* xbm);
```
## Text operations
``` C++
void drawString(int16_t x, int16_t y, String text);
// Draws a String with a maximum width at the given location.
// If the given String is wider than the specified width
// The text will be wrapped to the next line at a space or dash
void drawStringMaxWidth(int16_t x, int16_t y, int16_t maxLineWidth, String text);
// Returns the width of the const char* with the current
// font settings
uint16_t getStringWidth(const char* text, uint16_t length);
// Convencience method for the const char version
uint16_t getStringWidth(String text);
// Specifies relative to which anchor point
// the text is rendered. Available constants:
// TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH
void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment);
// Sets the current font. Available default fonts
// ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24
// Or create one with the font tool at http://oleddisplay.squix.ch
void setFont(const uint8_t* fontData);
```
## Ui Library (OLEDDisplayUi)
The Ui Library is used to provide a basic set of Ui elements called, `Frames` and `Overlays`. A `Frame` is used to provide
information the default behaviour is to display a `Frame` for a defined time and than move to the next. The library also provides an `Indicator` that will be updated accordingly. An `Overlay` on the other hand is a pieces of information (e.g. a clock) that is displayed always at the same position.
```C++
/**
* Initialise the display
*/
void init();
/**
* Configure the internal used target FPS
*/
void setTargetFPS(uint8_t fps);
/**
* Enable automatic transition to next frame after the some time can be configured with
* `setTimePerFrame` and `setTimePerTransition`.
*/
void enableAutoTransition();
/**
* Disable automatic transition to next frame.
*/
void disableAutoTransition();
/**
* Set the direction if the automatic transitioning
*/
void setAutoTransitionForwards();
void setAutoTransitionBackwards();
/**
* Set the approx. time a frame is displayed
*/
void setTimePerFrame(uint16_t time);
/**
* Set the approx. time a transition will take
*/
void setTimePerTransition(uint16_t time);
/**
* Draw the indicator.
* This is the default state for all frames if
* the indicator was hidden on the previous frame
* it will be slided in.
*/
void enableIndicator();
/**
* Don't draw the indicator.
* This will slide out the indicator
* when transitioning to the next frame.
*/
void disableIndicator();
/**
* Enable drawing of all indicators.
*/
void enableAllIndicators();
/**
* Disable drawing of all indicators.
*/
void disableAllIndicators();
/**
* Set the position of the indicator bar.
*/
void setIndicatorPosition(IndicatorPosition pos);
/**
* Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING
*/
void setIndicatorDirection(IndicatorDirection dir);
/**
* Set the symbol to indicate an active frame in the indicator bar.
*/
void setActiveSymbol(const char* symbol);
/**
* Set the symbol to indicate an inactive frame in the indicator bar.
*/
void setInactiveSymbol(const char* symbol);
/**
* Configure what animation is used to transition from one frame to another
*/
void setFrameAnimation(AnimationDirection dir);
/**
* Add frame drawing functions
*/
void setFrames(FrameCallback* frameFunctions, uint8_t frameCount);
/**
* Add overlays drawing functions that are draw independent of the Frames
*/
void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount);
/**
* Set the function that will draw each step
* in the loading animation
*/
void setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction);
/**
* Run the loading process
*/
void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount);
// Manuell Controll
void nextFrame();
void previousFrame();
/**
* Switch without transition to frame `frame`.
*/
void switchToFrame(uint8_t frame);
/**
* Transition to frame `frame`, when the `frame` number is bigger than the current
* frame the forward animation will be used, otherwise the backwards animation is used.
*/
void transitionToFrame(uint8_t frame);
// State Info
OLEDDisplayUiState* getUiState();
// This needs to be called in the main loop
// the returned value is the remaining time (in ms)
// you have to draw after drawing to keep the frame budget.
int8_t update();
```
## Example: SSD1306Demo
### Frame 1
![DemoFrame1](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame1.jpg)
This frame shows three things:
* How to draw an xbm image
* How to draw a static text which is not moved by the frame transition
* The active/inactive frame indicators
### Frame 2
![DemoFrame2](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame2.jpg)
Currently there are one fontface with three sizes included in the library: Arial 10, 16 and 24. Once the converter is published you will be able to convert any ttf font into the used format.
### Frame 3
![DemoFrame3](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame3.jpg)
This frame demonstrates the text alignment. The coordinates in the frame show relative to which position the texts have been rendered.
### Frame 4
![DemoFrame4](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame4.jpg)
This shows how to use define a maximum width after which the driver automatically wraps a word to the next line. This comes in very handy if you have longer texts to display.
### SPI version
![SPIVersion](https://github.com/neptune2/esp8266-oled-ssd1306/raw/master/resources/SPI_version.jpg)
This shows the code working on the SPI version of the display. See demo code for ESP8266 pins used.

View File

@ -0,0 +1,214 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#include <TimeLib.h>
// Include the correct display library
// For a connection via I2C using Wire include
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"`
// For a connection via I2C using brzo_i2c (must be installed) include
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Brzo.h"
// #include "SH1106Brzo.h"
// For a connection via SPI include
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Spi.h"
// #include "SH1106SPi.h"
// Include the UI lib
#include "OLEDDisplayUi.h"
// Include custom images
#include "images.h"
// Use the corresponding display class:
// Initialize the OLED display using SPI
// D5 -> CLK
// D7 -> MOSI (DOUT)
// D0 -> RES
// D2 -> DC
// D8 -> CS
// SSD1306Spi display(D0, D2, D8);
// or
// SH1106Spi display(D0, D2);
// Initialize the OLED display using brzo_i2c
// D3 -> SDA
// D5 -> SCL
// SSD1306Brzo display(0x3c, D3, D5);
// or
// SH1106Brzo display(0x3c, D3, D5);
// Initialize the OLED display using Wire library
SSD1306Wire display(0x3c, D3, D5);
// SH1106 display(0x3c, D3, D5);
OLEDDisplayUi ui ( &display );
int screenW = 128;
int screenH = 64;
int clockCenterX = screenW/2;
int clockCenterY = ((screenH-16)/2)+16; // top yellow part is 16 px height
int clockRadius = 23;
// utility function for digital clock display: prints leading 0
String twoDigits(int digits){
if(digits < 10) {
String i = '0'+String(digits);
return i;
}
else {
return String(digits);
}
}
void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
}
void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
// ui.disableIndicator();
// Draw the clock face
// display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius);
display->drawCircle(clockCenterX + x, clockCenterY + y, 2);
//
//hour ticks
for( int z=0; z < 360;z= z + 30 ){
//Begin at 0° and stop at 360°
float angle = z ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) );
int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) );
int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y);
}
// display second hand
float angle = second() * 6 ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
//
// display minute hand
angle = minute() * 6 ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
//
// display hour hand
angle = hour() * 30 + int( ( minute() / 12 ) * 6 ) ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
}
void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_24);
display->drawString(clockCenterX + x , clockCenterY + y, timenow );
}
// This array keeps function pointers to all frames
// frames are the single views that slide in
FrameCallback frames[] = { analogClockFrame, digitalClockFrame };
// how many frames are there?
int frameCount = 2;
// Overlays are statically drawn on top of a frame eg. a clock
OverlayCallback overlays[] = { clockOverlay };
int overlaysCount = 1;
void setup() {
Serial.begin(9600);
Serial.println();
// The ESP is capable of rendering 60fps in 80Mhz mode
// but that won't give you much time for anything else
// run it in 160Mhz mode or just set it to 30 fps
ui.setTargetFPS(60);
// Customize the active and inactive symbol
ui.setActiveSymbol(activeSymbol);
ui.setInactiveSymbol(inactiveSymbol);
// You can change this to
// TOP, LEFT, BOTTOM, RIGHT
ui.setIndicatorPosition(TOP);
// Defines where the first frame is located in the bar.
ui.setIndicatorDirection(LEFT_RIGHT);
// You can change the transition that is used
// SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
ui.setFrameAnimation(SLIDE_LEFT);
// Add frames
ui.setFrames(frames, frameCount);
// Add overlays
ui.setOverlays(overlays, overlaysCount);
// Initialising the UI will init the display too.
ui.init();
display.flipScreenVertically();
unsigned long secsSinceStart = millis();
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSinceStart - seventyYears * SECS_PER_HOUR;
setTime(epoch);
}
void loop() {
int remainingTimeBudget = ui.update();
if (remainingTimeBudget > 0) {
// You can do some work here
// Don't do stuff if you are below your
// time budget.
delay(remainingTimeBudget);
}
}

View File

@ -0,0 +1,21 @@
const unsigned char activeSymbol[] PROGMEM = {
B00000000,
B00000000,
B00011000,
B00100100,
B01000010,
B01000010,
B00100100,
B00011000
};
const unsigned char inactiveSymbol[] PROGMEM = {
B00000000,
B00000000,
B00000000,
B00000000,
B00011000,
B00011000,
B00000000,
B00000000
};

View File

@ -0,0 +1,233 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
// Include the correct display library
// For a connection via I2C using Wire include
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"`
// For a connection via I2C using brzo_i2c (must be installed) include
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Brzo.h"
// #include "SH1106Brzo.h"
// For a connection via SPI include
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Spi.h"
// #include "SH1106SPi.h"
// Use the corresponding display class:
// Initialize the OLED display using SPI
// D5 -> CLK
// D7 -> MOSI (DOUT)
// D0 -> RES
// D2 -> DC
// D8 -> CS
// SSD1306Spi display(D0, D2, D8);
// or
// SH1106Spi display(D0, D2);
// Initialize the OLED display using brzo_i2c
// D3 -> SDA
// D5 -> SCL
// SSD1306Brzo display(0x3c, D3, D5);
// or
// SH1106Brzo display(0x3c, D3, D5);
// Initialize the OLED display using Wire library
SSD1306Wire display(0x3c, D3, D5);
// SH1106 display(0x3c, D3, D5);
// Adapted from Adafruit_SSD1306
void drawLines() {
for (int16_t i=0; i<display.getWidth(); i+=4) {
display.drawLine(0, 0, i, display.getHeight()-1);
display.display();
delay(10);
}
for (int16_t i=0; i<display.getHeight(); i+=4) {
display.drawLine(0, 0, display.getWidth()-1, i);
display.display();
delay(10);
}
delay(250);
display.clear();
for (int16_t i=0; i<display.getWidth(); i+=4) {
display.drawLine(0, display.getHeight()-1, i, 0);
display.display();
delay(10);
}
for (int16_t i=display.getHeight()-1; i>=0; i-=4) {
display.drawLine(0, display.getHeight()-1, display.getWidth()-1, i);
display.display();
delay(10);
}
delay(250);
display.clear();
for (int16_t i=display.getWidth()-1; i>=0; i-=4) {
display.drawLine(display.getWidth()-1, display.getHeight()-1, i, 0);
display.display();
delay(10);
}
for (int16_t i=display.getHeight()-1; i>=0; i-=4) {
display.drawLine(display.getWidth()-1, display.getHeight()-1, 0, i);
display.display();
delay(10);
}
delay(250);
display.clear();
for (int16_t i=0; i<display.getHeight(); i+=4) {
display.drawLine(display.getWidth()-1, 0, 0, i);
display.display();
delay(10);
}
for (int16_t i=0; i<display.getWidth(); i+=4) {
display.drawLine(display.getWidth()-1, 0, i, display.getHeight()-1);
display.display();
delay(10);
}
delay(250);
}
// Adapted from Adafruit_SSD1306
void drawRect(void) {
for (int16_t i=0; i<display.getHeight()/2; i+=2) {
display.drawRect(i, i, display.getWidth()-2*i, display.getHeight()-2*i);
display.display();
delay(10);
}
}
// Adapted from Adafruit_SSD1306
void fillRect(void) {
uint8_t color = 1;
for (int16_t i=0; i<display.getHeight()/2; i+=3) {
display.setColor((color % 2 == 0) ? BLACK : WHITE); // alternate colors
display.fillRect(i, i, display.getWidth() - i*2, display.getHeight() - i*2);
display.display();
delay(10);
color++;
}
// Reset back to WHITE
display.setColor(WHITE);
}
// Adapted from Adafruit_SSD1306
void drawCircle(void) {
for (int16_t i=0; i<display.getHeight(); i+=2) {
display.drawCircle(display.getWidth()/2, display.getHeight()/2, i);
display.display();
delay(10);
}
delay(1000);
display.clear();
// This will draw the part of the circel in quadrant 1
// Quadrants are numberd like this:
// 0010 | 0001
// ------|-----
// 0100 | 1000
//
display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00000001);
display.display();
delay(200);
display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00000011);
display.display();
delay(200);
display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00000111);
display.display();
delay(200);
display.drawCircleQuads(display.getWidth()/2, display.getHeight()/2, display.getHeight()/4, 0b00001111);
display.display();
}
void printBuffer(void) {
// Initialize the log buffer
// allocate memory to store 8 lines of text and 30 chars per line.
display.setLogBuffer(5, 30);
// Some test data
const char* test[] = {
"Hello",
"World" ,
"----",
"Show off",
"how",
"the log buffer",
"is",
"working.",
"Even",
"scrolling is",
"working"
};
for (uint8_t i = 0; i < 11; i++) {
display.clear();
// Print to the screen
display.println(test[i]);
// Draw it to the internal screen buffer
display.drawLogBuffer(0, 0);
// Display it on the screen
display.display();
delay(500);
}
}
void setup() {
display.init();
// display.flipScreenVertically();
display.setContrast(255);
drawLines();
delay(1000);
display.clear();
drawRect();
delay(1000);
display.clear();
fillRect();
delay(1000);
display.clear();
drawCircle();
delay(1000);
display.clear();
printBuffer();
delay(1000);
display.clear();
}
void loop() { }

View File

@ -0,0 +1,123 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
// WiFi includes
#include <ESP8266WiFi.h>
// OTA Includes
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
const char *ssid = "[Your SSID]";
const char *password = "[Your Password]";
// Include the correct display library
// For a connection via I2C using Wire include
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"`
// For a connection via I2C using brzo_i2c (must be installed) include
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Brzo.h"
// #include "SH1106Brzo.h"
// For a connection via SPI include
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Spi.h"
// #include "SH1106SPi.h"
// Use the corresponding display class:
// Initialize the OLED display using SPI
// D5 -> CLK
// D7 -> MOSI (DOUT)
// D0 -> RES
// D2 -> DC
// D8 -> CS
// SSD1306Spi display(D0, D2, D8);
// or
// SH1106Spi display(D0, D2);
// Initialize the OLED display using brzo_i2c
// D3 -> SDA
// D5 -> SCL
// SSD1306Brzo display(0x3c, D3, D5);
// or
// SH1106Brzo display(0x3c, D3, D5);
// Initialize the OLED display using Wire library
SSD1306Wire display(0x3c, D3, D5);
// SH1106 display(0x3c, D3, D5);
void setup() {
WiFi.begin ( ssid, password );
// Wait for connection
while ( WiFi.status() != WL_CONNECTED ) {
delay ( 10 );
}
display.init();
display.flipScreenVertically();
display.setContrast(255);
ArduinoOTA.begin();
ArduinoOTA.onStart([]() {
display.clear();
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
display.drawString(display.getWidth()/2, display.getHeight()/2 - 10, "OTA Update");
display.display();
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
display.drawProgressBar(4, 32, 120, 8, progress / (total / 100) );
display.display();
});
ArduinoOTA.onEnd([]() {
display.clear();
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
display.drawString(display.getWidth()/2, display.getHeight()/2, "Restart");
display.display();
});
// Align text vertical/horizontal center
display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
display.setFont(ArialMT_Plain_10);
display.drawString(display.getWidth()/2, display.getHeight()/2, "Ready for OTA:\n" + WiFi.localIP().toString());
display.display();
}
void loop() {
ArduinoOTA.handle();
}

View File

@ -0,0 +1,197 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
// Include the correct display library
// For a connection via I2C using the Arduino Wire include:
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h" // legacy: #include "SSD1306.h"
// OR #include "SH1106Wire.h" // legacy: #include "SH1106.h"
// For a connection via I2C using brzo_i2c (must be installed) include:
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Brzo.h"
// OR #include "SH1106Brzo.h"
// For a connection via SPI include:
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Spi.h"
// OR #include "SH1106SPi.h"
// Optionally include custom images
#include "images.h"
// Initialize the OLED display using Arduino Wire:
SSD1306Wire display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL - SDA and SCL usually populate automatically based on your board's pins_arduino.h
// SSD1306Wire display(0x3c, D3, D5); // ADDRESS, SDA, SCL - If not, they can be specified manually.
// SSD1306Wire display(0x3c, SDA, SCL, GEOMETRY_128_32); // ADDRESS, SDA, SCL, OLEDDISPLAY_GEOMETRY - Extra param required for 128x32 displays.
// SH1106 display(0x3c, SDA, SCL); // ADDRESS, SDA, SCL
// Initialize the OLED display using brzo_i2c:
// SSD1306Brzo display(0x3c, D3, D5); // ADDRESS, SDA, SCL
// or
// SH1106Brzo display(0x3c, D3, D5); // ADDRESS, SDA, SCL
// Initialize the OLED display using SPI:
// D5 -> CLK
// D7 -> MOSI (DOUT)
// D0 -> RES
// D2 -> DC
// D8 -> CS
// SSD1306Spi display(D0, D2, D8); // RES, DC, CS
// or
// SH1106Spi display(D0, D2); // RES, DC
#define DEMO_DURATION 3000
typedef void (*Demo)(void);
int demoMode = 0;
int counter = 1;
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
// Initialising the UI will init the display too.
display.init();
display.flipScreenVertically();
display.setFont(ArialMT_Plain_10);
}
void drawFontFaceDemo() {
// Font Demo1
// create more fonts at http://oleddisplay.squix.ch/
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.setFont(ArialMT_Plain_10);
display.drawString(0, 0, "Hello world");
display.setFont(ArialMT_Plain_16);
display.drawString(0, 10, "Hello world");
display.setFont(ArialMT_Plain_24);
display.drawString(0, 26, "Hello world");
}
void drawTextFlowDemo() {
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.drawStringMaxWidth(0, 0, 128,
"Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore." );
}
void drawTextAlignmentDemo() {
// Text alignment demo
display.setFont(ArialMT_Plain_10);
// The coordinates define the left starting point of the text
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.drawString(0, 10, "Left aligned (0,10)");
// The coordinates define the center of the text
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(64, 22, "Center aligned (64,22)");
// The coordinates define the right end of the text
display.setTextAlignment(TEXT_ALIGN_RIGHT);
display.drawString(128, 33, "Right aligned (128,33)");
}
void drawRectDemo() {
// Draw a pixel at given position
for (int i = 0; i < 10; i++) {
display.setPixel(i, i);
display.setPixel(10 - i, i);
}
display.drawRect(12, 12, 20, 20);
// Fill the rectangle
display.fillRect(14, 14, 17, 17);
// Draw a line horizontally
display.drawHorizontalLine(0, 40, 20);
// Draw a line horizontally
display.drawVerticalLine(40, 0, 20);
}
void drawCircleDemo() {
for (int i=1; i < 8; i++) {
display.setColor(WHITE);
display.drawCircle(32, 32, i*3);
if (i % 2 == 0) {
display.setColor(BLACK);
}
display.fillCircle(96, 32, 32 - i* 3);
}
}
void drawProgressBarDemo() {
int progress = (counter / 5) % 100;
// draw the progress bar
display.drawProgressBar(0, 32, 120, 10, progress);
// draw the percentage as String
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(64, 15, String(progress) + "%");
}
void drawImageDemo() {
// see http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html
// on how to create xbm files
display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
}
Demo demos[] = {drawFontFaceDemo, drawTextFlowDemo, drawTextAlignmentDemo, drawRectDemo, drawCircleDemo, drawProgressBarDemo, drawImageDemo};
int demoLength = (sizeof(demos) / sizeof(Demo));
long timeSinceLastModeSwitch = 0;
void loop() {
// clear the display
display.clear();
// draw the current demo method
demos[demoMode]();
display.setTextAlignment(TEXT_ALIGN_RIGHT);
display.drawString(10, 128, String(millis()));
// write the buffer to the display
display.display();
if (millis() - timeSinceLastModeSwitch > DEMO_DURATION) {
demoMode = (demoMode + 1) % demoLength;
timeSinceLastModeSwitch = millis();
}
counter++;
delay(10);
}

View File

@ -0,0 +1,28 @@
#define WiFi_Logo_width 60
#define WiFi_Logo_height 36
const uint8_t WiFi_Logo_bits[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF,
0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00,
0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C,
0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00,
0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C,
0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00,
0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C,
0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00,
0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C,
0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00,
0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F,
0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00,
0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF,
0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00,
0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

View File

@ -0,0 +1,75 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
// Include the correct display library
// For a connection via I2C using Wire include
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
#include "images.h"
// Initialize the OLED display using Wire library
SSD1306Wire display(0x3c, D3, D5);
SSD1306Wire display2(0x3c, D1, D2);
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
// Initialising the UI will init the display too.
display.init();
display2.init();
// This will make sure that multiple instances of a display driver
// running on different ports will work together transparently
display.setI2cAutoInit(true);
display2.setI2cAutoInit(true);
display.flipScreenVertically();
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_LEFT);
display2.flipScreenVertically();
display2.setFont(ArialMT_Plain_10);
display2.setTextAlignment(TEXT_ALIGN_LEFT);
}
void loop() {
display.clear();
display.drawString(0, 0, "Hello world: " + String(millis()));
display.display();
display2.clear();
display2.drawString(0, 0, "Hello world: " + String(millis()));
display2.display();
delay(10);
}

View File

@ -0,0 +1,28 @@
#define WiFi_Logo_width 60
#define WiFi_Logo_height 36
const uint8_t WiFi_Logo_bits[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF,
0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00,
0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C,
0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00,
0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C,
0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00,
0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C,
0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00,
0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C,
0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00,
0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F,
0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00,
0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF,
0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00,
0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

View File

@ -0,0 +1,194 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
// Include the correct display library
// For a connection via I2C using Wire include
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"`
// For a connection via I2C using brzo_i2c (must be installed) include
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Brzo.h"
// #include "SH1106Brzo.h"
// For a connection via SPI include
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Spi.h"
// #include "SH1106SPi.h"
// Include the UI lib
#include "OLEDDisplayUi.h"
// Include custom images
#include "images.h"
// Use the corresponding display class:
// Initialize the OLED display using SPI
// D5 -> CLK
// D7 -> MOSI (DOUT)
// D0 -> RES
// D2 -> DC
// D8 -> CS
// SSD1306Spi display(D0, D2, D8);
// or
// SH1106Spi display(D0, D2);
// Initialize the OLED display using brzo_i2c
// D3 -> SDA
// D5 -> SCL
// SSD1306Brzo display(0x3c, D3, D5);
// or
// SH1106Brzo display(0x3c, D3, D5);
// Initialize the OLED display using Wire library
SSD1306Wire display(0x3c, D3, D5);
// SH1106Wire display(0x3c, D3, D5);
OLEDDisplayUi ui ( &display );
void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
display->setTextAlignment(TEXT_ALIGN_RIGHT);
display->setFont(ArialMT_Plain_10);
display->drawString(128, 0, String(millis()));
}
void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
// draw an xbm image.
// Please note that everything that should be transitioned
// needs to be drawn relative to x and y
display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
}
void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
// Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file
// Besides the default fonts there will be a program to convert TrueType fonts into this format
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_10);
display->drawString(0 + x, 10 + y, "Arial 10");
display->setFont(ArialMT_Plain_16);
display->drawString(0 + x, 20 + y, "Arial 16");
display->setFont(ArialMT_Plain_24);
display->drawString(0 + x, 34 + y, "Arial 24");
}
void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
// Text alignment demo
display->setFont(ArialMT_Plain_10);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(0 + x, 11 + y, "Left aligned (0,10)");
// The coordinates define the center of the text
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(64 + x, 22 + y, "Center aligned (64,22)");
// The coordinates define the right end of the text
display->setTextAlignment(TEXT_ALIGN_RIGHT);
display->drawString(128 + x, 33 + y, "Right aligned (128,33)");
}
void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
// Demo for drawStringMaxWidth:
// with the third parameter you can define the width after which words will be wrapped.
// Currently only spaces and "-" are allowed for wrapping
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(ArialMT_Plain_10);
display->drawStringMaxWidth(0 + x, 10 + y, 128, "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore.");
}
void drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
}
// This array keeps function pointers to all frames
// frames are the single views that slide in
FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5 };
// how many frames are there?
int frameCount = 5;
// Overlays are statically drawn on top of a frame eg. a clock
OverlayCallback overlays[] = { msOverlay };
int overlaysCount = 1;
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
// The ESP is capable of rendering 60fps in 80Mhz mode
// but that won't give you much time for anything else
// run it in 160Mhz mode or just set it to 30 fps
ui.setTargetFPS(60);
// Customize the active and inactive symbol
ui.setActiveSymbol(activeSymbol);
ui.setInactiveSymbol(inactiveSymbol);
// You can change this to
// TOP, LEFT, BOTTOM, RIGHT
ui.setIndicatorPosition(BOTTOM);
// Defines where the first frame is located in the bar.
ui.setIndicatorDirection(LEFT_RIGHT);
// You can change the transition that is used
// SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
ui.setFrameAnimation(SLIDE_LEFT);
// Add frames
ui.setFrames(frames, frameCount);
// Add overlays
ui.setOverlays(overlays, overlaysCount);
// Initialising the UI will init the display too.
ui.init();
display.flipScreenVertically();
}
void loop() {
int remainingTimeBudget = ui.update();
if (remainingTimeBudget > 0) {
// You can do some work here
// Don't do stuff if you are below your
// time budget.
delay(remainingTimeBudget);
}
}

View File

@ -0,0 +1,50 @@
#define WiFi_Logo_width 60
#define WiFi_Logo_height 36
const uint8_t WiFi_Logo_bits[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF,
0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00,
0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C,
0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00,
0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C,
0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00,
0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C,
0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00,
0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C,
0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00,
0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F,
0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00,
0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF,
0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00,
0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const uint8_t activeSymbol[] PROGMEM = {
B00000000,
B00000000,
B00011000,
B00100100,
B01000010,
B01000010,
B00100100,
B00011000
};
const uint8_t inactiveSymbol[] PROGMEM = {
B00000000,
B00000000,
B00000000,
B00000000,
B00011000,
B00011000,
B00000000,
B00000000
};

34
library.json Normal file
View File

@ -0,0 +1,34 @@
{
"name": "ESP8266, ESP32 and SAMD21 OLED driver for SSD1306 displays",
"version": "1.0.0",
"keywords": "ssd1306, oled, display, i2c, spi",
"description": "I2C/SPI display driver for SSD1306 OLED displays connected to ESP8266, ESP32, SAMD21",
"repository":
{
"type": "git",
"url": "https://git.xpablo.cz/xPablo.cz/OLED_SSD"
},
"authors":
[
{
"name": "Daniel Eichhorn, ThingPulse",
"email": "squix78@gmail.com",
"url": "https://thingpulse.com"
},
{
"name": "Fabrice Weinberg",
"email": "fabrice@weinberg.me"
},
{
"name": "Pavel Brychta",
"email": "pablo@xpablo.cz",
"url": "https://xpablo.cz"
}
],
"frameworks": "arduino",
"platforms": [
"espressif8266",
"espressif32",
"atmelsam"
]
}

9
library.properties Normal file
View File

@ -0,0 +1,9 @@
name=ESP8266, ESP32 and SAMD21 OLED driver for SSD1306 displays
version=1.0.0
author=ThingPulse, Fabrice Weinberg, Pavel Brychta
maintainer=xPablo.cz <pablo@xpablo.cz>
sentence=I2C/SPI display driver for SSD1306 OLED displays connected to ESP8266, ESP32, SAMD21
paragraph=The following geometries are currently supported: 128x64, 128x32, 64x48. The init sequence was inspired by Adafruit's library for the same display.
category=Display
url=https://git.xpablo.cz/xPablo.cz/OLED_SSD
architectures=esp8266,esp32,samd21

24
license Normal file
View File

@ -0,0 +1,24 @@
The MIT License (MIT)
Copyright (c) 2016 by Daniel Eichhorn
Copyright (c) 2016 by Fabrice Weinberg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
See more at http://blog.squix.ch

BIN
resources/DemoFrame1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
resources/DemoFrame2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
resources/DemoFrame3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
resources/DemoFrame4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
resources/FontTool.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
resources/SPI_version.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

664
resources/glyphEditor.html Normal file
View File

@ -0,0 +1,664 @@
<!--The MIT License (MIT)
Copyright (c) 2017 by Xavier Grosjean
Based on work
Copyright (c) 2016 by Daniel Eichhorn
Copyright (c) 2016 by Fabrice Weinberg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<!--
Standalone page to draw icons in matrix and generates the map definition with the format required by
library for OLED SD1306 screen: https://github.com/squix78/esp8266-oled-ssd1306
100% quick and dirty vanilla ECS6, no framework or library was injured for this project.
-->
<html>
<head>
<meta charset="utf-8" />
<style>
#form, #chars table {
user-select: none;
-moz-user-select: none;
margin-top: 5px;
}
#chars table, tr, td, th {
border-collapse: collapse;
}
#chars td, th {
border: 1px solid #CCC;
width: 12px;
height: 15px;
}
#chars th[action] {
cursor:pointer;
}
#chars th[action="up"], #chars th[action="down"], #chars th[action="left"], #chars th[action="right"] {
font-size: 10px;
}
#chars td.on {
background-color: #00F;
}
#addChar, #generate {
display: none;
}
body.started #addChar, body.started #generate {
display: inline;
}
body.started #create {
display: none;
}
#page {
position:relative;
}
#page>div {
position: relative;
float: left;
}
#output {
margin-left: 20px;
margin-top: 12px;
font-size:12px;
user-select: all;
-moz-user-select: all;
max-width:60%;
}
#header {
margin-top: 10px;
}
#inputText {
width: 100%;
height:120px;
}
</style>
</head>
<body>
<div id="form">
<table width="100%">
<tr>
<td>Font array name:</td><td><input placeholder="Font array name" type="text" id="name" value="My_Font"/></td>
<td width="75%" rowspan="5">
<textarea id="inputText" placeholder="Or paste a char array font definition here">
const uint8_t My_Font[] PROGMEM = {
0x0A, // Width: 10
0x0D, // Height: 13
0x01, // First char: 1
0x02, // Number of chars: 2
// Jump Table:
0x00, 0x00, 0x13, 0x0A, // 1
0x00, 0x13, 0x14, 0x0A, // 2
// Font Data:
0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0x10, 0x02, 0xF0, 0x03, 0xC0, // 1
0x00, 0x0C, 0x80, 0x0B, 0x70, 0x08, 0x0C, 0x08, 0xF3, 0x0A, 0xF3, 0x0A, 0x0C, 0x08, 0x70, 0x08, 0x80, 0x0B, 0x00, 0x0C, // 2
};
</textarea></td>
</tr>
<tr>
<td>First char code:</td><td><input placeholder="First char code" type="number" id="code" value="1" min="1"/></td>
</tr>
<tr>
<td>Width:</td><td><input placeholder="Font width" type="number" id="width" value="10"/></td>
</tr>
<tr>
<td>Height:</td><td><input placeholder="Font height" type="number" id="height" value="13" max="64"/></td>
</tr>
<tr>
<td colspan="3"> </td> <!--slightly improves layout-->
</tr>
<tr>
<td colspan="2">
<button id="create">Create</button>
<button id="shiftUp">Shift all up</button>
<button id="generate">Generate</button>
<button id="savetoFile">Save To File</button>
</td>
<td><input type="file" id="fileinput" /> <button id="parse">Parse</button></td>
</tr>
</table>
<br/>
</div>
<div id="page">
<div id="charxContainer" ondragstart="return false;">
<div id="chars"></div><br/>
<button id="addChar">Add a character</button>
</div>
<div id="output">
<div class="output" id="header"> </div>
<div class="output" id="jump"> </div>
<div class="output" id="data"> </div>
</div>
</div>
<script>
(function() {
let font;
class Font {
constructor() {
this.height = parseInt(document.getElementById('height').value);
this.width = parseInt(document.getElementById('width').value);
this.currentCharCode = parseInt(document.getElementById('code').value);
this.fontContainer = document.getElementById('chars');
this.bytesForHeight = (1 + ((this.height - 1) >> 3)); // number of bytes needed for this font height
document.body.className = "started";
document.getElementById('height').disabled = true;
document.getElementById('width').disabled = true;
}
// Should we have a Char and a Pixel class ? not sure it's worth it.
// Let's use the DOM to store everything for now
static updateCaption(element, code) {
element.textContent = `Char #${code}`;
}
// Add a pixel matrix to draw a new character
// jumpaData not used for now: some day, use the last byte for optimisation
addChar(jumpData, charData) {
let charContainer = this.fontContainer.appendChild(document.createElement("table"));
charContainer.setAttribute("code", this.currentCharCode);
let caption = charContainer.appendChild(document.createElement("caption"));
Font.updateCaption(caption, this.currentCharCode);
let header = charContainer.appendChild(document.createElement("tr"));
header.innerHTML = '<th title="Delete this char" action="delete">&cross;</th>'
+ '<th title="Add a char above" action="add">+</th>'
+ '<th title="Shift pixels to the left" action="left">&larr;</th>'
+ '<th title="Shift pixels down" action="down">&darr;</th>'
+ '<th title="Shift pixels up" action="up">&uarr;</th>'
+ '<th title="Shift pixels to the right" action="right">&rarr;</th>'
+ '<th title="Copy from another character" action="copy">&copy;</th>'
+ '<th title="Reset all pixels" action="clean">&empty;</th>';
// If data is provided, decode it to pixel initialization friendly structure
let pixelInit = [];
if (charData && charData.length) {
// charData byte count needs to be a multiple of bytesForHeight. End bytes with value 0 may have been trimmed
let missingBytes = charData.length % this.bytesForHeight;
for(let b = 0; b < missingBytes ; b++) {
charData.push(0);
}
while(charData.length) {
let row = charData.splice(0, this.bytesForHeight).reverse();
let pixelRow = [];
for (let b = 0; b < row.length; b++) {
let mask = 0x80;
let byte = row[b];
for (let bit = 0; bit < 8; bit++) {
if (byte & mask) {
pixelRow.push(1);
} else {
pixelRow.push(0);
}
mask = mask >> 1;
}
}
pixelRow.splice(0, pixelRow.length - this.height);
//Font.output('data', pixelRow);
pixelInit.push(pixelRow.reverse());
}
}
for(let r = 0; r < this.height; r++) {
let rowContainer = charContainer.appendChild(document.createElement("tr"));
for(let c = 0; c < this.width; c++) {
let pixContainer = rowContainer.appendChild(document.createElement("td"));
pixContainer.setAttribute('action', 'toggle');
// If there is some init data, set the pixel accordingly
if (pixelInit.length && pixelInit[c]) {
if (pixelInit[c][r]) {
pixContainer.className = 'on';
}
}
}
}
this.currentCharCode++;
return charContainer;
}
static togglePixel(pixel) {
pixel.className = pixel.className === 'on' ? '': 'on';
}
// Return anInt as hex string
static toHexString(aByte) {
let zero = aByte < 16?'0':'';
return `0x${zero}${aByte.toString(16).toUpperCase()}`
}
// Return least significant byte as hex string
static getLsB(anInt) {
return Font.toHexString(anInt & 0xFF);
}
// Return most significant byte as hex string
static getMsB(anInt) {
return Font.toHexString(anInt>>>8);
}
static output(targetId, msg) {
let output = document.getElementById(targetId);
let line = output.appendChild(document.createElement('div'));
line.textContent = msg;
}
static emptyChars() {
document.getElementById('chars').textContent = '';
}
static emptyOutput() {
document.getElementById('header').textContent = '';
document.getElementById('jump').textContent = '';
document.getElementById('data').textContent = '';
}
saveFile() {
let filename = document.getElementById('name').value.replace(/[^a-zA-Z0-9_$]/g, '_') + ".h";
let data = document.getElementById("output").innerText;
if(data.length < 10) return;
let blobObject = new Blob([data], {type:'text/plain'});
if(window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blobObject, filename);
} else {
let a = document.createElement("a");
a.setAttribute("href", URL.createObjectURL(blobObject));
a.setAttribute("download", filename);
if (document.createEvent) {
let event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
a.dispatchEvent(event);
} else {
a.click();
}
}
}
// Read from the <td> css class the pixels that need to be on
// generates the jump table and font data
generate() {
// this.width -= 3; // hack to narrow an existing font
Font.emptyOutput();
let chars = this.fontContainer.getElementsByTagName('table');
let firstCharCode = parseInt(document.getElementById('code').value);
let name = document.getElementById('name').value.replace(/[^a-zA-Z0-9_$]/g, '_');
let bits2add = this.bytesForHeight*8 - this.height; // number of missing bits to fill leftmost byte
let charCount = chars.length;
let charAddr = 0;
// Comments are used when parsing back a generated font
Font.output('jump', ' // Jump Table:');
Font.output('data', ' // Font Data:');
// Browse each character
for(let ch = 0; ch < charCount; ch++) {
// Fix renumbering in case first char code was modified
Font.updateCaption(chars[ch].getElementsByTagName('caption')[0], ch + firstCharCode);
let charBytes = [];
let charCode = ch + firstCharCode;
let rows = chars[ch].getElementsByTagName('tr');
let charNotZero = false;
// Browse each column
for(let col = 0; col < this.width ; col++) {
let bits = ""; // using string because js uses 32b ints when performing bit operations
// Browse each row starting from the bottom one, going up, and accumulate pixels in
// a string: this rotates the glyph
// Beware, row 0 is table headers.
for(let r = rows.length-1; r >=1 ; r--) {
let pixelState = rows[r].children[col].className;
bits += (pixelState === 'on' ? '1': '0');
}
// Need to complete missing bits to have a sizeof byte multiple number of bits
for(let b = 0; b < bits2add; b++) {
bits = '0' + bits;
}
// Font.output('data', ` // ${bits}`); // Debugging help: rotated bitmap
// read bytes from the end
for(let b = bits.length - 1; b >= 7; b -= 8) {
//Font.output('data', ` // ${bits.substr(b-7, 8)}`); // Debugging help: rotated bitmap
let byte = parseInt(bits.substr(b-7, 8), 2);
if (byte !== 0) {
charNotZero = true;
}
charBytes.push(Font.toHexString(byte));
}
}
// Compute the used width of the character:
// rightmost column with pixels plus one
// one column is spread over several bytes...
let charWidth = 0;
for(let i=charBytes.length - 1; i >= 0; i-=this.bytesForHeight) {
let sum = 0;
for(let j=0; j < this.bytesForHeight; j++) {
sum += parseInt(charBytes[i - j], 16);
}
if(sum !== 0) {
charWidth = (i + 1)/this.bytesForHeight + 1;
break;
}
}
// Memory optim: Remove bytes with value 0 at the end of the array.
while(parseInt(charBytes[charBytes.length-1], 16) === 0 && charBytes.length > 1) {
charBytes.pop();
}
if (charNotZero) {
Font.output('data', ` ${charBytes.join(', ')}, // ${charCode}`);
Font.output('jump', ` ${Font.getMsB(charAddr)}, ${Font.getLsB(charAddr)}, ${Font.toHexString(charBytes.length)}, ${Font.toHexString(charWidth)}, // ${charCode} `);
charAddr += charBytes.length;
} else {
Font.output('jump', ` 0xFF, 0xFF, 0x00, ${Font.toHexString(this.width)}, // ${charCode} `);
}
}
Font.output('data', '};');
Font.output('header', "// Font generated or edited with the glyphEditor");
Font.output('header', `const uint8_t ${name}[] PROGMEM = {`);
// Comments are used when parsing back a generated font
Font.output('header', ` ${Font.toHexString(this.width)}, // Width: ${this.width}`);
Font.output('header', ` ${Font.toHexString(this.height)}, // Height: ${this.height}`);
Font.output('header', ` ${Font.toHexString(firstCharCode)}, // First char: ${firstCharCode}`);
Font.output('header', ` ${Font.toHexString(charCount)}, // Number of chars: ${charCount}`);
}
}
document.getElementById('fileinput').addEventListener('change', function(e) {
let f = e.target.files[0];
if (f) {
let r = new FileReader();
r.onload = function(e) {
let contents = e.target.result;
alert( "Got the file.\n"
+"name: " + f.name + "\n"
+"type: " + f.type + "\n"
+"size: " + f.size + " bytes\n"
+"starts with: " + contents.substr(0, contents.indexOf("\n"))
);
document.getElementById("inputText").value = contents;
};
r.readAsText(f);
} else {
alert("Failed to load file");
}
});
document.getElementById('savetoFile').addEventListener('click', function() {
font.saveFile();
});
document.getElementById('shiftUp').addEventListener('click', function() {
var chars = document.getElementById("chars");
var tables = chars.getElementsByTagName("table");
for(var i=0; i< tables.length; i++) {
shiftUp(tables[i]);
}
});
document.getElementById('generate').addEventListener('click', function() {
font.generate();
});
document.getElementById('addChar').addEventListener('click', function() {
font.addChar();
});
document.getElementById('inputText').addEventListener('click', function(e) {
let target = e.target;
target.select();
});
document.getElementById('chars').addEventListener('mousedown', function(e) {
if (e.button !== 0) return;
let target = e.target;
let action = target.getAttribute('action') || '';
if (action === '') return;
let result, code, nextContainer, previousContainer, pixels ;
let currentContainer = target.parentNode.parentNode;
switch(action) {
case 'add':
code = currentContainer.getAttribute('code');
nextContainer = font.addChar();
currentContainer.parentNode.insertBefore(nextContainer, currentContainer);
do {
nextContainer.setAttribute('code', code);
Font.updateCaption(nextContainer.getElementsByTagName('caption')[0], code);
code ++;
} while (nextContainer = nextContainer.nextSibling);
break;
case 'delete':
result = confirm("Delete this character ?");
if (!result) return;
code = currentContainer.getAttribute('code');
nextContainer = currentContainer;
while (nextContainer = nextContainer.nextSibling) {
nextContainer.setAttribute('code', code);
Font.updateCaption(nextContainer.getElementsByTagName('caption')[0], code);
code ++;
}
currentContainer.parentNode.removeChild(currentContainer);
break;
// Shift pixels to the left
case 'left':
pixels = currentContainer.getElementsByTagName('td');
for(p = 0; p < pixels.length; p++) {
if((p + 1) % font.width) {
pixels[p].className = pixels[p + 1].className;
} else {
pixels[p].className = '';
}
}
break;
// Shift pixels to the right
case 'right':
pixels = currentContainer.getElementsByTagName('td');
for(p = pixels.length-1; p >=0 ; p--) {
if(p % font.width) {
pixels[p].className = pixels[p - 1].className;
} else {
pixels[p].className = '';
}
}
break;
// Shift pixels down
case 'down':
pixels = currentContainer.getElementsByTagName('td');
for(p = pixels.length-1; p >=0 ; p--) {
if(p >= font.width) {
pixels[p].className = pixels[p - font.width].className;
} else {
pixels[p].className = '';
}
} break;
// Shift pixels up
case 'up':
shiftUp(currentContainer);
break;
case 'toggle':
Font.togglePixel(target);
break;
case 'clean':
result = confirm("Delete the pixels ?");
if (!result) return;
pixels = currentContainer.getElementsByTagName('td');
for (let p = 0; p < pixels.length; p++) {
pixels[p].className = '';
}
break;
case 'copy':
let charNumber = parseInt(prompt("Source char #: "));
let chars = font.fontContainer.getElementsByTagName('table');
let tableOffset = charNumber - parseInt(document.getElementById('code').value);
let srcPixels = chars[tableOffset].getElementsByTagName('td');
let targetPixels = currentContainer.getElementsByTagName('td');
for(let i=0; i < srcPixels.length; i++) {
// First tds are in the th row, for editing actions. Skip them
if (targetPixels[i].parentNode.localName === 'th') continue; // In case we give them css at some point...
targetPixels[i].className = srcPixels[i].className;
}
break;
default:
// no.
}
});
function shiftUp(container) {
var pixels = container.getElementsByTagName('td');
for(p = 0; p < pixels.length; p++) {
if(p < font.width*(font.height -1)) {
pixels[p].className = pixels[p + font.width].className;
} else {
pixels[p].className = '';
}
}
}
document.getElementById('chars').addEventListener('mouseover', function(e) {
let target = e.target;
let action = target.getAttribute('action');
if (action !== 'toggle' || e.buttons !== 1) return;
Font.togglePixel(target);
});
document.getElementById('chars').addEventListener('dragstart', function() {
return false;
});
document.getElementById('create').addEventListener('click', function() {
font = new Font();
font.addChar();
});
// parse a char array declaration for an existing font.
// parsing heavily relies on comments.
document.getElementById('parse').addEventListener('click', function() {
if (document.getElementById('chars').childElementCount) {
let result = confirm("Confirm you want to overwrite the existing grids ?");
if (!result) return;
}
let lines = document.getElementById('inputText').value.split('\n');
let name = '';
let height = 0;
let width = 0;
let firstCharCode = 0;
let jumpTable = [];
let charData = [];
let readingJumpTable = false;
let readingData = false;
for(let l = 0 ; l < lines.length; l++) {
// TODO ? keep C compilation directives to copy them (not lowercased :)) to newly generated char array
let line = lines[l].trim();
//alert(line);
let fields;
// Declaration line: grab the name
if (line.indexOf('PROGMEM') > 0) {
fields = line.split(' ');
name = fields[2].replace(/[\[\]]/g, '');
continue;
}
line = line.toLowerCase();
// Todo: could rely on line order...
// width line: grab the width
if (line.indexOf('width') > 0) {
fields = line.split(',');
width = fields[0];
continue;
}
// height line: grab the height
if (line.indexOf('height') > 0) {
fields = line.split(',');
height = fields[0];
continue;
}
// first char code line: grab the first char code
if (line.indexOf('first char') > 0) {
fields = line.split(',');
firstCharCode = fields[0];
continue;
}
// End of array declaration
// TODO warn if more lines: next fonts are ignored
if (line.indexOf('};') === 0) {
break;
}
if (readingJumpTable || readingData) {
if (line.indexOf('#') !== 0 && line.length !== 0 && line.indexOf('//') !== 0) {
line = line.replace(/\/\/.*/, ''); // get rid of end of line comments
fields = line.split(',');
let newEntry = [];
for(let f=0; f < fields.length; f++) {
let value = parseInt(fields[f]);
if (isNaN(value)) continue;
if (readingData) {
charData.push(value);
}
if (readingJumpTable) {
newEntry.push(value);
}
}
if (readingJumpTable) {
jumpTable.push(newEntry);
}
}
}
// Begining of data
if (line.indexOf('font data') > 0) {
readingData = true;
readingJumpTable = false;
}
// Begining of jump table
if (line.indexOf('jump table') > 0) {
readingJumpTable = true;
}
}
if (!name || !height || !width || !firstCharCode) {
alert("Does not look like a parsable font. Try again.");
return;
}
Font.emptyChars();
Font.emptyOutput();
document.getElementById('name').value = name;
document.getElementById('height').value = parseInt(height);
document.getElementById('width').value = parseInt(width);
document.getElementById('code').value = parseInt(firstCharCode);
font = new Font();
for(let c = 0 ; c < jumpTable.length; c++) {
let jumpEntry = jumpTable[c];
let charEntry = [];
// displayable character
if (jumpEntry[0] !== 255 || jumpEntry[1] !== 255) {
charEntry = charData.splice(0, jumpEntry[2]);
}
font.addChar(jumpEntry, charEntry);
}
document.body.className = "started";
});
})();
</script>
</body>
</html>

BIN
resources/glyphEditor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
resources/xbmPreview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

1082
src/OLEDDisplay.cpp Normal file

File diff suppressed because it is too large Load Diff

392
src/OLEDDisplay.h Normal file
View File

@ -0,0 +1,392 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
* Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef OLEDDISPLAY_h
#define OLEDDISPLAY_h
#ifdef ARDUINO
#include <Arduino.h>
#elif __MBED__
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#include <mbed.h>
#define delay(x) wait_ms(x)
#define yield() void()
/*
* This is a little Arduino String emulation to keep the OLEDDisplay
* library code in common between Arduino and mbed-os
*/
class String {
public:
String(const char *s) { _str = s; };
int length() { return strlen(_str); };
const char *c_str() { return _str; };
void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const {
memcpy(buf, _str + index, std::min(bufsize, strlen(_str)));
};
private:
const char *_str;
};
#else
#error "Unkown operating system"
#endif
#include "OLEDDisplayFonts.h"
//#define DEBUG_OLEDDISPLAY(...) Serial.printf( __VA_ARGS__ )
//#define DEBUG_OLEDDISPLAY(...) dprintf("%s", __VA_ARGS__ )
#ifndef DEBUG_OLEDDISPLAY
#define DEBUG_OLEDDISPLAY(...)
#endif
// Use DOUBLE BUFFERING by default
#ifndef OLEDDISPLAY_REDUCE_MEMORY
#define OLEDDISPLAY_DOUBLE_BUFFER
#endif
// Header Values
#define JUMPTABLE_BYTES 4
#define JUMPTABLE_LSB 1
#define JUMPTABLE_SIZE 2
#define JUMPTABLE_WIDTH 3
#define JUMPTABLE_START 4
#define WIDTH_POS 0
#define HEIGHT_POS 1
#define FIRST_CHAR_POS 2
#define CHAR_NUM_POS 3
// Display commands
#define CHARGEPUMP 0x8D
#define COLUMNADDR 0x21
#define COMSCANDEC 0xC8
#define COMSCANINC 0xC0
#define DISPLAYALLON 0xA5
#define DISPLAYALLON_RESUME 0xA4
#define DISPLAYOFF 0xAE
#define DISPLAYON 0xAF
#define EXTERNALVCC 0x1
#define INVERTDISPLAY 0xA7
#define MEMORYMODE 0x20
#define NORMALDISPLAY 0xA6
#define PAGEADDR 0x22
#define SEGREMAP 0xA0
#define SETCOMPINS 0xDA
#define SETCONTRAST 0x81
#define SETDISPLAYCLOCKDIV 0xD5
#define SETDISPLAYOFFSET 0xD3
#define SETHIGHCOLUMN 0x10
#define SETLOWCOLUMN 0x00
#define SETMULTIPLEX 0xA8
#define SETPRECHARGE 0xD9
#define SETSEGMENTREMAP 0xA1
#define SETSTARTLINE 0x40
#define SETVCOMDETECT 0xDB
#define SWITCHCAPVCC 0x2
#ifndef _swap_int16_t
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
#endif
enum OLEDDISPLAY_COLOR {
BLACK = 0,
WHITE = 1,
INVERSE = 2
};
enum OLEDDISPLAY_TEXT_ALIGNMENT {
TEXT_ALIGN_LEFT = 0,
TEXT_ALIGN_RIGHT = 1,
TEXT_ALIGN_CENTER = 2,
TEXT_ALIGN_CENTER_BOTH = 3
};
enum OLEDDISPLAY_GEOMETRY {
GEOMETRY_128_64 = 0,
GEOMETRY_128_32,
GEOMETRY_RAWMODE,
};
enum HW_I2C {
I2C_ONE,
I2C_TWO
};
typedef char (*FontTableLookupFunction)(const uint8_t ch);
char DefaultFontTableLookup(const uint8_t ch);
#ifdef ARDUINO
class OLEDDisplay : public Print {
#elif __MBED__
class OLEDDisplay : public Stream {
#else
#error "Unkown operating system"
#endif
public:
OLEDDisplay();
virtual ~OLEDDisplay();
uint16_t width(void) const { return displayWidth; };
uint16_t height(void) const { return displayHeight; };
uint16_t getBufferSize(void) const { return displayBufferSize; };
// Use this to resume after a deep sleep without resetting the display (what init() would do).
// Returns true if connection to the display was established and the buffer allocated, false otherwise.
bool allocateBuffer();
// Allocates the buffer and initializes the driver & display. Resets the display!
// Returns false if buffer allocation failed, true otherwise.
bool init();
// Free the memory used by the display
void end();
// Cycle through the initialization
void resetDisplay(void);
/* Drawing functions */
// Sets the color of all pixel operations
void setColor(OLEDDISPLAY_COLOR color);
// Returns the current color.
OLEDDISPLAY_COLOR getColor();
// Draw a pixel at given position
void setPixel(int16_t x, int16_t y);
// Draw a pixel at given position and color
void setPixelColor(int16_t x, int16_t y, OLEDDISPLAY_COLOR color);
// Clear a pixel at given position FIXME: INVERSE is untested with this function
void clearPixel(int16_t x, int16_t y);
// Draw a line from position 0 to position 1
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1);
// Draw the border of a rectangle at the given location
void drawRect(int16_t x, int16_t y, int16_t width, int16_t height);
// Draw a rounded rectangle border at the given location
void drawRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r);
// Fill the rectangle
void fillRect(int16_t x, int16_t y, int16_t width, int16_t height);
// Fll rounded rectangle
void fillRoundRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r);
// Draw the border of a circle
void drawCircle(int16_t x, int16_t y, int16_t radius);
// Draw all Quadrants specified in the quads bit mask
void drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads);
// Fill circle
void fillCircle(int16_t x, int16_t y, int16_t radius);
// Draw a line horizontally
void drawHorizontalLine(int16_t x, int16_t y, int16_t length);
// Draw a line vertically
void drawVerticalLine(int16_t x, int16_t y, int16_t length);
// Draws a rounded progress bar with the outer dimensions given by width and height. Progress is
// a unsigned byte value between 0 and 100
void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress);
// Draw a bitmap in the internal image format
void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *image);
// Draw a XBM
void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *xbm);
// Draw icon 16x16 xbm format
void drawIco16x16(int16_t x, int16_t y, const char *ico, bool inverse = false);
/* Text functions */
// Draws a string at the given location
void drawString(int16_t x, int16_t y, const String& text);
// Draws a String with a maximum width at the given location.
// If the given String is wider than the specified width
// The text will be wrapped to the next line at a space or dash
void drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, const String& text);
// Returns the width of the const char* with the current
// font settings
uint16_t getStringWidth(const char* text, uint16_t length);
// Convencience method for the const char version
uint16_t getStringWidth(const String& text);
uint16_t getTextHeight(void);
// Specifies relative to which anchor point
// the text is rendered. Available constants:
// TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH
void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment);
// Sets the current font. Available default fonts
// ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24
void setFont(const uint8_t *fontData);
// Set the function that will convert utf-8 to font table index
void setFontTableLookupFunction(FontTableLookupFunction function);
// Dither an screen area at the given location
void ditherArea(int16_t x, int16_t y, int16_t w, int16_t h);
/* Display functions */
// Turn the display on
void displayOn(void);
// Turn the display offs
void displayOff(void);
// Inverted display mode
void invertDisplay(void);
// Normal display mode
void normalDisplay(void);
// Set display contrast
// really low brightness & contrast: contrast = 10, precharge = 5, comdetect = 0
// normal brightness & contrast: contrast = 100
void setContrast(uint8_t contrast, uint8_t precharge = 241, uint8_t comdetect = 64);
// Convenience method to access
void setBrightness(uint8_t);
// Reset display rotation or mirroring
void resetOrientation();
// Turn the display upside down
void flipScreenVertically();
// Mirror the display (to be used in a mirror or as a projector)
void mirrorScreen();
// Write the buffer to the display memory
virtual void display(void) = 0;
// Clear the local pixel buffer
void clear(void);
// Log buffer implementation
// This will define the lines and characters you can
// print to the screen. When you exeed the buffer size (lines * chars)
// the output may be truncated due to the size constraint.
bool setLogBuffer(uint16_t lines, uint16_t chars);
// Draw the log buffer at position (x, y)
void drawLogBuffer(uint16_t x, uint16_t y);
// Get screen geometry
uint16_t getWidth(void);
uint16_t getHeight(void);
// Implement needed function to be compatible with Print class
size_t write(uint8_t c);
size_t write(const char* s);
// Implement needed function to be compatible with Stream class
#ifdef __MBED__
int _putc(int c);
int _getc() { return -1; };
#endif
uint8_t *buffer;
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
uint8_t *buffer_back;
#endif
protected:
OLEDDISPLAY_GEOMETRY geometry;
uint16_t displayWidth;
uint16_t displayHeight;
uint16_t displayBufferSize;
// Set the correct height, width and buffer for the geometry
void setGeometry(OLEDDISPLAY_GEOMETRY g, uint16_t width = 0, uint16_t height = 0);
OLEDDISPLAY_TEXT_ALIGNMENT textAlignment;
OLEDDISPLAY_COLOR color;
const uint8_t *fontData;
// State values for logBuffer
uint16_t logBufferSize;
uint16_t logBufferFilled;
uint16_t logBufferLine;
uint16_t logBufferMaxLines;
char *logBuffer;
// the header size of the buffer used, e.g. for the SPI command header
virtual int getBufferOffset(void) = 0;
// Send a command to the display (low level function)
virtual void sendCommand(uint8_t com) {(void)com;};
// Connect to the display
virtual bool connect() { return false; };
// Send all the init commands
void sendInitCommands();
// converts utf8 characters to extended ascii
char* utf8ascii(const String& s);
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));
void drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth);
void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t corners, int16_t delta);
FontTableLookupFunction fontTableLookupFunction;
};
#endif

1272
src/OLEDDisplayFonts.cpp Normal file

File diff suppressed because it is too large Load Diff

14
src/OLEDDisplayFonts.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef OLEDDISPLAYFONTS_h
#define OLEDDISPLAYFONTS_h
#include <Arduino.h>
#ifdef __MBED__
# define PROGMEM
#endif
extern const uint8_t ArialMT_Plain_10[] PROGMEM;
extern const uint8_t ArialMT_Plain_16[] PROGMEM;
extern const uint8_t ArialMT_Plain_24[] PROGMEM;
#endif

501
src/OLEDDisplayUi.cpp Normal file
View File

@ -0,0 +1,501 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
* Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#include "OLEDDisplayUi.h"
void LoadingDrawDefault(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) {
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_10);
display->drawString(64, 18, stage->process);
display->drawProgressBar(4, 32, 120, 8, progress);
};
OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) {
this->display = display;
indicatorPosition = BOTTOM;
indicatorDirection = LEFT_RIGHT;
activeSymbol = ANIMATION_activeSymbol;
inactiveSymbol = ANIMATION_inactiveSymbol;
frameAnimationDirection = SLIDE_RIGHT;
lastTransitionDirection = 1;
frameCount = 0;
nextFrameNumber = -1;
overlayCount = 0;
indicatorDrawState = 1;
loadingDrawFunction = LoadingDrawDefault;
updateInterval = 33;
state.lastUpdate = 0;
state.ticksSinceLastStateSwitch = 0;
state.frameState = FIXED;
state.currentFrame = 0;
state.frameTransitionDirection = 1;
state.isIndicatorDrawen = true;
state.manuelControll = false;
state.userData = NULL;
shouldDrawIndicators = true;
autoTransition = true;
setTimePerFrame(5000);
setTimePerTransition(500);
}
OLEDDisplayUi::OLEDDisplayUi() {
this->display = nullptr;
indicatorPosition = BOTTOM;
indicatorDirection = LEFT_RIGHT;
activeSymbol = ANIMATION_activeSymbol;
inactiveSymbol = ANIMATION_inactiveSymbol;
frameAnimationDirection = SLIDE_RIGHT;
lastTransitionDirection = 1;
frameCount = 0;
nextFrameNumber = -1;
overlayCount = 0;
indicatorDrawState = 1;
loadingDrawFunction = LoadingDrawDefault;
updateInterval = 33;
state.lastUpdate = 0;
state.ticksSinceLastStateSwitch = 0;
state.frameState = FIXED;
state.currentFrame = 0;
state.frameTransitionDirection = 1;
state.isIndicatorDrawen = true;
state.manuelControll = false;
state.userData = NULL;
shouldDrawIndicators = true;
autoTransition = true;
setTimePerFrame(5000);
setTimePerTransition(500);
}
void OLEDDisplayUi::init() {
// this->display->init();
}
void OLEDDisplayUi::init(OLEDDisplay *display) {
this->display = display;
}
void OLEDDisplayUi::setTargetFPS(uint8_t fps){
this->updateInterval = ((float) 1.0 / (float) fps) * 1000;
this->ticksPerFrame = timePerFrame / updateInterval;
this->ticksPerTransition = timePerTransition / updateInterval;
}
// -/------ Automatic controll ------\-
void OLEDDisplayUi::enableAutoTransition(){
this->autoTransition = true;
}
void OLEDDisplayUi::disableAutoTransition(){
this->autoTransition = false;
}
void OLEDDisplayUi::setAutoTransitionForwards(){
this->state.frameTransitionDirection = 1;
this->lastTransitionDirection = 1;
}
void OLEDDisplayUi::setAutoTransitionBackwards(){
this->state.frameTransitionDirection = -1;
this->lastTransitionDirection = -1;
}
void OLEDDisplayUi::setTimePerFrame(uint16_t time){
this->timePerFrame = time;
this->ticksPerFrame = timePerFrame / updateInterval;
}
void OLEDDisplayUi::setTimePerTransition(uint16_t time){
this->timePerTransition = time;
this->ticksPerTransition = timePerTransition / updateInterval;
}
// -/------ Customize indicator position and style -------\-
void OLEDDisplayUi::enableIndicator(){
this->state.isIndicatorDrawen = true;
}
void OLEDDisplayUi::disableIndicator(){
this->state.isIndicatorDrawen = false;
}
void OLEDDisplayUi::enableAllIndicators(){
this->shouldDrawIndicators = true;
}
void OLEDDisplayUi::disableAllIndicators(){
this->shouldDrawIndicators = false;
}
void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) {
this->indicatorPosition = pos;
}
void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) {
this->indicatorDirection = dir;
}
void OLEDDisplayUi::setActiveSymbol(const uint8_t* symbol) {
this->activeSymbol = symbol;
}
void OLEDDisplayUi::setInactiveSymbol(const uint8_t* symbol) {
this->inactiveSymbol = symbol;
}
// -/----- Frame settings -----\-
void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) {
this->frameAnimationDirection = dir;
}
void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) {
this->frameFunctions = frameFunctions;
this->frameCount = frameCount;
this->resetState();
}
// -/----- Overlays ------\-
void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){
this->overlayFunctions = overlayFunctions;
this->overlayCount = overlayCount;
}
// -/----- Loading Process -----\-
void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) {
this->loadingDrawFunction = loadingDrawFunction;
}
void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) {
uint8_t progress = 0;
uint8_t increment = 100 / stagesCount;
for (uint8_t i = 0; i < stagesCount; i++) {
display->clear();
this->loadingDrawFunction(this->display, &stages[i], progress);
display->display();
stages[i].callback();
progress += increment;
yield();
}
display->clear();
this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress);
display->display();
delay(150);
}
// -/----- Manuel control -----\-
void OLEDDisplayUi::nextFrame() {
if (this->state.frameState != IN_TRANSITION) {
this->state.manuelControll = true;
this->state.frameState = IN_TRANSITION;
this->state.ticksSinceLastStateSwitch = 0;
this->lastTransitionDirection = this->state.frameTransitionDirection;
this->state.frameTransitionDirection = 1;
}
}
void OLEDDisplayUi::previousFrame() {
if (this->state.frameState != IN_TRANSITION) {
this->state.manuelControll = true;
this->state.frameState = IN_TRANSITION;
this->state.ticksSinceLastStateSwitch = 0;
this->lastTransitionDirection = this->state.frameTransitionDirection;
this->state.frameTransitionDirection = -1;
}
}
void OLEDDisplayUi::switchToFrame(uint8_t frame) {
if (frame >= this->frameCount) return;
this->state.ticksSinceLastStateSwitch = 0;
if (frame == this->state.currentFrame) return;
this->state.frameState = FIXED;
this->state.currentFrame = frame;
this->state.isIndicatorDrawen = true;
}
void OLEDDisplayUi::transitionToFrame(uint8_t frame) {
if (frame >= this->frameCount) return;
this->state.ticksSinceLastStateSwitch = 0;
if (frame == this->state.currentFrame) return;
this->nextFrameNumber = frame;
this->lastTransitionDirection = this->state.frameTransitionDirection;
this->state.manuelControll = true;
this->state.frameState = IN_TRANSITION;
this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1;
}
// -/----- State information -----\-
OLEDDisplayUiState* OLEDDisplayUi::getUiState(){
return &this->state;
}
int16_t OLEDDisplayUi::update(){
#ifdef ARDUINO
unsigned long frameStart = millis();
#elif __MBED__
Timer t;
t.start();
unsigned long frameStart = t.read_ms();
#else
#error "Unkown operating system"
#endif
int32_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate);
if ( timeBudget <= 0) {
// Implement frame skipping to ensure time budget is keept
if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil((double)-timeBudget / (double)this->updateInterval);
this->state.lastUpdate = frameStart;
this->tick();
}
#ifdef ARDUINO
return this->updateInterval - (millis() - frameStart);
#elif __MBED__
return this->updateInterval - (t.read_ms() - frameStart);
#else
#error "Unkown operating system"
#endif
}
void OLEDDisplayUi::tick() {
this->state.ticksSinceLastStateSwitch++;
switch (this->state.frameState) {
case IN_TRANSITION:
if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){
this->state.frameState = FIXED;
this->state.currentFrame = getNextFrameNumber();
this->state.ticksSinceLastStateSwitch = 0;
this->nextFrameNumber = -1;
}
break;
case FIXED:
// Revert manuelControll
if (this->state.manuelControll) {
this->state.frameTransitionDirection = this->lastTransitionDirection;
this->state.manuelControll = false;
}
if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){
if (this->autoTransition){
this->state.frameState = IN_TRANSITION;
}
this->state.ticksSinceLastStateSwitch = 0;
}
break;
}
this->display->clear();
this->drawFrame();
if (shouldDrawIndicators) {
this->drawIndicator();
}
this->drawOverlays();
this->display->display();
}
void OLEDDisplayUi::resetState() {
this->state.lastUpdate = 0;
this->state.ticksSinceLastStateSwitch = 0;
this->state.frameState = FIXED;
this->state.currentFrame = 0;
this->state.isIndicatorDrawen = true;
}
void OLEDDisplayUi::drawFrame(){
switch (this->state.frameState){
case IN_TRANSITION: {
float progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition;
int16_t x = 0, y = 0, x1 = 0, y1 = 0;
switch(this->frameAnimationDirection){
case SLIDE_LEFT:
x = -this->display->width() * progress;
y = 0;
x1 = x + this->display->width();
y1 = 0;
break;
case SLIDE_RIGHT:
x = this->display->width() * progress;
y = 0;
x1 = x - this->display->width();
y1 = 0;
break;
case SLIDE_UP:
x = 0;
y = -this->display->height() * progress;
x1 = 0;
y1 = y + this->display->height();
break;
case SLIDE_DOWN:
default:
x = 0;
y = this->display->height() * progress;
x1 = 0;
y1 = y - this->display->height();
break;
}
// Invert animation if direction is reversed.
int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1;
x *= dir; y *= dir; x1 *= dir; y1 *= dir;
bool drawenCurrentFrame;
// Prope each frameFunction for the indicator Drawen state
this->enableIndicator();
(this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y);
drawenCurrentFrame = this->state.isIndicatorDrawen;
this->enableIndicator();
(this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1);
// Build up the indicatorDrawState
if (drawenCurrentFrame && !this->state.isIndicatorDrawen) {
// Drawen now but not next
this->indicatorDrawState = 2;
} else if (!drawenCurrentFrame && this->state.isIndicatorDrawen) {
// Not drawen now but next
this->indicatorDrawState = 1;
} else if (!drawenCurrentFrame && !this->state.isIndicatorDrawen) {
// Not drawen in both frames
this->indicatorDrawState = 3;
}
// If the indicator isn't draw in the current frame
// reflect it in state.isIndicatorDrawen
if (!drawenCurrentFrame) this->state.isIndicatorDrawen = false;
break;
}
case FIXED:
// Always assume that the indicator is drawn!
// And set indicatorDrawState to "not known yet"
this->indicatorDrawState = 0;
this->enableIndicator();
(this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0);
break;
}
}
void OLEDDisplayUi::drawIndicator() {
// Only draw if the indicator is invisible
// for both frames or
// the indiactor is shown and we are IN_TRANSITION
if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawen && this->state.frameState != IN_TRANSITION)) {
return;
}
uint8_t posOfHighlightFrame = 0;
float indicatorFadeProgress = 0;
// if the indicator needs to be slided in we want to
// highlight the next frame in the transition
uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame;
// Calculate the frame that needs to be highlighted
// based on the Direction the indiactor is drawn
switch (this->indicatorDirection){
case LEFT_RIGHT:
posOfHighlightFrame = frameToHighlight;
break;
case RIGHT_LEFT:
default:
posOfHighlightFrame = this->frameCount - frameToHighlight;
break;
}
switch (this->indicatorDrawState) {
case 1: // Indicator was not drawn in this frame but will be in next
// Slide IN
indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition);
break;
case 2: // Indicator was drawn in this frame but not in next
// Slide OUT
indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition);
break;
}
//Space between indicators - reduce for small screen sizes
uint16_t indicatorSpacing = 12;
if (this->display->getHeight() < 64 && (this->indicatorPosition == RIGHT || this->indicatorPosition == LEFT)) {
indicatorSpacing = 6;
}
uint16_t frameStartPos = (indicatorSpacing * frameCount / 2);
const uint8_t *image;
uint16_t x = 0,y = 0;
for (uint8_t i = 0; i < this->frameCount; i++) {
switch (this->indicatorPosition){
case TOP:
y = 0 - (8 * indicatorFadeProgress);
x = (this->display->width() / 2) - frameStartPos + 12 * i;
break;
case BOTTOM:
y = (this->display->height() - 8) + (8 * indicatorFadeProgress);
x = (this->display->width() / 2) - frameStartPos + 12 * i;
break;
case RIGHT:
x = (this->display->width() - 8) + (8 * indicatorFadeProgress);
y = (this->display->height() / 2) - frameStartPos + 2 + 12 * i;
break;
case LEFT:
default:
x = 0 - (8 * indicatorFadeProgress);
y = (this->display->height() / 2) - frameStartPos + 2 + indicatorSpacing * i;
break;
}
if (posOfHighlightFrame == i) {
image = this->activeSymbol;
} else {
image = this->inactiveSymbol;
}
this->display->drawFastImage(x, y, 8, 8, image);
}
}
void OLEDDisplayUi::drawOverlays() {
for (uint8_t i=0;i<this->overlayCount;i++){
(this->overlayFunctions[i])(this->display, &this->state);
}
}
uint8_t OLEDDisplayUi::getNextFrameNumber(){
if (this->nextFrameNumber != -1) return this->nextFrameNumber;
return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount;
}

317
src/OLEDDisplayUi.h Normal file
View File

@ -0,0 +1,317 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
* Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef OLEDDISPLAYUI_h
#define OLEDDISPLAYUI_h
#ifdef ARDUINO
#include <Arduino.h>
#elif __MBED__
#include <mbed.h>
#else
#error "Unkown operating system"
#endif
#include "OLEDDisplay.h"
//#define DEBUG_OLEDDISPLAYUI(...) Serial.printf( __VA_ARGS__ )
#ifndef DEBUG_OLEDDISPLAYUI
#define DEBUG_OLEDDISPLAYUI(...)
#endif
enum AnimationDirection {
SLIDE_UP,
SLIDE_DOWN,
SLIDE_LEFT,
SLIDE_RIGHT
};
enum IndicatorPosition {
TOP,
RIGHT,
BOTTOM,
LEFT
};
enum IndicatorDirection {
LEFT_RIGHT,
RIGHT_LEFT
};
enum FrameState {
IN_TRANSITION,
FIXED
};
const uint8_t ANIMATION_activeSymbol[] PROGMEM = {
0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00
};
const uint8_t ANIMATION_inactiveSymbol[] PROGMEM = {
0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00
};
// Structure of the UiState
struct OLEDDisplayUiState {
uint64_t lastUpdate;
uint16_t ticksSinceLastStateSwitch;
FrameState frameState;
uint8_t currentFrame;
bool isIndicatorDrawen;
// Normal = 1, Inverse = -1;
int8_t frameTransitionDirection;
bool manuelControll;
// Custom data that can be used by the user
void* userData;
};
struct LoadingStage {
const char* process;
void (*callback)();
};
typedef void (*FrameCallback)(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
typedef void (*OverlayCallback)(OLEDDisplay *display, OLEDDisplayUiState* state);
typedef void (*LoadingDrawFunction)(OLEDDisplay *display, LoadingStage* stage, uint8_t progress);
class OLEDDisplayUi {
private:
OLEDDisplay *display;
// Symbols for the Indicator
IndicatorPosition indicatorPosition;
IndicatorDirection indicatorDirection;
const uint8_t* activeSymbol;
const uint8_t* inactiveSymbol;
bool shouldDrawIndicators;
// Values for the Frames
AnimationDirection frameAnimationDirection;
int8_t lastTransitionDirection;
uint16_t ticksPerFrame; // ~ 5000ms at 30 FPS
uint16_t ticksPerTransition; // ~ 500ms at 30 FPS
bool autoTransition;
FrameCallback* frameFunctions;
uint8_t frameCount;
// Internally used to transition to a specific frame
int8_t nextFrameNumber;
// Values for Overlays
OverlayCallback* overlayFunctions;
uint8_t overlayCount;
// Will the Indicator be drawen
// 3 Not drawn in both frames
// 2 Drawn this frame but not next
// 1 Not drown this frame but next
// 0 Not known yet
uint8_t indicatorDrawState;
// Loading screen
LoadingDrawFunction loadingDrawFunction;
// UI State
OLEDDisplayUiState state;
// Bookeeping for update
uint16_t updateInterval = 33;
uint16_t timePerFrame;
uint16_t timePerTransition;
uint8_t getNextFrameNumber();
void drawIndicator();
void drawFrame();
void drawOverlays();
void tick();
void resetState();
public:
OLEDDisplayUi(OLEDDisplay *display);
OLEDDisplayUi();
/**
* Initialise the display
*/
void init();
void init(OLEDDisplay *display);
/**
* Configure the internal used target FPS
*/
void setTargetFPS(uint8_t fps);
// Automatic Controll
/**
* Enable automatic transition to next frame after the some time can be configured with `setTimePerFrame` and `setTimePerTransition`.
*/
void enableAutoTransition();
/**
* Disable automatic transition to next frame.
*/
void disableAutoTransition();
/**
* Set the direction if the automatic transitioning
*/
void setAutoTransitionForwards();
void setAutoTransitionBackwards();
/**
* Set the approx. time a frame is displayed
*/
void setTimePerFrame(uint16_t time);
/**
* Set the approx. time a transition will take
*/
void setTimePerTransition(uint16_t time);
// Customize indicator position and style
/**
* Draw the indicator.
* This is the defaut state for all frames if
* the indicator was hidden on the previous frame
* it will be slided in.
*/
void enableIndicator();
/**
* Don't draw the indicator.
* This will slide out the indicator
* when transitioning to the next frame.
*/
void disableIndicator();
/**
* Enable drawing of indicators
*/
void enableAllIndicators();
/**
* Disable draw of indicators.
*/
void disableAllIndicators();
/**
* Set the position of the indicator bar.
*/
void setIndicatorPosition(IndicatorPosition pos);
/**
* Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING
*/
void setIndicatorDirection(IndicatorDirection dir);
/**
* Set the symbol to indicate an active frame in the indicator bar.
*/
void setActiveSymbol(const uint8_t* symbol);
/**
* Set the symbol to indicate an inactive frame in the indicator bar.
*/
void setInactiveSymbol(const uint8_t* symbol);
// Frame settings
/**
* Configure what animation is used to transition from one frame to another
*/
void setFrameAnimation(AnimationDirection dir);
/**
* Add frame drawing functions
*/
void setFrames(FrameCallback* frameFunctions, uint8_t frameCount);
// Overlay
/**
* Add overlays drawing functions that are draw independent of the Frames
*/
void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount);
// Loading animation
/**
* Set the function that will draw each step
* in the loading animation
*/
void setLoadingDrawFunction(LoadingDrawFunction loadingFunction);
/**
* Run the loading process
*/
void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount);
// Manual Control
void nextFrame();
void previousFrame();
/**
* Switch without transition to frame `frame`.
*/
void switchToFrame(uint8_t frame);
/**
* Transition to frame `frame`, when the `frame` number is bigger than the current
* frame the forward animation will be used, otherwise the backwards animation is used.
*/
void transitionToFrame(uint8_t frame);
// State Info
OLEDDisplayUiState* getUiState();
int16_t update();
};
#endif

39
src/SH1106.h Normal file
View File

@ -0,0 +1,39 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef SH1106_h
#define SH1106_h
#include "SH1106Wire.h"
// For make SH1106 an alias for SH1106Wire
typedef SH1106Wire SH1106;
#endif

141
src/SH1106Brzo.h Normal file
View File

@ -0,0 +1,141 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef SH1106Brzo_h
#define SH1106Brzo_h
#include "OLEDDisplay.h"
#include <brzo_i2c.h>
#if F_CPU == 160000000L
#define BRZO_I2C_SPEED 1000
#else
#define BRZO_I2C_SPEED 800
#endif
class SH1106Brzo : public OLEDDisplay {
private:
uint8_t _address;
uint8_t _sda;
uint8_t _scl;
public:
SH1106Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) {
setGeometry(g);
this->_address = _address;
this->_sda = _sda;
this->_scl = _scl;
}
bool connect(){
brzo_i2c_setup(_sda, _scl, 0);
return true;
}
void display(void) {
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
uint8_t minBoundY = UINT8_MAX;
uint8_t maxBoundY = 0;
uint8_t minBoundX = UINT8_MAX;
uint8_t maxBoundX = 0;
uint8_t x, y;
// Calculate the Y bounding box of changes
// and copy buffer[pos] to buffer_back[pos];
for (y = 0; y < (displayHeight / 8); y++) {
for (x = 0; x < displayWidth; x++) {
uint16_t pos = x + y * displayWidth;
if (buffer[pos] != buffer_back[pos]) {
minBoundY = _min(minBoundY, y);
maxBoundY = _max(maxBoundY, y);
minBoundX = _min(minBoundX, x);
maxBoundX = _max(maxBoundX, x);
}
buffer_back[pos] = buffer[pos];
}
yield();
}
// If the minBoundY wasn't updated
// we can savely assume that buffer_back[pos] == buffer[pos]
// holdes true for all values of pos
if (minBoundY == UINT8_MAX) return;
byte k = 0;
uint8_t sendBuffer[17];
sendBuffer[0] = 0x40;
// Calculate the colum offset
uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F;
uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 );
brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED);
for (y = minBoundY; y <= maxBoundY; y++) {
sendCommand(0xB0 + y);
sendCommand(minBoundXp2H);
sendCommand(minBoundXp2L);
for (x = minBoundX; x <= maxBoundX; x++) {
k++;
sendBuffer[k] = buffer[x + y * displayWidth];
if (k == 16) {
brzo_i2c_write(sendBuffer, 17, true);
k = 0;
}
}
if (k != 0) {
brzo_i2c_write(sendBuffer, k + 1, true);
k = 0;
}
yield();
}
if (k != 0) {
brzo_i2c_write(sendBuffer, k + 1, true);
}
brzo_i2c_end_transaction();
#else
#endif
}
private:
int getBufferOffset(void) {
return 0;
}
inline void sendCommand(uint8_t com) __attribute__((always_inline)){
uint8_t command[2] = {0x80 /* command mode */, com};
brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED);
brzo_i2c_write(command, 2, true);
brzo_i2c_end_transaction();
}
};
#endif

135
src/SH1106Spi.h Normal file
View File

@ -0,0 +1,135 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef SH1106Spi_h
#define SH1106Spi_h
#include "OLEDDisplay.h"
#include <SPI.h>
class SH1106Spi : public OLEDDisplay {
private:
uint8_t _rst;
uint8_t _dc;
public:
SH1106Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) {
setGeometry(g);
this->_rst = _rst;
this->_dc = _dc;
}
bool connect(){
pinMode(_dc, OUTPUT);
pinMode(_rst, OUTPUT);
SPI.begin ();
SPI.setClockDivider (SPI_CLOCK_DIV2);
// Pulse Reset low for 10ms
digitalWrite(_rst, HIGH);
delay(1);
digitalWrite(_rst, LOW);
delay(10);
digitalWrite(_rst, HIGH);
return true;
}
void display(void) {
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
uint8_t minBoundY = UINT8_MAX;
uint8_t maxBoundY = 0;
uint8_t minBoundX = UINT8_MAX;
uint8_t maxBoundX = 0;
uint8_t x, y;
// Calculate the Y bounding box of changes
// and copy buffer[pos] to buffer_back[pos];
for (y = 0; y < (displayHeight / 8); y++) {
for (x = 0; x < displayWidth; x++) {
uint16_t pos = x + y * displayWidth;
if (buffer[pos] != buffer_back[pos]) {
minBoundY = _min(minBoundY, y);
maxBoundY = _max(maxBoundY, y);
minBoundX = _min(minBoundX, x);
maxBoundX = _max(maxBoundX, x);
}
buffer_back[pos] = buffer[pos];
}
yield();
}
// If the minBoundY wasn't updated
// we can savely assume that buffer_back[pos] == buffer[pos]
// holdes true for all values of pos
if (minBoundY == UINT8_MAX) return;
// Calculate the colum offset
uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F;
uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 );
for (y = minBoundY; y <= maxBoundY; y++) {
sendCommand(0xB0 + y);
sendCommand(minBoundXp2H);
sendCommand(minBoundXp2L);
digitalWrite(_dc, HIGH); // data mode
for (x = minBoundX; x <= maxBoundX; x++) {
SPI.transfer(buffer[x + y * displayWidth]);
}
yield();
}
#else
for (uint8_t y=0; y<displayHeight/8; y++) {
sendCommand(0xB0 + y);
sendCommand(0x02);
sendCommand(0x10);
digitalWrite(_dc, HIGH); // data mode
for( uint8_t x=0; x < displayWidth; x++) {
SPI.transfer(buffer[x + y * displayWidth]);
}
yield();
}
#endif
}
private:
int getBufferOffset(void) {
return 0;
}
inline void sendCommand(uint8_t com) __attribute__((always_inline)){
digitalWrite(_dc, LOW);
SPI.transfer(com);
}
};
#endif

205
src/SH1106Wire.h Normal file
View File

@ -0,0 +1,205 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef SH1106Wire_h
#define SH1106Wire_h
#include "OLEDDisplay.h"
#include <Wire.h>
#define SH1106_SET_PUMP_VOLTAGE 0X30
#define SH1106_SET_PUMP_MODE 0XAD
#define SH1106_PUMP_ON 0X8B
#define SH1106_PUMP_OFF 0X8A
//--------------------------------------
class SH1106Wire : public OLEDDisplay {
private:
uint8_t _address;
uint8_t _sda;
uint8_t _scl;
bool _doI2cAutoInit = false;
TwoWire* _wire = NULL;
int _frequency;
public:
/**
* Create and initialize the Display using Wire library
*
* Beware for retro-compatibility default values are provided for all parameters see below.
* Please note that if you don't wan't SD1306Wire to initialize and change frequency speed ot need to
* ensure -1 value are specified for all 3 parameters. This can be usefull to control TwoWire with multiple
* device on the same bus.
*
* @param _address I2C Display address
* @param _sda I2C SDA pin number, default to -1 to skip Wire begin call
* @param _scl I2C SCL pin number, default to -1 (only SDA = -1 is considered to skip Wire begin call)
* @param g display geometry dafault to generic GEOMETRY_128_64, see OLEDDISPLAY_GEOMETRY definition for other options
* @param _i2cBus on ESP32 with 2 I2C HW buses, I2C_ONE for 1st Bus, I2C_TWO fot 2nd bus, default I2C_ONE
* @param _frequency for Frequency by default Let's use ~700khz if ESP8266 is in 160Mhz mode, this will be limited to ~400khz if the ESP8266 in 80Mhz mode
*/
SH1106Wire(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64, HW_I2C _i2cBus = I2C_ONE, int _frequency = 700000) {
setGeometry(g);
this->_address = _address;
this->_sda = _sda;
this->_scl = _scl;
#if !defined(ARDUINO_ARCH_ESP32)
this->_wire = &Wire;
#else
this->_wire = (_i2cBus==I2C_ONE) ? &Wire : &Wire1;
#endif
this->_frequency = _frequency;
}
bool connect() {
#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH8266)
_wire->begin();
#else
// On ESP32 arduino, -1 means 'don't change pins', someone else has called begin for us.
if(this->_sda != -1)
_wire->begin(this->_sda, this->_scl);
#endif
// Let's use ~700khz if ESP8266 is in 160Mhz mode
// this will be limited to ~400khz if the ESP8266 in 80Mhz mode.
if(this->_frequency != -1)
_wire->setClock(this->_frequency);
return true;
}
void display(void) {
initI2cIfNeccesary();
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
uint8_t minBoundY = UINT8_MAX;
uint8_t maxBoundY = 0;
uint8_t minBoundX = UINT8_MAX;
uint8_t maxBoundX = 0;
uint8_t x, y;
// Calculate the Y bounding box of changes
// and copy buffer[pos] to buffer_back[pos];
for (y = 0; y < (displayHeight / 8); y++) {
for (x = 0; x < displayWidth; x++) {
uint16_t pos = x + y * displayWidth;
if (buffer[pos] != buffer_back[pos]) {
minBoundY = min(minBoundY, y);
maxBoundY = max(maxBoundY, y);
minBoundX = min(minBoundX, x);
maxBoundX = max(maxBoundX, x);
}
buffer_back[pos] = buffer[pos];
}
yield();
}
// If the minBoundY wasn't updated
// we can savely assume that buffer_back[pos] == buffer[pos]
// holdes true for all values of pos
if (minBoundY == UINT8_MAX) return;
// Calculate the colum offset
uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F;
uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 );
byte k = 0;
for (y = minBoundY; y <= maxBoundY; y++) {
sendCommand(0xB0 + y);
sendCommand(minBoundXp2H);
sendCommand(minBoundXp2L);
for (x = minBoundX; x <= maxBoundX; x++) {
if (k == 0) {
_wire->beginTransmission(_address);
_wire->write(0x40);
}
_wire->write(buffer[x + y * displayWidth]);
k++;
if (k == 16) {
_wire->endTransmission();
k = 0;
}
}
if (k != 0) {
_wire->endTransmission();
k = 0;
}
yield();
}
if (k != 0) {
_wire->endTransmission();
}
#else
uint8_t * p = &buffer[0];
for (uint8_t y=0; y<8; y++) {
sendCommand(0xB0+y);
sendCommand(0x02);
sendCommand(0x10);
for( uint8_t x=0; x<8; x++) {
_wire->beginTransmission(_address);
_wire->write(0x40);
for (uint8_t k = 0; k < 16; k++) {
_wire->write(*p++);
}
_wire->endTransmission();
}
}
#endif
}
void setI2cAutoInit(bool doI2cAutoInit) {
_doI2cAutoInit = doI2cAutoInit;
}
private:
int getBufferOffset(void) {
return 0;
}
inline void sendCommand(uint8_t command) __attribute__((always_inline)){
_wire->beginTransmission(_address);
_wire->write(0x80);
_wire->write(command);
_wire->endTransmission();
}
void initI2cIfNeccesary() {
if (_doI2cAutoInit) {
#ifdef ARDUINO_ARCH_AVR
_wire->begin();
#else
_wire->begin(this->_sda, this->_scl);
#endif
}
}
};
#endif

39
src/SSD1306.h Normal file
View File

@ -0,0 +1,39 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef SSD1306_h
#define SSD1306_h
#include "SSD1306Wire.h"
// For legacy support make SSD1306 an alias for SSD1306
typedef SSD1306Wire SSD1306;
#endif

162
src/SSD1306Brzo.h Normal file
View File

@ -0,0 +1,162 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef SSD1306Brzo_h
#define SSD1306Brzo_h
#include "OLEDDisplay.h"
#include <brzo_i2c.h>
#if F_CPU == 160000000L
#define BRZO_I2C_SPEED 1000
#else
#define BRZO_I2C_SPEED 800
#endif
class SSD1306Brzo : public OLEDDisplay {
private:
uint8_t _address;
uint8_t _sda;
uint8_t _scl;
public:
SSD1306Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) {
setGeometry(g);
this->_address = _address;
this->_sda = _sda;
this->_scl = _scl;
}
bool connect(){
brzo_i2c_setup(_sda, _scl, 0);
return true;
}
void display(void) {
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
uint8_t minBoundY = UINT8_MAX;
uint8_t maxBoundY = 0;
uint8_t minBoundX = UINT8_MAX;
uint8_t maxBoundX = 0;
uint8_t x, y;
// Calculate the Y bounding box of changes
// and copy buffer[pos] to buffer_back[pos];
for (y = 0; y < (displayHeight / 8); y++) {
for (x = 0; x < displayWidth; x++) {
uint16_t pos = x + y * displayWidth;
if (buffer[pos] != buffer_back[pos]) {
minBoundY = _min(minBoundY, y);
maxBoundY = _max(maxBoundY, y);
minBoundX = _min(minBoundX, x);
maxBoundX = _max(maxBoundX, x);
}
buffer_back[pos] = buffer[pos];
}
yield();
}
// If the minBoundY wasn't updated
// we can savely assume that buffer_back[pos] == buffer[pos]
// holdes true for all values of pos
if (minBoundY == UINT8_MAX) return;
sendCommand(COLUMNADDR);
sendCommand(minBoundX);
sendCommand(maxBoundX);
sendCommand(PAGEADDR);
sendCommand(minBoundY);
sendCommand(maxBoundY);
byte k = 0;
uint8_t sendBuffer[17];
sendBuffer[0] = 0x40;
brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED);
for (y = minBoundY; y <= maxBoundY; y++) {
for (x = minBoundX; x <= maxBoundX; x++) {
k++;
sendBuffer[k] = buffer[x + y * displayWidth];
if (k == 16) {
brzo_i2c_write(sendBuffer, 17, true);
k = 0;
}
}
yield();
}
brzo_i2c_write(sendBuffer, k + 1, true);
brzo_i2c_end_transaction();
#else
// No double buffering
sendCommand(COLUMNADDR);
sendCommand(0x0);
sendCommand(0x7F);
sendCommand(PAGEADDR);
sendCommand(0x0);
if (geometry == GEOMETRY_128_64) {
sendCommand(0x7);
} else if (geometry == GEOMETRY_128_32) {
sendCommand(0x3);
}
uint8_t sendBuffer[17];
sendBuffer[0] = 0x40;
brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED);
for (uint16_t i=0; i<displayBufferSize; i++) {
for (uint8_t x=1; x<17; x++) {
sendBuffer[x] = buffer[i];
i++;
}
i--;
brzo_i2c_write(sendBuffer, 17, true);
yield();
}
brzo_i2c_end_transaction();
#endif
}
private:
int getBufferOffset(void) {
return 0;
}
inline void sendCommand(uint8_t com) __attribute__((always_inline)){
uint8_t command[2] = {0x80 /* command mode */, com};
brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED);
brzo_i2c_write(command, 2, true);
brzo_i2c_end_transaction();
}
};
#endif

152
src/SSD1306I2C.h Normal file
View File

@ -0,0 +1,152 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2019 by Helmut Tschemernjak - www.radioshuttle.de
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef SSD1306I2C_h
#define SSD1306I2C_h
#ifdef __MBED__
#include "OLEDDisplay.h"
#include <mbed.h>
#ifndef UINT8_MAX
#define UINT8_MAX 0xff
#endif
class SSD1306I2C : public OLEDDisplay {
public:
SSD1306I2C(uint8_t _address, PinName _sda, PinName _scl, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) {
setGeometry(g);
this->_address = _address << 1; // convert from 7 to 8 bit for mbed.
this->_sda = _sda;
this->_scl = _scl;
_i2c = new I2C(_sda, _scl);
}
bool connect() {
// mbed supports 100k and 400k some device maybe 1000k
#ifdef TARGET_STM32L4
_i2c->frequency(1000000);
#else
_i2c->frequency(400000);
#endif
return true;
}
void display(void) {
const int x_offset = (128 - this->width()) / 2;
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
uint8_t minBoundY = UINT8_MAX;
uint8_t maxBoundY = 0;
uint8_t minBoundX = UINT8_MAX;
uint8_t maxBoundX = 0;
uint8_t x, y;
// Calculate the Y bounding box of changes
// and copy buffer[pos] to buffer_back[pos];
for (y = 0; y < (this->height() / 8); y++) {
for (x = 0; x < this->width(); x++) {
uint16_t pos = x + y * this->width();
if (buffer[pos] != buffer_back[pos]) {
minBoundY = std::min(minBoundY, y);
maxBoundY = std::max(maxBoundY, y);
minBoundX = std::min(minBoundX, x);
maxBoundX = std::max(maxBoundX, x);
}
buffer_back[pos] = buffer[pos];
}
yield();
}
// If the minBoundY wasn't updated
// we can savely assume that buffer_back[pos] == buffer[pos]
// holdes true for all values of pos
if (minBoundY == UINT8_MAX) return;
sendCommand(COLUMNADDR);
sendCommand(x_offset + minBoundX); // column start address (0 = reset)
sendCommand(x_offset + maxBoundX); // column end address (127 = reset)
sendCommand(PAGEADDR);
sendCommand(minBoundY); // page start address
sendCommand(maxBoundY); // page end address
for (y = minBoundY; y <= maxBoundY; y++) {
uint8_t *start = &buffer[(minBoundX + y * this->width())-1];
uint8_t save = *start;
*start = 0x40; // control
_i2c->write(_address, (char *)start, (maxBoundX-minBoundX) + 1 + 1);
*start = save;
}
#else
sendCommand(COLUMNADDR);
sendCommand(x_offset); // column start address (0 = reset)
sendCommand(x_offset + (this->width() - 1));// column end address (127 = reset)
sendCommand(PAGEADDR);
sendCommand(0x0); // page start address (0 = reset)
if (geometry == GEOMETRY_128_64) {
sendCommand(0x7);
} else if (geometry == GEOMETRY_128_32) {
sendCommand(0x3);
}
buffer[-1] = 0x40; // control
_i2c->write(_address, (char *)&buffer[-1], displayBufferSize + 1);
#endif
}
private:
int getBufferOffset(void) {
return 0;
}
inline void sendCommand(uint8_t command) __attribute__((always_inline)) {
char _data[2];
_data[0] = 0x80; // control
_data[1] = command;
_i2c->write(_address, _data, sizeof(_data));
}
uint8_t _address;
PinName _sda;
PinName _scl;
I2C *_i2c;
};
#endif
#endif

163
src/SSD1306Spi.h Normal file
View File

@ -0,0 +1,163 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef SSD1306Spi_h
#define SSD1306Spi_h
#include "OLEDDisplay.h"
#include <SPI.h>
#if F_CPU == 160000000L
#define BRZO_I2C_SPEED 1000
#else
#define BRZO_I2C_SPEED 800
#endif
class SSD1306Spi : public OLEDDisplay {
private:
uint8_t _rst;
uint8_t _dc;
uint8_t _cs;
public:
SSD1306Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64) {
setGeometry(g);
this->_rst = _rst;
this->_dc = _dc;
this->_cs = _cs;
}
bool connect(){
pinMode(_dc, OUTPUT);
pinMode(_cs, OUTPUT);
pinMode(_rst, OUTPUT);
SPI.begin ();
SPI.setClockDivider (SPI_CLOCK_DIV2);
// Pulse Reset low for 10ms
digitalWrite(_rst, HIGH);
delay(1);
digitalWrite(_rst, LOW);
delay(10);
digitalWrite(_rst, HIGH);
return true;
}
void display(void) {
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
uint8_t minBoundY = UINT8_MAX;
uint8_t maxBoundY = 0;
uint8_t minBoundX = UINT8_MAX;
uint8_t maxBoundX = 0;
uint8_t x, y;
// Calculate the Y bounding box of changes
// and copy buffer[pos] to buffer_back[pos];
for (y = 0; y < (displayHeight / 8); y++) {
for (x = 0; x < displayWidth; x++) {
uint16_t pos = x + y * displayWidth;
if (buffer[pos] != buffer_back[pos]) {
minBoundY = _min(minBoundY, y);
maxBoundY = _max(maxBoundY, y);
minBoundX = _min(minBoundX, x);
maxBoundX = _max(maxBoundX, x);
}
buffer_back[pos] = buffer[pos];
}
yield();
}
// If the minBoundY wasn't updated
// we can savely assume that buffer_back[pos] == buffer[pos]
// holdes true for all values of pos
if (minBoundY == UINT8_MAX) return;
sendCommand(COLUMNADDR);
sendCommand(minBoundX);
sendCommand(maxBoundX);
sendCommand(PAGEADDR);
sendCommand(minBoundY);
sendCommand(maxBoundY);
digitalWrite(_cs, HIGH);
digitalWrite(_dc, HIGH); // data mode
digitalWrite(_cs, LOW);
for (y = minBoundY; y <= maxBoundY; y++) {
for (x = minBoundX; x <= maxBoundX; x++) {
SPI.transfer(buffer[x + y * displayWidth]);
}
yield();
}
digitalWrite(_cs, HIGH);
#else
// No double buffering
sendCommand(COLUMNADDR);
sendCommand(0x0);
sendCommand(0x7F);
sendCommand(PAGEADDR);
sendCommand(0x0);
if (geometry == GEOMETRY_128_64) {
sendCommand(0x7);
} else if (geometry == GEOMETRY_128_32) {
sendCommand(0x3);
}
digitalWrite(_cs, HIGH);
digitalWrite(_dc, HIGH); // data mode
digitalWrite(_cs, LOW);
for (uint16_t i=0; i<displayBufferSize; i++) {
SPI.transfer(buffer[i]);
yield();
}
digitalWrite(_cs, HIGH);
#endif
}
private:
int getBufferOffset(void) {
return 0;
}
inline void sendCommand(uint8_t com) __attribute__((always_inline)){
digitalWrite(_cs, HIGH);
digitalWrite(_dc, LOW);
digitalWrite(_cs, LOW);
SPI.transfer(com);
digitalWrite(_cs, HIGH);
}
};
#endif

217
src/SSD1306Wire.h Normal file
View File

@ -0,0 +1,217 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
* Copyright (c) 2018 by Fabrice Weinberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* ThingPulse invests considerable time and money to develop these open source libraries.
* Please support us by buying our products (and not the clones) from
* https://thingpulse.com
*
*/
#ifndef SSD1306Wire_h
#define SSD1306Wire_h
#include "OLEDDisplay.h"
#include <Wire.h>
#include <algorithm>
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_STM32)
#define _min min
#define _max max
#endif
//--------------------------------------
class SSD1306Wire : public OLEDDisplay {
private:
uint8_t _address;
int _sda;
int _scl;
bool _doI2cAutoInit = false;
TwoWire* _wire = NULL;
int _frequency;
public:
/**
* Create and initialize the Display using Wire library
*
* Beware for retro-compatibility default values are provided for all parameters see below.
* Please note that if you don't wan't SD1306Wire to initialize and change frequency speed ot need to
* ensure -1 value are specified for all 3 parameters. This can be usefull to control TwoWire with multiple
* device on the same bus.
*
* @param _address I2C Display address
* @param _sda I2C SDA pin number, default to -1 to skip Wire begin call
* @param _scl I2C SCL pin number, default to -1 (only SDA = -1 is considered to skip Wire begin call)
* @param g display geometry dafault to generic GEOMETRY_128_64, see OLEDDISPLAY_GEOMETRY definition for other options
* @param _i2cBus on ESP32 with 2 I2C HW buses, I2C_ONE for 1st Bus, I2C_TWO fot 2nd bus, default I2C_ONE
* @param _frequency for Frequency by default Let's use ~700khz if ESP8266 is in 160Mhz mode, this will be limited to ~400khz if the ESP8266 in 80Mhz mode
*/
SSD1306Wire(uint8_t _address, int _sda = -1, int _scl = -1, OLEDDISPLAY_GEOMETRY g = GEOMETRY_128_64, HW_I2C _i2cBus = I2C_ONE, int _frequency = 700000) {
setGeometry(g);
this->_address = _address;
this->_sda = _sda;
this->_scl = _scl;
#if !defined(ARDUINO_ARCH_ESP32)
this->_wire = &Wire;
#else
this->_wire = (_i2cBus==I2C_ONE) ? &Wire : &Wire1;
#endif
this->_frequency = _frequency;
}
bool connect() {
#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH8266)
_wire->begin();
#else
// On ESP32 arduino, -1 means 'don't change pins', someone else has called begin for us.
if(this->_sda != -1)
_wire->begin(this->_sda, this->_scl);
#endif
// Let's use ~700khz if ESP8266 is in 160Mhz mode
// this will be limited to ~400khz if the ESP8266 in 80Mhz mode.
if(this->_frequency != -1)
_wire->setClock(this->_frequency);
return true;
}
void display(void) {
initI2cIfNeccesary();
const int x_offset = (128 - this->width()) / 2;
#ifdef OLEDDISPLAY_DOUBLE_BUFFER
uint8_t minBoundY = UINT8_MAX;
uint8_t maxBoundY = 0;
uint8_t minBoundX = UINT8_MAX;
uint8_t maxBoundX = 0;
uint8_t x, y;
// Calculate the Y bounding box of changes
// and copy buffer[pos] to buffer_back[pos];
for (y = 0; y < (this->height() / 8); y++) {
for (x = 0; x < this->width(); x++) {
uint16_t pos = x + y * this->width();
if (buffer[pos] != buffer_back[pos]) {
minBoundY = std::min(minBoundY, y);
maxBoundY = std::max(maxBoundY, y);
minBoundX = std::min(minBoundX, x);
maxBoundX = std::max(maxBoundX, x);
}
buffer_back[pos] = buffer[pos];
}
yield();
}
// If the minBoundY wasn't updated
// we can savely assume that buffer_back[pos] == buffer[pos]
// holdes true for all values of pos
if (minBoundY == UINT8_MAX) return;
sendCommand(COLUMNADDR);
sendCommand(x_offset + minBoundX);
sendCommand(x_offset + maxBoundX);
sendCommand(PAGEADDR);
sendCommand(minBoundY);
sendCommand(maxBoundY);
byte k = 0;
for (y = minBoundY; y <= maxBoundY; y++) {
for (x = minBoundX; x <= maxBoundX; x++) {
if (k == 0) {
_wire->beginTransmission(_address);
_wire->write(0x40);
}
_wire->write(buffer[x + y * this->width()]);
k++;
if (k == 16) {
_wire->endTransmission();
k = 0;
}
}
yield();
}
if (k != 0) {
_wire->endTransmission();
}
#else
sendCommand(COLUMNADDR);
sendCommand(x_offset);
sendCommand(x_offset + (this->width() - 1));
sendCommand(PAGEADDR);
sendCommand(0x0);
if (geometry == GEOMETRY_128_64) {
sendCommand(0x7);
} else if (geometry == GEOMETRY_128_32) {
sendCommand(0x3);
}
for (uint16_t i=0; i < displayBufferSize; i++) {
_wire->beginTransmission(this->_address);
_wire->write(0x40);
for (uint8_t x = 0; x < 16; x++) {
_wire->write(buffer[i]);
i++;
}
i--;
_wire->endTransmission();
}
#endif
}
void setI2cAutoInit(bool doI2cAutoInit) {
_doI2cAutoInit = doI2cAutoInit;
}
private:
int getBufferOffset(void) {
return 0;
}
inline void sendCommand(uint8_t command) __attribute__((always_inline)){
initI2cIfNeccesary();
_wire->beginTransmission(_address);
_wire->write(0x80);
_wire->write(command);
_wire->endTransmission();
}
void initI2cIfNeccesary() {
if (_doI2cAutoInit) {
#if !defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH8266)
_wire->begin();
#else
_wire->begin(this->_sda, this->_scl);
#endif
}
}
};
#endif