Kara 发表于 2023-8-9 16:56:46

机友分享|ESP8266+机智云平台实现APP控制舵机旋转

本帖最后由 Kara 于 2023-8-9 17:42 编辑

机友分享|ESP8266+机智云平台实现APP控制舵机旋转
一、ESP8266模块简介
ESP8266是一款超低功耗的UART-WiFi 透传模块,拥有业内极富竞争力的封装尺寸和超低能耗技术,专为移动设备和物联网应用设计,可将用户的物理设备连接到Wi-Fi 无线网络上,进行互联网或局域网通信,实现联网功能。

1.1 模块特点

[*]支持无线802.11 b/g/n 标准
[*]支持STA/AP/STA+AP三种工作模式
[*]内置TCP/IP协议栈,支持多路TCP Client连接
[*]支持丰富的Socket AT指令
[*]支持UART/GPIO数据通信接口
[*]支持Smart Link 智能联网功能
[*]支持远程固件升级(OTA)
[*]内置32位MCU, 可兼作应用处理器
[*]超低能耗,适合电池供电应用
[*]3.3V 单电源供电

1.2 引脚介绍
ESP8266硬件接口丰富,可支持UART,IIC,PWM,GPIO,ADC等,适用于各种物联网应用场合。



1.3 主要功能和工作模式
*主要功能
[*]ESP8266可以实现的主要功能包括:串口透传,PWM 调控,GPIO控制。
[*]串口透传:数据传输,传输的可靠性好,最大的传输速率为:460800bps。
[*]PWM 调控:灯光调节,三色LED 调节,电机调速等。
[*]GPIO控制:控制开关,继电器等。

*工作模式ESP8266模块支持STA/AP/STA+AP 三种工作模式。
[*]STA 模式:ESP8266模块通过路由器连接互联网,手机或电脑通过互联网实现对设备的远程控制。
[*]AP 模式:ESP8266模块作为热点,实现手机或电脑直接与模块通信,实现局域网无线控制。
[*]STA+AP 模式:两种模式的共存模式,即可以通过互联网控制可实现无缝切换,方便操作。

1.4 调试模块
*硬件接线
注意: 面板板处于通电状态

*发送AT+RST指令
接好线后,将USB转TTL模块接入电脑打开串口助手,发送AT+RST指令。


串口接收到模块返回的信息,调试完成。

二、机智云平台
机智云平台是机智云物联网公司经过多年行业内的耕耘及对物联网行业的深刻理解,而推出的面向个人、企业开发者的一站式智能硬件开发及云服务平台。平台提供了从定义产品、设备端开发调试、应用开发、产测、云端开发、运营管理、数据服务等覆盖智能硬件接入到运营管理全生命周期服务的能力。
机智云平台为开发者提供了自助式智能硬件开发工具与开放的云端服务。通过傻瓜化的自助工具、完善的SDK与API服务能力最大限度降低了物联网硬件开发的技术门槛,降低开发者的研发成本,提升开发者的产品投产速度,帮助开发者进行硬件智能化升级,更好的连接、服务最终消费者。

*文档中心:提供一些开发教程和资料,让快速掌握物联网开发技术变得非常简单。


*开发者中心:创建产品、APP和自动生成代码服务等,提供更高效、更便捷的开发平台服务与交互体验


三、基于机智云平台的物联网开发

3.1 开发流程
[*]在平台开发者界面创建产品和小程序
[*]将GAgent固件烧入WiFi模组中
[*]平台自动生成MCU方案代码
[*]将自动生成的代码移植到ST标准库(主要完成硬件功能设计、WiFi模块与MCU的通信)

3.2关键词介绍
*GAgent:机智云官方提供的固件,可将其烧录进ESP8266 WiFi模组;烧录后,模组原来的AT指令集失去作用,模组能够接入机智云平台,并自动完成模组与平台间的数据交换。GAgent配网方式有airlink和softap。


*MCU与WIFI模块的通信ESP8266用UART通信,并有应答机制;MCU与WIFI模块的通讯可以用MCU自带的USART(支持UART)资源。

四、实操内容
4.1 GAgent固件的烧写(ESP8266)
烧录的方法有两种,一是用烧录器烧录,二是用USB转TTL模块烧录。由于没有烧录器,下面介绍用USB转TTL烧录的方式。
1)下载GAgent固件包


下载好的固件包的内容,根据参数选择烧录的固件包


2)下载安可信ESP8266资料


下载链接:安信可ESP8266系列接入机智云方案及问题排查指引 - Gizwits
3)硬件接线(ESP-01s为例)
接线:

4)打开ESP8266资料中的烧录软件

