昨天完成了手工的连接,今天用STM32F103连接开发板成功发布数据。
两块开发板的连接:STM32F103C8T6的串口2 TX(PA2)—— EC-01 RX1
STM32F103C8T6的串口2 RX(PA3)—— EC-01 TX1
STM32F103C8T6的开发板的VCC 3.3V —— EC-01 VCC3.3V
STM32F103C9T6 GND —— EC-01 GND
EC的库文件借鉴了RitaChen/STM32_Drive_EC-01F (gitee.com)这位大神的代码。大家要是想连阿里云、OneNET的,可以不用改代码就可以用上。然而我要连自己的服务器,有点费事,还得改改。
EC616S.h
#ifndef __EC616S_H
#define __EC616S_H
#include "stm32f10x.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#if defined ( __CC_ARM )
#pragma anon_unions
#endif
//EC616S模式选择
typedef enum
{
STA,
AP,
STA_AP
}ENUM_Net_ModeTypeDef;
typedef enum
{
RF_Least_Func=0,
RF_Full_Func=1,
RF_OFF=4
}RF_Switch;
//附着网络
typedef enum{
Register_Forbid=0,
Register_status,
Register_status_Location,
Register_status_Location_failResaon,
Register_status_Location_timer,
Register_status_Location_failResaon_timer
} Attach_Net_Mode;
#define EC616S_RST_Pin GPIO_Pin_4 //复位管脚
#define EC616S_RST_Pin_Port GPIOA //复位
#define EC616S_RST_Pin_Periph_Clock RCC_APB2Periph_GPIOA //复位时钟
#define EC616S_CH_PD_Pin GPIO_Pin_5 //使能管脚
#define EC616S_CH_PD_Pin_Port GPIOA //使能端口
#define EC616S_CH_PD_Pin_Periph_Clock RCC_APB2Periph_GPIOA //使能时钟
#define EC616S_RST_Pin_SetH GPIO_SetBits(EC616S_RST_Pin_Port,EC616S_RST_Pin)
#define EC616S_RST_Pin_SetL GPIO_ResetBits(EC616S_RST_Pin_Port,EC616S_RST_Pin)
#define EC616S_CH_PD_Pin_SetH GPIO_SetBits(EC616S_CH_PD_Pin_Port,EC616S_CH_PD_Pin)
#define EC616S_CH_PD_Pin_SetL GPIO_ResetBits(EC616S_CH_PD_Pin_Port,EC616S_CH_PD_Pin)
#define EC616S_USART(fmt, ...) USART_printf (USART2, fmt, ##__VA_ARGS__)
#define PC_USART(fmt, ...) printf(fmt, ##__VA_ARGS__) //这是串口打印函数,串口1,执行printf后会自动执行fput函数,重定向了printf。
#define RX_BUF_MAX_LEN 1024 //最大字节数
extern struct STRUCT_USART_Fram //数据帧结构体
{
char Data_RX_BUF[RX_BUF_MAX_LEN];
union
{
__IO u16 InfAll;
struct
{
__IO u16 FramLength :15; // 14:0
__IO u16 FramFinishFlag :1; // 15
}InfBit;
};
}EC616S_Fram_Record_Struct;
//初始化和TCP功能函数
void EC616S_Init(u32 bound);
void EC616S_AT_Test(void);
bool EC616S_Send_AT_Cmd(char *cmd,char *ack1,char *ack2,u32 time);
void EC616S_Rst(void);
bool EC616S_RF_Mode_Choose(RF_Switch RFmode);
bool EC616S_Net_Attached_Mode_Choose(Attach_Net_Mode Netmode, bool stepSwitch);
bool EC616S_MQTT_Pub( char * topic, char * message);
bool EC616S_MQTT_Sub( char * topic );
bool EC616S_Port_Inquire(char * confirmMsg, bool stepSwitch);
bool EC616S_Network_Status_Inquire(char * confirmMsg, bool stepSwitch);
bool EC616S_Create_OneNET_Connection(bool stepSwitch);
bool EC616S_ADD_Object(char * objectID,int16_t instanceCount, char * instanceBitmap, int16_t attributecount, int16_t actioncount, bool stepSwitch );
bool EC616S_Register_Request(int16_t lifeCycle, int16_t registerUnit,bool stepSwitch);
bool EC616S_Register_Source(int16_t resourceIDLen, char * resourceID, bool stepSwitch);
bool EC616S_Logout(void);
bool EC616S_Delete_Object(char ClientNum, char * objectID);
bool EC616S_Delete_instance(int16_t ClientNum);
bool EC616S_Notify(char * objectID, char * resourceID, int16_t valueLen, char * value);
bool EC616S_Link_lugl_Server( bool stepSwitch);
bool EC616S_Open_lugl_Client_Link ( bool stepSwitch);
void USART_printf( USART_TypeDef * USARTx, char * Data, ... );
#endif
EC616S.c
#include "EC616S.h"
#include "usart.h"
#include "delay.h"
#include <stdarg.h>
#include "stdlib.h"
static char msgID[10] = {0};
struct STRUCT_USART_Fram EC616S_Fram_Record_Struct = { 0 }; //定义了一个数据帧结构体
void EC616S_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(EC616S_RST_Pin_Periph_Clock|EC616S_CH_PD_Pin_Periph_Clock, ENABLE);
GPIO_InitStructure.GPIO_Pin = EC616S_RST_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(EC616S_RST_Pin_Port, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = EC616S_CH_PD_Pin;
GPIO_Init(EC616S_CH_PD_Pin_Port, &GPIO_InitStructure);
uart2_Init(bound);
EC616S_Rst();
}
//对EC616S模块发送AT指令
// cmd 待发送的指令
// ack1,ack2;期待的响应,为NULL表不需响应,两者为或逻辑关系
// time 等待响应时间
//返回1发送成功, 0失败
bool EC616S_Send_AT_Cmd(char *cmd,char *ack1,char *ack2,u32 time)
{
EC616S_Fram_Record_Struct .InfBit .FramLength = 0; //重新接收新的数据包
EC616S_USART("%s\r\n", cmd);
if(ack1==0&&ack2==0) //不需要接收数据
{
return true;
}
printf("delay some time to get receive data\r\n");
delay_ms(time); //提供足够的时间用于接收数据,(数据接收部分在uart中断usart.h --> USART2_IRQHandler(void) 中完成)
delay_ms(1000);
EC616S_Fram_Record_Struct.Data_RX_BUF[EC616S_Fram_Record_Struct.InfBit.FramLength ] = '\0';
printf("EC616S_data:%s",EC616S_Fram_Record_Struct .Data_RX_BUF);
if(ack1!=0&&ack2!=0)
{
return ( ( bool ) strstr ( EC616S_Fram_Record_Struct .Data_RX_BUF, ack1 ) || //查询
( bool ) strstr ( EC616S_Fram_Record_Struct .Data_RX_BUF, ack2 ) );
}
else if( ack1 != 0 ) //strstr(s1,s2);检测s2是否为s1的一部分,是返回该位置,否则返回false,它强制转换为bool类型了
return ( ( bool ) strstr ( EC616S_Fram_Record_Struct .Data_RX_BUF, ack1 ) );
else if( ack2 != 0 )
return ( ( bool ) strstr ( EC616S_Fram_Record_Struct .Data_RX_BUF, ack2 ) );
else
return false;
}
//复位重启
void EC616S_Rst(void)
{
EC616S_RST_Pin_SetL;
delay_ms(500);
EC616S_RST_Pin_SetH;
}
//发送恢复出厂默认设置指令将模块恢复成出厂设置
void EC616S_AT_Test(void)
{
char count=0;
delay_ms(1000);
while(count < 10)
{
printf("AT Test\r\n");
if(EC616S_Send_AT_Cmd("AT","OK",NULL,500))
{
printf("OK\r\n");
return;
}
++ count;
}
}
//EC616S的射频电路的关闭与开启方法
//设置成功返回OK 反之ERROR
bool EC616S_RF_Mode_Choose(RF_Switch RFmode)
{
switch ( RFmode )
{
case RF_Least_Func:
printf("set RF mode: 0\r\n");
return EC616S_Send_AT_Cmd ( "AT+CFUN=0", "OK", NULL, 500 );
case RF_Full_Func:
printf("set RF mode: 1\r\n");
return EC616S_Send_AT_Cmd ( "AT+CFUN=1", "OK", NULL, 500 );
case RF_OFF:
printf("set RF mode: 4\r\n");
return EC616S_Send_AT_Cmd ( "AT+CFUN=4", "OK", NULL, 500 );
default:
printf("illegle command");
return false;
}
}
//EC616S 附着网络
//EC616S的Attach_Net_Mode附着网络类型
//AT+CEREG=0:禁止附着网络
//设置成功返回OK 反之ERROR
bool EC616S_Net_Attached_Mode_Choose(Attach_Net_Mode Netmode, bool stepSwitch)
{
if(stepSwitch){
switch ( Netmode )
{
case Register_Forbid:
printf("Attach_Net_Mode: 0\r\n");
return EC616S_Send_AT_Cmd ( "AT+CEREG=0", "OK", NULL, 500 );
case Register_status:
printf("Attach_Net_Mode: 1\r\n");
return EC616S_Send_AT_Cmd ( "AT+CFUN=1", "OK", NULL, 500 );
case Register_status_Location:
printf("Attach_Net_Mode: 2\r\n");
return EC616S_Send_AT_Cmd ( "AT+CEREG=2", "OK", NULL, 500 );
case Register_status_Location_failResaon:
return EC616S_Send_AT_Cmd ( "AT+CEREG=3", "OK", NULL, 500 );
case Register_status_Location_timer:
return EC616S_Send_AT_Cmd ( "AT+CEREG=4", "OK", NULL, 500 );
case Register_status_Location_failResaon_timer:
return EC616S_Send_AT_Cmd ( "AT+CEREG=4", "OK", NULL, 500 );
default:
printf("illegle command");
return false;
}
}else
return false;
}
//EC616S 配置入网云平台 AT+ECMTCFG="cloud",<tcpconnectID>,<cloudtype>,<data type>
//tcpconnectID MQTT类型入网设置为0
//cloudtype:
// 0 mosquitto平台
// 1 OneNet平台
// 2 阿里云
// 3-255 用户自定义
//data type:
// 阿里云定义如下
// 1 Json 数据
// 2 字符串数据
//设置成功返回OK 反之ERROR
bool EC616S_Cloud_Select( uint8_t cloudType, uint8_t dataType, bool stepSwitch)
{
char cCmd [20];
if(stepSwitch){
printf("Log in cloud: 1\r\n");
sprintf ( cCmd, "AT+ECMTCFG =\"cloud\",0,%d,%d", cloudType, dataType );
return EC616S_Send_AT_Cmd( cCmd, "OK", NULL, 1000 );
}else
return false;
}
//EC616S 打开客户端连接 AT+ECMTOPEN=<tcpconnectID>,“<host_name>”,<port>
bool EC616S_Open_lugl_Client_Link(bool stepSwitch)
{
if(stepSwitch){
char cStr [80];
sprintf ( cStr, "AT+ECMTOPEN=0,\"your host_name\",1883" );
return EC616S_Send_AT_Cmd ( cStr, "OK", 0, 1000 );
}else
return false;
}
//设置成功返回true,反之fasle
bool EC616S_Link_lugl_Server( bool stepSwitch)
{
if(stepSwitch){
char cStr [60];
sprintf ( cStr, "AT+ECMTCONN=0,\"78376LS6RW10U\",\"user_name\",\"pwd\"");
return EC616S_Send_AT_Cmd ( cStr, "OK", 0, 3000 );
}else
return false;
}
//EC616S发布数据
//设置成功返回true, 反之false
bool EC616S_MQTT_Pub ( char * topic, char * message)
{
char cStr [120];
sprintf ( cStr, "AT+ECMTPUB=0,0,0,0,\"%s\",\"%s\"", topic , message );
return EC616S_Send_AT_Cmd ( cStr, "OK", 0, 1000 );
}
//EC616S订阅数据
//设置成功返回true, 反之false
bool EC616S_MQTT_Sub ( char * topic )
{
char cStr [50];
sprintf ( cStr, "AT+ECMTSUB=0,1,\"%s\",1", topic );
return EC616S_Send_AT_Cmd ( cStr, "OK", 0, 1000 );
}
/* 以下内容为入网OneNET的一些操作指令 */
//查询OneNET入网IP以及端口 服务器IP号以及端口号:"+MIPLCONFIG: 1,183.230.40.39,5683"
bool EC616S_Port_Inquire(char * confirmMsg, bool stepSwitch)
{
char cStr [50];
if(stepSwitch){
printf("OneNET IP & PORT\r\n");
sprintf ( cStr, "AT+MIPLCONFIG?" );
return EC616S_Send_AT_Cmd ( cStr, "OK", confirmMsg, 1500 );
}else
return false;
}
//查询当前的网络状态,当且仅当返回“+CEREG:1,1”时表示网络正常,可入网OneNET
bool EC616S_Network_Status_Inquire(char * confirmMsg, bool stepSwitch)
{
if(stepSwitch){
printf("OneNET net status\r\n");
return(EC616S_Send_AT_Cmd("AT+CEREG?","OK",confirmMsg,2000));
}else
return false;
}
//创建OneNET的连接,当且仅当返回“+MIPLCREATE: 0”时表示网络正常,可入网OneNET
bool EC616S_Create_OneNET_Connection(bool stepSwitch)
{
if(stepSwitch){
printf("create OneNET Cloud mount\r\n");
return(EC616S_Send_AT_Cmd("AT+MIPLCREATE","OK",NULL,2000));
}else
return false;
}
//添加对象实例
//"AT+MIPLADDOBJ"命令需要
// 对象ID号
// 实例个数
// 实例位图(一个字符表示一个实例的位图,0表示不可用,1表示可用)
// 属性个数
// 操作个数
bool EC616S_ADD_Object(char * objectID,int16_t instanceCount, char * instanceBitmap, int16_t attributecount, int16_t actioncount, bool stepSwitch )
{
if(stepSwitch){
char cStr [40];
printf("create oneNET object\r\n");
sprintf ( cStr, "AT+MIPLADDOBJ=0,%s,%d,\"%s\",%d,%d", objectID, instanceCount, instanceBitmap, attributecount, actioncount );
return EC616S_Send_AT_Cmd ( cStr, "OK", NULL, 2000 );
}else
return false;
}
//向OneNET发送注册请求
// 参数内容:生命周期
// 注册周期的单位(s)
bool EC616S_Register_Request(int16_t lifeCycle, int16_t registerUnit, bool stepSwitch)
{
char * tempData;
int i = 0;
bool flag = 0;
tempData = (char *) malloc(sizeof(char) * 15);
if(stepSwitch){
char cStr [40];
printf("send oneNET register request\r\n");
sprintf ( cStr, "AT+MIPLOPEN=0,%d,%d", lifeCycle ,registerUnit);
flag = EC616S_Send_AT_Cmd ( cStr, "OK", "MIPLOBSERVE:", 3000 );
if(flag){
tempData = strstr(EC616S_Fram_Record_Struct .Data_RX_BUF, "MIPLOBSERVE:"); //获取"MIPLOBSERVE"在字符串中的位置返回char指针
printf("template Data:%s\n",tempData);
tempData = tempData + sizeof("MIPLOBSERVE:") + 2;
while(*tempData!=','){
msgID[i] = *tempData;
i ++;
tempData++;
}
printf("msgID is:%s\n",msgID);
}
return flag;
}else
return false;
}
//向OneNET注册资源
// 参数:msgID,信息ID,每次注册分配的msgID均会变
// resourceIDLen,资源长度
// resourceID,资源内容
bool EC616S_Register_Source(int16_t resourceIDLen, char * resourceID, bool stepSwitch)
{
if(stepSwitch){
char cStr [40];
sprintf ( cStr, "AT+MIPLDISCOVERRSP=0,%s,1,%d,\"%s\"", msgID, resourceIDLen, resourceID);
return EC616S_Send_AT_Cmd ( cStr, "OK", NULL, 2000 );
}else
return false;
}
//删除对象,参数:对象ID
bool EC616S_Logout(void)
{
return(EC616S_Send_AT_Cmd("AT+MIPLCLOSE=0","OK", 0, 1000 ));
}
//发送注销请求
bool EC616S_Delete_Object(char ClientNum, char * objectID)
{
char cStr [40];
sprintf ( cStr, "AT+MIPLDELOBJ=%c,%s", ClientNum, objectID);
return EC616S_Send_AT_Cmd ( cStr, "OK", NULL, 500 );
}
//删除实例,AT+MIPLDELETE
bool EC616S_Delete_instance(int16_t ClientNum)
{
char cStr [30];
sprintf ( cStr, "AT+MIPLDELETE=%d", ClientNum );
return EC616S_Send_AT_Cmd ( cStr, "OK", NULL, 500 );
}
//上传数据到OneNET平台
bool EC616S_Notify(char * objectID, char * resourceID, int16_t valueLen, char * value)
{
char cStr [30];
sprintf ( cStr, "AT+MIPLNOTIFY=0,%s,%s,0,%s,1,%d,\"%s\",0,0", msgID,objectID,resourceID,valueLen,value);
return EC616S_Send_AT_Cmd ( cStr, "OK", NULL, 3000 );
}
static char *itoa( int value, char *string, int radix )
{
int i, d;
int flag = 0;
char *ptr = string;
/* This implementation only works for decimal numbers. */
if (radix != 10)
{
*ptr = 0;
return string;
}
if (!value)
{
*ptr++ = 0x30;
*ptr = 0;
return string;
}
/* if this is a negative value insert the minus sign. */
if (value < 0)
{
*ptr++ = '-';
/* Make the value positive. */
value *= -1;
}
for (i = 10000; i > 0; i /= 10)
{
d = value / i;
if (d || flag)
{
*ptr++ = (char)(d + 0x30);
value -= (d * i);
flag = 1;
}
}
/* Null terminate the string. */
*ptr = 0;
return string;
} /* NCL_Itoa */
void USART_printf ( USART_TypeDef * USARTx, char * Data, ... )
{
const char *s;
int d;
char buf[16];
va_list ap;
va_start(ap, Data);
while ( * Data != 0 ) // 判断数据是否到达结束符
{
if ( * Data == 0x5c ) //'\'
{
switch ( *++Data )
{
case 'r': //回车符
USART_SendData(USARTx, 0x0d);
Data ++;
break;
case 'n': //换行符
USART_SendData(USARTx, 0x0a);
Data ++;
break;
default:
Data ++;
break;
}
}
else if ( * Data == '%')
{
switch ( *++Data )
{
case 's': //字符串
s = va_arg(ap, const char *);
for ( ; *s; s++)
{
USART_SendData(USARTx,*s);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
}
Data++;
break;
case 'd':
//十进制
d = va_arg(ap, int);
itoa(d, buf, 10);
for (s = buf; *s; s++)
{
USART_SendData(USARTx,*s);
while( USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET );
}
Data++;
break;
default:
Data++;
break;
}
}
else USART_SendData(USARTx, *Data++);
while ( USART_GetFlagStatus ( USARTx, USART_FLAG_TXE ) == RESET );
}
}
mqtt.c
#include "mqtt.h"
#include "EC616S.h"
#include "delay.h"
/*
*以下参数需要用户自行修改才能测试用过
*/
#define pub_topic "temp/1"
#define sub_topic "temp/+"
RF_Switch RFMode = RF_Full_Func;
Attach_Net_Mode NetMode = Register_status;
void EC616S_lugl_MQTTClient_Test(char * buff)
{
EC616S_MQTT_Pub (pub_topic,buff); //发布消息到MQTT服务器
}
void EC616S_lugl_MQTTClient_connect(void)
{
bool stepFlag = 0;
printf("正在配置EC616S参数\r\n");
EC616S_AT_Test(); //AT指令测试
printf("正在关闭飞行模式\r\n");
stepFlag = EC616S_RF_Mode_Choose(RFMode); //关闭飞行模式
printf("附着网络\r\n");
stepFlag = EC616S_Net_Attached_Mode_Choose(NetMode, stepFlag); //附着网络
printf("正在连接MQTT服务器\r\n");
stepFlag = EC616S_Open_lugl_Client_Link(1); //打开TCP客户端
printf("正在认证用户名\r\n");
stepFlag = EC616S_Link_lugl_Server(1); //连接服务器
}
主要的代码就是在这里了,串口配置、中断收发大家去用原子开板的就行了,这里考虑到版权,就不上了,如果需要的话可以私信我。
main.c
int main(void)
{
u16 t;
u16 len;
char cData[4];
u16 times=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
EC616S_Init(9600); //command serial
LED_Init(); //LED端口初始化
//KEY_Init(); //初始化与按键连接的硬件接口
EC616S_lugl_MQTTClient_connect();
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n\r\n");
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
sprintf(cData,USART_RX_BUF,4);
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
EC616S_lugl_MQTTClient_Test(cData);
}else
{
times++;
if(times%5000==0)
{
EC616S_lugl_MQTTClient_connect();
}
if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}
用串口助手发送数到单片机,单片机上传给服务器,并成功收到。效果为:
至此,STM32F103C8T6的测试基本上可以了,下一步,采集温度或者电压,用手机APP进行接收并展示。