【STM32】MCU(STM32f103)+TCP模组(ESP8266)移植阿里云MQTT的C_SDK到mcu 连接阿里云物联网

零、写在前面

1.开发板 以及 配套工具

开板:野火 stm32f103 指南者

wifi芯片 :ESP8266 板载的模块 出厂自带固件

C_SDK :阿里云官网 c_sdk_3.0.1版本

IDE:keil 5

2.开发过程

首先学习、简单了解MQTT协议:

1.简介

Message Queuing Telemetry Transport 消息队列遥测传输协议,物联网的应用层协议,运行在TCP/IP,依赖TCP协议。

基于TCP协议的 CS模型,发布/订阅主题消息的轻量级协议。

2.通信模型

mqtt提供一对多的消息发布,协议中三种身份:发布者publisher、服务器broker、订阅者Subscriber

publisher和Subscriber都是客户端,服务器起到的是中转作用

 

MQTT 客户端的功能:

  • \1. 发布消息给其它相关的客户端。

  • \2. 订阅主题请求接收相关的应用消息。

  • \3. 取消订阅主题请求移除接收应用消息。

  • \4. 从服务端终止连接

mqtt服务器 :一般是云服务器,BAT三家中的物联网平台常使用mqtt协议。

  • \1. 接受来自客户端的网络连接请求。

  • \2. 接受客户端发布的应用消息

  • \3. 处理客户端的订阅和取消订阅请求。

  • \4. 转发应用消息给符合条件的已订阅客户端(包括发布者自身)。

3.消息主题 服务质量

主题 (Topic ):

客户端知道服务上有很多个主题。就好比如说有很多消息的分类一样子。有社会新闻、体育讲坛等。那么客户端只要找到自己感兴趣的进行订阅就可以了。一个客户端可以向服务器订阅多个主题。而所谓的发布就是客户端对不同的主题进行发布信息。

如果主题只是一个字符串值的话,那么显然会比较单调。这样子功能也显得比较无力。所以在主题上面就了所谓的分隔符和通配符的说法(个人想法)。分隔符的意思就是让主题可以分层次。就好如说主题“体育讲坛/篮球/NBA”。看到这样子的主题,请问一下你还有什么不明白的话。是不是感觉很有层次感。

如果我们订阅了主题“体育讲坛/篮球/NBA”,并向主题“体育讲坛/篮球”发布一个信息。那么已经订阅主题“体育讲坛/篮球/NBA”的客户端们是不是可以接受到信息呢?反过来讲如果我们订阅了主题“体育讲坛/篮球”,向主题“体育讲坛/篮球/NBA”发信息,客户端们是否又能接受信息呢?? (答案:不行!)

如果想要收到NBA就是必须订阅主题“体育讲坛/篮球/NBA”。

通配符:

可是总是有一些人只要是篮球的新闻有喜欢。怎么办。通配符的功能就出来了。通配符有俩种——"+"和“#”。

+为单层的通配符。表示当前这一层的全都合法。

订阅一个主题为“体育讲坛/篮球/+”,那么“体育讲坛/篮球/NBA”和“体育讲坛/篮球/ABC”各发布的信息订阅者都可以收到。

“#“通配符就是表示当前本身和下面子层所有。

服务质量:

服务质量分3等级:

QoS0:最多发送一次消息,在消息发送出去后,接收者不会发送回应,发送者也

不会重发消息,消息可能送达一次也可能根本没送达,这个服务质量常用在不重

要的消息传递中,因为即使消息丢了也没有太大关系。

QoS1:最少发送一次消息(消息最少需要送达一次,也有可送达多次),QoS 1

的 PUBLISH 报文的可变报头中包含一个报文标识符,需要 PUBACK 报文确认。

即需要接收者返回 PUBACK 应答报文。

QoS2:这是最高等级的服务质量,消息丢失和重复都是不可接受的,只不过使用

这个服务质量等级会有额外的开销,这个等级常用于支付中,因为支付是必须有