一直点进去直到找到.exe文件


打开后是这样的图


查看芯片参数(之前调试的时候有)


参数配置


点击start


完成烧录

注意: 烧写失败有可能是线接触不良(Combine包比较大),有时候需重试几次才能烧录成功。

4.2 检查GAgent固件是否烧录成功
1)进入机智云平台随便新建一个产品


随便加个数据点(不然调试助手会检测不到产品)


可以看到左上角有PK和PS


2)下载机智云的串口调试助手


打开串口调试助手


3)将EPS8266模块与usb-TTL连接


注意:ESP其他引脚都接VCC(手册上说悬空也行,但有的芯片必须得全接好才能正常工作)

4)进入模拟MCU、选择串口、SoftAP


点击SoftAp后,串口向模块发送进入SoftAP模式的信息,模块收到后会进行应答。若能接收到模块的信息则说明GAgent烧录成功。

5)打开手机WIFI界面可以看到XPG-GAgent开头的WiFi


4.3 创建产品
根据自己的需求,按照步骤完成产品的创建





4.4 机智云虚拟设备
1)下载中心下载机智云APP


2)开发者中心->虚拟设备->打开APP扫码绑定设备


3)APP上改变舵机角度,云端数据发生相应变化


4.5 MCU自动代码生成+代码移植到标准库(*)
1)自动生成代码服务


下载代码即可


2)自动生成代码说明a. 两个重要的包


自动生成代码中,Gizwits和Utils是我们需要的,一个建立起与机智云的通讯,一个是工具包。

b. 打开MDK-ARM文件夹,打开keil工程文件
可以看到,自动生成的代码是基于Hal库的,我们需要实现自己的功能,并将其移植到标准库中


c. 打开Gizwits中的gizwits_product.c


d. 机智云服务用到的三个外设

可以看到,需要用一个定时器(Timer)和两个串口(USART)

说明:
[*]定时器也可以用TIM1、TIM3,同理串口也不一定要用USART1和USART2
[*]USART1用于打印调试信息,这一部分功能可以删去,但相应要修改一些代码

e. 关于USART1在gizwits_product.c大概两百多行的位置,重写了fputc函数


然后再utils/common.h文件中可以看到GIZWITS_LOG(日志函数)就是printf



在自动生成的代码中,很多调试信息的打印都调用了GIZWITS_LOG


当完成USART1的初始化并重写fputc函数后, 将USART1的端口与usb转TTL模块连接后接入电脑,借助串口助手可以打印调试信息到串口助手


重写的方式如上(本质上就是用USART1发送数据)

f. 主要文件和接口




可参考官方文档: 独立MCU方案接入机智云 - Gizwits

3)代码移植 (需要根据需求进行修改)
*串口通信(Serial.c)#include "stm32f10x.h"
#include "Server.h"
#include "gizwits_protocol.h"
/*
* WIFI模块通信初始化 USART2
*/
void Serial_WIFI_Init(uint32_t BoundRate)
{
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      
      // 配置GPIO
      // Tx
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
      GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
      GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_InitStructure);
      // Rx
      GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
      GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;
      GPIO_Init(GPIOA, &GPIO_InitStructure);
      
      // 配置USART
      USART_InitTypeDef USART_InitStructure;
      USART_InitStructure.USART_BaudRate=BoundRate;
      USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
      USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
      USART_InitStructure.USART_Parity=USART_Parity_No;// 无奇偶校验
      USART_InitStructure.USART_StopBits=USART_StopBits_1; // 一位停止位
      USART_InitStructure.USART_WordLength=USART_WordLength_8b;// 传输字长
      USART_Init(USART2, &USART_InitStructure);
      
      // 打开USART中断
      USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);// 打开接收寄存器非空中断
      // 配置NVIC
      NVIC_InitTypeDef NVIC_InitStructure;
      NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;// 抢占优先级
      NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;// 响应优先级
      NVIC_Init(&NVIC_InitStructure);
      // 启动USART
      USART_Cmd(USART2, ENABLE);
}

void USART2_IRQHandler(void)
{
      uint8_t Data=0;
      if(USART_GetITStatus(USART2, USART_IT_RXNE)!=RESET)
      {
                Data = USART_ReceiveData(USART2);
                gizPutData(&Data, 1);// 解析数据
      }
}

