2022年1月

微信小程序和支付宝小程序对比

这儿有一个对比: https://blog.csdn.net/qq_32279193/article/details/109130401

在阿里提供的小程序开发平台中, 基于mPaaS的小程序开发中, 有uni-appremax两个选项.

其中uni-app是dcloud制作的基于vue的跨平台小程序发布, remax是基于react构建跨平台小程序的工具

支付宝小程序开发工具下载和介绍

基于 阿里的mPaaS https://help.aliyun.com/document_detail/49549.html
需要下载IDE
https://help.aliyun.com/document_detail/67444.html
https://help.aliyun.com/document_detail/172408.html

关于 uni-app

uni-app要做的是对小程序的一次开发到处运行. Dcloud做的, 理念我很喜欢, 就是官网有点儿山寨.
uni-app的项目结构和node项目/vue-cli创建的项目非常像, 都有package.json, node_modules/, 以及src/下的main.js, app.vue, 编译后都会生成到dist/目录.
然而并非支持vue的库或者其他npm库在这儿都可以用. 比如vuetify就用不了. 怎么判断能不能用,哪些能用, 在uni-app的论坛里讲到:
https://ask.dcloud.net.cn/article/35489
总之, vuetify必须得换成其他实现方式了.
uni-app又基于了另一个mpvue工具. mpvue是将vue移植到小程序平台上运行的库. megalo是另一个类似功能的库. 似乎uni-app都有支持.

病症分类

  • 膨出: 纤维环上起个包, 但髓核没有突破出来
  • 突出: 纤维环已破, 但髓核起来的包没有掉出来
  • 脱出: 突出基础上, 髓核掉了一块出来, 但还和纤维环里面的髓核保持一些连接
  • 游离: 脱出的髓核完全断裂, 在椎管里乱跑, 最后会附着在不知道什么地方
    一般都是左边或者右边起个包

引起症状

  • 坐骨神经痛. 坐骨神经被压迫所致
  • 下肢麻木. 不同轻重情况不同. 最多可麻到脚趾尖尖
  • 晨起疼痛加重,运动或过段时间减轻: 据百度百科某网友回答, 是因为晚上血液循环能力降低, 突出位置产生水肿, 晨起后血液循环回复, 消除了水肿.

治疗方法

  • 激素类: 促进水肿吸收, 减少对神经的压迫
  • 手术类: 切除髓核, 也分为多种手术方式
  • 理疗类: 按摩/针灸/正确坐姿/硬板卧床30天
  • 运动类: 桥/大小燕飞
  • 自体吸收: 有研究发现有少部分患者突出的髓核被吸收. 医生排除了突出的髓核被纳回的可能, 主要考虑是免疫系统将突出的髓核作为抗原消灭了.参考

原文https://pyserial.readthedocs.io/en/latest/tools.html
安装pyserial

python -m pip install pyserial

使用Miniterm工具

python -m serial.tools.miniterm COM5 115200

cmd命令行窗口支持ESC颜色

python -m serial.tools.miniterm COM5 115200 -f direct

远程不能直接使用vscode idf插件的命令行, 需要打开idf插件的配置,将其中的path等信息改成bat运行一下.
示例如下:

set path=D:\r\esp\.espressif\tools\xtensa-esp32-elf\esp-2021r2-8.4.0\xtensa-esp32-elf\bin;D:\r\esp\.espressif\tools\xtensa-esp32s2-elf\esp-2021r2-8.4.0\xtensa-esp32s2-elf\bin;D:\r\esp\.espressif\tools\xtensa-esp32s3-elf\esp-2021r2-8.4.0\xtensa-esp32s3-elf\bin;D:\r\esp\.espressif\tools\riscv32-esp-elf\esp-2021r2-8.4.0\riscv32-esp-elf\bin;D:\r\esp\.espressif\tools\esp32ulp-elf\2.28.51-esp-20191205\esp32ulp-elf-binutils\bin;D:\r\esp\.espressif\tools\esp32s2ulp-elf\2.28.51-esp-20191205\esp32s2ulp-elf-binutils\bin ;D:\r\esp\.espressif\tools\cmake\3.20.3\bin;D:\r\esp\.espressif\tools\openocd-esp32\v0.10.0-esp32-20211111\openocd-esp32\bin;D:\r\esp\.espressif\tools\ninja\1.10.2;D:\r\esp\.espressif\tools\idf-exe\1.0.3;D:\r\esp\.espressif\tools\ccache\4.3\ccache-4.3-windows-64;D:\r\esp\.espressif\tools\dfu-util\0.9\dfu-util-0.9-win64;D:\r\esp\.espressif\tools\idf-git\2.30.1\cmd\;D:\r\esp\.espressif\python_env\idf4.4_py3.8_env\Scripts\;%PATH%
set IDF_PATH=d:\r\esp\esp-idf\
set IDF_TOOLS_PATH=D:\r\esp\.espressif
Set OPENOCD_SCRIPTS=D:\\r\\esp\\.espressif\\tools\\openocd-esp32\\v0.10.0-esp32-20211111/openocd-esp32/share/openocd/scripts
set IDF_CCACHE_ENABLE=1