且仅有一次成功,总不能没给钱或者给了多次钱吧。

-------------------------------------------------------------------------

学习完总结:

要让开发板连接到阿里云,要使用mqtt协议,移植mqtt(因为阿里云物联网的平台就是基于mqtt的应用层)
    移植MQTT有两种方式:一、移植MQTT的AT指令固件到esp8266中,MCU使用MQTT的AT指令连接阿里云、订阅、发布  
                        二、MQTT移植到MUC的代码中,应用层代码
                        
    这里选择第二种:MQTT移植到MUC的代码中,移植方法有以下几种:
        1.写好ESP8266的代码后,根据MQTT协议,自己写mqtt_connect mqtt_publish mqtt_subcribe
        2.写好ESP8266的AT指令封装函数 ,移植SDK ,改写SDK层中的几个接口....
        3.将mqtt源码编译到项目,修改transport.c文件
    
    这里选择方法2:移植SDK到MCU
        0.阿里云文档:SDK获取 https://help.aliyun.com/document_detail/164249.html?spm=a2c4g.11186623.2.15.6110fd92j5qQJW#concept-2050379
        1.基于KEIL、IAR等开发工具进行开发,需要了解如何裁剪:https://help.aliyun.com/document_detail/111026.html?spm=a2c4g.11186623.2.29.34544e1eQnfBnc
        2.MCU+TCP模组 https://help.aliyun.com/document_detail/97921.html?spm=a2c4g.11186623.2.31.34544e1e2xUS61
            抽取mqtt功能的移植SDK说明https://help.aliyun.com/document_detail/111026.html?spm=a2c4g.11186623.6.599.2cb965b2dvPiuc
            SDK官方文档:HAL库接口说明https://help.aliyun.com/document_detail/100111.html?spm=a2c4g.11186623.2.54.10045f4f29qKrw
        3.看懂文档,按照文档去配置SDK功能(运行config.bat) 生成代码后,实现HAL库对接 

太艰难了.....走了能走的所有弯路!

这里我选择的是:将协议移植到MCU上,就是MCU运行程序,wifi芯片esp8266只调用AT指令去完成网络功能:接入手机热点、TCP连接connect服务器等等...

下面是走的弯路心路历程:

    有时候想想,并不是不会做,而是不知道从哪里入手,从哪里开始,各种方法哪一种最简单高效...
	最开始,准备找支持mqtt协议的esp8266固件,将固件烧写后直接调用AT指令
	然后确定没有这样开源的固件,就算有这种方法不太好...
	后开始看官方文档,准备在MCU层写MQTT协议,应用层嘛就写好协议报文就行,底层的esp8266等等都写好了
	怎么写?移植MQTT的源码,修改源码? CSDN上查了下发现大都做出来的是:自己根据协议写好mqtt_connect 等函数 ,我觉得这样速度快 高效 简单点
	写到最后连不上去,后来问人
	然后说 这样写不行,自己写mqtt_connect publish不太现实,协议是开源的库,不要重复造轮子
	移植阿里云官方的SDK....选择C SDK 下载下来
	移植官方的SDK,有3.x版本、4.x版本,好像4.x的更适用于通信模组的SDK 
	于是选择3.x版本 
	准备移植,发现3.0.1版本 和 3.1.0版本 坑死我,3.1.0的不管怎么配置功能,最后生成的代码就是不对,我明明没配置SSL功能,但是程序就是需要我去实现SSL的4个函数
	于是再配置3.0.1版本,配置的时候要选择功能:选ATM 还是不选 ,选择使用阿里云提供的层层接口?
		配置不同的功能,出来的代码量不一样,需要自己实现的函数也不一样
		使能ATM :FEATURE_ATM_ENABLED :系统是使用MCU+外接模组的架构, 并且SDK运行在MCU上, 必须打开该选项, 然后进行配置	
			但是:文档里开始说://FEATURE_MQTT_ATM_ENABLED	是否使能SDK中的ATM模块	可选
				这个可选 让我觉得不重要... 等等 这两个东西 不一样....
				我草!!看混了.... 
			文档中又看到://FEATURE_ATM_ENABLED 如果希望使用阿里提供的AT TCP或者AT Parser,请使能本选项。当本模块使能之后, 还会出现是否使能AT TCP的配置选项	
		使能TCP :不需要
		使能AT parase :AT指令数据收发解析模块 (SDK提供的AT收发/解析的框架)
	终于 成功配置出代码,将代码放到keil工程中 编译还算顺利(之前的3.1.0版本的 编译出几百个错误 根本就解决不了 解决)
	补全wappers.c中的接口:补全后测试
	改3-4个bug 
	终于 终于..... 
	我的STM32F103开发板连接到了阿里云!

