NodeMCU-V1.0

  1. 核心板型号:ESP-12-E,4Mbytes(32Mbits)的flash
  2. 开源官网
  3. 官网烧录工具使用:
    1. SPI模式选择:DIO
    2. FLASH SIZE选择:32Mbit
    3. 将最新的固件烧写到0x00000处
    4. 在烧写之前,需要按住FLASH按键不动然后按下RST按键一次
  4. nodemcu专用烧录工具:只需连接USB串口,选择好烧录文件,单击Flash即可完成烧写
  5. Pin map
    Node MCU Pin Map
    Node MCU Pin Map
    • 其中D0(GPIO16)只能被用作gpio read/write,不支持中断,不支持pwm、i2c、onewire
  6. Lua core based on eLua project
  7. cjson based on lua-cjson
  8. File system based on spiffs
  9. 事件驱动的编程模型
  10. 内建的模块:node,json,file,timer,pwm,i2c,spi,onewire,net,mqtt,coap,gpio,wifi,adc,uart,bit,u8g,ucg,ws2801,ws2812,crypto,dht,rtc,sntp,bmp085,tls2561,hx711,system
  11. Node MCU API

ESP8266介绍

ESP8266结构图
ESP8266结构图
  1. 内核架构:XtensaLX106(32位),由Tensilica公司开发,是个可配置软核CPU,指令集可扩展,扩展的指令集通过硬件实现。通常扩展指令集有几种方式:
    1. fusion,就是将多条指令合并为一条指令,从而缩短程序所需要的cycle数
    2. SIMD,就是单指令多数据
  2. 默认串口波特率9600。当工作在AP模式时,默认的ip地址为192.168.4.1
  3. CPU主频支持80MHz和160MHz,支持RTOS。内部三条总线:iBus访问内部,外部存储器;dBus访问数据RAM;AHB访问内部寄存器
  4. 支持的无线网络类型:STA/AP/STA+AP
  5. 安全机制:WEP/WPA-PSK/WPA2-PSK
  6. 加密类型:WEP64/WEP128/TKIP/AES
  7. 固件升级:本地串口,OTA云端升级
  8. 网络协议:IPv4,TCP/UDP/FTP/HTTP
  9. 支持的硬件接口:UART,I2C,PWM,GPIO,ADC(10位,ADC的范围在0~1V)
  10. 串口数据传输最大传输速率为460800bps
  11. 系统上电会运行厂商芯片内部的Boot loader,而在bootloader中串口波特率被设置为了74880
  12. ESP8266的PWM频率100~500Hz
  13. GPIO输出电压为VDD_IO(比如3.3V),输出电流应该不超过20mA
  14. ESP8266有两个uart,其中uart0有Tx、Rx,可做数据传输,uart1仅有Tx,可做串口调试信息打印
  15. ESP8266有两套MAC,因此可以支持softAP+station共存的模式
  16. ESP8266 softAP可连接4个station
  17. ESP8266低功耗只针对station模式,对于softAP则没有低功耗模式
  18. ESP8266的TCP连接最多可以建立5个,UDP连接最多可以建立5个,可同时建立5个TCP连接和5个UDP连接
  19. ESP8266片上没有ROM,用户程序存放在外部SPI Flash中。最大支持16MByte的容量。支持的SPI模式:Standard SPI,Dual SPI,DIO SPI,QIO SPI以及Quad SPI。注意,在下载固件时需要在下载工具选择对应模式,否则下载后程序将无法得到正确的运行
  20. ESP8266芯片定义了1个SDIO Slave接口,SDIO由硬件实现,支持4位25MHz SDIOv1.1和4位50MHz SDIO v2.0

最小系统

ESP8266最下系统
ESP8266最下系统
  1. Pin11和Pin17两个数字电源管脚,数字电源无需在电路中增加滤波电容。数字电源工作电压范围1.8v~3.3V
  2. 在模拟电源部分,要注意当ESP8266工作在TX时候,瞬间电流会加大,往往会引起电源的轨道塌陷,所以在设计时在模拟电源电路上增加一个0603或者0805封装的10uF电容,此电容可与0402封装的0.1uF电容搭配
  3. 在PIN21 SD_CLK管脚上串联一个0402封装的电阻连接到Flash CLK管脚上。此电阻的作用主要为:降低驱动电流,减小串扰和外部干扰,调节时序等。串联电阻大小为200ohm
  4. RES12K(Pin31)需外接12K对地电阻,该电阻作为芯片bias控制电流的电阻,对精度要求比较高,建议采用12K±1%精度的电阻
  5. ESP8266模组
    1. Layout:
      1. 第一层Top层主要用于走信号线和摆件
      2. 第二层为GND层,不走信号线,保证一个完整的GND平面
      3. 第三层为POWER层,尽量走电源线
      4. 第四层为Bottom层,建议Bottom层不摆件,只走信号线
    2. 3.3V电源线线宽必须>15mil,走线尽量走第三层(POWER层),到达芯片管脚处时打过孔到达TOP层连接芯片管脚。在过孔处理上,VIA的直径需要大于电源走线的宽度,而且drill应始终,略大于VIA的半径即可
    3. 晶振位置尽量靠近芯片的XTAL Pins,走线不要太长,同时晶振走线必须用地包起来良好屏蔽;晶振的输入输出走线不能打孔走线,即不能跨层。金正的输入输出走线不能交叉,跨层较差也不行。晶振的输入输出的bypass电容要靠近芯片左右侧摆放,尽量不要放在走线上;晶振下方4层都不能走高频数字信号,最佳情况是晶振下方不走任何信号线,晶振TOP面的铺铜区域越大越好。晶振为敏感器件,晶振周围不能有磁感应器件,比如大电感。
    4. RF走线必须控制特性阻抗为50Ω,保证第二层完整地平面,周围地孔屏蔽,走线长度尽量短。RF走线尽量保持在10mil以上;RF走线需预留一个π型匹配网络,且π性匹配电路靠近芯片RF Pin脚摆放。芯片到天线的RF走线不能有过孔,即不能跨层走线。RF走线不能走直角或者45°角,如果有需要则使用圆弧走线。RF走线附近不能有高频信号线。RF上的天线必须远离所有传输高频信号的器件,比如晶振,DDR,一些高频时钟

编译

编译参数
编译参数
  1. esp_iot_sdk_v0.9.5及之后版本的软件简化了编译脚本,编译指令:./gen_misc.sh,根据提示按要求输入编译参数
    1. boot_v1.1与boot_v1.2+:boot_v1.2相对编译时将程序排列的更紧凑,省flash空间;boot_v1.3主要支持增强启动模式可用于产测
    2. 不支持云端升级:flash.bin+iromtext.bin,支持云端升级:boot.bin+user1.bin
    3. 注意编译不同大小的bin时,其烧录地址不同
  2. bin目录存放需要下载到Flash的bin文件
    1. at文件夹:Espressif提供的支持AT指令的bin文件
    2. upgrade文件夹,编译生成的支持云端升级的bin文件(user1.bin或user2.bin)
    3. bin文件下根目录,编译生成的不支持云端升级的bin文件,和其他Espressif提供的bin文件
  3. 编译生成user1.bin后,先运行make clean清除上次编译生成的临时文件后,再编译生成user2.bin
  4. 每个bin编译成功后,会提示该bin的烧录位置,典型烧写位置:
    烧写
    烧写
  5. 编译esp_iot_sdk_v0.9.4及之前版本软件
    1. 指令:./gen_misc.sh
    2. 支持云端升级(FOTA)的编译步骤如下:
      1. 运行./gen_misc_plus.sh 1,在esp_iot_sdk/bin/upgrade路径下生成user1.bin
      2. 运行make clean,清除之前的编译信息
      3. 运行./gen_misc_plus.sh 2,在esp_iot_sdk/bin/upgrade路径下生成user2.bin
  6. 针对编译时STEP1和STEP5的选择不同,对应的flash size和flash map不同。
    1. 系统参数区(system param)始终为flash的最后16KB
    2. 用户参数区(User param)指Espressif提供的示例软件(IOT_Demo或AT)中设定的用户参数区。如果用户自行实现应用程序,则可以将用户参数存放在flash任意空闲区域
    3. 用户数据区(UserData),可能空闲,当程序区域未占满flash空间时,剩余空间可供用户存储数据
  7. none boot-不支持云端升级。编译时Step1选择2,编译生成eagle.flash.bin(简称flash.bin)和eagle.irom0text.bin(简称irom0text.bin),不支持云端升级,则STEP5时选择不同flash size对应的布局如下:
    1. 512KB flash
      none-boot-512KB
      none-boot-512KB
      none-boot-512KB-ld文件
      none-boot-512KB-ld文件
    2. 1024KB flash
      none-boot-1024KB
      none-boot-1024KB
  • \esp_iot_sdk\ld 路径的“eagle.app.v6.ld”文件,其中irom0_0_seg的len即设置irom0text.bin的上限值。对于1024KB flash,此len最大可修改为0xBC000,irom0text.bin 最大支持到752KB

    1. 2048KB flash
      none-boot-2048KB
      none-boot-2048KB
  • \esp_iot_sdk\ld 路径的“eagle.app.v6.ld”文件,其中irom0_0_seg的len即设置irom0text.bin的上限值。对于2048KB flash,此len最大可修改为0xC0000,irom0text.bin 最大支持到768KB(因为ESP8266目前程序区最大支持1024KB,1024-256=768)

    1. 4096KB flash
      none-boot-4096KB
      none-boot-4096KB
  • \esp_iot_sdk\ld 路径的“eagle.app.v6.ld”文件,其中irom0_0_seg的len即设置irom0text.bin的上限值。对于2048KB flash,此len最大可修改为0xC0000,irom0text.bin 最大支持到768KB(因为ESP8266目前程序区最大支持1024KB,1024-256=768)

  1. with boot-支持云端升级。编译时STEP1选择1,便一两次,分别生成user1.bin和user2.bin,支持云端升级功能。STEP5时选择不同的flash size对应的布局:
    with-boot-512KB
    with-boot-512KB
    with-boot-1024KB
    with-boot-1024KB
    with-boot-2048KB
    with-boot-2048KB
    with-boot-4096KB
    with-boot-4096KB