/*
*调试端口USART1
*/
void Serial_DebugInit(void)
{
      GPIO_InitTypeDef GPIO_InitStructure;
      USART_InitTypeDef USART_InitStructure;
      NVIC_InitTypeDef NVIC_InitStructure;
         
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);      
      //TX   PA9
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;      //复用推挽输出
      GPIO_Init(GPIOA, &GPIO_InitStructure);
   
      //RX      PA10
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
      GPIO_Init(GPIOA, &GPIO_InitStructure);

      //Usart1 NVIC 配置
      NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
      NVIC_Init(&NVIC_InitStructure);      

   //USART 初始化设置
      USART_InitStructure.USART_BaudRate = 9600;//串口波特率
      USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
      USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
      USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
      USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
      USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;      //收发模式

      USART_Init(USART1, &USART_InitStructure); //初始化串口1
      USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
      USART_Cmd(USART1, ENABLE);                  
}      

void USART1_IRQHandler(void)   
{
      
}
*Timer.c#include "stm32f10x.h"
#include "Serial.h"
#include "gizwits_product.h"

// TIM3
void Timer_TIM3Init(uint16_t PSC, uint16_t CNT)
{
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
      
      TIM_InternalClockConfig(TIM3);
      // TimeBase
      TIM_TimeBaseInitTypeDef TimerBaseInitStructure;
      TimerBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
      TimerBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
      TimerBaseInitStructure.TIM_Period=CNT;
      TimerBaseInitStructure.TIM_Prescaler=PSC;
      TimerBaseInitStructure.TIM_RepetitionCounter=0;
      TIM_TimeBaseInit(TIM1, &TimerBaseInitStructure);
      TIM_ClearFlag(TIM1, TIM_FLAG_Update);
      // 使能更新中断
      TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
      NVIC_InitTypeDef NVIC_InitStructure;
      NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;// 抢占优先级
      NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;// 响应优先级
      NVIC_Init(&NVIC_InitStructure);
      // 开启TIM
      TIM_Cmd(TIM1, ENABLE);
}

/*
*TIM3 中断函数
*/
void TIM3_IRQHandler(void)
{
      if(TIM_GetITStatus(TIM3, TIM_IT_Update))
      {
                gizTimerMs();// 机智云计数
                // 清除标志位
                TIM_ClearITPendingBit(TIM3, TIM_IT_Update);      
      }
}
*Key.c (用于配置模式)#include "stm32f10x.h"
#include "delay.h"
#include "Server.h"
#include "gizwits_protocol.h"
// PA1作为按键输入
// 2023年4月18日16:32:06

/*
*按键初始化函数下降沿触发 配置为上拉输入
*/
void Key_Init(void)
{
      // RCC 使能时钟
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
      
      // 配置GPIO
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
      GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
      GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_InitStructure);
      
      // 配置AFIO
      GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource1);// PA1
      
      // 配置EXTI
      EXTI_InitTypeDef EXTI_InitStructure;
      EXTI_InitStructure.EXTI_Line=EXTI_Line1;
      EXTI_InitStructure.EXTI_LineCmd=ENABLE;
      EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
      EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;// 下降沿触发
      EXTI_Init(&EXTI_InitStructure);
      
      // 配置NVIC分组 只需配置一次
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      // 配置NVIC
      NVIC_InitTypeDef NVIC_InitStructure;
      NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;// 抢占优先级
      NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;// 响应优先级
      NVIC_Init(&NVIC_InitStructure);
      
}

/*
*EXTI 中断函数
*/
void EXTI1_IRQHandler(void)
{
      if(EXTI_GetITStatus(EXTI_Line1))
      {
                // 消抖
                Delay_ms(40);
                if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)==0)
                {      
                        Serve_Angle += 30;
                }
                while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)==0);
                if(Serve_Angle>180)
                {
                        Serve_Angle = 0;
                }
                Server_SetAngle((float)Serve_Angle);
                // 清除标志位
                EXTI_ClearITPendingBit(EXTI_Line1);
      }

}

// 机智云模式配置按钮 AirLink SoftAP Reset 三种模式
void Key_WIFIModeInit(void)
{
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
      
      GPIO_InitTypeDef GPIO_InitStructure;
      // PB10 按下低电平 配置成上拉输入
      GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
      GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
      GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
      GPIO_Init(GPIOB, &GPIO_InitStructure);
      
      // PB12 PB13 按下高电平 配置成下拉输入
      GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;
      GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12|GPIO_Pin_14;
      GPIO_Init(GPIOB, &GPIO_InitStructure);
      
      // AFIO
      GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource10);
      GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);
      GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
      
      // EXTI
      // PB1 下降沿触发
      EXTI_InitTypeDef EXTI_InitStructure;
      EXTI_InitStructure.EXTI_Line=EXTI_Line10;
      EXTI_InitStructure.EXTI_LineCmd=ENABLE;
      EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
      EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
      EXTI_Init(&EXTI_InitStructure);
      
      // PB12 PB14 上升沿触发
      EXTI_InitStructure.EXTI_Line=EXTI_Line12|EXTI_Line14;
      EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
      EXTI_Init(&EXTI_InitStructure);

      // NVIC
      NVIC_InitTypeDef NVIC_InitStructure;
      NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;// 抢占优先级
      NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;// 响应优先级
      NVIC_Init(&NVIC_InitStructure);
}