好吐槽完,下面开始正式移植:按照步骤即可

 

 

一、阿里云物联网创建产品、设备

这部分内容网上非常多的例子,随便搜一篇博客都有操作方法,并且官网文档资料也很详细...

建议仔细研究阿里云官网文档!

仔细研究阿里云官网文档!

仔细研究阿里云官网文档!

参考链接:

https://www.mscto.com/iot/382885.html

https://help.aliyun.com/document_detail/73705.html?spm=a2c4g.11186623.6.566.4f965a10nivV49

下面是我创建好产品的实例:

 

二、官网文档阅读

这一步骤很重要!做完之后我才意识到:为什么我走了那么多弯路,就是因为没好好看文档,文档中有介绍很全面很细节的地方,我忽略了,每次都是看两眼文档觉得差不多了就闷头去干!

1.重点内容

第一部分:了解创建产品、设备,如何上云,使用技术等

2.第二部分:

了解MQTT的过程,方法,具体操作

https://help.aliyun.com/document_detail/96623.html?spm=a2c4g.11186623.2.16.11a22cf0nbQPag

 

三、移植过程

参考博客:https://blog.csdn.net/spiremoon/article/details/104754432

这位博主的做法和我的做法是一模一样的。

1.下载C_SDK

这里我下载了c_sdk3.0.1 ,因为在配置功能的时候走了坑啊!坑死我了!3.1.0的不管怎么配置功能,最后生成的代码就是不对,我明明没配置SSL功能,但是程序就是需要我去实现SSL的4个函数。实在找不到原因,最后无奈试了试c_sdk3.0.1,没想到配置出后,生成的接口是我需要的。

下载链接官网都有:

下面是我下载下的c_sdk,解压后:(同样 官网有文档介绍每个文件夹的内容)

其中:config.bat是windows脚本,点击改文件在Windows下配置(其他文件makefile等就是在linux下使用的),

extract.bat是配置好功能后,使用该脚本抽取代码,抽取代码后会生成一个output文件夹,我们配置的功能代码就在这个文件夹。(其他的文件夹的代码就不需要了,只需要这个output)

2.功能配置

点击config.bat,出现如下界面:

这里我配置的功能不多,只配置了必须的,如下:

一共就这6个 :
PLATFORM_HAS_STDINT (告诉SDK当前要移植的嵌入式平台是否有自带标准头文件<stdint.h>)
PLATFORM_HAS_DYNMEM(可以使用动态内存分配 我直接使用了malloc 和 free)
FEATURE_MQTT_COMM_ENABLED(长连接功能, 打开后将使SDK提供MQTT网络收发的能力和接口)
FEATURE_MQTT_DEFAULT_IMPL(SDK内包含的MQTT Client实现, 打开则表示使用SDK内置的MQTT客户端实现)
FEATURE_MQTT_DIRECT(mqtt直连)
FEATURE_ATM_ENABLED(系统是使用MCU+外接模组的架构, 并且SDK运行在MCU上, 必须打开该选项, 然后进行配置)

有两个功能当时很纠结,看阿里云文档也不知道配置不配置差距多大,所以这里我没有配置:

FEATURE_AT_TCP_ENABLED   (一个模组TCP连接和数据缓存的模块)