烧录

  1. 系统参数区固定为flash的最后四个扇区,每个扇区4KBytes,即flash最后16KB
  2. master_device_key.bin是ESP8266设备享受Espressif云端服务的身份证明,如果不使用Espressif Cloud可以不少路,否则仅烧录一次。烧录地址在IOT_Demo中设置为用户参数区的第三个扇区
  3. blank.bin初始化系统参数,烧录地址为flash的倒数第二个扇区
  4. esp_init_data_default.bin初始化射频相关参数,烧录地址为flash的倒数第四个扇区
  5. 不支持云端升级
    不支持云端程序的烧录地址
    不支持云端程序的烧录地址
  6. 支持云端升级(FOTA)。支持云端升级的软件无需烧录user2.bin,可以通过网络升级下载user2.bin到Flash并重启运行。
    支持云端程序的烧录地址
    支持云端程序的烧录地址
  7. 从esp_iot_sdk_v1.4.0版本起,开发者可以通过设置esp_init_data_default.bin(0~128byte)的114byte控制上电时的RF初始化的行为。
    改变RF初始化行为
    改变RF初始化行为

SDK二次开发

  1. 如果函数添加了ICACHE_FLASH_ATTR,该函数会被放在irom中,CPU仅在调用到他们的时候,将他们读到cache中运行;没有条件ICACHE_FLASH_ATTR宏的函数,将在一开始上电运行时,就加载到iram中运行。由于空间有限,我们无法将所有代码都一次性加载到iram中运行,因此在大部分函数前添加ICACHE_FLASH_ATTR宏。注意,不能在GPIO或UART中断处理函数中调用带有“ICACHE_FLASH_ATTR”宏的函数,否则将引起异常。
  2. wifi_set_ip_info、wifi_set_macaddr仅在user_init中调用才生效
  3. system_timer_reinit建议在user_init中调用,否则调用后,需要重新arm所有timer
  4. wifi_station_set_config如果在user_init中调用,底层会自动连接对应的路由,不需要再调用wifi_station_connect来进行连接,否则需要。
  5. 使能us级定时器
    1. 在user_config.h中#define USE_US_TIMER,并在user_init中调用system_timer_reinit(),此时可以同时使用os_timer_arm_us和os_timer_arm
    2. 未定义USE_US_TIMER时,os_timer_arm()的时间参数范围0~6871947ms,os_timer_arm_us不可用
    3. 定义了USE_US_TIMER时,os_timer_arm()的时间参数范围0~429496ms,os_timer_arm_us的时间参数范围是0~429496729us
  6. blank.bin,有Espressif提供,烧录到0x7E000地址。不是每次都要烧录,仅当sdk升级版本或需要擦除WIFI配置参数时进行烧录
  7. eagle.app.v6.flash.bin,用户编译生成,烧录到0x0000地址
  8. master_device_key.bin,向Espressif服务器申请,烧录到0x3E000地址
  9. eagle.app.v6.irom0text.bin,用户编译生成,烧录到0x40000地址
  10. esp_init_data_default.bin有Espressif提供,烧录到0x7c000地址
  11. system_restore将wifi相关参数复位,即擦出了路由器信息以及恢复了softAP默认名称
  12. 固件云端升级成功后,需要调用system_upgrade_reboot,否则不切换
  13. system_timer_reinit需要放在程序最开始,usr_init第一句

重要API介绍

  1. bool wifi_station_scan (struct scan_config *config, scan_done_cb_t cb);功能:获取AP的信息。注意:不能在user_init中调用此接口,该接口必须在系统初始化完成后,并且ESP8266 station接口使能的情况下调用
  2. bool wifi_station_set_config (struct station_config *config);功能:设置WiFi station接口的配置参数,并保存到flash。注意:如果wifi_station_set_config在user_init中调用,则ESP8266 station接口在系统初始化完成后,自动连接AP(路由),无需再调用wifi_station_connect;否则,需要调用wifi_station_connect连接AP(路由)。station_config.bssid_set一般设置为0,仅当需要检查AP的MAC地址时(用于有重名AP的情况下)设置为1、本设置如果与原设置不同,会更新保存到flash系统参数区

降低功耗的方法

  1. Modem-Sleep:CPU一直工作,在保持wifi连接,如果没有数据传输则关闭WiFi Modem电路来省电
  2. Light-Sleep:CPU暂停工作,保持Wifi连接
  3. Deep-Sleep:WiFi不需要一直保持连接时采用该模式

ADC应用场景

  1. 测量VDD3P3管脚3和4的电源电压
    1. TOUT必须悬空
    2. RF_init参数:esp_init_data_default.bin(0~127byte)中的低107byte为“vdd33_const”,必须设为0xFF,即255
    3. RF Calibration工作过程:自测VDD3P3管脚3和管脚4上的电源电压,根据测量结果优化RF电路工作状态
    4. 用户软件:可使用system_get_vdd33,不可使用system_adc_read
  2. 测量TOUT管脚6的输入电压
    1. TOUT管脚接外部电路,输入电压范围限定为0-1.0V
    2. RF_init参数:esp_init_data_default.bin(0~127byte)中的第107byte为“vdd33_const”,必须设为真实的VDD3P3管脚3和管脚4上的电源电压,ESP8266的工作电压范围1.8V-3.6V,“vdd33_const”单位0.1V,因此“vdd33_const”有效取值18~36.若电源电压不稳定,会动态变化,“vdd33_const”应输入为电源电压变化的最小值0x10.
    3. RF Calibration工作过程:根据RF_init第107byte“vdd33_const”的值来优化RF电路工作状态,容许误差约为±0.2V
    4. 用户软件:不可使用system_get_vdd33;可使用system_adc_read

电源管理

  1. 关闭(OFF):CHIP_PD管脚处于低功耗状态。RTC失效,所有寄存器被清空
  2. 深度睡眠(DEEP_SLEEP):RTC开着,芯片的其他部分都是关着的。RTC内部recovery memory可保存基本的WiFi连接信息
  3. 睡眠(SLEEP):只有RTC在运行。晶体振荡器停止工作。任何部位唤醒(MAC、主机、RTC计时器、外部中断)将唤醒整个芯片
  4. 环形(WAKEUP):在这种状态下,系统从睡眠状态下转为启动(PWR)状态。晶体振荡器和PLL均转化为使能状态
  5. 开启状态(CPU ON):告诉时钟可以运行,并发送至各个被时钟控制寄存器使能的模块。各个模块,包括CPU在内,执行较低电平的时钟门控。系统运作时,可以通过WAITI指令关闭CPU内部时钟
  6. 工作状态(RF WORK):在开启状态的基础上打开WiFi功能

2.4GHz接收器

2.4GHz接收器把RF信号降频,编程正交基带信号,用2个高分辨率的高速ADC将后者转为数字信号。为了适应不同的信号频道,无线电接收器集成了RF滤波器、自动增益控制AGC、DC偏移补偿电路和基带滤波器

2.4GHz发射器

2.4GHz发射器将正交基带信号升频到2.4GHz,使用大功率CMOS功率放大器驱动天线。数字校准的使用进一步改善了功率放大器的线性,从而在802.11b传输中达到+17dBm的平均功率,在802.11n中达到了13dBm的平均功率。为了抵消无线电接收器的瑕疵,还另增了校准措施:

1. 载波泄露
2. I/Q相位匹配
3. 基带非线性

SDK_IOT_Demo使用方法

  1. ESP8266物联网平台的所有网络功能均在库中实现,对用户不透明。用户应用的初始化功能可以在user_main.c中实现。
  2. void user_init(void)是上层程序的入口函数,给用户提供一个初始化接口,用户可在该函数内增加硬件初始化、网络参数配置、定时器初始化等功能。
  3. SDK中提供了对json包的处理API,用户也可以采用自定义数据包格式,自行对数据进行处理
  4. user_config.h,该头文件中可以选择具体的应用示例,仅支持每次打开一个宏定义,使能一个设备,具体支持:
    1. PLUG_DEVICE(只能插座)
    2. LIGHT_DEVICE(灯)
    3. SENSOR_DEVICE(传感器)
      1. HUMITURE_SUB_DEVICE(温湿度传感器)
      2. FLAMMABLE_GAS_SUB_DEVICE(可燃气体检测)
  5. 需要注意,以下头文件中的宏定义只是用户参数区,用户需要根据编译时的flash map自行调整
    1. user_esp_platform.h中的#define ESP_PARAM_START_SEC 0x3D //or 0x7D, or 0xFD
    2. user_light.h中的#define PRIV_PARAM_START_SEC 0x3C //or ox7C, or 0xFC
    3. user_plug.h中的#define PRIV_PARAM_START_SEC 0x3C // or 0x7C, or 0xFC

