近日笔者自己动手修理自家的门铃时,发现只要接通开门的电源线,就可以打开楼宇的大门了,突发奇想:在门铃引出线加装继电器就可以用esp8266连上互联,实现远程开门了。(我家门铃比较老旧,不知其他牌子门铃是否也是可以这样操作,知道的朋友留言告诉一下)。
具体要实现的功能:通过手机app操作实现开门功能。实现原理:esp8266连接服务器,并与之交互数据,手机app向服务器发送开锁指令,服务器转发指令到esp8266,esp8266通过gpio发送高电平给继电器,接通开门电源线,实现开门。本文只从程序开发方面去说明开发的过程。更多的硬件方面的说明请看相关文档。
需要的设备清单:
继电器:信号线连接esp8266的gpio2,当高电平是继电器断开,门锁关状态,开锁时将gpio2置为低电平2秒钟,即可实现开锁.
esp8266
家庭wifi
立即上源码
esp8266部分,使用udp与服务器通信:(在ESP8266_NONOS_SDK-2.1.0-18上编译通过)
#include "user_interface.h"
#include "espconn.h"
#include "gpio.h"
#include "eagle_soc.h"
#include "osapi.h" //os_printf所需要的头文件
ETSTimer my_timer,act_timer,low_timer;
struct espconn conn;
esp_udp user_udp;
ip_addr_t esp_server_ip;
void ICACHE_FLASH_ATTR init_cb(void);
void ICACHE_FLASH_ATTR espconn_cli_timer_cb(void *timer_arg);
void ICACHE_FLASH_ATTR act_timer_cb(void *timer_arg);
void ICACHE_FLASH_ATTR client_connect();
void ICACHE_FLASH_ATTR act_timer_cb(void *timer_arg);
void ICACHE_FLASH_ATTR ret_back_cb(void *timer_arg);
void ICACHE_FLASH_ATTR espconn_recv_data_cb(void *arg,char *pdata,unsigned short len);
void ICACHE_FLASH_ATTR espconn_sent_cb(void *arg);
static int gLoginSucess = 0;
void ICACHE_FLASH_ATTR espconn_recv_data_cb(void *arg,char *pdata,unsigned short len)
{//接收到服务发来的信息,进行相关的操作
os_printf("espconn_recv_data_cb datalen:%d,conetnt[%s]\n",len,pdata);
if(strncmp(pdata,"LOGIN_RESP",11) == 0)
{
gLoginSucess = 1;
os_printf("login succeed\n");
if(strcmp(pdata+11,"LOW") == 0)
GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 0);
else if(strcmp(pdata+11,"HEIGHT") == 0)
GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 1);
os_timer_disarm(&act_timer);
os_timer_setfn(&act_timer,act_timer_cb, NULL);
os_timer_arm(&act_timer, 60000, 1);
}
else if(strcmp(pdata,"ACK_RESP") == 0)
{
os_printf("act resp\n");
}
else if(strcmp(pdata,"OPEN") == 0)
{
os_printf("opt:OPEN\n");
//gpio16_output_set(0);
GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 0);
os_timer_disarm(&low_timer);
os_timer_setfn(&low_timer,ret_back_cb, NULL);
os_timer_arm(&low_timer, 500, 0);
}
else if(strcmp(pdata,"LOW")==0)
{
GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 0);
char szData[16] = {0};
os_sprintf(szData,"LOW_RESP_%d",system_get_chip_id());
espconn_sent(&conn,szData,strlen(szData));
}
else if(strcmp(pdata,"HEIGHT")==0)
{
GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 1);
char szData[20] = {0};
os_sprintf(szData,"HEIGHT_RESP_%d",system_get_chip_id());
espconn_sent(&conn,szData,strlen(szData));
}
else if(strcmp(pdata,"UPDATE")==0)
{
}
else
{
os_printf("unknow command\n");
}
}
void ICACHE_FLASH_ATTR espconn_sent_cb(void *arg)
{
os_printf("espconn_sent_cb\n");
}
void ICACHE_FLASH_ATTR ret_back_cb(void *timer_arg)
{
//gpio16_output_set(1);
GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 1);
char szData[16] = {0};
os_sprintf(szData,"OPEN_RESP_%d",system_get_chip_id());
espconn_sent(&conn,szData,strlen(szData));
}
void ICACHE_FLASH_ATTR act_timer_cb(void *timer_arg)
{
os_printf("act_timer_cb\n");
char szData[16] = {0};
os_sprintf(szData,"ACK_%d",system_get_chip_id());
espconn_sent(&conn,szData,strlen(szData));
}
void ICACHE_FLASH_ATTR login_timer_cb(void *timer_arg)
{
if(!gLoginSucess)
{
os_printf("login_timer_cb\n");
char szData[16] = {0};
os_sprintf(szData,"LOGIN_%d",system_get_chip_id());
espconn_sent(&conn,szData,strlen(szData));
os_timer_disarm(&my_timer);
os_timer_setfn(&my_timer,login_timer_cb, NULL);
os_timer_arm(&my_timer, 5000, 0);
}
}
LOCAL void ICACHE_FLASH_ATTR
my_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
{
struct espconn *pespconn = (struct espconn *)arg;
if (ipaddr == NULL)
{
os_printf("my_dns_found NULL\n");
/*
if (++device_recon_count == 5) {
device_status = DEVICE_CONNECT_SERVER_FAIL;
user_esp_platform_reset_mode();
}*/
return;
}
os_printf("my_dns_found %d.%d.%d.%d\n",
*((uint8 *)&ipaddr->addr), *((uint8 *)&ipaddr->addr + 1),
*((uint8 *)&ipaddr->addr + 2), *((uint8 *)&ipaddr->addr + 3));
if (esp_server_ip.addr == 0 && ipaddr->addr != 0)
{
esp_server_ip.addr = ipaddr->addr;
os_memcpy(user_udp.remote_ip, &ipaddr->addr, 4);
}
os_timer_disarm(&my_timer);
os_timer_setfn(&my_timer,espconn_cli_timer_cb, NULL);
os_timer_arm(&my_timer, 10000, 0);
}
void ICACHE_FLASH_ATTR client_connect()
{
conn.type = ESPCONN_UDP;
conn.state = ESPCONN_NONE;
user_udp.remote_port = 9004;
conn.proto.udp = &user_udp;
espconn_regist_sentcb(&conn, espconn_sent_cb);
espconn_regist_recvcb(&conn, espconn_recv_data_cb);
os_printf("begin espconn_connect\n");
//espconn_connect(&conn);
espconn_create(&conn);
char szData[16] = {0};
os_sprintf(szData,"LOGIN_%d",system_get_chip_id());
espconn_sent(&conn,szData,strlen(szData));
os_timer_disarm(&my_timer);
os_timer_setfn(&my_timer,login_timer_cb, NULL);//设置监听登陆成功的函数
os_timer_arm(&my_timer, 10000, 0);
}
void ICACHE_FLASH_ATTR espconn_cli_timer_cb(void *timer_arg)
{//连接wifi,如果未连上wifi会不停重试
init_cb();
}
void ICACHE_FLASH_ATTR init_cb(void)
{//初始化函数主要功能获取服务器的地址
//struct ip_info info;
//wifi_get_ip_info(STATION_IF,&info);
uint8 status=wifi_station_get_connect_status();
os_printf("wifi_station_get_connect_status:%d\n",status);
if(status==STATION_GOT_IP)
{
if(esp_server_ip.addr == 0)
{
char *servername = "www.aomomo.net";
espconn_gethostbyname(&conn, servername, &esp_server_ip, my_dns_found);
os_printf("espconn_gethostbyname\n");
os_timer_disarm(&my_timer);
os_timer_setfn(&my_timer,espconn_cli_timer_cb, NULL);
os_timer_arm(&my_timer, 10000, 0);
}
else
client_connect();//连上wifi后开始登陆服务器
}
else
{
os_printf("setup my_timer for espconn_cli_timer_cb\n");
os_timer_disarm(&my_timer);
os_timer_setfn(&my_timer,espconn_cli_timer_cb, NULL);
os_timer_arm(&my_timer, 5000, 0);
}
}
void ICACHE_FLASH_ATTR user_init()
{
os_printf("2222222222222222222222222222222\n");
uint8 opmode;
//uart_init(115200,115200);
wifi_set_opmode(STATION_MODE);//把esp8266设置为工作站的工作模式
opmode = wifi_get_opmode();
os_printf("current mode is: %d\n", opmode);
esp_server_ip.addr = 0;
/*
struct ip_info info;
IP4ADDR(&info.ip,192,168,1,111);
IP4ADDR(&info.gw,192,168,1,1);
IP4ADDR(&info.netmask,255,255,255,0);
wifi_set_ip_info(STATION_IF,&info);
*/
struct station_config stationconf;
stationconf.bssid_set = 0;
memset(stationconf.ssid, 0, sizeof(stationconf.ssid));
memset(stationconf.password, 0, sizeof(stationconf.password));
os_strncpy(stationconf.ssid, "esp8266", sizeof(stationconf.ssid));//设置wifi接入点
os_strncpy(stationconf.password, "esp12345", sizeof(stationconf.password));//设置wifi接入点密码
wifi_station_set_config(&stationconf);//设置
gpio_init();
//gpio16_output_conf();
//gpio16_output_set(0);
//gpio_output_set(0, BIT2, BIT2, 0);
//PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U,FUNC_GPIO2);//
//PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO16_U,FUNC_GPIO16);//
//PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO2);
GPIO_OUTPUT_SET(GPIO_ID_PIN(2), 1);//设置gpio2为高电平,连接继电器的信号输入,此时继电器状态为断开。
system_init_done_cb(init_cb);//初始化完成后的回调函数
}
void ICACHE_FLASH_ATTR
user_rf_pre_init(){}
服务器部分
//同时支持tcp与udp服务,9003为tcp服务,9004为udp服务,部署在linux服务器上
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <string>
#include <list>
#include <map>
#include <sstream>
using namespace std;
int g_iMaxFd = 0;
int g_iServerSocketFd;
int g_iUdpFd;
fd_set g_set;
typedef struct _ConnInfo
{
int iFd;
struct sockaddr_in addr;
unsigned int addrLen;
time_t iLastTime;
} ConnInfo;
list<ConnInfo> g_servlist;
map<string,ConnInfo> g_client_info;
int CanSend(int fd,int p_iUsec);
int CanReceive(int fd,int p_iUsec);
void WriteLog(string str)
{
printf("%s\n",str.c_str());
}
int main(int argc,char *argv[])
{
g_iServerSocketFd = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in cli_addr;
unsigned int iNameLen;
iNameLen = sizeof(cli_addr);
int iFlags = fcntl(g_iServerSocketFd, F_GETFL, 0);
fcntl(g_iServerSocketFd, F_SETFL, iFlags | O_NONBLOCK);
//#ifdef LINUX
int iOpt;
socklen_t iLen;
iOpt = 1;
iLen = sizeof(iOpt);
//free the port
setsockopt(g_iServerSocketFd, SOL_SOCKET, SO_REUSEADDR, (void *)&iOpt, iLen);
//#endif
struct sockaddr_in serv_addr;
memset((void *)&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(9003);
//bind to the specify port
if(bind(g_iServerSocketFd,(struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
WriteLog( "Server:Can't bind local address! 9003\n");
return false;
}
//begin listen
listen(g_iServerSocketFd,5);
struct sockaddr_in localAddr;
g_iUdpFd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&localAddr, sizeof(localAddr));
localAddr.sin_family = AF_INET;
localAddr.sin_port = htons(9004);
localAddr.sin_addr.s_addr = INADDR_ANY;
iFlags = fcntl(g_iUdpFd, F_GETFL, 0);
fcntl(g_iUdpFd, F_SETFL, iFlags | O_NONBLOCK);
int result = bind(g_iUdpFd, (struct sockaddr *)&localAddr, sizeof(localAddr));
if(result < 0)
{
WriteLog( "Server:Can't bind local address! 9004\n");
return false;
}
g_client_info.clear();
WriteLog( "start..." );
FD_ZERO( &g_set );
ConnInfo conn;
conn.iFd = g_iServerSocketFd;
conn.iLastTime = time(NULL);
g_servlist.push_back(conn);
conn.iFd = g_iUdpFd;
conn.iLastTime = time(NULL);
g_servlist.push_back(conn);
FD_SET(g_iServerSocketFd, &g_set);
FD_SET(g_iUdpFd, &g_set);
g_iMaxFd = (g_iServerSocketFd > g_iUdpFd? g_iServerSocketFd : g_iUdpFd);
while(1)
{
struct timeval ptv;
ptv.tv_sec = 30;
ptv.tv_usec = 0;
fd_set fdset = g_set;
int iRet = select(g_iMaxFd+1, &fdset, NULL, NULL, &ptv);
if(iRet > 0)
{
for(list<ConnInfo>::iterator it=g_servlist.begin();it!=g_servlist.end();it++)
{
int fd = it->iFd;
if(FD_ISSET(fd,&fdset))
{
if(fd == g_iServerSocketFd)
{
struct sockaddr_in clientAddr;
unsigned int nameLen = sizeof(clientAddr);
int clientSock = accept( g_iServerSocketFd, (sockaddr*)&clientAddr, &nameLen );
printf( "get accept:%d\n" , clientSock);
if(clientSock > 0)
{
conn.iFd = clientSock;
conn.iLastTime = time(NULL);
conn.addr = clientAddr;
conn.addrLen = nameLen;
g_servlist.push_back(conn);
FD_SET(clientSock, &g_set);
if(clientSock > g_iMaxFd) g_iMaxFd = clientSock;
}
}
else
{
struct sockaddr_in remoteAddr;
socklen_t remoteAddrLength = sizeof(remoteAddr);
char szData[24] = {0};
int iLen = recvfrom(fd,szData,24,0,(struct sockaddr *)&remoteAddr, &remoteAddrLength);
if(iLen <=0)
{
close(fd);
it = g_servlist.erase(it);
FD_CLR(fd,&g_set);
printf( "socket break:%d" , fd);
break;
}
printf( "fd:%d,recv:%s\n" , fd , szData );
if(strncmp(szData,"LOGIN",5)==0)
{
it->iLastTime = time(NULL);
it->addr = remoteAddr;
it->addrLen = remoteAddrLength;
string strClientId = szData+6;
memset(szData,0,sizeof(szData));
strncpy(szData,"LOGIN_RESP_HEIGHT",sizeof(szData));
sendto(fd,szData,strlen(szData),0,(struct sockaddr *)&remoteAddr ,remoteAddrLength);
string strSign = "000000000000000000";
string strVersion = "1.0";
map<string,ConnInfo>::iterator p = g_client_info.find(strClientId);
if(p == g_client_info.end())
g_client_info[strClientId] = *it;
else
{
p->second.iLastTime = it->iLastTime;
p->second.addr = remoteAddr;
p->second.addrLen = remoteAddrLength;
}
}
else if(strncmp(szData,"ACK",3)==0)
{
it->iLastTime = time(NULL);
it->addr = remoteAddr;
it->addrLen = remoteAddrLength;
string strClientId = szData+4;
memset(szData,0,sizeof(szData));
strncpy(szData,"ACK_RESP",sizeof(szData));
sendto(fd,szData,strlen(szData),0,(struct sockaddr *)&remoteAddr ,remoteAddrLength);
map<string,ConnInfo>::iterator p = g_client_info.find(strClientId);
if(p != g_client_info.end())
{
p->second.iLastTime = it->iLastTime;
p->second.addr = remoteAddr;
p->second.addrLen = remoteAddrLength;
}
}
else if(strncmp(szData,"CTRL",4)==0)
{
memset(szData,0,sizeof(szData));
strncpy(szData,"OPEN",4);
for(list<ConnInfo>::iterator pit=g_servlist.begin();pit!=g_servlist.end();pit++)
{
if(pit->iFd != g_iServerSocketFd)
{
printf( "send to fd:%d\n" ,pit->iFd);
sendto(pit->iFd,szData,strlen(szData),0,(struct sockaddr *)&pit->addr ,pit->addrLen);
}
}
sendto(fd,szData,strlen(szData),0,(struct sockaddr *)&remoteAddr ,remoteAddrLength);
}
else if(strncmp(szData,"ORDER|",6)==0)//收到手机端的开门指令,转发给esp8266
{
stringstream ss(szData);
string strOrder,strClientId,strOP;
getline(ss,strOrder,'|');
getline(ss,strClientId,'|');
getline(ss,strOP,'|');
map<string,ConnInfo>::iterator p = g_client_info.find(strClientId);
if(p == g_client_info.end())
printf("can not find clientid:%s\n" , strClientId);
else
{
printf( "send to fd:%d,op:%s\n" , p->second.iFd ,strOP.c_str());
sendto(p->second.iFd,strOP.c_str(),strOP.length(),0,(struct sockaddr *)&p->second.addr ,p->second.addrLen);
}
sendto(fd,"OK",2,0,0,0);
}
else if(strncmp(szData,"OPEN_RESP",9)==0)
{
string strClientId = szData+10;
map<string,ConnInfo>::iterator p = g_client_info.find(strClientId);
if(p != g_client_info.end())
{
p->second.iLastTime = time(NULL);
}
}
else if(strncmp(szData,"HEIGHT_RESP",11)==0)
{
string strClientId = szData+12;
}
else if(strncmp(szData,"LOW_RESP",8)==0)
{
string strClientId = szData+9;
}
else
{
close(fd);
it = g_servlist.erase(it);
FD_CLR(fd,&g_set);
printf( "unbknow command socket close" );
break;
}
}
}
}
}
else if(iRet == 0)
{
for(list<ConnInfo>::iterator it=g_servlist.begin();it!=g_servlist.end();)
{
if( it->iFd != g_iUdpFd && it->iFd != g_iServerSocketFd && difftime(time(NULL),it->iLastTime) > 120)
{
printf( "clean not active fd:%d\n" , it->iFd);
FD_CLR(it->iFd,&g_set);
close(it->iFd);
it = g_servlist.erase(it);
}
else
it++;
}
}
else
;
}
}
int CanReceive(int fd,int p_iUsec)
{
fd_set rf;
struct timeval tox;
FD_ZERO(&rf);
FD_SET(fd, &rf);
int i = p_iUsec / 1000 / 1000;
tox.tv_sec = i;
tox.tv_usec = p_iUsec % (1000 * 1000);
//return select(fd + 1, &rf, NULL, NULL, &tox);
return select(FD_SETSIZE, &rf, NULL, NULL, &tox);
}
int CanSend(int fd,int p_iUsec)
{
fd_set rf;
struct timeval tox;
FD_ZERO(&rf);
FD_SET(fd, &rf);
int i = p_iUsec / 1000 / 1000;
tox.tv_sec = i;
tox.tv_usec = p_iUsec % (1000 * 1000);
return select(FD_SETSIZE, NULL, &rf, NULL, &tox);
//return select(fd + 1, NULL, &rf, NULL, &tox);
}