还有一种方式是在esp-idf下有一些export.bat/.sh文件, 是用于配置idf环境的,不过可能需要修改.

通过 SSHFS windows manager可以挂载远程硬盘. 参考文章

项目地址: https://github.com/tencentyun/qcloud-iot-esp-wifi/tree/master/qcloud-iot-esp8266-demo
文档地址: https://cloud.tencent.com/document/product/1081/48370
8266项目的SDK又是基于腾讯的C SDK抽取的, C SDK地址: https://github.com/tencentyun/qcloud-iot-explorer-sdk-embedded-c

要修改的和供调用的

8266项目目录下components/qcloud_iot/qcloud_iot_c_sdk/includes中的几个文件是要修改的参数和调用的api放置的地方.
还有三元组信息在 components/qcloud_iot/qcloud_iot_c_sdk/platform下的HAL_Device_freertos.c文件中. 居然不是在头文件里定义, 感觉挺糟糕的.

要调用的API: qcloud_iot_export.h

这个qcloud_iot_export.h文件实际上include了所有在exports目录里面的.h文件. 所有的接口API在其中描述.

参数修改 qcloud_iot_export_variables.h

MQTT的心跳时间240s建议改为200s. 默认240s与服务器端太一致了, 设备时钟慢一点就会被服务器踢出.

/* default MQTT keep alive interval (unit: ms) */
#define QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL (200 * 1000)  // 原设置: 240*1000

参数修改 HAL_Device_freertos.c


#ifdef DEBUG_DEV_INFO_USED
/* product Id  */
static char sg_product_id[MAX_SIZE_OF_PRODUCT_ID + 1] = "PRODUCT_ID"; // 修改

/* device name */
static char sg_device_name[MAX_SIZE_OF_DEVICE_NAME + 1] = "YOUR_DEV_NAME"; // 根据mac生成的话, 需要在代码中动态修改 . 似乎应该在HAL_SetDevInfo()调用前或者干脆就在这个函数里修改

/* device secret of PSK device */
static char sg_device_secret[MAX_SIZE_OF_DEVICE_SECRET + 1] = "YOUR_IOT_PSK";

/* region */
static char sg_region[MAX_SIZE_OF_PRODUCT_REGION + 1] = "ap-guangzhou";

#ifdef GATEWAY_ENABLED
/* sub-device product id  */
static char sg_sub_device_product_id[MAX_SIZE_OF_PRODUCT_ID + 1] = "PRODUCT_ID";
/* sub-device device name */
static char sg_sub_device_name[MAX_SIZE_OF_DEVICE_NAME + 1] = "YOUR_SUB_DEV_NAME";
#endif

#ifdef DEV_DYN_REG_ENABLED
/* product secret for device dynamic Registration  */
static char sg_product_secret[MAX_SIZE_OF_PRODUCT_SECRET + 1] = "YOUR_PRODUCT_SECRET";  // 动态验证需要改这儿
#endif

编译文件修改component.mk

如果在main目录下自定义了新的文件夹用于放源码, 需要将这个文件夹加入编译路径 ,在component.mk中, 如增加一行:

COMPONENT_SRCDIRS += ./lamploop

主流程

samples目录下有4个demo程序, 实际上是根据sdkconfig里面的配置选择不同的demo进行编译. 以data_template_light的主流程示例如下:

main.c中的app_main()创建qcloud_demo_task任务
-->演示获取WIFI信息
-->联网
-->设置时间服务器
-->调用主逻辑qcloud_iot_explorer_demo
-->qcloud_iot_explorer_demo在samples/data_template_light/light_data_template_sample.c中定义
-->初始化连接信息_setup_connect_init_params
-->构建物模型也就是所谓的数据模板IOT_Template_Construct
-->初始化数据模板_init_data_template
-->注册模板属性_register_data_template_property
-->注册模板动作_register_data_template_action
-->获取系统信息_get_sys_info, 并上报IOT_Template_Report_SysInfo_Sync
-->获取数据状态IOT_Template_GetStatus_sync
-->处理下行数据逻辑deal_down_stream_user_logic
-->使能ota任务enable_ota_task
-->初始化报告时钟
-->倒计时10秒
-->进入task的死循环
  -->断开连接(!IOT_Template_IsConnected)超过20秒直接跳出死循环
  -->判断固件在下载is_fw_downloading, 睡0.5秒后,进入下次循环
  -->收到控制消息sg_control_msg_arrived
    -->进入控制信令处理deal_down_stream_user_logic
    -->回复服务器IOT_Template_ControlReply
  -->构造变化属性的报告deal_up_stream_user_logic(pReportDataList, &ReportCount)
    -->如果属性有变化, 则构造报告数组IOT_Template_JSON_ConstructReportArray
    -->上传报告数组IOT_Template_Report
  -->如果开启了事件处理, 则进行事件处理.
  -->睡到1秒
-->如果各种原因跳出了循环(看到的原因只有断开连接)
-->关闭ota任务
-->销毁模板

根据乐鑫v3.1模板加入qcloud_iot模块的方式

下面基于乐鑫的v3.1example/project_template项目基础上, 增加qcloud_iot功能
为解说方便, 将qcloud-iot-esp8266-demo项目称之为qcloud项目, 将将project_template项目称之为template项目

复制

  1. 要将qcloud项目components\qcloud_iot\qcloud_iot_c_sdk目录复制到template项目的components\qcloud_iot_c_sdk,去掉一级目录qcloud_iot.
  2. components\component.mk复制过来
  3. sdkconfig复制过来, 或者进入make menuconfig手动修改. 主要要改component config-->SSL-->mbedTLS下的内容:
  • (2560) TLS maximum OUTPUT message content length
  • (2560) TLS maximum INPUT message content length
  • TLS Protocol Role (Client)--->
    在`TLS Key Exchange Methods`下修改:
  • [*] Enable pre-shared-key ciphersuites
  • [*] Enable PSK based ciphersuite modes
  • [ ] Enable DHE-PSK based ciphersuite modes
  • [ ] Enable ECDHE-PSK based ciphersuite modes
  • [*] Enable RSA-PSK based ciphersuite modes
  • [*] Enable RSA-only based ciphersuite modes
  • [ ] Enable DHE-RSA based ciphersuite modes

深入文档

要深入了解文档,还是要看C-SDK项目的示例而不是8266-C-SDK的项目示例
qcloud-iot-explorer-sdk-embedded-c
视频教程

OTA分片下载例程更新

https://git.code.tencent.com/hubertxxu/qcloud_iot_explorer_esp8266

参考https://code.visualstudio.com/docs/remote/ssh-tutorial

远程开发的机器称为服务器, 如果是win10的话, 要安装并运行sshd服务, 并且服务使用powershell作为命令行, 参见前一篇文章.
如果开发服务器在内网,可以使用中转穿透服务frp.
如果都装好了, 使用ssh也可以登录了, 如果是通过frp登录的话, 命令可能是ssh username@frp_server_ip -p 7777 -oPort=7000, 其中7777是frpc客户端(也就是开发服务器)要求frps服务端开启的端口, 7000是frps固有服务端口.
在vscode上点击左下角, 选择connect to host... 输入ssh命令, 不需要输入-oPort=7000部分,按说明还需要加一个-A(干吗用的还不知道), 如: ssh username@frp_server_ip -p 7777 -A
然后就可以连接成功了.

如果远程开发服务器上有docker容器, 可以在vscode的cmd窗口进入docker容器, 命令如下:
docker exec -it 容器名 /bin/bash
如果容器里面没有bash还可以把bash换成sh

有个很有意思的现象: 如果vscode连接的开发服务器必须同时开着sshdfrpc, 但是如果vscode已经连上了, sshd就可以关掉了, 只开着frpc就可以.这时候ssh命令行已经无法连接上了. 似乎vscode借用了22端口,但连接上以后没有走sshd协议