SDK编程指南

  1. SDK_v1.1.0及之后版本,请在user_main.c增加void user_rf_pre_init(void),可参考IOT_Demo的user_main.c。用户可在user_rf_pre_init中配置RF初始化,相关RF设置接口为system_phy_set_rfoption,或者在deep-sleep前调用system_deep_sleep_set_option。如果设置为RF不打开,则ESP8266 station及soft-AP均无法使用
  2. 非OS SDK中,由于是单线程,任何task都不能长期占用CPU
    1. 如果一个task占用CPU不退出,将导致看门狗的喂狗函数无法执行,系统重启
    2. 如果关闭中断,请勿占用CPU超过10微妙;如果不关闭中断,建议不超过500毫秒
  3. 建议使用定时器实现周期性的查询功能,如需在定时器的执行函数中调用os_delay_us或者while、for等函数进行延时或者循环操作,占用时间请勿超过15毫秒
  4. 非OS SDK在终端处理函数中,请勿使用任何ICACHE_FLASH_ATTR定义的函数
  5. 内存必须4字节对齐进行读写,请勿直接进行指针转换。例如语句:float temp=((float)data);可能引起异常,建议使用os_memcpy
  6. 如需在中断处理函数中打印,请使用os_printf_plus,且不能加入太多打印信息,尤其是频繁的中断,中断占用时间过长可能引起底层异常

