深圳晶彩智能3.5寸彩色屏PlatformIO本地气象站时钟和播放GIF-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 692|回复: 0

深圳晶彩智能3.5寸彩色屏PlatformIO本地气象站时钟和播放GIF

[复制链接]
发表于 2022-9-10 20:55 | 显示全部楼层 |阅读模式
本帖最后由 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。

[pre]{
  "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"
}[/pre]
2,示例项目文件结构:
A:项目要用到TFT_eSPI 、ESP32Time库,直接用pio home安装,其他的TJpg_Decoder 、SparkFun_BME280就需要安装到lib文件夹里面,点击New Terminal输入以下命令:
[pre]cd lib
git clone https://github.com/Bodmer/TJpg_Decoder.git
git clone https://github.com/sparkfun/SparkFun_BME280_Arduino_Library.git[/pre]

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   

文件结构.PNG

3,深圳晶彩智能3.5寸彩色屏,针对TFT_eSPI 学习而设计制作,能够非常完美地实现触碰所有功能。这里没用用到触碰功能所以没有设置,不用的字库也没有引用。对应的platformio.ini 做如下修改:
[pre][env:JCZN_WROOM_4M]
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[/pre]

4,程序入口main.cpp如下:
[pre]#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();
}[/pre]
5,成品效果见抖音短视频。
https://www.douyin.com/video/7141723080203799819

6,示例项目开源Arduino版的见附件
JCZN_Best_wishes_for_Mid_Autumn_Festival.rar (273.3 KB, 下载次数: 0)










您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-12-1 01:22 , Processed in 0.157802 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表