FEATURE_AT_PARSER_ENABLED  (AT指令数据收发解析模块 (SDK提供的AT收发/解析的框架))

 这两个功能阿里云文档有介绍,我一开始配置了但是出来的代码功能多,编译各种报错,后来就放弃,准备自己写接口...去实现

3.移植代码到项目

将output文件夹下的几乎所有.c文件移植到keil工程中,个别文件除外

移植后修改mqtt_example.c中的main函数名,因为此时程序中有两个main,编译报错!

这里我修改成mqtt_main:并在主函数中调用这个mqtt_main。

编译!解决语法问题:这里应该会出现很多头文件找不到,需要在项目中把头文件路径加上。

 

我一个日志不打印的问题,修改了项目中的代码:很奇怪我明明配置正确功能,但是日志功能....出错

移植后 日志不打印 后修改mqtt_internal.h 把之前的屏蔽掉 使用HAL_Printf
			#ifdef INFRA_LOG
			#include "infra_log.h"
		/*  #define mqtt_emerg(...)             log_emerg("MQTT", __VA_ARGS__)
			#define mqtt_crit(...)              log_crit("MQTT", __VA_ARGS__)
			#define mqtt_err(...)               log_err("MQTT", __VA_ARGS__)
			#define mqtt_warning(...)           log_warning("MQTT", __VA_ARGS__)
			#define mqtt_info(...)              log_info("MQTT", __VA_ARGS__)
			#define mqtt_debug(...)             log_debug("MQTT", __VA_ARGS__)*/
			#define mqtt_emerg(...)             do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
			#define mqtt_crit(...)              do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
			#define mqtt_err(...)               do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
			#define mqtt_warning(...)           do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
			#define mqtt_info(...)              do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
			#define mqtt_debug(...)             do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)

 

四、编写wapper.c中的接口

到了最难的一步了!

第一部分的函数:相对简单,网上有例子,c_sdk中有参考实例:因为我项目中没有使用操作系统

第二部分:

最难最难的地方,我没有配置TCP的两个中间层的功能,所以就要自己写好这几个函数。其中最难写的是HAL_TCP_Read,这个函数我还有bug,所以我的项目还是没调通,只是能够连接上阿里云,阿里云显示我的设备在线.....

以下是我的wapper.c

#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "infra_types.h"
#include "infra_defs.h"
#include "wrappers_defs.h"
#include "mqtt.h"
#include <time.h>
#include "esp8266.h"
#include "bsp_usart.h"
/**
 * @brief Deallocate memory block
 *
 * @param[in] ptr @n Pointer to a memory block previously allocated with platform_malloc.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_Free(void *ptr)
{
    free(ptr);
}

/**
 * @brief Allocates a block of size bytes of memory, returning a pointer to the beginning of the block.
 *
 * @param [in] size @n specify block size in bytes.
 * @return A pointer to the beginning of the block.
 * @see None.
 * @note Block value is indeterminate.
 */
void *HAL_Malloc(uint32_t size)
{
	printf("HAL_Malloc(uint32_t size :%d)",size);
    return malloc(size);
}

/**
 * @brief Create a mutex.
 *
 * @retval NULL : Initialize mutex failed.
 * @retval NOT_NULL : The mutex handle.
 * @see None.
 * @note None.
 */
void *HAL_MutexCreate(void)
{
    return (void *)1;
}


