29浏览
查看: 29|回复: 0

[项目] FireBeetle 2 ESP32-S3驱动64×32 LED点阵屏:时钟与汉字显示

[复制链接]
本帖最后由 云天 于 2025-8-1 20:53 编辑

【项目概述】

在本项目中,我将介绍如何使用FireBeetle 2 ESP32-S3开发板驱动64×32 RGB LED点阵屏,实现时钟显示和汉字滚动显示功能。通过模式切换,可以使用手机APP发送指令来控制显示内容。这个项目不仅展示了FireBeetle 2 ESP32-S3的强大功能,还结合了硬件驱动、网络通信和图形显示技术,适合有一定Arduino编程基础和硬件连接经验的创客。
FireBeetle 2 ESP32-S3驱动64×32  LED点阵屏:时钟与汉字显示图6
【硬件准备】
  • FireBeetle 2 ESP32-S3开发板:一款功能强大的物联网开发板,支持WiFi和蓝牙5.0双模通信,具备丰富的外设接口。
  • 64×32 RGB LED点阵屏:一款高亮度、全彩的LED显示屏,适合制作小型广告牌或信息显示设备。
  • 杜邦线若干:用于连接开发板和点阵屏。
  • 电源适配器:为点阵屏提供稳定的5V电源。
FireBeetle 2 ESP32-S3驱动64×32  LED点阵屏:时钟与汉字显示图7
【软件准备】
  • Arduino IDE 2.3.6:用于编写和上传代码到FireBeetle 2 ESP32-S3。
  • ESP32库:通过Arduino IDE的库管理器安装。
  • ESP32-HUB75-MatrixPanel-I2S-DMA库:用于驱动RGB LED点阵屏。
  • Mit App Inventor 2:用于创建手机APP,实现通过UDP发送指令的功能。
  • pctolcd2002:生成点阵数据。
【硬件连接】
  • 连接点阵屏
  • 将点阵屏的16P排线接口与FireBeetle 2 ESP32-S3的对应引脚连接。

  1. #define R1_PIN 0
  2. #define G1_PIN 9
  3. #define B1_PIN 18
  4. #define R2_PIN 7
  5. #define G2_PIN 38
  6. #define B2_PIN 3
  7. #define A_PIN 4
  8. #define B_PIN 5
  9. #define C_PIN 6
  10. #define D_PIN 8
  11. #define E_PIN -1 // 对于1/32扫描面板,如64x64,需要连接到ESP32的任意可用引脚,例如GPIO 32
  12. #define LAT_PIN 13
  13. #define OE_PIN 14
  14. #define CLK_PIN 12
复制代码
FireBeetle 2 ESP32-S3驱动64×32  LED点阵屏:时钟与汉字显示图5

  • 确保电源线(VCC和GND)连接正确。
2.连接电源适配器
  • 为点阵屏提供5V电源,确保电源适配器的电流足够(建议使用5V/4A或更高规格)。

【点阵数据】Pctolcd2002 是一款在中文创客社区中广泛使用的免费软件,用于生成字符点阵数据,这些数据可以被微控制器读取并在LED点阵屏或LCD显示屏上显示。该软件支持多种点阵大小和字体,可以生成汉字、ASCII字符等的点阵数据,非常适合嵌入式系统开发者使用。
FireBeetle 2 ESP32-S3驱动64×32  LED点阵屏:时钟与汉字显示图1

FireBeetle 2 ESP32-S3驱动64×32  LED点阵屏:时钟与汉字显示图2


【APP开发】

使用Mit App Inventor 2创建一个简单的APP,实现以下功能:
  • 输入FireBeetle 2 ESP32-S3的IP地址。
  • 发送UDP指令切换显示模式。
  • 显示当前模式对应的图片。

FireBeetle 2 ESP32-S3驱动64×32  LED点阵屏:时钟与汉字显示图3FireBeetle 2 ESP32-S3驱动64×32  LED点阵屏:时钟与汉字显示图4
注:udp扩展下载地址:http://ullisroboterseite.de/android-AI2-UDP/UrsAI2UDP.zip

