برد Wemos Lolin32 مجتمع شده با یک نمایشگر OLED، بر اساس تراشه ESP32 و یک نمایشگر OLED، 0.96 اینچی (168 در 64 پیکسل) ساخته شده است. نمایشگر تعبیه شده بر روی برد، توسط کنترلر SSD1306 و به وسیله باس I2C کنترل می شود.
برد Wemos Lolin32 با OLED تعبیه شده روی آن
در این برد، 19 پایه ورودی/خروجی در اختیار برنامه نویس قرار گرفته شده (ماژول ESP32 به تنهایی دارای 38 پایه میباشد) که 11 عدد از آن ها ورودی آنالوگ به دیجیتال (ADC) هستند. پایههای 4 (SDA) و 5 (SCL) برای باس I2C استفاده شده که نمایشگر OLED نیز به همین دو پایه متصل شده است. این برد دارای دو دکمه BOOT و EN (RST) می باشد که در بعضی از مدل ها بر روی برد و در بعضی از مدل ها در پشت برد تعبیه شده است.
مشخصات پایانههای روی برد
مشخصات ماژول ESP-WROOM-32
- میکروپروسسور دو هسته ای با فرکانس MHz240 باMB 4 حافظه فلش SPI
- پشتیبانی از حافظه فلش تاMB 16
- وایفای b / g / n 11
- بلوتوث 0 LE
- میزان مصرف در حالت Sleep : 5 μA
- حسگرهای تعبیه شده بر روی ماژول
- اثر هال
نصب کتابخانه های مورد نیاز برای استفاده از OLED در محیط Arduino
در محیط آردوینو، از منوی Sketch ← Include Library ← Manage Libraries پنجره Library Manager را باز و عبارت SSD1306 را جستجو نمایید. در نهایت کتابخانه Adafruit SSD1306 را نصب کنید.
کتابخانه SSD1306
حال با جستجو عبارت GFX کتابخانه Adafruit GFX Library را نیز نصب نمایید.
کتابخانه GFX
در نهایت بعد از نصب این کتابخانه ها، با باز و بسته کردن Arduino آن را ریستارت نمایید.
نمایش بر روی OLED
کتابخانه های Adafruit به صورت پیش فرض از پایههای 21 و 22 برای ارتباط I2C استفاده مینماید. جهت عوض کردن این دو پایه در بخش setup()، دستور زیر را اضافه نمایید :
Wire.begin(5, 4);
همچنین برای راهاندازی نمایشگر از کد زیر استفاده نمایید :
display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false)
مقدار false در پارامترهای دوم و سوم در این تابع مشخص میکند که به جای استفاده از پینهای پیشفرض از پینهای اختصاص یافته در کد برای باس I2C استفاده شود.
تست نمایشگر
از منوی File ← Examples ← Adafruit SSD1306 ← ssd1306_128X64_i2c را انتخاب نمایید. کد زیر در یک پنجره جدید بارگزاری میشود :
کد تست نمایشگر
/*********
Complete project details at https://randomnerdtutorials.com
This is an example for our Monochrome OLEDs based on SSD1306 drivers. Pick one up today in the adafruit shop! ——> http://www.adafruit.com/category/63_98
This example is for a 128×32 pixel display using I2C to communicate 3 pins are required to interface (two I2C and one reset).
Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries, with contributions from the open source community. BSD license, check license.txt for more information All text above, and the splash screen below must be included in any redistribution.
*********/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NUMFLAKES 10 // Number of snowflakes in the animation example
#define LOGO_HEIGHT 16
#define LOGO_WIDTH 16
static const unsigned char PROGMEM logo_bmp[] =
{ B00000000, B11000000,
B00000001, B11000000,
B00000001, B11000000,
B00000011, B11100000,
B11110011, B11100000,
B11111110, B11111000,
B01111110, B11111111,
B00110011, B10011111,
B00011111, B11111100,
B00001101, B01110000,
B00011011, B10100000,
B00111111, B11100000,
B00111111, B11110000,
B01111100, B11110000,
B01110000, B01110000,
B00000000, B00110000 };
void setup() {
Serial.begin(115200);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F(“SSD1306 allocation failed”));
for(;;); // Don’t proceed, loop forever
}
// Show initial display buffer contents on the screen —
// the library initializes this with an Adafruit splash screen.
display.display();
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
// Draw a single pixel in white
display.drawPixel(10, 10, WHITE);
// Show the display buffer on the screen. You MUST call display() after
// drawing commands to make them visible on screen!
display.display();
delay(2000);
// display.display() is NOT necessary after every single drawing command,
// unless that’s what you want…rather, you can batch up a bunch of
// drawing operations and then update the screen all at once by calling
// display.display(). These examples demonstrate both approaches…
testdrawline(); // Draw many lines
testdrawrect(); // Draw rectangles (outlines)
testfillrect(); // Draw rectangles (filled)
testdrawcircle(); // Draw circles (outlines)
testfillcircle(); // Draw circles (filled)
testdrawroundrect(); // Draw rounded rectangles (outlines)
testfillroundrect(); // Draw rounded rectangles (filled)
testdrawtriangle(); // Draw triangles (outlines)
testfilltriangle(); // Draw triangles (filled)
testdrawchar(); // Draw characters of the default font
testdrawstyles(); // Draw ‘stylized’ characters
testscrolltext(); // Draw scrolling text
testdrawbitmap(); // Draw a small bitmap image
// Invert and restore display, pausing in-between
display.invertDisplay(true);
delay(1000);
display.invertDisplay(false);
delay(1000);
testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps
}
void loop() {
}
void testdrawline() {
int16_t i;
display.clearDisplay(); // Clear display buffer
for(i=0; i<display.width(); i+=4) {
display.drawLine(0, 0, i, display.height()-1, WHITE);
display.display(); // Update screen with each newly-drawn line
delay(1);
}
for(i=0; i<display.height(); i+=4) {
display.drawLine(0, 0, display.width()-1, i, WHITE);
display.display();
delay(1);
}
delay(250);
display.clearDisplay();
for(i=0; i<display.width(); i+=4) {
display.drawLine(0, display.height()-1, i, 0, WHITE);
display.display();
delay(1);
}
for(i=display.height()-1; i>=0; i-=4) {
display.drawLine(0, display.height()-1, display.width()-1, i, WHITE);
display.display();
delay(1);
}
delay(250);
display.clearDisplay();
for(i=display.width()-1; i>=0; i-=4) {
display.drawLine(display.width()-1, display.height()-1, i, 0, WHITE);
display.display();
delay(1);
}
for(i=display.height()-1; i>=0; i-=4) {
display.drawLine(display.width()-1, display.height()-1, 0, i, WHITE);
display.display();
delay(1);
}
delay(250);
display.clearDisplay();
for(i=0; i<display.height(); i+=4) {
display.drawLine(display.width()-1, 0, 0, i, WHITE);
display.display();
delay(1);
}
for(i=0; i<display.width(); i+=4) {
display.drawLine(display.width()-1, 0, i, display.height()-1, WHITE);
display.display();
delay(1);
}
delay(2000); // Pause for 2 seconds
}
void testdrawrect(void) {
display.clearDisplay();
for(int16_t i=0; i<display.height()/2; i+=2) {
display.drawRect(i, i, display.width()-2*i, display.height()-2*i, WHITE);
display.display(); // Update screen with each newly-drawn rectangle
delay(1);
}
delay(2000);
}
void testfillrect(void) {
display.clearDisplay();
for(int16_t i=0; i<display.height()/2; i+=3) {
// The INVERSE color is used so rectangles alternate white/black
display.fillRect(i, i, display.width()-i*2, display.height()-i*2, INVERSE);
display.display(); // Update screen with each newly-drawn rectangle
delay(1);
}
delay(2000);
}
void testdrawcircle(void) {
display.clearDisplay();
for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
display.drawCircle(display.width()/2, display.height()/2, i, WHITE);
display.display();
delay(1);
}
delay(2000);
}
void testfillcircle(void) {
display.clearDisplay();
for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
// The INVERSE color is used so circles alternate white/black
display.fillCircle(display.width() / 2, display.height() / 2, i, INVERSE);
display.display(); // Update screen with each newly-drawn circle
delay(1);
}
delay(2000);
}
void testdrawroundrect(void) {
display.clearDisplay();
for(int16_t i=0; i<display.height()/2-2; i+=2) {
display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
display.height()/4, WHITE);
display.display();
delay(1);
}
delay(2000);
}
void testfillroundrect(void) {
display.clearDisplay();
for(int16_t i=0; i<display.height()/2-2; i+=2) {
// The INVERSE color is used so round-rects alternate white/black
display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
display.height()/4, INVERSE);
display.display();
delay(1);
}
delay(2000);
}
void testdrawtriangle(void) {
display.clearDisplay();
for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
display.drawTriangle(
display.width()/2 , display.height()/2-i,
display.width()/2-i, display.height()/2+i,
display.width()/2+i, display.height()/2+i, WHITE);
display.display();
delay(1);
}
delay(2000);
}
void testfilltriangle(void) {
display.clearDisplay();
for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
// The INVERSE color is used so triangles alternate white/black
display.fillTriangle(
display.width()/2 , display.height()/2-i,
display.width()/2-i, display.height()/2+i,
display.width()/2+i, display.height()/2+i, INVERSE);
display.display();
delay(1);
}
delay(2000);
}
void testdrawchar(void) {
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.cp437(true); // Use full 256 char ‘Code Page 437’ font
// Not all the characters will fit on the display. This is normal.
// Library will draw what it can and the rest will be clipped.
for(int16_t i=0; i<256; i++) {
if(i == ‘\n’) display.write(‘ ‘);
else display.write(i);
}
display.display();
delay(2000);
}
void testdrawstyles(void) {
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(0,0); // Start at top-left corner
display.println(F(“Hello, world!”));
display.setTextColor(BLACK, WHITE); // Draw ‘inverse’ text
display.println(3.141592);
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(WHITE);
display.print(F(“0x”)); display.println(0xDEADBEEF, HEX);
display.display();
delay(2000);
}
void testscrolltext(void) {
display.clearDisplay();
display.setTextSize(2); // Draw 2X-scale text
display.setTextColor(WHITE);
display.setCursor(10, 0);
display.println(F(“scroll”));
display.display(); // Show initial text
delay(100);
// Scroll in various directions, pausing in-between:
display.startscrollright(0x00, 0x0F);
delay(2000);
display.stopscroll();
delay(1000);
display.startscrollleft(0x00, 0x0F);
delay(2000);
display.stopscroll();
delay(1000);
display.startscrolldiagright(0x00, 0x07);
delay(2000);
display.startscrolldiagleft(0x00, 0x07);
delay(2000);
display.stopscroll();
delay(1000);
}
void testdrawbitmap(void) {
display.clearDisplay();
display.drawBitmap(
(display.width() – LOGO_WIDTH ) / 2,
(display.height() – LOGO_HEIGHT) / 2,
logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
display.display();
delay(1000);
}
#define XPOS 0 // Indexes into the ‘icons’ array in function below
#define YPOS 1
#define DELTAY 2
void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
int8_t f, icons[NUMFLAKES][3];
// Initialize ‘snowflake’ positions
for(f=0; f< NUMFLAKES; f++) {
icons[f][XPOS] = random(1 – LOGO_WIDTH, display.width());
icons[f][YPOS] = -LOGO_HEIGHT;
icons[f][DELTAY] = random(1, 6);
Serial.print(F(“x: “));
Serial.print(icons[f][XPOS], DEC);
Serial.print(F(” y: “));
Serial.print(icons[f][YPOS], DEC);
Serial.print(F(” dy: “));
Serial.println(icons[f][DELTAY], DEC);
}
for(;;) { // Loop forever…
display.clearDisplay(); // Clear the display buffer
// Draw each snowflake:
for(f=0; f< NUMFLAKES; f++) {
display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, WHITE);
}
display.display(); // Show the display buffer on the screen
delay(200); // Pause for 1/10 second
// Then update coordinates of each flake…
for(f=0; f< NUMFLAKES; f++) {
icons[f][YPOS] += icons[f][DELTAY];
// If snowflake is off the bottom of the screen…
if (icons[f][YPOS] >= display.height()) {
// Reinitialize to a random position, just off the top
icons[f][XPOS] = random(1 – LOGO_WIDTH, display.width());
icons[f][YPOS] = -LOGO_HEIGHT;
icons[f][DELTAY] = random(1, 6);
}
}
}
}
بعد از آپلود کد، بر روی نمایشگر خطوط و متن های مختلف رسم خواهند شد (دقت نمایید که قبل از آپلود کد، پین های باس I2C را از حالت پیشفرض تغییر داده و بر روی پینهای 4 و 5 تنظیم نمایید).
تست نمایشگر روی برد
نمایش متن بر روی نمایشگر
کد زیر متن “Hello World” را بر روی نمایشگر نشان میدهد.
کد نمایش Hello World بر روی نمایشگر
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(115200);
Wire.begin(5, 4);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false)) { // Address 0x3D for 128×64
Serial.println(F(“SSD1306 allocation failed”));
for(;;);
}
delay(2000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 10);
// Display static text
display.println(“Hello, world!”);
display.display();
}
void loop() {
}
بررسی و توضیح دستورهای مهم در کد
دستور
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
اندازه تصویر و پروتکل ارتباطی مورد استفاده را به عنوان پارامترهای ورودی به شی display میدهد. پارامتر چهارم (-1) به معنی این است که نمایشگر دارای پین Reset نمیباشد.
بعد از راه اندازی نمایشگر توسط کد
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, false, false))
با استفاده از دستور delay(2000); یک تاخیر با اندازه دو ثانیه ایجاد میکنیم، تا نمایشگر فرصت تنظیم شدن را پیدا نماید.
دستور
display.clearDisplay();
باعث پاکسازی بافر نمایشگر خواهد شد.
با استفاده از دستورهای
display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0);
به ترتیب اندازه، رنگ و مکان قرارگیری متن را مشخص مینماییم.
سپس با استفاده از دستور
display.println(“Hello, world!”);
متن مورد نظر خود (که در اینجا “Hello World!”) می باشد را بر روی نمایشگر چاپ مینماییم.
در نهایت با استفاده از دستور
display.display();
متن مورد نظر جهت نمایش به نمایشگر ارسال میشود.
منابع :
[1] “Unpacking Wemos ESP32 LoLin clone with 0.96” SSD1306 monochrome OLED display,” 14 04 2021. [Online]. Available: https://diyprojects.io/unpacking-wemos-esp32-lolin-clone-0-96-ssd1306-monochrome-oled-display/.
[2] “ESP32 Built-in OLED Board (Wemos Lolin32): Pinout, Libraries and OLED Control,” randomnerdtutorials, [Online]. Available: https://randomnerdtutorials.com/esp32-built-in-oled-ssd1306/. [Accessed 14 04 2021].
[3] “ESP32 OLED Display with Arduino IDE,” [Online]. Available: https://randomnerdtutorials.com/esp32-ssd1306-oled-display-arduino-ide/. [Accessed 14 04 2021].