/**
 * @brief Destroy the specified mutex object, it will release related resource.
 *
 * @param [in] mutex @n The specified mutex.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_MutexDestroy(void *mutex)
{
    return;
}


/**
 * @brief Waits until the specified mutex is in the signaled state.
 *
 * @param [in] mutex @n the specified mutex.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_MutexLock(void *mutex)
{
    return;
}


/**
 * @brief Releases ownership of the specified mutex object..
 *
 * @param [in] mutex @n the specified mutex.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_MutexUnlock(void *mutex)
{
    return;
}


/**
 * @brief Writes formatted data to stream.
 *
 * @param [in] fmt: @n String that contains the text to be written, it can optionally contain embedded format specifiers
     that specifies how subsequent arguments are converted for output.
 * @param [in] ...: @n the variable argument list, for formatted and inserted in the resulting string replacing their respective specifiers.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_Printf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
    fflush(stdout);
}


int HAL_Snprintf(char *str, const int len, const char *fmt, ...)
{
    va_list args;
    int rc;

    va_start(args, fmt);
    rc = vsnprintf(str, len, fmt, args);
    va_end(args);
    return rc;
}


/**
 * NOTE:
 *
 * HAL_TCP_xxx API reference implementation: wrappers/os/ubuntu/HAL_TCP_linux.c
 *
 */

#include "infra_compat.h"
#include "stdarg.h"
/**
 * 函数 HAL_GetDeviceName() 需要SDK的使用者针对SDK将运行的硬件平台填充实现, 供SDK调用
 * 如果需要参考如何实现函数 HAL_GetDeviceName(), 可以查阅SDK移植到 Ubuntu Linux 上时的示例代码
 * https://code.aliyun.com/linkkit/c-sdk/blob/v3.0.1/wrappers/os/ubuntu/HAL_OS_linux. 
 */
/**
 * @brief Get device name from user's system persistent storage
 *
 * @param [ou] device_name: array to store device name, max length is IOTX_DEVICE_NAME_LEN
 * @return the actual length of device name
 */
int HAL_GetDeviceName(char device_name[IOTX_DEVICE_NAME_LEN])
{
	int len = strlen(DEVICENAME);
	
    memset(device_name,0,IOTX_DEVICE_NAME_LEN);
	strncpy(device_name,DEVICENAME,len);
    
	return len;
}


/**
 * @brief Get device secret from user's system persistent storage
 *
 * @param [ou] device_secret: array to store device secret, max length is IOTX_DEVICE_SECRET_LEN
 * @return the actual length of device secret
 */
int HAL_GetDeviceSecret(char device_secret[IOTX_DEVICE_SECRET_LEN])
{
    int len = strlen(DEVICESECRET);
    memset(device_secret,0,IOTX_DEVICE_SECRET_LEN);
    strncpy(device_secret,DEVICESECRET,len);
	return len;
}

/**
 * @brief Get firmware version
 *
 * @param [ou] version: array to store firmware version, max length is IOTX_FIRMWARE_VER_LEN
 * @return the actual length of firmware version
 */
int HAL_GetFirmwareVersion(char *version)
{
    char *ver = "app-1.0.0-20200915.1000";
    int len = strlen(ver);
    memset(version, 0x0, IOTX_FIRMWARE_VER_LEN);
    strncpy(version, ver, IOTX_FIRMWARE_VER_LEN);
    version[len] = '\0';
    return strlen(version);
}

/**
 * @brief Get product key from user's system persistent storage
 *
 * @param [ou] product_key: array to store product key, max length is IOTX_PRODUCT_KEY_LEN
 * @return  the actual length of product key
 */
int HAL_GetProductKey(char product_key[IOTX_PRODUCT_KEY_LEN])
{
    int len = strlen(PRODUCTKEY);
    memset(product_key,0,IOTX_PRODUCT_KEY_LEN);
    strncpy(product_key,PRODUCTKEY,len);
    
	return len;
}


int HAL_GetProductSecret(char *product_secret)
{
    int len = strlen(PRODUCYSECRET);
    memset(product_secret,0,len);
    strncpy(product_secret,PRODUCYSECRET,len);
	return len;
}


uint32_t HAL_Random(uint32_t region)
{
	return (uint32_t)1;
}



/*      set不需要实现          */
int HAL_SetDeviceName(char *device_name)
{
	return (int)1;
}
int HAL_SetDeviceSecret(char *device_secret)
{
	return (int)1;
}
int HAL_SetProductKey(char *product_key)
{
	return (int)1;
}
int HAL_SetProductSecret(char *product_secret)
{
	return (int)1;
}


