一、前言
项目介绍:小区管理比较严格,平常都是一车一杆,行人通过闸门出入,但并不是感应门,只有在高峰期闸门才是常开的,平常每次出入小区的时候需要叫保安开门,有的时候保安在保安亭里没有听到、或者是登记车辆信息的时候,只能呆着等着保安开门。所以有了这个想法制作一个门闸遥控器,复制保安遥控器的射频等信息,制作一个自己的遥控器,方便出入小区。
设计思路:使用ESP-12F为主控,远-T2L远距离发射模块作为发送端,远-R1接收模块作为接受端。参考来自开源项目高处不胜韩
框图介绍:
二、原理图及PCB介绍
锂电池充电电路使用的是ADI的锂电池管理IC LTC4054-4.2,这个电路是基于LTC4054-4.2的锂电池充电电路。LTC4054-4.2是一款锂电池线性充电管理IC,专门用于单节锂电池的恒流-恒压充电,1脚是充电状态指示引脚。当电池正在充电时,此引脚会拉低,点亮LED指示充电状态。5脚是通过外部电阻(R11 2.5kΩ)设定充电电流。根据LTC4054的数据手册,充电电流 I_charge = V_PROG/ R_PROG,可以通过控制R11的电阻值来设定充电电流。
LTC4054-4.2
ADI的锂电池管理IC,采用 ThinSOT 封装并具有热调节功能的独立线性锂离子电池充电器。
- 高达 800mA 的可编程充电电流
- 无需 MOSFET、检测电阻器或隔离二极管
- 用于单节锂离子电池、采用 ThinSOT™ 封装的完整线性充电器
- 恒定电流/恒定电压操作,并具有可在无过热危险的情况下实现充电速率较大化的热调 节功能*
- 直接从 USB 端口给单节锂离子电池充电
- 精度达 ± 1% 的 4.2V 预设充电电压
- 用于电池电量检测的充电电流监控器输出
- 自动再充电
- 充电状态输出引脚
- C/10 充电终止
- 停机模式下的供电电流为 25µA
- 2.9V 涓流充电门限 (LTC4054)
- 可提供无涓流充电器件版本 (LTC4054X)
- 软起动限制了浪涌电流
- 采用 5 引脚 SOT-23 封装
这个电路图展示了MCP23017芯片的I/O扩展电路。MCP23017是一款I²C接口的16位GPIO扩展器,可以通过SCL和SDA引脚与主控(如微控制器)通信,MCP23017通过I²C总线扩展系统的输入输出能力,解决ESP-12F主控I/O端口不够的问题,增加额外的数字输入输出引脚。
MCP23017:
MCP23017是Microchip的一款IO扩展芯片,通过I2C 总线或SPI应用提供16位的通用并行I/O扩展。
• 16位远程双向I/O端口- I/O引脚默认为输入引脚
•高速I2C™接口(MCP23017)- 100kHz- 400kHz- 1.7MHz
•高速SPI接口(MCP23S17)- 10MHz(最高)
•三个硬件地址引脚,最多可允许总线上连接8个器 件
•可配置的中断输出引脚-可配置为高电平有效(输出)、低电平有效 (输出)或开漏(输出)
• INTA和INTB可配置为独立或联合工作 •可配置的中断源-根据已配置寄存器默认值或引脚电平变化而发 生电平变化中断 •用于配置输入端口数据极性的“极性反转”寄存 器
•外部复位输入 •待机电流低:1µA(最大值)
•工作电压:- 1.8V至5.5V(-40°C至+85°C)- 2.7V至5.5V(-40°C至+85°C)- 4.5V至5.5V(-40°C至+125°C)
三、成品功能测试
PCB预览图:
实际上成品的样子:
在PCB板上我有一些元器件是暂时没有焊接的,这些部分是属于IO扩展的部分和复位、ADC等电路。在选择元器件的时候需要注意一下SMA座子的空隙需要与PCB的板厚匹配,我这里就没有考虑到,只能将SMA的引脚强行掰开距离。还有就是电源端子的正负需要与锂电池的正负相对应,我这里就搞错了,就只能反着焊接了。
#define DebugBegin(message) Serial.begin(message)
#define DebugPrint(message) Serial.print(message)
#define DebugPrintln(message) Serial.println(message)
#include <ESP8266WiFi.h>
#include "Gr_Define.h" //该文件调用各种库 以及声明
#include "DFRobot_MCP23017.h"
#include "User_Interface.h"
#include "keyandec11.h"
unsigned long lastTime_1 = 0;
unsigned long timerDelay_1 = 30000;
unsigned long lastKeyPressTime = 0;
unsigned long sleepTime = 15000; // 30秒没有检测到按键则进入休眠模式
void setup() {
DebugBegin(115200);
keyBegin(); //初始化按键io
ssd1306displaybegin();
for(int i; i < 100; i++)
{
if(digitalRead(DOWN_KEY)== LOW)//按键组合
menu.State = true;
else
menu.State = false;
delay(1);
}
while (!Serial) continue;
if (!DateBegin()) {
u8g2begin();
Startup_error(); //存储报错显示
DebugPrintln("存储启动失败");
} else {
u8g2begin(); //初始化屏幕驱动
SwitchBegin(); //初始化射频io
if(menu.State == true)
Main_menu(menu.Main_menu_inde1);
else
mainUIdisplay();
Enumerate_Events(); //初始化内部文件存储
DebugPrintln(Read("System"));
//Set_logo(true);
DebugPrintln("存储启动成功");
}
}
void light_sleep(){
wifi_station_disconnect();
wifi_set_opmode_current(NULL_MODE);
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); // set sleep type, the above posters wifi_set_sleep_type() didnt seem to work for me although it did let me compile and upload with no errors
wifi_fpm_open(); // Enables force sleep
//gpio_pin_wakeup_enable(GPIO_ID_PIN(0), GPIO_PIN_INTR_LOLEVEL); // GPIO_ID_PIN(2) corresponds to GPIO2 on ESP8266-01 , GPIO_PIN_INTR_LOLEVEL for a logic low, can also do other interrupts, see gpio.h above
wifi_fpm_do_sleep(0xFFFFFFF); // Sleep for longest possible time
}
void loop() {
unsigned long currentTime = millis(); // 获取当前时间
ec11loop();
keyloop();
SwitchAvailable();
// 如果按键被按下,重置计时器
if (digitalRead(UP_KEY) == LOW||digitalRead(M_KEY) == LOW||digitalRead(DOWN_KEY) == LOW) {
Serial.print("reset time");
lastKeyPressTime = currentTime;
}
// 如果超过设定的时间阈值按键未被按下,进入休眠模式
if (currentTime - lastKeyPressTime >= sleepTime) {
//Serial.print("IN lightsleep");
display.clearDisplay();//清屏
display.display();//开显示
delay(100);
lastKeyPressTime = currentTime;
//ESP.deepSleep(15e6);
light_sleep(); // 进入 light_sleep() 休眠模式
delay(100);
//Serial.print("OUT lightsleep");
switch (counter) {
case 1:
page1();
break;
case 2:
page2();
break;
}
}
Serial.print("Current millis value: ");
Serial.println(currentTime); // 打印当前时间到串口监视器
}
void Enumerate_Events() {
Dir dir = LittleFS.openDir("/");
List1("[返回上级]", "");
String filename;
while (dir.next()) {
filename = dir.fileName();
// DebugPrintln(filename);
if (filename != "System" && filename != "web") {
List1(filename, Base10_1(filename));
} //判断文件不是系统文件将添加到链表
}
List1("[删除全部数据]", "");
}
关键代码及说明:
代码部分是使用Arduino IDE编写的,然后生成bin文件通过安信可官网上的烧入软件将代码下载到ESP-12F上,这个下载过程还是有一定麻烦的,它需要手动进入下载模式,在这里是按住IO0的那个按键不放再上电,这样就可以正常下载程序了。整个代码的功能概述如下,初始化ESP8266设备及其相关模块(如WiFi、按键、显示屏和射频等),通过按键操作控制用户界面,提供交互功能,提供了节能功能,在一段时间内如果没有按键活动则进入休眠状态,列举设备存储的文件,供用户进行管理。
//发射数据信号
void SwitchSend(String filename) {
String data = Read(filename);
String pattern = data.substring(0, 2);
String Base10_length = data.substring(2, 4);
String Base10 = data.substring(4, Base10_length.toInt() + 4);
String Position = data.substring(Base10_length.toInt() + 4, Base10_length.toInt() + 7);
String Pulse = data.substring(Base10_length.toInt() + 7, Base10_length.toInt() + 12);
String Protocol = data.substring(Base10_length.toInt() + 12, Base10_length.toInt() + 14);
// DebugPrintln(pattern);
// DebugPrintln(Base10_length);
// DebugPrintln(Base10);
// DebugPrintln(Position);
// DebugPrintln(Pulse);
// DebugPrintln(Protocol);
if (pattern == "F4") {
Switch_433.setProtocol(Protocol.toInt());
Switch_433.setPulseLength(Pulse.toInt());
Switch_433.send(atoi(Base10.c_str()), Position.toInt());
}
if (pattern == "F3") {
Switch_315.setProtocol(Protocol.toInt());
Switch_315.setPulseLength(Pulse.toInt());
Switch_315.send(atoi(Base10.c_str()), Position.toInt());
}
}
void SwitchAvailable() {
uint32_t Switch;
String Base10;
Switch = millis();
if (menu.Switch) {
if (Switch > SwitchTime & Switch - SwitchTime > 10) {
//延迟10毫秒
SwitchTime = Switch;
if (Switch_433.available()) {
Base10 = String(Switch_433.getReceivedValue());
if (Base10.length() > 3) {
bool List_Base10 = Get_ListExisBase10(Base10);
if (!List_Base10) {
bool state = File_save("433_" + are[i++], "F4", Base10, Switch_433.getReceivedBitlength(), Switch_433.getReceivedDelay(), Switch_433.getReceivedProtocol());
if (state) {
DeleString("[删除全部数据]");
List1("433_" + are[i], Base10); //将文件名添加到链表
List1("[删除全部数据]", "");
Receiving_mode("433_" + are[i] , "文件保存成功");
} else {
Receiving_mode("433_" + are[i] , "文件保存失败");
}
} else {
Receiving_mode(Get_ListBase10_Name(Base10), "文件已存在");
}
}
Switch_433.resetAvailable();
}
if (Switch_315.available()) {
Base10 = String(Switch_315.getReceivedValue());
if (Base10.length() > 3) {
bool List_Base10 = Get_ListExisBase10(Base10);
if (!List_Base10) {
bool state = File_save("315_" + Base10, "F3", Base10, Switch_315.getReceivedBitlength(), Switch_315.getReceivedDelay(), Switch_315.getReceivedProtocol());
if (state) {
DeleString("[删除全部数据]");
List1("315_" + Base10, Base10); //将文件名添加到链表
List1("[删除全部数据]", "");
Receiving_mode("315_" + Base10, "文件保存成功");
} else {
Receiving_mode("315_" + Base10, "文件保存失败");
}
} else {
Receiving_mode(Get_ListBase10_Name(Base10), "文件已存在");
}
}
Switch_315.resetAvailable();
}
}
}
}
这段代码是无线通信部分,能够发送和接收433MHz和315MHz的信号。代码分为两个主要函数:SwitchSend 用于处理信号的发送,SwitchAvailable 用于处理信号的接收。在接收过程中,它还会检查接收到的信号是否已经存在,以避免重复存储相同的数据。这种设计用于无线设备的控制和管理,适用于需要无线遥控的场景。
四、活动总结
感谢电子森林发布的FastBond活动这次的机会。祝举办方越来越好,希望有越来越多的好项目涌现出来。