应用程序接口(APIs)

  1. 软件定时器(/esp_iot_sdk/include/osapi.h)

    1. 该定时器由软件实现,定时器的函数在任务中被执行,因为任务可能被中断,或者被其他高优先级的任务延迟,因此以下os_timer系列的接口并不能保证定时器精确执行
    2. 如果需要精确的定时,请使用硬件中断定时器,硬件定时器的执行函数在中断里被执行
    3. 对于同一个timer,os_timer_arm或os_timer_arm_us不能重复调用,必须先os_timer_disarm
    4. os_timer_setfn必须在timer未使能的情况下调用,在os_timer_arm或os_timer_arm_us之前或者os_timer_disarm之后
      os_timer_arm
      os_timer_arm
      os_timer_disarm
      os_timer_disarm
      os_timer_setfn
      os_timer_setfn
      system_timer_reinit
      system_timer_reinit
      os_timer_arm_us
      os_timer_arm_us
  2. 硬件中断定时器(esp_iot_sdk/example/driver_lib/hw_timer.c)

    1. 如果使用NMI中断源,且为自动填装的定时器,调用hw_timer_arm时参数val必须大于100
    2. 如果使用NMI中断源,那么该定时器将为最高优先级,可打断其他ISR
    3. 如果使用FRC1中断源,那么该定时器无法打断其他ISR
    4. hw_timer.c的接口不能跟PWM驱动函数同时使用,因为两者共用了同一个硬件定时器
      hw_timer_init
      hw_timer_init
      hw_timer_arm
      hw_timer_arm
      hw_timer_set_func
      hw_timer_set_func
  3. 系统接口

    system_get_sdk_version
    system_get_sdk_version
    system_restore
    system_restore
    system_restart
    system_restart
    system_init_done_cb
    system_init_done_cb
    system_get_chip_id
    system_get_chip_id
    system_get_vdd33
    system_get_vdd33
    system_adc_read
    system_adc_read
    system_deep_sleep
    system_deep_sleep
    system_deep_sleep_set_option
    system_deep_sleep_set_option
    system_phy_set_rfoption
    system_phy_set_rfoption
    system_phy_set_powerup_option
    system_phy_set_powerup_option
    system_phy_set_max_tpw
    system_phy_set_max_tpw
    system_phy_set_tpw_via_vdd33
    system_phy_set_tpw_via_vdd33
    system_set_os_print
    system_set_os_print
    system_print_meminfo
    system_print_meminfo
    system_get_free_heap_size
    system_get_free_heap_size
    system_os_task
    system_os_task
    system_os_post
    system_os_post
    system_get_time
    system_get_time
    system_get_rtc_time
    system_get_rtc_time
    system_rtc_clock_cali_proc
    system_rtc_clock_cali_proc
    system_rtc_mem_read
    system_rtc_mem_read
    system_uart_swap
    system_uart_swap
    system_uart_de_swap
    system_uart_de_swap
    system_get_boot_version
    system_get_boot_version
    system_get_userbin_addr
    system_get_userbin_addr
    system_get_boot_mode
    system_get_boot_mode
    system_restart_enhance
    system_restart_enhance
    system_update_cpu_freq
    system_update_cpu_freq
    system_get_cpu_freq
    system_get_cpu_freq
    system_get_flash_size_map
    system_get_flash_size_map
    system_get_rst_info
    system_get_rst_info
    system_soft_wdt_stop
    system_soft_wdt_stop
    system_soft_wdt_restart
    system_soft_wdt_restart
    system_soft_wdt_feed
    system_soft_wdt_feed
    system_show_malloc
    system_show_malloc
    os_memcpy
    os_memcpy
    os_strlen
    os_strlen
    os_printf
    os_printf
    os_bzero
    os_bzero
    os_delay_us
    os_delay_us
    os_install_putc1
    os_install_putc1
  4. SPI Flash接口

    spi_flash_get_id
    spi_flash_get_id
    spi_flash_erase_sector
    spi_flash_erase_sector
    spi_flash_write
    spi_flash_write
    spi_flash_read
    spi_flash_read
    system_param_save_with_protect
    system_param_save_with_protect
    system_param_load
    system_param_load
    spi_flash_set_read_func
    spi_flash_set_read_func
  5. Wi-Fi接口

    1. wifi_station系列接口以及ESP8266 station相关的设置、查询接口,请在ESP8266 station使能的情况下调用
    2. wifi_softap系列接口以及ESP8266 soft-AP相关的设置、查询接口,请在ESP8266 soft-AP使能的情况下调用
    3. 后文的“flash系统参数区”位于flash的最后16KB
      wifi_get_opmode
      wifi_get_opmode
      wifi_get_opmode_default
      wifi_get_opmode_default
      wifi_set_opmode
      wifi_set_opmode
      wifi_set_opmode_current
      wifi_set_opmode_current
      wifi_station_get_config
      wifi_station_get_config
      wifi_station_get_config_default
      wifi_station_get_config_default
      wifi_station_set_config
      wifi_station_set_config
      wifi_station_set_config_current
      wifi_station_set_config_current
      wifi_station_set_cert_key
      wifi_station_set_cert_key
      wifi_station_clear_cert_key
      wifi_station_clear_cert_key
      wifi_station_connect
      wifi_station_connect
      wifi_station_disconnect
      wifi_station_disconnect
      wifi_station_get_connect_status
      wifi_station_get_connect_status
      wifi_station_scan
      wifi_station_scan
      scan_done_cb_t
      scan_done_cb_t
      wifi_station_ap_number_set
      wifi_station_ap_number_set
      wifi_station_get_ap_info
      wifi_station_get_ap_info
      wifi_station_ap_change
      wifi_station_ap_change
      wifi_station_get_current_ap_id
      wifi_station_get_current_ap_id
      wifi_station_get_auto_connect
      wifi_station_get_auto_connect
      wifi_station_set_auto_connect
      wifi_station_set_auto_connect
      wifi_station_dhcpc_start
      wifi_station_dhcpc_start
      wifi_station_dhcpc_stop
      wifi_station_dhcpc_stop
      wifi_station_dhcpc_status
      wifi_station_dhcpc_status
      wifi_station_dhcpc_set_maxtry
      wifi_station_dhcpc_set_maxtry
      wifi_station_set_reconnect_policy
      wifi_station_set_reconnect_policy
      wifi_station_get_rssi
      wifi_station_get_rssi
      wifi_station_set_hostname
      wifi_station_set_hostname
      wifi_station_get_hostname
      wifi_station_get_hostname
      wifi_softap_get_config
      wifi_softap_get_config
      wifi_softap_get_config_default
      wifi_softap_get_config_default
      wifi_softap_set_config
      wifi_softap_set_config
      wifi_softap_set_config_current
      wifi_softap_set_config_current
      wifi_softap_get_station_num
      wifi_softap_get_station_num
      wifi_softap_get_station_info
      wifi_softap_get_station_info
      wifi_softap_free_station_info
      wifi_softap_free_station_info
      wifi_softap_dhcps_start
      wifi_softap_dhcps_start
      wifi_softap_dhcps_stop
      wifi_softap_dhcps_stop
      wifi_softap_set_dhcps_lease
      wifi_softap_set_dhcps_lease
      wifi_softap_get_dhcps_lease
      wifi_softap_get_dhcps_lease
      wifi_softap_set_dhcps_lease_time
      wifi_softap_set_dhcps_lease_time
      wifi_soft_dhcps_status
      wifi_soft_dhcps_status
      wifi_softap_set_dhcps_offer_option
      wifi_softap_set_dhcps_offer_option
      wifi_set_phy_mode
      wifi_set_phy_mode
      wifi_get_phy_mode
      wifi_get_phy_mode
      wifi_get_ip_info
      wifi_get_ip_info
      wifi_set_ip_info
      wifi_set_ip_info
      wifi_set_macaddr
      wifi_set_macaddr
      wifi_get_macaddr
      wifi_get_macaddr
      wifi_set_sleep_type
      wifi_set_sleep_type
      wifi_get_sleep_type
      wifi_get_sleep_type
      wifi_status_led_install
      wifi_status_led_install
      wifi_status_led_uninstall
      wifi_status_led_uninstall
      wifi_set_broadcase_if
      wifi_set_broadcase_if
      wifi_get_broadcast_if
      wifi_get_broadcast_if
      wifi_set_event_handler_cb
      wifi_set_event_handler_cb
      wifi_wps_enable
      wifi_wps_enable
      wifi_wps_disable
      wifi_wps_disable
      wifi_wps_start
      wifi_wps_start
      wifi_set_wps_cb
      wifi_set_wps_cb
      wifi_register_send_pkt_freedom_cb
      wifi_register_send_pkt_freedom_cb
      wifi_send_pkt_freedom
      wifi_send_pkt_freedom
      wifi_rfid_locp_recv_open
      wifi_rfid_locp_recv_open
      wifi_rfid_locp_recv_close
      wifi_rfid_locp_recv_close
      wifi_register_rfid_locp_recv_cb
      wifi_register_rfid_locp_recv_cb
      wifi_unregister_rfid_locp_recv_cb
      wifi_unregister_rfid_locp_recv_cb
  6. Rate Control 接口

    wifi_set_user_fixed_rate
    wifi_set_user_fixed_rate
    wifi_get_user_fixed_rate
    wifi_get_user_fixed_rate
    wifi_set_user_sup_rate
    wifi_set_user_sup_rate
    wifi_set_user_rate_limit
    wifi_set_user_rate_limit
    wifi_set_user_limit_rate_mask
    wifi_set_user_limit_rate_mask
    wifi_get_user_limit_rate_mask
    wifi_get_user_limit_rate_mask
  7. 强制休眠接口
    使用强制休眠功能,必须先设置WiFi工作模式位NULL_MODE,从强制休眠中唤醒ESP8266,或者休眠时间到,进入唤醒回调(wifi_fpm_set_wakeup_cb注册)后,先关闭强制休眠功能,才能再设置WiFi工作模式为station、soft-AP或sta+AP的正常工作模式。

    wifi_fpm_open
    wifi_fpm_open
    wifi_fpm_close
    wifi_fpm_close
    wifi_fpm_do_wakeup
    wifi_fpm_do_wakeup
    wifi_fpm_set_wakeup_cb
    wifi_fpm_set_wakeup_cb
    wifi_fpm_do_sleep
    wifi_fpm_do_sleep
    wifi_fpm_set_sleep_type
    wifi_fpm_set_sleep_type
    wifi_fpm_get_sleep_type
    wifi_fpm_get_sleep_type

    示例

    示例代码
    示例代码
  8. ESP-NOW
    ESP-NOW软件接口使用时注意:

    1. ESP-NOW目前不支持广播包和组播包
    2. ESP-NOW现阶段主要为智能灯项目,建议slave角色对应ESP8266 soft-AP模式或者soft-AP+station共存模式;controller角色对应station模式
    3. 当ESP8266处于soft-AP+station共存模式时,若作为slave角色,将从soft-AP接口通信;若作为controller角色,将从station接口通信
    4. ESP-NOW不实现休眠环形功能,因此如果通信对方的ESP8266 station正处于休眠状态,ESP-NOW发包将会失败
    5. ESP8266 station模式下,最多可设置10个加密的ESP-NOW peer,加上不加密的设备,综述不超过20个
    6. ESP8266 soft-AP模式或者soft-AP+station模式下,最多设置6个加密的ESP-NOW peer,加上不加密的设备,总数不超过20个
      esp_now_init
      esp_now_init
      esp_now_deinit
      esp_now_deinit
      esp_now_register_recv_cb
      esp_now_register_recv_cb
      esp_now_unregister_recv_cb
      esp_now_unregister_recv_cb
      esp_now_register_send_cb
      esp_now_register_send_cb
      esp_now_unregister_send_cb
      esp_now_unregister_send_cb
      esp_now_send
      esp_now_send
      esp_now_add_peer
      esp_now_add_peer
      esp_now_del_peer
      esp_now_del_peer
      esp_now_set_self_role
      esp_now_set_self_role
      esp_now_get_self_role
      esp_now_get_self_role
      esp_now_set_peer_role
      esp_now_set_peer_role
      esp_now_get_peer_role
      esp_now_get_peer_role
      esp_now_set_peer_key
      esp_now_set_peer_key
      esp_now_get_peer_key
      esp_now_get_peer_key
      esp_now_set_peer_channel
      esp_now_set_peer_channel
      esp_now_get_peer_channel
      esp_now_get_peer_channel
      esp_now_is_peer_exist
      esp_now_is_peer_exist
      esp_now_fetch_peer
      esp_now_fetch_peer
      esp_now_get_cnt_info
      esp_now_get_cnt_info
      esp_now_set_tok
      esp_now_set_tok
  9. 云端升级(FOTA)

    system_upgrade_userbin_check
    system_upgrade_userbin_check
    system_upgrade_flag_set
    system_upgrade_flag_set
    system_upgrade_flag_check
    system_upgrade_flag_check
    system_upgrade_start
    system_upgrade_start
    system_upgrade_reboot
    system_upgrade_reboot
  10. Sniffer接口

    wifi_promiscuous_enable
    wifi_promiscuous_enable
    wifi_promiscuous_set_mac
    wifi_promiscuous_set_mac
    wifi_set_promiscuous_rx_cb
    wifi_set_promiscuous_rx_cb
    wifi_get_channel
    wifi_get_channel
    wifi_set_channel
    wifi_set_channel
  11. smart config
    开启smart config功能前,先要确保AP已经开启

    smartconfig_start
    smartconfig_start
    smartconfig_stop
    smartconfig_stop
    smartconfig_set_type
    smartconfig_set_type
  12. SNTP

    sntp_setserver
    sntp_setserver
    sntp_getserver
    sntp_getserver
    sntp_setservername
    sntp_setservername
    sntp_getservername
    sntp_getservername
    sntp_init
    sntp_init
    sntp_stop
    sntp_stop
    sntp_get_current_timestamp
    sntp_get_current_timestamp
    sntp_get_real_time
    sntp_get_real_time
    sntp_set_timezone
    sntp_set_timezone
    sntp_get_timezone
    sntp_get_timezone
    sntp实例
    sntp实例
  13. TCP/UDP接口

    1. 通用接口
      espconn_delete
      espconn_delete
      espconn_gethostbyname
      espconn_gethostbyname
      espconn_port
      espconn_port
      espconn_regist_sentcb
      espconn_regist_sentcb
      espconn_regist_recvcb
      espconn_regist_recvcb
      espconn_sent_callback
      espconn_sent_callback
      espconn_recv_callback
      espconn_recv_callback
      espconn_send
      espconn_send
    2. TCP APIs
      espconn_accept
      espconn_accept
      espconn_regist_time
      espconn_regist_time
      espconn_get_connection_info
      espconn_get_connection_info
      espconn_connect
      espconn_connect
      espconn_regist_connectcb
      espconn_regist_connectcb
      espconn_connect_callback
      espconn_connect_callback
      espconn_set_opt
      espconn_set_opt
      espconn_clear_opt
      espconn_clear_opt
      espconn_set_keepalive
      espconn_set_keepalive
      espconn_get_keepalive
      espconn_get_keepalive
      espconn_reconnect_callback
      espconn_reconnect_callback
      espconn_regist_reconcb
      espconn_regist_reconcb
      espconn_disconnect
      espconn_disconnect
      espconn_regist_disconcb
      espconn_regist_disconcb
      espconn_abort
      espconn_abort
      espconn_regist_write_finish
      espconn_regist_write_finish
      espconn_tcp_get_max_con
      espconn_tcp_get_max_con
      espconn_tcp_set_max_con
      espconn_tcp_set_max_con
      espconn_tcp_get_max_con_allow
      espconn_tcp_get_max_con_allow
      espconn_tcp_set_max_con_allow
      espconn_tcp_set_max_con_allow
      espconn_recv_hold
      espconn_recv_hold
      espconn_recv_unhold
      espconn_recv_unhold
      espconn_secure_accept
      espconn_secure_accept
      espconn_secure_delete
      espconn_secure_delete
      espconn_secure_set_size
      espconn_secure_set_size
      espconn_secure_get_size
      espconn_secure_get_size
      espconn_secure_connect
      espconn_secure_connect
      espconn_secure_send
      espconn_secure_send
      espconn_secure_disconnect
      espconn_secure_disconnect
      espconn_secure_ca_disable
      espconn_secure_ca_disable
      espconn_secure_ca_enable
      espconn_secure_ca_enable
      espconn_secure_cert_req_enable
      espconn_secure_cert_req_enable
      espconn_secure_cert_req_disable
      espconn_secure_cert_req_disable
      espconn_secure_set_default_certificate
      espconn_secure_set_default_certificate
      espconn_secure_set_default_private_key
      espconn_secure_set_default_private_key
    3. UDP APIs
      espconn_crerat
      espconn_crerat
      espconn_sendto
      espconn_sendto
      espconn_igmp_join
      espconn_igmp_join
      espconn_igmp_leave
      espconn_igmp_leave
      espconn_dns_setserver
      espconn_dns_setserver
    4. mDNS APIs
      espconn_mdns_init
      espconn_mdns_init
      espconn_mdns_close
      espconn_mdns_close
      espconn_mdns_server_register
      espconn_mdns_server_register
      espconn_mdns_server_unregister
      espconn_mdns_server_unregister
      espconn_mdns_get_servername
      espconn_mdns_get_servername
      espconn_mdns_set_servername
      espconn_mdns_set_servername
      espconn_mdns_set_hostname
      espconn_mdns_set_hostname
      espconn_mdns_get_hostname
      espconn_mdns_get_hostname
      espconn_mdns_disable
      espconn_mdns_disable
      espconn_mdns_enable
      espconn_mdns_enable
      nDNS示例
      nDNS示例
  14. MESH接口

    espconn_mesh_enable
    espconn_mesh_enable
    espconn_mesh_disable
    espconn_mesh_disable
    espconn_mesh_get_status
    espconn_mesh_get_status
    espconn_mesh_connect
    espconn_mesh_connect
    espconn_mesh_disconnect
    espconn_mesh_disconnect
    espconn_mesh_sent
    espconn_mesh_sent
    espconn_mesh_set_max_hop
    espconn_mesh_set_max_hop
    espconn_mesh_get_max_hop
    espconn_mesh_get_max_hop
    espconn_mesh_get_node_info
    espconn_mesh_get_node_info
    espconn_mesh_local_addr
    espconn_mesh_local_addr
    espconn_mesh_server_init
    espconn_mesh_server_init
    espconn_mesh_get_router
    espconn_mesh_get_router
    espconn_mesh_set_router
    espconn_mesh_set_router
    espconn_mesh_encrypt_init
    espconn_mesh_encrypt_init
    espconn_mesh_set_ssid_prefix
    espconn_mesh_set_ssid_prefix
    espconn_mesh_group_id_init
    espconn_mesh_group_id_init
    espconn_mesh_set_dev_type
    espconn_mesh_set_dev_type
    espconn_mesh_get_dev_type
    espconn_mesh_get_dev_type
    espconn_mesh_print_ver
    espconn_mesh_print_ver
    espconn_mesh_scan
    espconn_mesh_scan
  15. AT接口

    at_response_ok
    at_response_ok
    at_response_error
    at_response_error
    at_cmd_array_regist
    at_cmd_array_regist
    at_get_next_int_dec
    at_get_next_int_dec
    at_data_str_copy
    at_data_str_copy
    at_init
    at_init
    at_port_print
    at_port_print
    at_set_custom_info
    at_set_custom_info
    at_enter_special_state
    at_enter_special_state
    at_leave_special_state
    at_leave_special_state
    at_get_version
    at_get_version
    at_register_uart_rx_intr
    at_register_uart_rx_intr
    at_response
    at_response
    at_register_response_func
    at_register_response_func
  16. JSON接口

    jsonparse_setup
    jsonparse_setup
    jsonparse_next
    jsonparse_next
    jsonparse_copy_value
    jsonparse_copy_value
    jsonparse_get_value_as_int
    jsonparse_get_value_as_int
    jsonparse_get_value_as_long
    jsonparse_get_value_as_long
    jsonparse_get_value_len
    jsonparse_get_value_len
    jsonparse_get_value_as_type
    jsonparse_get_value_as_type
    jsonparse_strcmp_value
    jsonparse_strcmp_value
    jsontree_set_up
    jsontree_set_up
    jsontree_reset
    jsontree_reset
    jsontree_path_name
    jsontree_path_name
    jsontree_write_int
    jsontree_write_int
    jsontree_write_int_array
    jsontree_write_int_array
    jsontree_write_string
    jsontree_write_string
    jsontree_print_next
    jsontree_print_next
    jsontree_find_next
    jsontree_find_next
  17. GPIO接口

    PIN相关宏定义
    PIN相关宏定义
    gpio_output_set
    gpio_output_set
    GPIO输入输出相关宏
    GPIO输入输出相关宏
    GPIO中断
    GPIO中断
    gpio_pin_intr_state_set
    gpio_pin_intr_state_set
    GPIO中断处理函数
    GPIO中断处理函数
  18. UART接口
    默认情况下,UART0作为系统的打印信息输出接口,当配置为双UART时,UART0作为数据收发接口,UART1作为打印信息输出接口

    uart_init
    uart_init
    uart0_tx_buffer
    uart0_tx_buffer
    uart0_rx_intr_handler
    uart0_rx_intr_handler
  19. I2C Master接口
    ESP8266不能作为I2C从设备,但可以作为I2C主设备,对其他I2C从设备进行控制和读写。每个GPIO管脚内部都可以配置为开漏模式,从而可以灵活地将GPIO口用作I2C data或者clock功能。同时芯片内部提供上拉电阻,以节省外部的上拉电阻

    i2c_master_gpio_init
    i2c_master_gpio_init
    i2c_master_init
    i2c_master_init
    i2c_master_start
    i2c_master_start
    i2c_master_stop
    i2c_master_stop
    i2c_master_send_ack
    i2c_master_send_ack
    i2c_master_send_nack
    i2c_master_send_nack
    i2c_master_checkAck
    i2c_master_checkAck
    i2c_master_readByte
    i2c_master_readByte
    i2c_master_writeByte
    i2c_master_writeByte
  20. PWM接口
    PWM驱动接口不能跟hw_timer的接口同时使用,因为二者共用了同一个硬件定时器

    pwm_init
    pwm_init
    pwm_start
    pwm_start
    pwm_set_duty
    pwm_set_duty
    pwm_get_duty
    pwm_get_duty
    pwm_set_period
    pwm_set_period
    pwm_get_period
    pwm_get_period
    pwm_get_version
    pwm_get_version

