2_Arduino-ESP32_呼吸灯,ST7789_SPI_1.54

4,199 阅读6分钟

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

github.com/lorol/LITTL…

修改图片尺寸并转换图片格式为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);
}

20210630155107.jpg

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

image.png

#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);
    }