【代码实现】
  1. #include <WiFi.h>
  2. #include <WiFiUdp.h>
  3. #include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
  4. #include <NTPClient.h>
  5. #define R1_PIN 0
  6. #define G1_PIN 9
  7. #define B1_PIN 18
  8. #define R2_PIN 7
  9. #define G2_PIN 38
  10. #define B2_PIN 3
  11. #define A_PIN 4
  12. #define B_PIN 5
  13. #define C_PIN 6
  14. #define D_PIN 8
  15. #define E_PIN -1 // 对于1/32扫描面板,如64x64,需要连接到ESP32的任意可用引脚,例如GPIO 32
  16. #define LAT_PIN 13
  17. #define OE_PIN 14
  18. #define CLK_PIN 12
  19. HUB75_I2S_CFG::i2s_pins _pins = {R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN};
  20. HUB75_I2S_CFG mxconfig(64, 32, 1, _pins);
  21. MatrixPanel_I2S_DMA dma_display(mxconfig);
  22. // WiFi网络配置
  23. const char* ssid = "*****";  // 替换为你的WiFi名称
  24. const char* password = "*********";  // 替换为你的WiFi密码
  25. // UDP配置
  26. WiFiUDP udp,ntpUDP;
  27. NTPClient timeClient(ntpUDP);
  28. unsigned int localPort = 8888;  // 本地UDP端口号
  29. char packetBuffer[255];  // 接收缓冲区
  30. // 数字和冒号的点阵数据(32x32分辨率)
  31. const uint8_t Dian[32][4] = {{0x00,0x00,0x00,0x00},
  32. {0x00,0x00,0x00,0x00},
  33. {0x00,0x01,0x00,0x00},
  34. {0x00,0x01,0x80,0x00},
  35. {0x00,0x01,0x80,0x00},
  36. {0x00,0x01,0x80,0x00},
  37. {0x00,0x01,0x80,0x60},
  38. {0x00,0x01,0xFF,0xF0},
  39. {0x00,0x01,0x80,0x00},
  40. {0x00,0x01,0x80,0x00},
  41. {0x00,0x01,0x80,0x00},
  42. {0x00,0x01,0x80,0x00},
  43. {0x01,0x01,0x80,0x80},
  44. {0x01,0xFF,0xFF,0xC0},
  45. {0x01,0x80,0x01,0x80},
  46. {0x01,0x80,0x01,0x80},
  47. {0x01,0x80,0x01,0x80},
  48. {0x01,0x80,0x01,0x80},
  49. {0x01,0x80,0x01,0x80},
  50. {0x01,0x80,0x01,0x80},
  51. {0x01,0xFF,0xFF,0x80},
  52. {0x01,0x80,0x01,0x80},
  53. {0x01,0x80,0x01,0x00},
  54. {0x00,0x00,0x00,0x00},
  55. {0x00,0x10,0x20,0x40},
  56. {0x02,0x08,0x30,0x60},
  57. {0x02,0x0C,0x18,0x30},
  58. {0x06,0x0C,0x18,0x38},
  59. {0x0C,0x06,0x18,0x18},
  60. {0x1C,0x04,0x08,0x18},
  61. {0x18,0x04,0x00,0x10},
  62. {0x00,0x00,0x00,0x00}};
  63. const uint8_t Zhen[32][4] = {{0x00,0x00,0x00,0x00},
  64. {0x00,0x00,0x00,0x00},
  65. {0x00,0x00,0x20,0x00},
  66. {0x00,0x00,0x38,0x00},
  67. {0x10,0x20,0x30,0x00},
  68. {0x1F,0xF0,0x20,0x00},
  69. {0x18,0x30,0x60,0x30},
  70. {0x18,0x6F,0xFF,0xF8},
  71. {0x18,0x40,0x40,0x00},
  72. {0x18,0x40,0xC0,0x00},
  73. {0x18,0x80,0xC8,0x00},
  74. {0x18,0x80,0x8E,0x00},
  75. {0x19,0x01,0x8C,0x00},
  76. {0x19,0x01,0x8C,0x00},
  77. {0x18,0x81,0x0C,0x00},
  78. {0x18,0x43,0x0C,0x30},
  79. {0x18,0x67,0xFF,0xF0},
  80. {0x18,0x22,0x0C,0x00},
  81. {0x18,0x30,0x0C,0x00},
  82. {0x18,0x30,0x0C,0x00},
  83. {0x18,0x30,0x0C,0x00},
  84. {0x1C,0x30,0x0C,0x18},
  85. {0x1B,0xEF,0xFF,0xFC},
  86. {0x18,0xE0,0x0C,0x00},
  87. {0x18,0x80,0x0C,0x00},
  88. {0x18,0x00,0x0C,0x00},
  89. {0x18,0x00,0x0C,0x00},
  90. {0x18,0x00,0x0C,0x00},
  91. {0x18,0x00,0x0C,0x00},
  92. {0x18,0x00,0x0C,0x00},
  93. {0x10,0x00,0x08,0x00},
  94. {0x00,0x00,0x00,0x00}};
  95. const uint8_t Shi[32][4] = {{0x00,0x00,0x00,0x00},
  96. {0x00,0x00,0x00,0x00},
  97. {0x00,0x00,0x02,0x00},
  98. {0x00,0x00,0x03,0x80},
  99. {0x00,0x00,0x03,0x00},
  100. {0x00,0x20,0x03,0x00},
  101. {0x1F,0xF0,0x03,0x00},
  102. {0x18,0x30,0x03,0x00},
  103. {0x18,0x30,0x03,0x00},
  104. {0x18,0x30,0x03,0x18},
  105. {0x18,0x3F,0xFF,0xFC},
  106. {0x18,0x30,0x03,0x00},
  107. {0x18,0x30,0x03,0x00},
  108. {0x18,0x30,0x03,0x00},
  109. {0x18,0x32,0x03,0x00},
  110. {0x1F,0xF1,0x03,0x00},
  111. {0x18,0x31,0xC3,0x00},
  112. {0x18,0x30,0xC3,0x00},
  113. {0x18,0x30,0xE3,0x00},
  114. {0x18,0x30,0x43,0x00},
  115. {0x18,0x30,0x03,0x00},
  116. {0x18,0x30,0x03,0x00},
  117. {0x18,0x30,0x03,0x00},
  118. {0x1F,0xF0,0x03,0x00},
  119. {0x18,0x30,0x03,0x00},
  120. {0x18,0x30,0x03,0x00},
  121. {0x18,0x00,0x03,0x00},
  122. {0x00,0x00,0x03,0x00},
  123. {0x00,0x00,0x3F,0x00},
  124. {0x00,0x00,0x07,0x00},
  125. {0x00,0x00,0x06,0x00},
  126. {0x00,0x00,0x00,0x00}};
  127. const uint8_t Zhong[32][4] = {
  128. {0x00,0x00,0x00,0x00},
  129. {0x00,0x00,0x00,0x00},
  130. {0x02,0x00,0x04,0x00},
  131. {0x03,0x80,0x06,0x00},
  132. {0x03,0x00,0x06,0x00},
  133. {0x02,0x00,0x06,0x00},
  134. {0x06,0x18,0x06,0x00},
  135. {0x07,0xFC,0x06,0x00},
  136. {0x04,0x00,0x06,0x10},
  137. {0x0C,0x01,0xFF,0xF8},
  138. {0x08,0x01,0x86,0x10},
  139. {0x08,0x31,0x86,0x10},
  140. {0x1F,0xF9,0x86,0x10},
  141. {0x13,0x01,0x86,0x10},
  142. {0x23,0x01,0x86,0x10},
  143. {0x43,0x01,0x86,0x10},
  144. {0x03,0x01,0x86,0x10},
  145. {0x03,0x19,0xFF,0xF0},
  146. {0x3F,0xFD,0x86,0x10},
  147. {0x03,0x01,0x06,0x10},
  148. {0x03,0x00,0x06,0x00},
  149. {0x03,0x00,0x06,0x00},
  150. {0x03,0x04,0x06,0x00},
  151. {0x03,0x08,0x06,0x00},
  152. {0x03,0x10,0x06,0x00},
  153. {0x03,0x60,0x06,0x00},
  154. {0x03,0xC0,0x06,0x00},
  155. {0x03,0x80,0x06,0x00},
  156. {0x01,0x00,0x06,0x00},
  157. {0x00,0x00,0x06,0x00},
  158. {0x00,0x00,0x04,0x00},
  159. {0x00,0x00,0x00,0x00}};
  160. const uint8_t num0[16][1] = {{0x00},{0x00},{0x00},{0x40},{0xA0},{0xA0},{0xA0},{0xA0},{0xA0},{0xA0},{0xA0},{0xA0},{0xA0},{0x40},{0x00},{0x00},/*"0",0*/};
  161. const uint8_t num1[16][1] = { {0x00},{0x00},{0x00},{0x00},{0x60},{0x20},{0x20},{0x20},{0x20},{0x20},{0x20},{0x20},{0x20},{0x70},{0x00},{0x00},/*"1",1*/
  162. };
  163. const uint8_t num2[16][1] = {
  164. {0x00},{0x00},{0x00},{0x40},{0xA0},{0xA0},{0xA0},{0x20},{0x40},{0x40},{0x40},{0x80},{0xA0},{0xE0},{0x00},{0x00},/*"2",2*/
  165. };
  166. const uint8_t num3[16][1] = {
  167. {0x00},{0x00},{0x00},{0x40},{0xA0},{0xA0},{0x20},{0x40},{0x20},{0x20},{0x20},{0xA0},{0xA0},{0xC0},{0x00},{0x00},/*"3",3*/
  168. };
  169. const uint8_t num4[16][1] = {{0x00},{0x00},{0x00},{0x20},{0x20},{0x20},{0x60},{0xA0},{0xA0},{0xA0},{0xF0},{0x20},{0x20},{0x30},{0x00},{0x00},/*"4",4*/
  170. };
  171. const uint8_t num5[16][1] = {{0x00},{0x00},{0x00},{0xE0},{0x80},{0x80},{0x80},{0xE0},{0x20},{0x20},{0x20},{0xA0},{0xA0},{0x40},{0x00},{0x00},/*"5",5*/
  172. };
  173. const uint8_t num6[16][1] = {
  174. {0x00},{0x00},{0x00},{0x60},{0xA0},{0x80},{0x80},{0xA0},{0xD0},{0x90},{0x90},{0x90},{0x90},{0x60},{0x00},{0x00},/*"6",6*/
  175. };
  176. const uint8_t num7[16][1] = {
  177. {0x00},{0x00},{0x00},{0x70},{0x50},{0x10},{0x20},{0x20},{0x20},{0x20},{0x20},{0x20},{0x20},{0x20},{0x00},{0x00},/*"7",7*/
  178. };
  179. const uint8_t num8[16][1] = {
  180. {0x00},{0x00},{0x00},{0x40},{0xA0},{0xA0},{0xA0},{0xE0},{0x40},{0xA0},{0xA0},{0xA0},{0xA0},{0x40},{0x00},{0x00},/*"8",8*/
  181. };
  182. const uint8_t num9[16][1] = {
  183. {0x00},{0x00},{0x00},{0x40},{0xA0},{0xA0},{0xA0},{0xA0},{0xA0},{0xE0},{0x20},{0x20},{0x60},{0x40},{0x00},{0x00},/*"9",9*/
  184. };
  185. const uint8_t colon[16][1] = {
  186. {0x00},{0x00},{0x00},{0x00},{0x00},{0x00},{0x20},{0x20},{0x00},{0x00},{0x00},{0x00},{0x20},{0x20},{0x00},{0x00},/*":",10*/
  187. };
  188. const uint8_t dian[16][1] = {{0x00},{0x00},{0x00},{0x00},{0x00},{0x00},{0x00},{0x00},{0x00},{0x00},{0x00},{0x60},{0x60},{0x60},{0x00},{0x00},};
  189. const uint8_t tu1[16][2]={
  190. {0x01,0xC0},{0x0E,0x38},{0x18,0x0C},{0x30,0x04},{0x60,0x06},{0x62,0x26},{0x20,0x84},{0x20,0x84},{0x38,0x04},{0x18,0x1C},{0x30,0x04},{0x30,0x04},{0xE0,0x0C},{0x70,0x04},{0x1F,0xF8},{0x01,0x20}
  191. };
  192. const uint8_t tu2[16][2]={
  193. {0x01,0xC0},{0x0E,0x38},{0x18,0x0C},{0x30,0x04},{0x60,0x06},{0x62,0x26},{0x20,0x84},{0x21,0xC4},{0x38,0x14},{0x18,0x1C},{0x34,0x04},{0x30,0x04},{0xE0,0x0C},{0x70,0x04},{0x1F,0xF8},{0x04,0x40},};
  194. const uint8_t (*digits[10])[16][1] = {&num0, &num1, &num2, &num3, &num4, &num5, &num6, &num7, &num8, &num9};
  195. const uint8_t (*characters[6])[32][4] = {&Dian, &Zhen, &Shi, &Zhong,&Dian, &Zhen}; // 字符数组
  196. const uint8_t (*colon_ptr)[16][1] = :
  197. int x_offset = -16; // 横向偏移量
  198. int char_width = 8; // 每个字符的宽度
  199. int x_offset2 = 0; // 横向偏移量
  200. int num_chars2 = 6; // 字符数量
  201. int char_width2 = 32; // 每个字符的宽度
  202. int py1=0;
  203. IPAddress myip;
  204. int foot=0;
  205. int bs=0;
  206. void setup() {
  207.   // 初始化串口
  208.   Serial.begin(115200);
  209.   Serial.println("ESP32-S3 UDP接收端启动中...");
  210.   dma_display.begin();
  211.   dma_display.setBrightness(128);
  212.   dma_display.fillScreen(0);
  213.   // 连接WiFi
  214.   WiFi.mode(WIFI_STA);
  215.   WiFi.begin(ssid, password);
  216.   // 等待连接WiFi
  217.   while (WiFi.status() != WL_CONNECTED) {
  218.     delay(500);
  219.     Serial.print(".");
  220.   }
  221.   Serial.println("");
  222.   Serial.println("WiFi连接成功!");
  223.   Serial.print("IP地址: ");
  224.   Serial.println(WiFi.localIP());
  225.   myip=WiFi.localIP();
  226.   // 启动UDP监听
  227.   udp.begin(localPort);
  228.   Serial.print("UDP服务器在端口 ");
  229.   Serial.print(localPort);
  230.   Serial.println(" 上启动");
  231.   timeClient.begin();
  232.   timeClient.setTimeOffset(28800); // 设置时区为 GMT+8 (8 * 3600 = 28800 秒)
  233.   timeClient.update();
  234. }
  235. String receivedString ="";
  236. void loop() {
  237.   static unsigned long previousMillis = 0; // 上一次更新时间
  238.   static unsigned long previousMillis2 = 0; // 上一次更新时间
  239.   static unsigned long previousMillis3 = 0; // 上一次更新时间
  240.   static unsigned long previousMillis4 = 0; // 上一次更新时间
  241.   unsigned long currentMillis = millis(); // 获取当前时间(毫秒)
  242.   // 检查是否有UDP数据包到达
  243.   if(receivedString ==""){
  244.     if (currentMillis - previousMillis >= 200) {
  245.     previousMillis = currentMillis; // 更新上一次时间
  246.     dma_display.fillScreen(0);
  247.     extractIPAddress(myip);
  248.     py1=py1-1;
  249.     if(py1<-14*8){
  250.       py1=64;
  251.     }
  252.     }
  253.   }
  254.   else if(receivedString =="1"){
  255.     if (currentMillis - previousMillis3 >= 1000) {
  256.         previousMillis3 = currentMillis; // 更新上一次时间
  257.         bs=1-bs;
  258.     }
  259.    if (currentMillis - previousMillis2 >= 200) {
  260.     previousMillis2 = currentMillis; // 更新上一次时间
  261.     // 更新时间
  262.     timeClient.update();
  263.      // 获取当前时间
  264.      int hours = timeClient.getHours();
  265.      int minutes = timeClient.getMinutes();
  266.      int seconds = timeClient.getSeconds();
  267.      // 清屏
  268.      dma_display.fillScreen(0);
  269.      // 绘制时间
  270.      drawDigit(2, 0, *digits[hours / 10], dma_display.color565(0, 0, 255));
  271.      drawDigit(2+char_width, 0, *digits[hours % 10], dma_display.color565(0, 0, 255));
  272.      if(bs==0){
  273.         drawDigit(2+2 * char_width, 0, colon, dma_display.color565(255, 0,0));
  274.      }
  275.      else{
  276.         drawDigit(2+2 * char_width, 0, colon, dma_display.color565(255, 255,0));
  277.      }
  278.      drawDigit(2+3 * char_width, 0, *digits[minutes / 10], dma_display.color565(0, 0, 255));
  279.      drawDigit(2+4 * char_width, 0, *digits[minutes % 10], dma_display.color565(0, 0, 255));
  280.      if(bs==0){
  281.         drawDigit(2+5 * char_width, 0, colon, dma_display.color565(255, 0,0));
  282.      }
  283.      else{
  284.         drawDigit(2+5 * char_width, 0, colon, dma_display.color565(255, 255,0));
  285.      }
  286.      drawDigit(2+6 * char_width, 0, *digits[seconds / 10], dma_display.color565(0, 0, 255));
  287.      drawDigit(2+7 * char_width, 0, *digits[seconds % 10], dma_display.color565(0, 0, 255));
  288.      if(foot==1){
  289.         drawpic(x_offset, 16, tu1, dma_display.color565(0, 255, 0));
  290.      }
  291.      else{
  292.         drawpic(x_offset, 16, tu2, dma_display.color565(0, 255, 0));
  293.      }
  294.      foot=1-foot;
  295.      // 更新偏移量
  296.      x_offset++;
  297.      if (x_offset >80) {
  298.           x_offset = -16; // 重置偏移量,实现循环滚动
  299.      }
  300.    }
  301.   }
  302.   else if(receivedString =="2"){
  303.    if (currentMillis - previousMillis4 >= 500) {
  304.     previousMillis4 = currentMillis; // 更新上一次时间
  305.     dma_display.fillScreen(0);
  306.      // 绘制所有字符
  307.     for (int i = 0; i < num_chars2; i++) {
  308.       int char_x = x_offset2 + i * char_width2; // 计算当前字符的起始x坐标
  309.       drawChar(char_x, 0, *characters[i], dma_display.color565(0, 0, 255)); // 绘制字符
  310.     }
  311.     // 更新偏移量
  312.     x_offset2--;
  313.     // 更新偏移量
  314.     x_offset2--;
  315.     if (x_offset2 < -char_width2*4) {
  316.       x_offset2 =0; // 重置偏移量,实现循环滚动
  317.     }
  318.    }
  319.   }
  320.   int packetSize = udp.parsePacket();
  321.   if (packetSize) {
  322.     Serial.print("收到来自 ");
  323.     IPAddress remoteIp = udp.remoteIP();
  324.     Serial.print(remoteIp);
  325.     Serial.print(":");
  326.     Serial.print(udp.remotePort());
  327.     Serial.print(" 的数据包,大小: ");
  328.     Serial.println(packetSize);
  329.     // 读取数据包内容
  330.     int len = udp.read(packetBuffer, 255);
  331.     if (len > 0) {
  332.       packetBuffer[len] = 0;  // 添加字符串结束符
  333.     }
  334.     Serial.print("内容: ");
  335.     Serial.println(packetBuffer);
  336.     // 将packetBuffer转换为字符串
  337.     receivedString = String(packetBuffer);
  338.     // 清空packetBuffer
  339.     memset(packetBuffer, 0, sizeof(packetBuffer));
  340.     // 可以在这里添加对接收到的数据的处理逻辑
  341.   }
  342.   // 短暂延时,让CPU有机会处理其他任务
  343.   delay(10);
  344. }
  345. void extractIPAddress(IPAddress ip) {
  346.   String ipStr = ip.toString();  // 将IPAddress对象转换为字符串
  347.   Serial.print("IP地址逐字符处理: ");
  348.   int py2=0;
  349.   for (int i = 0; i < ipStr.length(); i++) {
  350.     char c = ipStr.charAt(i);  // 获取当前字符
  351.     if (c == '.') {
  352.       Serial.print(".");
  353.       drawDigit(py1+py2*8, 0, dian, dma_display.color565(0, 0, 255));
  354.       py2=py2+1;
  355.     } else {
  356.       // 将字符转换为数字
  357.       int num = c - '0';  // ASCII码转换
  358.       Serial.print(num);
  359.       drawDigit(py1+py2*8, 0, *digits[num], dma_display.color565(0, 0, 255));
  360.       py2=py2+1;
  361.     }
  362.   }
  363.   Serial.println();
  364. }
  365. // 绘制字符的函数
  366. void drawDigit(int x, int y, const uint8_t charData[16][1], uint16_t color) {
  367.   for (int row = 0; row < 16; row++) {
  368.     for (int col = 0; col < 1; col++) {
  369.       for (int i = 0; i < 8; i++) {
  370.         if (charData[row][col] & (1 << i)) {
  371.           // 在点阵屏上绘制像素
  372.           dma_display.drawPixel(x + col * 8 + 7 - i, y + row, color);
  373.         }
  374.       }
  375.     }
  376.   }
  377. }
  378. void drawpic(int x, int y, const uint8_t charData[16][2], uint16_t color) {
  379.   for (int row = 0; row < 16; row++) {
  380.     for (int col = 0; col < 2; col++) {
  381.       for (int i = 0; i < 8; i++) {
  382.         if (charData[row][col] & (1 << i)) {
  383.           // 在点阵屏上绘制像素
  384.           dma_display.drawPixel(x + col * 8 + 7 - i, y + row, color);
  385.         }
  386.       }
  387.     }
  388.   }
  389. }
  390. // 绘制字符的函数
  391. void drawChar(int x, int y, const uint8_t charData[32][4], uint16_t color) {
  392.   for (int row = 0; row < 32; row++) {
  393.     for (int col = 0; col < 4; col++) {
  394.       // 检查当前列是否需要点亮
  395.       for(int i=0;i<8;i++){
  396.        if (charData[row][col] & (1 <<i)) {
  397.         // 在点阵屏上绘制像素
  398.         dma_display.drawPixel(x + col*8+7-i, y + row, color);
  399.       }
  400.     }
  401.     }
  402.   }
  403. }
复制代码
程序是在ESP32-S3开发板上控制64x32 RGB LED点阵屏。程序通过WiFi连接到网络,并使用UDP协议接收来自App Inventor 2制作的APP的指令,以切换显示模式。点阵屏可以显示时钟、小动画,以及滚动显示汉字“点阵时钟”。
程序的主要功能和流程如下:
  • 初始化串口、WiFi、UDP服务和NTP时间客户端。
  • 连接到指定的WiFi网络,并启动UDP服务以监听特定端口。
  • 定义了多个汉字和数字的点阵数据,这些数据用于在点阵屏上显示字符。
  • 在loop函数中,程序首先检查是否有UDP数据包到达。如果有,它将读取数据包并根据内容切换显示模式。
  • 根据当前的显示模式,程序将执行不同的显示逻辑:
    • 模式一(默认):显示时钟和小动画。
    • 模式二:滚动显示汉字“点阵时钟”。
  • 使用drawDigit、drawChar和drawpic函数在点阵屏上绘制数字、汉字和自定义图案。
代码中还包含了一个extractIPAddress函数,用于将ESP32-S3的IP地址显示在点阵屏上。

【演示视频】





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

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail