Prvni odevzdani
406
README.md
@ -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.
|
||||||
|
214
examples/SSD1306ClockDemo/SSD1306ClockDemo.ino
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
21
examples/SSD1306ClockDemo/images.h
Normal 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
|
||||||
|
};
|
233
examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino
Normal 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() { }
|
123
examples/SSD1306OTADemo/SSD1306OTADemo.ino
Normal 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();
|
||||||
|
}
|
197
examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino
Normal 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);
|
||||||
|
}
|
28
examples/SSD1306SimpleDemo/images.h
Normal 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,
|
||||||
|
};
|
75
examples/SSD1306TwoScreenDemo/SSD1306TwoScreenDemo.ino
Normal 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);
|
||||||
|
}
|
28
examples/SSD1306TwoScreenDemo/images.h
Normal 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,
|
||||||
|
};
|
194
examples/SSD1306UiDemo/SSD1306UiDemo.ino
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
50
examples/SSD1306UiDemo/images.h
Normal 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
@ -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
@ -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
@ -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
After Width: | Height: | Size: 16 KiB |
BIN
resources/DemoFrame2.jpg
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
resources/DemoFrame3.jpg
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
resources/DemoFrame4.jpg
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
resources/FontTool.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
resources/SPI_version.jpg
Normal file
After Width: | Height: | Size: 26 KiB |
664
resources/glyphEditor.html
Normal 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">✗</th>'
|
||||||
|
+ '<th title="Add a char above" action="add">+</th>'
|
||||||
|
+ '<th title="Shift pixels to the left" action="left">←</th>'
|
||||||
|
+ '<th title="Shift pixels down" action="down">↓</th>'
|
||||||
|
+ '<th title="Shift pixels up" action="up">↑</th>'
|
||||||
|
+ '<th title="Shift pixels to the right" action="right">→</th>'
|
||||||
|
+ '<th title="Copy from another character" action="copy">©</th>'
|
||||||
|
+ '<th title="Reset all pixels" action="clean">∅</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
After Width: | Height: | Size: 67 KiB |
BIN
resources/xbmPreview.png
Normal file
After Width: | Height: | Size: 41 KiB |
1082
src/OLEDDisplay.cpp
Normal file
392
src/OLEDDisplay.h
Normal 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
14
src/OLEDDisplayFonts.h
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
|