xld0932 发表于 2022-3-15 15:21

基于MM32与M26物联网入门:远程呼叫及开锁

本帖最后由 xld0932 于 2022-3-16 08:14 编辑

#申请原创#   @21小跑堂

应用场景适用于庭院大门,当有来客呼叫时,实现手动开锁、远程开锁的功能;
应用功能通过MM32主控芯片与M26 2G通讯模块与远程服务器进行连接,当有访客按下CALL按键时,本地响应门铃(本应用暂未接门铃),同时进行远程呼叫;户主可以通过OPEN按键直接给访客开门,也可以通过服务器发送OPEN指令进行远程开门;
在没有访客呼叫时,MM32与服务器之间每间隔10秒钟进行一次心跳包传输,以保证与服务器之间SOCKET链路的连接;
在有访客呼叫时,若没有进行开锁动作,则每间隔3秒钟向服务器端发送一个呼叫指令;当接收到服务器端OPEN开锁命令时,执行开锁动作,开锁时间保持3秒钟,开销期间并向服务器反馈开锁是否成功的状态;
硬件模块在网上淘到一块基于M26的2G通讯板,将主控MCU替换成MM32F0273D6P,这样省去了通讯板的设计及绘制,因为之前做过不少的工业远程控制项目,所以对通讯板的原理、控制都已经很熟悉了;通讯板如下图所示,带有5个接口及一个用户自己定义按键:1路光耦输出控制、2路带极性的光耦输入采集、1路无极性的光耦输入采集、以及1个UART程序烧录接口;
对硬件设计进行分析后,正好满足我们当前的硬件需求:1路光耦输出控制用于门锁的控制、2路带极性的光耦输入采集用于手动开锁按键和门外的呼叫按键、最后1路无极性的光耦输入采集则用于门锁的开关状态检测(我们选用的电磁锁是带有开关状态输出的);
为什么选用MM32F0273D6P这个芯片呢?基于通讯板,我们需要一个LQFP48封装的芯片,其次MM32F0273D6P是一个32位的Cortex-M0的控制芯片,最高工作主频可达到96MHz,运行速度够快;带有128KB FLASH程序存储空间,以及16KB SRAM的运行内存,能够满足在物联网工程项目功能上的应用;最后就是通讯板是通过UART下载程序的,MM32F0273D6P出厂自带了ISP功能,可以通过BOOT引脚来选择启动程序,配合上位机软件实现程序的烧录。
原理图设计除了通讯板之外,我们还需要另外一块硬件单板配合演示/调试,功能如下:2个按键(1个手动开锁按键、1个门外呼叫按键)、1个门锁开关控制电路(继电器控制方式)、1个门锁开关状态检测电路(用于检测门锁的开关状态,以及确认门锁控制是否正常),最后就是开发板的供电(5V)以及门锁供电(12V);原理图如下所示:
PCB设计3D效果图
整机实物图
功能实现代码:按键检测及处理/*******************************************************************************
* @brief      
* @param      
* @retval      
* @Attention   
*******************************************************************************/
void KEY_SubScan(uint8_t *State, uint8_t *Count, uint8_t Value, char *Name)
{
    if(*State == 0)
    {
      if(Value == Bit_RESET) *Count += 1;
      else                   *Count= 0;

      if(*Count > 5)
      {
            *Count = 0; *State = 1;
            printf("\r\n%s Pressed", Name);

            if(strcmp(Name, "OPEN") == 0)
            {
                BSP_LOCK_Control(LOCK_MODE_MANUAL, ENABLE);

                if(RemoteState != 0)
                {
                  RemoteState = 0;
                  M26_SendMessage("Manual Open The Door!");
                }
            }
      }
    }
    else
    {
      if(Value != Bit_RESET) *Count += 1;
      else                   *Count= 0;

      if(*Count > 5)
      {
            *Count = 0; *State = 0;
            printf("\r\n%s Release", Name);

            if(strcmp(Name, "OPEN") == 0)
            {
                BSP_LOCK_Control(LOCK_MODE_MANUAL, DISABLE);
            }
            else if(strcmp(Name, "CALL") == 0)
            {
                if(RemoteState == 0)
                {
                  RemoteState = 1;
                  M26_SendMessage("Calling...");
                }
            }
      }
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @Attention   
*******************************************************************************/
void KEY_Scan(void)
{
    static uint8_t KeyState = {0, 0, 0};
    static uint8_t KeyCount = {0, 0, 0};

    KEY_SubScan(&KeyState, &KeyCount, GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_11), "KEY");
    KEY_SubScan(&KeyState, &KeyCount, GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8), "CALL");
    KEY_SubScan(&KeyState, &KeyCount, GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4), "OPEN");
}
功能实现代码:M26启动及配置uint8_tM26_Buffer;
uint16_t M26_Length=0;


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @Attention   
*******************************************************************************/
void M26_InitBuffer(void)
{
    M26_Length = 0;
    memset(M26_Buffer, 0, sizeof(M26_Buffer));
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @Attention   
*******************************************************************************/
void M26_SendData(uint8_t Data)
{
    UART_SendData(UART2, Data);
    while(UART_GetFlagStatus(UART2, UART_IT_TXIEN) == RESET);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void M26_SendString(char *str)
{
    while(*str)
    {
      M26_SendData(*str++);
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
uint32_t M26_AT_WaitForReply(char *Keyword, uint32_t Timeout)
{
    M26_InitBuffer();

    while(Timeout--)
    {
      if(QUEUE_EMPTY(QUEUE_M26_RX_IDX) == 0)
      {
            M26_Buffer = QUEUE_READ(QUEUE_M26_RX_IDX);

            if(strstr((char *)M26_Buffer, Keyword) != NULL)
            {
                printf("\r\n%s", (char *)M26_Buffer);break;
            }
      }

      SysTick_DelayMS(1);
    }

    return Timeout;
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
uint32_t M26_AT_SendCommand(char *Command, char *Response, uint32_t Timeout)
{
    if(Command != NULL)
    {
      M26_SendString(Command);
      M26_SendString("\r\n");
    }

    if(Timeout != 0)
    {
      return (Timeout - M26_AT_WaitForReply(Response, Timeout));
    }
    else
    {
      return 1;
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void M26_SendMessage(char *str)
{
    char Buffer;

    memset(Buffer, 0, sizeof(Buffer));

    sprintf(Buffer, "AT+QISEND=0,%d", strlen(str));

    M26_AT_SendCommand(Buffer, ">", 1000);

    M26_SendString(str);

    printf("\t%dms", M26_AT_SendCommand(NULL, "SEND OK", 1000));
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void M26_AT_Startup(void)
{
    printf("\t%dms", M26_AT_SendCommand(NULL, "RDY",          10000));
    printf("\t%dms", M26_AT_SendCommand(NULL, "+CFUN: 1",   10000));
    printf("\t%dms", M26_AT_SendCommand(NULL, "+CPIN: READY", 10000));
    printf("\t%dms", M26_AT_SendCommand(NULL, "Call Ready",   10000));
    printf("\t%dms", M26_AT_SendCommand(NULL, "SMS Ready",    10000));
    printf("\t%dms", M26_AT_SendCommand("AT", "OK",         10000));

    while(M26_AT_SendCommand("AT+CSQ", "OK", 500) != 0)
    {
      if(strstr((char *)M26_Buffer, "99,99") == NULL)
      {
            break;
      }

      SysTick_DelayMS(500);
    }


    M26_AT_SendCommand("AT+CCID",   "OK", 500);

    M26_AT_SendCommand("AT+COPS?","OK", 500);

    M26_AT_SendCommand("AT+CREG?","OK", 500);

    M26_AT_SendCommand("AT+CGATT?", "OK", 500);


    M26_AT_SendCommand("AT+QIFGCNT=0", "OK", 500);

    M26_AT_SendCommand("AT+QICSGP=1,\"CMMTM\"", "OK", 500);

    M26_AT_SendCommand("AT+QIDNSIP=1", "OK", 500);

    M26_AT_SendCommand("AT+QIMUX=1", "OK", 500);

    M26_AT_SendCommand("AT+QIOPEN=0,\"TCP\",\"xld0932.xicp.net\",10520", NULL, 0);

    printf("\t%dms", M26_AT_SendCommand(NULL, "CONNECT OK", 20000));
}
功能实现代码:呼叫及开锁控制/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void BSP_LOCK_Control(uint8_t Mode, uint8_t Enable)
{
    LockMode = Mode;

    if(Enable == 0)
    {
      GPIO_WriteBit(GPIOA, GPIO_Pin_6, Bit_SET);      /* 上锁 */
    }
    else
    {
      GPIO_WriteBit(GPIOA, GPIO_Pin_6, Bit_RESET);    /* 开锁 */
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void BSP_LOCK_Handler(void)
{
    static uint8_t LockTimeout = 0;

    if(LockMode == LOCK_MODE_REMOTE)
    {
      LockTimeout++;

      if(LockTimeout == 15)
      {
            if(!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5))
            {
                M26_SendMessage("Open The Door Failure!!!");
            }
            else
            {
                M26_SendMessage("Open The Door Successfully!");
            }
      }

      if(LockTimeout >= 30)
      {
            RemoteState = 0;
            BSP_LOCK_Control(LOCK_MODE_MANUAL, DISABLE);
      }
    }
    else
    {
      LockTimeout = 0;
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void REMOTE_Handler(void)
{
    if(RemoteState == 0)
    {
      if(RemoteHeartbeat++ >= 100)
      {
            RemoteHeartbeat = 0;
            M26_SendMessage("Alive!!!");
      }
    }

    if(RemoteState == 1)
    {
      RemoteCount++;

      if(RemoteCount >= 30)
      {
            RemoteCount = 0;
            M26_SendMessage("Calling...");
      }
    }
    else
    {
      RemoteCount = 0;
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void MESSAGE_Handler(void)
{
    if(QUEUE_EMPTY(QUEUE_M26_RX_IDX) == 0)
    {
      MSG_Buffer = QUEUE_READ(QUEUE_M26_RX_IDX);

      /* 远程开锁控制 & 返回开锁成功或失败 */
      if(strstr((char *)MSG_Buffer, "OPEN") != NULL)
      {
            RemoteState = 2;

            BSP_LOCK_Control(LOCK_MODE_REMOTE, ENABLE);

            MSG_Length = 0;
            memset(MSG_Buffer, 0, sizeof(MSG_Buffer));
      }
    }
}

演示效果服务器我们使用花生壳内网穿透映射的方式,将域名及端口号映射到本地内网的IP和端口,再使用网络调试助手软件建立一个TCP/IP服务器,来进行演示,如下图:
ISP下载程序
系统启动&M26初始化配置
呼叫后手动开锁
呼叫后远程开锁
附件原理图:工程源代码:其它手册资料:

   

ap0405209 发表于 2022-3-25 17:46

请问主控板上的按键是什么型号的按键?能否告知?或者发个链接?谢谢

xld0932 发表于 2022-3-25 18:13

ap0405209 发表于 2022-3-25 17:46
请问主控板上的按键是什么型号的按键?能否告知?或者发个链接?谢谢

TPB01-107L1B,带红色灯 带支架:https://item.szlcsc.com/2885279.html

redone 发表于 2022-3-30 11:02

理解为类似只能门锁的远程开门?

xld0932 发表于 2022-3-30 17:55

redone 发表于 2022-3-30 11:02
理解为类似只能门锁的远程开门?

只是一个雏形,基于这个可以加上摄像头、手机APP、语音等等,就是一个完整的产品了

redone 发表于 2022-4-2 10:33

xld0932 发表于 2022-3-30 17:55
只是一个雏形,基于这个可以加上摄像头、手机APP、语音等等,就是一个完整的产品了 ...

很不错

tpgf 发表于 2022-4-3 09:58

安全性能如何呀

aoyi 发表于 2022-4-3 10:21

整个系统的里子都有了

nawu 发表于 2022-4-3 10:30

这个有有效距离的说法吗

zljiu 发表于 2022-4-3 10:37

是不是还需要什么声波识别啊

gwsan 发表于 2022-4-3 10:46

这个是要加一个通讯卡吗

tfqi 发表于 2022-4-3 10:58

感觉在国内的话用的不会很多

caigang13 发表于 2022-4-3 11:22

不错,帮顶一下

ap0405209 发表于 2022-5-12 16:14

xld0932 发表于 2022-3-25 18:13
TPB01-107L1B,带红色灯 带支架:https://item.szlcsc.com/2885279.html

谢谢楼主,{:handshake:}
页: [1]
查看完整版本: 基于MM32与M26物联网入门:远程呼叫及开锁