/*
*      EXTI通道10-15中断函数 PB10 PB12 PB14 分别对应WIFI模块工作的三种模式
*/
void EXTI15_10_IRQHandler(void)
{
      if(EXTI_GetITStatus(EXTI_Line10))
      {
                // 消抖
                Delay_ms(40);
                if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10)==0)
                {      
                        gizwitsSetMode(WIFI_AIRLINK_MODE);
                        GIZWITS_LOG("AirLink mode\r\n");
                }
                while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10)==0);
               
                // clear
                EXTI_ClearITPendingBit(EXTI_Line10);
      }
      else if(EXTI_GetITStatus(EXTI_Line12))
      {
                // 消抖
                Delay_ms(40);
                if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)==1)
                {      
                        gizwitsSetMode(WIFI_SOFTAP_MODE);
                        GIZWITS_LOG("Soft AP mode\r\n");
                }
                while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12)==1);
               
                // clear
                EXTI_ClearITPendingBit(EXTI_Line12);
      }
      else if(EXTI_GetITStatus(EXTI_Line14))
      {
                // 消抖
                Delay_ms(40);
                if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14)==1)
                {      
                        gizwitsSetMode(WIFI_RESET_MODE);
                        GIZWITS_LOG("Reset mode\r\n");
                }
                while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14)==1);
                // clear
                EXTI_ClearITPendingBit(EXTI_Line14);
      }

}
*gizwits_product.c (注释掉Hal库的内容, 替换成ST库,完成User Handle部分内容)/**
****
* @file         gizwits_product.c
* @brief      Gizwits control protocol processing, and platform-related       hardware initialization
* @author       Gizwits
* @date         2017-07-19
* @version      V03030000
* @copyright    Gizwits
*
* @note         机智云.只为智能硬件而生
*               Gizwits Smart Cloudfor Smart Products
*               链接|增值ֵ|开放|中立|安全|自有|自由|生态
*               www.gizwits.com
*
****/

#include <stdio.h>
#include <string.h>
// #include "hal_key.h"
#include "gizwits_product.h"
#include "common.h"
#include "LED.h"
#include "Server.h"
#include "AD.h"
static uint32_t timerMsCount;
//static uint32_t timerMsCount;
uint8_t aRxBuffer;

/** User area the current device state structure*/
dataPoint_t currentDataPoint;

//extern TIM_HandleTypeDef htim2;
//extern UART_HandleTypeDef huart1;
//extern UART_HandleTypeDef huart2;

/**@} */
/**@name Gizwits User Interface
* @{
*/

/**
* @brief Event handling interface

* Description:

* 1. Users can customize the changes in WiFi module status

* 2. Users can add data points in the function of event processing logic, such as calling the relevant hardware peripherals operating interface

* @param info: event queue
* @param data: protocol data
* @param len: protocol data length
* @return NULL
* @ref gizwits_protocol.h
*/
int8_t gizwitsEventProcess(eventInfo_t *info, uint8_t *gizdata, uint32_t len)
{
    uint8_t i = 0;
    dataPoint_t *dataPointPtr = (dataPoint_t *)gizdata;
    moduleStatusInfo_t *wifiData = (moduleStatusInfo_t *)gizdata;
    protocolTime_t *ptime = (protocolTime_t *)gizdata;
   
#if MODULE_TYPE
    gprsInfo_t *gprsInfoData = (gprsInfo_t *)gizdata;
#else
    moduleInfo_t *ptModuleInfo = (moduleInfo_t *)gizdata;
#endif

    if((NULL == info) || (NULL == gizdata))
    {
      return -1;
    }

    for(i=0; i<info->num; i++)
    {
      switch(info->event)
      {


      case EVENT_Angle:
            currentDataPoint.valueAngle = dataPointPtr->valueAngle;
            GIZWITS_LOG("Evt:EVENT_Angle %4f\n",currentDataPoint.valueAngle);
            //user handle
                        Serve_Angle=currentDataPoint.valueAngle;
                        Server_SetAngle(currentDataPoint.valueAngle);// 设置电机角度
            break;


      case WIFI_SOFTAP:
            break;
      case WIFI_AIRLINK:
            break;
      case WIFI_STATION:
            break;
      case WIFI_CON_ROUTER:

            break;
      case WIFI_DISCON_ROUTER:

            break;
      case WIFI_CON_M2M:

            break;
      case WIFI_DISCON_M2M:
            break;
      case WIFI_RSSI:
            GIZWITS_LOG("RSSI %d\n", wifiData->rssi);
            break;
      case TRANSPARENT_DATA:
            GIZWITS_LOG("TRANSPARENT_DATA \n");
            //user handle , Fetch data from , size is
            break;
      case WIFI_NTP:
            GIZWITS_LOG("WIFI_NTP : [%d-%d-%d %02d:%02d:%02d][%d] \n",ptime->year,ptime->month,ptime->day,ptime->hour,ptime->minute,ptime->second,ptime->ntp);
            break;
      case MODULE_INFO:
            GIZWITS_LOG("MODULE INFO ...\n");
#if MODULE_TYPE
            GIZWITS_LOG("GPRS MODULE ...\n");
            //Format By gprsInfo_t
            GIZWITS_LOG("moduleType : [%d] \n",gprsInfoData->Type);
#else
            GIZWITS_LOG("WIF MODULE ...\n");
            //Format By moduleInfo_t
            GIZWITS_LOG("moduleType : [%d] \n",ptModuleInfo->moduleType);
#endif
      break;
      default:
            break;
      }
    }

    return 0;
}