##参数结构和宏定义

  1. 定时器
    ETSTimer
    ETSTimer
  2. WiFi参数
    station参数
    station参数
    soft-AP参数
    soft-AP参数
    scan参数
    scan参数
    WiFi event结构体
    WiFi event结构体
    smart config结构体
    smart config结构体
  3. json相关结构体
    json结构体
    json结构体
    json宏定义
    json宏定义
  4. espconn参数
    回调函数
    回调函数
    espconn
    espconn
  5. 中断相关宏定义
    中断宏定义
    中断宏定义

ESPCONN编程

  1. TCP client模式,步骤:
    1. 依据工作协议初始化espconn参数
    2. 注册连接成功的回调函数和连接失败重连的回调函数
    3. 调用espconn_connect建立与TCP Secver的连接
    4. TCP连接建立成功后,在连接成功的回调函数(espconn_connect_callback)中,注册接收数据的回调函数,发送数据成功的回调函数和断开连接的回调函数
    5. 在接收数据的回调函数,或者发送数据成功的回调函数中,执行断开连接操作时,建议适当延时一定时间,确保底层函数执行结束
  2. TCP Server模式,步骤
    1. 依据工作协议初始化espconn参数
    2. 注册连接成功的回调函数和连接失败重连的回调函数
    3. 调用espconn_accept侦听TCP连接
    4. TCP连接建立成功后,在连接成功的回调函数中,注册接收数据的回调函数,发送数据成功的回调函数和断开连接的回调函数
  3. espconn callback
    espconn callback
    espconn callback
    • 注意:回调函数中传入的指针arg,对应网络连接的结构体espconn指针。该指针为SDK内部维护的指针,不同回调传入的指针地址可能不一样,请勿依此判断网络连接。可根据espconn结构体中的remote_ip,remote_port判断多连接中的不同网络传输
    • 如果espconn_connect(或者espconn_secure_connect)失败,返回非零值,连接未建立,不会进入任何espconn callback
    • 请勿在espconn热河回调中调用espconn_disconnect(或者espconn_secure_disconnect)断开连接。如果有需要,可以在espconn回调中使用触发任务的方式(system_os_task和system_os_post)调用espconn_disconnect(或者espconn_secure_disconnect)断开连接

RTC使用实例

以下测试示例,可以验证RTC时间和系统时间,在system_restart时的变化,以及读写RTC memory

RTC示例
RTC示例

##Sniffer结构体说明

  1. ESP8266可以进入混杂模式,接收空气中的IEEE802.11包,可支持如下HT20的包:
    1. 802.11b
    2. 802.11g
    3. 802.11n(MCS0到MCS7)
    4. AMPDU
  2. 尽管有些类型的IEEE802.11包是ESP8266不能完全接收的,但ESP8266可以获得它们的包长。因此,sniffer模式下,ESP8266或者可以接收完整的包,或者可以获得包的长度
  3. ESP8266可完全接收的包,包含:
    1. 一定长度的MAC头信息(包含了收发双发的MAC地址和加密方式)
    2. 整个包的长度
  4. ESP8266不可完全接收的包,它包含:
    1. 整个包的长度
  5. 结构体RxControl和sniffer_buf分别用于via哦是这两种类型的包。其中结构体sniffer_buf包含结构体RxControl。
    sniffer结构体
    sniffer结构体
    回调函数wifi_promiscuous_rx含两个参数(buf和len)。len表示buf的长度,分为三种情况:len=128,len为10的整数倍,len=12;
    1. LEN==128的情况
      • buf的数据是结构体sniffer_buf2,该结构体对应的数据包是管理包,含有112字节的数据
      • sniffer_buf2.cnt为1
      • sniffer_buf2.len为管理包的长度
    2. LEN为10的整数倍的情况
      • buf的数据是结构体sniffer_buf,该结构体是比较可信的,它对应的数据包是通过CRC校验正确的
      • sniffer_buf.cnt表示了该buf包含的包的个数,len的值由sniffer_buf.cnt决定
      • sniffer_buf.cnt==0表示此buf无效;否则len=5=+cnt*10
      • sniffer_buf.buf表示IEEE802.11包的前36字节。从成员sniffer_buf.lenseq[0]开始,每一个lenseq结构体表示一个包长信息
      • 当sniffer_buf.cnt>1,由于该包是一个AMPDU,认为每个MPDU的包头基本是相同的,因此没有给出所有的MPDU包头,只给出了每个包的长度(从MAC包头开始到FCS)
      • 该结构体中较为游泳的信息有:包长、包的发送者和接收者、包头长度
    3. LEN==12的情况
      • buf的数据是一个结构体RxControl,该结构体是不太可信的,它无法表示包所属的发送和接收者,也无法判断该报的包头长度
      • 对于AMPDU包,也无法判断子包的个数和每个子包的长度
      • 该结构体中较为有用的信息有:包长,rssi和FEC_CODING
      • RSSI和FEC_CODING可以用于评估是否是同一个设备所发

