Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
Hand tools and fabrication machines | ||||||
|
A smart home is one that is equipped with lighting, heating, and electronic devices that can be controlled remotely by smartphone or via the internet. An internet-based home automation system focuses on controlling home electronic devices whether you are inside or outside your home. Home automation gives an individual the ability to remotely or automatically control things around the home. A home appliance is a device or instrument designed to perform a specific function, especially an electrical device, such as a refrigerator, for household use. The words appliance and devices are used interchangeably.
This project shows how we can make a home automation system using WCH CH32V307 MCU. MQTT protocol is used for remotely controlling home appliances. CH32V307 MCU board is connected to the internet using the Ethernet port.
A free MQTT server broker.hivemq.com is used for establishing MQTT communication with the microcontroller board.
The following diagram shows the connection of the Relay board with the microcontroller board. The diagram shows 4 lamps connected with the relay board. You can connect any AC appliances with the relay board instead of lamps.
The following image shows the real hardware connection of the relay board with the microcontroller board without the lamp connected.
For the programming, I used the example program of MQTT provided by WCH on GitHub. I just added the IP address of the MQTT broker and modify the subscription topic. The image below shows the modification.
I also added the relay control functionality based on the MQTT data received from the Android application. The complete code is provided in the code section. The following video shows the operation of the relay on the MQTT data receive.
A custom Android app was developed for controlling the relay remotely. The source code for the android application is also provided in the code section. The app was designed by MIT APP Inventor.
/********************************** (C) COPYRIGHT *******************************
* File Name : main.c
* Author : WCH
* Version : V1.0.0
* Date : 2022/09/12
* Description : Main program body.
* SPDX-License-Identifier: Apache-2.0
*******************************************************************************/
#include "string.h"
#include "debug.h"
#include "WCHNET.h"
#include "eth_driver.h"
#include "MQTTPacket.h"
/*
*@Note
MQTT: This program is used to demonstrate smart home system using TCP/IP-based MQTT protocol communication
*/
u8 MACAddr[6]; /* MAC Address*/
u8 IPAddr[4] = {192,168,0,10}; /* IP address of the device*/
u8 GWIPAddr[4] = {192,168,0,1}; /* IP address of the gateway*/
u8 IPMask[4] = {255,255,255,0}; /* IP subnet musk*/
u8 DESIP[4] = {35,157,5,219}; /* MQTT broker IP address for hivemq*/
u8 SocketId; /* socket id*/
u8 SocketRecvBuf[RECE_BUF_LEN]; /* socket buffer*/
u8 MyBuf[RECE_BUF_LEN];
u16 desport = 1883; /* MQTT broker port */
u16 srcport = 4200;
char *username = ""; /* server login username */
char *password = ""; /* server login password */
char *sub_topic = "wch/hackster/home/control"; /* subscribe topic */
char *pub_topic = "test/topic"; /* publish topic */
int pub_qos = 0; /* quality of the service*/
int sub_qos = 0;
char *payload = "WCHNET MQTT"; /* message payload*/
u8 con_flag = 0;
u8 pub_flag = 0;
u8 sub_flag = 0;
u8 tout_flag = 0;
u16 packetid = 5;
/*********************************************************************
* @fn GPIO Initialize
*/
void GPIO_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/*********************************************************************
* @fn mStopIfError
*
* @brief check if error.
*
* @return none
*/
void mStopIfError(u8 iError)
{
if (iError == WCHNET_ERR_SUCCESS) return; /* success */
printf("Error: %02X\r\n", (u16)iError); /* error */
}
/*********************************************************************
* @fn TIM2_Init
*
* @brief Initializes TIM2.
*
* @return none
*/
void TIM2_Init( void )
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure={0};
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = SystemCoreClock / 1000000 - 1;
TIM_TimeBaseStructure.TIM_Prescaler = WCHNETTIMERPERIOD * 1000 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM2, TIM_IT_Update ,ENABLE);
TIM_Cmd(TIM2, ENABLE);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update );
NVIC_EnableIRQ(TIM2_IRQn);
}
/*******************************************************************************
* Function Name : Transport_Open
* Description : TCP/IP
* Input : None
* Output : None
* Return : None
*******************************************************************************/
u8 Transport_Open(void)
{
u8 i;
SOCK_INF TmpSocketInf; /* 创建临时socket变量 */
memset((void *)&TmpSocketInf,0,sizeof(SOCK_INF)); /* 库内部会将此变量复制,所以最好将临时变量先全部清零 */
memcpy((void *)TmpSocketInf.IPAddr,DESIP,4); /* 设置目的IP地址 */
TmpSocketInf.DesPort = desport; /* 设置目的端口 */
TmpSocketInf.SourPort = srcport; /* 设置源端口 */
TmpSocketInf.ProtoType = PROTO_TYPE_TCP; /* 设置socket类型 */
TmpSocketInf.RecvBufLen = RECE_BUF_LEN; /* 设置接收缓冲区的接收长度 */
i = WCHNET_SocketCreat(&SocketId,&TmpSocketInf); /* 创建socket,将返回的socket索引保存在SocketId中 */
mStopIfError(i); /* 检查错误 */
i = WCHNET_SocketConnect(SocketId); /* TCP连接 */
mStopIfError(i); /* 检查错误 */
return SocketId;
}
/*******************************************************************************
* Function Name : Transport_Close
* Description : 关闭TCP连接
* Input : None
* Output : None
* Return : None
*******************************************************************************/
u8 Transport_Close(void)
{
u8 i;
i = WCHNET_SocketClose(SocketId,TCP_CLOSE_NORMAL);
mStopIfError(i);
return i;
}
/*******************************************************************************
* Function Name : Transport_SendPacket
* Description : 以太网发送数据
* Input : *buf 发送数据的首字节地址
len 发送数据的长度
* Output : None
* Return : None
*******************************************************************************/
void Transport_SendPacket(u8 *buf, u32 len)
{
u32 totallen;
u8 *p = buf;
totallen = len;
while(1)
{
len = totallen;
WCHNET_SocketSend(SocketId, p, &len); /* 将MyBuf中的数据发送 */
totallen -= len; /* 将总长度减去以及发送完毕的长度 */
p += len; /* 将缓冲区指针偏移*/
if(totallen)continue; /* 如果数据未发送完毕,则继续发送*/
break; /* 发送完毕,退出 */
}
}
/*******************************************************************************
* Function Name : MQTT_Connect
* Description : 创建MQTT连接
* Input : *username 设备名
*password 服务器连接密码
* Output : None
* Return : None
*******************************************************************************/
void MQTT_Connect(char *username, char *password)
{
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
u32 len;
u8 buf[200];
data.clientID.cstring = "admin1";
data.keepAliveInterval = 2000;
data.cleansession = 1;
data.username.cstring = username;
data.password.cstring = password;
len = MQTTSerialize_connect(buf,sizeof(buf),&data);
Transport_SendPacket(buf,len); /*建立MQTT连接*/
}
/*******************************************************************************
* Function Name : MQTT_Subscribe
* Description : MQTT订阅一个主题
* Input : *topic 订阅的主题名
* req_qos 服务质量
* Output : None
* Return : None
*******************************************************************************/
void MQTT_Subscribe( char *topic,int req_qos)
{
MQTTString topicString = MQTTString_initializer;
u32 len;
u32 msgid = 1;
u8 buf[200];
topicString.cstring = topic;
len = MQTTSerialize_subscribe(buf,sizeof(buf),0,msgid,1,&topicString,&req_qos);
Transport_SendPacket(buf,len);
}
/*******************************************************************************
* Function Name : MQTT_Unsubscribe
* Description : MQTT取消订阅一个主题
* Input : *topic 取消订阅的主题名
* Output : None
* Return : None
*******************************************************************************/
void MQTT_Unsubscribe(char *topic)
{
MQTTString topicString = MQTTString_initializer;
u32 len;
u32 msgid = 1;
u8 buf[200];
topicString.cstring = topic;
len = MQTTSerialize_unsubscribe(buf,sizeof(buf),0,msgid,1,&topicString);
Transport_SendPacket(buf,len);
}
/*******************************************************************************
* Function Name : MQTT_Publish
* Description : MQTT发布一个主题
* Input : topic 发布的主题名
* qos 服务质量等级
* payload 有效载荷
* Output : None
* Return : None
*******************************************************************************/
void MQTT_Publish(char *topic, int qos, char *payload)
{
MQTTString topicString = MQTTString_initializer;
u32 payloadlen;
u32 len;
u8 buf[1024];
topicString.cstring = topic;
payloadlen = strlen(payload);
len = MQTTSerialize_publish(buf,sizeof(buf),0,qos,0,packetid++,topicString,payload,payloadlen);
Transport_SendPacket(buf,len);
}
/*******************************************************************************
* Function Name : MQTT_Pingreq
* Description : MQTT发送心跳包
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void MQTT_Pingreq(void)
{
u32 len;
u8 buf[200];
len = MQTTSerialize_pingreq(buf,sizeof(buf));
Transport_SendPacket(buf,len);
}
/*******************************************************************************
* Function Name : MQTT_Disconnect
* Description : 断开MQTT连接
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void MQTT_Disconnect(void)
{
u32 len;
u8 buf[50];
len = MQTTSerialize_disconnect(buf,sizeof(buf));
Transport_SendPacket(buf,len);
}
/*******************************************************************************
* Function Name : msgDeal
* Description : 处理订阅信息
* Input : msg 订阅信息
* len 接收数据长度
* Output : None
* Return : None
*******************************************************************************/
void msgDeal(unsigned char *msg, int len)
{
char command = 0;
unsigned char *ptr = msg;
printf("payload len = %d\r\n",len);
printf("payload: ");
for(u8 i = 0; i < len; i++)
{
command = (u16)*ptr;
//printf("%c ",(u16)*ptr);
printf("%c ",command);
ptr++;
}
printf("\r\n");
if(command == '1'){
GPIO_SetBits(GPIOB,GPIO_Pin_0);
printf("Lamp 1 on");
}
else if(command == '2'){
GPIO_ResetBits(GPIOB,GPIO_Pin_0);
printf("Lamp 1 off");
}
else if(command == '3'){
GPIO_SetBits(GPIOB,GPIO_Pin_1);
printf("Lamp 2 on");
}
else if(command == '4'){
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
printf("Lamp 2 off");
}
else if(command == '5'){
GPIO_SetBits(GPIOB,GPIO_Pin_12);
printf("Lamp 3 on");
}
else if(command == '6'){
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
printf("Lamp 3 off");
}
else if(command == '7'){
GPIO_SetBits(GPIOB,GPIO_Pin_15);
printf("Lamp 4 on");
}
else if(command == '8'){
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
printf("Lamp 4 off");
}
else if(command == '9'){
GPIO_SetBits(GPIOB,GPIO_Pin_15);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
GPIO_SetBits(GPIOB,GPIO_Pin_1);
GPIO_SetBits(GPIOB,GPIO_Pin_0);
printf("Lamp all on");
}
else if(command == '0'){
GPIO_ResetBits(GPIOB,GPIO_Pin_15);
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
GPIO_ResetBits(GPIOB,GPIO_Pin_1);
GPIO_ResetBits(GPIOB,GPIO_Pin_0);
printf("Lamp all off");
}
}
/*********************************************************************
* @fn WCHNET_HandleSockInt
*
* @brief Socket Interrupt Handle
*
* @return none
*/
void WCHNET_HandleSockInt(u8 socketid,u8 initstat)
{
u32 len;
int qos, payloadlen;
MQTTString topicName;
unsigned short packetid;
unsigned char retained, dup;
unsigned char *payload;
if(initstat & SINT_STAT_RECV) /* socket接收中断*/
{
len = WCHNET_SocketRecvLen(socketid,NULL); /* 查询长度 */
WCHNET_SocketRecv(socketid,MyBuf,&len); /* 将接收缓冲区的数据读到MyBuf中*/
switch(MyBuf[0]>>4)
{
case CONNACK:
printf("CONNACK\r\n");
con_flag = 1;
MQTT_Subscribe(sub_topic, sub_qos);
break;
case PUBLISH:
MQTTDeserialize_publish(&dup,&qos,&retained,&packetid,&topicName,
&payload,&payloadlen,MyBuf,len);
msgDeal(payload, payloadlen);
break;
case SUBACK:
sub_flag = 1;
printf("SUBACK\r\n");
break;
default:
break;
}
memset(MyBuf, 0 ,sizeof(MyBuf));
}
if(initstat & SINT_STAT_CONNECT) /* socket连接成功中断*/
{
WCHNET_ModifyRecvBuf(socketid, (u32)SocketRecvBuf, RECE_BUF_LEN);
MQTT_Connect(username, password);
printf("TCP Connect Success\r\n");
}
if(initstat & SINT_STAT_DISCONNECT) /* socket连接断开中断*/
{
con_flag = 0;
printf("TCP Disconnect\r\n");
}
if(initstat & SINT_STAT_TIM_OUT) /* socket连接超时中断*/
{
con_flag = 0;
printf("TCP Timeout\r\n");
Transport_Open();
}
}
/*********************************************************************
* @fn WCHNET_HandleGlobalInt
*
* @brief Global Interrupt Handle
*
* @return none
*/
void WCHNET_HandleGlobalInt(void)
{
u8 initstat;
u16 i;
u8 socketinit;
initstat = WCHNET_GetGlobalInt(); /* 获取全局中断标志*/
if(initstat & GINT_STAT_UNREACH) /* 不可达中断 */
{
printf("GINT_STAT_UNREACH\r\n");
}
if(initstat & GINT_STAT_IP_CONFLI) /* IP冲突中断 */
{
printf("GINT_STAT_IP_CONFLI\r\n");
}
if(initstat & GINT_STAT_PHY_CHANGE) /* PHY状态变化中断 */
{
i = WCHNET_GetPHYStatus(); /* 获取PHY连接状态*/
if(i&PHY_Linked_Status)
printf("PHY Link Success\r\n");
}
if(initstat & GINT_STAT_SOCKET)
{
for(i = 0; i < WCHNET_MAX_SOCKET_NUM; i++)
{
socketinit = WCHNET_GetSocketInt(i);
if(socketinit)WCHNET_HandleSockInt(i,socketinit);
}
}
}
/*********************************************************************
* @fn main
*
* @brief Main program
*
* @return none
*/
int main(void)
{
u8 i;
Delay_Init();
GPIO_INIT();
USART_Printf_Init(115200); /*串口打印初始化*/
printf("MQTT\r\n");
printf("SystemClk:%d\r\n",SystemCoreClock);
printf("net version:%x\n",WCHNET_GetVer());
if( WCHNET_LIB_VER != WCHNET_GetVer() ){
printf("version error.\n");
}
WCHNET_GetMacAddr(MACAddr); /*获取芯片MAC地址*/
printf("mac addr:");
for(int i=0;i<6;i++) printf("%x ",MACAddr[i]);
printf("\n");
TIM2_Init();
i = ETH_LibInit(IPAddr,GWIPAddr,IPMask,MACAddr); /*以太网库初始化*/
mStopIfError(i);
if(i == WCHNET_ERR_SUCCESS) printf("WCHNET_LibInit Success\r\n");
Transport_Open(); /*创建TCP socket*/
while(1)
{
WCHNET_MainTask(); /*以太网库主任务函数,需要循环调用*/
if(WCHNET_QueryGlobalInt()) /*查询以太网全局中断,如果有中断,调用全局中断处理函数*/
{
WCHNET_HandleGlobalInt();
}
if (publishValid == 1) {
publishValid = 0;
if(con_flag) MQTT_Publish(pub_topic,pub_qos,payload);
// if(con_flag) MQTT_Pingreq(); /*心跳包*/
}
}
}
Comments