/**
* User data acquisition

* Here users need to achieve in addition to data points other than the collection of data collection, can be self-defined acquisition frequency and design data filtering algorithm

* @param none
* @return none
*/
void userHandle(void)
{
      currentDataPoint.valueLED = LED_Info();//Add Sensor Data Collection
    currentDataPoint.valueAD_Voltage = AD_Voltage;//Add Sensor Data Collection
}

/**
* Data point initialization function

* In the function to complete the initial user-related data
* @param none
* @return none
* @note The developer can add a data point state initialization value within this function
*/
void userInit(void)
{
    memset((uint8_t*)¤tDataPoint, 0, sizeof(dataPoint_t));
   
    /** Warning !!! DataPoint Variables Init , Must Within The Data Range **/
    /*
    currentDataPoint.valueLED = ;
    currentDataPoint.valueAD_Voltage = ;
    currentDataPoint.valueAngle = ;
    */

}


/**
* @brief Millisecond timing maintenance function, milliseconds increment, overflow to zero

* @param none
* @return none
*/
void gizTimerMs(void)
{
    timerMsCount++;
}

/**
* @brief Read millisecond count

* @param none
* @return millisecond count
*/
uint32_t gizGetTimerCount(void)
{
    return timerMsCount;
}

/**
* @brief MCU reset function

* @param none
* @return none
*/
void mcuRestart(void)
{
    __set_FAULTMASK(1);
    NVIC_SystemReset();
}

/**@} */

#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @briefRetargets the C library printf function to the USART.
* @paramNone
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART1 and Loop until the end of transmission */
//HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
      USART_SendData(USART1, (uint8_t)ch);
      while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);
      return ch;
}

///**
//* @briefPeriod elapsed callback in non blocking mode
//* @paramhtim : TIM handle
//* @retval None
//*/
//void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
//{
//      if(htim==&htim2)
//      {
//                        keyHandle();
//                        gizTimerMs();
//      }
//}

///**
//* @brief Timer TIM3 init function

//* @param none
//* @return none
//*/
//void timerInit(void)
//{
//      HAL_TIM_Base_Start_IT(&htim2);
//}

///**
//* @briefThis function handles USART IDLE interrupt.
//*/
//void HAL_UART_RxCpltCallback(UART_HandleTypeDef*UartHandle)
//{
//    if(UartHandle->Instance == USART2)
//    {
//                              gizPutData((uint8_t *)&aRxBuffer, 1);

//      HAL_UART_Receive_IT(&huart2, (uint8_t *)&aRxBuffer, 1);//开启下一次接收中断
//    }
//}

///**
//* @brief USART init function

//* Serial communication between WiFi modules and device MCU
//* @param none
//* @return none
//*/
//void uartInit(void)
//{
//      HAL_UART_Receive_IT(&huart2, (uint8_t *)&aRxBuffer, 1);//开启下一次接收中断
//}