/**
 * @brief Sleep thread itself.
 *
 * @param [in] ms @n the time interval for which execution is to be suspended, in milliseconds.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_SleepMs(uint32_t ms)
{
	HAL_Delay(ms);
}


void HAL_Srandom(uint32_t seed)
{
	srand(seed);
}


/**
 *
 * 函数 HAL_TCP_Destroy() 需要SDK的使用者针对SDK将运行的硬件平台填充实现, 供SDK调用
 * ---
 * Interface of HAL_TCP_Destroy() requires to be implemented by user of SDK, according to target device platform
 *
 * 如果需要参考如何实现函数 HAL_TCP_Destroy(), 可以查阅SDK移植到 Ubuntu Linux 上时的示例代码
 * ---
 * If you need guidance about how to implement HAL_TCP_Destroy, you can check its reference implementation for Ubuntu platform
 *
 * https://code.aliyun.com/linkkit/c-sdk/blob/v3.0.1/wrappers/os/ubuntu/HAL_TCP_linux.c
 *
 *
 * 注意! HAL_XXX() 系列的函数虽然有阿里提供的对应参考实现, 但不建议您不做任何修改/检视的应用于您的商用设备!
 * 
 * 注意! 参考示例实现仅用于解释各个 HAL_XXX() 系列函数的语义!
 * 
 */
/**
 * @brief Destroy the specific TCP connection.
 *
 * @param [in] fd: @n Specify the TCP connection by handle.
 *
 * @return The result of destroy TCP connection.
 * @retval < 0 : Fail.
 * @retval   0 : Success.
 */
int HAL_TCP_Destroy(uintptr_t fd)
{
	return (int)1;
}


/**
 *
 * 函数 HAL_TCP_Establish() 需要SDK的使用者针对SDK将运行的硬件平台填充实现, 供SDK调用
 * ---
 * Interface of HAL_TCP_Establish() requires to be implemented by user of SDK, according to target device platform
 *
 * 如果需要参考如何实现函数 HAL_TCP_Establish(), 可以查阅SDK移植到 Ubuntu Linux 上时的示例代码
 * ---
 * If you need guidance about how to implement HAL_TCP_Establish, you can check its reference implementation for Ubuntu platform
 *
 * https://code.aliyun.com/linkkit/c-sdk/blob/v3.0.1/wrappers/os/ubuntu/HAL_TCP_linux.c
 *
 *
 * 注意! HAL_XXX() 系列的函数虽然有阿里提供的对应参考实现, 但不建议您不做任何修改/检视的应用于您的商用设备!
 * 
 * 注意! 参考示例实现仅用于解释各个 HAL_XXX() 系列函数的语义!
 * 
 */
/**
 * @brief Establish a TCP connection.
 *
 * @param [in] host: @n Specify the hostname(IP) of the TCP server
 * @param [in] port: @n Specify the TCP port of TCP server
 *
 * @return The handle of TCP connection.
   @retval (uintptr_t)(-1): Fail.
   @retval All other values(0 included): Success, the value is handle of this TCP connection.
 */
uintptr_t HAL_TCP_Establish(const char *host, uint16_t port)
{
	static uintptr_t fd = 1;
    //连接服务器
    Esp8266_Connect_Server_host_port(host,port);

	return fd++;
}

/**
 * @brief Read data from the specific TCP connection with timeout parameter.
 *        The API will return immediately if 'len' be received from the specific TCP connection.
 *
 * @param [in] fd @n A descriptor identifying a TCP connection.
 * @param [out] buf @n A pointer to a buffer to receive incoming data.
 * @param [out] len @n The length, in bytes, of the data pointed to by the 'buf' parameter.
 * @param [in] timeout_ms @n Specify the timeout value in millisecond. In other words, the API block 'timeout_ms' millisecond maximumly.
 *
 * @retval       -2 : TCP connection error occur.
 * @retval       -1 : TCP connection be closed by remote server.
 * @retval        0 : No any data be received in 'timeout_ms' timeout period.
 * @retval (0, len] : The total number of bytes be received in 'timeout_ms' timeout period.
                       超时期间接收的字节总数
 * @see None.
 */

