1_呼吸灯
#include<Arduino.h>
#define LED_PWM 2 //把调用的GPIO引脚进行了一个宏定义
int freq = 5000;
int ledChannel = 0;
int resolution = 8;
void setup() {
ledcSetup(ledChannel, freq, resolution);
ledcAttachPin(LED_PWM, ledChannel);
}
void loop() {
for (int dutyCycle = 0; dutyCycle <= 255; dutyCycle++) {
ledcWrite(ledChannel, dutyCycle);
delay(7);
}
for (int dutyCycle = 255; dutyCycle >= 0; dutyCycle--) {
ledcWrite(ledChannel, dutyCycle);
delay(7);
}
}
2_ST7789_SPI_1.54
#define TFT_MOSI 23
#define TFT_SCLK 18
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST 4
安装库
TFT_eSPI
2.1_必要设置
2.2_中文显示
安装库
U8g2_for_TFT_eSPI
#include "SPI.h"
#include "TFT_eSPI.h"
#include "U8g2_for_TFT_eSPI.h"
TFT_eSPI tft = TFT_eSPI(); // tft实例
U8g2_for_TFT_eSPI u8f; // U8g2字体实例
//要阅读包含所有字形的简短故事,您至少需要带有_gb2312b后缀的字体
//但是,带有_gb2312b后缀的字体非常大,并不总是适合
//目标控制器。 为了进行测试,您可以使用_chinese1扩展名,但是
//许多字形被跳过并且不可见。
//
// wqy字体有不同的大小,仅列出了wqy12,wqy14和wqy16
//#define FONT u8g2_font_wqy12_t_chinese1
//#define FONT u8g2_font_wqy12_t_gb2312b
//#define FONT u8g2_font_wqy14_t_chinese1
//#define FONT u8g2_font_wqy14_t_gb2312b
//#define FONT u8g2_font_wqy16_t_chinese1
#define FONT u8g2_font_wqy16_t_gb2312b
const char c_str[] =
"莫狄\n";
char buf[48]; //每行最多8个中文字形,最大buf大小为8 * 3 = 24
uint8_t total_lines; //故事中的总行数
uint8_t i; //循环变量
uint8_t line_cnt; //要绘制的线数,通常等于lines_per_draw
uint8_t start_line; //从top_window_pos派生的最上方可见行
uint8_t lines_per_draw; //根据字体和显示高度得出在屏幕上绘制多少行
uint16_t glyph_height; //字形的高度
uint16_t glyph_width;
uint16_t top_window_pos; //定义文本中以像素为单位的显示位置
uint16_t total_height; //总高度(以像素为单位),由字体高度和total_lines得出
uint16_t top_offset; //第一条可见线和显示之间的偏移
uint8_t u8x8_GetStringLineCnt(const char *str)
{
char e;
uint8_t line_cnt = 1;
if ( str == NULL )
return 0;
for(;;)
{
e = *str;
if ( e == '\0' )
break;
str++;
if ( e == '\n' )
line_cnt++;
}
return line_cnt;
}
/*
假定字符串,在“ str”中用“ \ n”分隔。
返回索引为“ line_idx”的字符串。 第一个strng的line_idx = 0
例:
对于line_idx = 1和str =“ abc \ nxyz”,返回“ xyz”
同时支持UTF8和普通字符串。
*/
const char *u8x8_GetStringLineStart(uint8_t line_idx, const char *str )
{
char e;
uint8_t line_cnt = 1;
if ( line_idx == 0 )
return str;
for(;;)
{
e = *str;
if ( e == '\0' )
break;
str++;
if ( e == '\n' )
{
if ( line_cnt == line_idx )
return str;
line_cnt++;
}
}
return NULL; /* line not found */
}
/*复制到str中的第一个'\ n'或'\ 0'*/
/*重要说明:没有字符串溢出检查,请确保*/
/*目标缓冲区足够大*/
void u8x8_CopyStringLine(char *dest, uint8_t line_idx, const char *str)
{
if ( dest == NULL )
return;
str = u8x8_GetStringLineStart( line_idx, str );
if ( str != NULL )
{
for(;;)
{
if ( *str == '\n' || *str == '\0' )
break;
*dest = *str;
dest++;
str++;
}
}
*dest = '\0';
}
void setup() {
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
u8f.begin(tft); // 将u8g2过程连接到TFT_eSPI
u8f.setFont(FONT);
u8f.setForegroundColor(TFT_WHITE); // 应用颜色
/*以行为单位计算文本的长度*/
total_lines = u8x8_GetStringLineCnt(c_str);
/*获取字形的高度*/
//glyph_height = u8f.getMaxCharHeight();
glyph_height = u8f.u8g2.font_info.max_char_height;
glyph_width = u8f.u8g2.font_info.max_char_width;
/*以像素为单位计算文本的高度*/
total_height = (uint16_t)total_lines * (uint16_t)glyph_height;
/*计算必须在屏幕上绘制几行*/
lines_per_draw = tft.height() / glyph_height;
lines_per_draw += 2;
/*从文本顶部开始*/
top_window_pos = 0;
start_line = top_window_pos / glyph_height;
top_offset = top_window_pos % glyph_height;
line_cnt = total_lines - start_line;
if ( line_cnt > lines_per_draw )
line_cnt = lines_per_draw;
for( i = 0; i < line_cnt; i++ )
{
/*将一行文本复制到本地缓冲区*/
u8x8_CopyStringLine(buf, i+start_line, c_str);
/*绘制本地缓冲区的内容*/
tft.fillRect(0, i*glyph_height-top_offset, line_cnt*glyph_width, glyph_height, TFT_BLACK);
u8f.drawUTF8(0, i*glyph_height-top_offset +glyph_height, buf);
}
}
void loop() {
}
2.3_SD卡加载图片
ST7789引脚连接
#define TFT_MOSI 23
#define TFT_SCLK 18
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST 4
SD卡模块引脚连接
CS=5
SCK=18
MOSI=23
MISO=19
安装库
TJpg_Decoder
修改图片尺寸并转换图片格式为JPEG格式
https://www.gimp.org/
不支持渐进式!!!
#include <TJpg_Decoder.h>
// Include SD
#define FS_NO_GLOBALS
#include <FS.h>
#ifdef ESP32
#include "SPIFFS.h" // ESP32 only
#endif
#define SD_CS 5
// Include the TFT library https://github.com/Bodmer/TFT_eSPI
#include "SPI.h"
#include <TFT_eSPI.h> // Hardware-specific library
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
// This next function will be called during decoding of the jpeg file to
// render each block to the TFT. If you use a different TFT library
// you will need to adapt this function to suit.
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
{
// Stop further decoding as image is running off bottom of screen
if ( y >= tft.height() ) return 0;
// This function will clip the image block rendering automatically at the TFT boundaries
tft.pushImage(x, y, w, h, bitmap);
// This might work instead if you adapt the sketch to use the Adafruit_GFX library
// tft.drawRGBBitmap(x, y, bitmap, w, h);
// Return 1 to decode next block
return 1;
}
void setup()
{
Serial.begin(115200);
// Initialise SD before TFT
if (!SD.begin(SD_CS)) {
Serial.println(F("SD.begin failed!"));
while (1) delay(0);
}
// Initialise the TFT
tft.begin();
tft.setTextColor(0xFFFF, 0x0000);
tft.fillScreen(TFT_BLACK);
tft.setSwapBytes(true); // We need to swap the colour bytes (endianess)
// The jpeg image can be scaled by a factor of 1, 2, 4, or 8
TJpgDec.setJpgScale(1);
// The decoder must be given the exact name of the rendering function above
TJpgDec.setCallback(tft_output);
}
void loop()
{
tft.fillScreen(TFT_RED);
// Draw the image, top left at 0,0
TJpgDec.drawSdJpg(0, 0, "/panda.jpg");
// Wait before drawing again
delay(2000);
}
2.4_通过WiFi接收视频
代码必要工具地址
https://github.com/modi12jin/arduino-ESP32-Tutorial
#pragma GCC optimize ("O3")
#include <TFT_eSPI.h>
TFT_eSPI Tft;
#include <WiFi.h>
#include <esp_wifi.h>
#include "TCPReceiver.h"
#include "DMADrawer.h"
TCPReceiver recv;
void setup(void)
{
Serial.begin(115200);
Serial.flush();
Tft.begin();
Tft.setRotation(0);
if (Tft.width() < Tft.height())
Tft.setRotation(1);
int width = Tft.width();
int height = Tft.height();
if (width > 320) width = 320;
if (height > 240) height = 240;
Tft.setTextFont(2);
Serial.println("WiFi begin.");
Tft.println("WiFi begin.");
//尝试连接到存储的AP
WiFi.mode(WIFI_MODE_STA);
WiFi.begin();
//等待10秒钟再连接
for (int i = 0; WiFi.status() != WL_CONNECTED && i < 100; i++) { delay(100); }
//如果无法连接,请启动SmartConfig
// https://itunes.apple.com/app/id1071176700
// https://play.google.com/store/apps/details?id=com.cmmakerclub.iot.esptouch
if (WiFi.status() != WL_CONNECTED) {
Serial.print("SmartConfig start.");
Tft.println("SmartConfig start.");
WiFi.mode(WIFI_MODE_APSTA);
WiFi.beginSmartConfig();
while (WiFi.status() != WL_CONNECTED) {
delay(100);
}
WiFi.stopSmartConfig();
WiFi.mode(WIFI_MODE_STA);
}
Serial.println(String("IP:") + WiFi.localIP().toString());
Tft.println(WiFi.localIP().toString());
setup_t s;
Tft.getSetup(s);
int spi_freq = SPI_FREQUENCY;
//如果您进入80MHz的重启循环,请尝试降低到40MHz。
//如果(spi_freq> 40000000)spi_freq = 40000000;
recv.setup( s.r0_x_offset
, s.r0_y_offset
, width
, height
, spi_freq
, TFT_MOSI
, TFT_MISO
, TFT_SCLK
, TFT_CS
, TFT_DC
);
}
void loop(void)
{
recv.loop();
}
2.5_VNC
安装库
https://github.com/moononournation/Arduino_GFX
https://github.com/moononournation/arduinoVNC
vnc 服务端 安卓APP
https://github.com/bk138/droidVNC-NG
设置压缩等级
src/VNC_config.h
// zlib related
#define VNC_COMPRESS_LEVEL 1
const char *SSID_NAME = "FAST_BA74";
const char *SSID_PASSWORD = "12345678";
const char *VNC_IP = "192.168.0.104";
const uint16_t VNC_PORT = 5901;
const char *VNC_PASSWORD = "12345";
#include <Arduino_GFX_Library.h>
#define GFX_BL 45
Arduino_DataBus *bus = new Arduino_ESP32LCD8(
0 /* DC */, GFX_NOT_DEFINED /* CS */, 47 /* WR */, GFX_NOT_DEFINED /* RD */,
9 /* D0 */, 46 /* D1 */, 3 /* D2 */, 8 /* D3 */, 18 /* D4 */, 17 /* D5 */, 16 /* D6 */, 15 /* D7 */);
Arduino_GFX *gfx = new Arduino_ST7796(bus, 4 /* RST */, 0 /* rotation */, true /* IPS */);
#include <WiFi.h>
#include "VNC_GFX.h"
#include <VNC.h>
VNC_GFX *vnc_gfx = new VNC_GFX(gfx);
arduinoVNC vnc = arduinoVNC(vnc_gfx);
void TFTnoWifi(void)
{
gfx->fillScreen(BLACK);
gfx->setCursor(0, ((gfx->height() / 2) - (5 * 8)));
gfx->setTextColor(RED);
gfx->setTextSize(5);
gfx->println("NO WIFI!");
gfx->setTextSize(2);
gfx->println();
}
void TFTnoVNC(void)
{
gfx->fillScreen(BLACK);
gfx->setCursor(0, ((gfx->height() / 2) - (4 * 8)));
gfx->setTextColor(GREEN);
gfx->setTextSize(4);
gfx->println("connect VNC");
gfx->setTextSize(2);
gfx->println();
gfx->print(VNC_IP);
gfx->print(":");
gfx->println(VNC_PORT);
}
void setup(void)
{
Serial.begin(115200);
// while (!Serial);
// Serial.setDebugOutput(true);
Serial.println("Arduino VNC");
Serial.println("Init display");
gfx->begin();
gfx->fillScreen(BLACK);
#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif
TFTnoWifi();
Serial.println("Init WiFi");
gfx->println("Init WiFi");
WiFi.mode(WIFI_STA);
WiFi.begin(SSID_NAME, SSID_PASSWORD);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
gfx->print(".");
}
Serial.println(" CONNECTED");
gfx->println(" CONNECTED");
Serial.println("IP address: ");
gfx->println("IP address: ");
Serial.println(WiFi.localIP());
gfx->println(WiFi.localIP());
TFTnoVNC();
Serial.println(F("[SETUP] VNC..."));
#ifdef SEPARATE_DRAW_TASK
draw_task_setup();
#endif
vnc.begin(VNC_IP, VNC_PORT);
vnc.setPassword(VNC_PASSWORD); // optional
}
void loop()
{
if (WiFi.status() != WL_CONNECTED)
{
vnc.reconnect();
TFTnoWifi();
delay(100);
}
else
{
vnc.loop();
if (!vnc.connected())
{
TFTnoVNC();
// some delay to not flood the server
delay(5000);
}
}
}
2.6_3D显示
安装库
https://github.com/vindar/tgx
#include <Arduino.h>
// screen driver library
#include <TFT_eSPI.h>
// graphic library
#include <tgx.h>
// the mesh to draw
#include "naruto.h"
// let's not burden ourselves with the tgx:: prefix
using namespace tgx;
// the screen driver
TFT_eSPI tft = TFT_eSPI();
// size of the drawing framebuffer
// (limited by the amount of memory in the ESP32).
#define SLX 140
#define SLY 200
// the framebuffer we draw onto
uint16_t fb[SLX*SLY];
// second framebuffer used by eSPI_TFT for DMA update
// allocated via malloc
uint16_t * fb2;
// the z-buffer
float* zbuf;
// the image that encapsulate framebuffer fb
Image<RGB565> imfb(fb,SLX,SLY);
// the 3D mesh drawer (with zbuffer and perspective projection)
Renderer3D<RGB565, SLX, SLY, true, false> renderer;
// the setup function runs once when you press reset or power the board
void setup()
{
Serial.begin(115200);
// allocate the second framebuffer
fb2 = (uint16_t*)malloc(SLX * SLY * sizeof(uint16_t));
while (fb2 == nullptr)
{
Serial.println("Error: cannot allocate memory for fb2");
delay(1000);
}
// allocate the zbuffer
zbuf = (float*)malloc(SLX * SLY * sizeof(float));
while (zbuf == nullptr)
{
Serial.println("Error: cannot allocate memory for zbuf");
delay(1000);
}
// initialize the screen driver
tft.init();
tft.setRotation(0);
tft.setSwapBytes(true);
tft.fillScreen(TFT_CYAN);
tft.initDMA();
tft.startWrite();
// setup the 3D renderer.
renderer.setImage(&imfb); // set the image to draw onto (ie the screen framebuffer)
renderer.setZbuffer(zbuf, SLX * SLY); // set the z buffer for depth testing
renderer.setPerspective(45, ((float)SLX) / SLY, 0.1f, 1000.0f); // set the perspective projection matrix.
renderer.setMaterial(RGBf(0.85f, 0.55f, 0.25f), 0.2f, 0.7f, 0.8f, 64); // bronze color with a lot of specular reflexion.
renderer.setOffset(0, 0);
}
/** Compute the model matrix according to the current time */
tgx::fMat4 moveModel(int & loopnumber)
{
const float end1 = 6000;
const float end2 = 2000;
const float end3 = 6000;
const float end4 = 2000;
int tot = (int)(end1 + end2 + end3 + end4);
int m = millis();
loopnumber = m / tot;
float t = m % tot;
const float dilat = 9; // scale model
const float roty = 360 * (t / 4000); // rotate 1 turn every 4 seconds
float tz, ty;
if (t < end1)
{ // far away
tz = -25;
ty = 0;
}
else
{
t -= end1;
if (t < end2)
{ // zooming in
t /= end2;
tz = -25 + 18 * t;
ty = -6.5f * t;
}
else
{
t -= end2;
if (t < end3)
{ // close up
tz = -7;
ty = -6.5f;
}
else
{ // zooming out
t -= end3;
t /= end4;
tz = -7 - 18 * t;
ty = -6.5 + 6.5 * t;
}
}
}
fMat4 M;
M.setScale({ dilat, dilat, dilat }); // scale the model
M.multRotate(-roty, { 0,1,0 }); // rotate around y
M.multTranslate({ 0,ty, tz }); // translate
return M;
}
/** Main loop */
void loop()
{
// compute the model position
int loopnumber;
fMat4 M = moveModel(loopnumber);
renderer.setModelMatrix(M);
// choose the shader to use
int shader = TGX_SHADER_GOURAUD | TGX_SHADER_TEXTURE; // default choice
switch (loopnumber % 3)
{
case 1: shader = TGX_SHADER_GOURAUD; break;
case 2: shader = TGX_SHADER_FLAT; break;
}
// draw the 3D mesh
imfb.fillScreen(RGB565_Black); // clear the framebuffer (black background)
renderer.clearZbuffer(); // clear the z-buffer
renderer.drawMesh(shader, &naruto_1, false);// draw the mesh !
// upload the framebuffer to the screen (async. via DMA)
tft.dmaWait();
tft.pushImageDMA((tft.width() - SLX)/2, (tft.height()-SLY)/2, SLX, SLY, fb, fb2);
}