/**
* @brief Serial port write operation, send data to WiFi module
*
* @param buf      : buf address
* @param len      : buf length
*
* @return : Return effective data length;-1,return failure
*/
int32_t uartWrite(uint8_t *buf, uint32_t len)
{
      uint8_t crc = {0x55};
    uint32_t i = 0;
      
    if(NULL == buf)
    {
      return -1;
    }

    for(i=0; i<len; i++)
    {
//      HAL_UART_Transmit_IT(&huart2, (uint8_t *)&buf, 1);
//                              while (huart2.gState != HAL_UART_STATE_READY);//Loop until the end of transmission

//      if(i >=2 && buf == 0xFF)
//      {
//                                                HAL_UART_Transmit_IT(&huart2, (uint8_t *)&crc, 1);
//                                                while (huart2.gState != HAL_UART_STATE_READY);//Loop until the end of transmission
//      }
                USART_SendData(USART2, (uint8_t)buf);
                while(USART_GetFlagStatus(USART2, USART_FLAG_TXE)==RESET);
                if(i>=2 && buf == 0xFF)
                {
                        USART_SendData(USART2, crc);
                        while(USART_GetFlagStatus(USART2, USART_FLAG_TXE)==RESET);
                }
    }

#ifdef PROTOCOL_DEBUG
    GIZWITS_LOG("MCU2WiFi[%4d:%4d]: ", gizGetTimerCount(), len);
    for(i=0; i<len; i++)
    {
      GIZWITS_LOG("%02x ", buf);

      if(i >=2 && buf == 0xFF)
      {
            GIZWITS_LOG("%02x ", 0x55);
      }
    }
    GIZWITS_LOG("\n");
#endif
               
                return len;
}
*gizwits_protocol.c (注释掉Hal库的代码)main.c#include "stm32f10x.h"
#include "delay.h"
#include "OLED.h"
#include "key.h"
#include "Server.h"
#include "Timer.h"
#include "Serial.h"
#include "AD.h"
#include "LED.h"
#include "gizwits_protocol.h"
#include "gizwits_product.h"

/*
*单片机课设
*2023年4月18日16:06:37
*需求: 1. 上位机控制舵机旋转的角度(0-180°)
*      2. 每5秒返回一次信息(传给上位机和OLED屏幕)
*      3. 按下按键使舵机角度增加30°(若超过180°则回到0°)
*      4. LED灯闪烁作为系统指示灯
*      5. 光敏传感器 控制蜂鸣器报警
*      6. 加入机智云物联网方案
*程序设计:
*      1. 硬件: STM32F103c8t6、面包板、舵机、OLED屏幕
*      2. 上位机与单片机的通信用USART1和DMA1(PA9 Tx, PA10 Rx)
*      3. TIM1用来计时
*      4. TIM2_CH1 用来输出PWM信号控制舵机旋转角度PA0
*                   5. B6、B7、B8、B9接OLED屏幕
*      6. PA1作为按键输入
*/

void Gitwits_Init(void)
{
      Timer_TIM3Init(10-1, 7200-1);// 1ms
      Serial_WIFI_Init(9600);// 波特率9600
      Serial_DebugInit();// 串口初始化
      userInit();// 数据初始化
      gizwitsInit();// 机智云初始化
      Key_WIFIModeInit();// 模式选择按键初始化
}

int main(void)
{
      OLED_Init();
      Gitwits_Init();
      Key_Init();
      Server_Init();
      AD_Init();
      LED_Init();
      OLED_ShowString(1, 1, "Angle:");
      OLED_ShowString(2, 1, "AD_Value:");
      OLED_ShowString(3, 1, "Voltage:0.00V");
      
      while(1)
      {
                gizwitsHandle((dataPoint_t *)¤tDataPoint);// 机智云协议处理 必须
                OLED_ShowNum(1, 7, Serve_Angle, 3);
                OLED_ShowNum(2, 10, AD_Value, 4);
                AD_Voltage = (float)AD_Value*3.3/4096;
                OLED_ShowNum(3, 9, AD_Voltage, 1);
                OLED_ShowNum(3, 11, (uint16_t)(AD_Voltage * 100)% 100, 2);
                LED_Off();
                userHandle(); //数据上行 必须上行的数据可在gizwits_product.c中修改
      }
}
4)其他代码 (MCU实现的功能)
*LED.c#include "stm32f10x.h"
#include "delay.h"

/*
*      初始化LEDPB10低电平驱动
*/
void LED_Init(void)
{
      // RCC使能时钟
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
      
      // 配置GPIO
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
      GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
      GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
      GPIO_Init(GPIOB, &GPIO_InitStructure);
      
      GPIO_SetBits(GPIOB, GPIO_Pin_10);
}

/*
*LED灭
*/
void LED_Off(void)
{
      GPIO_SetBits(GPIOB, GPIO_Pin_10);
}

/*
*LED亮
*/
void LED_On(void)
{
      GPIO_ResetBits(GPIOB, GPIO_Pin_10);

}

uint8_t LED_Info(void)
{
      return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10);
}
#include "stm32f10x.h"
#include "delay.h"