ESP8266信道的定义

  1. 虽然ESP8266支持soft-AP和station共存模式,但是ESP8266实际只有一个硬件通道。因此在soft-AP+station模式时,ESP8266 soft-AP会动态调整信道值与ESP8266 station一致
    ESP8266 soft-AP+station模式注意事项
    ESP8266 soft-AP+station模式注意事项

AT 官方指令

  1. 如果开发板Flash为4Mbit,则无法使用固件升级功能(对应指令AT+CIUPDATE),只能采用non-boot的烧录方式。固件升级功能要求Flash容量为8Mbit或以上,采用boot mode的烧录方式
  2. AT底层已经占用system_os_task优先级0和1,因此用户如基于AT开发,仅支持建立一个优先级为2的任务
  3. 波特率为115200
  4. AT指令必须大写,以回车换行符结尾“\r\n”
  5. AT Demo仅在ESP8266作为TCP client单连接或者UDP传输时,支持透传
  6. 目前AT Demo ESP8266仅支持一个TCP服务器,且必须使能多连接,即可连接多个TCP client
    基础AT指令
    基础AT指令
    WiFi功能AT指令
    WiFi功能AT指令
    TCP/IP相关AT指令
    TCP/IP相关AT指令
    保存设置到Flash的AT指令
    保存设置到Flash的AT指令

GPIO

ESP8266-GPIO
ESP8266-GPIO
  1. ESP8266共有16个通用IO,管脚的位置和管脚的名称分别为:
  2. 在四线(QUAD)模式flash下,有6个IO用于flash通讯;在两线(DUAL)模式flash下,有四个IO用于与flash通讯
  3. 与其他IO口不同,GPIO16不属于通用GPIO模块,它属于RTC模块,可以用来在深度睡眠时候唤醒整个芯片,可以配置为输入或者输出模式,但是无法触发IO中断
    1. 将GPIO16配置为输出模式:gpio16_output_conf(void)
    2. 从GPIO16输出高/低电平,需要先配置为输出模式:gpio16_output_set(uint8 value)
    3. 将GPIO16配置为输入模式:gpio16_input_conf(void)
    4. 读取GPIO16的输入电平状态,需要先配置为输入模式:gpio16_input_get(void)

Free-RTOS

  1. 编程注意事项:
    1. 建议使用定时器实现长时间的查询功能,可将定时器设置为循环调用,注意:
      1. 定时器(freeRTOS timer或os_timer)执行函数内部请勿使用while(1)或其他能阻塞线程的方式延时,例如,不能在定时器回调中进行socket send操作,因为send函数会阻塞线程
      2. 定时器回调执行请勿超过15毫秒
      3. os_timer_t建立的变量不能为局部变量,必须为全局变量、静态变量或os_malloc分配的指针
    2. 从esp_iot_rtos_sdk_v1.2.0起,无需添加宏ICACHE_FLASH_ATTR,函数默认存放在CACHE区,中断函数也可以存放在CACHE区;如需将部分频繁调用的函数定义在RAM中,请在函数前面添加宏IRAM_ATTR
    3. 网络编程使用通用的socket编程,网络通信时,socket请勿绑定在同一个端口
    4. RTOS SDK的系统任务最高优先级为14,创建任务的接口xTaskCreate为freeRTOS自带接口,使用下TaskCreate创建任务时,任务堆栈甚至范围为【176,512】
      1. 在任务内部如需使用长度超过60的大数组,建议使用os_malloc和os_free的方式操作,否则,大数组将占用任务的堆空间
      2. SDK底层已占用部分优先级:watchdog task优先级14,pp task优先级13,高精度timer(ms)线程优先级12,TCP/IP task优先级10,freeRTOS timer优先级2,idle task优先级为0,pm task优先级1
      3. 可供用户线程使用的优先级为1~9
      4. 请勿修改FreeRTOSConfig.h,此处修改头文件并不能生效,设置由SDK库文件决定
  2. esp_iot_rtos_sdk默认使用UART0打印调试信息,默认波特率为74880
  3. esp_iot_rtos_sdk支持多线程,可以建立多个任务。创建任务的接口xTaskCreate为freeRTOS自带接口,使用xTaskCreate创建任务时,任务堆栈设置范围为[176,512]
  4. RTC使用
    复位对RTC的影响
    复位对RTC的影响

非OS SDK与RTOS SDK创建任务的方式对比

非OS SDK创建任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#define Q_NUM (10)
ETSEvent test_q[Q_NUM];
void test_task(ETSEvent *e)
{
switch(e->sig)
{
case 1:
func1(e->par);
break;
case 2:
func2();
break;
case 3:
func3();
break;
default:
break;
}
}
void func_send_Sig(void)
{
ETSSignal sig = 2;
system_os_post(2,sig,0);
}
void task_ini(void)
{
system_os_task(test_task, 2, test_q,Q_NUM);
// test_q is the corresponding array of test_task.
// (2) is the priority of test_task.
// Q_NUM is the queue length of test_task.
}
RTOS SDK创建任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#define Q_NUM (10)
xQueueHandle test_q;
xTaskHandle test_task_hdl;
void test_task(void *pvParameters)
{
int *sig;
for(;;){
if(pdTRUE == xQueueReceive(test_q, &sig, (portTickType)portMAX_DELAY) ){
vTaskSuspendAll();
switch(*sig)
{
case 1:
func1();
break;
case 2:
func2();
break;
default:
break;
}
free(sig);
xTaskResumeAll();
}
}
}
void func_send_Sig(void)
{
int *evt = (int *)malloc(sizeif(int));
*evt = 2;
if(xQueueSend(test_q,&evt,10/portTick_RATE_MS)!=pdTRUE){
os_printf("test_q is full\n");
}
// It is the address of parameter that stored in test_q, so int *evt and int
*sig can be other types.
}
void task_ini(void)
{
test_q = xQueueCreate(Q_NUM,sizeof(void *));
xTaskCreate(test_task,(signed portCHAR *)"test_task", 512, NULL, (1),
&test_task_hdl );
// 512 means the heap size of this task, 512 * 4 byte.
// NULL is a pointer of parameter to test_task.
// (1) is the priority of test_task.
// test_task_hdl is the pointer of the task of test_task.
}

强制系统休眠

  1. 强制休眠接口调用后,并不会立即休眠,而是等到系统idle task执行时才进入休眠
  2. Modem-sleep

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #define FPM_SLEEP_MAX_TIME 0xFFFFFFF
    wifi_station_disconnect();
    wifi_set_opmode(NULL_MODE); // set WiFi mode to null mode
    wifi_fpm_set_sleep_type(MODEM_SLEEP_T); // set modem sleep
    wifi_fpm_open(); // enable force sleep
    wifi_fpm_do_sleep(FPM_SLEEP_MAX_TIME);
    ...
    wifi_fpm_do_wakeup(); // wake up to use WiFi again
    wifi_fpm_close(); // disable force sleep
    wifi_set_opmode(STATION_MODE); //set station mode
    wifi_station_connect(); //connect to AP
  3. Light-sleep

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    void fpm_wakup_cb_func1(void)
    {
    wifi_fpm_close(); // disable force sleep function
    wifi_set_opmode(STATION_MODE); // set station mode
    wifi_station_connect(); // connect to AP
    }
    void user_func(...)
    {
    wifi_station_disconnect();
    wifi_set_opmode(NULL_MODE); // set WiFi mode to null mode.
    wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); // light sleep
    wifi_fpm_open(); // enable force sleep
    wifi_fpm_set_wakeup_cb(fpm_wakup_cb_func1); // Set wakeup callback
    wifi_fpm_do_sleep(10*1000);
    ...
    }

spiffs文件系统应用

Windows下网络防火墙对TCP Server的屏蔽

  1. Win+R,输入wf.msc,进入高级安全Windows防火墙
  2. 入站规则右击新建规则
  3. 规则类型对话框中选择“程序”
  4. 指名程序的路径
  5. 允许连接

PWM接口

  1. ESP8266系统的PWM由FRC1在软件上实现,可实现同频率、不同占空比的多路PWM,可用来控制彩灯、蜂鸣器和电机等设备
  2. FRC1是一个23bit的硬件定时器
  3. 使用NMI中断,更加精确
  4. 可扩展最多8路PWM信号
  5. 大于14bit的分辨率,最小分辨率45ns
  6. PWM的时钟源由高速系统时钟提供,其频率高达80MHz。PWM通过预分频器将时钟源16分频,其输入时钟频率为5MHz。PWM通过FRC1来产生粗调定时,结合高速系统时钟的微调,可将分辨率提高到45ns
  7. PWM时钟周期:100Hz~1KHz

UART接口

  1. UART0默认情况下会在上电booting期间输出一些打印,此期间打印内容的波特率与所用的外部晶振频率有关。使用40M晶振时,该段打印波特率为115200。使用26M晶振时,该段打印波特率为74880
  2. UART0和UART1各有一个长度为128Byte的硬件FIFO,读写FIFO都在同一个地址操作
  3. 如何屏蔽上电打印:使用uart的内部引脚交换功能,在初始化的时候,将U0TXD、U0RXD分别与U0RTS、U0CTS交换

Sleep接口

