基于TCS3200和ESP32-E的颜色识别显示与报警系统
- 概述
本文使用DFROBOT公司的TCS3200颜色传感器模块和FireBeetle ESP32-E主控板,开发了一款颜色识别、显示与报警系统。
该系统使用TCS3200传感器采集颜色信息,将采集结果以频率信号形式发送到主控板;主控板外接了一块0.96寸OLED单色显示屏和2.4寸TFT彩色液晶屏,单色屏显示RGB色彩值及颜色判定结果,彩色屏浮现出检测到的色块颜色效果;系统还带有1个蜂鸣器,当检测到红、绿、蓝等不同色块时,会发出高低不同的报警音。
- 硬件设计
系统原理图如图1所示。
图1 系统原理图
系统使用的主要模块见表1.
表1 主要模块信息
序号 |
名称 |
型号 |
制造商 |
备注 |
1 |
主控板 |
FireBeetle ESP32-E |
DFROBOT |
|
2 |
颜色传感器 |
TCS3200 |
DFROBOT |
|
3 |
V-Meter模块 |
SSD1306驱动128*64 |
硬禾学堂 |
0.96寸OLED 屏I2C接口 |
4 |
TFT显示屏 |
ILI9341驱动240*320 |
|
2.4寸TFT屏 SPI接口 |
表2 具体接线对应表
- 软件设计
软件开发使用Arduino开发环境,目前使用的版本是1.8.13。
首先,设置附加开发板管理器网址。
然后,安装开发板对应的库文件。
安装完成后,在工具菜单下,找到并选中开发板。
安装U8g2库文件,以支持OLED屏显示。
安装TFT_eSPI库文件,以支持TFT屏驱动。
接下来,需要指定SPI屏的驱动及引脚信息。
进入C:\Users\Administrator\Documents\Arduino\libraries\TFT_eSPI文件夹下,编辑User_Setup.h文件。
主要是编辑以下几行代码。
#define ILI9341_DRIVER //选择屏幕驱动芯片
#define TFT_CS D10 // 指定Chip select control pin,
#define TFT_DC D11 // 指定Data Command control pin
#define TFT_RST -1 // 指定Reset引脚,若直接接开发板RST,此值设为-1
因使用的是开发板的硬件SPI接口,所有SPI引脚无需指定;若使用软件SPI,还需要指定SPI引脚。
主要程序代码如下:
#include <TFT_eSPI.h> //TFT屏驱动
TFT_eSPI tft = TFT_eSPI();
#include <U8g2lib.h> //OLED屏驱动
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
#define S0_PIN D7 //颜色传感器与开发板连接引脚定义
#define S1_PIN D6
#define S2_PIN D5
#define S3_PIN D3
#define OUT_PIN D2 //传感器的输出脚
//PWM参数
int freq = 2000; // 频率
int channel = 0; // 通道
int resolution = 8; // 分辨率
#define Buzzer_PIN D12 //指定蜂鸣器的引脚,一定要是D脚,A脚不可以。
void setup() {
pinMode(S0_PIN,OUTPUT); //设定引脚输入输出模式
pinMode(S1_PIN,OUTPUT);
pinMode(S2_PIN,OUTPUT);
pinMode(S3_PIN,OUTPUT);
pinMode(OUT_PIN,INPUT);//控制器采集传感器信号
pinMode(Buzzer_PIN,OUTPUT);//控制器控制蜂鸣器信号
digitalWrite(S0_PIN,HIGH); //频率缩放20% S0-HIGH,S1-LOW
digitalWrite(S1_PIN,LOW);
Serial.begin(9600);
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
u8g2.begin();
ledcSetup(channel, freq, resolution);//PWM初始化函数
ledcAttachPin(Buzzer_PIN, channel);
}
void loop()
{
int r_RGB, g_RGB, b_RGB;
float k_R=7.6, k_G=6.8, k_B=7.3;
int r_H, g_H, b_H;
int r_L, g_L, b_L;
int data_RGB_TFT16;
digitalWrite(S2_PIN, LOW);//只采集红光
digitalWrite(S3_PIN, LOW);
r_L = pulseIn(OUT_PIN, LOW);
r_H = pulseIn(OUT_PIN, HIGH);
r_RGB=calcRGB(r_L,r_H,k_R);
Serial.print("r_RGB = ");
Serial.print(r_RGB);
Serial.print(" ");
delay(200);
digitalWrite(S2_PIN, HIGH);//只采集绿光
digitalWrite(S3_PIN, HIGH);
g_L = pulseIn(OUT_PIN, LOW);
g_H = pulseIn(OUT_PIN, HIGH);
g_RGB=calcRGB(g_L,g_H,k_G);
Serial.print("g_RGB = ");
Serial.print(g_RGB);
Serial.print(" ");
delay(200);
digitalWrite(S2_PIN, LOW);//只采集蓝光
digitalWrite(S3_PIN, HIGH);
b_L = pulseIn(OUT_PIN, LOW);
b_H = pulseIn(OUT_PIN, HIGH);
b_RGB=calcRGB(b_L,b_H,k_B);
Serial.print("b_RGB = ");
Serial.print(b_RGB);
Serial.print(" ");
Serial.println();
delay(200);
data_RGB_TFT16=r_RGB*31/255*2048+g_RGB*63/255*32+b_RGB*31/255;// 16位色 红0xF800 绿0x07E0 蓝0x001F 注意乘除的先后顺序
// tft.fillScreen(data_RGB_TFT16);//写屏幕背景色
tft.fillRect(70, 60, 100, 200, data_RGB_TFT16); //屏幕240*320
// u8g2.setFont(u8g2_font_ncenB10_tr); //设置英文字体
u8g2.enableUTF8Print(); //中文字体需要有此行,否则只能显示字母
u8g2.setFont(u8g2_font_wqy16_t_gb2312);//设置中文字体
u8g2.setFontDirection(0);
u8g2.firstPage();
do {
u8g2.setCursor(30, 30);//设置X,Y起始坐标
if ((r_RGB > g_RGB) && (r_RGB > b_RGB) && (r_RGB>80) )
{
Serial.println("Colour Red");
// u8g2.print("RED");
u8g2.print("红");
ledcWrite(channel, 30);//控制蜂鸣器发声 255对应100%
}
else if ((g_RGB > r_RGB) && (g_RGB > b_RGB)&& (g_RGB>80))
{
Serial.println("Colour Green");
// u8g2.print("GREEN");
u8g2.print("绿");
ledcWrite(channel, 15);
}
else if ((b_RGB > r_RGB) && ( b_RGB > g_RGB) && (b_RGB>80))
{
Serial.println("Blue");
// u8g2.print("BLUE");
u8g2.print("蓝");
ledcWrite(channel, 5);
}
else
{
Serial.println("READY");
//u8g2.print("READY");
u8g2.print("就绪");
ledcWrite(channel, 0);
}
//显示颜色RGB值
u8g2.setCursor(80, 20);//设置X,Y起始坐标
u8g2.print("R=");
u8g2.setCursor(100, 20);//设置X,Y起始坐标
u8g2.print(r_RGB);
u8g2.setCursor(80, 40);//设置X,Y起始坐标
u8g2.print("G=");
u8g2.setCursor(100, 40);//设置X,Y起始坐标
u8g2.print(g_RGB);
u8g2.setCursor(80, 60);//设置X,Y起始坐标
u8g2.print("B=");
u8g2.setCursor(100, 60);//设置X,Y起始坐标
u8g2.print(b_RGB);
} while ( u8g2.nextPage() );
}
int calcRGB(int time_L,int time_H,float k)
{
int data1;
byte data_Output;
time_L = pulseIn(OUT_PIN, LOW); //检测低电平的时间长度 ms
time_H = pulseIn(OUT_PIN, HIGH);//检测高电平的时间长度 ms
data1=int(1000.0/(time_L+time_H)*k);
if (data1<=255)
{data_Output=data1;
}
else
{data_Output=255;
}
return data_Output;
}
- 演示效果
系统上电后,OLED屏会显示“就绪”,此时TFT屏背景为黑色。
当放置不同颜色色块时,TFT屏上会浮现出相应色块的颜色。同时,OLED屏会判断大致颜色,并用汉字显示。
当检测到不同的颜色时,蜂鸣器会发出报警声,红色报警声最高,绿色居中,蓝色报警声最低。
- 总结
试验过程中遇到了以下问题:
- 颜色传感器校准及数据处理。
如需精确测量RGB数值,需要对传感器进行校准。校准方法是,将标准白色物体放在传感器上方1cm左右,通过调节RGB各通道的系数,使显示值为255,255,255。然后才能进行测量。
本文是使用pulseIn函数获取脉冲的低电平时间和高电平时间,相加后得到信号周期(因传感器输出频率信号占空比为50%,也可用低电平时间的2倍)。然后换算出频率,进而乘上系数,再和255进行对应。
- U8G2库的中文显示。
U8G2库支持中文“文泉驿字体”,这样就避免了手动创建汉字字模。具体使用时,需要先调用u8g2.enableUTF8Print(),启用UTF-8支持,否则会显示空白。
- ESP32的PWM输出。
普通Arduino开发板,PWM输出往往使用模拟量端口(A口),使用analogWrite函数。而ESP32-E不支持该函数,需要通过数字量端口(D口)使用ledcSetup(), ledcAttachPin(), ledcWrite()等3个函数来实现PWM输出。
- RGB24位色和屏幕16位色彩的转换。
打开C:\Users\Administrator\Documents\Arduino\libraries\TFT_eSPI\TFT_Drivers目录下,在ILI9341_Defines.h中可发现。TFT屏幕的颜色是用16位数据表示的。
#define ILI9341_BLACK 0x0000 /* 0, 0, 0 */
#define ILI9341_NAVY 0x000F /* 0, 0, 128 */
#define ILI9341_DARKGREEN 0x03E0 /* 0, 128, 0 */
#define ILI9341_DARKCYAN 0x03EF /* 0, 128, 128 */
#define ILI9341_MAROON 0x7800 /* 128, 0, 0 */
#define ILI9341_PURPLE 0x780F /* 128, 0, 128 */
#define ILI9341_OLIVE 0x7BE0 /* 128, 128, 0 */
#define ILI9341_LIGHTGREY 0xC618 /* 192, 192, 192 */
#define ILI9341_DARKGREY 0x7BEF /* 128, 128, 128 */
#define ILI9341_BLUE 0x001F /* 0, 0, 255 */
#define ILI9341_GREEN 0x07E0 /* 0, 255, 0 */
#define ILI9341_CYAN 0x07FF /* 0, 255, 255 */
#define ILI9341_RED 0xF800 /* 255, 0, 0 */
#define ILI9341_MAGENTA 0xF81F /* 255, 0, 255 */
#define ILI9341_YELLOW 0xFFE0 /* 255, 255, 0 */
#define ILI9341_WHITE 0xFFFF /* 255, 255, 255 */
#define ILI9341_ORANGE 0xFD20 /* 255, 165, 0 */
#define ILI9341_GREENYELLOW 0xAFE5 /* 173, 255, 47 */
#define ILI9341_PINK 0xF81F
而传感器计算出的标准RGB色255,255,255组合起来是24位,因此需要将传感器数据,压缩成16位,才能发送给屏幕显示。
也就是将24位111111111111111111111111转换成16位的1111111111111111,注意16位数据红、绿、蓝分别占5位、6位、5位。
本文使用的是下面的公式,来进行数据转换。data_RGB_TFT16=r_RGB*31/255*2048+g_RGB*63/255*32+b_RGB*31/255。;