/*
*      初始化LEDPB10低电平驱动
*/
void LED_Init(void)
{
      // RCC使能时钟
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
      
      // 配置GPIO
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
      GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
      GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
      GPIO_Init(GPIOB, &GPIO_InitStructure);
      
      GPIO_SetBits(GPIOB, GPIO_Pin_10);
}

/*
*LED灭
*/
void LED_Off(void)
{
      GPIO_SetBits(GPIOB, GPIO_Pin_10);
}

/*
*LED亮
*/
void LED_On(void)
{
      GPIO_ResetBits(GPIOB, GPIO_Pin_10);

}

uint8_t LED_Info(void)
{
      return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10);
}
*Server.c (舵机驱动)#include "stm32f10x.h"
float Serve_Angle;
// TIM2_CH1 用来输出PWM信号控制舵机旋转角度PA0
// 频率50Hz
void Server_Init(void)
{
      // RCC使能时钟
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      
      // 配置GPIO
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
      GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
      GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_InitStructure);

      // 选择内部时钟作为TIM2的时钟
      TIM_InternalClockConfig(TIM2);
      
      // 配置时基单元
      TIM_TimeBaseInitTypeDef TimeBase_InitStructure;
      TimeBase_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
      TimeBase_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;
      TimeBase_InitStructure.TIM_Period=20000-1;   // ARR
      TimeBase_InitStructure.TIM_Prescaler=72-1;
      TimeBase_InitStructure.TIM_RepetitionCounter=0;
      TIM_TimeBaseInit(TIM2, &TimeBase_InitStructure);
      
      // 配置OC单元(输出比较单元 OutPut Compare)OC1
      TIM_OCInitTypeDef OC_InitStructure;
      TIM_OCStructInit(&OC_InitStructure);
      OC_InitStructure.TIM_OCMode=TIM_OCMode_PWM1;
      OC_InitStructure.TIM_Pulse=0;// CCR寄存器
      OC_InitStructure.TIM_OCPolarity=TIM_OCPolarity_High; // 设置有效电平
      OC_InitStructure.TIM_OutputState=TIM_OutputState_Enable;
      TIM_OC1Init(TIM2, &OC_InitStructure);
      
      // 开启TIM
      TIM_Cmd(TIM2, ENABLE);
}

/*
*设置OC1 CCR寄存器的值
*/
void Server_Set_Compare1(uint16_t Compare)
{
      TIM_SetCompare1(TIM2, Compare);
}


/*
      设置舵机角度
      因为舵机需要20ms的波长来驱动 可知频率为50Hz 若ARR设置为 20000-1 则PSC设置为72-1
      又0.5ms~2.5ms对应舵机角度的0~180°
      0.5ms 对应ARR为5002.5ms 对应ARR为2500
      对应角度的CCR应该设置为(Angle / 180 * 2000 + 500)
*/
void Server_SetAngle(float Angle)
{
      Server_Set_Compare1(Angle / 180 * 2000 + 500);
}
*AD.c#include "stm32f10x.h"
#include "MyDMA.h"
#include "LED.h"
uint16_t AD_Value;
float AD_Voltage;
/*
*初始化ADC
*/
void AD_Init(void)
{
      // 初始化DMA通道1运输ADC1的数据
      AD_MyDMA_Init();
      // RCC使能时钟
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
      RCC_ADCCLKConfig(RCC_PCLK2_Div6);// 12MHz mm
      // 配置GPIO口
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
      GPIO_Init(GPIOA, &GPIO_InitStructure);
      // 选择规则通道
      ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 1, ADC_SampleTime_55Cycles5);
      
      // 配置ADC转换器
      ADC_InitTypeDef ADC_InitStructure;
      ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;// 单次转换或者连续转换
      ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;// 数据对齐模式
      ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;// ADC模式, 单独还是交叉
      ADC_InitStructure.ADC_NbrOfChannel = 1;// 扫描的通道数
      ADC_InitStructure.ADC_ScanConvMode = DISABLE;// 扫描模式或者非扫描模式
      ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;// 触发控制
      ADC_Init(ADC1, &ADC_InitStructure);
      
      // 开启DMA转运
      ADC_DMACmd(ADC1, ENABLE);
      
      // 开启模拟看门狗
      ADC_AnalogWatchdogThresholdsConfig(ADC1, 0xFFF, 0x5DC);// 设置看门狗阈值
      ADC_AnalogWatchdogSingleChannelConfig(ADC1, ADC_Channel_7); // 对通道7设置看门狗
      ADC_AnalogWatchdogCmd(ADC1, ADC_AnalogWatchdog_SingleRegEnable);// 使能单通道模拟看门狗
      ADC_ITConfig(ADC1, ADC_IT_AWD, ENABLE);// 开启模拟看门狗中断
      
      // 配置NVCI
      NVIC_InitTypeDef NVIC_InitStructure;
      NVIC_InitStructure.NVIC_IRQChannel=ADC1_2_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
      NVIC_Init(&NVIC_InitStructure);
      // 开启ADC功能
      ADC_Cmd(ADC1, ENABLE);
      
      // ADC校准
      ADC_ResetCalibration(ADC1);
      while(ADC_GetResetCalibrationStatus(ADC1) == SET);// 已初始化为零
      ADC_StartCalibration(ADC1);
      while (ADC_GetCalibrationStatus(ADC1) == SET);
      ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

