harbys / pico-ssd1306 Goto Github PK
View Code? Open in Web Editor NEWSSD1306 Library for RP2040
License: BSD 3-Clause "New" or "Revised" License
SSD1306 Library for RP2040
License: BSD 3-Clause "New" or "Revised" License
the pico SDK is setup and the env var is setup correctly. Have tried a few things with CMakeLists.txt with no luck. Trying to build the examples for basic text. Any help / direction would be appreciated.
Can you share your workflow for generating the existing font headers?
I've been struggling to find a reliable conversion process for the expected format, especially for fonts taller than 8px.
Hello,
I've just tested the full ascii chars feature.
I tried to display following characters:
\xe8 \xe9 \xea \xeb
This should result to the following display:
è é ê ë
Instead I got some unexpected greek letters
The buffer
in the FrameBuffer
is allocated but never freed. This could cause memory leak if the SSD1306
is out of scope or a new buffer is set. Granted this may not be a very big issue since in most of the use cases SSD1306
lifespan will be the whole program lifespan. It's just that personally I think this still should be handled properly. Alternatively, instead of allocating the buffer on heap, just put it on stack, as the buffer size will always be 1024, and keep a member variable to point to member buffer or user-set buffer.
It seems that the letter "M" has been substituted in the 5x8_font
with the letter "H".
The cause seems to be that the following data appears twice in each #ifndef
or #else
section in 5x8_font.h
, once for "H" (as it should) and again for "M":
0x7c,
0x10,
0x10,
0x7c,
0x0,
Note that the 'ndef' SSD1306_ASCII_FULL
entries have trailing whitespace but the SSD1306_ASCII_FULL
entries don't, which can screw with non-regexp file contents searches.
I think the line this->frameBuffer = FrameBuffer();
(ssd1306.cpp:19) in the SSD1306
constructor is both redundant and can cause problems.
What I think is happening is that frameBuffer
is already initialized by the time the body of the SSD1306
constructor is entered. The line this->frameBuffer = FrameBuffer();
replaces the address to first FrameBuffer
with the address of a second FrameBuffer
that is constructed in the scope of the SSD1306
constructor. When the SSD1306
constructor is exited, the second FrameBuffer
is destructed and it's buffer is freed. This leaves the just-constructed SSD1306
object with a frameBuffer
member pointing to a FrameBuffer
object with a buffer that has been freed, causing it to corrupt other data when that memory is allocated to something else.
On member initialization, from https://en.cppreference.com/w/cpp/language/constructor:
Before the compound statement that forms the function body of the constructor begins executing, initialization of all direct bases, virtual bases, and non-static data members is finished.
As a test, I added print statements to the constructor and destructor in FrameBuffer.cpp
:
#include <stdio.h>
FrameBuffer::FrameBuffer() {
this->buffer = new unsigned char[FRAMEBUFFER_SIZE];
printf("FrameBuffer() (%p) new buffer: %p\n", this, this->buffer);
}
FrameBuffer::~FrameBuffer() {
printf("~FrameBuffer() (%p) delete buffer: %p\n", this, this->buffer);
delete[] this->buffer;
}
I then modified the basic_text
example to add in some more print statements and to match the hardware I have:
#include <cstdio>
#include "pico/stdlib.h"
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/textRenderer/TextRenderer.h"
#include "hardware/i2c.h"
// Use the namespace for convenience
using namespace pico_ssd1306;
int main(){
// Initialise usb serial for debugging and give our terminal monitor
// a bit of time to connect.
stdio_init_all();
sleep_ms(2000);
printf("comms test\n");
// Init i2c0 controller
i2c_init(i2c0, 1000000);
// Set up pins 12 and 13
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
// If you don't do anything before initializing a display pi pico is too fast and starts sending
// commands before the screen controller had time to set itself up, so we add an artificial delay for
// ssd1306 to set itself up
sleep_ms(250);
// Create a new display object at address 0x3D and size of 128x64
printf("before SSD1306()\n");
SSD1306 display = SSD1306(i2c0, 0x3C, Size::W128xH32);
printf("after SSD1306()\n");
printf("display.frameBuffer.get(): %p\n", display.frameBuffer.get());
// Here we rotate the display by 180 degrees, so that it's not upside down from my perspective
// If your screen is upside down try setting it to 1 or 0
display.setOrientation(0);
// Draw text on display
// After passing a pointer to display, we need to tell the function what font and text to use
// Available fonts are listed in textRenderer's readme
// Last we tell this function where to anchor the text
// Anchor means top left of what we draw
drawText(&display, font_12x16, "TEST text", 0 ,0);
// Send buffer to the display
display.sendBuffer();
printf("end\n");
}
Note that I also made frameBuffer
public
so I could call display.frameBuffer.get()
in main()
.
The resulting output was:
19:24:20:727 -> comms test
19:24:20:977 -> before SSD1306()
19:24:20:980 -> FrameBuffer() (20041FE4) new buffer: 200035E0
19:24:20:984 -> FrameBuffer() (20041F9C) new buffer: 200039E8
19:24:20:988 -> ~FrameBuffer() (20041F9C) delete buffer: 200039E8
19:24:21:003 -> after SSD1306()
19:24:21:004 -> display.frameBuffer.get(): 200039E8
19:24:21:018 -> end
19:24:21:020 -> ~FrameBuffer() (20041FE4) delete buffer: 200039E8
This output seems to show two FrameBuffer
objects being created, and the second freeing its buffer before we get back to main()
. Further, the address of that freed buffer is what display.frameBuffer.get()
returns so the SSD1306
will use that freed buffer, clobbering anything written there after it is reallocated.
The fix seems simple. Delete this->frameBuffer = FrameBuffer();
from ssd1306.cpp
.
Hello,
Thank you for your simple but usefull driver.
The available fonts, with a size from 5x8 to 16x32 are perfect for my usage.
However, the charset of these fonts are limited to the range 32 to 127 in the ascii table.
Would it be possible to get an extended charset (say 32 to 255 for instance) in order to include also characters such as é, ï ê, etc?
Or even better, a mechanism that allow to build our own font set ?
Compared to the other C++ libraries out there, I've had better luck integrating this library into an external project and getting it to compile, but I'm not seeing anything on the screen.
This is the 128x32 OLED I'm using: https://www.amazon.com/DSD-TECH-OLED-Display-Arduino/dp/B07D9H83R4
SDA pin is GPIO 4, SCL pin is GPIO 5.
#include "pico-ssd1306/ssd1306.h"
#include "hardware/i2c.h"
#define I2C_PORT i2c0
#define I2C_PIN_SDA 4
#define I2C_PIN_SCL 5
int main() {
i2c_init(I2C_PORT, 1000000); //Use i2c port with baud rate of 1Mhz
gpio_set_function(I2C_PIN_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C_PIN_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C_PIN_SDA);
gpio_pull_up(I2C_PIN_SCL);
sleep_ms(250); // Allow time for initialization
// Create a new display object
pico_ssd1306::SSD1306 display = pico_ssd1306::SSD1306(I2C_PORT, 0x3D, pico_ssd1306::Size::W128xH32);
for (int y=0; y < 32; y++) {
display.setPixel(20, y);
}
display.sendBuffer();
sleep(10000);
}
Compiles and runs beautifully but the screen is blank. Am I missing something here?
I know they're wired up correctly to the I2C pins physically, because I can get the martinkooij driver's example code to run and display stuff on screen. I've tried I2C address 0x3C and 0x3D.
Every include file starts with #pragma once
and continues with #ifndef FILENAME_H
and #define FILENAME_H
The two methods do fundamentally the same thing.
I would remove the #ifndef / #define / #endif as it is more verbose and supposedly, #pragma once can be more efficient.
Supposedly, not all compilers have supported #pragma once, but GCC and Clang do and I have yet to encounter a modern compiler that does not. The pico SDK uses GCC, so even if some exotic compiler does not like #pragma once, it is not an issue for this project.
Hello,
Thank your for this simple but efficient driver.
It works fine for my usage, which is very basic.
I've however noticed that, for some letters with long legs, such as 'p', 'q', 'y' are not displayed very well. It's like if the bottom pixels where displayed at the top of the letter.
The current driver does not have the facility to turn the display off so as to allow screensaver like feature. It would be nice to add this feature to the library.
If you move the SSD1306 outside the constructor, you will be able to create a new object as global.
Otherwise, if you try right now, the RP2040 never boot up.
namespace pico_ssd1306 {
SSD1306::SSD1306(i2c_inst *i2CInst, uint16_t Address, Size size) {
// Set class instanced variables
this->i2CInst = i2CInst;
this->address = Address;
this->size = size;
this->width = 128;
if (size == Size::W128xH32) {
this->height = 32;
} else {
this->height = 64;
}
// create a frame buffer
this->frameBuffer = FrameBuffer();
// display is not inverted by default
this->inverted = false;
}
void SSD1306::init() {
// this is a list of setup commands for the display
uint8_t setup[] = {
SSD1306_DISPLAY_OFF,
SSD1306_LOWCOLUMN,
SSD1306_HIGHCOLUMN,
SSD1306_STARTLINE,
SSD1306_MEMORYMODE,
SSD1306_MEMORYMODE_HORZONTAL,
SSD1306_CONTRAST,
0xFF,
SSD1306_INVERTED_OFF,
SSD1306_MULTIPLEX,
63,
SSD1306_DISPLAYOFFSET,
0x00,
SSD1306_DISPLAYCLOCKDIV,
0x80,
SSD1306_PRECHARGE,
0x22,
SSD1306_COMPINS,
0x12,
SSD1306_VCOMDETECT,
0x40,
SSD1306_CHARGEPUMP,
0x14,
SSD1306_DISPLAYALL_ON_RESUME,
SSD1306_DISPLAY_ON
};
// send each one of the setup commands
for (uint8_t &command: setup) {
this->cmd(command);
}
// clear the buffer and send it to the display
// if not done display shows garbage data
this->clear();
this->sendBuffer();
}
#include "../pico-ssd1306/ssd1306.h"
pico_ssd1306::SSD1306 display(I2C_PORT, 0x3C, pico_ssd1306::Size::W128xH64);
[...]
int main() {
display.init();
[...]
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.