FireBeetle 蓝牙温湿度记录仪
本帖最后由 zoologist 于 2021-8-28 10:23 编辑对于Aduino玩家来说,记录大量数据最常见的手段是配合 SD卡模块将数据记录到SD卡中,有些特别情况下,Arduino可以支持通过SPI来直接支持 SD卡。但是实际使用中存在着严重的兼容性问题,另外读取数据也并不方便。这次介绍直接使用U盘来存储获得的小米蓝牙温湿度计取得的数据。这次的项目使用 DFRobot 的 FireBeetle 来完成。首先为了实现U盘的读写,选择国产的 CH376 芯片。CH376是南京沁恒出品的U盘和SD卡文件管理控制芯片,CH376是文件管理控制芯片,用于单片机系统读写U盘或者SD卡中的文件。CH376支持USB设备方式和USB主机方式,并且内置了USB通讯协议的基本固件,内置了处理Mass-Storage海量存储设备的专用通讯协议的固件,内置了SD卡的通讯接口固件,内置了FAT16和FAT32以及FAT12文件系统的管理固件,支持常用的USB存储设备(包括U盘/USB 硬盘/USB 闪存盘/USB 读卡器)和SD卡(包括标准容量SD卡和高容量HC-SD卡以及协议兼容的MMC卡和TF 卡)。CH376支持三种通讯接口:8位并口、SPI接口或者异步串口,单片机/DSP/MCU/MPU等控制器可以通过上述任何一种通讯接口控制CH376芯片,存取U 盘或者SD 卡中的文件或者与计算机通讯。此外,这一系列还有 CH375 (比CH376少了SPI 接口),CH378(USBFull Speed)。为了实现目标,首先制作一个 FireBeetle CH376 Shield。 为了方便手工焊接,选择了SOP-28封装的CH376S,电路设计如下,芯片和FireBeetle 使用串口连接,这样虽然速度无法和SPI接口相比,但是我们数据量不大完全够用:
从图中可以看到,外围元件主要是12M的晶振和一些电容,从外还有一个工作指示灯,当芯片有读写动作时,会熄灭。
成品照片如下:
接下来是要找到驱动 CH376的方法,在Github上找到djuseeq的Ch376msc项目【参考1】。测试发现,这个项目能够在 Arduino Uno 上正常工作,但是在ESP32上会遇到奇怪不稳定的问题。最终在逻辑分析仪的帮助下确定出现问题是在切换频率之后。这个CH376上电之后根据硬件Strapping结果有一个默认的波特率,这次设计的Shield默认使用 9600,之后通过串口命令通知CH376使用指定的波特率进行通讯。前面提到的Ch376msc库在切换之后会使用 FireBeetle立刻使用新的波特率进行通讯,此时在ESP32串口缓冲中仍然会有尚未通过串口发送出去的数据,切换之后的命令实际上并没有正确的传输给 CH376。用具体的例子来说明:FireBeetle 串口发送 57 AB 02 03 CC命令通知CH376即将切换为115200 波特率。代码中在执行完发送最后2个数据之后,这两个数据只是加入到串口发送缓冲中,执行完之后FireBeetle马上进行了波特率切换,串口随之发送动作,串口缓冲区仍然存在的数据将会用新的波特率发送,此时CH376还没有按照新的波特率开始响应,因此将会收到错误的数据。
_comPortHW->write(uint8_t(0x03));_comPortHW->write(uint8_t(0xCC));
解决方法也很简单:在切换波特率之间加入足够大的延时,保证串口发送缓冲以切换之前的波特率发送完成。为了更好的记录数据,增加了一个 RTC 模块,起初使用的是 DS1307模块,但是测试下来发现这个模块有奇怪的问题(1.模块实际上是为可充电电池设计的,如果使用非充电电池可能有**烧毁的风险2.特别注意电池型号,市面常见的纽扣电池会比原装的可充电电池薄一些,使用时会有接触的问题3.解决上面三个问题之后,仍然有经常丢失时间的问题)。最终选择的是 DS3231 模块,搭配 FireBeetle 测试良好。最终还设计了一个底板,左上角是为了让整体能在充电宝供电的情况下正常工作电路(很多充电宝如果电流低于200ma会自动切断供电)。FireBeetle上还有一个按钮,这是为了当我们需要让其停止工作时,通知FireBeetle将缓冲中的数据写入U盘(U盘也是一种 FLASH 介质,每次写入时需要先擦除,因此有寿命上的考虑,所以采用先将收集到的数据存储在RAM中,收集到足够的数据后一次性写入文件中)。
PCB 如下:
成品:
关键代码如下:
1. 1.,U盘读写模块 CH376 相关代码
// CH376 对象,使用115200 波特率
Ch376msc flashDrive(Serial2, 115200);
flashDrive.init();
// 检查当前是否有 U盘插入
if (flashDrive.checkIntMessage()) {
if (flashDrive.getDeviceStatus()) {
Serial.println(F("Flash drive attached!"));
} else {
Serial.println(F("Flash drive detached!"));
}
}
// 检查U盘是否可以工作(要求必须是 FAT 分区)
while (flashDrive.driveReady() != true) {
Serial.println("Attach flash drive first!");
Serial.print(flashDrive.pingDevice());
delay(500);
}
// 设定存储数据的文件名称,特别注意:文件名不能是小写,这里使用从日期生成的纯数字组成文件名
flashDrive.setFileName(Filename);
flashDrive.openFile();
flashDrive.writeFile(header, strlen(header));
flashDrive.closeFile();
2. 2.DS3231 相关代码// 声明 DS3231 对象
RTC_DS3231 RTC;
初始化时有一个检查的动作,如果时间不正确那么重新进行设定
// 检查 RTC 状态,如果有掉电发生就说明
if (RTC.lostPower()) {
Serial.println("RTC lost power, let's set the time!");
// 使用当前的编译时间重新设定 RTC
RTC.adjust(DateTime(F(__DATE__), F(__TIME__)));
// 下面是直接设定时间的例子
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
之后,通过 RTC.now()可以取得 DS3231 的时间
3. 小米温湿度传感器是下面这个东西,非常省电,一节7号电池可以使用一年(我亲自测试)。除了显示之外,它还会使用 BLE 广播的方式对外发送蓝牙信号。FireBeetle可以监听蓝牙广播,收到之后做进一步处理。具体代码在classMyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {}中。
同一个函数中,下面的代码将数据收取下来后先保存在 RAM 中的Recorder结构体数组中:
Data.tm = RTC.now();
Data.humidity = current_humidity;
Data.temperature = current_temperature;
// 存储收到的温湿度数据
struct Recorder {
char Address; // 地址
DateTimetm; // 发生的时间
float humidity; // 湿度数据
float temperature; // 温度数据
};
当收集到足够数据或者发现有按键按下后,就开始写入U盘上的文件进行保存。4. 定义了一个按键,在下面这个函数中处理按键中断。上电后第一次按下,进入函数后检查距离上一次按键的时间,只有超过3秒才会响应按键,这样避免了按键抖动和误触发。StopMark记录按键的历史状态,0表示没有触发过,记录会正常进行;1表示触发过,当前是停止向U盘写入数据的状态;此外,如果当前 StopMark==1,再次按下就会触发重启命令,重新开始记录数据。void IRAM_ATTR StopFunction() {
if (millis() - StopElsp > 3000) {
Serial.println("Pressed\n");
if (StopMark == 0) {
StopMark = 1;
} else {
esp_restart();
}
StopElsp = millis();
}
}
工作的照片:
参考:1. https://github.com/djuseeq/Ch376msc
源代码
测试得到的结果(数据来自2个小米温湿度传感器,所以结果中有2个地址)
Humi and temp recorder
58:2d:34:3a:81:cf,2021/8/26 22:25:46,76.1,31.3
58:2d:34:3a:81:cf,2021/8/26 22:25:48,76.1,31.3
58:2d:34:3a:81:cf,2021/8/26 22:25:50,76.1,31.2
58:2d:34:3a:81:cf,2022/14/81 143:62:29,76.0,31.3
58:2d:34:3a:e6:d1,2021/8/26 22:25:54,75.0,31.4
58:2d:34:3a:e6:d1,2021/8/26 22:25:56,75.1,31.4
58:2d:34:3a:e6:d1,2021/8/26 22:25:58,74.9,31.3
58:2d:34:3a:e6:d1,2021/8/26 22:26:0,75.0,31.3
58:2d:34:3a:e6:d1,2021/8/26 22:26:4,75.0,31.3
58:2d:34:3a:e6:d1,2021/8/26 22:26:6,75.1,31.4
58:2d:34:3a:e6:d1,2021/8/26 22:26:8,75.0,31.4
58:2d:34:3a:e6:d1,2021/8/26 22:26:11,75.1,31.3
58:2d:34:3a:e6:d1,2021/8/26 22:26:15,75.1,31.4
58:2d:34:3a:81:cf,2021/8/26 22:26:18,76.2,31.3
58:2d:34:3a:e6:d1,2021/8/26 22:26:19,75.1,31.3
58:2d:34:3a:81:cf,2021/8/26 22:26:20,76.2,31.3
58:2d:34:3a:e6:d1,2021/8/26 22:26:21,75.1,31.3
膜拜大神
页:
[1]