新开发的Raspberry Pi RP2040开发板,带有DVI接口和以太网。该项目可以显示 adafruit IO 仪表板并跟踪任何更改。
转发: RP2040-ETH-DVI-ZERO: Adafruit IO Dashboard Monitor ( HDMI / DVI Mode )
项目介绍
我新开发了一块Raspberry Pi RP2040开发板,具有DVI/HDMI输出接口,使用W5100S作为网络接入,并具有TF卡插槽。我将这款开发板命名为“RP2040-ETH-DVI-ZERO”。
据我所知,RP2040-ETH-DVI-ZERO是第一款具有网络功能的RP2040 DVI开发板。
RP2040-ETH-DVI-ZERO采用与Raspberry Pi ZERO W官方相同的外部尺寸和接口设计,RP2040-ETH-DVI-ZERO可以使用Raspberry Pi ZERO W官方外壳。
RP2040-ETH-DVI-ZERO可以同时进行网络通信和图像DVI输出显示。
可以生成320*240分辨率的图像,并通过扩展至640*480(60Hz)输出到显示器
为什么要开发 RP2040-ETH-DVI-ZERO?
市面上有很多带DVI接口的RP2040开发板,但均不具备网络通信功能。由于PICO W的无线通讯部分占用大量动态内存,DVI输出功能无法与网络功能同时使用,或者其使用受到很大限制。因为使用W5100S可以让网络通信只使用少量的动态内存,所以我开发了RP2040-ETH-DVI-ZERO和项目。
这只是 W5100S 使用的动态内存的 2%。
这是 PICO W 使用的动态内存的 25%,对比相当明显。
RP2040-DVI-ETH-ZERO 硬件:
顶视图:
底视图:
我对W5100S部分使用与W5100S_EVB_PICO相同的引脚设计,因此我可以使用W5100S_EVB_PICO的所有参考代码。
该项目的名称是“Adafruit IO Dashboard Monitor”。我的想法是为 adafruit.io 开发一个通用的仪表板显示界面。所有显示内容均从adafruit.io获取,包括显示adafruit.io的dashboard框架并解析dashboard中包含的所有Block,自动订阅dashboard中包含的所有“feed”内容,并通过DVI输出到显示器。
因为是通用的显示框架,所以我在程序中没有定义任何固定的参数。所有参数都是通过Adafruit.io获取的,包括显示框架。
因为没有固定的参数,所以我们在第一次开机的时候需要通过内嵌的网页输入adafruit的访问参数。
RP2040-ETH-DVI-ZERO的屏幕显示:
通过浏览嵌入式网页输入三个参数,即 adafruit 的名称、密钥和仪表板 id。
通过浏览嵌入式网页输入三个参数,即 adafruit 的名称、密钥和仪表板 id。点击“提交”后,将被存储到RP2040内部的Flash中。下次打开RP2040-ETH-DVI-ZERO时,将跳过从浏览器获取参数的步骤,直接运行“获取adafruit仪表板”。
该嵌入网页的HTML代码:
#ifndef PROGMEM
#define PROGMEM
#endif
const char html_page[] PROGMEM = {
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n" // the connection will be closed after completion of the response
//"Refresh: 5\r\n" // refresh the page automatically every n sec
"\r\n"
"<!DOCTYPE HTML>"
"<html>"
"<head>"
"<meta charset='UTF-8'>"
"<title>RP2040-DVI-ETH-ZERO : adafruit io monitor</title>"
"<link rel='icon' href='https://cdn-learn.adafruit.com/guides/images/000/000/570/medium800/AIO_LOGO.png' type='image/x-icon'>"
"</head>"
"<body>"
"<p style='text-align:center;'>"
"<img alt='ChatGPT' src='https://cdn-learn.adafruit.com/guides/images/000/000/570/medium800/AIO_LOGO.png' height='200' width='200'>"
"<h1 align='center'>RP2040-DVI-ETH-ZERO</h1>"
"<h1 align='center'>adafruit io monitor</h1>"
"<div style='width:900px;margin: 0 auto;text-align: center'>"
"<form action='/' method='post'>"
"<input type='text' placeholder='adafruit io user' size='35' name='adafruit_user' required='required'/><br><br>"
"<input type='text' placeholder='adafruit io key' size='35' name='adafruit_key' required='required'/><br><br>"
"<input type='text' placeholder='adafruit io dashboard key' size='35' name='dashboard_key' required='required'/><br><br>"
"<input type='submit' value='Submit' style='height:30px; width:80px;'/>"
"</form>"
"<div style='text-align: left;'>"
"<h5>"
"</h5>\r\n"
"</div>"
"</div>"
"</body>\r\n"
"<html>\r\n"
};
获取adafruit.io的仪表板,我们可以得到屏幕上需要显示的所有框架的布局,包括所有块信息、位置、大小、对应的Feeds和Groups。
#define dashboard_layout_max 16
struct _Dashboard_Layout{
boolean exist = false;
uint8_t x;
uint8_t y;
uint8_t x_size;
uint8_t y_size;
String id;
};
_Dashboard_Layout dashboard_layout[dashboard_layout_max];
这是仪表板“RP2040-DVI-ETH-ZERO”的 Adafruit io 网络版本。
A_1_temp (W5100S_POE_EVB_PICO)
A_2_温度(W5100S_EVB_PICO)
A_3_temp (W5100S_EVB_PICO)
A_4_温度(W5500_EVB_PICO)
四块 PICO 板每 10 秒将这些内置温度传感器数据上传到 adafruit.io。
(因为我使用的是Adafruit的免费服务,所以通讯次数有限制)
这是我获取仪表板信息并重构显示框架后的仪表板。
在解析仪表板消息时,我们还获取了所有相关的 Feed 和 Group 信息。代码会自动订阅所有的Feed和Group,并将它们放入消息回调处理函数中。
#define dashboard_block_max 16
struct _Dashboard_block{
boolean exist = false;
String id;
String name;
String type;
String label;
uint16_t minValue;
uint16_t maxValue;
uint16_t minWarning;
uint16_t maxWarning;
uint8_t decimal;
String feed_id;
String feed_key;
String feed_name;
String group_key;
};
_Dashboard_block dashboard_block[dashboard_block_max];
消息回调处理代码:
// you can also attach multiple feeds to the same
// meesage handler function. both counter and counter-two
// are attached to this callback function, and messages
// for both will be received by this function.
void feed_handle(AdafruitIO_Data *data) {
Serial.print("received <- ");
// since we are using the same function to handle
// messages for two feeds, we can use feedName() in
// order to find out which feed the message came from.
Serial.print(data->feedName());
Serial.print(" ");
// print out the received count or counter-two value
Serial.println(data->value());
for(int i=0; i<dashboard_block_max; i++)
{
String Feed_data_decimal;
if(dashboard_block[i].feed_key == data->feedName())
{
display.setTextColor(DashBoard_Text_Color);
std::string Feed_data_decimal(data->value());
if(Feed_data_decimal.find('.')!= -1)
{
Feed_data_decimal = (Feed_data_decimal.substr(0,Feed_data_decimal.find('.')+1+dashboard_block[i].decimal));
Serial.println(Feed_data_decimal.c_str());
}
if(dashboard_block[i].type == "slider")
{
display.fillRoundRect(dashboard_layout[i].x*20+4,dashboard_layout[i].y*20+2+_y+dashboard_layout[i].y_size*20-16,dashboard_layout[i].x_size*20-8,10,3,DashBoard_Back_Color);
display.fillRoundRect(dashboard_layout[i].x*20+5,dashboard_layout[i].y*20+2+_y+dashboard_layout[i].y_size*20-15,dashboard_layout[i].x_size*20-10,8,3,adafruit_grey);
uint16_t slider_point = (atoi(data->value()))*(dashboard_layout[i].x_size*20-10)/100;
display.fillRoundRect(dashboard_layout[i].x*20+5,dashboard_layout[i].y*20+2+_y+dashboard_layout[i].y_size*20-15,slider_point,8,3,DashBoard_Bar_Color);
display.fillRect(dashboard_layout[i].x*20+dashboard_layout[i].x_size*10-7,dashboard_layout[i].y*20+4+_y+10,30,10,DashBoard_Back_Color);
display.setTextSize(1);
display.setCursor(dashboard_layout[i].x*20+dashboard_layout[i].x_size*10-7,dashboard_layout[i].y*20+4+_y+10);
display.println(atoi(data->value()));
}
else if(dashboard_block[i].type == "gauge")
{
display.fillArc(dashboard_layout[i].x*20+dashboard_layout[i].x_size*10,dashboard_layout[i].y*20+4+_y+40, 24, 30, 105, 75, DashBoard_Bar_Back_Color);
display.setCursor(dashboard_layout[i].x*20 + 13, dashboard_layout[i].y*20 + _y + dashboard_layout[i].y_size*20 - 12);
display.println(dashboard_block[i].minValue);
display.setCursor(dashboard_layout[i].x*20 + dashboard_layout[i].x_size*20 -18 , dashboard_layout[i].y*20 + _y + dashboard_layout[i].y_size*20 - 12);
display.println(dashboard_block[i].maxValue);
display.setCursor(dashboard_layout[i].x*20+dashboard_layout[i].x_size*10, dashboard_layout[i].y*20+4+_y+48);
if((atoi(data->value()) <= dashboard_block[i].minWarning)||(atoi(data->value()) >= dashboard_block[i].maxWarning))
{
display.fillArc(dashboard_layout[i].x*20+dashboard_layout[i].x_size*10,dashboard_layout[i].y*20+4+_y+40, 24, 30, 105, 105+ (atoi(data->value()) * 330 / 100), adafruit_red);
DashBoard_warning = dashboard_block[i].maxWarning * 330 / 100;
display.fillArc(dashboard_layout[i].x*20+dashboard_layout[i].x_size*10,dashboard_layout[i].y*20+4+_y+40, 24, 30, 105+DashBoard_warning-1, 105+DashBoard_warning, BLACK);
}
else
{
display.fillArc(dashboard_layout[i].x*20+dashboard_layout[i].x_size*10,dashboard_layout[i].y*20+4+_y+40, 24, 30, 105, 105+ (atoi(data->value()) * 330 / 100), DashBoard_Bar_Color);
DashBoard_warning = dashboard_block[i].maxWarning * 330 / 100; display.fillArc(dashboard_layout[i].x*20+dashboard_layout[i].x_size*10,dashboard_layout[i].y*20+4+_y+40, 24, 30, 105+DashBoard_warning-1, 105+DashBoard_warning, adafruit_red);
}
display.fillRect(dashboard_layout[i].x*20+dashboard_layout[i].x_size*10-11,dashboard_layout[i].y*20+4+_y+30,30,10,DashBoard_Back_Color);
display.setTextSize(1);
display.setCursor(dashboard_layout[i].x*20+dashboard_layout[i].x_size*10-11,dashboard_layout[i].y*20+4+_y+30);
display.println(Feed_data_decimal.c_str());
}
}
}
}
将Feed和Group消息数据更新到显示屏后,效果如下:
由于该项目没有使用固定的显示参数,因此可以随着仪表板网页版的页面布局的任何变化而更改后续。
编辑布局后并保存。
那么RP2040-ETH-DVI-ZERO的显示界面也会相应改变显示布局。
仪表盘中包含了各个区块的警告值信息,显示界面也会根据警告值的不同显示不同。
我人为地提高了A_4_temp(W5500_EVB_PICO)的温度,您可以在屏幕上看到一条警告消息。
而且,显示界面的深色模式和浅色模式也会自动切换。
灯光模式视图:
我使用Arduino开发它,这些是该项目中使用的所有库:
#include <AdafruitIO_Ethernet.h>
#include <SPI.h>
#include <EEPROM.h>
#include <Ethernet.h>
#include <LittleFS.h>
#include <SD.h>
#include <PicoDVI.h>
#include <Arduino_GFX_Library.h>
#include "html.h"
#include "icon.h"
#include <PNGdec.h>
#include <iostream>
#include <string>