三种sleep模式的区别
三种sleep模式的区别
  1. 对于Modem-sleep和Light-sleep模式,SDK提供接口来使能睡眠模式,并由系统底层决定何时进入睡眠。在deep-sleep模式下,何时进入睡眠由用户控制,调用接口函数就可立即进入deep-sleep模式,
  2. Modem-sleep仅工作在Station模式下,连接路由器后生效,ESP8266通过Wi-Fi的DTIM(Delivery Traffic Indication Message)Beacon机制与路由器保持连接。一般路由器的DTIM Beacon间隔为100ms~1000ms。在Modem-sleep模式下,ESP8266会在两次DTIM Beacon间隔时间内,关闭Wi-Fi模块电路,达到省电效果,在下次Beacon到来前自动唤醒。睡眠时间由路由器的DTIM Beacon时间决定。睡眠同时可以保持与路由器的Wi-Fi连接,并通过路由器接受来自手机或者服务器的交互信息。
  3. 在Modem-sleep模式下,系统可以自动被唤醒,无需配置接口。Modem-sleep一般用于必须打开CPU的应用场景,例如PWM彩灯,需要CPU实时控制
  4. Light-sleep的工作模式与Modem-sleep相似,不同的是,除了关闭Wi-Fi模块电路以为,在Light-sleep模式下,还会关闭时钟并暂停内部的CPU。在Wi-Fi连接后,并且CPU处于空闲状态时,会自动进入Light-sleep状态
  5. 在Light-sleep模式下,CPU在暂停状态下不会响应来自外围硬件接口的信号与终端,因此需要配置通过外部GPIO信号将ESP8266唤醒,唤醒过程小于3ms。通过GPIO唤醒只能配置为电平触发模式。接口如下:void gpio_pin_wakeup_enable(uint32 i, GPIO_INT_TYPE intr_state);,其中i为唤醒功能的IO序号,intr_state为唤醒的触发模式,只能是GPIO_PIN_INTR_LOLEVEL或者GPIO_PIN_INTR_HILEVEL。
  6. Light-sleep模式可用于需要保持与路由器的链接,可以实时响应路由器发来的数据的场合。并且在未收到命令时,CPU可以处于空闲状态。比如Wi-Fi开关的应用,大部分时间CPU都是空闲的,知道收到控制命令,CPU才需要进行GPIO的操作。若系统应用中有小于DTIM Beacon间隔时间的循环定时,系统将不能进入Light-sleep模式。
  7. Deep-sleep由用户控制,调用接口函数就可立即进入Deep-sleep模式,在该模式下,芯片会断开所有的Wi-Fi连接与数据连接,进入睡眠模式,只有RTC模块仍然工作,负责芯片的定时唤醒。使用Deep-sleep必须将GPIO16与芯片EXT_RSTB管脚连接。
  8. 配置Deep-sleep
    配置Deep-sleep
    配置Deep-sleep
  9. 在Deep-sleep状态下,可以通过外部IO在芯片EXT_RSTB管脚上产生一个低电平脉冲,芯片即可被唤醒并启动
  10. Deep-sleep可以用于低功耗的传感器应用,或者大部分时间都不需要进行数据传输的情况。设备可以每隔一段时间从Deep-sleep状态醒来测量数据并上传,之后继续进入Deep-sleep。也可以将多个数据存储于RTC memory(RTC memory在Deep-sleep模式下任然可以保存数据),然后一次发送出去

I2C接口

  1. 每个GPIO管脚内部都可以配置为开漏模式,从而可以灵活地将GPIO口用作I2C data或clock功能,同时芯片内部提供上拉电阻,以节省外部的上拉电阻。ESP8266作为I2C主机的SDA与SCL线波形由GPIO模拟产生,在SCL的上升沿之后SDA读取数据。SCL高低电平各保持5us,因此I2C时钟频率约为100KHz

OTA升级

  1. 在支持云端升级的软件中,boot.bin用于选择运行user1还是user2,而主程序由原本的eagle.flash.bin和eagle.iromtext.bin合并为user1.bin或user2.bin
  2. system param区存了一个flag,标识启动时应当运行user1还是user2
  3. 启动时先运行boot,boot读取system param区中的flag,判断运行user1还是user2,然后到SPI Flash的对应位置去取
  4. 上传时,将新版本的 user1.bin 和 user2.bin 均上传⾄至服务器,由设备⾃自⾏行判断应该下载user1.bin 还是 user2.bin
  5. user1.bin 和 user2.bin 是同样的可执⾏行软件,差别仅在于 flash 的存放位置不同
  6. 固件升级服务器网址
  7. 软件接口
    struct upgrade_server_info
    struct upgrade_server_info
    FOTA升级
    FOTA升级
  8. 上传服务器的固件版本命名形如:[v|b]Num1.Num2.Num3.tPTYPE([o|l|a|n])
    1. v:表示发布版本,b:表示测试版本
    2. 版本值为:Num110001000 + Num2*1000 + Num3
    3. Light ptype = 45772
    4. Switch ptype = 23701
    5. general ptype = 27388
    6. o表示支持在线升级
    7. l表示支持本地升级
    8. a表示既支持在线升级,也支持本地升级
    9. n表示不支持升级

红外遥控

  1. 用于发送的载波可以采用以下几种方式:
    1. I2S的BCK
    2. WS脚产生38KHz载波
    3. 由GPIO中的sigma-delta功能在任意GPIO口产生载波,但sigma-delta产生的载波占空比约为20%,推荐使用MTMS脚(GPIO14),可产生准确的38KHz且占空比为50%的标准方波
    4. 通过系统FRC2的DSR TIMER接口,产生发送序列并驱动红外发送状态机。由于发送NEC红外码需要精确到us级的定时,所以在IR TX初始化时候,会先调用system_timer_reinit来提高FRC2 timer的精度。
  2. 红外接收功能主要通过GPIO的边沿中断完成。读取系统时间,将两次时间相减可以得到波形持续时间。由软件状态机ir_intr_handler进行处理
  3. 红外接收通过GPIO中断实现,而同时,系统只能注册一个IO中断处理程序,如果有其他IO口也需要中断的话,请将这些中断在同一个处理程序中处理(判断中断源并相应处理)
  4. 在非OS版本的SDK中,进入中断处理(GPIO、UART、FRC等)直到退出中断的整个过程中,不可调用带ICACHE_FLASH_ATTR属性的函数,包括打印函数os_printf
  5. 硬件电路图
硬件电路图
硬件电路图

SSL/TLS加密