int32_t HAL_TCP_Read(uintptr_t fd, char *buf, uint32_t len, uint32_t timeout_ms)
{
    printf("HAL_TCP_Read len=%d\n",len);

    return TCP_Read_From_ALIYUN_Data(buf,len,timeout_ms);
}


/**
 * @brief Write data into the specific TCP connection.
 *        The API will return immediately if 'len' be written into the specific TCP connection.
 *
 * @param [in] fd @n A descriptor identifying a connection.
 * @param [in] buf @n A pointer to a buffer containing the data to be transmitted.
 * @param [in] len @n The length, in bytes, of the data pointed to by the 'buf' parameter.
 * @param [in] timeout_ms @n Specify the timeout value in millisecond. In other words, the API block 'timeout_ms' millisecond maximumly.
 *
 * @retval      < 0 : TCP connection error occur..
 * @retval        0 : No any data be write into the TCP connection in 'timeout_ms' timeout period.
 * @retval (0, len] : The total number of bytes be written in 'timeout_ms' timeout period.

 * @see None.
 */
int32_t HAL_TCP_Write(uintptr_t fd, const char *buf, uint32_t len, uint32_t timeout_ms)
{
    printf("HAL_TCP_Write\n");
    
    Esp8266_SendData(buf,len,timeout_ms);
    
	return len;
}


/**
 * @brief Retrieves the number of milliseconds that have elapsed since the system was boot.
 *
 * @return the number of milliseconds.
 * @see None.
 * @note None.
 */
//返回一个uint64_t类型的数值, 表达设备启动后到当前时间点过去的毫秒数
uint64_t HAL_UptimeMs(void)
{
	return (uint64_t)1;
}


int HAL_Vsnprintf(char *str, const int len, const char *format, va_list ap)
{
    return vsnprintf(str, len, format, ap);
}


 

五、调试

程序能打印出log,但运行总会出错,调试吧......我这边有4个bug要解决:

最后一个bug还没完全调好... 有点遗憾 

bug1 : mallo申请空间时,heap堆太小,导致malloc返回0,mqtt内部的接口出错,程序直接return
		解决方法:去start_up_stm32f103xe.s 中修改heap大小
	
	bug2:发送数据从透传模式 改为 非透传。透传模式,非常不建议使用!因为wifi转到透传模式后,后面就非常难弄,设备升级、还需要退出...
		 AT+CIPSEND=<length> 该指令说明要发送数据长度 后面紧接着给8266的串口发送数据
		 
	bug3:发送数据中有\0 ,导致我服务器一直接受不到数据。原因:我串口发送数据:遇到\0结尾 ,改写串口发送程序	
	
	bug4:HAL_TCP_Read 第一次connect后需要接受1字节?? 但我的buff中有241字节 
		数据需要解析:
		发送AT指令的数据后,esp8266将发送结果返回 同时也将服务器的响应结果返回,二者混合在了一起
			解决方法:1.再写一个缓存区buff 将服务器的结果单独存放
					  2.改成透传模式 这样esp8266就只会将服务器结果返回(不会对AT指令的结果返回)

六、写在后面 总结

总结的话:8266的驱动程序写的很完善的话,应该就不会有那么多bug...

这个移植做了好久好久... 做到后面放弃了都要... 还有点bug也不想调试了...

感觉没多难,原理什么的感觉都明白,mqtt就是上层应用层的代码,一个通讯协议而已,规定数据格式,发送、接收、解析数据就好了.....

但是整个下来后还是各种问题.... 

参考链接的博主说的对:

 

最后我的项目代码:

链接:https://pan.baidu.com/s/1XYuOobpp1bRxT5_j7sNArQ 
提取码:es5j 
复制这段内容后打开百度网盘手机App,操作更方便哦

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页