内网穿透
https://zhuanlan.zhihu.com/p/303175108
本想用ngork, 然而注册总是不成功. 另一个natapp的页面看起来似乎运营实力一般,再加上免费版本有些限制, 所以就看了看其他的方式.
因为有个阿里云的服务器, 最后选择frp
行不通的方法
- 端口映射. 现在家庭和公司PPPoE获取的地址都已经是运营商的内部NAT地址了, 以100开头, 所以路由器端口映射行不通.
https://zhuanlan.zhihu.com/p/303175108
本想用ngork, 然而注册总是不成功. 另一个natapp的页面看起来似乎运营实力一般,再加上免费版本有些限制, 所以就看了看其他的方式.
因为有个阿里云的服务器, 最后选择frp
注意: 固件编译只能通过 docker进行.
原因是:
xtensa-lx106-elf-linux64-1.22.0-92-g8facf4c-5.2.0.tar.gz), 所以干脆用了腾讯提供的docker img了.docker img获取方式: docker pull hubertxxu/esp8266_build:0.1, 由腾讯的xph提供.
docker项目文件夹在/r/下.
docker linux 版本:
uname -a
Linux bff9dfda8fd0 5.10.16.3-microsoft-standard-WSL2 #1 SMP Fri Apr 2 22:23:49 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
支持yum install命令
docker容器挂载windows目录的启动方式:
docker run --name esp -v path/to/host/folder:/path/to/container/folder -dt hubertxxu/esp8266_build:0.1
挂载以后进行编译.
container修改esp工具链可访问性
chmod -R 777 /home/esp8266
cd /etc
mv yum.repos.d yum.repos.d.backup
mkdir yum.repos.d
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-8.repo
另外, dnf是比yum更先进的包管理工具,可以换用dnf
使用了下面一长串的命令启动了ssh, 但是docker内的ip不是真实ip,host ping container是不通的. 我在host上建立了ftp server,通过container登录来看container的ip,发现竟然就是host的ip.
使用如下步骤开启了sshd
yum install net-tools
yum install openssh-server
yum install passwd
mkdir -p /var/run/sshd
ssh-keygen -A
然后编辑sshd配置
vi /etc/ssh/sshd_config
打开以下配置
Port 22
ListenAddress 0.0.0.0
PasswordAuthentication Yes
设置root密码
passwd
启动服务
/usr/sbin/sshd -D &
查看进程和端口占用
ps -A
netstat -nptl
本地连接测试
ssh localhost 最近做完了通过8266 Qcloud AT做腾讯连连的台灯的项目, 使用enum做连接状态记录, 在做网络连接的过程中发现了一些问题, 这儿讨论一下.
typedef enum _ESP_STATUS{
ESP_STATUS_INIT,
ESP_STATUS_READY,
ESP_STATUS_GOT_MAC,
ESP_STATUS_CONFIGED_NTP_SERVER,
ESP_STATUS_GOT_TIME,
ESP_STATUS_CONNECTED_MQTT,
ESP_STATUS_SUBED_MQTT,
ESP_STATUS_WIFI_DISCONNECTTED,
}ESP_STATUS;
上面这种enum用下划线, type去掉下划线, 并在枚举项内体现type名称的命名习惯从华为开始就确定了. 这个不需要讨论.
从上到下依次为按时间顺序会依次进入的状态, 这个也没有问题.
GOT_MAC先动作再对象也是对的.
想讨论的READY/GOT/CONFIGED这种过去时态. 我认为不如进行时态. 过去时态会造成当前状态与上一个状态在意义上的耦合. 因为READY以后就要获取MAC, 所以READY不如改名为GETTING_MAC, GOT_MAC不如改名为CONFIGING_NTP_SERVER. 也就是凡是要主动做操作的, 都用进行时态.
另外还有一种是处在状态中不主动动作, 而是被动等待状态改变(相当于什么也不做). 名称应改为WAITFOR_DO_XXX, 如WAITFOR_RECONNECT_WIFI.
修改如下:
typedef enum _ESP_STATUS{
ESP_STATUS_WAITFOR_READY,
ESP_STATUS_GETTING_MAC,
ESP_STATUS_CONFIGING_NTP_SERVER,
ESP_STATUS_GETTING_TIME,
ESP_STATUS_CONNECTING_MQTT,
ESP_STATUS_SUBING_MQTT,
ESP_STATUS_REPORTING_MQTT_PROPERTY,
ESP_STATUS_WAITFOR_CONNECT_WIFI
}ESP_STATUS;
其中, ESP_STATUS_SUBING_MQTT又可以分为对SERVICE和PROPERTY分别SUB, 两者虽然没有先后之分, 但是仍应该人为规定先后顺序并放入枚举项中. 改为:
typedef enum _ESP_STATUS{
ESP_STATUS_INIT, // 这时候等待READY
ESP_STATUS_GETTING_MAC,
ESP_STATUS_CONFIGING_NTP_SERVER,
ESP_STATUS_GETTING_TIME,
ESP_STATUS_CONNECTING_MQTT,
ESP_STATUS_SUBING_MQTT_SERVICE,
ESP_STATUS_SUBING_MQTT_PROPERTY,
ESP_STATUS_REPORTING_MQTT_PROPERTY,
}ESP_STATUS;
.h文件是用来放置public公有性质的内容, #define 常量, typedef enum, typedef struct, extern 全局变量, 公用函数声明返回值 函数值(参数列表);. 不可以做c文件的私有全局变量声明, 也不可以做函数定义, 也不可以做c文件私有函数声明.
.c文件用来放置公有和私有的全局变量定义, 私有函数声明, 共有和私有的函数定义.
.h文件内部由于有了#ifndef 头文件名 #define 头文件名 #endif的结构, 所以可以#include其他.h文件.
.c文件任何时候都不应被其他文件#include
将1对同名.h和.c文件作为一个模块的话, 项目中为模块划分层次, 底层模块为上层提供服务, 设置基本共识. 需要注意的是上层模块可以#include下层模块, 但下层模块不能#include上层模块, 也就是说, 下层不应依赖上层, 上层的任何变化不应使下层模块无法使用, 无论上层如何变化下层都应提供一致的服务.
为进一步让上层解耦, 摆脱对特定实现方式的依赖, 下层可以定义标准接口性质的.h文件, 上层#include和调用标准.h, 下层在不修改标准.h的情况下进行升级修改, 或者完全替换为另一种实现.
同层模块可以相互#include.
目前考虑, 项目基础配置处于最底层L0. 硬件资源驱动处于L1, 基于硬件驱动提供的服务处于L2, 操作系统处于L3, 基于操作系统提供的服务处于L4, 具体应用处于L5.
按这种划分, 以灯为例, 作表如下:
| 层数 | 层名 | 功能模块 |
|---|---|---|
| L5 | 应用层 | 灯的逻辑功能 |
| L4 | 系统服务层 | 系统Tick的时钟 |
| L3 | 系统层 | RTOS |
| L2 | 硬件服务层 | 驱动回调 |
| L1 | 硬件驱动层 | Uart, Adc |
| L0 | 配置层 |
amazon官方文档, 中文好评!https://docs.amazonaws.cn/freertos/index.html
官网: https://freertos.org/
官方文档,英文的
AWS上的文档,也是英文的
FreeRTOS与RT-Thread对比: 后者自带很多第三方协议, 如i2c/mqtt等等, 而FreeRTOS似乎要自己找自己组装进去.
任务:
xTaskCreate建立多个任务, 然后使用vTaskStartScheduler调度任务函数. 任务函数执行完成任务后应该进入等待.
队列:
xQueueCreate建立队列, xQueueReceive接收队列.
根据FreeRTOS操作系统最全面使用指南 整理
似乎就主要3个功能: 任务调度/FIFO队列通讯/内存管理.
时间管理主要就是一个延迟函数vTaskDelay
下面看官网教程https://www.freertos.org/FreeRTOS-quick-start-guide.html的Developer Docs部分. 描述Kernel部分.
FreeRTOS用的是MISRA标准.
| 变量类型 | 前缀 |
|---|---|
| uint32_t | ul |
| uint16_t | us |
| uint8_t | uc |
| 有符号非标准int(如Type_t/TickType_t/size_t) | x |
| 无符号非标准int(如UbaseType_t) | ux |
| enum类型 | e |
| * 指针 | p |
| 函数类型 | 前缀 |
|---|---|
| 文件内函数(类似private) | prv |
| void空返回函数 | v |
| 其他返回值函数 | 参照变量前缀 |
宏也有前缀, 应含有或部分含有所在的文件名(小写).其他部分为大写, 如: configUSE_PREEMPTION, 习惯了全大写的看起来怪怪的
//注释, 哈哈. 一行80列以内.#include的顺序是标准库/自己的库/硬件特定的库#include后面依次是#define, 文件内函数声明(都加了static), 文件内全局变量声明, 函数定义/*-------*/分行FreeRTOS要做的跟Windows一样, 一个任务是一个窗口, 有自己的上下文和环境, 看上去就像独立运行的一样. 多个任务看起来就像是同时在运行, 实际上由任务调度去决定任务的切换, 每一时间只有一个任务占领CPU(当然Windows也是一样的)
占领CPU的时候就是 running态, 暂时调度给其他任务就是ready态.
任务自己进入等待而把CPU主动让出来交Blocked态, 如调用了vBlockDelay()或等待其他事件. 等到了就恢复ready,或者实在等不到(超时timeout了)也恢复ready
啥也不干的任务可以调用vTaskSuspend()进入Suspended态, 跟Blocked区别就是不会超时. 只有调用xTaskResume()才会回到ready态.
数字高的先运行. 0是最低数字. 当前运行的就是最高优先级的一个或多个ready任务. 多个任务同时的话就用时间片切换.
高优先级永远最先运行, 如果占着CPU不放, 低优先级永远不会运行. 所以要高风亮节, 等事的时候就把CPU让出来, 进入Blocked或者Suspended, 事儿来了再处理.
抢占式和时间分片这两个特性可以在FreeRTOSConfig.h中关闭.
另外支持大小核(AMP)和同等多核(SMP)等CPU
void vATaskFunction( void *pvParameters )
{
for( ;; )
{
-- Task application code here. --
}
/* Tasks must not attempt to return from their implementing
function or otherwise exit. In newer FreeRTOS port
attempting to do so will result in an configASSERT() being
called if it is defined. If it is necessary for a task to
exit then have the task call vTaskDelete( NULL ) to ensure
its exit is clean.
翻译: 任务别想着return, 新的port里这样做会导致configASSERT()
调用报错. 如果非得退出, 调用vTaskDelete( NULL ) */
vTaskDelete( NULL );
}
函数形态咯. for(;;)的意思是任务一直执行, 直到vTaskDelete, 相当于windows的×. 任务里面是坚决不能有return的, 否则就会报错.
一直执行的任务里面, 自己决定是不是要Blocked或者Suspended把CPU让出来. 所以这个for(;;)在裸机系统里面看起来像是个死锁, 实际上却是让任务保持运行的方式, 毕竟它不能return
任务函数的类型是TaskFunction_t
void vATaskFunction( void *pvParameters )
{
for( ;; )
{
/* Psudeo code showing a task waiting for an event
with a block time. If the event occurs, process it.
If the timeout expires before the event occurs, then
the system may be in an error state, so handle the
error. Here the pseudo code "WaitForEvent()" could
replaced with xQueueReceive(), ulTaskNotifyTake(),
xEventGroupWaitBits(), or any of the other FreeRTOS
communication and synchronisation primitives.
翻译: 下面伪码展示了任务等待事件的方式. 事件发生了就处理事件,
超时了就处理报错. 伪码WaitForEvent可以换成 xQueueReceive(),
ulTaskNotifyTake(), xEventGroupWaitBits()或其他通讯同步机制 */
if( WaitForEvent( EventObject, TimeOut ) == pdPASS )
{
-- Handle event here. --
}
else
{
-- Clear errors, or take actions here. --
}
}
/* As per the first code listing above. */
vTaskDelete( NULL );
}
任务使用 xTaskCreate() xTaskCreateStatic()创建, 使用vTaskDelete()销毁.
超小RAM的MCU可以用协程, 32位的CPU一般就用不到了. 这说的是Arduino的那个8位机吗? 这儿也不介绍了, 一般这种情况估计都用裸机了.
fromISR结尾的API函数. ISR意思是Interrupt Service Routine中断服务事务,就是中断处理函数.先说啥是信号量. 简单地说就是解决资源占用的方式, 跟十字路口的信号灯一样. 拿到绿灯的占用路口走, 给另一个方向的车辆红灯, 另一个方向的就等着(Blocking).有限的资源多个任务都想用, 就用信号量来解决. 也有点像停车场剩余车位数(计数信号量). 没车位的都在外面等着(Blocking).
这儿和这儿有中文讲解.
互斥和同步. 互斥就是一个球只有一个人拍, "排斥"了其他人拍球, 想玩的排队. 同步就是想做才得先小工洗菜,再大厨炒菜,再跑堂端菜, 都是菜, 但就是得按顺序处理, 没到操作步骤的也排着队等.这儿看到同步也是一种互斥, 除了等资源, 还得步骤到了.
我在处理腾讯连连的MQTT时可能就是一种同步机制, 需要按顺序处理ready->GOT IP->时间->MQTT.只不过我是用enum的不同状态表示的.
二进制信号量就是占用时加锁, 释放时解锁, 表明这两种状态的.
Task Notifications任务通知有时候可以替代二进制信号量, 并且更轻量一些.
xSemaphoreGiveFromISR()同样
Task Notifications任务通知有时候做轻量级替代.
我在MQTT处理对pub消息的回应时用了类似的计数机制. 为防止在连续发出的第二条pub指令之后, 实际执行pub之前, 收到了上一条pub指令的success消息, 将pub指令取消导致第二条pub没有执行(同时还要兼顾失败重发机制), 我在pub发出指令后增加计数/在success时减少计数, 只有计数为0时才完全停止pub.
用于对有限资源的访问.
互斥量借用完要自己还回去(只有1个且不会被消耗), 而二进制信号量用完不用还(持续被生产出来并且被消耗掉).
低优先级的任务持有互斥量时, 高优先级的再去获取, 会把低优先级的任务的优先级暂时提高到高优先级.
适合任务间确认谁访问资源, 不适合中断使用. 因为中断是不可能去等待任务给他返还可用性的.
可以被一个任务连续借用多次, 但也要被还回去这么多次, 其他任务才能用.
算是新特性, 从v8.2.0开始引入, 在v10.4.0中增加了单任务多通知功能
xTaskNotifyWait()/xTaskNotifyWaitIndexed()读了值以后, 通知状态就会变成Not Pending.xTaskNotifyStateClear()/xTaskNotifyStateClearIndexed()清除通知的Pending态xTaskNotifyIndexed() xTaskNotifyGiveIndexed()发通知. 通知进入Pending态, 直到任务B调用 xTaskNotifyWaitIndexed() ulTaskNotifyTakeIndexed(). 函数名里面的Indexed可以去掉, 就对应的是Index==0的通知(感觉是为了保持版本的兼容性)10.0.0以后才有的特性
TBD. 这么新的特性估计暂时用不到, 晚些在研究.
最新版本有5种堆管理方式.
这部分感觉暂时也用不上, 看原文吧.
直到vTaskStartScheduler()调用后, 程序才表现得像个RTOS的样子.
百度搜索了资料, 似乎只能通过打印信息调试, 搜索"FreeRTOS 断点调试"找不到结果.
Bing搜索"FreeRTOS breakpoint debug"还是能找到几条使用Keil设置断点调试的方式, 主要是说要在task里面设置断点, 并且不能用步入模式调试.
我设想一种调试方式, 即只针对task进行调试, 把task当做一个系统. 把FreeRTOS的API函数和硬件函数全部Stub来调试逻辑. 剩下的硬件逻辑和OS相关逻辑通过真机printf调试. 当然单元测试应该是可以用的.
似乎要先看这个FreeRTOS+lwIP 平台接入指引, 里面会以8266举例. 然后再看C SDK使用参考
另外还有个C SDK 移植接入指引不知道有用否.
SDK 说明及下载似乎包括了所有有关SDK的下载.
本来用流程图做, 但发现流程图这个形式很难做, 箭头重复的太多: 【腾讯文档】腾讯连连ESP8266-QCloud-AT固件使用指南 ,大概原因是腾讯文档只能用直角箭头.
改用思维导图做.【腾讯文档】腾讯连连ESP8266-QCloud-AT固件使用指南
主要系列是按CPU划分的.
其中ESP32和S2/S3/C3已经是不同的产品了, 这样命名挺讨厌, 自己既是大类的名称又是小类的名称...
| 系列 | CPU |
|---|---|
| 8266/8285 | Xtensa L106 |
| ESP32 | Xtensa LX6 单核或双核 |
| ESP32-S2 | Xtensa LX7单核 |
| ESP32-S3 | Xtensa LX7双核 |
| ESP32-C3 | RISC-V |
| 后缀 | 意义 |
|---|---|
| FHn | n MB的flash |
| Rn | n MB的PSRAM |
| FNn | n MB的Flash |
多年来都是瞎打,中午虽然有半小时出汗,也还想多获得一点儿乐趣。
视频: https://www.bilibili.com/video/BV1ux411L73q
要找到适合自己的舒服的投篮姿势. 每个人都是不同的.
侧对篮筐而不要正对篮筐. 投篮侧的脚在前指向篮筐, 可减轻肩膀压力有利于命中.脚尖着地跳投. 跳投前脚后跟接触地面会泄力. 脚尖着地也能增加射程跳投双脚前摆(而不是落在原地)能让出手点提高和弧度提高从而提高命中率投篮手部姿势从起手位就要弯好,而不是要发力投球的时候再后弯手腕和准备其他手指篮球口袋(大概意思就是在衣服下口袋的位置?), 并沿着垂直面向上起球. 同样不能因为着急而不沿着自己熟悉的投球线路起球.总结: 投球要准就是要将身体准备姿势放置到最舒适位置才投.
AHK用了很久却一直没有深入学过, 今天深入一点写个入门.
AHK不比什么RPA工具效率低. 最大的缺失可能是元素识别.
;0. 自动执行的,跟js一样写什么执行什么
Some code
return
;1.定义快捷键(热键)的
^j::
Some Code
Return
;2. 定义热字符串的
::btw::
Send, by the way
return
;3. 定义子程序的. 相当于汇编里面的label, 然后用gosub调用(跟goto有点像)
subName:
Some Code
return
;4. 定义函数的
function(param1, param2:="xxx"){
Some Code
return "A value"
}
可以用#include包含其他ahk文件
ahk说明里面讲到ahk历史悠久,所以有一些传统语法(就是看上去有些恶心的语法), 比如说某些命令里面字符串可以不加引号让你搞不清哪儿是字符串开始和结尾...
; 单行注释
/*
多行注释
但有趣(烦躁)的是, /*和*/必须在行首. 像这一行的这俩是不起作用的
*/
"这是一个字符串, 需要双引号"
变量 := 赋值
讲讲字符串里面的转义. C语言是用\转义, AHK比较变态使用<code></code>转义,搞得我markdown都得写code标签. 主要转义有. %t rn%用于创建动态变量引用和动态函数调用, 有点让我想起来.bat格式
但是但是但是, "的转义是"", 很神奇吧, 那就是说如果要表示一个引号字符串,加上把它括起来的引号实际上是4个引号哈哈哈哈""""
+-*/(), 和++ -- += -=等运算符都支持, condition?value1:value2的三元运算也支持.
:=是赋值运算符, 不太一样. and or not是逻辑运算符
支持类似js的对象, 可以用这样的obj := {Key1: Value1, Key2: Value2}形式创建, 以obj.Key1的形式访问
数组也是[]形式的, 但是第一个成员的序号是1而不是0
按键:
| 软件 | ABCDEFGHIJKLMNOPQRSTUVWXYZ | 12346890 | F123456789⒑⒒⒓ |
|---|---|---|---|
| EXCEL | JMQ | 无 | F1 F2 |
Excel参考: https://www.asap-utilities.com/excel-tips-shortcuts.php
Windowspy.ahk 用于查找句柄ClassNN
; 设置鼠标坐标为屏幕
CoordMode, Mouse, Screen
; 按窗口标题名称点击登录
ControlClick, X161 Y489, 系统登录
; 点击坐标
Click, 123 456
; 激活不在前台的窗口
WinWaitActive, ahk_exe EXCEL.EXE
; 停1000ms
Sleep, 1000
; 发送alt+F4
Send, !{F4}
; 发送字符串并且不激活输入法
Send, {text}some string
; 运行程序
Run, "D:\Program Files\网店管家云端版\wdgjyun.exe"
; 创建函数
saveExcel(filename)
{
; 删除文件
FileDelete, %filename%
Sleep, 1000
send, {F12}
Sleep, 2000
send, {text}%filename%
Sleep, 1000
send, {Enter}
Sleep, 1000
send, {Enter}
Sleep, 2000
Send, !{F4}
}
; 点击图片. x1,y1 ~ x2,y2是图片的搜索区域. delta是点击位置与图片左上角在xy坐标上的偏移位置
imageClick(x1, y1, x2, y2, imageFile, delta){
ImageSearch, FoundX, FoundY, x1, y1, x2, y2, %imageFile%
if (ErrorLevel = 2)
MsgBox Could not conduct the search.
else if (ErrorLevel = 1)
MsgBox Icon could not be found on the screen.
else{
MouseClick, left, FoundX+delta, FoundY+delta
}
} 乐鑫已经停止了对8266 AT开发的支持, 后续只维护BUG. 现在乐鑫推荐 ESP32-C3也即是RISC-V平台.
ESP-AT Git项目能找到8266最后版本的技术文档
官方文档是这个: ESP8266 SDK的开发指南, 目前只有英文
CSDN上半颗心脏同学的8266开发笔记
乐鑫关于8266和32开发用IDF的区别, 8266 SDK从V3.0开始与IDF风格统一, 但由于不是完全兼容, 所以名称没有改为IDF.
ESP8266 SDK的GIT库
ESP-IDF的GIT库
还挺费劲, 下载的东西还挺多
msys32放到一个简短点儿的目录里比较好, 如D:\msys32mingw32.exe, 这就是以后的主要工作环境了. 先在home\用户名\也就是~目录下建立一个esp目录, 把开发支持工具(等会儿会有2个), 放在这儿. home目录就在D:\msys32下面.esp目录里面.mingw32里面用vim .bashrc增加一句话export PATH="$PATH:$HOME/esp/xtensa-lx106-elf/bin"esp目录里下载第二个支持工具. 这儿要用git和github. 所以你可能要先在.bashrc里面设置个代理export http_proxy="192.168.0.1:8080", 然后重启mingw32, 进入esp目录运行: git clone https://github.com/espressif/ESP8266_RTOS_SDK.git , 也可以下载zip包https://github.com/espressif/ESP8266_RTOS_SDK/releases/download/v3.4/ESP8266_RTOS_SDK-v3.4.zip直接解压, 然后把clone后的路径设置为系统变量IDF_PATH. 可以在.bashrc里面设置, 增加一行: export IDF_PATH="$HOME/esp/ESP8266_RTOS_SDK",也可以再windows系统的环境变量里面设置, 如果在winows环境变量设置, 值要设置为/home/用户名/esp/ESP8266_RTOS_SDK这样的形式. 似乎~符号不认. 重启mingw32使设置生效.python -m pip install --user -r $IDF_PATH/requirements.txthello world出来cp -r $IDF_PATH/examples/get-started/hello_worldhello_world目录运行make menuconfig, 出来个UI界面配置一下串口名称如COM18, 保存退出. 注意这个是ESP8266的烧录串口, 波特率74880make flash, 会检查环境, 并且从github下载项目, 编译, 挺花时间, 没个十几分钟搞不定. 可以增加一个-j8参数, 使用8核编译,速度会快很多make monitor可以监控串口输出.好了, 看起来终于可以开始了.
make all 只编译而不烧录, 也会在最后显示烧录命令.
make print_flash_cmd 打印烧录命令, 可以用在其他地方
make partition_table 打印分区表, 一般分为两种: 非OTA的分区表Single factory app, no OTA和OTA分区表Two OTA app, 可以在make menuconfig中选择.
分区表烧录在0x8000位置, 长度为0xC00
非OTA的分区表:
# Espressif ESP8266 Partition Table
# Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 0xF0000
NVS是非易失存储, PHY似乎是物理什么的
OTA的分区表
# Espressif ESP8266 Partition Table
# Name, Type, SubType, Offset, Size
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000
ota_0, 0, ota_0, 0x10000, 0xF0000
ota_1, 0, ota_1, 0x110000, 0xF0000
可以看到OTA分区表是兼容非OTA分区表的, 兼容性明显比以前的8266项目好多了. 网上有很多介绍以前的8266分区表的, OTA分为user0/user1, user0起始于0x1000而不是现在的0x10000, 现在应该已经不适用了.
otadata是指示启动那个app的, 如果为空就启动ota_0
分区表可以自定义, 不过我感觉一般是没必要了
大写T在最后面的是IDF的任务, 其他的是FREERTOS的任务.
uiT 用户初始化任务, 初始化完以后调用app_main再销毁自己
IDLE FREERTOS的空闲任务, 在其钩子vApplicationIdleHook中调用sleep和喂狗任务
Tmr FREERTOS的软时钟
ppT 处理WIFI硬件驱动,我猜是process protocol的缩写
ppT 系统电源管理, power managerment.
rtT 高优先级的硬时钟中断任务. 主要处理WIFI实时事件. 基于这个组件的程序不要在应用(application)里面调用,以免影响WIFI收发.
tiT Tcp-Ip协议栈的任务(LwIP), 处理TCP-IP包.
esp_event_loop_task 处理系统事件
优先级从低到高依次是:
0 IDLE
2 Tmr
8 tiT
10 esp_event_loop_task
11 pmT
12 rtT
13 ppT
14 uiT
用户任务的优先级要低于rtT也就是12.
如果要加快 TCP/UDP 吞吐量,可以尝试将发送/接收任务的优先级设置为高于“tiT”任务的优先级(8)
SmartConfig应该属于一种Sniffer.
8266是没有硬件PWM的, 它是用硬时钟模拟的软件PWM. 硬时钟同时用于WIFI, 所以WIFI sniffer和PWM同时用会出现资源争用的问题.
N个通道的PWM每次翻转GPIO将占用6+8N ns的时长. 例如一个通道PWM就是14ns.
占用期间, 如果收到了必须要实时处理的LPDC或HT40包, 这些包就会被丢弃.
同时使用PWM和smartconfig会使得配网时间更长. 而且调整PWM的频率/duty cycle/phase都会影响smartconfig的速度.
如果要同时使用, 需要:
非常神奇的, 8266仿照IDF风格的API编程手册, 乐鑫已经放弃治疗了, 8266 RTOS SDK 编程指南最后版本V3.4中, 居然只是把每个函数罗列了一遍, 有个球用. 找了网上各种指南, 发现只能参考ESP32的手册, 在同样的目录结构下, Uart的使用就有非常详细的描述.
make menuconfig工具感觉有些操蛋~ 很多在程序里面定义的参数还要再menuconfig里面预先定义一下, 否则程序编译后运行得总有些问题. 而且make menuconfig重新定义以后,这个编译速度就慢的惊人了
使用linux服务器或者使用docker中加载linux环境, make的速度可以快好几倍.
menuconfig主要是在根目录下生成sdkconfig文件作为编译依据. 同时会把旧的文件改名为sdkconfig.old. 如果熟练的情况可以直接修改sdkconfig文件.
是否要重新编译, 似乎是根据sdkconfig有没有更新决定的. 所以即使修改了一个和编译完全无关的项目, 如PC烧录串口, 也会导致全部重新编译.
IDF各个版本兼容性不佳, 如8266 v3.4和v3.1是不兼容的. 发现的就有 uart_driver_install在v3.4比v3.1多一个参数,导致编译不能通过.
8266只有1.5个串口, 也就是1个完整串口UART0和1个只有TX的串口. 所谓有时候看到的UART2(IO13/15)是UART0交换(swap)过去的.
基于idf, 函数中直接使用printf函数将直接从uart0口输出, 不需要串口初始化的步骤.
在menuconfig中将Swap UART0 I/O pins选上, 编译烧录后, 刚刚启动打印的boot信息在UART0输出, 后面printf的信息都在UART2上输出. 如果使用了软重启esp_restart();, 软重启后的boot信息也将在UART2上输出.
在menuconfig中将Uart for console output选项改为Custom, 如果UART peripheral to use for console output (0-1)仍然为UART0, 结果和上面的一样不变.
如果将UART peripheral to use for console output (0-1)改为UART1, 那么第一次启动的boot信息在UART0输出, printf的信息在UART1输出, 软重启后的boot信息在UART2输出.
在meunconfig中将Partition Table改为Factory app, two OTA definitions不会对运行结果有什么影响, 但是flash烧录的固件名称会改为partitions_two_ota.bin
如果是单文件, 烧录命令是(project_template是项目的目录名):
D:\r\esp\esp-idf\components\esptool_py\esptool\esptool.py --chip esp8266 --port COM18 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 2MB 0x0000 build/bootloader/bootloader.bin 0x10000 build/project_template.bin 0x8000 build/partitions_singleapp.bin
如果是ota,烧录命令是:
D:\r\esp\esp-idf\components\esptool_py\esptool\esptool.py --chip esp8266 --port COM18 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size 2MB 0x0000 build/bootloader/bootloader.bin 0x10000 build/project_template.bin 0x8000 build/partitions_two_ota.bin
menuconfig只能控制所谓console的uart设置, 如果要使用uart,还是要自己通过程序进行uart初始化设置.
uart初始化使用uart0的影子uart2, 如果在menuconfig中已经设置了swap, 在初始化中就不需要再次调用swap函数了.
static void uartInit()
{
// Configure parameters of an UART driver,
// communication pins and install the driver
uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
uart_param_config(UART_NUM_0, &uart_config);
uart_driver_install(UART_NUM_0, BUF_SIZE * 2, 0, 0, NULL);
}
v3.1printf的问题: 有时候不会及时输出, 尤其是在使用了格式化符号%d%x等等后, 必须要用fflush(stdout)后才能输出.
idf头文件#include是有顺序的,否则编译不通过.
感觉顺序似乎应该是标准库文件->freertos头文件->esp系统文件->驱动文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/uart.h"
最近用一款51单片机与8266(AT固件)的UART2接口TTL串口通讯, 使用115200的波特率总是每70多个字节或100多个字节出现误码, 8266使用的WROOM-02D模块, 26MHz晶振, 而51使用的是RC震荡.
具体误码的情况是: 8266发送到51不会误码, 而51发送给8266就会误码. 使用电脑HTerm查看, 8266只要启动了AT固件, 51发送的就会误码, 8266不正常启动(如进入烧录模式)或者启动一个啥也不干的空程序, 就不会误码. 怀疑是8266初始化UART2串口以后才有的问题.
尝试了如下各种方法都失败了:
参考了这篇文章, 里面讲到一次不要发的太多. 考虑到每次发送都是在相对固定的位置误码(70个字符以后), 最后成功的方法是:
每次发送50个字符后, 延时5ms再继续发送.
延时时长是反复测试得出的.测试发现每发送50字符延时2.5ms正好可以不误码, 为了容错将这个时间加大一倍为5ms.
硬件上继续尝试解决, 看是否能够找到最终原因.
久了不用就会忘
参考: https://zhuanlan.zhihu.com/p/85383698
.A .B{}.A>.B{}.A+.B{}.A~.B{}AB{}, 选择这样的元素: <div class="A B"></div>A,B{}, 选择这样的元素 <div class="A"></div>和<div class="B"></div>