SSL协议报文
SSL协议报文
  1. SSL运行在TCP/IP层之上、应用层之下,为应用程序提供加密数据通道,它采用了RC4、MD5以及RSA等加密算法,使用40位的密钥。
  2. HTTPS实际上就是HTTP over SSL,它使用默认端口443,而不是像HTTP那样使用端口80。
  3. HTTPS协议使用SSL在发送方把原始数据进行加密,然后在接收方进行解密,加密和解密需要发送方和接收方通过交换公知的密钥来实现,因此,所传送的数据不容易被网络何可截获和解密
    SSL通信过程
    SSL通信过程
  4. 工作流程
    1. 建立安全能力。SSL捂手的第一阶段启动逻辑连接,建立这个连接的安全能力。首先客户机向服务器发出client hello消息并等待服务器响应,随后服务器向客户机返回server hello消息,对client hello消息中的信息进行确认。
      1. Client hello消息包括:
        1. 客户端可以支持的SSL最高版本号
        2. 一个客户端生成的随机数,稍后用于生成“对话密钥”
        3. 一个确定会话的会话ID
        4. 一个客户端可以支持的密码套件列表,每个套件都以SSL开头,紧跟着的是密钥交换算法,用with这个词把密钥交换算法、机密算法、散列算法分开。例如:SSL_DHE_RSA_WITH_DES_CBC_SHA, 表示把DHE_RSA(带有RSA数字签名的暂时Diffie-HellMan)定义为密钥交换算法;把DES_CBC定义为加密算法;把SHA定义为散列算法。
        5. 一个客户端可以支持的压缩算法列表
      2. Server Hello消息包括
        1. 一个SSL版本号。去客户端支持的最高版本号和服务端支持的最高笨笨好中的较低者
        2. 一个服务器生成的随机数,稍后用于生成“对话密钥”
        3. 会话ID
        4. 从客户端的密码条件列表中选择的一个密码套件
        5. 从客户端的压缩方法的列表中选择的压缩方法
      3. 这个阶段之后,客户端服务端知道了下列内容:
        1. SSL版本
        2. 密钥交换、信息验证和加密算法
        3. 压缩方法
        4. 有关密钥生成的两个随机数
    2. 服务器鉴别与密钥交换。服务器启动SSL握手第二阶段,是本阶段所有消息的唯一发送方,客户机是所有消息的唯一接收方。该阶段分为4步
      1. 证书:服务器将数字证书和到根CA整个链发给客户端,使客户端能用服务器证书中的服务器公钥认证服务器
      2. 服务器密钥交换:这里视密钥交换算法而定
      3. 证书请求:服务端可能会要求客户自身进行验证
      4. 服务器握手完成
    3. 客户机鉴别与密钥交换。客户机启动SSL握手第三阶段,是本阶段所有消息的唯一发送方,服务器是所有消息的唯一接收方。该阶段分为3歩:
      1. 证书:为了对服务器证明自身,客户要发送一个整数信息,这是可选的
      2. 客户机密钥交换:这里客户端将预备主密钥发送给服务端,注意这里会使用服务端的公钥进行加密
      3. 证书验证:对预备密钥和随机数进行签名
    4. 完成,客户机启动SSL握手的第四阶段,是服务器结束。该阶段分成4歩
  5. SSL协议可分为两层:
    1. SSL记录协议:它建立在可靠的传输协议之上,为高层协议提供数据封装、压缩、加密等基本功能的支持
    2. SSL握手协议:它建立在SSL记录协议之上,用于在实际数据传输开始前,通讯双方进行身份认证、协商加密算法、交换密钥等
  6. ESP8266作为SSL server时,提供加密证书的制作脚本,生成SSL加密所需的头文件cert.h和private_key.h。CA认证功能默认关闭,用户可调用接口espconn_secure_ca_enable使能CA认证
    1. 证书制作:tool文件夹下,修改makefile.sh文件里面的IP地址为实际SSL 服务器的IP地址;然后./makefile.sh
    2. 开发者必须调用espconn_secure_set_default_certificate和espconn_secure_set_default_private_key传入证书和密钥
    3. 证书制作脚本makefile.sh生成默认SSL server证书由Espressif System颁发,并非由CA颁发。如果用户需要CA认证,请将运行脚本Makefile.sh生成的TLS.ca_x509.cer导入SSL client,并使用脚本make_cacert.py将CA文件生成eap_ca_cert.bin烧写到Flash对应的地址
  7. ESP8266作为SSL client时,可支持双向认证。CA认证功能默认关闭,用户可调用接口espconn_secure_ca_enable使能CA认证
  8. ESP8266作为SSL client时支持证书认证功能,但此功能默认关闭,开发者可以调用接口espconn_secure_cert_req_enable使能证书认证,证书制作:
    1. 修改脚本makefile.sh,制作开发者自行签发的CA证书,例如,证书实例中的TLS.ca_x509.cer
    2. 使用签发的CA制作供SSL client使用的证书,例如,证书示例中的TLS.x509_1024.cer
    3. 去除制作SSL client使用的证书时所用的密钥,例如证书示例中的TLS.key_1024
    4. 将证书合成脚本make_cacert.py与CA文件放在同一目录下
    5. 运行脚本“make_cacert.py”将合成同一目录下的CA文件生成sap_ca_cert.bin,esp_ca_cert.bin的烧录位置由接口espconn_secure_ca_enable设置,用户可以自行定义
    6. 重命名证书名称(例如TLS.x509_1024.cer);重命名密钥名称,改为private_key.key_1024。
    7. 将重命名后的文件,与脚本make_cert.py拷贝到同一目下下
    8. 运行脚本make_cert.py生成esp_cert_private_key.bin,esp_cert_private_key.bin的烧录位置由接口espconn_secure_cert_enable设置,用户可自行定义
  9. 软件接口
    1. SSL系列软件接口与普通TCP软件接口,在SDK底层是两套不同的处理流程,因此不能混用两种软件接口。SSL连接时,仅支持使用:
      1. espconn_secure_XXX系列接口
      2. espconn_regist_XXX系列注册回调的接口
      3. espconn_port获得一个空闲的端口
  10. 在SSL中会使用密钥交换算法交换密钥;使用密钥对数据进行加密;使用散列算法对数据的完整性进行验证,使用数字证书证明自己的身份
  11. SSL/TLS协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器所要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。
    1. 如何保证公钥不被篡改?
      • 将公钥放在数字证书中。只要证书时可信的,公钥就是可信的
    2. 公钥加密计算量太大,如何减少耗用的时间?
      • 每一次对话(session),客户端和服务器都生成一个“对话密钥”,用它来加密信息。由于“对话密钥”是对称加密,所以运算速度非常快,而服务器公钥只用于加密“对话密钥”本身,这样就减少了加密运算的消耗时间
    3. SSL/TLS协议的基本过程是这样的:
      1. 客户端向服务端索要并验证公钥
      2. 双方协商生成“对话密钥”
      3. 双方采用“对话密钥”进行加密通信
    4. 为什么要用三个随机数来生成“会话密钥”?
      • 不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。对于RSA密钥交换算法来说,pre-master-key本身就是一个随机数,再加上hello消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。pre master的存在在于SSL协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么pre master secret就有可能被猜出来,那么仅适用pre master secret作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。

Flash接口

  1. 一个扇区为4KB,从扇区0开始计数,以下接口可以读写整个Flash的任意区域
    1. SpiFlashOpResult spi_flash_erase_sector (uint16 sec):擦除Flash的某个扇区
    2. SpiFlashOpResult spi_flash_write (uint32 des_addr,uint32 *src_addr, uint32 size):将数据写入Flash
    3. SpiFlashOpResult spi_flash_read(uint32 src_addr,uint32 * des_addr, uint32 size):读取Flash中的数据
  2. 在IoT_Demo中,将应用级数据存储在了0x3C000开始的4X4KB区域。例如,master_device_key.bin(用户参数,仅在需使用Espressif Cloud的情况需烧录)烧录在0x3E000地址
  3. Flash擦除的最小单元为一个扇区,当存储在某个扇区的数据需要改写时,流程是先擦掉整个扇区,再将该扇区的数据写回去
  4. 读写保护的方法:
    • 使用三个扇区,提供4KB的可靠存储空间
      1. 将sector1和sector2作为数据sector,轮流读写,时钟分别存放“本次”数据和“前一次”数据,确保了至少有一份数据存储安全;sector3作为flag sector,标志最新的数据存储sector。
      2. 初次上电时,数据存储在sector2中,从sector2中将数据读到RAM
      3. 第一次写数据时,将数据写入sector1.此时若突然掉电,sector1写入失败,sector2&3数据未改变;重新上电时,仍是从sector2中读取数据,不影响使用。
      4. 改写sector3,将标志置为0,表示数据存于sector1.此时若突然掉电,sector3写入失败,sector1&2均存有一份完整的数据,重新上电时,因为sector3无效,默认从sector2中读取数据,则仍能正常使用,只是未能包含掉电前对sector1写入的数据
      5. 再一次写数据时,先从sector3读取flag,若flag为0,则上次数据存于sector1,此次应将数据写入sector2;若flag为非0,则认为上次数据存于sector2,此时应将数据写入sector1.
      6. 写入sector1或者sector2完成后才会写sector3,重置flag

cJSON使用

1
2
3
4
5
6
7
8
9
typedef struct cJSON {  
struct cJSON *next,*prev;
struct cJSON *child;
int type;
char *valuestring;
int valueint;
double valuedouble;
char *string;
} cJSON;
  1. cJSON结构体为一个双向链表,并可通过child指针访问下一层
  2. type变量决定数据类型,数据项可以是字符串可以是整形,也可以是浮点型。如果是整形的话可以从valueint取出,如果是浮点型的话可以从valuedouble取出,以此类推
  3. 主要函数说明
    1. 解析
      1. cJSON_Parse函数负责解析JSON数据包,并按照cJSON结构体的结构序列化整个数据包。使用该函数会通过malloc函数在内存中开辟一个空间,使用完成需要手动释放
      2. cJSON_GetObjectItem函数可以从cJSON结构体中查找某个子节点名称,如果查找成功,可把该子节点序列化到cJSON结构体中
      3. 如果需要使用cJSON结构体中的内容,可通过cJSON结构体中的valueint和valuestring取出有价值的内容
      4. 通过cJSON_Delete释放内存空间
    2. 组装
      1. cJSON_CreateObject函数可创建一个根数据项,之后便可向该根数据项中添加string或int等内容
      2. cJSON_AddNumberToObject向节点中添加子节点
      3. cJSON_Print函数可以打印跟数据项
  4. 使用例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    cJSON* pRoot = cJSON_CreateObject();
    cJSON* pArray = cJSON_CreateArray();
    cJSON_AddItemToObject(pRoot, "students_info", pArray);
    char* szOut = cJSON_Print(pRoot);

    cJSON* pItem = cJSON_CreateObject();
    cJSON_AddStringToObject(pItem, "name", "chenzhongjing");
    cJSON_AddStringToObject(pItem, "sex", "male");
    cJSON_AddNumberToObject(pItem, "age", 28);
    cJSON_AddItemToArray(pArray, pItem);

    pItem = cJSON_CreateObject();
    cJSON_AddStringToObject(pItem, "name", "fengxuan");
    cJSON_AddStringToObject(pItem, "sex", "male");
    cJSON_AddNumberToObject(pItem, "age", 24);
    cJSON_AddItemToArray(pArray, pItem);

    pItem = cJSON_CreateObject();
    cJSON_AddStringToObject(pItem, "name", "tuhui");
    cJSON_AddStringToObject(pItem, "sex", "male");
    cJSON_AddNumberToObject(pItem, "age", 22);
    cJSON_AddItemToArray(pArray, pItem);

    char* szJSON = cJSON_Print(pRoot);
    cJSON_Delete(pRoot);
    //free(szJSON);

    pRoot = cJSON_Parse(szJSON);
    pArray = cJSON_GetObjectItem(pRoot, "students_info");
    if (NULL == pArray)
    {
    return -1;
    }

    int iCount = cJSON_GetArraySize(pArray);
    for (int i = 0; i < iCount; ++i)
    {
    cJSON* pItem = cJSON_GetArrayItem(pArray, i);
    if (NULL == pItem)
    {
    continue;
    }

    string strName = cJSON_GetObjectItem(pItem, "name")->valuestring;
    string strSex = cJSON_GetObjectItem(pItem, "sex")->valuestring;
    int iAge = cJSON_GetObjectItem(pItem, "age")->valueint;
    }

    cJSON_Delete(pRoot);
    free(szJSON);