参考https://winaero.com/enable-openssh-server-windows-10/#:~:text=Enable%20the%20OpenSSH%20Server%20in%20Windows%2010%201,on%20the%20Install%20button.%205%20Restart%20Windows%2010.
https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse

  1. 打开"设置->应用->可选功能->添加功能", 搜索SSH, 将SSH服务器SSH客户端都安装.
  2. 文章要求装完重启,但好像不重启也可以. 在C:\windows\system32\OpenSSH目录下就会安装ssh的相关文件, 包括sshd.exe, 可以直接运行sshd就开启服务,也可以按文章的介绍在服务中开启.
  3. 运行菜单services.msc, 查看OpenSSH SSH server项, 双击, 选择登录页面,查看或修改可以登录的用户. 设置服务为自动并开启运行.
  4. 打开一个cmd,运行ssh-keygen -A生成服务端key, 默认生成的位置在C:\ProgramData\ssh. 这儿还存放sshd_config文件, 如果没有就创建一个.
  5. 运行ssh-keygen ,生成本地ssh keyid_rsaid_rsa.pub,位置在C:\Users\username/.ssh/,这儿还存放authorized_keys. authorized_keys就是其他客户端的pub key的集合
  6. 在客户端运行ssh-keygen ,生成本地ssh keyid_rsaid_rsa.pub, 将id_rsa.pub复制一份改名为authorized_keys到服务端的C:\Users\username/.ssh/目录
  7. 设置服务端开启哪个命令行, 可以在powershell里开启, 也可以打开regedit,
    New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force

    当然你也可以改成其他命令行如cmd或者bash,但是如果要使用vscode远程的话必须设置为如上的powershell

总结: 涉及的目录和注册表有以下几项:
软件安装目录: C:\windows\system32\OpenSSH
服务key存放目录: C:\ProgramData\ssh
用户Key存放目录: C:\Users\username/.ssh/
服务名: OpenSSH SSH Server, 或 sshd
注册表项: HKLM:\SOFTWARE\OpenSSH, DefaultShell

frp

官网示例是针对端口全开的公网ip, 而阿里云是有安全策略, 需要开放端口.

frp用起来算是及其简单惬意了.

  1. 阿里云服务器和内网SSH服务器分别下载frp release, 目前最新版本0.38.0
  2. 我用的阿里云服务器是CentOS, 其他linux也类似. 登录阿里云服务器这一步安装到systemd系统服务. 先进入frp解压目录, 然后是如下步骤:
    cp systemd/*.* /usr/lib/systemd/system/
    cp frps /usr/bin/
    chmod +x /usr/bin/frps
    mkdir /etc/frp
    cp frps.ini /etc/frp/
    systemctl start frps
    systemctl status frps

    最后一条命令可以看到frps是否启动成功.
    可以看一下cat /etc/frp/frps.ini的内容, 默认是:

    [common]
    bind_port = 7000

    这时候可以看一下frps的端口占用:

    #netstat -tunlp|grep frps
    tcp6       0      0 :::7000                 :::*                    LISTEN      4928/frps
  3. 登录阿里云服务器后台aliyun.com, 在ECS服务器->实例->安全组中的入方向添加一条TCP记录, 端口号写一个区间:7000/7100.后面frp配置的所有端口号都要在这个范围内才能访问. 授权对象为0.0.0.0/0
  4. SSH服务器解压缩frp, 修改目录中的frpc.ini,假设服务器地址是x.x.x.x,如下:
    
    [common]
    server_addr = x.x.x.x
    server_port = 7000

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 7001

运行`frpc`
5.这时候可以回到阿里云服务器看一下frps的端口占用,发现服务器打开了7001端口:

netstat -tunlp|grep frps

tcp6 0 0 :::7000 ::: LISTEN 4928/frps
tcp6 0 0 :::7001 :::
LISTEN 4928/frps

6. SSH客户端连接SSH服务器

ssh username@x.x.x.x -p 7001 -oPort=7000

https://zhuanlan.zhihu.com/p/303175108

本想用ngork, 然而注册总是不成功. 另一个natapp的页面看起来似乎运营实力一般,再加上免费版本有些限制, 所以就看了看其他的方式.
因为有个阿里云的服务器, 最后选择frp

行不通的方法

  1. 端口映射. 现在家庭和公司PPPoE获取的地址都已经是运营商的内部NAT地址了, 以100开头, 所以路由器端口映射行不通.

注意: 固件编译只能通过 docker进行.

原因是:

  1. 乐鑫的最新8266 RTOS SDK v3.4腾讯连连的components不支持;
  2. 腾讯连连只支持8266 RTOS SDK v3.1, 而8266 RTOS SDK v3.1的文档中竟然没有提编译工具链, 而v3.4文档中的编译工具链gcc版本v8.4.0也不支持v3.1
  3. 腾讯开发人员用的编译工具链gcc版本是v5.2.0(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

2023/6/15更新: yum命令的源用不了了, 可以换Aliyun的

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但却连不上IP...

使用了下面一长串的命令启动了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

1. C语言使用enum做顺序执行状态机的思考

最近做完了通过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;

2. C语言文件结构

.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 配置层