深圳晶彩智能3.5寸彩色屏PlatformIO本地气象站时钟和播放GIF
本帖最后由 topdog 于 2022-9-11 08:06 编辑深圳晶彩智能3.5寸彩色屏采用分辨率480x320彩色液晶屏,驱动芯片是ST7796,板载乐鑫公司出品ESP-WROOM-32,Flash 4M。本示例外接一块Bosch BME280 I2C测量本地的温湿度、大气压等做气象站,通过WIFI校时显示北京时间,为了增加趣味性播放一段小黄人的GIF。使用PlatformIO编译速度非常快捷,为了节约时间所以推荐采用此方法。
1,自定义核心
PlatformIO IDE允许自定义核心的,Windows 10系统依C:\Users\用户名\.platformio\platforms\espressif32\boards路径,用记事本或者nodepad++把以下文字保存为JCZN_WROOM_4M.json ,这样在Boards中就能找到自定义名称板子了,设置核心为ESP-WROOM-32 ,外接闪存设置成了4M。
{
"build": {
"arduino":{
"ldscript": "esp32_out.ld"
},
"core": "esp32",
"extra_flags": "-DARDUINO_ESP32_DEV",
"f_cpu": "240000000L",
"f_flash": "40000000L",
"flash_mode": "qio",
"mcu": "esp32",
"variant": "esp32"
},
"connectivity": [
"wifi",
"bluetooth",
"ethernet",
"can"
],
"debug": {
"openocd_board": "esp-wroom-32.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "JCZN WROOM 4M",
"upload": {
"flash_size": "4MB",
"maximum_ram_size":327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed":460800
},
"url": "http://www.jczn1688.com/sy",
"vendor": "JCZN"
}
2,示例项目文件结构:
A:项目要用到TFT_eSPI 、ESP32Time库,直接用pio home安装,其他的TJpg_Decoder 、SparkFun_BME280就需要安装到lib文件夹里面,点击New Terminal输入以下命令:
cd lib
git clone https://github.com/Bodmer/TJpg_Decoder.git
git clone https://github.com/sparkfun/SparkFun_BME280_Arduino_Library.git
B:录一段小黄人的视频或者下载GIF,在线编辑 https://ezgif.com/修改成你需要的大小,然后切片放入data文件夹里面。不要超过flash的容量呦。
C:特色字体库的制作,参见《合宙ESP32C3使用TFT_eSPI库操作ST7735s屏幕 (中)》,自定义字库放入include文件夹,特别强调注意vlw文件转C的网站地址:https://tomeko.net/online_tools/file_to_hex.php?lang=en
3,深圳晶彩智能3.5寸彩色屏,针对TFT_eSPI 学习而设计制作,能够非常完美地实现触碰所有功能。这里没用用到触碰功能所以没有设置,不用的字库也没有引用。对应的platformio.ini 做如下修改:
platform = espressif32
board = JCZN_WROOM_4M
framework = arduino
upload_speed = 921600
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_speed = 115200
upload_port = COM9
board_build.mcu = esp32
upload_protocol = esptool
board_build.f_cpu = 240000000L
lib_ldf_mode = deep
board_build.partitions = huge_app.csv
platform_packages =
platformio/framework-arduinoespressif32@^3.20004.0
build_flags = -DCORE_DEBUG_LEVEL=3
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
-DUSER_SETUP_LOADED=1
-DDEBUG=1
-DST7796_DRIVER=1
-DTFT_RGB_ORDER=TFT_BGR
-DTFT_WIDTH=320
-DTFT_HEIGHT=480
-DTFT_BACKLIGHT_ON=HIGH
-DTFT_BACKLIGHT_OFF=LOW
-DTFT_MISO=12
-DTFT_MOSI=13
-DTFT_SCLK=14
-DTFT_CS=15
-DTFT_DC=2
-DTFT_RST=-1
-DTFT_BL=27
-DLOAD_FONT4=1
-DSMOOTH_FONT=1
-DSPI_FREQUENCY=65000000
-DSPI_READ_FREQUENCY=20000000
-DSPI_TOUCH_FREQUENCY=2500000
-DUSE_HSPI_PORT=1
lib_deps =
bodmer/TFT_eSPI@^2.4.75
fbiego/ESP32Time@^2.0.0
4,程序入口main.cpp如下:
#include <Arduino.h>
#include <WiFiMulti.h>
#include <ESP32Time.h>
#include "sntp.h"
#include <Wire.h>
#include "SparkFunBME280.h"
#include "SPIFFS.h"
#include <TJpg_Decoder.h>
#include "SPI.h"
#include <TFT_eSPI.h> // Hardware-specific library
#include "HANYI30.h"
struct tm timeinfo;
BME280 mySensor;
WiFiMulti WiFiMulti;
ESP32Time rtc;
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
TFT_eSprite greeting = TFT_eSprite(&tft);//创建子画面实体
TFT_eSprite LowerLeft = TFT_eSprite(&tft); //创建子画面实体
#define TFT_PINK 0xFE19
int imgNum = 0;
String IP;
const char *ssid = "ssid";
const char *password = "password";
const char *ntpServer1 = "ntp1.aliyun.com"; //阿里云NTP时间源服务器
const char *ntpServer2 = "s1a.time.edu.cn"; //北京邮电大学
const long gmtOffset_sec = 8 * 3600; //参数就是用来修正时区的,比如对于我们东八区(UTC/GMT+08:00)来说该参数就需要填写 8 * 3600
const int daylightOffset_sec = 0; //使用夏令时 daylightOffset_sec 就填写3600,否则就填写0;
void listSPIFFS(void)
{
Serial.println(F("\r\nListing SPIFFS files:"));
static const char line[] PROGMEM = "=================================================";
Serial.println(FPSTR(line));
Serial.println(F("File name Size"));
Serial.println(FPSTR(line));
fs::File root = SPIFFS.open("/");
if (!root)
{
Serial.println(F("Failed to open directory"));
return;
}
if (!root.isDirectory())
{
Serial.println(F("Not a directory"));
return;
}
fs::File file = root.openNextFile();
while (file)
{
if (file.isDirectory())
{
Serial.print("DIR : ");
String fileName = file.name();
Serial.print(fileName);
}
else
{
String fileName = file.name();
Serial.print("" + fileName);
// File path can be 31 characters maximum in SPIFFS
int spaces = 33 - fileName.length(); // Tabulate nicely
if (spaces < 1)
spaces = 1;
while (spaces--)
Serial.print(" ");
String fileSize = (String)file.size();
spaces = 10 - fileSize.length(); // Tabulate nicely
if (spaces < 1)
spaces = 1;
while (spaces--)
Serial.print(" ");
Serial.println(fileSize + " bytes");
}
file = root.openNextFile();
}
Serial.println(FPSTR(line));
Serial.println();
delay(1000);
}
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);
// Return 1 to decode next block
return 1;
}
void setup()
{
Serial.begin(115200);
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
pinMode(16, OUTPUT);
digitalWrite(16, HIGH);
pinMode(17, OUTPUT);
digitalWrite(17, HIGH);
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
WiFi.mode(WIFI_STA);
WiFiMulti.addAP(ssid, password);
if (WiFiMulti.run() == WL_CONNECTED)
{
IP = WiFi.localIP().toString();
sntp_servermode_dhcp(1);
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);
}
if (getLocalTime(&timeinfo))
{
rtc.setTimeStruct(timeinfo);
}
Wire.begin();
Wire.setClock(400000); //调节到快速 I2C 速度!
mySensor.setI2CAddress(0x76); // I2C地址0x76
mySensor.beginI2C();
mySensor.setFilter(1); // 0 至 4 有效, 滤波器系数, 见手册 3.4.4 IIR filter
mySensor.setStandbyTime(0); // 0 至 7 有效 读数之间的时间
mySensor.setTempOverSample(1); // 0 至 16 有效 ,为0时禁用温度传感.见手册 4.2.2、5.4.8
mySensor.setPressureOverSample(1); // 0 至 16 有效 ,为0时禁用大气压传感.见手册 5.4.7
mySensor.setHumidityOverSample(1); // 0 至 16 有效 ,为0时禁用湿度传感. 见手册5.4.9
mySensor.setMode(MODE_NORMAL); // MODE_SLEEP(睡眠模式), MODE_FORCED(突发模式), MODE_NORMAL (普通模式)有效,见手册 3.3 Sensor modes
if (!SPIFFS.begin())
{
Serial.println("SPIFFS initialisation failed!");
while (1)
yield(); // Stay here twiddling thumbs waiting
}
Serial.println("\r\nInitialisation done.");
listSPIFFS();
tft.begin();
tft.init();
tft.setRotation(3);
tft.fillScreen(TFT_BLACK);
TJpgDec.setJpgScale(1);
TJpgDec.setSwapBytes(true);
TJpgDec.setCallback(tft_output);
}
void loop()
{
greeting.setColorDepth(8);
// 创建子画面清晰的背景大小设置色彩为黑色
greeting.createSprite(479, 179);
greeting.fillSprite(TFT_BLACK);
greeting.setTextWrap(true);
greeting.loadFont(hanyi30);
greeting.setTextDatum(MR_DATUM); //顶部中心基准
greeting.setCursor(5, 10);
greeting.setTextColor(TFT_PINK, TFT_BLACK);
greeting.print("深圳晶彩智能");
greeting.println("祝大家中秋节快乐!");
greeting.println();
greeting.setTextColor(TFT_GREEN, TFT_BLACK);
greeting.print("温度:");
greeting.println(mySensor.readTempC(), 0);
greeting.print("湿度:");
greeting.println(mySensor.readFloatHumidity(), 0);
greeting.print("高度:");
greeting.println(mySensor.readFloatAltitudeMeters(), 0);
greeting.print("大气压:");
greeting.println(mySensor.readFloatPressure(), 0);
greeting.unloadFont();
greeting.pushSprite(0, 0); //将子画面推到 x, y 处的TFT
greeting.deleteSprite(); //删除子画面
LowerLeft.setColorDepth(8);
LowerLeft.createSprite(240, 180);
LowerLeft.fillSprite(TFT_BLACK);
LowerLeft.loadFont(hanyi30);
LowerLeft.setTextWrap(true);
LowerLeft.setTextColor(TFT_YELLOW, TFT_BLACK);
LowerLeft.setCursor(5, 0);
LowerLeft.print("日期:");
LowerLeft.setCursor(5, 55);
LowerLeft.print("时间:");
LowerLeft.setCursor(5, 105);
LowerLeft.print("地址:");
LowerLeft.unloadFont();
LowerLeft.setTextColor(TFT_CYAN, TFT_BLACK);
LowerLeft.setTextFont(4);
LowerLeft.setCursor(5, 25);
LowerLeft.print(rtc.getDate());
LowerLeft.setCursor(5, 75);
LowerLeft.print(rtc.getTime());
LowerLeft.setCursor(75, 105);
LowerLeft.print(IP);
LowerLeft.pushSprite(0, 180);
LowerLeft.deleteSprite();
if (imgNum > 9)
imgNum = 0;
String imgPath = "/frame_";
imgPath += imgNum++;
imgPath += ".jpg";
TJpgDec.drawFsJpg(240, 180, imgPath);
yield();
}
5,成品效果见抖音短视频。
https://www.douyin.com/video/7141723080203799819
6,示例项目开源Arduino版的见附件
页:
[1]