/*
*ADC中断函数
*/
void ADC1_2_IRQHandler(void)
{
      if(ADC_GetITStatus(ADC1, ADC_IT_AWD)==SET)
      {
                // 开启警报灯
                LED_On();
                // 清除中断标志
                ADC_ClearITPendingBit(ADC1, ADC_IT_AWD);
      }

}
*MyDMA.c#include "stm32f10x.h"
#include "Serial.h"
#include "AD.h"
#define TXBUFFERSIZE 11
#define RXBUFFERSIZE 15

/*
*      ADC DMA初始化
*/
void AD_MyDMA_Init(void)
{
      // RCC使能时钟
      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
      // 配置DMA
      DMA_InitTypeDef DMA_InitStructure;
      DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // ADC的数据寄存器
      DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
      // 地址非自增, ADC可以理解为上菜的桌子只有一个
      DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
      DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&AD_Value;// 保存的地址
      DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
      DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// 这里要自增
      DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;// 转运方向
      DMA_InitStructure.DMA_BufferSize = 1;
      DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// 自动重装
      DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;         // 硬件触发
      DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
      DMA_Init(DMA1_Channel1, &DMA_InitStructure);
      // 开启DMA
      DMA_Cmd(DMA1_Channel1, ENABLE);// 通道1
}将移植后的代码下载进单片机中,连接好硬件电路后,即可进入下一步接线示范 (仅供参考,根据自己的实际需求接)


说明:
[*]A2、A3为USART的端口,分别接WIFI模块的TX、RX
[*]WIFI模块出了TX、RX和GND,其余引脚工作时接高电平(手册解释有些引脚浮空也行,但我这块实测都得接高电平)
[*]B10、B12、B14为选择WIFI模块工作模式的三个按键分别对应(RESET、SoftAP、AirLink)
[*]A7为光敏传感器模拟信号输入口
[*]A0为控制舵机PWM信号输出口
[*]A1接按键,控制角度加30°
[*]这里USART1的A9、A10未接线,可接USB转TTL模块将调试信息打印到电脑的串口助手

5)设备连网
a. 准备工作
需要: 机智云APP,两台移动设备(手机,一台用来开热点,热点频率为2.4G)

b. 机智云APP下载

c. 用另外一台设备开启热点 (注意频段为2.4G)


d. 进入机智云连接设备




e. 输入热点密码,下一步


f. 选择乐鑫 (选择模块对应的模组) 继续点直到进入,这时候先别点,先按下B10的按键(SoftAP模式的按键),让模组进入SoftAP工作模式,然后点几蓝色字体



g. 点击XPG-GAgent-7067(漏了一步,在点击XPG前,手机要先连上热点)




注意:

[*]若找不到XPG开头的,则可将MCU与WIFI模组通信的串口的发送口,通过USB转TTL接到电脑上,用串口助手查看发送的信息是否正确(与实操02中的协议一致),若不一致则需进一步进行检查。


[*]还有一种情况,需接受调试串口的信息,看程序是否运行正常


h. 回到机智云调试APP,等待设备连接


若连接失败: 则检查热点质量,检查输入的热点密码是否正确

i. 可以看到设备在线,点进去后




j. 可以通过手机控制舵机的角度



五、APP生成
1)创建→ \rightarrow→移动应用→ \rightarrow→应用名称、应用包名随便填(应用包名最好英文)→ \rightarrow→关联应用,不关联



2)关联应用→ \rightarrow→选择产品 (图标壁纸可以根据喜好,自己上传资源)


3)构建应用→ \rightarrow→应用构建→ \rightarrow→构建测试版



4)构建成功后可以用手机下载


5)添加设备 (过程与在调试APP添加设备类似)

6)界面展示



原文地址:https://blog.csdn.net/nanxl1/article/details/131903720



页: [1]
查看完整版本: 机友分享|ESP8266+机智云平台实现APP控制舵机旋转