diff --git a/docs/API_reference/zh/stdlib/uzlib.md b/docs/API_reference/zh/stdlib/uzlib.md index 008ca54269d6b2b67add692c216a5f3d259014eb..c001c501178dd5f13d39040153c93736bdf4cc5f 100644 --- a/docs/API_reference/zh/stdlib/uzlib.md +++ b/docs/API_reference/zh/stdlib/uzlib.md @@ -1,4 +1,4 @@ -# class uhashlib - zlib解压缩 +# uzlib - zlib解压缩 该模块解压缩用[DEFLATE算法](https://en.wikipedia.org/wiki/DEFLATE)压缩二进制数据 (通常在zlib库和gzip存档器中使用)。该模块实现相应CPython模块的子集,更多信息请参阅CPython文档:[zlib](https://docs.python.org/3.5/library/zlib.html#module-zlib) diff --git a/docs/Application_guide/zh/Note.md b/docs/Application_guide/zh/Note.md deleted file mode 100644 index 935e06655945f9a5384a1f88bbbeb408935bff74..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/Note.md +++ /dev/null @@ -1,55 +0,0 @@ -# QuecPython 高级教程文档编写注意事项 - -## 目录结构 - -目录结构示例如下: - -``` -QuecPython官网文档中心/Application_guide -├── Note.md -├── README.md -├── bsp -│   ├── README.md -│   ├── gpio.md -│   ├── iic.md -│   ├── spi.md -│   └── uart.md -├── config.json -├── media -│   └── network -│   └── test.png -├── network -│   ├── README.md -│   ├── datacall.md -│   ├── http -│   │   ├── http_get.md -│   │   └── http_post.md -│   ├── mqtt.md -│   └── socket -│   ├── tcp_client.md -│   ├── tcp_server.md -│   └── udp.md -└── sidebar.yaml -``` - -## README.md - -README.md作为板块的首页入口,需要对该板块做综合性描述,并且在最后提供板块的所有二级目录链接。 - -## 正文注意事项 - -1. 若该技术板块存在多种应用场景,每种应用场景均需对应一篇文档,如socket编程,会存在 tcp client,tcp server 和 udp 三种不同场景,需编写3篇文档。 -2. 每篇文档按照常规的应用开发流程描述即可,可粘贴每个开发环节对应的代码片段,完整的代码由于篇幅过程,禁止粘贴在文档中,而是在文末给出代码链接。 -3. 代码链接必须指向[https://github.com/QuecPython](https://github.com/QuecPython)组织中的仓库,禁止将代码仓库放在任何其他组织中。代码仓库的建立,联系Chavis。 -4. 所有md文档中引用的图片,都应根据文档所属功能在media目录下新建合适的子目录,并将图片文件存放在该子目录下: - -* 图片格式统一为png格式; -* 图片命名符合见名知意的要求,如bsp.uart.xxx.png,其中`xxx`表示用简短英文编写的图片作用描述。 - -> 代码仓库的建立,联系Chavis。 - - - -## QuecPython 应用指导 - -[点此查看](./README.md) \ No newline at end of file diff --git a/docs/Getting_started/zh/background/README.md b/docs/Application_guide/zh/background/README.md similarity index 100% rename from docs/Getting_started/zh/background/README.md rename to docs/Application_guide/zh/background/README.md diff --git a/docs/Getting_started/zh/background/about-qpy.md b/docs/Application_guide/zh/background/about-qpy.md similarity index 99% rename from docs/Getting_started/zh/background/about-qpy.md rename to docs/Application_guide/zh/background/about-qpy.md index c786bc39e35433003cf5c95c8f2f1719d6541074..4b6539b84a08e7e4612a344030ba78e22e591f26 100644 --- a/docs/Getting_started/zh/background/about-qpy.md +++ b/docs/Application_guide/zh/background/about-qpy.md @@ -11,7 +11,7 @@ Python 是一个高层次的结合了解释性、编译性、互动性和面向 .. tabset:: 读取并显示键盘输入 ## 使用 C 语言 - + ```c #include @@ -22,17 +22,17 @@ Python 是一个高层次的结合了解释性、编译性、互动性和面向 scanf("%d", &number); scanf("%f", &decimal); scanf("%s", string); - + printf("%d\n", number); printf("%f\n", decimal); printf("%s\n", string); - + return 0; } ``` - + ## 使用 Python 语言 - + ```python number = int(input()) decimal = float(input()) @@ -46,7 +46,7 @@ Python 是一个高层次的结合了解释性、编译性、互动性和面向 .. tabset:: 简单的 for 循环 ## 使用 C 语言 - + ```c #include @@ -57,9 +57,9 @@ Python 是一个高层次的结合了解释性、编译性、互动性和面向 } } ``` - + ## 使用 Python 语言 - + ```python for i in range(0, 10): print(i) @@ -80,7 +80,7 @@ MicroPython 是 Python 语言的精简高效实现,可理解为一个可以运 .. details:: 关于解释器 解释器(Interpreter)是一个和编译器(Compiler)相对的概念。作为一种解释型语言,Python 的源码是在运行中(而非运行前)被转换为机器可识别和执行的二进制形式。实现这一流程的工具称为解释器。 - +

@@ -88,23 +88,23 @@ MicroPython 是 Python 语言的精简高效实现,可理解为一个可以运 解释型语言和编译型语言的执行流程差异
- + 对于初学者而言,解释器可以粗略地理解为 Python 脚本的运行环境。目前,在电脑端,CPython 是最为常用的 Python 解释器。绝大部分资料和工具中涉及的“Python”默认指的就是 CPython。在嵌入式领域,除了 MicroPython,CircuitPython、PikaPython 等解释器也受到国内开发者的欢迎。 - + 关于 Python 代码执行机制的更多讲解,可以参考 [编译器与解释器的区别和工作原理](https://zhuanlan.zhihu.com/p/39141067)、[浅谈字节码 + 虚拟机](https://blog.csdn.net/qq_39478403/article/details/106298611) 等在线资料,或是阅读 Python 的专业书籍。 .. details:: 关于 REPL REPL,全称 Read-Eval-Print Loop,即“读取-求值-输出”循环,是一种简单的交互式编程环境。REPL 通常会提供一个 CLI(Command-Line Interface,命令行界面),接收用户的输入,解析并执行后,再将结果返回给用户。从功能和使用方法上,它类似于 Windows 的命令提示符(CMD)或 macOS / Linux 的 Shell。 - +

Python 的 REPL 环境
-
- + + REPL 的优势在于可以使得探索性的编程和调试更加便捷,因为“读取-求值-输出”循环通常会比经典的“编辑-编译-运行-调试”模式要更加方便快捷。REPL 对于学习一门新的编程语言具有很大的帮助,因为它能立刻对初学者的尝试做出回应。
diff --git a/docs/Application_guide/zh/background/hardware-platform.md b/docs/Application_guide/zh/background/hardware-platform.md new file mode 100644 index 0000000000000000000000000000000000000000..92ca632888be2dc14d963525b6dd6175fd65f91b --- /dev/null +++ b/docs/Application_guide/zh/background/hardware-platform.md @@ -0,0 +1,141 @@ +# QuecPython 硬件平台支持 + +在前面的 [无线通信模块简介](./wireless-modules.md) 章节中,我们介绍了主芯片的概念。在实际应用中,基于同一厂商生产的同一系列芯片而制造的各种型号的模块往往具有相似的功能和特性,通常也使用着相同的开发工具和驱动程序。为了便于表述和分类,我们引入了平台(platform)的概念,用于代指这些基于相似的主芯片制造的不同型号的模块。 + +## 芯片原厂及产品介绍 + +目前,QuecPython 所支持的各个型号的蜂窝通信模块,其主芯片通常来自移芯、展锐、高通和 ASR 这四大厂商。 + +### 移芯通信(Eigencomm) + +上海移芯通信科技有限公司(Eigencomm)成立于 2017 年 2 月,是一家专注于蜂窝移动通信芯片及其软件的研发和销售的高科技企业,致力于打造世界上最好的蜂窝物联网芯片。公司团队在蜂窝通信芯片领域有着辉煌的历史和丰富的经验,曾参与过多个国际标准的制定和多款芯片的开发。在全球范围内,移芯通信是除了高通、海思、三星、MTK、展锐等世界级大公司之外,极少数有能力独立研发蜂窝通信芯片的公司之一。 + +#### LTE Cat.1 物联网芯片 EC618 + +EC618 是移芯通信推出的首颗 Cat.1bis 芯片,也是全球首款基带、射频、电源实现一体化设计的高集成度 Cat.1bis 芯片。EC618 内部集成了电源管理芯片,外围器件数量减少了 30% 以上,支持 WiFiScan,支持无外部 32K 晶体工作模式,以更低的成本满足客户多样化的功能需求。EC618 的尺寸仅有 6.1x6.1mm,是目前市场上最小的 Cat.1bis 芯片。EC618 的功耗表现也非常出色,PSM 功耗低至 1.3uA,连接态功耗下降了 50% 以上。EC618 采用了 Cortex-M3 双核架构,充分满足场景算力需求。EC618 还具备丰富的外设接口:UART,I2C,SPI,USB,PWM,ADC,PCM/I2S ,Keypad,OneWire 等适配 Cat.1 各种典型应用。 + +
+ +
+
+ EC618 芯片功能框图 +
+
+ +### 紫光展锐(Unisoc) + +紫光展锐(上海)科技有限公司是中国领先的集成电路设计企业,也是全球少数掌握 2G/3G/4G/5G、Wi-Fi、蓝牙、电视调频、卫星通信等全场景通信技术的企业之一。2018 年,展讯通信(上海)有限公司和锐迪科微电子有限公司合并成为紫光展锐,实现了强强联合,扛起了中国芯片设计产业的大旗。紫光展锐的崛起,将打破国外垄断,领跑 5G 芯片,成为中国芯片设计行业的标杆。 + +#### LTE Cat.1 物联网芯片 8910DM + +紫光展锐公司的 8910DM 芯片是一款 LTE Cat.1bis 物联网芯片,它采用了 28nm 成熟工艺,支持 LTE Cat.1bis 和 GSM 双模,上行速率达 5Mbps,下行速率达 10Mbps。此外,它还集成了蓝牙通讯和 Wi-Fi 室内定位,可实现更稳定的连接,支持 VoLTE,并通过系统优化设计实现显著的低功耗优势 。8910DM 的推出解决了目前物联网连接中的痛点,填补了低功耗窄带物联网与传统宽带物联网之间的蜂窝通信方案空白。其相比 NB-IoT、2G 模组在网络覆盖、速度和延时上具有优势,相比传统 LTE Cat.4 模组则拥有更低的成本和功耗,同时适配当前国内的 4G 网络,非常适用于对性价比、时延性、覆盖范围、通信速度有要求的应用场景。 + +
+ +
+
+ UIS8910DM 芯片功能框图 +
+
+ +#### LTE Cat.1 物联网芯片 V8850 + +紫光展锐公司的 V8850 芯片是业界首个融合室内外定位的安全可信 Cat.1bis 芯片。它在上一代产品基础上进行了智能化升级,具备高集成度更小尺寸、室内外融合定位、更强 Open CPU 能力、更低功耗、工规宽温、安全可信等六大亮点。V8850 高度集成了 BB/RF/PMIC/GNSS,拥有更小尺寸 8.2x8.2mm, 可使模组尺寸较上一代减小 30%, 最小可至 16x18mm,适合对于设备尺寸要求较高的更广泛应用场景。V8850 支持多种卫星定位系统(GPS/北斗/GLONASS/Galileo),并且通过与 Wi-Fi 室内定位技术融合,实现室内外无缝切换定位。 + +
+ +
+
+ UIS8850DG 芯片功能框图 +
+
+ +#### NB-IoT 芯片春藤 8908A + +紫光展锐公司的春藤 8908A 芯片平台是一款超低功耗、超大容量、超强覆盖的 NB-IoT 芯片解决方案。它采用 40nm 工艺,在定点场景和移动场景下的业务成功率、业务时延、功耗等关键性能上表现优异。频率范围覆盖 690~2200MHz 宽频段,可以适配全球主流运营商网络。春藤 8908A 支持多种电源管理模式,并且具有超低待机功耗,在 eDRX 模式下待机电流仅为 1.2uA。此外,春藤 8908A 还支持多种安全特性,包括硬件安全引擎、安全启动、安全调试和安全存储等。 + +
+ +
+
+ RDA8908A 芯片功能框图 +
+
+ +### 高通(Qualcomm) + +高通(Qualcomm)公司是一家全球领先的半导体和无线通信技术公司。公司总部位于美国加利福尼亚州圣地亚哥,成立于 1985 年。高通以其在移动通信领域的创新技术而闻名,是全球最大的无线通信芯片供应商之一。公司的产品包括移动处理器、调制解调器、射频前端、无线充电技术等,广泛应用于智能手机、平板电脑、物联网设备等各种移动终端和通信设备。高通也在 5G 技术研发和标准制定方面发挥重要作用,推动了全球移动通信的发展和创新。 + +#### 多模 LTE 物联网芯片 MDM9X05 + +以物联网应用及一系列可穿戴跟踪器提供支持可靠、优化的蜂窝连接为宗旨,高通推出 Qualcomm 9X05 LTE 调制解调器,其在单芯片上集成了支持蜂窝物联网产品及服务所需的关键创新,包括全球多模 LTE category M1(eMTC)和 NB2(NB-IoT)以及 2G/E-GPRS 连接、应用处理、地理定位、基于硬件的安全、云服务支持及配套开发者工具,同时内部集成高通第 9 代 GNSS 引擎系统,支持 GPS/GLONASS/BeiDou/Galileo。 + +
+ +
+
+ MDM9205 芯片功能框图 +
+
+ +### 翱捷科技(ASR) + +翱捷科技(ASR)是一家提供无线通信、超大规模芯片的平台型芯片企业。公司自设立以来一直专注于无线通信芯片的研发和技术创新,同时拥有全制式蜂窝基带芯片及多协议非蜂窝物联网芯片设计与供货能力,且具备提供超大规模高速 SoC 芯片定制及半导体 IP 授权服务能力。 + +公司各类芯片产品下游应用场景广阔,可应用于以手机、智能可穿戴设备为代表的消费电子市场及以智慧安防、智能家居、自动驾驶为代表的智能物联网市场。 + +#### LTE Cat.1 物联网芯片 ASR1603 + +ASR1603 是一款高性价比、超低功耗的多模蜂窝芯片,采用 22nm 工艺制造。与其前身相比,ASR1603 减少了 14% 的面积,降低了 20%~25% 的功耗,并丰富了外围接口。此外,ASR1603 还是一款高集成度的片上系统(SoC)设备,集成了应用程序处理子系统、通信子系统、音频编解码器和嵌入式 pSRAM + Flash,能够支持最紧凑的单芯片 LTE/GSM 多模数据模块、POC、POS 和其他物联网解决方案。 + +
+ +
+
+ ASR1603 芯片功能框图 +
+
+ +#### LTE Cat.1 物联网芯片 ASR1606 + +ASR1606 是 ASR160x 系列的新一代成员,兼容并继承了 ASR160x 系列软件基线及优势。它不仅针对物联网应用场景提供了更为丰富的外围接口,而且针对芯片集成度、整体性能、存储空间进行了进阶优化。ASR1606 采用 22nm 制程工艺,将 CPU、Modem 通信单元、射频、Codec 音频单元、pSRAM & Flash 存储单元以及 PMU 集成在单芯片 SoC 上,搭载 Cortex-R5 处理器,可强化中低速率应用场景及运行低功耗 RTOS 系统设备的处理性能,适用于环境监测、智慧城市、移动支付、共享经济、工业物联网等领域。 + +
+ +
+
+ ASR1606 芯片功能框图 +
+
+ +#### LTE Cat.4 移动智能终端芯片 ASR1803S + +ASR1803S 是一款基于先进工艺的 4G 基带射频一体化芯片,是集成了射频和内存的高集成度基带通信芯片。该芯片采用 22nm 先进工艺,支持 6 层 1 阶 PCB,拥有 450MHz~2.7G 的频宽,支持 RTOS 操作系统,所占内存小。同时该芯片集成有应用处理器(ARM Cortex-A7),可用于支持 Linux 环境下的各种应用程序,为客户不同产品开发提供灵活选择。ASR1803S 支持全新的动态电压调节技术,能有效降低工作电压,降低功耗,并且支持 QSPI NOR/NAND flash,令 boot 速度更快。该芯片可广泛应用于民用及工业与行业应用当中,如 POS 机、POC 对讲机、数据卡 Dongle、MiFi 终端以及各类水、电、气表的行业综合应用中。 + +
+ +
+
+ ASR1803 芯片功能框图 +
+
+ +## 模组型号与平台的对应关系 + +以下的一些表格展示了当前 QuecPython 支持的各种型号的模块与各个芯片厂家和芯片型号的对应关系。 + +### 移芯平台 + +![移芯平台硬件支持情况](../media/background/hardware-platform/modules_with_eigencomm_chip.png) + +### 展锐平台 + +![展锐平台硬件支持情况](../media/background/hardware-platform/modules_with_unisoc_chip.png) + +### 高通平台 + +![高通平台硬件支持情况](../media/background/hardware-platform/modules_with_qualcomm_chip.png) + +### ASR 平台 + +![ASR 平台硬件支持情况](../media/background/hardware-platform/modules_with_asr_chip.png) diff --git a/docs/Getting_started/zh/background/iot-and-low-code.md b/docs/Application_guide/zh/background/iot-and-low-code.md similarity index 100% rename from docs/Getting_started/zh/background/iot-and-low-code.md rename to docs/Application_guide/zh/background/iot-and-low-code.md diff --git a/docs/Application_guide/zh/audio/README.md b/docs/Application_guide/zh/background/project-dev.md similarity index 100% rename from docs/Application_guide/zh/audio/README.md rename to docs/Application_guide/zh/background/project-dev.md diff --git a/docs/Getting_started/zh/background/wireless-modules.md b/docs/Application_guide/zh/background/wireless-modules.md similarity index 83% rename from docs/Getting_started/zh/background/wireless-modules.md rename to docs/Application_guide/zh/background/wireless-modules.md index 54b90339aeb6c6b3b36689c81c766e92bcbde500..d56c0bf29625d6b79f791ab283821655c255fafb 100644 --- a/docs/Getting_started/zh/background/wireless-modules.md +++ b/docs/Application_guide/zh/background/wireless-modules.md @@ -1,4 +1,3 @@ - # 无线通信模块简介 QuecPython 是运行在无线通信模块上的开发框架。对于首次接触物联网开发的用户而言,无线通信模块可能是一个相对陌生的概念。本文主要针对无线通信和蜂窝网络本身,以及模块的概念、特性和开发方式进行简要的介绍。 @@ -12,9 +11,9 @@ QuecPython 是运行在无线通信模块上的开发框架。对于首次接触 .. details:: 关于通信 物联网无线通信的种类繁多,面向场景多样,因此在具体的功能结构和技术细节上存在较大的差异,但他们的基本流程和模式是类似的。为了便于理解,我们不妨使用人和人之间的通信进行类比。 - + 设想这样一个场景,在没有现代电子科技的情况下,位于两座相邻的山顶上的两人如何实现互相通信? - +

@@ -22,9 +21,9 @@ QuecPython 是运行在无线通信模块上的开发框架。对于首次接触 两座孤单的山头
- + 最简单直接的方法是互相喊叫,利用声音作为信息载体和空气作为传输媒介。但当两人的距离远到一定程度时,声音会衰减或被干扰,这种方法就不太可行了。此时,通过视觉信号进行交流是更好的方式。因此,两人可以使用彩色旗帜或火把作为信息载体和光线作为传输媒介来传递信息。不过,这一方法还有一个前提:双方都需要建立一种共识,了解并遵守不同的旗帜或者火光所代表的含义。这样,两人就可以在相距较远的山头实现低效但相对可靠的通信了。 - +

@@ -32,18 +31,18 @@ QuecPython 是运行在无线通信模块上的开发框架。对于首次接触 使用旗语互相交流
- + 这个简陋的双人通信流程包含了通信系统的基本要素:信源、信宿、信道、信息载体和编码规则。信源是产生和发送信息的一端,信宿是接收和处理信息的一端,信道是信号在信源和信宿之间传输的通道,信息载体是承载信息的物理量,编码规则是约定好的信息表示方式。 - + 现代通信同样包含类似的要素,只是更加复杂和高效。有过使用电话线进行拨号上网经历的用户对于“猫”(Modem,调制解调器)应该不会感到陌生。电话线及电话网络是被设计成用于传输 300 Hz 到 3400 Hz 的模拟音频信号的,并不适合直接传输网络数据。要使得计算机之间可以通过电话线实现双向通信,需要满足两个基本条件: - + - 通过操作系统和网卡,实现用户原始数据与可在网络设备间互相传输和辨识的标准数据形式之间的相互转换。这种通过规范化数据格式,使之符合某一标准或共识,从而提升信息传输效率和系统有效性的方法通常称为编码(Coding),在接收端则称为解码(Decoding)。 - 通过“猫”,实现数字信号与可通过电话线传输的模拟信号之间的相互转换。这种通过对原始信号进行各种转换,使之能借助不同的通信介质(或载体)进行传播的方法通常称为调制(Modulation),在接收端则称之为解调(Demodulation)。调制和解调的目的是为了适应信道的特性,克服信道的干扰,提高信号的传输质量。 - + 除了电话线之外,还有许多其他的通信介质,如无线电波、光纤、卫星等。不同的通信介质有不同的特点和优势,需要采用不同的编码和调制技术。例如,无线电波可以在空气中传播,不需要物理连接,但容易受到其他无线电波的干扰,需要采用频率复用、编码复用、正交复用等技术来分配和利用频谱资源。光纤可以传输高速高容量的光信号,具有低损耗、高带宽、抗干扰等优点,但需要特殊的光电转换设备,需要采用脉冲编码调制、相位调制、频移键控等技术来实现光与电之间的转换。卫星可以覆盖广阔的地区,实现远程通信,但需要高功率的发射器和接收器,需要采用多址接入、跳频扩频、正交相移键控等技术来实现卫星与地面之间的通信。 - + 通信技术是一门涉及多个学科领域的综合性科学,它包括信息论、信号处理、电路理论、微波技术、天线技术、网络技术等方面。当然,作为用户和普通开发者,我们无需了解太多细节,各类终端设备和通信模块已经帮助我们处理好了一切。我们需要关注的,只是具体通信方式的选择和使用方法。 - +

@@ -51,20 +50,20 @@ QuecPython 是运行在无线通信模块上的开发框架。对于首次接触 无线通信系统简略框图
- + > 需要注意的是,为降低入门难度,本节的讲解并不全面和严谨。对于无线通信原理以及各类物联网无线通信方式的具体内容,本文不做系统介绍。有兴趣的读者可以自行参考《通信原理》《无线通信原理与应用》《物联网》等教材进行自学。 不同的物联网应用场景,对于无线通信的需求存在很大差别。下表展示了当前常见的物联网无线通信技术的特性和使用场景差异。 -| 通信技术 | 速率 | 传输距离 | 功耗 | 成本 | 应用场景 | -| :------: | :--: | :------: | :--: | :--: | ------------------------------------------------------------ | -| Wi-Fi | 高 | 短 | 高 | 中 |
  • 室内无线上网
  • 智能家居
| +| 通信技术 | 速率 | 传输距离 | 功耗 | 成本 | 应用场景 | +| :------: | :--: | :------: | :--: | :--: | -------------------------------------------------------------------------------------------- | +| Wi-Fi | 高 | 短 | 高 | 中 |
  • 室内无线上网
  • 智能家居
| | 蓝牙 | 中 | 短 | 中 | 低 |
  • 资产追踪
  • 定位标签
  • 医疗传感器
  • 智能手表
| -| Zigbee | 低 | 短 | 低 | 低 |
  • 无线传感器
  • 智能家居
| -| UWB | 高 | 短 | 高 | 高 |
  • 高精度定位
| -| 4G | 高 | 长 | 高 | 高 |
  • 可移动通信
  • 车辆追踪
  • 智慧城市
| -| NB-IoT | 低 | 长 | 低 | 中 |
  • 远程抄表
  • 井盖检测
| -| LoRa | 低 | 长 | 低 | 中 |
  • 园区覆盖
  • 近海渔船检测
| +| Zigbee | 低 | 短 | 低 | 低 |
  • 无线传感器
  • 智能家居
| +| UWB | 高 | 短 | 高 | 高 |
  • 高精度定位
| +| 4G | 高 | 长 | 高 | 高 |
  • 可移动通信
  • 车辆追踪
  • 智慧城市
| +| NB-IoT | 低 | 长 | 低 | 中 |
  • 远程抄表
  • 井盖检测
| +| LoRa | 低 | 长 | 低 | 中 |
  • 园区覆盖
  • 近海渔船检测
| ### 蜂窝网络的概念 @@ -91,17 +90,17 @@ QuecPython 是运行在无线通信模块上的开发框架。对于首次接触 .. details:: 从 1G 到 5G 蜂窝网络最早由美国贝尔实验室在 1947 年提出,并在 1979 年在日本首次商用。最初的蜂窝网络只能提供模拟语音电话业务,被称为第一代(The 1st Generation,1G)移动通信系统。1G 移动通信系统采用频分多址接入(Frequency Division Multiple Access,FDMA)技术,将不同用户分配到不同频率的信道上。1G 移动通信系统虽然开创了移动通信的时代,但也存在很多缺点,如频谱利用率低、业务种类有限、无高速数据业务、保密性差以及设备成本高等。 - + 为了解决模拟系统中存在的问题,在 20 世纪 90 年代初,第二代(The 2nd Generation,2G)移动通信系统即数字移动通信技术出现了。2G 移动通信系统采用数字调制技术,并使用时分多址接入(Time Division Multiple Access,TDMA)或者码分多址接入(Code Division Multiple Access,CDMA)技术。2G 移动通信系统相比 1G 系统有了很大的改进,如系统容量、保密性和语音通话质量都大幅提升,并且开始提供数据业务。2G 移动通信系统最具代表性的是欧洲的全球移动通信系统(Global System for Mobile Communication,GSM),它使得全球范围内的漫游成为可能,并成为世界上最广泛使用的标准之一。 - + 随着互联网的发展,人们对数据业务的需求也越来越高。为了提高数据传输速率,在 2G 系统的基础上又出现了一些增强技术,如通用分组无线服务(General Packet Radio Service,GPRS)、增强型数据速率 GMS 演进(Enhanced Data for GSM Evolution,EDGE)等。这些技术被称为 2.5G 或 2.75G,它们在 2G 系统的基础上提供了更高的数据传输速率,但仍然不能满足人们对高速宽带数据业务的需求。 - + 2001 年,以数字多媒体移动通信为目的的第三代(The 3rd Generation,3G)移动通信系统进入商用阶段。3G 移动通信系统采用更先进的宽带码分多址技术(Wideband Code Division Multiple Access,WCDMA),并在更高频段使用更大的系统带宽进行数据发送,因而其数据传输速率得到进一步提升。3G 移动通信系统可以同时传输语音和数据信息,支持图像、音乐、视频等多媒体业务。3G 移动通信系统的主要代表有北美的 CDMA2000、欧洲和日本的 WCDMA、中国的时分同步的码分多址技术(Time Division-Synchronization Code Division Multiple Access,TD-SCDMA)等。这些技术都是基于国际电信联盟(International Telecommunication Union,ITU)制定的国际标准 IMT-2000(International Mobile Telecommunications 2000)。 - + 随着 3G 网络的发展,出现了一些增强技术,如高速下行分组接入(High Speed Downlink Packet Access,HSDPA)、高速上行分组接入(High Speed Uplink Packet Access,HSUPA)和增强型高速分组接入 (High-Speed Packet Access+,HSPA+)等。这些技术被称为 3.5G 或 3.75G,它们在 3G 系统的基础上提供了更高的数据传输速率和更好的用户体验。 - + 2011 年,以数字宽带数据移动互联网通信为目的的第四代(The 4th Generation,4G)移动通信系统正式发布。4G 移动通信系统基于扁平化网络架构设计,在 3G 的长期演进(Long Term Evolution,LTE)基础上进行升级。LTE 系统采用正交频分多址(Orthogonal Frequency-Division Multiple Access,OFDMA)、自适应调制编码(Adaptive Modulation and Coding,AMC)和多天线(Multiple-input Multiple-output,MIMO)等关键技术,大大提高了频谱效率和网络性能。4G 移动通信系统拥有非常高的数据传输速度,是 3G 网络的 50 倍以上,其视频图像的传输效果与高清电视相当。4G 移动通信系统最具代表性的是以时分双工(Time Division Duplexing)/频分双工(Frequency Division Duplexing,FDD)为工作模式的高级长期演进技术(Long Term Evolution Advanced,LTE-A)技术。LTE-A 技术在 LTE 技术的基础上采用了载波聚合(Carrier Aggregation,CA)、中继和多点协同传输(Coordinated Multiple Point,CoMP)等技术,在提高网络容量和覆盖范围方面有了突破性进展。 - + 随着智能终端、物联网、云计算等新兴技术和应用的快速发展,人们对移动通信的需求也越来越高。为了满足未来人类信息社会的需求,第五代(The 5th Generation,5G)移动通信系统应运而生。5G 移动通信系统不仅提供了更高的数据传输速率、更低的时延和更高的可靠性,还支持海量的连接和多样化的业务场景。当前,5G 移动通信系统已渗透到工业、医疗、交通等领域,与各种设备和物体深度融合,实现万物互联,为社会经济发展和人类生活质量提供强大的支撑。 蜂窝网络作为一种无线通信技术,从诞生至今已经经历了五代的发展,每一代都对应着不同的技术和标准,为用户提供了更高的数据传输速度,更好的语音通话质量,以及一系列新特性和新功能。从 1G 的模拟语音通信,到 5G 的全场景互联,蜂窝网络已经成为现代社会运转的必不可少的信息基础设施之一。 @@ -128,19 +127,19 @@ QuecPython 是运行在无线通信模块上的开发框架。对于首次接触 目前,在国内,物联网领域应用最多的蜂窝通信标准包括 LTE Cat.4,LTE Cat.1 和 NB-IoT。以下的表格对比了它们的特点。 -| 特点 | LTE Cat.4 | LTE Cat.1bis | NB-IoT | -| :------: | :----------------------------------------------------: | :----------------------------------------------------------: | :------------------------------------------------: | -| 下行速率 | 150 Mbps | 10.3 Mbps | 250 kbps | -| 上行速率 | 50 Mbps | 5.2 Mbps | 250 kbps | -| 网络覆盖 | 全球 LTE 网络 | 全球 LTE 网络 | 需要部署新的基站或升级现有基站 | -| 移动性 | 支持高速移动 | 支持 100 km/h 移动 | 不支持移动 | -| 延迟 | 毫秒级 | 毫秒级 | 秒级 | -| 成本 | 较高 | 较低 | 较低 | -| 功耗 | 较高 | 较低 | 较低 | +| 特点 | LTE Cat.4 | LTE Cat.1bis | NB-IoT | +| :------: | :----------------------------------------------------: | :------------------------------------------------------------------: | :------------------------------------------------: | +| 下行速率 | 150 Mbps | 10.3 Mbps | 250 kbps | +| 上行速率 | 50 Mbps | 5.2 Mbps | 250 kbps | +| 网络覆盖 | 全球 LTE 网络 | 全球 LTE 网络 | 需要部署新的基站或升级现有基站 | +| 移动性 | 支持高速移动 | 支持 100 km/h 移动 | 不支持移动 | +| 延迟 | 毫秒级 | 毫秒级 | 秒级 | +| 成本 | 较高 | 较低 | 较低 | +| 功耗 | 较高 | 较低 | 较低 | | 应用场景 | 高清视频监控、路由器、销售终端等需要高速数据传输的场景 | 共享支付、工业控制、车载支付、公网对讲、POS 等需要中速数据传输的场景 | 水表、电表、气表等需要低速数据传输且固定不动的场景 | -| 语音支持 | 支持 VoLTE(语音通话) | 支持 VoLTE(语音通话) | 不支持语音通话 | -| 天线配置 | 2x2 MIMO(多输入多输出) | 1x1 SISO(单输入单输出) | 1x1 SISO(单输入单输出) | -| 带宽 | 20 MHz | 1.4 MHz | 180 kHz | +| 语音支持 | 支持 VoLTE(语音通话) | 支持 VoLTE(语音通话) | 不支持语音通话 | +| 天线配置 | 2x2 MIMO(多输入多输出) | 1x1 SISO(单输入单输出) | 1x1 SISO(单输入单输出) | +| 带宽 | 20 MHz | 1.4 MHz | 180 kHz | ## 初识模块 @@ -207,7 +206,7 @@ QuecPython 是运行在无线通信模块上的开发框架。对于首次接触 .. details:: 关于 AT 指令 AT 指令是目前业界历史最悠久,使用领域最广泛的通讯指令集之一。它构建起了一套用户和模块间的完备的双向通信机制:用户(或 MCU)通过向模块发送 AT 指令,控制模块执行包括联网、通话、定位等在内的各类功能,模块则将执行结果和状态返回给用户。这种“一发一收”的机制和相对单一的处理方式非常适合在资源有限的嵌入式环境中使用。如今,市面上的绝大多数模块在出厂时都内置了 AT Server 程序,可以接收、解析和执行特定的 AT 指令。 - +

@@ -249,21 +248,21 @@ QuecPython 是运行在无线通信模块上的开发框架。对于首次接触 .. tabset:: LED 闪灯代码 ## 使用 QuecOpen(CSDK) - + ```c #include "ql_application.h" #include "ql_gpio.h" #include - - static quec_gpio_cfg_t led_gpio_cfg[] = + + static quec_gpio_cfg_t led_gpio_cfg[] = { {GPIO_PIN_NO_75, PIN_DIRECTION_OUT, PIN_NO_EDGE, PIN_PULL_DISABLE, PIN_LEVEL_LOW} }; - + static void led_test(void *argv) { ql_gpio_init(led_gpio_cfg[0].gpio_pin_num, led_gpio_cfg[0].pin_dir, led_gpio_cfg[0].pin_pull, led_gpio_cfg[0].pin_level); - + while (1) { ql_gpio_set_level(led_gpio_cfg[0].gpio_pin_num, PIN_LEVEL_LOW); @@ -272,25 +271,25 @@ QuecPython 是运行在无线通信模块上的开发框架。对于首次接触 ql_rtos_task_sleep_s(1); } } - + application_init(led_test, "led_test", 2, 2); ``` - + ## 使用 QuecPython - + ```python from machine import Pin import utime as time - + led = Pin(Pin.GPIO15, Pin.OUT, Pin.PULL_DISABLE, 0) - + def led_test(): while 1: led.write(0) time.sleep(1) led.write(1) time.sleep(1) - + if __name__ == "__main__": led_test() ``` @@ -301,14 +300,14 @@ QuecPython 是运行在无线通信模块上的开发框架。对于首次接触 下表比较了标准模式,CSDK 二次开发和脚本二次开发在多个维度的差异。用户可根据实际需求选择最适合自己的开发方式。 -| 特性 | 标准模式 | 二次开发(CSDK) | 二次开发(脚本语言) | 备注 | -| :------: | :------: | :--------------: | :------------------: | :----------------------------------------------------------- | -| 物料成本 | ⭐⭐ | ⭐ | ⭐ |
  • 二次开发时,可将模块作为系统主控使用,节省单片机部分的成本。
| -| 上手门槛 | ⭐ | ⭐⭐⭐ | ⭐ |
  • 模块厂商通常只面向大客户提供 CSDK 开发技术支持和其他相关服务。
| -| 开发难度 | ⭐ | ⭐⭐⭐ | ⭐ |
  • CSDK 开发需要开发者拥有 RTOS 或 Linux 开发经验,普通单片机开发者很难快速掌握。
  • 标准模式仅需用户掌握单片机串口通信和字符串处理方法,无特殊要求。
  • 脚本语言开发方式仅需用户掌握基础语法,无特殊要求。
| -| 开发周期 | ⭐ | ⭐⭐⭐ | ⭐ |
  • CSDK 开发的复杂度决定了其较长的开发周期和较高的开发成本。
| -| 维护成本 | ⭐⭐ | ⭐⭐⭐ | ⭐ |
  • 脚本语言的模块化开发的特性有助于保证其长期运行的稳定性,同时普遍内置了 OTA 功能,便于执行远程升级。
| -| 灵活性 | ⭐ | ⭐⭐⭐ | ⭐⭐ |
  • 标准模式下,开发者通常只能调用串口、ADC 等有限的功能。
  • 使用脚本语言开发时,开发者可以通过内置的功能库调用大部分的模块资源。
  • 使用 CSDK 开发时,开发者可以根据自身需求和想法直接控制所有可用资源。
| -| 性能 | - | ⭐⭐⭐ | ⭐⭐ |
  • 脚本语言的性能远低于 C 语言,因此在对于运行速度、时间精度(时序)、资源数量等要求较高的少部分场合,不建议采用脚本语言开发。
| -| 功耗 | ⭐ | ⭐⭐ | ⭐⭐ |
  • 一般的 4G 模块在自主休眠时的功耗水平在 mA 级别。
  • 在标准模式下,可以通过使用系统主控切断模块供电的方式实现最大限度的省电,最低功耗可达前者的 1/10。
| -| 生态系统 | ⭐⭐⭐ | ⭐ | ⭐⭐⭐ |
  • 标准 AT 指令开发和脚本开发在网络上都具有较多的公开资料和教程。
  • CSDK 开发通常不具备开放生态。
| +| 特性 | 标准模式 | 二次开发(CSDK) | 二次开发(脚本语言) | 备注 | +| :------: | :------: | :--------------: | :------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| 物料成本 | ⭐⭐ | ⭐ | ⭐ |
  • 二次开发时,可将模块作为系统主控使用,节省单片机部分的成本。
| +| 上手门槛 | ⭐ | ⭐⭐⭐ | ⭐ |
  • 模块厂商通常只面向大客户提供 CSDK 开发技术支持和其他相关服务。
| +| 开发难度 | ⭐ | ⭐⭐⭐ | ⭐ |
  • CSDK 开发需要开发者拥有 RTOS 或 Linux 开发经验,普通单片机开发者很难快速掌握。
  • 标准模式仅需用户掌握单片机串口通信和字符串处理方法,无特殊要求。
  • 脚本语言开发方式仅需用户掌握基础语法,无特殊要求。
| +| 开发周期 | ⭐ | ⭐⭐⭐ | ⭐ |
  • CSDK 开发的复杂度决定了其较长的开发周期和较高的开发成本。
| +| 维护成本 | ⭐⭐ | ⭐⭐⭐ | ⭐ |
  • 脚本语言的模块化开发的特性有助于保证其长期运行的稳定性,同时普遍内置了 OTA 功能,便于执行远程升级。
| +| 灵活性 | ⭐ | ⭐⭐⭐ | ⭐⭐ |
  • 标准模式下,开发者通常只能调用串口、ADC 等有限的功能。
  • 使用脚本语言开发时,开发者可以通过内置的功能库调用大部分的模块资源。
  • 使用 CSDK 开发时,开发者可以根据自身需求和想法直接控制所有可用资源。
| +| 性能 | - | ⭐⭐⭐ | ⭐⭐ |
  • 脚本语言的性能远低于 C 语言,因此在对于运行速度、时间精度(时序)、资源数量等要求较高的少部分场合,不建议采用脚本语言开发。
| +| 功耗 | ⭐ | ⭐⭐ | ⭐⭐ |
  • 一般的 4G 模块在自主休眠时的功耗水平在 mA 级别。
  • 在标准模式下,可以通过使用系统主控切断模块供电的方式实现最大限度的省电,最低功耗可达前者的 1/10。
| +| 生态系统 | ⭐⭐⭐ | ⭐ | ⭐⭐⭐ |
  • 标准 AT 指令开发和脚本开发在网络上都具有较多的公开资料和教程。
  • CSDK 开发通常不具备开放生态。
| diff --git a/docs/Application_guide/zh/bsp/gpio.md b/docs/Application_guide/zh/bsp/gpio.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/bsp/iic.md b/docs/Application_guide/zh/bsp/iic.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/bsp/media/media_SPI_1.jpg b/docs/Application_guide/zh/bsp/media/media_SPI_1.jpg deleted file mode 100644 index f9e636fa9fc329c164c2e2f42f31795b5c61abaf..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/bsp/media/media_SPI_1.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/bsp/media/media_SPI_2.jpg b/docs/Application_guide/zh/bsp/media/media_SPI_2.jpg deleted file mode 100644 index 7ac14f0715250687e91e68bf13d5b98b4efac075..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/bsp/media/media_SPI_2.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/bsp/media/media_SPI_3.jpg b/docs/Application_guide/zh/bsp/media/media_SPI_3.jpg deleted file mode 100644 index cf87c486c4832c74f3d20170f7cd64faf10d774a..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/bsp/media/media_SPI_3.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/bsp/media/media_SPI_4.jpg b/docs/Application_guide/zh/bsp/media/media_SPI_4.jpg deleted file mode 100644 index 66b9a7b203f332146efb947dc5c550a98c413dd5..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/bsp/media/media_SPI_4.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/bsp/media/media_SPI_5.jpg b/docs/Application_guide/zh/bsp/media/media_SPI_5.jpg deleted file mode 100644 index 88c9a0922849681691935bf954f22daf1123b45e..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/bsp/media/media_SPI_5.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/bsp/media/media_SPI_6.jpg b/docs/Application_guide/zh/bsp/media/media_SPI_6.jpg deleted file mode 100644 index 86e7fbfce741edf00face5e7bebed76fbf98c6b9..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/bsp/media/media_SPI_6.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/bsp/spi.md b/docs/Application_guide/zh/bsp/spi.md deleted file mode 100644 index f0a6de297c26bd37506c85f13381caa6305683dc..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/bsp/spi.md +++ /dev/null @@ -1,93 +0,0 @@ -# SPI通信 - -SPI,是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。是 Motorola 首先在其 MC68HCXX 列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。 - -本文档主要介绍如何使用 QuecPython_SPI 功能,通过本文你将了解到 QuecPython_SPI 的参数设置及使用方法。 - - - -## SPI 简介 - -### 主-从模式(Master-Slave) - -SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave)。一个 Master 设备可以通过提供 Clock 以及对 Slave 设备进行片选 (Slave Select) 来控制多个 Slave 设备,SPI 协议还规定 Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备,Slave 设备本身不能产生或控制 Clock,没有 Clock 则 Slave 设备不能正常工作。 - -![media_SPI_1](media/media_SPI_1.jpg) - - - -### 同步方式(Synchronous) - -Master 设备会根据将要交换的数据来产生相应的时钟脉冲(Clock Pulse),时钟脉冲组成了时钟信号(Clock Signal),时钟信号通过时钟极性 (CPOL) 和 时钟相位 (CPHA) 控制着两个 SPI 设备间何时数据交换以及何时对接收到的数据进行采样,来保证数据在两个设备之间是同步传输的。 - -![media_SPI_2](media/media_SPI_2.jpg) - - - -### 四种通信模式 - -**时钟极性 CPOL** 是指 SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号(即 SPI 通讯开始前、 NSS 线为高电平时 SCK 的状态)。 CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。 - - **时钟相位 CPHA** 是指数据的采样的时刻,当 CPHA=0 时, MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿” 被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿” 采样。 - -根据 **时钟极性 CPOL** 与 **时钟相位 CPHA** 的不同,SPI具有四种通信模式。 - -![media_SPI_3](media/media_SPI_3.jpg) - -![media_SPI_4](media/media_SPI_4.jpg) - -![media_SPI_5](media/media_SPI_5.jpg) - - - -### 数据交换(Data Exchanges) - -SPI 设备间的数据传输之所以又被称为数据交换,是因为 SPI 协议规定一个 SPI 设备不能在数据通信过程中仅仅只充当一个 "发送者(Transmitter)" 或者 "接收者(Receiver)"。在每个 Clock 周期内,SPI 设备都会发送并接收一个 bit 大小的数据,相当于该设备有一个 bit 大小的数据被交换了。 - -一个 Slave 设备要想能够接收到 Master 发过来的控制信号,必须在此之前能够被 Master 设备进行访问 (Access)。所以, Master 设备必须首先通过 SS/CS pin 对 Slave 设备进行片选,把想要访问的 Slave 设备选上,在数据传输的过程中,每次接收到的数据必须在下一次数据传输之前被采样。如果之前接收到的数据没有被读取,那么这些已经接收完成的数据将有可能会被丢弃,导致 SPI 物理模块最终失效。因此,在程序中一般都会在 SPI 传输完数据后,去读取 SPI 设备里的数据,即使这些数据(Dummy Data)在我们的程序里是无用的。 - -SPI只有主模式和从模式之分,没有读和写的说法。因为实质上每次SPI是主从设备在交换数据。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。 - - - -## SPI通信 - -### 实验代码 - -以下代码适合EC600x 开发板进行 SPI 自发自收通信测试。**实验前提:短接 SPI_MISO 与 SPI_MOSI 引脚**。 - -```python -# -*- coding: UTF-8 -*- -import utime -from machine import SPI -from machine import Pin - -# 屏蔽GNSS模块数据干扰. 由于EC600S/N的SPI_MISO与SPI_MOSI引脚还被复用为UART1. 开发板还连接GNSS模块L76K, 为了断开L76K吐数据对SPI通信的干扰, 需要添加下面两句代码. -gpio11 = Pin(Pin.GPIO11, Pin.OUT, Pin.PULL_PD, 0) # EC600S/EC600N使用 -gpio11.write(0) # EC600S/EC600N使用 - -w_data = "test" -r_data = bytearray(len(w_data)) -count = 10 # 运行次数 - -# 以下代码根据模块型号进行选择. -spi_obj = SPI(1, 0, 1) # EC600S/N使用 -# spi_obj = SPI(0, 0, 1) # EC600U使用 - -while count: - count -= 1 - utime.sleep(1) - ret = spi_obj.write_read(r_data, w_data, 100) - if ret == -1: - SPI_msg = "SPIReadError" - else: - SPI_msg = "SPIRead:{} running:{:0>2d}".format(r_data, count) - print(SPI_msg) -``` - - - -### 实验现象 - -![media_SPI_6](media/media_SPI_6.jpg) - diff --git a/docs/Application_guide/zh/bsp/uart.md b/docs/Application_guide/zh/bsp/uart.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/cloud/AliyunCloud.md b/docs/Application_guide/zh/cloud/AliyunCloud.md deleted file mode 100644 index 67a6a9538a310b300b98ff8033d0a6a76bfdf1d2..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/cloud/AliyunCloud.md +++ /dev/null @@ -1,240 +0,0 @@ -# 阿里云应用指导文档 - -## 1. 功能简介 - -基于MQTT协议连接到阿里云物联网平台,设备快速连云,支持“一机一密和“一型一密”两种认证方式。 - -## 2. 应用场景说明 - -通过阿里云物联网平台对同一产品下的设备进行管理,处理设备事件,完成消息转发,OTA升级等应用功能。 - -## 3. 功能应用流程 - -### 3.1. 阿里云物联网平台 - -详细文档请查看:https://help.aliyun.com/document_detail/145493.html - -#### 3.1.1.名称解释 - -连接三元组:三元组指的是ProductKey(产品标识)DeviceName(设备名)DeviceSecret(设备密钥),是设备与物联网平台建立连接时的认证信息。 - -一机一密:每个设备烧录其唯一的设备证书(ProductKey、DeviceName 和 DeviceSecret),当设备与物联网平台建立连接时,物联网平台对其携带的设备证书信息进行认证。 - -一型一密:同一产品下所有设备可以烧录相同产品证书(即 ProductKey 和 ProductSecret ),设备发送激活请求时,物联网平台进行产品身份确认,认证通过,下发该设备对应的 DeviceSecret - -#### 3.1.2.平台地址 - -- 阿里云:https://www.aliyun.com - - ![aliyun_1](../media/cloud/aliyun_1.png) - - 注册个人或者企业账号进行账号登录 - -#### 3.1.3.创建实例 - -- 进入物联网平台 - - ![aliyun_2.png](../media/cloud/aliyun_3.png) - -- 选择实例,根据需求选择,试用选择公共实例即可 - - ![image-20230322101737405](../media/cloud/aliyun_2.png) - -- 实例概览 - - ![image-20230322102046889](../media/cloud/aliyun_4.png) - -#### 3.1.4.创建产品 - -- 点击实例进行产品创建 - - ![image-20230322102235862](../media/cloud/aliyun_5.png) - -- 按需填写产品创建信息 - - ![image-20230322102434008](../media/cloud/aliyun_6.png) - -- 产品列表展示 - - ![image-20230322102516271](../media/cloud/aliyun_7.png) - -#### 3.1.5.查看产品信息 - -- 产品信息包含ProductKey 和 ProductSecret - - ![image-20230322144045597](../media/cloud/aliyun_14.png) - -#### 3.1.6.创建设备 - -- 选择产品进行设备创建 - - ![image-20230322102731999](../media/cloud/aliyun_8.png) - -- 添加设备 - - ![image-20230322102805164](../media/cloud/aliyun_9.png) - -- 手动编辑设备信息 - - ![image-20230322102932866](../media/cloud/aliyun_10.png) - -- 设备创建完成后处于未激活状态 - - ![image-20230322103049475](../media/cloud/aliyun_11.png) - -#### 3.1.7.查看设备信息 - -- 查看设备信息,设备信息包含ProductKey(产品标识)DeviceName(设备名)DeviceSecret(设备密钥) - - ![image-20230322103634903](../media/cloud/aliyun_13.png) - -### 3.2. QuecPython连接阿里云 - -QuecPython 官网地址:https://python.quectel.com - -#### 3.2.1.开发环境搭建 - -- 驱动安装 - - 驱动下载地址:https://python.quectel.com/download - - 选择对应平台的USB驱动进行安装 - - ![image-20230322110809659](../media/cloud/aliyun_15.png) - -- QPYcom 图形化工具下载 - - 应用调试基于此工具,下载地址:https://python.quectel.com/download - - ![image-20230322111017197](../media/cloud/aliyun_16.png) - -- 模组固件下载 - - 根据所用的模组型号选择固件下载后烧录,此文档调试选择EC600N CNLE进行演示。 - - ![image-20230322111312207](../media/cloud/aliyun_17.png) - -#### 3.2.2.设备调试 - -- 打开电脑设备管理器,查看端口 - - ![image-20230322114434665](../media/cloud/aliyun_18.png) - -- 选择 Quectel USB MI05 COM Port串口,使用QPYcom工具打开该串口 - - ![image-20230322114711697](../media/cloud/aliyun_19.png) - -- 查询SIM卡状态和拨号状态 - - API 使用以及说明查阅Wiki文档:https://python.quectel.com/wiki/#/ - - ```python - >>> import sim - >>> sim.getStatus() # 返回1表示SIM状态正常 - 1 - >>> import dataCall - >>> dataCall.getInfo(1,2) # 成功返回拨号信息 - (1, 0, [1, 0, '10.145.246.10', '211.138.180.2', '211.138.180.3'], [1, 0, '::', '::', '::']) - >>> - ``` - -#### 3.2.3.设备连云 - -阿里云API 使用以及说明查阅Wiki文档:[Aliyun](../../../API_reference/zh/cloudlib/aLiYun.md) - -- 导入阿里云API - - ```python - >>> from aLiYun import aLiYun - ``` - -- 创建aliyun连接对象 - - ```python - >>> from aLiYun import aLiYun - - >>> productKey = "a1YoiRfLSbV" - >>> DeviceName = "QuecPyhon_Dev" - >>> DeviceSecret = "a86aadbbe78de7b8248adfc09e458bb9" - >>> productSecre = None - >>> - >>> ali_obj = aLiYun(productKey, productSecre, DeviceName, DeviceSecret) - ``` - -- 注册事件回调函数 - - ```python - >>> def event_callback(topic, data): - ... print("aliyun callback recv: {}".format(data)) - ... - ... - ... - >>> ali_obj.setCallback(event_callback) - ``` - -- 设置连接参数并连接平台,state为0时表示连接成功,连接成功后调用start方法 - - ```python - >>> clientID = "Quecpython" - >>> clean_session = False - >>> keepAlive = 300 - >>> reconn = True - - >>> state = ali_obj.setMqtt(clientID, clean_session=False, keepAlive=300,reconn=True) - >>> state - 0 - >>> ali_obj.start() - ``` - - 云端查看设备状态,由创建时未激活状态变成在线状态 - - ![image-20230322133745959](media/cloud/AliyunCloud/aliyun_20.png) - -#### 3.2.4.订阅Topic - -- 根据平台订阅规则选择操作权限为订阅的Topic - - /a1YoiRfLSbV/${deviceName}/user/get:$ {deviceName}替换成我们的设备名称即可,例如"/a1YoiRfLSbV/QuecPyhon_Dev/user/get" - - ![image-20230322134239010](../media/cloud/aliyun_21.png) - - ```python - >>> ali_obj.subscribe("/a1YoiRfLSbV/QuecPyhon_Dev/user/get") - 0 - >>> - ``` - -#### 3.2.5.数据上行 - -- 发布主题消息到平台,Topic选择操作权限为发布的进行数据上报 - - ```python - >>> ali_obj.publish("/a1YoiRfLSbV/QuecPyhon_Dev/user/update", "Hello, Aliyun!") - True - >>> - ``` - - 云端查看设备上行消息 - - ![image-20230322135259133](../media/cloud/aliyun_23.png) - - ![image-20230322135342205](../media/cloud/aliyun_24.png) - -#### 3.2.6.数据下行 - -- 找到设备订阅的Topic,选择发布消息 - - ![image-20230322135552300](../media/cloud/aliyun_25.png) - - ![image-20230322135630333](../media/cloud/aliyun_26.png) - - 设备查看平台下行数据,通过我们注册的回调函数接收数据 - - ![image-20230322135957158](../media/cloud/aliyun_27.png) - -## 4. 注意事项 - -- 设备进行云连接时需确认网络状态,例如SIM卡是否能够注网,设备是否拨号成功 -- 确保所用模组包含阿里云连接API可供使用 -- Topic注意操作权限 - diff --git a/docs/Application_guide/zh/bluetooth/README.md b/docs/Application_guide/zh/dev-tools/QPYcom/README.md similarity index 100% rename from docs/Application_guide/zh/bluetooth/README.md rename to docs/Application_guide/zh/dev-tools/QPYcom/README.md diff --git a/docs/Getting_started/zh/qpycom/qpycom-dw.md b/docs/Application_guide/zh/dev-tools/QPYcom/qpycom-dw.md similarity index 80% rename from docs/Getting_started/zh/qpycom/qpycom-dw.md rename to docs/Application_guide/zh/dev-tools/QPYcom/qpycom-dw.md index b9962a1d3396e236418ce6a88350075bf77bccda..b46051816e36497ee86bfbad723a5490ee8f5fe3 100644 --- a/docs/Getting_started/zh/qpycom/qpycom-dw.md +++ b/docs/Application_guide/zh/dev-tools/QPYcom/qpycom-dw.md @@ -1,90 +1,90 @@ -# QPYcom 下载功能说明 - -## 下载固件脚本 - -用户选择'**下载**'按钮,可以实现本地python脚本文件批量上传以及模块烧录固件功能,操作步骤为: - -- 在工具下载页面左侧导航栏创建项目 - -- 点击选择固件从本地选择固件,可从官网下载对应模组的固件,注意选择固件的后缀有zip和bin两种 - -- 通过图示位置选择要下载的脚本,可以通过点击脚本文件进行指定删除 - -- 通过左键点击图中倒三角标识来切换下载脚本和下载固件的功能 - -- 如图所示: - -![](../media/qpycom/image40.png) - - -![](../media/qpycom/image41.png) - - - - -### 下载固件 - -- 工具进入下载页面,点击"创建"项目,新建要下载的固件。如图所示: - -![](../media/qpycom/image9.png) - - -- 选择固件,如图所示: - -![](../media/qpycom/image10.png) - - -- 左击下拉选择箭头,选择"下载固件",即"Download FW", 如图所示: - -![](../media/qpycom/image11.png) - -- 等待进度条完成,弹窗下载成功后即为固件下载成功 - - - -### 下载脚本 - -本地python脚本文件上传到模块有如下两种方法 - -**下载方法一:** - -image-2021081301 - -- **Step1:打开串口** - -首先选择模组的交互口,点击"打开串口"按钮 - -- **Step2:通过工具按钮下载** - -可以通过文件页面右侧上面的 "**+**","**-**" 按钮来上传和删除文件 - -- **Step3:通过拖拽形式下载** - -也可以通过拖拽的方式将文件页面左侧显示的本地文件直接拖拽到右侧模组中去(也可以拖拽文件夹) - -- **Step4:下载进度和结果** - -下载过程中会在状态栏显示下载文件名和下载进度 - -**下载方法二:** - -image-2021081301 - -- **Step1:创建项目** - -根据需求,创建用户项目(点击"创建"按钮),步骤同上文烧录固件 - -- **Step2:配置要下载的文件** - -选择需要下载到模块的用户脚本(在"用户脚本"区域通过右键菜单添加) - -- **Step3:设置下载模式** - -左击下拉选择箭头,选择"下载脚本",即"Download Script" - -- **Step4:开始下载脚本** - -点击"下载脚本"开始下载脚本,下载过程中有进度条提示 - - - +# QPYcom 下载功能说明 + +## 下载固件脚本 + +用户选择'**下载**'按钮,可以实现本地python脚本文件批量上传以及模块烧录固件功能,操作步骤为: + +- 在工具下载页面左侧导航栏创建项目 + +- 点击选择固件从本地选择固件,可从官网下载对应模组的固件,注意选择固件的后缀有zip和bin两种 + +- 通过图示位置选择要下载的脚本,可以通过点击脚本文件进行指定删除 + +- 通过左键点击图中倒三角标识来切换下载脚本和下载固件的功能 + +- 如图所示: + +![](../../media/dev-tools/qpycom/image40.png) + + +![](../../media/dev-tools/qpycom/image41.png) + + + + +### 下载固件 + +- 工具进入下载页面,点击"创建"项目,新建要下载的固件。如图所示: + +![](../../media/dev-tools/qpycom/image9.png) + + +- 选择固件,如图所示: + +![](../../media/dev-tools/qpycom/image10.png) + + +- 左击下拉选择箭头,选择"下载固件",即"Download FW", 如图所示: + +![](../../media/dev-tools/qpycom/image11.png) + +- 等待进度条完成,弹窗下载成功后即为固件下载成功 + + + +### 下载脚本 + +本地python脚本文件上传到模块有如下两种方法 + +**下载方法一:** + +image-2021081301 + +- **Step1:打开串口** + +首先选择模组的交互口,点击"打开串口"按钮 + +- **Step2:通过工具按钮下载** + +可以通过文件页面右侧上面的 "**+**","**-**" 按钮来上传和删除文件 + +- **Step3:通过拖拽形式下载** + +也可以通过拖拽的方式将文件页面左侧显示的本地文件直接拖拽到右侧模组中去(也可以拖拽文件夹) + +- **Step4:下载进度和结果** + +下载过程中会在状态栏显示下载文件名和下载进度 + +**下载方法二:** + +image-2021081301 + +- **Step1:创建项目** + +根据需求,创建用户项目(点击"创建"按钮),步骤同上文烧录固件 + +- **Step2:配置要下载的文件** + +选择需要下载到模块的用户脚本(在"用户脚本"区域通过右键菜单添加) + +- **Step3:设置下载模式** + +左击下拉选择箭头,选择"下载脚本",即"Download Script" + +- **Step4:开始下载脚本** + +点击"下载脚本"开始下载脚本,下载过程中有进度条提示 + + + diff --git a/docs/Getting_started/zh/qpycom/qpycom-ext.md b/docs/Application_guide/zh/dev-tools/QPYcom/qpycom-ext.md similarity index 89% rename from docs/Getting_started/zh/qpycom/qpycom-ext.md rename to docs/Application_guide/zh/dev-tools/QPYcom/qpycom-ext.md index e699639b75e889e6cc980eb519240d7aac8959b5..279072a92c74e40a84a81d84c8c16d786c2434fa 100644 --- a/docs/Getting_started/zh/qpycom/qpycom-ext.md +++ b/docs/Application_guide/zh/dev-tools/QPYcom/qpycom-ext.md @@ -1,53 +1,53 @@ -# QuecPython QPYcom扩展功能说明 - -## fota包制作流程 - -点击fota升级按钮后,会弹出两个子菜单,分别是ASR_FOTA 和 RDA_FOTA - -![](media/fota_1.png) - -**ASR_FOTA包制作** - -步骤1:选择需要做差分包的新旧固件包 - -步骤2:点击ok生成差分包,生成的差分包位置为folder指定目录,若未指定则在exes/ASR_Fotatools目录下 - - -**RDA_FOTA包制作** - -步骤1:选择需要做差分包的新旧固件包 - -步骤2: 点击ok生成差分包,实现原理参考[RDA_FOTA](https://python.quectel.com/doc/doc/sbs/zh/QuecPythonPlatform/FOTA_RDA.html), - -`注意事项:差分需要选择新旧固件包` - -**200A_FOTA包制作** - -步骤1:选择需要做差分包的新旧固件包 - -步骤2: 点击ok生成差分包 - -`注意事项:差分需要选择新旧固件包` - -## QPYcom在线升级 - -升级触发有两种方式 - -方式一: 用户点击'**帮助\--\>检查升级**'菜单会联机进行版本升级检测,如果有版本升级会弹窗升级选择页面 - -用户可选择升级、忽略本次升级、忽略本次弹窗等 - - -方式二: QuecPython官网上传新版本时会向用户推送最新版本的升级通知,同样,用户可选择升级、忽略本次升级、忽略本次弹窗等 - -如图所示: - -![软件更新](media/image45.png) - - -## 工具箱 - - - - - +# QuecPython QPYcom扩展功能说明 + +## fota包制作流程 + +点击fota升级按钮后,会弹出两个子菜单,分别是ASR_FOTA 和 RDA_FOTA + +![](../../media/dev-tools/qpycom/fota_1.png) + +**ASR_FOTA包制作** + +步骤1:选择需要做差分包的新旧固件包 + +步骤2:点击ok生成差分包,生成的差分包位置为folder指定目录,若未指定则在exes/ASR_Fotatools目录下 + + +**RDA_FOTA包制作** + +步骤1:选择需要做差分包的新旧固件包 + +步骤2: 点击ok生成差分包,实现原理参考[RDA_FOTA](https://python.quectel.com/doc/doc/sbs/zh/QuecPythonPlatform/FOTA_RDA.html), + +`注意事项:差分需要选择新旧固件包` + +**200A_FOTA包制作** + +步骤1:选择需要做差分包的新旧固件包 + +步骤2: 点击ok生成差分包 + +`注意事项:差分需要选择新旧固件包` + +## QPYcom在线升级 + +升级触发有两种方式 + +方式一: 用户点击'**帮助\--\>检查升级**'菜单会联机进行版本升级检测,如果有版本升级会弹窗升级选择页面 + +用户可选择升级、忽略本次升级、忽略本次弹窗等 + + +方式二: QuecPython官网上传新版本时会向用户推送最新版本的升级通知,同样,用户可选择升级、忽略本次升级、忽略本次弹窗等 + +如图所示: + +![软件更新](../../media/dev-tools/qpycom/image45.png) + + +## 工具箱 + + + + + diff --git a/docs/Getting_started/zh/qpycom/qpycom-gui.md b/docs/Application_guide/zh/dev-tools/QPYcom/qpycom-gui.md similarity index 79% rename from docs/Getting_started/zh/qpycom/qpycom-gui.md rename to docs/Application_guide/zh/dev-tools/QPYcom/qpycom-gui.md index 2a147c9d3b57636880ff1d71b364b125251e94af..71d401a901189be49f62337dec164b5884c8ea3d 100644 --- a/docs/Getting_started/zh/qpycom/qpycom-gui.md +++ b/docs/Application_guide/zh/dev-tools/QPYcom/qpycom-gui.md @@ -1,220 +1,220 @@ -# QuecPython QPYcom GUI页面介绍 - -## 工具获取 - -## 主界面功能 - -本章主要介绍QPYcom工具的主界面的主要功能说明,具体操作功能使用说明详见[扩展功能说明](./qpycom-ext.md) - -在上文步骤获取工具后,解压下载的QPYcom.zip文件,进入解压后的目录找到 QPYcom.exe 工具。 -通过该工具,我们可以将自己的python脚本文件下载到模块中,与模块进行命令行交互,同时该脚本工具也支持固件下载合并、升级检测等功能,用户可根据需求选择相关功能模块进行操作。 - -工具主要页面如下图所示: -1. 与模组交互页面 - -![](../media/qpycom/image16.png) - - -2. 操作模组文件页面 - -![](../media/qpycom/image17.png) - - -2. 固件及python脚本下载页面 - -![](../media/qpycom/image18.png) - - -3. 设置页面 - -![](../media/qpycom/image19.png) - -## 交互页面按钮 - -交互主界面相关按钮说明如下: - -![按钮说明](../media/qpycom/image24.png) - -- ![播放](../media/qpycom/image25.png) - 开启与模块间命令行交互 -- ![暂停](../media/qpycom/image26.png) - 暂停与模块间命令行交互 -- ![停止](../media/qpycom/image27.png) - 停止与模块间命令行交互 -- ![清除](../media/qpycom/image28.png) - 屏幕打印清除 -- ![打印时间](../media/qpycom/image29.png) - 显示打印时间 -- ![行号显示](../media/qpycom/image30.png) - 行号显示 -- ![主题切换](../media/qpycom/image31.png) - 主交互界面主题切换(黑色与白色主题切换) -- ![日志保存1](../media/qpycom/image32.png) - 交互日志保存 -- ![关键字搜索](../media/qpycom/image33.png) - 交互日志关键字搜索 -- ![设置](../media/qpycom/image34.png) - 模块日志设置、串口参数配置等选项设置 -- ![工具箱](../media/qpycom/image35.png) - 工具箱,可手动添加快捷指令(配置在config.ini中) -- ![配置工具箱](../media/qpycom/image36.png) - 配置工具箱按钮,可自行配置需要的命令,注意命令数量最大16条,命令名称长度最大16个字符 -- ![页面置顶](../media/qpycom/image37.png) - 置顶按钮,点击按钮使工具处于置顶状态 -- ![fota包制作](../media/qpycom/image38.png) - fota升级按钮用于制造fota包 - - -## 文件栏目 - -### 保存 - -- 文件栏目,有日志文件保存选项,点击'**保存**'用户可选择日志保存的路径。如图所示: - -![](../media/qpycom/image20.png) - - -文件栏目 - -- 用户点击保存按钮后,选择日志保存路径,如图所示: - -![](../media/qpycom/image21.png) - - -文件保存 - -### 退出 - -- 用户选择退出,则退出整个应用程序。 - -![](../media/qpycom/image22.png) - - -## 查看栏目 - -查看栏目包含【**交互命令行、文件浏览、下载固件/脚本、软件设置、语言切换、模块日志和下载日志保存查看**】功能。如图所示。 - -![](../media/qpycom/image23.png) - -### 交互命令行 - -用户依次点击'**查看\--\>交互命令行**'可进入交互主界面,在交互主界面可以通过交互窗口与模块进行手动输入交互,交互主界面说明如图所示: - -![](../media/qpycom/image14.png) - -### 文件浏览 - -用户选择'**查看\--\>文件浏览**',可以实现本地python文件与模块端python文件的上传、查看、添加、删除操作,如图所示: - -![](../media/qpycom/image39.png) - - -除了通过上传按钮上传文件之外,还可通过拖拽实现本地文件上传模块,需要选中左侧本地文件拖拽至右侧文件夹中实现文件上传功能,拖拽后状态栏会有下载进度显示,注意文件只可拖放至文件夹中,拖到错误的位置会提示相应错误,可以通过uos.mkdir()来新建文件夹然后拖拽本地文件来实现在在模块中新建文件夹的需求,注意文件只可拖拽至显示'+','-'的文件夹中,对于没有显示相应符号的文件夹,重新刷新即可显示。 - -### 下载固件脚本 - -用户选择'**下载**'按钮,可以实现本地python脚本文件批量上传以及模块烧录固件功能,操作步骤为: - -- 在左侧导航栏创建项目 - -- 点击选择固件从本地选择固件,固件位置为提供的SDK包中firmware目录下,注意选择固件只可选择zip文件 - -- 通过图示位置选择要下载的脚本,可以通过点击脚本文件进行指定删除 - -- 通过左键点击图中倒三角标识来切换下载脚本和下载固件的功能 - -- 用户选择固件和所需要的脚本文件后点击合并按钮生成量产文件,合并过程中,除进度条外所有控件皆不可用,注意下载过程中保持USB AT Port不被占用 - -- 注意V1.6之后的下载页面下载脚本改成以目录结构的形式下载到模块,取消原先的按钮,可通过右键点击的形式来增加文件和文件夹以及删除,也可通过拖放来将文件从本地拖到工具中,点击下载脚本下载到模块中。 - -如图所示: - -![](../media/qpycom/image40.png) - - -![](../media/qpycom/image41.png) - - -### 软件设置 - -用户选择'**查看\--\>软件设置**'栏目,可以进入软件设置界面,在软件设置界面,用户可以进行模块日志自动保存、单条日志保存大小限制、日志保存条数设置、串口参数配置(包括校验位、数据位、停止位、流控制)、mpy-cross加密工具路径设置,新增设置交互页面字体设置,且设置可保存持久生效,如图所示。 - -![](../media/qpycom/image44.png) - - - -### Language 栏目 - -- 用户选择**'查看\--\>Language'**栏目,可以进行软件语言切换,目前支持简体中文和英文两种语言的切换。 - -### 模块日志 - -- 用户选择**'查看\--\>模块日志'**,可以对模块日志进行查看以及选择模块日志新的保存路径。 - -### 下载日志 - -- 用户选择**'查看\--\>下载日志'**,可以对下载日志进行查看以及选择下载日志新的保存路径。 - -## 教程栏目 - -### 官方网站 - -- 用户选择'**教程\--\>官方网站**'可以访问我们QuecPython的官方网址 - -### 在线wiki - -- 用户选择 '**教程\--\>在线wiki**',可以访问我们QuecPython的wiki教程 - -### 在线教程 - -- 用户选择'**教程\--\>在线教程**',可以访问我们QuecPython的视频教程 - -### 在线交流 - -- 用户选择 '**教程\--\>在线交流**',可以访问我们QuecPython的在线社区 - -### 资料下载 - -- 用户选择'**教程\--\>资料下载**',可以访问我们QuecPython的资源下载 - -## 帮助 - -在帮助界面,用户可以进行版本更新检查,版本查阅,访问QuecPython官网及移远官网。 - -### 检查升级 - -用户选择'**帮助\--\>检查升级**'栏目,则软件会检查是否有新版本更新,如果没有新版本则会提示目前已经是最新版本,如果有新版本则会把更新内容及版本号提示用户。 - -- 如果是强制升级提醒,用户选择升级则会开始更新操作,用户选择取消则会直接退出应用程序; - -- 如果是忽略升级,是本次可忽略的文件,用户选择忽略则会在下次软件启动时重新提示用户更新,本次不再提示,用户选择升级则会开始更新操作; - -- 如果是忽略升级,是永久可忽略的文件,用户选择忽略则会在下次软件启动时不再重新提示用户更新,用户选择升级则会开始更新操作。 - -- 升级过程中会有进度条和升级日志显示,且在升级过程中无法对工具进行操作 - -如图所示: - -![软件更新](../media/qpycom/image45.png) - - - -### changelog - -- 用户选择**'帮助\--\>changelog'**,将会打开工具changelog,查看工具迭代功能点,changelog文件在工具根目录下。 - -### 使用指导 - -- 用户选择**'帮助\--\>使用指导**',将会打开本文档,查看工具的使用说明书。 - -### 关于Quectel移远 - -- 用户选择**'帮助\--\>关于Quectel移远'**,将会访问移远Quectel官网。 - -### 关于QuecPython - -- 用户选择**'帮助\--\>关于QuecPython'**,将会访问移远QuecPython官网。 - -### 版本 - -- 用户选择**'帮助\--\>版本'**,将会提示工具当前版本号。 +# QuecPython QPYcom GUI页面介绍 + +## 工具获取 + +## 主界面功能 + +本章主要介绍QPYcom工具的主界面的主要功能说明,具体操作功能使用说明详见[扩展功能说明](./qpycom-ext.md) + +在上文步骤获取工具后,解压下载的QPYcom.zip文件,进入解压后的目录找到 QPYcom.exe 工具。 +通过该工具,我们可以将自己的python脚本文件下载到模块中,与模块进行命令行交互,同时该脚本工具也支持固件下载合并、升级检测等功能,用户可根据需求选择相关功能模块进行操作。 + +工具主要页面如下图所示: +1. 与模组交互页面 + +![](../../media/dev-tools/qpycom/image16.png) + + +2. 操作模组文件页面 + +![](../../media/dev-tools/qpycom/image17.png) + + +2. 固件及python脚本下载页面 + +![](../../media/dev-tools/qpycom/image18.png) + + +3. 设置页面 + +![](../../media/dev-tools/qpycom/image19.png) + +## 交互页面按钮 + +交互主界面相关按钮说明如下: + +![按钮说明](../../media/dev-tools/qpycom/image24.png) + +- ![播放](../../media/dev-tools/qpycom/image25.png) + 开启与模块间命令行交互 +- ![暂停](../../media/dev-tools/qpycom/image26.png) + 暂停与模块间命令行交互 +- ![停止](../../media/dev-tools/qpycom/image27.png) + 停止与模块间命令行交互 +- ![清除](../../media/dev-tools/qpycom/image28.png) + 屏幕打印清除 +- ![打印时间](../../media/dev-tools/qpycom/image29.png) + 显示打印时间 +- ![行号显示](../../media/dev-tools/qpycom/image30.png) + 行号显示 +- ![主题切换](../../media/dev-tools/qpycom/image31.png) + 主交互界面主题切换(黑色与白色主题切换) +- ![日志保存1](../../media/dev-tools/qpycom/image32.png) + 交互日志保存 +- ![关键字搜索](../../media/dev-tools/qpycom/image33.png) + 交互日志关键字搜索 +- ![设置](../../media/dev-tools/qpycom/image34.png) + 模块日志设置、串口参数配置等选项设置 +- ![工具箱](../../media/dev-tools/qpycom/image35.png) + 工具箱,可手动添加快捷指令(配置在config.ini中) +- ![配置工具箱](../../media/dev-tools/qpycom/image36.png) + 配置工具箱按钮,可自行配置需要的命令,注意命令数量最大16条,命令名称长度最大16个字符 +- ![页面置顶](../../media/dev-tools/qpycom/image37.png) + 置顶按钮,点击按钮使工具处于置顶状态 +- ![fota包制作](../../media/dev-tools/qpycom/image38.png) + fota升级按钮用于制造fota包 + + +## 文件栏目 + +### 保存 + +- 文件栏目,有日志文件保存选项,点击'**保存**'用户可选择日志保存的路径。如图所示: + +![](../../media/dev-tools/qpycom/image20.png) + + +文件栏目 + +- 用户点击保存按钮后,选择日志保存路径,如图所示: + +![](../../media/dev-tools/qpycom/image21.png) + + +文件保存 + +### 退出 + +- 用户选择退出,则退出整个应用程序。 + +![](../../media/dev-tools/qpycom/image22.png) + + +## 查看栏目 + +查看栏目包含【**交互命令行、文件浏览、下载固件/脚本、软件设置、语言切换、模块日志和下载日志保存查看**】功能。如图所示。 + +![](../../media/dev-tools/qpycom/image23.png) + +### 交互命令行 + +用户依次点击'**查看\--\>交互命令行**'可进入交互主界面,在交互主界面可以通过交互窗口与模块进行手动输入交互,交互主界面说明如图所示: + +![](../../media/dev-tools/qpycom/image14.png) + +### 文件浏览 + +用户选择'**查看\--\>文件浏览**',可以实现本地python文件与模块端python文件的上传、查看、添加、删除操作,如图所示: + +![](../../media/dev-tools/qpycom/image39.png) + + +除了通过上传按钮上传文件之外,还可通过拖拽实现本地文件上传模块,需要选中左侧本地文件拖拽至右侧文件夹中实现文件上传功能,拖拽后状态栏会有下载进度显示,注意文件只可拖放至文件夹中,拖到错误的位置会提示相应错误,可以通过uos.mkdir()来新建文件夹然后拖拽本地文件来实现在在模块中新建文件夹的需求,注意文件只可拖拽至显示'+','-'的文件夹中,对于没有显示相应符号的文件夹,重新刷新即可显示。 + +### 下载固件脚本 + +用户选择'**下载**'按钮,可以实现本地python脚本文件批量上传以及模块烧录固件功能,操作步骤为: + +- 在左侧导航栏创建项目 + +- 点击选择固件从本地选择固件,固件位置为提供的SDK包中firmware目录下,注意选择固件只可选择zip文件 + +- 通过图示位置选择要下载的脚本,可以通过点击脚本文件进行指定删除 + +- 通过左键点击图中倒三角标识来切换下载脚本和下载固件的功能 + +- 用户选择固件和所需要的脚本文件后点击合并按钮生成量产文件,合并过程中,除进度条外所有控件皆不可用,注意下载过程中保持USB AT Port不被占用 + +- 注意V1.6之后的下载页面下载脚本改成以目录结构的形式下载到模块,取消原先的按钮,可通过右键点击的形式来增加文件和文件夹以及删除,也可通过拖放来将文件从本地拖到工具中,点击下载脚本下载到模块中。 + +如图所示: + +![](../../media/dev-tools/qpycom/image40.png) + + +![](../../media/dev-tools/qpycom/image41.png) + + +### 软件设置 + +用户选择'**查看\--\>软件设置**'栏目,可以进入软件设置界面,在软件设置界面,用户可以进行模块日志自动保存、单条日志保存大小限制、日志保存条数设置、串口参数配置(包括校验位、数据位、停止位、流控制)、mpy-cross加密工具路径设置,新增设置交互页面字体设置,且设置可保存持久生效,如图所示。 + +![](../../media/dev-tools/qpycom/image44.png) + + + +### Language 栏目 + +- 用户选择**'查看\--\>Language'**栏目,可以进行软件语言切换,目前支持简体中文和英文两种语言的切换。 + +### 模块日志 + +- 用户选择**'查看\--\>模块日志'**,可以对模块日志进行查看以及选择模块日志新的保存路径。 + +### 下载日志 + +- 用户选择**'查看\--\>下载日志'**,可以对下载日志进行查看以及选择下载日志新的保存路径。 + +## 教程栏目 + +### 官方网站 + +- 用户选择'**教程\--\>官方网站**'可以访问我们QuecPython的官方网址 + +### 在线wiki + +- 用户选择 '**教程\--\>在线wiki**',可以访问我们QuecPython的wiki教程 + +### 在线教程 + +- 用户选择'**教程\--\>在线教程**',可以访问我们QuecPython的视频教程 + +### 在线交流 + +- 用户选择 '**教程\--\>在线交流**',可以访问我们QuecPython的在线社区 + +### 资料下载 + +- 用户选择'**教程\--\>资料下载**',可以访问我们QuecPython的资源下载 + +## 帮助 + +在帮助界面,用户可以进行版本更新检查,版本查阅,访问QuecPython官网及移远官网。 + +### 检查升级 + +用户选择'**帮助\--\>检查升级**'栏目,则软件会检查是否有新版本更新,如果没有新版本则会提示目前已经是最新版本,如果有新版本则会把更新内容及版本号提示用户。 + +- 如果是强制升级提醒,用户选择升级则会开始更新操作,用户选择取消则会直接退出应用程序; + +- 如果是忽略升级,是本次可忽略的文件,用户选择忽略则会在下次软件启动时重新提示用户更新,本次不再提示,用户选择升级则会开始更新操作; + +- 如果是忽略升级,是永久可忽略的文件,用户选择忽略则会在下次软件启动时不再重新提示用户更新,用户选择升级则会开始更新操作。 + +- 升级过程中会有进度条和升级日志显示,且在升级过程中无法对工具进行操作 + +如图所示: + +![软件更新](../../media/dev-tools/qpycom/image45.png) + + + +### changelog + +- 用户选择**'帮助\--\>changelog'**,将会打开工具changelog,查看工具迭代功能点,changelog文件在工具根目录下。 + +### 使用指导 + +- 用户选择**'帮助\--\>使用指导**',将会打开本文档,查看工具的使用说明书。 + +### 关于Quectel移远 + +- 用户选择**'帮助\--\>关于Quectel移远'**,将会访问移远Quectel官网。 + +### 关于QuecPython + +- 用户选择**'帮助\--\>关于QuecPython'**,将会访问移远QuecPython官网。 + +### 版本 + +- 用户选择**'帮助\--\>版本'**,将会提示工具当前版本号。 diff --git a/docs/Getting_started/zh/qpycom/qpycom-merge.md b/docs/Application_guide/zh/dev-tools/QPYcom/qpycom-merge.md similarity index 89% rename from docs/Getting_started/zh/qpycom/qpycom-merge.md rename to docs/Application_guide/zh/dev-tools/QPYcom/qpycom-merge.md index 79f25ab67131c679f42aed8f10e08b72eb9e2fd0..ff4c64c5fabffd7142413c5608de5453aed29c60 100644 --- a/docs/Getting_started/zh/qpycom/qpycom-merge.md +++ b/docs/Application_guide/zh/dev-tools/QPYcom/qpycom-merge.md @@ -1,78 +1,78 @@ -# QuecPython 固件脚本合并 - -## 固件脚本合并 - -用户选择'**下载**'页面,可以实现本地python脚本文件与固件合并生成量产文件功能,操作步骤为: - -- 在左侧导航栏创建项目 - -- 点击选择固件从本地选择固件 - -- 通过图示位置选择要合并的脚本,可以通过点击脚本文件进行指定删除,可以一键导入整个目录结构来实现合并具有复杂目录结构的项目 - -- 用户选择加密和备份按钮来实现备份和加密功能 - -如图所示: - -![](../media/qpycom/image46.png) - -- 点选备份后(bak/usr区要为空或者不存在),usr自动导入到bak/usr下,如下图所示: - -![](../media/qpycom/image47.png) - -- 在此之后再在usr下添加文件,文件只会存在usr区,不会从bak/usr目录下恢复到usr;再在bak下添加文件,文件只会存在bak区,不会从bak/usr目录下恢复到usr,如图所示: - -![](../media/qpycom/image48.png) - -- ASR固件合并,可选择 .zip 和 .bin 后缀(QPYcom工具2.9版本添加) - -![](../media/qpycom/suffix.png) - -- RDA固件合并,后缀为pac的固件可选择设置 usr和bak分区大小(新固件才可,若不支持并且有勾选则会仅显示大小,不支持修改; 工具2.9版本添加) - -![](../media/qpycom/size_conf.png) - - -- 注: - - 1. 备份按钮未勾选,usr区和 bak下非usr部分会合并到固件,且均不备份 - 2. 文件只有同时在 usr 和 bak/usr 并勾选了备份才会备份 - - ## 代码加密 - -### Python脚本加密概述 - -工具可以在合并的固件的过程中将待合入的py脚本加密成mpy文件以达到代码安全的要求 - -加密后的mpy文件不可读但是仍然可以通过导入库的形式来使用,但是无法通过工具的执行按钮和来运行 - -工具在加密脚本文件的过程中会避开main.py, 应用程序的编写规范一般是将main.py作为入口文件来调用其他代码 - -如:user.py加密后,就会变成user.mpy,我们可以直接用user.mpy代替原有的user.py文件,使用起来和原来一样。 - -### 加密操作步骤 - -1. 按照上文固件脚本合并的过程,在选择好要合并的固件和脚本之后,通过勾选加密按钮开开启加密功能 - -2. 勾选加密之后点击合并按钮,在合并前会对已选择的脚本进行加密操作,最后合并到固件中的脚本文件均为mpy文件,此外,在合并完成后可以在工具目录下的mpy文件夹中找到本次合并过程中生成的mpy文件 - - -## 调整镜像分区 - -### 调整用户区备份区大小 - -1. 按照上文固件脚本合并的过程,在选择好要合并的固件和脚本之后,通过勾选size自配按钮开开启调整镜像分区功能,可以动态调配用户文件系统和备份文件系统的大小 - -2. 勾选之后点击合并按钮开始合并,合并过程中工具会弹窗用于配置分区大小,在弹窗中调整好镜像分区大小之后,点击ok按钮生成新固件 - - ![](../media/qpycom/set_size.png) - -### 合并外置分区 - -1. 用户在合并固件制作文件系统时除了用户分区和备份分区以外需要增加外置分区的镜像时,可以通过选择此功能来实现 -2. 勾选下载页面的 `外置分区` 复选框,此时工具的用户脚本页面的目录会增加一个`ext` 目录 -3. 在该目录下增加文件再通合并流程即可完成包含外置分区的固件合并 - -![](../media/qpycom/QPYcom_ext.png) - +# QuecPython 固件脚本合并 + +## 固件脚本合并 + +用户选择'**下载**'页面,可以实现本地python脚本文件与固件合并生成量产文件功能,操作步骤为: + +- 在左侧导航栏创建项目 + +- 点击选择固件从本地选择固件 + +- 通过图示位置选择要合并的脚本,可以通过点击脚本文件进行指定删除,可以一键导入整个目录结构来实现合并具有复杂目录结构的项目 + +- 用户选择加密和备份按钮来实现备份和加密功能 + +如图所示: + +![](../../media/dev-tools/qpycom/image46.png) + +- 点选备份后(bak/usr区要为空或者不存在),usr自动导入到bak/usr下,如下图所示: + +![](../../media/dev-tools/qpycom/image47.png) + +- 在此之后再在usr下添加文件,文件只会存在usr区,不会从bak/usr目录下恢复到usr;再在bak下添加文件,文件只会存在bak区,不会从bak/usr目录下恢复到usr,如图所示: + +![](../../media/dev-tools/qpycom/image48.png) + +- ASR固件合并,可选择 .zip 和 .bin 后缀(QPYcom工具2.9版本添加) + +![](../../media/dev-tools/qpycom/suffix.png) + +- RDA固件合并,后缀为pac的固件可选择设置 usr和bak分区大小(新固件才可,若不支持并且有勾选则会仅显示大小,不支持修改; 工具2.9版本添加) + +![](../../media/dev-tools/qpycom/size_conf.png) + + +- 注: + + 1. 备份按钮未勾选,usr区和 bak下非usr部分会合并到固件,且均不备份 + 2. 文件只有同时在 usr 和 bak/usr 并勾选了备份才会备份 + + ## 代码加密 + +### Python脚本加密概述 + +工具可以在合并的固件的过程中将待合入的py脚本加密成mpy文件以达到代码安全的要求 + +加密后的mpy文件不可读但是仍然可以通过导入库的形式来使用,但是无法通过工具的执行按钮和来运行 + +工具在加密脚本文件的过程中会避开main.py, 应用程序的编写规范一般是将main.py作为入口文件来调用其他代码 + +如:user.py加密后,就会变成user.mpy,我们可以直接用user.mpy代替原有的user.py文件,使用起来和原来一样。 + +### 加密操作步骤 + +1. 按照上文固件脚本合并的过程,在选择好要合并的固件和脚本之后,通过勾选加密按钮开开启加密功能 + +2. 勾选加密之后点击合并按钮,在合并前会对已选择的脚本进行加密操作,最后合并到固件中的脚本文件均为mpy文件,此外,在合并完成后可以在工具目录下的mpy文件夹中找到本次合并过程中生成的mpy文件 + + +## 调整镜像分区 + +### 调整用户区备份区大小 + +1. 按照上文固件脚本合并的过程,在选择好要合并的固件和脚本之后,通过勾选size自配按钮开开启调整镜像分区功能,可以动态调配用户文件系统和备份文件系统的大小 + +2. 勾选之后点击合并按钮开始合并,合并过程中工具会弹窗用于配置分区大小,在弹窗中调整好镜像分区大小之后,点击ok按钮生成新固件 + + ![](../../media/dev-tools/qpycom/set_size.png) + +### 合并外置分区 + +1. 用户在合并固件制作文件系统时除了用户分区和备份分区以外需要增加外置分区的镜像时,可以通过选择此功能来实现 +2. 勾选下载页面的 `外置分区` 复选框,此时工具的用户脚本页面的目录会增加一个`ext` 目录 +3. 在该目录下增加文件再通合并流程即可完成包含外置分区的固件合并 + +![](../../media/dev-tools/qpycom/QPYcom_ext.png) + > 注意: 只有支持外置分区的定制固件可支持外置分区合并功能 \ No newline at end of file diff --git a/docs/Getting_started/zh/qpycom/qpycom-repl.md b/docs/Application_guide/zh/dev-tools/QPYcom/qpycom-repl.md similarity index 95% rename from docs/Getting_started/zh/qpycom/qpycom-repl.md rename to docs/Application_guide/zh/dev-tools/QPYcom/qpycom-repl.md index c1cadef9b1d110f2a85df55314bf9aa5f6c5e2df..aa1cebe4bec68b150aa32f3db1a556f1ba12c0d2 100644 --- a/docs/Getting_started/zh/qpycom/qpycom-repl.md +++ b/docs/Application_guide/zh/dev-tools/QPYcom/qpycom-repl.md @@ -1,59 +1,59 @@ -# QuecPython REPL调试 - -## REPL调试 - - *REPL*意为读取-求值-打印-循环(Read Evaluate Print Loop),是交互式提示的名称,可以通过在串口工具上访问交互式终端来进行REPL交互 - -在调试模组和设备的过程中,需要和模组进行通讯来执行指令进行调试,这个时候就需要用的REPL调试 - -通过REPL调试串口,可以输入任意的Python代码,并查看即时结果,这种编码方式有助于了解与实验 API 和库,并以交互方式开发要包含在项目中的工作代码,有助于提示开发和调试的效率。 - -下图为Python的REPL调试窗口: - -![](../media/qpycom/repl_py.png) - -QuecPython的 REPL调试需要通过串口来交互,连接QuecPython模组的交互口即可开始REPL交互。 - -## 开启REPL调试 - -只需将已烧录QuecPython固件的模组连接电脑,通过串口工具连接交互口即可开始REPL串口交互调试 - -不同的串口工具连接串口的步骤不一致,以QPYcom为例, - -![](../media/qpycom/repl_qpycom.png) - -1. 打开QPYcom工具,在交互页面选择模组的交互口并点击 `OPEN` 按钮 -2. 工具的`REPL`页面,页面上会有有 `>>>`字符,在页面输入回车,会换行并出现新的`>>>`字符 -3. 在页面输入Python代码即可进行调试,输入完指令后在按下回车后指令会输入到模组中执行并返回执行的结果 - -## 关闭REPL调试 - -当项目进入量产阶段时,出于设备的稳定运行和代码安全方面的考虑需要关闭设备的REPL调试功能,限制从串口访问设备的操作或者关闭交互串口的通讯,如果出厂后有返厂测试的需求只有通过特定的方式才能重新打开REPL调试 - -有以下两种方案 - -- 方案一:**使用UART接口** - -通过`UART`接口将 **USB CDC PORT** 初始化成串口 - -```python ->>> # 创建uart对象 ->>> from machine import UART ->>> uart1 = UART(UART.UART3, 115200, 8, 0, 1, 0) -``` - -完成初始化后,此时通过 **USB CDC PORT** 输入的数据将不再被REPL解析,通过该方式可以达到关闭交互口的效果,此时通过交互口输入的数据将作为串口数据被解析,使用教程参考[UART API使用手册](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.UART.html) - -如果有关闭再重新打开的需求,使用`close`接口关闭上文创建的uart对象即可恢复REPL调试串口,以上方法通常用在产测流程中,需要在串口数据解析中增加对特定数据格式的数据进行解析,已达到指定方式可以重新打开REPL调试的串口的需求 - -- 方案二:**使用system接口** - -使用`system`接口开启交互保护,设置开启交互保护后串口输入的所有外部指令以及代码都无法执行 - -```python -system.replSetEnable(flag,**kw_args) -``` - -开启后可以通过启动时设置的密码来关闭交互保护 - +# QuecPython REPL调试 + +## REPL调试 + + *REPL*意为读取-求值-打印-循环(Read Evaluate Print Loop),是交互式提示的名称,可以通过在串口工具上访问交互式终端来进行REPL交互 + +在调试模组和设备的过程中,需要和模组进行通讯来执行指令进行调试,这个时候就需要用的REPL调试 + +通过REPL调试串口,可以输入任意的Python代码,并查看即时结果,这种编码方式有助于了解与实验 API 和库,并以交互方式开发要包含在项目中的工作代码,有助于提示开发和调试的效率。 + +下图为Python的REPL调试窗口: + +![](../../media/dev-tools/qpycom/repl_py.png) + +QuecPython的 REPL调试需要通过串口来交互,连接QuecPython模组的交互口即可开始REPL交互。 + +## 开启REPL调试 + +只需将已烧录QuecPython固件的模组连接电脑,通过串口工具连接交互口即可开始REPL串口交互调试 + +不同的串口工具连接串口的步骤不一致,以QPYcom为例, + +![](../../media/dev-tools/qpycom/repl_qpycom.png) + +1. 打开QPYcom工具,在交互页面选择模组的交互口并点击 `OPEN` 按钮 +2. 工具的`REPL`页面,页面上会有有 `>>>`字符,在页面输入回车,会换行并出现新的`>>>`字符 +3. 在页面输入Python代码即可进行调试,输入完指令后在按下回车后指令会输入到模组中执行并返回执行的结果 + +## 关闭REPL调试 + +当项目进入量产阶段时,出于设备的稳定运行和代码安全方面的考虑需要关闭设备的REPL调试功能,限制从串口访问设备的操作或者关闭交互串口的通讯,如果出厂后有返厂测试的需求只有通过特定的方式才能重新打开REPL调试 + +有以下两种方案 + +- 方案一:**使用UART接口** + +通过`UART`接口将 **USB CDC PORT** 初始化成串口 + +```python +>>> # 创建uart对象 +>>> from machine import UART +>>> uart1 = UART(UART.UART3, 115200, 8, 0, 1, 0) +``` + +完成初始化后,此时通过 **USB CDC PORT** 输入的数据将不再被REPL解析,通过该方式可以达到关闭交互口的效果,此时通过交互口输入的数据将作为串口数据被解析,使用教程参考[UART API使用手册](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.UART.html) + +如果有关闭再重新打开的需求,使用`close`接口关闭上文创建的uart对象即可恢复REPL调试串口,以上方法通常用在产测流程中,需要在串口数据解析中增加对特定数据格式的数据进行解析,已达到指定方式可以重新打开REPL调试的串口的需求 + +- 方案二:**使用system接口** + +使用`system`接口开启交互保护,设置开启交互保护后串口输入的所有外部指令以及代码都无法执行 + +```python +system.replSetEnable(flag,**kw_args) +``` + +开启后可以通过启动时设置的密码来关闭交互保护 + 详细使用教程参考 [system API使用手册](https://python.quectel.com/doc/API_reference/zh/syslib/system.html) \ No newline at end of file diff --git a/docs/Application_guide/zh/bsp/README.md b/docs/Application_guide/zh/dev-tools/README.md similarity index 100% rename from docs/Application_guide/zh/bsp/README.md rename to docs/Application_guide/zh/dev-tools/README.md diff --git a/docs/Application_guide/zh/network/README.md b/docs/Application_guide/zh/dev-tools/Thonny/README.md similarity index 100% rename from docs/Application_guide/zh/network/README.md rename to docs/Application_guide/zh/dev-tools/Thonny/README.md diff --git a/docs/Application_guide/zh/fota/fota.md b/docs/Application_guide/zh/fota/fota.md deleted file mode 100644 index 8530323262deeb9829bc0799267c965dac1bf8a8..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/fota/fota.md +++ /dev/null @@ -1,206 +0,0 @@ -# OTA升级 - -OTA:Over-the-Air Technology空中下载技术,是通过移动通信的空中接口实现对移动终端设备进行远程管理的技术。QuecPython的OTA升级包含两部分:**OTA升级-文件**及**OTA升级-固件**。OTA升级功能测试需用到HTTP服务器作为升级内容的存放点,该部分内容本章节不做介绍,客户自行实现。本章节先介绍基本概念与理论,结合实际操作进行讲解OTA升级的使用。 - - - -## OTA升级-文件 - -### 什么是OTA升级-文件 - -OTA:Over-the-Air Technology空中下载技术,是通过移动通信的空中接口实现对移动终端设备进行远程管理的技术。 - -OTA升级-文件就是利用这项技术对模块进行文件升级。在QuecPython中该功能接口为[APP_FOTA](../../../API_reference/zh/syslib/app_fota.md)。为阅读体验以下均命名为APP_FOTA。 - -### 怎么使用APP_FOTA - -#### 升级准备 - -在之前的引言中已经说到OTA升级需要使用到HTTP服务器作为升级内容的存放点。对于APP_FOTA而言,服务器需要存储客户需要升级文件内容。此部分工作由客户自主完成。目的就是得到一个可以访问下载到该文件的URL地址即可。 - -#### 硬件设计 - -APP_FOTA升级主要为网络侧与模块内部交互,除前提条件模块需要组网成功外无需其他外围硬件支持。 - -#### 软件应用 - -QuecPython中APP_FOTA功能支持单文件下载与批量文件下载。API接口分别为:**fota.download(url, file_name)**与**fota.bulk_download(info=[])**,有关API的详细介绍请参考[APP_FOTA](../../../API_reference/zh/syslib/app_fota.md)。 - -需要注意是:调用APP_FOTA接口后文件不是直接下载到**usr**分区(模块存放用户文件的分区),而是下载到一个临时的**.updater**文件夹中。该文件夹可能存在与**usr.updater**文件夹下,也可能存在于**./fota/usr/.updater**。但对于实际使用不需要关注这些。只需了解APP_FOTA后文件并非直接存在**usr**目录,需要**fota.set_update_flag()**设置升级标志位后重启模组才会自动将对应文件搬运至**usr**目录。 - -示例代码如下: - -```python -import app_fota -from misc import Power -import utime as time - - -files = ["file_name.file_suffix"] -download_list = [] -url = r"url_connection" - -for file in files: - download_list.append({'url': (url + file), 'file_name': '/usr/%s' % file}) - -if download_list: - print("downlist: %d\r\n" % len(download_list), download_list) - fota = app_fota.new() - result = fota.bulk_download(download_list) - fota.set_update_flag() - - print("update ....", result) - time.sleep(1) - Power.powerRestart() # 重启模块 -``` - -注:`file_name.file_suffix`替换为需要下载的文件名加后缀一起。`url_connection`替换为文件存放服务器的连接。 - -### APP_FOTA文件下载测试 - -使用QPYcom工具和模组进行交互,下载示例代码至模组进行运行。 - -APP_FOTA_DEMO运行前: - -![APP_FOTA_1](media/APP_FOTA_1.jpg) - -APP_FOTA_DEMO运行后: - -![APP_FOTA_2](media/APP_FOTA_2.jpg) - - - - - -## OTA升级-固件 - -### 什么是OTA升级-固件 - -OTA:Over-the-Air Technology空中下载技术,是通过移动通信的空中接口实现对移动终端设备进行远程管理的技术。 - -OTA升级-固件就是利用这项技术进行模块固件升级。在QuecPython中该功能接口为[FOTA](/../../../API_reference/zh/syslib/fota.md)。为阅读体验以下均命名为FOTA。 - -### 怎么使用FOTA - -#### 硬件设计 - -[FOTA](/../../../API_reference/zh/syslib/fota.md)升级主要为网络侧与模块内部交互,除前提条件模块需要组网成功外无需其他外围硬件支持。 - -#### 软件应用 - -在之前的引言中已经提到OTA升级需要使用到HTTP服务器作为升级内容的存放点。对于FOTA而言是否就是存储需要升级的固件呢?答案不是。其主要原因也容易想到,无法就是固件太大与没内存缓存等。问题是不存储原始固件存储什么呢?答案是差分包。也是两版固件差异的部分。这样一来以上问题就得到了解决。 - -那问题来了,怎么制作差分包FOTA升级时怎么操作呢?下面我们分:`FOTA分类`;`差分包制作`;`软件设计`三个部分来分别介绍。 - -##### FOTA分类 - -FOTA类型分为三种:`差分升级`;`全量升级`;`最小系统升级`。下面就这三种FOTA类型的功能与支持的型号分别进行介绍。 - -| 序号 | FOTA类型 | 使用模组 | 实现功能 | -| ---- | ------------ | ----------------------------------- | ----------------- | -| 1 | 差分升级 | ASR_16MFlash模组及ASR平台外所有模组 | 固件升级 | -| 2 | 全量升级 | ASR_16MFlash模组 | 客户脚本升级 | -| 3 | 最小系统升级 | ASR平台所有模组 | 固件+客户脚本升级 | - -##### 差分包制作 - -为便于客户制作差分包简化流程,QuecPython团队将制作差分包功能集成到了QPYcom工具中。如下图添加制作FOTA包按钮进入制作FOTA工具。 - -![FOTA_1](media/FOTA_1.jpg) - -选择模块对应的平台。 - -![FOTA_1](media/FOTA_2.jpg) - -选择对应的FOTA类型后,依次选择旧/新固件包,选择差分包输出的文件夹,最后选择`OK`按钮即可生成对应的差分包。 - -**注:不同平台上传与生成的固件后缀有所不同。** - -![FOTA_1](media/FOTA_3.jpg) - -若是进行全量或是最小系统升级,会需要选择”用户分区文件“。该文件存在于ASR的固件包中。将官网提供的.bin后缀固件修改为.zip后缀。然后使用压缩工具打开,其中的`customer_fs.bin`文件就是我们这里上传的文件。 - -**注:只有ASR平台模组才会支持这两种升级类型。** - -![FOTA_1](media/FOTA_4.jpg) - -其他操作与上述差分升级流程类似,这里不做赘述。 - -##### 软件设计 - -首先将上面步骤制作的差分包上传至HTTP服务器中。然后模块执行如下代码进行FOTA升级。 - -```Python -# 差分升级 -import fota -import utime - -DEF_URL1 = 'url1_connection' - -def result(args): - print('download status:', args[0], 'download process:', args[1]) - -def run(): - fota_obj = fota() # 创建 Fota 对象 - print("进入升级状态......") - res = fota_obj.httpDownload(url1=DEF_URL1, callback=result) - if res != 0: - print("httpDownload error") - return - print("wait httpDownload , update...") - utime.sleep(2) - -run() -``` - -```python -# 全量升级 -import fota -import utime - -DEF_URL1 = 'url1_connection' - -def result(args): - print('download status:', args[0], 'download process:', args[1]) - -def run(): - fota_obj = fota() # 创建Fota对象 - print("httpDownload...") - res = fota_obj.httpDownload(url1=DEF_URL1, callback=result) - if res != 0: - print("httpDownload error") - return - print("wait httpDownload , update...") - utime.sleep(2) - -run() -``` - -```python -# 最小系统升级 -import fota -import utime - -DEF_URL1 = 'url1_connection' -DEF_URL2 = 'url2_connection' - -def run(): - fota_obj = fota() # 创建Fota对象 - print("httpDownload...") - res = fota_obj.httpDownload(url1=DEF_URL1, url2=DEF_URL2) - if res != 0: - print("httpDownload error") - return - print("wait httpDownload , update...") - utime.sleep(2) - -run() -``` - -注:`url_connection`替换为文件存放服务器的连接。 - -### FOTA文件下载测试 - -使用QPYcom工具和模组进行交互,下载示例代码至模组进行运行。 - -FOTA过程模块会反复重启,请耐心等待。 \ No newline at end of file diff --git a/docs/Application_guide/zh/fota/media/APP_FOTA_1.jpg b/docs/Application_guide/zh/fota/media/APP_FOTA_1.jpg deleted file mode 100644 index 067326f9d71975013cd7ccf17d392ad384ce459a..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/fota/media/APP_FOTA_1.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/fota/media/APP_FOTA_2.jpg b/docs/Application_guide/zh/fota/media/APP_FOTA_2.jpg deleted file mode 100644 index db946ad046dc9c359a393aee1860938d98d19add..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/fota/media/APP_FOTA_2.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/fota/media/FOTA_1.jpg b/docs/Application_guide/zh/fota/media/FOTA_1.jpg deleted file mode 100644 index 96bf6cac844bedc520341720371b34fafafb2ec1..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/fota/media/FOTA_1.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/fota/media/FOTA_2.jpg b/docs/Application_guide/zh/fota/media/FOTA_2.jpg deleted file mode 100644 index a52a1d910a1276a1974288c7d0833a8ea98d80ea..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/fota/media/FOTA_2.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/fota/media/FOTA_3.jpg b/docs/Application_guide/zh/fota/media/FOTA_3.jpg deleted file mode 100644 index 4ab9838f00690d7c33ae9cc4142e417af502fc39..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/fota/media/FOTA_3.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/fota/media/FOTA_4.jpg b/docs/Application_guide/zh/fota/media/FOTA_4.jpg deleted file mode 100644 index 4874ea88ff2eb04ec2879b3c2c4a0be6d4efbfc2..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/fota/media/FOTA_4.jpg and /dev/null differ diff --git a/docs/Application_guide/zh/gnss/README.md b/docs/Application_guide/zh/gnss/README.md deleted file mode 100644 index 64122c81590f0075653c7daf38acd5eeb78e8da5..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/gnss/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# GNSS应用指导说明 - -本文中对GNSS一些基础概念进行了简要说明,描述了如何使用 QuecPython 的`gnss`模块和`quecgnss`模块的功能来获取定位信息,同时包含了一些注意事项。 - - - -## 1. 基础概念说明 - -### 1.1 什么是GNSS - -GNSS是指全球导航卫星系统(Global Navigation Satellite System),它是泛指所有的卫星导航系统。主要包括美国的全球定位系统(GPS)、欧洲的伽利略卫星导航系统(GALILEO)、俄罗斯的格洛纳斯卫星导航系统(GLONASS)、中国的北斗卫星导航系统(BDS)以及区域系统和增强系统。 - - - -### 1.2 什么是AGPS - -AGPS是指辅助全球卫星定位系统(Assisted Global Positioning System)。它本身并不是一种定位系统,而是一种辅助定位的方式,用来加快定位速度。传统GPS定位中需要全频段搜索以找到可用卫星而导致耗时较长,而AGPS可以通过无线网络直接下载当前地区的可用卫星信息,提高了搜星的速度,也就加快了定位速度,同时起到了减少设备电量消耗的作用。 - - - -> 目前以下型号模组均内置GNSS定位功能,并默认都支持AGPS: -> -> EC200UCNAA/EC200UCNLA/EC200UEUAA/EC800MCNGA/EC800GCNGA/EG810MCNGA/EG915NEUAG - - - -### 1.3 参考坐标系 - -坐标都是相对于某一参考系而言,才有意义,GNSS定位坐标也不例外。我们通过各种渠道获取到的经纬度坐标,很可能并不是同一个坐标系下的数据。如果想利用这些坐标在地图上做可视化相关功能,就需要经过计算转换为对应地图坐标系下的坐标。目前在中国比较常见的坐标系有: - -| 坐标系 | 说明 | -| ------ | ------------------------------------------------------------ | -| WGS-84 | 世界大地测量系统(World Geodetic System 1984),是全球通用的坐标系,也是使用最广泛的坐标系。一般GPS定位设备得到的经纬度坐标都是使用WGS-84坐标系。 | -| GCJ-02 | 由中国国家测绘局制定的地理信息系统的坐标系,也叫火星坐标系。它是由WGS-84坐标加密得到。国内的高德地图、腾讯地图使用的都是GCJ-02坐标系。 | -| BD-09 | 由百度开发定制的坐标系统,它是在GCJ-02坐标的基础上再次加密得到。百度地图使用的是BD-09坐标系。 | - -通过上述说明,应明确一点,通过GNSS定位芯片拿到的经纬度坐标是WGS-84坐标系下的数据。在中国,如果用户需要将获取的经纬度坐标放到地图上显示位置,则需要将WGS-84坐标进行计算转换为该地图所使用坐标系下的坐标,才可用于在地图上拾取位置。 - - - -### 1.4 QuecPython `gnss`和`quecgnss`的区别 - -QuecPython 的内置库中,有`gnss`模块和`quecgnss`模块,都是和GNSS定位相关的功能模块。具体区别如下: - -* `gnss` - 外置GNSS功能模块。 - -* `quecgnss` - 内置GNSS功能模块。 - -所谓的“内置”和“外置”,是相对模组而言。如果模组内部集成了GNSS定位功能,则为“内置”,须使用QuecPython的`quecgnss`模块来获取定位信息;如果模组是通过串口外接了L76K系列定位芯片,则为“外置”,须使用QuecPython的`gnss`模块来获取定位信息。 - - - -## 2. GNSS应用指导文档列表 - -* [外置GNSS功能应用指导](./gnss.md) - -* [内置GNSS功能应用指导](./quecgnss.md) - - diff --git a/docs/Application_guide/zh/gnss/coordinate_convert.md b/docs/Application_guide/zh/gnss/coordinate_convert.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/gnss/gnss.md b/docs/Application_guide/zh/gnss/gnss.md deleted file mode 100644 index 9a26eb6d12e8fa261731969ca82c3bea58aa4bb9..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/gnss/gnss.md +++ /dev/null @@ -1,231 +0,0 @@ -# 1. 简介 - -QuecPython提供了`gnss`功能模块来获取外置GNSS模块的定位数据。该功能模块直接在内部完成了原始定位数据的处理解析工作,将用户关心的一些定位参数提取出来并提供对应接口让用户可直接获取。避免了用户自己通过串口去读取原始的定位数据,并进行复杂的正则匹配查找和解析的情况,提高了用户开发效率。 - -目前该模块自动解析的NEMA语句包括:GGA、RMC和GSV。 - -> 本文档中示例代码前面有 `>>> `字符串的,表示在QuecPython的命令交互界面输入的代码。 - - - -# 2. 使用说明 - -`gnss`模块接口的详细说明,请参考QuecPython官网的Wiki文档中相关部分的说明。下面以L76K定位芯片为例,说明如何使用`gnss`模块的相关功能。 - -## 2.1 获取定位数据 - -下面将详细描述使用gnss模块接口获取定位数据的步骤,同时说明在使用过程中一些注意事项。 - -### 2.1.1 使用步骤 - -步骤1:确定串口信息 - -即确定L76K定位芯片接到模组哪个串口,以及使用的波特率等信息,本示例中将L76K接到了模组的UART2,波特率默认为9600bps(可在L76K使用手册中查询到)。 - - - -步骤2:实例化一个对象 - -```python ->>> from gnss import GnssGetData ->>> gnss_obj = GnssGetData(2, 9600, 8, 0, 1, 0) -``` - - - -步骤3:读取数据并解析 - -用户只需要调用如下接口,该接口直接完成定位数据的读取和解析工作: - -```python ->>> gnss_obj.read_gnss_data() -822 -``` - -上述接口将读取原始数据、以及复杂的解析操作都放在接口内部实现,只返回了通过串口读取的数据长度。如用户想看本次读取解析的原始数据,可调用如下接口: - -```python ->>> data = gnss_obj.getOriginalData() ->>> print(data) -$GNGGA,063957.000,3802.01852,N,11437.92027,E,1,13,1.2,129.2,M,-15.7,M,,*62 -$GNGLL,3802.01852,N,11437.92027,E,063957.000,A,A*40 -$GNGSA,A,3,01,03,06,14,30,194,,,,,,,1.8,1.2,1.4,1*00 -$GNGSA,A,3,13,23,33,38,40,43,59,,,,,,1.8,1.2,1.4,4*3C -$GPGSV,3,1,11,01,38,044,12,03,44,101,24,06,30,233,31,14,79,184,34,0*6F -$GPGSV,3,2,11,17,58,319,,19,40,296,04,21,11,051,,30,14,202,34,0*67 -$GPGSV,3,3,11,194,44,156,27,195,68,074,,199,44,160,25,0*6F -$BDGSV,3,1,11,03,44,186,,07,83,121,05,10,65,309,,13,37,206,31,0*79 -$BDGSV,3,2,11,23,21,181,31,28,69,354,14,33,24,115,27,38,65,190,30,0*7A -$BDGSV,3,3,11,40,70,093,18,43,28,065,15,59,38,143,29,0*44 -$GNRMC,063957.000,A,3802.01852,N,11437.92027,E,0.69,168.35,260422,,,A,V*0B -$GNVTG,168.35,T,,M,0.69,N,1.29,K,A*2F -$GNZDA,063957.000,26,04,2022,00,00*44 -$GPTXT,01,01,01,ANTENNA OPEN*25 -``` - - - -步骤4:确认是否定位成功 - -如用户只关心是否定位到经纬度坐标以及坐标是否有效,可使用如下接口,返回1即表示定位成功且有效: - -```python ->>> gnss_obj.isFix() -1 -``` - - - -步骤5:获取坐标信息 - -调用如下接口即可获取到定位坐标: - -```python ->>> gnss_obj.getLocation() -(114.6320045, 'E', 38.033642, 'N') -``` - - - -> 上述接口获取的坐标是WGS-84坐标系下的经纬度数据,不可直接用于高德地图、腾讯地图以及百度地图等地图上拾取位置信息,必须先转换为对应地图参考坐标系下的坐标。 - - - -### 2.1.2 示例代码 - -如下代码是一个完整的使用`gnss`模块方法来获取定位坐标的例程: - -```python - -import utime -from gnss import GnssGetData - - -def main(): - gnss_obj = GnssGetData(2, 9600, 8, 0, 1, 0) - while True: - try: - read_size = gnss_obj.read_gnss_data() - except Exception: - print('数据异常,解析出错!') - data = gnss_obj.getOriginalData() - print('===============================================') - print(data) - print('===============================================') - utime.sleep(2) - continue - - if read_size > 0: - if gnss_obj.isFix(): - coordinate = gnss_obj.getLocation() - longitude = coordinate[0] - latitude = coordinate[2] - print('定位成功,当前经纬度:({}, {})'.format(longitude, latitude)) - utime.sleep(10) - else: - print('定位中,请稍后...') - utime.sleep(2) - else: - print('未读取到定位数据...') - utime.sleep(2) - - -if __name__ == '__main__': - main() -``` - - - -## 2.2 配置NEMA串口波特率 - -查阅L76K的使用手册,确定默认使用的波特率为9600bps,如用户需要修改波特率,则需要使用PCAS语句来发送一条修改波特率的指令给L76K芯片。 - -### 2.2.1 PCAS01指令格式 - -PCAS01语句即用来配置L76K的NEMA串口波特率。格式如下: - -``` -$PCAS01,* -``` - -参数说明: - -| 字段 | 描述 | -| -------- | ------------------------------------------------------------ | -| CMD | 支持如下波特率:
0 - 4800
1 - 9600
2 - 19200
3 - 38400
4 - 57600
5 - 115200 | -| Checksum | 校验和,校验和是对语句中所有字符的 8 位(不包括起始和结束位)进行异或运算,所有字符是指在定界符“$”与“*”之间,但不包括这些定界符的全部字符,包括“,”在内。 | -| CR和LF | NEMA语句的结束字符,即`\r\n` | - - - -### 2.2.2 步骤说明 - -步骤1:确定波特率 - -即确定需要配置的波特率,以确定CMD的值。假如需要配置L76K NEMA串口波特率为115200,即CMD为5。则PCAS01语句为: - -``` -$PCAS01,5*\r\n -``` - -步骤2:checksum计算 - -确定波特率后,PCAS01语句就只差checksum待计算,可按照如下算法计算: - -```python - -""" -参数说明 -dstr - 需要计算校验和的字符串,对NEMA语句而言,即“$”与“*”之间的字符串 -返回值:返回十进制的校验和 -""" -def get_checksum(dstr): - lista = list(dstr) - list_len = len(lista) - 1 - checksum = ord(lista[0]) - i = 0 - while i < list_len: - checksum = checksum ^ ord(lista[i+1]) - i = i + 1 - return checksum - -# 取“$”与“*”之间的字符串 -dstr = 'PCAS01,5' -checksum = get_checksum(dstr) -print('checksum={},{}'.format(checksum, hex(checksum))) - -#计算结果 -checksum=25,0x19 -``` - -得到对应的checksum之后,可确定完整的PCAS01语句如下: - -``` -$PCAS01,5*19\r\n -``` - -步骤3:发送指令 - -使用machine类下的UART功能模块来发送该指令到L76K,本示例中将L76K接到了模组的UART2,具体如下: - -```python - -from machine import UART -from gnss import GnssGetData - -# L76K默认波特率是9600,故先需要配置9600波特率来打开串口 -uart2 = UART(UART.UART2, 9600, 8, 0, 1, 0) -# 发送波特率配置指令 -uart2.write('$PCAS01,5*19\r\n') -# 配置成功后须先关闭当前串口 -uart2.close() -# 按照115200波特率来示例话一个gnss对象 -gnss_obj = GnssGetData(2, 115200, 8, 0, 1, 0) -``` - - - - - - - diff --git a/docs/Application_guide/zh/gnss/quecgnss.md b/docs/Application_guide/zh/gnss/quecgnss.md deleted file mode 100644 index a0c9fc5b51a33ddb8d974958dbcade2b0f2b85fb..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/gnss/quecgnss.md +++ /dev/null @@ -1,452 +0,0 @@ -# 1. 简介 - -QuecPython提供了`quecgnss`功能模块来获取模组内置GNSS的定位数据。当前`quecgnss`模块暂不支持像`gnss`模块那样直接完成了原始定位数据的处理解析功能,而是需要用户自己来完成原始数据的处理解析工作。为了提高用户开发效率和使用便捷性,本指导文档中会直接给出较为完整的数据处理解析例程,供用户参考使用。 - -> 本文档中示例代码前面有 `>>> `字符串的,表示在QuecPython的命令交互界面输入的代码。 - - - -# 2. 使用说明 - -quecgnss模块的接口详细说明,请参考QuecPython官网的Wiki文档中相关部分的说明。本文档中主要从整体流程上说明quecgnss模块的使用方式。 - - - -## 2.1 获取定位数据 - -### 2.2.1 使用步骤 - -步骤1:功能初始化 - -内置GNSS功能默认是关闭的,需要用户主动初始化并开启。直接调用quecgnss模块的init接口即可完成初始化并打开的工作。如果初始化成功,则会返回0,失败返回-1。 - -```python ->>> import quecgnss ->>> quecgnss.init() -0 -``` - - - -步骤2:确定GNSS的状态 - -内置GNSS功能初始化后,应确定GNSS当前的状态是否为”定位中“,只有处于这种状态,才可以开始读取NEMA数据。使用如下接口确定状态,返回值为2说明已经处于”定位中“的状态: - -```python ->>> quecgnss.get_state() -2 -``` - - - -步骤3:读取NEMA数据 - -当GNSS状态值为”定位中“时,即可使用如下接口去读取定位数据,读取出来的是尚未处理过的原始数据: - -```python ->>> data = quecgnss.read(2048) ->>> print(data) -(2048, '$GNRMC,020943.00,A,3149.30070,N,11706.93208,E,0.052,,310323,,,A,V*15\r\n$GNGGA,020943.00,3149.30070,N,11706.93208,E,1,29,0.54,101.2,M,,M,,*52\r\n$GNGSA,A,3,07,08,09,16,21,27,31,04,194,199,195,50,1.06,0.54,0.91,1*3B\r\n$GNGSA,A,3,02,03,05,10,11,25,36,,,,,,1.06,0.54,0.91,3*0A\r\n$GNGSA,A,3,78,88,67,66,76,86,87,68,,,,,1.06,0.54,0.91,2*0C\r\n$GPGSV,5,1,17,04,48,238,45,07,20,314,40,08,63,215,48,09,37,285,44,0*65\r\n$GPGSV,5,2,17,16,50,034,45,21,09,175,40,27,77,054,48,31,15,124,43,0*63\r\n$GPGSV,5,3,17,18,08,045,,26,24,068,,194,65,058,43,199,51,161,41,0*6A\r\n$GPGSV,5,4,17,195,47,127,43,41,37,232,45,42,45,141,,50,51,161,45,0*59\r\n$GPGSV,5,5,17,40,15,254,41,0*55\r\n$GAGSV,3,1,09,02,21,281,36,03,42,271,40,05,33,201,42,10,34,093,42,0*7F\r\n$GAGSV,3,2,09,11,32,127,37,25,57,330,43,36,09,172,36,08,13,317,36,0*78\r\n$GAGSV,3,3,09,12,26,065,,0*49\r\n$GLGSV,3,1,10,66,13,216,32,67,26,269,37,68,13,319,34,76,40,058,36,0*7D\r\n$GLGSV,3,2, ......')# 数据较多,仅列出部分数据 -``` - -需要注意的是,`quecgnss.read`接口返回值是一个元组,第一个参数是读取的数据长度,第二参数才是读取的数据,调试过程中,如果想让数据看起来整洁清晰,可按如下方式打印: - -```python ->>> print(data[1].decode()) -$GNRMC,020943.00,A,3149.30070,N,11706.93208,E,0.052,,310323,,,A,V*15 -$GNGGA,020943.00,3149.30070,N,11706.93208,E,1,29,0.54,101.2,M,,M,,*52 -$GNGSA,A,3,07,08,09,16,21,27,31,04,194,199,195,50,1.06,0.54,0.91,1*3B -$GNGSA,A,3,02,03,05,10,11,25,36,,,,,,1.06,0.54,0.91,3*0A -$GNGSA,A,3,78,88,67,66,76,86,87,68,,,,,1.06,0.54,0.91,2*0C -$GPGSV,5,1,17,04,48,238,45,07,20,314,40,08,63,215,48,09,37,285,44,0*65 -$GPGSV,5,2,17,16,50,034,45,21,09,175,40,27,77,054,48,31,15,124,43,0*63 -$GPGSV,5,3,17,18,08,045,,26,24,068,,194,65,058,43,199,51,161,41,0*6A -$GPGSV,5,4,17,195,47,127,43,41,37,232,45,42,45,141,,50,51,161,45,0*59 -...... # 数据较多,仅列出部分数据 -``` - -我们需要的定位信息就在上述这些NEMA语句中,比如经纬度坐标、海拔等。后面我们将讲述如何对这些数据进行处理解析,提取出我们需要的信息。 - - - -### 2.1.2 示例代码 - -如下例程,即是按照上述流程编写的较为完整的使用例程。 - -```python - -""" -本例程示范了如何使用quecgnss模块的方法 -例程中设定10s获取一次定位信息,仅为示例,实际可由用户自行决定获取定位信息的周期 -""" -import utime -import quecgnss - - -def main(): - if quecgnss.get_state() == 0: - ret = quecgnss.init() - if ret == 0: - print('GNSS 初始化成功') - else: - print('GNSS 初始化失败,请检查问题') - return -1 - - while True: - gnss_state = quecgnss.get_state() - if gnss_state == 2: - print('GNSS 开始定位') - break - elif gnss_state == 1: - print('GNSS 固件烧录中,请稍后') - utime.sleep(2) - continue - else: - print('GNSS 初始化异常,请检查问题') - return -1 - - while True: - try: - data = quecgnss.read(2048) - except Exception as e: - print('读取NEMA数据异常:{}'.format(e)) - utime.sleep(2) - continue - data_len = data[0] - nema_data = data[1] - if data_len == 0: - print('未读到定位数据,重试中') - utime.sleep(2) - else: - print('===============================================') - print(nema_data.decode()) - print('===============================================') - # 用户自行决定多久获取一次定位数据,此处10s仅为示例 - utime.sleep(10) - # 注[1] - try: - quecgnss.read(4096) - except Exception as e: - print('{}'.format(e)) - utime.sleep(2) - continue - - -if __name__ == '__main__': - main() - -``` - - - -> 注[1] -> -> 上述示例代码中,在休眠一段时间后,读取了4K的定位数据并丢弃了。这段代码的作用如下: -> -> 实际使用中,设备可能会间隔较长一段时间才会获取一次定位数据。有的模组,串口部分底层驱动代码缓存机制是如果缓存满了,就不会再接收新的数据;这就导致如果设备在移动中,间隔较长一段时间才获取一次定位信息时,获取到的是较长时间之前缓存的定位数据,而不是当前位置实时的定位信息。所以为了确保这种情形下,每次获取的定位信息都是实时的,需要在每次获取定位数据之前,先读一次串口数据,把串口缓存中的数据读出并丢弃,休眠1~2s后再读定位数据。 -> -> 如设备获取定位信息较为频繁,可去掉上述代码片段。 - - - -## 2.2 数据解析 - -NEMA语句都是以字符`$`开头,并且以`\r\n`作为结束。而从一堆原始数据中找到我们需要的某一条NEMA语句,本质上利用的就是NEMA语句的该特点,比如正则匹配或是判断字符串头尾。 - -下面以获取经纬度坐标信息为例,说明对NEMA语句的处理解析过程。经纬度坐标信息在RMC和GGA语句中都有包含,下面例程中以从RMC语句中提取经纬度信息为例进行说明。 - -### 2.2.1 解析步骤 - -步骤1:查找相关RMC语句 - -从读取的原始数据中找出RMC语句,这里使用的方式为——先分割再查找。 - -在这种先分割再查找的方式中,需要注意的是,对原始数据的分割,必须使用字符`$`来分割。理论上每一条NEMA语句都应以字符`$`开始,以`\r\n`结束。但在实际的定位数据中,会出现一些NEMA语句不完整的情况,即某一条或几条NEMA语句缺少了一部分,并且直接和下一条语句粘在了一起。比如: - -``` -$GPGSV,4,2,16,21,2$GNRMC,024843.00,A,3149.30313,N,11706.92780,E,0.157,,310323,,,A,V*16 -``` - -这种情况下,使用字符`$`来分割原始字符串数据,可以将这种两条粘在一起的语句分割开,再结合”NEMA语句都应以字符`$`开始,以`\r\n`结束“的特性加以判断,基本可以避免获取到不正常的语句导致后续解析出错的情况。 - -下面示例中data中就是通过`quecgnss.read`接口读出的原始数据,数据如下: - -```python -(2048,'*3B\r\n$GNGSA,A,3,02,11,25,30,34,36,,,,,,,1.20,0.65,$GNRMC,060154.00,A,3149.30510,N,11706.93089,E,0.016,,310323,,,A,V*17\r\n$GNGGA,0$GNRMC,062306.00,A,3149.30472,N,11706.93365,E,0.053,,310323,,,A,V*15\r\n$GNGGA,062306.00,3149.30472,N,11706.93365,E,1,28,0.59,91.9,M,,M,,*6C\r\n$GNGSA,A,3,01,03,07,14,17,21,30,194,199,195,19,06,0.99,0.59,0.80,1*3D\r\n$GNGSA,A,3,02,11,25,30,34,36,15,,,,,,0.99,0.59,0.80,3*07\r\n$GNGSA,A,3,78,80,79,88,82,81,,,,,,,0.99,0.59,0.80,2*0C\r\n$GPGSV,5,1,17,01,63,040,46,03,31,131,42,06,08,220,40,07,26,193,44,0*63\r\n$GPGSV,5,2,17,14,60,326,45,17,39,294,43,19,15,273,43,21,35,041,42,0*6E\r\n$GPGSV,5,3,17,30,43,234,45,08,14,073,,194,61,126,45,199,51,161,40,0*64\r\n$GPGSV,5,4,17,195,69,065,46,42,45,141,,50,51,161,44,40,15,254,41,0*53\r\n$GPGSV,5,5,17,41,37,232,44,0*51\r\n$GAGSV,2,1,07,02,82,078,40,11,12,040,32,15,10,223,35,25,33,129,36,0*7D\r\n$GAGSV,2,2,07,30,37,319,39,34,61,229,44,36,60,034,41,0*4D\r\n$GLGSV,2,1,07,78,52,161,45,79,68,316,41,80,15,330,34,81,61,325,38,0*79\r\n$GLGSV,2,2,07,82,34,252,33,88,26,031,33,65,04,073,,0*43\r\n$GNRMC,062307.00,A,3149.30472,N,11706.93366,E,0.023,,310323,,,A,V*10\r\n$GNGGA,062307.00,3149.30472,N,11706.93366,E,1,28,0.59,91.9,M,,M,,*6E\r\n$GNGSA,A,3,01,03,07,14,17,21,30,194,199,195,19,06,0.99,0.59,0.80,1*3D\r\n$GNGSA,A,3,02,11,25,30,34,36,15,,,,,,0.99,0.59,0.80,3*07\r\n$GNGSA,A,3,78,80,79,88,82,81,,,,,,,0.99,0.59,0.80,2*0C\r\n$GPGSV,5,1,17,01,63,040,46,03,31,131,42,06,08,220,40,07,26,193,44,0*63\r\n$GPGSV,5,2,17,14,60,326,44,17,39,294,45,19,15,273,43,21,35,041,42,0*69\r\n$GPGSV,5,3,17,30,43,234,44,08,14,073,,194,61,126,47,199,51,161,40,0*67\r\n$GPGSV,5,4,17,195,69,065,46,42,45,141,,50,51,161,43,40,15,254,42,0*57\r\n$GPGSV,5,5,17,41,37,232,44,0*51\r\n$GAGSV,2,1,07,02,82,078,40,11,12,040,32,15,10,223,34,25,33,129,36,0*7C\r\n$GAGSV,2,2,07,30,37,319,39,34,61,229,44,36,60,034,42,0*4E\r\n$GLGSV,2,1,07,78,52,161,45,79,68,316,41,80,15,330,35,81,61,325,38,0*78\r\n$GLGSV,2,2,07,82,34,252,33,88,26,031,32,65,04,073,,0*42\r\n$GNRMC,062308.00,A,3149.30473,N,11706.93366,E,0.032,,310323,,,A,V*1E\r\n$GNGGA,062308.00,3149.30473,N,11706.9336') -``` - -解析代码如下: - -```python - -rmc = '' -split_data = data[1].split('$') -for i in split_data: - # 查找RMC语句并进行数据完整性检查 - if i.startswith('GNRMC') and i.endswith('\r\n'): - rmc = i - split_rmc = rmc.split(',') - break -print(rmc) -print(split_rmc) - -############执行结果如下################ -GNRMC,060154.00,A,3149.30510,N,11706.93089,E,0.016,,310323,,,A,V*17 -['GNRMC', '060154.00', 'A', '3149.30510', 'N', '11706.93089', 'E', '0.016', '', '310323', '', '', 'A', 'V*17\r\n'] - -``` - - - -步骤2:语句完整性校验 - -上述解析代码中,判断是否以`GNRMC`开头并且以`\r\n`结尾,本身也是一种完整性检查。如果用户希望更准确的检查某一条NEMA语句的完整性,那就需要对该条语句进行checksum计算,并将计算结果与该条语句中的checksum进行比较,如果相等,则说明该条语句是完整且正确的。 - -这里提供一个校验NEMA语句checksum的接口: - -```python - -""" -功能:checksum计算 -参数说明: -dstr - 需要计算校验和的字符串,应取NEMA语句的”$“字符和”*“字符之间的部分,不包含”$“字符和”*“字符 -返回值:返回十六进制字符串类型的checksum -""" -def checksum(dstr): - lista = list(dstr) - list_len = len(lista) - 1 - tmp = ord(lista[0]) - i = 0 - while i < list_len: - tmp = tmp ^ ord(lista[i+1]) - i = i + 1 - strtmp = str(hex(tmp)) - return strtmp.replace('0x', '') - -""" -功能:NEMA语句checksum确认 -参数说明: -nema - 一条以‘$’字符开始和‘\r\n’字符结束的NEMA语句 -返回值:checksum校验通过返回Ture,否则返回False -""" -def checksum_verify(nema): - find_sck = ure.search("\*(.+?)\r\n", nema) - if find_sck: - sck = find_sck.group(1) - else: - return False - data = ure.search("\$(.+?)\*", nema) - if data: - print('待计算数据:{}'.format(nema)) - ck = checksum(data.group(1)) - print('计算checksum为:{}'.format(ck)) - if ck.upper() == sck.upper(): - return True - else: - return False - else: - return False - -``` - - - -步骤3:确认定位有效性 - -在获取经纬度数据之前,应先确认当前定位数据是有效的,这里是通过检查RMC语句中的`Status`位来判断是否有效,该位表示定位系统状态。当`Status`为字符`A`时,即表示有效。 - -RMC语句格式: - -``` -$RMC,,,,,,,,,,,,,* -``` - -以步骤1中查找到的RMC数据为例: - -```python - -# 通过步骤1可知split_rmc如下 -split_rmc = ['GNRMC', '060154.00', 'A', '3149.30510', 'N', '11706.93089', 'E', '0.016', '', '310323', '', '', 'A', 'V*17\r\n'] - -if split_rmc[2] == 'A': - print('有效定位') -else: - print('无效定位') - -``` - - - -步骤4:提取经纬度坐标 - -确定定位数据有效后,即可提取经纬度相关信息。需注意的是,提取出的数据默认都是字符串,需要转换为浮点数: - -```python - -lat = float(split_rmc[3]) -lon = float(split_rmc[5]) -print('经度信息:{}'.format(lon)) -print('纬度信息:{}'.format(lat)) - -############执行结果如下################ -经度信息:11706.93089 -纬度信息:3149.3051 - -``` - - - -步骤5:将经纬度数据转换为单位为”度“的坐标 - -查阅NEMA语句格式说明可知,NEMA语句中的经纬度格式如下: - -| 字段 | 格式 | 说明 | -| ---- | ----------- | ------------------------------------------------------------ | -| Lat | ddmm.mmmmm | 纬度
dd - 度(00-90)
mm - 分(00-59)
mmmmm - 分的十进制小数 | -| Lon | dddmm.mmmmm | 经度
ddd - 度(000-180)
mm - 分(00-59)
mmmmm - 分的十进制小数 | - -转换计算如下: - -```python - -longitude = lon // 100 + (lon % 100) / 60 -latitude = lat // 100 + (lat % 100) / 60 -print('({}, {})'.format(longitude, latitude)) - -############执行结果如下################ -(117.1155148333333, 31.82175166666667) - -``` - - - -> 此处计算出来的是WGS-84坐标系下经纬度坐标,不可直接用于高德、腾讯、百度等地图上。 - - - -### 2.2.2 示例代码 - -下面示例是基于2.1.2章节中的代码基础上,增加了数据解析部分的代码。 - -```python - -""" -本例程示范了如何使用quecgnss模块的方法 -例程中设定10s获取一次定位信息,仅为示例,实际可由用户自行决定获取定位信息的周期 -""" -import ure -import utime -import quecgnss - -cycle = 10 - -def checksum(dstr): - lista = list(dstr) - list_len = len(lista) - 1 - tmp = ord(lista[0]) - i = 0 - while i < list_len: - tmp = tmp ^ ord(lista[i+1]) - i = i + 1 - strtmp = str(hex(tmp)) - return strtmp.replace('0x', '') - -def checksum_verify(nema): - find_sck = ure.search("\*(.+?)\r\n", nema) - if find_sck: - sck = find_sck.group(1) - else: - return False - data = ure.search("\$(.+?)\*", nema) - if data: - print('待计算数据:{}'.format(nema)) - ck = checksum(data.group(1)) - print('计算checksum为:{}'.format(ck)) - if ck.upper() == sck.upper(): - return True - else: - return False - else: - return False - - -def main(): - global cycle - if quecgnss.get_state() == 0: - ret = quecgnss.init() - if ret == 0: - print('GNSS 初始化成功') - else: - print('GNSS 初始化失败,请检查问题') - return -1 - - while True: - gnss_state = quecgnss.get_state() - if gnss_state == 2: - print('GNSS 开始定位') - break - elif gnss_state == 1: - print('GNSS 固件烧录中,请稍后') - utime.sleep(2) - continue - else: - print('GNSS 初始化异常,请检查问题') - return -1 - - while True: - try: - data = quecgnss.read(2048) - except Exception as e: - print('读取NEMA数据异常:{}'.format(e)) - utime.sleep(2) - continue - data_len = data[0] - nema_data = data[1] - if data_len == 0: - print('未读到定位数据,重试中') - utime.sleep(2) - else: - print('===============================================') - print(nema_data.decode()) - print('===============================================') - split_nema = nema_data.split('$') - for i in split_nema: - if i.startswith('GNRMC') and i.endswith('\r\n'): - split_rmc = i.split(',') - if split_rmc[2] == 'A': - print('获取到有效定位') - # 确认定位有效后,再计算checksum确认语句完整性,避免对无效数据进行计算 - # 用户自行决定是否需要计算,如不需要可屏蔽checksum校验相关代码 - rmc = i.replace('GNRMC', '$GNRMC') - if not checksum_verify(rmc): - continue - print('checksum 校验通过') - - lat = float(split_rmc[3]) - lon = float(split_rmc[5]) - longitude = lon // 100 + (lon % 100) / 60 - latitude = lat // 100 + (lat % 100) / 60 - print('经纬度坐标:({}, {})'.format(longitude, latitude)) - break - else: - continue - # 用户自行决定多久获取一次定位数据,此处10s仅为示例 - utime.sleep(cycle) - # 下面这段代码是否需要,取决于用户获取定位的频繁程度。 - if cycle > 60: - try: - quecgnss.read(4096) - except Exception as e: - print('{}'.format(e)) - utime.sleep(2) - - continue - - -if __name__ == '__main__': - main() - -``` - - - diff --git a/docs/Application_guide/zh/network/http/README.md b/docs/Application_guide/zh/hardware/README.md similarity index 100% rename from docs/Application_guide/zh/network/http/README.md rename to docs/Application_guide/zh/hardware/README.md diff --git a/docs/Application_guide/zh/hardware/USB-interfaces.md b/docs/Application_guide/zh/hardware/USB-interfaces.md new file mode 100644 index 0000000000000000000000000000000000000000..c8fd6efbae6685fb382d057fb7d0cc5fcb502f40 --- /dev/null +++ b/docs/Application_guide/zh/hardware/USB-interfaces.md @@ -0,0 +1,302 @@ +# USB 应用指导 + +USB(Universal Serial Bus) 是一种通用的总线标准,用于连接主机和外设设备。USB 主机可以通过 USB 接口与 USB 设备连接,实现数据传输、电源供给等功能。 + +USB IF(USB Implementers Forum)是 USB 标准的制定者,它制定了 USB 标准,包括 USB 1.1、USB 2.0、USB 3.0 等,定义了 USB 接口的物理层、数据链路层、传输层、会话层、表示层等协议,以及 USB 设备类(Device Class)标准,常见的设备类包括 HID(Human Interface Device,人机接口设备)、MSC(Mass Storage Class,大容量存储设备)、CDC(Communication Device Class,通信设备)、Audio、Video 等。 + + + +## 概述 + +蜂窝通信模组具有USB接口,以方便将模组连接到主机设备(如微控制器或处理器)。USB接口不仅提供数据通信,还可以为蜂窝通信模组提供电源。 + + 蜂窝通信模组的USB接口通常用于以下目的: + +1. **数据通信**:主机设备通过USB接口与蜂窝通信模组通信。 +2. **模组控制**:通过USB接口发送AT命令至模组,进行网络通信、文件操作、硬件控制等。 +3. **固件下载**:USB接口可以用于将新的固件下载到蜂窝通信模组。 +4. **USB网卡**:某些蜂窝通信模组支持USB网卡模式。在这种模式下,设备可以将USB接口视为网络接口,就像以太网接口一样。这允许设备通过USB接口直接进行网络通信,就像通过有线网络一样。通常,这需要在设备上安装适当的驱动程序。 +5. **诊断和调试**:USB接口还可以用于诊断和调试蜂窝通信模组。例如,设备可以通过USB接口获取模组的状态信息,或者发送特殊的命令来测试模组的功能。 +7. **电源供应**:USB接口通常也用于为蜂窝通信模组供电。 + +QuecPython系列模组支持情况如下: + +| 模组 | USB Host | USB Device | USB OTG | +| ---------- | -------- | ---------- | ------- | +| EC200U | * | √ | * | +| EC600U | * | √ | * | +| EG912U | * | √ | * | +| EG915U | * | √ | * | +| EC600G | √ | √ | √ | +| EC800G | √ | √ | √ | +| EG912N | × | √ | × | +| EG915N | × | √ | × | +| EC600N | × | √ | × | +| EC600S | × | √ | × | +| EC800N | × | √ | × | +| EC600M | × | √ | × | +| EC800M | × | √ | × | +| EG810M | × | √ | × | +| EC200A | × | √ | × | +| UC200A | × | √ | × | +| EC600E | × | √ | × | +| EC800E | × | √ | × | +| BG77 | × | √ | × | +| BG95_XX | × | √ | × | +| BG600L | × | √ | × | + +> 注释: +> +> - √:支持 +> - ×:不支持 +> - *:开发中 + + + +QuecPython支持各种各样的USB应用,包括USB多媒体类应用。 + +![usb_solutions](../media/hardware/USB-interfaces/usb_solutions.png) + +## USB速率 + +目前QuecPython支持的,并且带USB接口的模组,USB协议为 *USB2.0*。 + +USB 2.0标准中包含三种数据传输速率: + +1. **低速(Low-Speed)**:此速率最高达到1.5 Mbps(Megabits per second),这种模式主要用于鼠标和键盘等低带宽设备。 +2. **全速(Full-Speed)**:全速模式的传输速度最高达到12 Mbps,这是USB 1.1的最高速率,对于许多常规设备(如USB摄像头,打印机等)来说,全速模式已经足够使用。 +3. **高速(High-Speed)**:高速模式是USB 2.0的主要新特性,最高传输速度可达480 Mbps。这使得USB 2.0能够支持大量数据传输需求的设备,如外部硬盘,高分辨率的摄像头,网络适配器等。 + +以上的速率都是理论上的最高值,在实际应用中,由于各种原因(包括但不限于USB控制器的性能,设备的性能,线缆的质量,以及系统的负载等),实际的传输速率可能会低于这些值。 + + + +## USB 电气属性 + +Type-A 接口的 USB 电气属性如下: + +| **Pin** | **Name** | **Cable color** | **Description** | +| ------- | -------- | --------------- | ---------------- | +| 1 | VBUS | **Red** | +5V | +| 2 | D- | **White** | Data-(0或3.3V) | +| 3 | D+ | **Green** | Data+(0或3.3V) | +| 4 | GND | **Black** | Ground | + +- 对于**自供电设备,需要使用 1 个额外 IO 检测 VBUS 电压**,用于检测设备是否拔出。 +- D- D+ 接反不会损坏硬件,但是主机将无法识别。 + + + +## 驱动安装 + +驱动程序(device driver)是一种可以使计算机和设备通信的特殊程序,操作系统只能通过这个接口,才能控制硬件设备的工作。 + +详细安装请参考[快速入门文档](https://python.quectel.com/doc/Quick_start/zh/index.html) + +**目前QuecPython主要生成如下类型的串口和设备类型(以EC600M为例,其它平台类似):** + +Device_Manager + +![1688367069194](../media/hardware/USB-interfaces/Net_Manager.png) + +- AT串口:用于AT命令交互 +- DIAG:用于USB Debug 输出 +- USB串行交互串口:用于QuecPython交互使用 +- RNDIS:网卡 + +具体各个平台支持情况参考[QuecPython USB COM口说明](TODO 待添加) + +### AT + + 蜂窝通信模组的USB-AT通信是基于USB Communication Device Class (CDC)规范。通常,蜂窝通信模组会在USB接口上提供一或多个虚拟的串行接口,这些接口可以被主机系统识别为标准的COM口,从而可以通过AT命令与模组进行交互。 + +实际操作中,主机系统通过USB线连接蜂窝通信模组,模组的驱动程序将在主机系统中创建一个或多个虚拟COM口。这些虚拟COM口可以被主机系统中的应用程序如同真实的串行接口那样使用,以实现AT命令的收发。 + +> 注:若想了解蜂窝通信模组支持的AT命令集,请联系QuecPython团队的技术支持 +> + +### DIAG + +USB-DIAG是一种用于诊断和调试的通信协议。DIAG,也称为诊断接口,主要用于访问和控制蜂窝通信模组的内部状态和功能。以下是关于蜂窝通信模组USB模拟DIAG的基本原理和应用的详细说明: + +**原理**: + +蜂窝通信模组的USB模拟DIAG是通过USB接口实现的。当主机设备(如计算机或嵌入式系统)通过USB接口与蜂窝通信模组连接时,模组可以识别主机设备发送的特定命令,并返回相应的诊断信息。这种诊断信息可能包括硬件状态、网络状态、固件版本、系统日志等。 + +诊断命令通常包括获取状态信息、设置参数、执行特定操作等。具体的命令集和返回的信息取决于模组的固件和硬件设计。 + +**应用**: + +1. **硬件和网络状态诊断**:通过DIAG接口,可以获取模组的硬件状态(如功率、温度、电压等)和网络状态(如信号强度、数据速率等)。 +2. **固件升级和配置**:DIAG接口还可以用于下载新的固件到模组,或更改模组的配置参数。 +3. **故障排查和调试**:如果模组出现问题,可以通过DIAG接口获取详细的系统日志和错误信息,帮助排查和解决问题。 +4. **性能测试和优化**:通过获取模组的详细状态信息,可以进行性能测试和优化。例如,可以通过调整网络参数来优化数据传输速率。 + +总的来说,蜂窝通信模组的USB模拟DIAG功能是一种强大的工具,可以用于设备状态监控、故障排查和调试、性能测试和优化等多种应用。 + +### REPL + +REPL,全称 Read-Eval-Print Loop(读取-评估-打印循环),是一种简单的交互式编程环境。它允许用户输入代码(或命令),然后立即看到执行结果。Python是著名的语言之一,支持REPL环境。 + +在蜂窝通信模组上,USB-REPL的原理是通过USB接口提供一种交互式的编程和调试环境。在这个环境中,用户可以通过输入命令(或代码)并立即看到执行结果,以便于进行实时的交互式编程和调试。 + +应用方面,USB模拟REPL的主要用途包括: + +1. **实时编程和测试**:用户可以通过REPL环境,输入命令或代码,立即看到执行结果,这对于开发人员进行快速原型开发或实时测试非常有用。 +2. **设备配置和管理**:在REPL环境中,用户可以输入命令来查询和更改模组的配置,以便于进行设备的配置和管理。 +3. **故障排查和调试**:当模组出现问题时,用户可以通过REPL环境输入命令或代码,查看执行结果,从而帮助排查和修复问题。 + +总的来说,USB模拟REPL的原理是通过USB接口,提供一种实时的交互式编程和调试环境,它的主要应用是在实时编程和测试、设备配置和管理,以及故障排查和调试等方面。 + +> 注意:在QuecPython系列部分模组中REPL口也可以通过[machine.UART](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.UART.html#%3Ccode%3Emachine.UART%3C/code%3E)初始化UART3来实现普通串口功能。当我们使用UART3时,该REPL口为普通串口功能,关闭UART3时,该口自动恢复为REPL。 + + + +### Modem + +Modem 是Modulator(调制器)与Demodulator(解调器)的简称,原本是指将数字信号转换为可以通过电话线路传输的模拟信号的设备。但在计算机领域,我们常说的"Modem模式",通常是指USB设备模拟一个虚拟的串行端口(COM端口),使得计算机可以通过这个虚拟串行端口与USB设备通信。 + +在蜂窝通信模组中,USB模拟Modem的原理是通过USB接口将模组模拟为一个虚拟的Modem设备。通过模拟Modem,蜂窝通信模组可以与主机(如计算机、嵌入式设备)进行数据通信。 + +应用方面,USB模拟Modem的主要用途包括: + +1. **数据通信**:主机可以通过虚拟的Modem端口与蜂窝通信模组进行数据通信,例如发送AT命令进行控制操作,或者收发网络数据。 +2. **网络连接**:蜂窝通信模组可以通过模拟Modem,提供拨号上网的功能。这就是我们常说的"3G Modem"或"4G Modem",也就是通过蜂窝网络提供Internet连接的设备。 +3. **设备管理**:主机可以通过Modem端口进行设备管理操作,例如查询设备状态、更新固件等。 + +因此,USB模拟Modem在蜂窝通信模组中,可以提供数据通信、网络连接和设备管理等功能。 + + + +### ECM/RNDIS + +蜂窝通信模组的USB接口可以模拟ECM(Ethernet Control Model)和RNDIS(Remote Network Driver Interface Specification)接口,这两种接口都是用于实现USB以太网的功能。 + +**ECM** 是USB接口的一种工作模式,用于实现USB网络。USB ECM模式的工作原理是将USB接口模拟为一个以太网接口,从而可以通过USB接口进行以太网通信。USB ECM接口可以支持TCP/IP等网络协议,可以用于实现USB设备与主机的网络通信。 + +**RNDIS** 是微软提出的一种用于USB设备的网络接口标准,是一种USB以太网的实现方式。RNDIS通过将USB设备模拟为一个虚拟的以太网设备,从而实现USB设备与主机的网络通信。 + +在蜂窝通信模组中,USB模拟ECM和RNDIS的主要应用包括: + +1. **网络共享**:通过ECM或RNDIS接口,蜂窝通信模组可以将自身的蜂窝网络共享给通过USB接口连接的设备,从而实现USB设备的网络接入。 +2. **远程管理**:通过ECM或RNDIS接口,主机设备可以通过网络远程管理和控制蜂窝通信模组,例如查询设备状态、更改配置、进行固件升级等。 +3. **数据通信**:通过ECM或RNDIS接口,蜂窝通信模组可以与主机设备进行高速的数据通信,例如进行文件传输、数据同步等。 + +综上,蜂窝通信模组的USB模拟ECM和RNDIS接口,提供了一种高效的方式,实现设备间的网络通信和设备的远程管理。 + +具体使用请参考**[网络应用文档-USB网卡](TODO 链接)**。 + + + +## 固件下载 + +固件下载示例请参考[QPYcom](https://python.quectel.com/doc/Advanced_development/zh/QuecPythonTools/QPYcom.html)工具使用,或参考[快速入门文档](https://python.quectel.com/doc/Quick_start/zh/index.html) + +模组的USB固件下载主要涉及以下几个步骤 : + +1. **驱动安装**:参考[驱动安装](#驱动安装)章节安装,若已安装则跳过。 +2. **模组识别**:连接模组到计算机的USB接口,你的操作系统应该能识别并成功加载驱动。在Windows上,你可以在设备管理器中查看模组的状态;在Linux上,你可以使用dmesg命令查看系统日志,确认模组已被系统识别。 +3. **进入下载模式**:你需要将模组置于固件下载模式。这通常需要发送特定的AT命令或通过某种特殊的操作。具体的方法应参考模组的[用户手册或开发指南](TODO 待快速入门添加)。 +4. **固件下载**:在模组进入固件下载模式后,你可以通过USB接口将新的固件数据发送给模组。在某些情况下,可能需要使用特殊的下载工具或者协议。完成下载后,通常需要重启模组以应用新的固件。 + + + + + +## 常见应用 + +### 插拔检测 + +USB拔插检测接口请参考[misc.USB](https://python.quectel.com/doc/API_reference/zh/peripherals/misc.USB.html) + +蜂窝通信模组作为一个USB设备(或称为USB从设备,USB Device),可以通过USB接口连接到主机(比如PC或嵌入式主控板)。在这种情况下,插拔检测通常由主机端进行,但模组自身也应能够识别这些连接和断开事件。 + +硬件层面,大多数USB主控制器(Host Controller)都能够在USB设备被插入或拔出时产生一个中断。这是通过USB设备在连接时拉高或拉低USB数据线上的电压来实现的。USB数据线上的电压变化可以被USB主控制器检测到,从而产生一个中断。同时,模组使用 1 个额外 IO 检测 VBUS 电压,用于检测设备是否拔出。 + +软件层面,USB设备需要使用一个USB设备控制器驱动来管理设备的连接和断开连接事件。当设备被连接到主机时,设备控制器驱动需要识别这个事件,并响应主机的初始化请求,例如提供设备描述符(Device Descriptor),这是一个描述设备特性(例如设备类型、制造商ID、产品ID等)的数据结构。当设备从主机断开连接时,设备控制器驱动需要识别这个事件,并停止所有与主机的通信。 + + + +**模组拔插检测功能主要在以下应用场景中发挥作用:** + + 1. 省电模式: + + 在低功耗应用中,模组可能会在不需要通信时被断电或断开连接,以节省电力。当需要通信时,再将其重新连接。插拔检测功能可以使主机自动响应这些变化,无需手动干预。 + + 2. 充电检测: + + 当模组通过USB接口连接到一个电源(例如电脑的USB端口或者USB充电器)时,USB的Vbus线(电源线)会有电压,模组可以通过检测这个电压来知道自己是否正在被充电。这种方法通常被用于判断模组是否正在被USB端口充电 + +> 注意: 尽管模组可以通过USB接口来检测充电状态,但并不能直接控制充电过程。充电过程通常需要通过一个专门的充电控制器(Charge Controller)来管理,这个控制器可能是模组内部的一部分,也可能是外部的一个单独的芯片。 + + + +示例: + +```python +from misc import USB +usb = USB() + +def usb_callback(conn_status): + status = conn_status + if status == 0: + print('USB disconnected, charging ended.') + elif status == 1: + print('USB connected, charging enabled.') + +usb.setCallback(usb_callback) + +``` + + + +## 常见问题 + +### RNDIS驱动安装 + +**RNDIS未安装时,如下所示:** + + 1688388626069 + + 在 Windows 系统上安装 RNDIS 驱动的步骤如下: + +1. 首先连接你的设备到计算机的 USB 端口。在设备管理器中,你可能会看到一个名为 "其他设备" 的未识别设备,或者名为 "RNDIS" 的设备。 +2. 右键点击这个设备,选择 "更新驱动软件"。 +3. 在弹出的窗口中,选择 "浏览计算机以查找驱动软件"。 +4. 接着选择 "从计算机的设备驱动列表中选择"。 +5. 在弹出的列表中,选择 "网络适配器",然后点击 "下一步"。 +6. 在 "制造商" 列表中选择 "Microsoft",在 "网络适配器" 列表中选择 "Remote NDIS Compatible Device",然后点击 "下一步" 完成驱动的安装。 + +> 注意:以上步骤可能会因为你的设备型号、Windows 版本和系统设置的不同而略有差异。在进行驱动安装时,建议按照你的设备制造商提供的具体指南进行操作。 +> + + 在 Linux 或 macOS 系统上,大部分情况下系统会自动加载 RNDIS 驱动,无需手动安装。如果遇到问题,你可能需要手动加载驱动,或者从设备制造商处获取适合你的系统版本的驱动。 + +### 没有REPL口 + + REPL全称为**Read-Eval-Print-Loop (交互式解释器)**,可以在REPL中进行QuecPython程序的调试。 + +![1688389115895](../media/hardware/USB-interfaces/REPL.png) + +没有REPL口体现为图中红框中设备不存在。不同的模组REPL口名称可能不同,具体的参考 [QuecPython USB COM口说明](TODO 待添加) + +若使用QuecPython系列模组时,发现PC端口管理器中没有REPL口时,请按照如下步骤确定问题: + +1. **驱动是否安装**:USB连接设备时,在设备管理器中,是否有模组对应的设备。若存在且有相应的AT等串口时,表示驱动成功安装。 若设备在“其它设备”类别下,且带有一个黄色感叹号,说明驱动并未安装或安装错误。此时参考上面[驱动安装](#驱动安装)章节中,安装模组对应驱动。 +2. **更新固件**:确保模组内为QuecPython提供的固件。可参考[固件下载](#固件下载)一章节。 + +### 驱动安装错误 + +**驱动安装失败或未安装时,设备管理显示如下所示:** + +![1688388626069](../media/hardware/USB-interfaces/drive_fail.png) + +如果在安装模组驱动时出现错误,以下是一些可能的解决方法: + +1. **重新启动计算机**:有时,只需简单地重新启动计算机,就可以解决某些安装问题。 +2. **检查模组兼容性**:确保你的模组兼容你正在使用的操作系统。检查设备制造商的技术规格和要求。 +3. **下载最新驱动**:前往QuecPython官方网站下载最新版本的驱动。旧的驱动可能与新的操作系统版本不兼容。 +4. **以管理员身份运行安装程序**:在Windows系统中,右键点击驱动安装程序,然后选择 "以管理员身份运行"。这可以解决由权限问题导致的安装错误。 +5. **关闭防病毒软件**:某些防病毒软件可能会阻止驱动程序的安装。在安装驱动程序时暂时禁用防病毒软件。 +6. **手动安装驱动**:在设备管理器中,你可以手动安装驱动。右键点击设备,选择 "更新驱动",然后选择 "浏览我计算机以查找驱动",导航至驱动程序的位置进行安装。 +7. **联系QuecPython团队的技术支持**:如果尝试了上述步骤仍然无法解决问题,那么请联系QuecPython的技术支持以获取帮助。 \ No newline at end of file diff --git a/docs/Application_guide/zh/hardware/audio-driver.md b/docs/Application_guide/zh/hardware/audio-driver.md new file mode 100644 index 0000000000000000000000000000000000000000..6cc3d721e7db92a4d3886fcea54438af009c8168 --- /dev/null +++ b/docs/Application_guide/zh/hardware/audio-driver.md @@ -0,0 +1,172 @@ + +# 1. 概述 +音频处理是指对音频信号进行处理的过程,包括播放、录音和TTS(文本转语音)功能。播放是指将已经录制好的音频文件播放出来,录音是指将声音转化为数字信号并保存到模组中,TTS是指将文本转化为语音并播放出来。 + + +# 2. 音频原理 + +## 2.1 DAC原理 +DAC(Digital-Analog-Converter)是数字到模拟转换器的缩写,它的作用是将数字信号转换成模拟信号。 +音频DAC是一种将数字音频信号转换为模拟音频信号的电路。它的工作原理一般分为三个步骤,即解码、滤波和放大。 +- **解码**:将数字音频信号按照一定的规则,还原为模拟信号的采样值,这个过程通常采用多级幅度量化高阶Σ - Δ调制器结构。 +- **滤波**:将解码后的模拟信号通过一个低通滤波器,去除高频噪声和抖动,得到平滑的模拟信号。滤波器可以有两种滚降特性,慢滚降和陡滚降。 +- **放大**:将滤波后的模拟信号通过一个耳机放大器,增加信号的功率和驱动能力,使之能够推动耳机或扬声器发声。 + +音频DAC的性能指标主要有: +- **位数**:表示解码前的数字音频信号有多少位二进制数,决定了解码的精度和动态范围。 +- **采样率**:表示每秒钟解码多少次,决定了信号的频带宽度。 +- **信噪比**:表示模拟音频信号中有效信号与噪声的比例,反映了信号的质量。 +- **总谐波失真**:表示模拟音频信号中谐波失真与有效信号的比例,反映了信号的线性度。 +音频DAC的选型要根据具体的应用场景和需求来确定。一般来说,高端音频设备需要高位数、高采样率、高信噪比和低失真的音频DAC, +而低端音频设备则可以使用低位数、低采样率、低信噪比和高失真的音频DAC。 + +## 2.2 ADC原理 +ADC(Analog-Digital-Converter)是模拟到数字转换器的缩写,它的作用是将模拟信号转换成数字信号。 +它的工作原理一般分为四个步骤,即采样、保持、量化和编码。 +- **采样**:按照一定的频率,对输入的模拟信号进行离散化,得到一系列采样值。 +- **保持**:将采样值暂时存储在一个电容上,以便后续的量化和编码。 +- **量化**:将采样值按照一定的规则,分成若干个等级,并赋予每个等级一个数值,这个数值就是量化值。 +- **编码**:将量化值用二进制数表示,得到数字信号。 +音频ADC的性能指标主要有: + +- **位数**:表示编码后的数字信号有多少位二进制数,决定了量化的精度和动态范围。 +- **采样率**:表示每秒钟采样多少次,决定了信号的频带宽度。 +- **信噪比**:表示数字信号中有效信号与噪声的比例,反映了信号的质量。 +- **总谐波失真**:表示数字信号中谐波失真与有效信号的比例,反映了信号的线性度。 +音频ADC的选型要根据具体的应用场景和需求来确定。一般来说,高端音频设备需要高位数、高采样率、高信噪比和低失真的音频ADC, +而低端音频设备则可以使用低位数、低采样率、低信噪比和高失真的音频ADC。 + +# 3. 常见数字音频接口 +数字音频接口的作用是将数字音频信号在不同的设备之间进行传输,例如DAC、ADC、DSP、音频处理器、音频播放器等。数字音频接口可以提高音频信号的抗干扰能力,减少信号损失和噪声,提高音质和效率。数字音频接口也可以支持多声道和高采样率的音频信号,满足不同的应用需求。常见的数字音频接口有以下几种: + +- **I2S接口**:Inter-IC Sound Interface的缩写,是一种串行音频接口,用于连接数字音频设备,如DAC、ADC、DSP等。它使用三根线来传输数据,分别是时钟线(BCLK)、帧同步线(LRCK)和数据线(SD)。时钟线用于提供数据的采样率,帧同步线用于区分左右声道,数据线用于传输音频数据。I2S接口可以支持多达32位的数据位宽和高达192kHz的采样率。 +- **PCM/TDM接口**:Pulse Code Modulation/Time Division Multiplexing的缩写,是一种类似于I2S的串行音频接口,但是可以支持多路音频信号的复用。它也使用三根线来传输数据,分别是时钟线(BCLK)、帧同步线(FS)和数据线(SD)。时钟线用于提供数据的采样率,帧同步线用于区分不同的音频信号,数据线用于传输音频数据。PCM/TDM接口可以支持多达16路音频信号和高达768kHz的采样率。 + +目前支持Quecpython开发方式的模组主要支持PCM接口,可用于外接Codec(音频编解码器芯片)。引脚定义如下, +- PCM + +|引脚名 |I/O | 描述 | +|-------|-------|---------| +|PCM_CLK |DO | PCM 时钟 | +|PCM_SYNC |DO | PCM 帧同步 | +|PCM_DIN |DI | PCM 数据输入 | +|PCM_DOUT |DO | PCM 数据输出 | + + +不同平台支持PCM通道数如下: +|平台 |I2S | PCM | +|-------|-------|---------| +|EC600M-CN系列 |0 | 1 | +|EC800M-CN系列 |0 | 1 | +|EC600U系列 |0 | 1 | +|EG912U-GL系列 |0 | 1 | +|EG915U系列 |0 | 1 | +|EC200U系列 |0 | 1 | +|EC200A系列 |0 | 1 | +|EC600E-CN系列 |0 | 1 | +|EC800E-CN系列 |0 | 1 | +|BG95系列 |0 | 1 | +|BG77系列 |0 | 1 | +|BG600L-M3系列 |0 | 1 | +|EC600G系列 |0 | 1 | +|EC800G-CN系列 |0 | 1(0或1,可选) | +|BC25系列 |0 | 0 | +|BC95系列 |0 | 0 | +|EG912N-EN系列 |0 | 1 | +|EG915N系列 |0 | 1 | + +# 4. 蜂窝模组硬件框架 + +## 4.1 内置Codec +Codec(音频编解码器芯片)主要用于对音频信号进行数字化和还原。音频编解码器一般由两部分组成:ADC(模拟-数字转换器)和DAC(数字-模拟转换器)。ADC负责将模拟信号(如麦克风、线路输入等)转换为数字信号(如PCM等),DAC负责将数字信号(如I2S、SPDIF等)转换为模拟信号(如耳机、扬声器等)。部分模组内部已经内置了一路Codec,此时模组可以直接输入输出音频模拟信号,如下图: + +## 4.2 外置Codec +当模组内部不带内置Codec时,或者需要使用两路Codec时,可以使用PCM或者I2S接口外接Codec。此时外部输入输出模拟信号需要和Codec连接,如下图: + +## 4.3 外置DAC +音频DAC是一种音频数模转换器,即一种将数字信号转换为模拟信号的装置,可以将存储在数字媒体中的音频数据,如CD、DVD、MP3等,转换为可以通过扬声器或耳机输出的模拟信号,使人们可以听到声音。模组可以通过PCM或者I2S接口外接DAC,如下图: + +## 4.4 PWM 音频 +PWM音频是一种利用PWM(脉冲宽度调制)信号来播放和处理音频的方法,无需使用外部的音频DAC(数模转换器)芯片,适用于对音质要求不高而对成本敏感的应用。 + +PWM音频的主要原理如下: +- PWM信号是一种周期性的方波信号,其占空比(高电平时间占一个周期的比例)可以调节,从而改变其平均电压值。 +- 音频信号是一种模拟信号,其幅度和频率可以表达声音的响度、音调和音色等特征。 +- 通过将音频信号进行采样和量化,可以得到一系列的数字数据,表示声音在不同时间点的幅度值。 +- 通过将数字数据转换为对应的PWM占空比,可以用PWM信号来模拟音频信号的波形变化。 +- 通过将PWM信号输出到扬声器或耳机等设备,可以还原出声音。 + +## 4.5 PA 管理 +PA(Power Amplifier)音频功率放大器的作用是把来自音源或前级放大器的弱信号放大,推动音箱放声。模组或者Codec输出的音频模拟信号不够强,需要音频功率放大器(PA)的控制和优化,以提高音频输出的质量和效率,保护音频器件不受损坏。当模组对外输出音频,如播放音频文件或者使用TTS时,需要打开PA。当模组在录音状态时则需要关闭PA可以降低功耗,以及避免底噪被PA放大导致杂音。 + +# 5. 常见音频应用 + + +## 5.1 音频播放 +音频播放是指使用音频编解码器将音频文件或音频流转换为模拟信号,通过输出通道(如听筒、耳机、喇叭等)播放出来的过程。 +- 音频播放需要创建一个Audio对象,指定输出通道,如aud = audio.Audio(0)表示使用听筒输出。 +- 音频播放可以使用aud.play(priority, breakin, filename)方法,指定播放优先级、打断模式和文件名称。支持mp3、amr和wav格式的文件播放。 +- 使用aud.playStream(format, buf)方法,指定音频流格式和内容。支持mp3、amr和wav格式的音频流播放。 +音频播放的更多接口和详细使用方法见[WIKI-Audio](https://python.quectel.com/doc/API_reference/zh/medialib/audio.Audio.html)。 +## 5.2 语音电话 +语音电话是指使用语音编解码器将语音信号转换为数字信号,通过网络传输到对方,再由对方的语音编解码器还原为语音信号的过程。 +语音电话的更多接口和详细使用方法见[WIKI-voiceCall](https://python.quectel.com/doc/API_reference/zh/iotlib/voiceCall.html)。 +## 5.3 录音 +外部传入的声音通过麦克风的输入设备转化为电信号后,再通过Codec转化为数字信号传递给模组,模组将数字信号转化为不同格式(如AMR、WAV)的音频文件存储到Flash上。 +录音的原理是将声音信号转换为电信号或数字信号,然后存储在某种介质上,以便于回放或处理。 +- 录音需要创建一个Record对象,指定输入通道,如record = audio.Record(0)表示使用听筒输出。 +- 使用record.start(file_name,seconds)方法,指定录音文件名称和录音长度。 +语音电话的更多接口和详细使用方法见[WIKI-Record](https://python.quectel.com/doc/API_reference/zh/medialib/audio.html)。 +## 5.4 TTS +TTS是Text To Speech的缩写,即文本转语音,是一种能把文字内容转换为语音输出的技术。当前TTS模块主要包含如下接口: +- **初始化**:使用audio.TTS()创建一个TTS对象,指定输出通道。 +- **播放**:使用TTS对象的play()方法,可以将指定的文本转换为语音并播放,可以指定是否打断、优先级、语速、语调等参数。 +- **停止**:使用TTS对象的stop()方法,可以停止当前正在播放的语音。 +- **暂停**:使用TTS对象的pause()方法,可以暂停当前正在播放的语音。 +- **恢复**:使用TTS对象的resume()方法,可以恢复暂停的语音。 +- **设置**:使用TTS对象的set_volume()、set_speed()等方法,可以设置音量、语速等参数。 +语音电话的更多接口和详细使用方法见[WIKI-TTS](https://python.quectel.com/doc/API_reference/zh/medialib/audio.TTS.html)。 + +*使用注意事项:* +- **数字和数值**:数字:数字之间添加空格。如“1 2 3”,会播报成 “一二三”; 数值:“123” 会 播报成 “一百二十三” +- **多音字**:”您已超速,请保持安全行车速度“ ,默认情况下 “行” 会播报成 hang,应采用如下写法:"您已超速,请保持安全行[=xing2]车速度" *[=xing2]即 xing 第二声*。 +- **停顿**:**[z1]** 标识起始位置,**[z0]** 标识末尾,**\*** 表示短停顿,**#** 表示长停顿 + +# 6. 音频参数校准 + +## 6.1 概述 +音频校准工具可调整内置 codec 输入增益(包含模拟增益,adc增益),输出增益(包含模拟增益,dac增益,算法增益),侧音增益。生效模式分为实时生效模式,和非实时生效模式,通过 API控制。其中非实时生效模式下,调整的参数将会在下次播放/通话时才能生效。工具可适配整多种应用场景,和多种输入/输出通道。音频工具调整好参数后,可以直接固定到NV文件中,作为默认音频参数。 +## 6.2 各平台支持情况和工具适用 + +### 6.2.1 ASR平台 +目前ASR平台可以通过CATStuido软件中的CAT-Audio工具上进行音频参数的校准。主要功能有: +- **音频通路校准** 可以设置不同音量等级下的增益。 +CAT-Audio 音频通路自带 audio_gain.nvm 的修改和 AT*NVMFLUSH=1 的发送功能。 当所有增益修改完成,需要将 +改动保存至 flash 时,可以鼠标选中 Profiles 右击,会弹出 3 个选项: +“Save Gain to NVM”-----将修改保存到 NVM,即时生效, 重启也能保持设定的参数值; +“Create Default NVMs”--参数没保存到 NVM 时,点击可恢复原始 NVM; +“Reload NVM files”-----从 cp 调取原始 NVM。 +- **通话录音和播放** 点击 “track view”按钮 ,界面显示 tracks view; + +在 VE 拓扑图里勾选需要 dump 录音的点; + +点击红色圆形按钮进入实时录音,在 tracks view 处可以看到实时波形; + + + +- **语音增强音频校准** 语音增强拓扑图提供对所有语音增强模块的控制。它支持打开和关闭每个模块,并可以设置模块中的参数。 + + +### 6.2.2 展锐平台 +展锐平台使用AudioCalibrator软件去进行音频参数的标定。 +Codec参数校主要包含: +**内置codec输入增益(atctst_aud_codec_get_ingain)**: +输入增益即通话模式下的MIC增益,该增益在音乐播放模式下不可调。如下图,可以修改模拟增益和adc增益。不同通道下的模拟增益范围不同,adc增益范围相同。值不可过大,否则会造成输入失真。 + + +**内置codec输出增益(atctst_aud_codec_get_outgain)**: +输出增益在通话模式,或者音乐播放模式下均可调。输出增益与音量等级相关,当前支持0~11级音量,分别对应增益列表中的outGains[0] ~ outGains[11]。用户若要重新调整音量增益,可以调整对应音量等级下的dac,ana与alg增益。Alg调到最小可以实现静音。DAC的值不建议超过63,易造成失真。用户可根据应用场景,设置不同的增益组合。建议可保持ana与alg不变,调整dac增益。 + + +**侧音增益(atctst_aud_codec_get_sidetonegain)**: + 侧音增益用于通话场景。侧音默认关闭,客户若需要侧音,可在此调整。 \ No newline at end of file diff --git a/docs/Application_guide/zh/hardware/display.md b/docs/Application_guide/zh/hardware/display.md new file mode 100644 index 0000000000000000000000000000000000000000..1e3c75fc6bebcb059d2d069852cf924d5d3e4db9 --- /dev/null +++ b/docs/Application_guide/zh/hardware/display.md @@ -0,0 +1,1092 @@ +# 显示器和触摸屏 + +## 概述 + + 嵌入式设备,如智能家电、医疗设备、车载系统等,也经常使用显示器和触摸屏。这些设备通常专为特定的任务或功能设计,因此它们的显示器和触摸屏可能具有特定的用途或功能 。 + +​ 在嵌入式设备中,显示器和触摸屏的作用和主要功能如下: + +`显示器`: + +​ 显示器在嵌入式设备中的主要作用通常是提供一个界面,让用户可以看到设备的状态信息、反馈或者进行操作 。 + +- 状态显示:例如,洗衣机的显示器可能会显示当前洗衣程序的进度,或者微波炉的显示器可能会显示剩余的加热时间。 + +- 反馈信息:例如,医疗监测设备的显示器可能会显示患者的心率、血压等生命体征。 +- 操作指南:例如,车载信息娱乐系统的显示器可能会显示地图、导航指南等 。 + +`触摸屏` + +​ 触摸屏在嵌入式设备中通常用作一个输入设备,让用户可以直接在屏幕上进行操作。 + +- 直接控制: 例如,用户可以在智能家电的触摸屏上选择或调整设置,或者在车载信息娱乐系统的触摸屏上输入目的地。 +- 便利的交互:对于某些嵌入式设备,如便携式医疗设备或者工业控制设备,触摸屏提供了一种方便且直观的交互方式。 +- 多点操作:许多触摸屏支持多点触控,这使得用户可以使用更复杂的手势来控制设备。 + + + + 总的来说,显示器和触摸屏在嵌入式设备中的主要作用和功能是提供了一种使设备易于使用和理解的用户界面。 + + + + + +## 显示器 + +​ 许多应用需要把信息显示给用户,因此显示屏是很重要的一个显示设备。 QuecPython系列芯片为屏幕显示应用提供了丰富的外设支持,包含的接口类型有`SPI` 和 `MIPI` + + 一块屏幕通常有两个主要部分:`显示基板` 和 `驱动 IC`。显示基板(也称显示面板)决定了显示的尺寸、分辨率、色彩等参数;驱动 IC (也称屏幕控制器)是匹配显示基板并与基板封装在一起的,它通过一定的接口对外通信并控制显示基板进行显示。 + +### 类型 + +现代显示器的种类非常多样化,它们用于各种不同的设备和应用,从个人计算机到商业广告屏,从手机到电视,从游戏设备到嵌入式设备等。以下是一些常见的显示器类型: + +1. **液晶显示器(Liquid Crystal Display,LCD)**: + - 工作原理:LCD显示器使用液晶分子控制光线的通过,从而显示图像。液晶本身不发光,所以需要背光源。 + - 特点:LCD显示器体积小、功耗低、发热少,适合嵌入式系统使用。它可以根据需求进行各种尺寸和形状的定制。 + - 应用:LCD显示器广泛应用于各种嵌入式设备,如手持设备、仪器仪表、家用电器等。 +2. **有机发光二极管(Organic Light Emitting Diode,OLED)**: + - 工作原理:OLED显示器利用有机物质发光的特性,通过施加电压使得有机物质发出光线,从而显示图像。每一个像素都可以单独发光。 + - 特点:OLED显示器具有色彩鲜明、对比度高、反应速度快的特点,且可以制造出柔性和透明的显示器。 + - 应用:OLED显示器目前主要应用于高端移动设备,如智能手机和可穿戴设备。 +3. **电子纸显示(E-Ink Display)**: + - 工作原理:电子纸通过在两个透明电极间夹层中的微胶囊改变显示颜色。每个微胶囊内含有黑色和白色粒子,通过施加电压使得黑色和白色粒子在微胶囊中的位置发生变化,从而改变显示颜色。 + - 特点:电子纸的功耗极低,且具有非常好的阅读体验,尤其在光照条件下,清晰度极高。 + - 应用:电子纸主要应用于电子阅读器,也逐渐被应用到各种显示标签和长时间显示的设备上。 +4. **微型LED(Micro LED)**: + - 工作原理:微型LED显示器由无数微小的LED像素组成,每个像素可以单独发光和调整亮度。 + - 特点:微型LED显示器色彩丰富、对比度极高、响应速度快,且功耗低、寿命长。 + - 应用:目前微型LED主要被应用于高端的大型显示屏和电视产品,但随着技术的进步,未来可能会逐渐应用于更多的嵌入式设备中。 + +以上就是一些常见的嵌入式系统中的显示器类型。嵌入式系统中的显示器需求可能会根据具体的应用和性能需求有所不同,因此选择适合的显示器类型是非常重要的。 + + + +### 驱动框图 + +其主控,驱动IC, 接口关系如下: + +![1687920840462](../media/hardware/display/lcd_controller.png) + +1. **主控(MCU/CPU)**:这是嵌入式系统的核心部分,负责处理数据,执行代码,以及管理和控制系统的各个部分。 +2. **显示接口**:这是主控和显示芯片之间的通信连接。常见的显示接口包括:SPI、I2C、HDMI、VGA、LVDS、MIPI DSI等等。主控通过这些接口向显示芯片发送数据和命令。 +3. **显示芯片**:显示芯片接收来自主控的数据和命令,然后根据这些信息驱动显示面板进行相应的显示。 +4. **显示基板(或显示面板)**:显示面板是嵌入式系统中实际显示图像的部分。它包含一系列像素,这些像素根据来自显示芯片的信号来改变其状态,从而显示出图像。 + + 主控通过显示接口向显示芯片发送数据和命令,然后显示芯片根据这些信息驱动显示面板显示图像。这就是这些组件在一个嵌入式系统中的基本关系。 + +### 适配说明 + +​ 大部分屏幕控制器可以支持多种显示接口。如 [ILI9486](https://www.waveshare.net/w/upload/7/78/ILI9486_Datasheet.pdf) 支持 MCU 接口(即 8080 接口)、SPI 接口、RGB 接口和 MIPI 接口。 + +QuecPython目前支持接口如下: + +`SPI 接口` + +QuecPython系列芯片的 SPI 接口特点: + +- SPI 时钟频率最高可达 50MHz + + + +`MIPI接口` + +QuecPython系列芯片的 MIPI 接口特点: + +- MIPI时钟频率最高可达 100MHz + +- 最高支持2Lane + + + +**QuecPython系列模组支持情况如下:** + +| SoC | MIPI接口 | SPI接口 | +| ---------- | -------- | ------- | +| EC200U_XXX | √ | √ | +| EC600U_XXX | √ | √ | +| EC600N_XXX | × | √ | +| EC800N_XXX | × | √ | +| EC600M_XXX | × | √ | +| EC800M_XXX | × | √ | +| EC800G_XXX | √ | √ | +| EC810M_XXX | × | √ | +| EG912N_XXX | × | √ | +| EG912U_XXX | √ | √ | +| EG915N_XXX | × | √ | +| EG915U_XXX | √ | √ | + +**已适配的屏幕控制器型号如下表所示:** + +| 控制器型号 | 类型 | 接口 | 分辨率 | 驱动 | 数据手册 | +| ---------- | ----- | ---- | -------- | ----------------------- | ------------------------ | +| ILI9225 | Color | SPI | 176*220 | [ILI9225.py](TODO 链接) | [ILI9225.pdf](TODO 链接) | +| ILI9341 | Color | SPI | 240*320 | [ILI9341.py](TODO 链接) | [ILI9341.pdf](TODO 链接) | +| ILI9806 | Color | MIPI | 480*800 | [ILI9806.py](TODO 链接) | [ILI9806.pdf](TODO 链接) | +| ST7735 | Color | SPI | 128*160 | [ST7735.py](TODO 链接) | [ST7735.pdf](TODO 链接) | +| ST7789 | Color | SPI | 240*320 | [ST7789.py](TODO 链接) | [ST7789.pdf](TODO 链接) | +| ST7701 | Color | MIPI | 480*854 | [ST7701.py](TODO 链接) | [ST7701.pdf](TODO 链接) | +| JD9365 | Color | MIPI | 800*1280 | [JD9365.py](TODO 链接) | [JD9365.pdf](TODO 链接) | +| SC7705 | Color | MIPI | 800*1280 | [SC7705.py](TODO 链接) | [SC7705.pdf](TODO 链接) | +| SSD1306 | Mono | SPI | 128*64 | [SSD1306.py](TODO 链接) | [SSD1306.pdf](TODO 链接) | +| UC1628 | Mono | SPI | 128*64 | [UC1628.py](TODO 链接) | [UC1628.pdf](TODO 链接) | + +​ **说明**:使用lcd方法时,只要参数传输正确,理论是支持任意SPI MIPI屏幕的。 + +### 驱动结构 + +![1686981785042](../media/hardware/display/controller_structure.png) + +**QuecPython为了更自由的控制屏幕,python脚本可以通过lcd.lcd_init传入的参数驱动屏幕。** + +**在设计上,每驱动一款屏幕差异点仅仅为脚本lcd.lcd_init或lcd.mipi_init中的参数不一样而已。** + +### 屏幕的分类 + + 对屏幕进行分类的讨论将有助于我们对驱动的理解,这里将按照屏幕可显示的色彩来分类,而非 OLED、LCD 等屏幕的面板材料。 一般情况下,屏幕显示的色彩决定了 BPP (Bits Per Pixel),而 BPP 的不同导致程序的处理方式有一些不同,下面将更直观地列举几种 GRAM 映射到像素点的方式: + +![](../media/hardware/display/screen_driver_RGB565.png) + + BPP = 16 GRAM 结构 + +![](../media/hardware/display/screen_driver_mono.png) + + BPP = 1 GRAM 结构 + + ![](../media/hardware/display/screen_driver_gray.png) + +​ BPP = 4 GRAM 结构 + +​ + +从以上图中可以看出,映射方式大概可以分为两类: + +- **BBP >= 8**,通常是支持 RGB888、RGB666、RGB565 等编码的彩色屏幕。 +- **BPP < 8**,通常是单色的屏幕,可能是黑白的,也可能是灰阶的。 + +BPP < 8 时,一个字节映射到了多个像素,因此无法直接地控制单个像素。而QuecPython为了更好的控制单色屏幕这种情况,lcd_write接口依旧传入RGB565数据,底层将自动完成转换,这样python脚本能达到访问单个像素的目的。BPP >= 8 时,则可以轻松地访问单个像素。 + +**注意**:对于彩色屏幕,驱动仅支持 RGB565 颜色编码。 + + + +### 屏幕控制器驱动 + + 该部分将根据不同的屏幕控制器分别实现显示等功能 。 + +#### 显示方向 + + 这里设置的屏幕显示方向是完全由屏幕硬件实现的,这个功能在不同的屏幕控制器上会有差异。一共有 8 种可能的显示方向, 显示器可以旋转 0°、90°、180° 或 270°,也可以从顶部或底部查看,默认方向为 0° 和顶部查看。 这 8 (4 × 2) 个不同的显示方向也可以表示为 3 个二进制开关的组合:X-mirroring、Y-mirroring 和 X/Y swapping。 + + 下表将列出全部 8 种组合显示的方向。如果显示方向不正常,请查看下表中的配置开关使其正常工作。 + +| | **L2R_U2D** | | Y-mirroring
[L2R_D2U] | +| ---------------------------------------------------------- | ------------------------------------------------ | ----------------------------------------------------------- | ------------------------------------------------------------ | +| | **X_mirroring**
**[R2L_U2D]** | | **X-mirroring**
**Y-mirroring**
**[R2L_D2U]** | +| | **X/Y swapping
[U2D_L2R]** | | **X/Y swapping
Y-mirroring
[D2U_L2R]** | +| | **X/Y swapping
X-mirroring
[U2D_R2L]** | | **X/Y swapping
X-mirroring
Y-mirroring
[D2U_R2L]** | + +对于不同屏幕控制器,屏幕显示方向的实现并不完全相同,通常分为以下的情况: + +> - 对于彩色屏幕,支持 8 个方向的旋转。 +> - 对于单色屏幕,如 SSD1306 等屏幕来说,只支持前 4 个方向,即不支持交换 XY 轴。 + + + +​ **注解:** + +显示方向还和使用的屏幕面板有关系,你可能会发现两种异常的情况: + +- 显示方向设置为 L2R_U2D,屏幕却不是按照上表中对应的显示方向。这可能是因为屏幕面板上的走线对 X/Y 方向上进行了镜像,这时应该根据实际情况调整旋转以得到期望的显示方向。 + +- 旋转了屏幕后,显示内容不见了。这可能是因为屏幕面板的分辨率小于屏幕控制器的分辨率,导致旋转后显示区域没有完全落在屏幕面板上,这时应考虑设置正确的显示区域偏移。 + + + +### 常见屏幕参数解析 + +屏幕驱动IC包含多种寄存器,它们控制着各种功能和设定。以下是一些你可能需要关注的寄存器参数: + +1. 模式寄存器:这些寄存器通常控制显示模式(如分辨率、颜色深度等)和电源管理模式。例如,一些屏幕驱动IC可能有专门的寄存器用于切换睡眠模式或正常模式。 + +2. 控制寄存器:控制寄存器用于配置屏幕驱动IC的各种功能,如屏幕旋转、翻转、反色等。 + +3. 定时寄存器:这些寄存器用于控制像素时钟、水平同步和垂直同步信号的定时。 + +4. Gamma寄存器:Gamma寄存器用于设置屏幕的gamma校正。这对于图像质量和颜色准确性非常重要。 + +5. RAM寄存器:RAM寄存器是用来存储帧缓冲区数据的。驱动IC会从这些寄存器读取数据,然后把数据发送到屏幕。 + +6. 状态寄存器:状态寄存器可以让你读取IC的当前状态,比如是否在正常工作、是否有错误发生等。 + +7. 亮度/对比度寄存器:一些驱动IC可能会有这些寄存器来调整屏幕的亮度或对比度。 + +8. 偏移寄存器:这些寄存器通常用于控制图像在屏幕上的位置。 + + + + 需要注意的是 ,每个屏幕驱动IC都有其自己的寄存器映射和功能。所以在开始使用新的驱动IC时,你应该阅读其数据手册来了解其寄存器的详细信息。 + +### API说明 + +详细的接口说明请参考wiki [machine.lcd](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html) + +**注意:**该部分为QuecPython LCD驱动的相关API,请驱动屏幕前,详细阅读 + + + +### 屏幕调试 + +#### SPI LCD + + SPI LCD显示屏是使用SPI接口进行通信的LCD显示屏。这种显示屏的优点在于其接口简单,线数少,使得硬件布线更加简洁。它们非常适合用于微控制器和其他资源有限的系统。 + +​ 在SPI LCD中,数据和命令通过MOSI线发送到显示器,而SCLK线用于同步数据传输。CS线用于在与多个设备通信时,选择特定的设备进行通信。 + +​ 在一般使用中,控制器(如微控制器)会根据显示器的规格(例如分辨率和颜色深度)以及需要显示的内容,通过SPI接口发送适当的命令和数据。这可能包括设置显示参数、写入像素数据等。 + + + +本章节将基于QuecPython 开发板驱动[st7789(240*320](TODO 链接)屏幕,介绍SPI LCD的调试。 + +##### 前期准备工作 + +​ 在实现 LCD 驱动之前,我们需要一些准备工作,该部分让我了解 quecpython 驱动一款屏幕需要 +哪些前期准备,我们需要知道 quecpython lcd 驱动 LCD 接口以及接口需要哪些参数。还需要对拿到 +手的屏幕了解其 ID,驱动 IC 的类型,以及相关的命令。 + +**注意:** 原厂提供的初始化参数非常重要,后面SPI LCD初始化时,实际为将该参数替换成lcd.lcd_init所需要格式的参数。 + + + +了解[quecpython lcd接口](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.lcd_init%3C/code%3E),才能更好是利用它点亮屏幕 。 + +**注意:**目前QuecPython SPI驱动分为两种,LCM( Liquid Crystal Module )和通用SPI( Serial Peripheral Interface )。两者的初始化接口有差异,具体详细可参考[quecpython lcd接口](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.lcd_init%3C/code%3E) + +**了解LCD.lcd_init中的参数,对于QuecPython驱动LCD至关重要,请仔细阅读如下内容。** + +参数格式如下所示: + +**type + len + value** + +| 参数 | 含义 | 说明 | +| ----- | -------- | ------------------------------------------------------------ | +| type | 类型 | 表示后面value表示类型
0:命令
1:数据
2:延时 | +| len | 数据个数 | 若type为命令时,len表示后面接多少个数据
若type为数据时,len表示数据的长度。注:数据可以为1byte也可能为2byte
若type为延时,len无实际意义。为0接口。 | +| value | 数据 | 若type为命令时,value表示命令值
若type为数据时,value表示数据值。
若type为延时时,value表示延时的时长,单位ms。 | + +注意:由于lcd_init传入的是buff,需要进行bytearray转换。后续初始化参数,区域写屏参数,亮屏,息屏等参数均是按照该格式进行。 + +举例说明: + +```python +init = ( +0,1,0xXX, #命令,后接一个 data, cmd 值为 0xXX +1,2,0xXX,0xXX, #数据, 数据长度为 2, data 值为 0x0100 +2,0,120, #延时 120ms +0,2,0xXX, #命令,后接 2 个 data, cmd 值为 0xXX +1,1,0xXX, #数据,命令的第一个 data,data 值为 0xXX +1,2,0xXX,0xFF #数据,命令的第二个 data,数据的长度为 2,data 值 为 0xXXFF +… +) +``` + + + +###### 初始化参数 + +该部分比较容易出错且非常重要,请**重点关注** + +该部分参数主要由屏幕厂家提供的源码转换而来,而厂家提供大多数为C代码,下面我们将C代码转换为lcd_init所需要的参数。转换示例如下: + +小技巧:使用vscode直接替换正则表示替换 + +![1688125456671](../media/hardware/display/lcd_init_para.png) + + + +###### 区域写屏参数 + +不同的 lcd 屏有不同的设置区域方式,我们将参数设置之后,用户就无需关心底层如何调用了。 + +一般屏幕设置有两种方式:(以ili9225和st7789v为例) + +一:分两次写:高八位和低八位此参数根据具体的屏幕去驱动而定(如st7789v) + +二:一次写一个short (如ili9225) + +关于显示区域有几个重要的参数需要提交了解。 + +```python +XSTART_H = 0xf0 #代表X起始坐标 高八位 (以此值写入,底层会识别该位是x坐标的高位) +XSTART_L = 0xf1 #代表X起始坐标 低八位 (以此值写入,底层会识别该位是x坐标的低位) +YSTART_H = 0xf2 #代表Y起始坐标的高八位 +YSTART_L = 0xf3 #代表Y起始坐标的低八位 +XEND_H = 0xE0 #代表X结束坐标的高八位 +XEND_L = 0xE1 #代表X结束坐标的低八位 +YEND_H = 0xE2 #代表Y结束坐标的高八位 +YEND_L = 0xE3 #代表Y结束坐标的低八位 + +XSTART = 0xD0 #代表X的起始坐标 +XEND = 0xD1 #代表X的结束坐标 +YSTART = 0xD2 #代表Y的起始坐标 +YEND = 0xD3 #代表Y的结束坐标 +``` + +注意:该参数为定位符,用于底层lcd.lcd_write接口替换坐标使用 + +```python +举例如下: +#ILI9225 – 一次写一个short +ili9225_invalid = ( +0,1,0x36, +1,2,XEND, +0,1,0x37, +1,2,XSTART, +0,1,0x38, +1,2,YEND, +0,1,0x39, +1,2,YSTART, +0,1,0x20, +1,2,XSTART, +0,1,0x21, +1,2,YSTART, +0,1,0x22, +) + +#st7789v - 分两次写,一次一个char: +st7789_invalid = ( +0,4,0x2a, +1,1,XSTART_H, +1,1,XSTART_L, +1,1,XEND_H, +1,1,XEND_L, +0,4,0x2b, +1,1,YSTART_H, +1,1,YSTART_L, +1,1,YEND_H, +1,1,YEND_L, +0,0,0x2c, +) +``` + +原厂提供C转Python示例如下: + +![1688124724127](../media/hardware/display/lcd_invalid_para.png) + +###### 亮屏息屏参数 + +由于每款LCD的亮屏命令不一样,故需要用户提供该配置参数。此参数根据具体的屏幕去驱动而定。 + +以ST7789V为例: + +![1688124980736](../media/hardware/display/lcd_display_para.png) + +​ 根据图中内容可以确认该LCD的亮屏息屏参数 为29h 和28h。退出休眠和进入休眠对应的命令为11h和10h。即可确定亮屏和息屏参数(也可由屏厂提供的示例确定)。 + +​ 顾亮屏息屏参数如下: + +```python +lcd_displayON_data = ( +0,0,0x11, #写命令0x11, 后面不接data +2,0,120, #延时120ms +0,0,0x29, #写命令0x29, 后面不接data +) +lcd_displayON_data = bytearray(lcd_displayON_data) + +lcd_displayOFF_data = ( +0,0,0x28, #写命令0x11, 后面不接data +2,0,120, #延时20ms +0,0,0x10, #写命令0x29, 后面不接data +) +lcd_displayOFF_data = bytearray(lcd_displayOFF_data) + +``` + + + +##### **脚本编写** + +###### 创建对象 + +``` +from machine import LCD +mipi_lcd = LCD() +``` + +###### **SPI LCD初始化接口介绍** + +请参考[LCM 接口API](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Cstrong%3E%E6%8E%A5%E5%8F%A31%EF%BC%9A%E8%AE%BE%E5%A4%87%E6%8E%A5%E6%A8%A1%E5%9D%97LCM%E6%8E%A5%E5%8F%A3%3C/strong%3E) 和 [SPI LCD接口API](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Cstrong%3E%E6%8E%A5%E5%8F%A32%EF%BC%9A%E8%AE%BE%E5%A4%87%E6%8E%A5%E6%A8%A1%E5%9D%97SPI%E6%8E%A5%E5%8F%A3%3C/strong%3E) + + + +###### **编写初始化参数** + +以前期准备中的参数,按照接口介绍中顺序,传入lcd.lcd_init中即可。 + +以ST7789V为例: + +```python +XSTART_H = 0xf0 +XSTART_L = 0xf1 +YSTART_H = 0xf2 +YSTART_L = 0xf3 +XEND_H = 0xE0 +XEND_L = 0xE1 +YEND_H = 0xE2 +YEND_L = 0xE3 +XSTART = 0xD0 +XEND = 0xD1 +YSTART = 0xD2 +YEND = 0xD3 + +init_st7789_240X320=( +0, 0, 0x11, +2, 0, 120, +0, 0, 0x00, +0, 1, 0x36, +1, 1, 0x00, +0, 1, 0x3A, +1, 1, 0x05, +0, 1, 0x35, +1, 1, 0x00, +0, 1, 0xC7, +1, 1, 0x00, +0, 1, 0xCC, +1, 1, 0x09, +0, 5, 0xB2, +1, 1, 0x0C, +1, 1, 0x0C, +1, 1, 0x00, +1, 1, 0x33, +1, 1, 0x33, +0, 1, 0xB7, +1, 1, 0x35, +0, 1, 0xBB, +1, 1, 0x36, +0, 1, 0xC0, +1, 1, 0x2C, +0, 1, 0xC2, +1, 1, 0x01, +0, 1, 0xC3, +1, 1, 0x0D, +0, 1, 0xC4, +1, 1, 0x20, +0, 1, 0xC6, +1, 1, 0x0F, +0, 2, 0xD0, +1, 1, 0xA4, +1, 1, 0xA1, +0, 14, 0xE0, +1, 1, 0xD0, +1, 1, 0x17, +1, 1, 0x19, +1, 1, 0x04, +1, 1, 0x03, +1, 1, 0x04, +1, 1, 0x32, +1, 1, 0x41, +1, 1, 0x43, +1, 1, 0x09, +1, 1, 0x14, +1, 1, 0x12, +1, 1, 0x33, +1, 1, 0x2C, +0, 14, 0xE1, +1, 1, 0xD0, +1, 1, 0x18, +1, 1, 0x17, +1, 1, 0x04, +1, 1, 0x03, +1, 1, 0x04, +1, 1, 0x31, +1, 1, 0x46, +1, 1, 0x43, +1, 1, 0x09, +1, 1, 0x14, +1, 1, 0x13, +1, 1, 0x31, +1, 1, 0x2D, +0, 0, 0x29, +0, 1, 0x36, +1, 1, 0x00, +0, 0, 0x2c, +) +init_st7789_240X320_p = bytearray(init_st7789_240X320) +invalid_st7789_240X320 = ( +0,4,0x2a, +1,1,XSTART_H, +1,1,XSTART_L, +1,1,XEND_H, +1,1,XEND_L, +0,4,0x2b, +1,1,YSTART_H, +1,1,YSTART_L, +1,1,YEND_H, +1,1,YEND_L, +0,0,0x2c, +) +invalid_st7789_240X320_p = bytearray(invalid_st7789_240X320) + +displayOFF_st7789_240X320 = ( +0,0,0x28, +2,0,120, +0,0,0x10, +) +displayOFF_st7789_240X320_p = bytearray(displayOFF_st7789_240X320) + +displayON_st7789_240X320 = ( +0,0,0x11, +2,0,20, +0,0,0x29, +) +displayON_st7789_240X320_p = bytearray(displayON_st7789_240X320) +from machine import LCD +spilcd = LCD() +spilcd.lcd_init(init_st7789_240X320_p, 240,320,52000,1,4,0,invalid_st7789_240X320_p,displayON_st7789_240X320_p,displayOFF_st7789_240X320_p,None) +``` + +效果如下: + +![1688126689262](../media/hardware/display/spilcd_show1.png) + + + +###### **区域写屏** + +该接口 十分重要 ,UI就是基于该接口对屏幕进行UI绘制。 + +[lcd.lcd_write 接口原型](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.lcd_write%3C/code%3E) + +注意: + +​ QuecPython屏幕数据为大端模式。 + + + +**示例如下(ST7789V):** + +```python +buf = bytearray(10240) #此时buf值全为0, 颜色体现为黑色 +spilcd.lcd_write(buf,110,150,130,170) +``` + +效果如下: + +![1688127026026](../media/hardware/display/spilcd_show2.png) + +###### 清屏 + +[lcd.lcd_clear 接口原型](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.lcd_clear%3C/code%3E) + +**示例如下(ST7789V):** + +```python +spilcd.lcd_clear(0xf800) #0xf800 is represented in red in RGB565 +``` + +效果如下: + +![1688127082390](../media/hardware/display/spilcd_show3.png) + +###### **图片显示** + +[lcd.lcd_show_jpg原型](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.lcd_show_jpg%3C/code%3E) + +注意 :该接口并非所有模组都支持,具体支持情况请参考WIKI. + +**示例如下(ST7789V):** + +```python +spilcd.lcd_show_jpg("usr/background1.jpeg", 0, 0) +``` + +效果如下: + +![1688127171526](../media/hardware/display/spilcd_show4.png) + +#### MIPI LCD + +​ MIPI协议实际上是一系列接口的协议,包含液晶、摄像头等等。液晶中所用的MIPI接口,其专业名称叫DSI,全称Display Serial Interface。顾名思义,该接口是指用于显示模块的一个串行接口,基于MIPI协议而产生,兼容DPI(显示像素接口,Display Pixel Interface)、DBI(显示总线接口,Display Bus Interface)和DCS(显示命令集,Display Command Set)。 + +本章节将基于QuecPython 铀235开发板驱动ST7701S(480*854)屏幕,介绍MIPI LCD的调试。 + +由于QuecPython下LCD的相关**[API](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.mipi_init%3C/code%3E)**已经开发好,用户可以直接依照[API](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.mipi_init%3C/code%3E)格式,编写MIPI屏幕驱动。 + + **注意:** 目前QuecPython支持mipi的平台仅支持RGB565格式. + +**注意:**实际使用中,LCD多与UI([LVGL](TODO 链接))搭配使用。 + +##### 前期准备工作 + +​ 确定使用的MIPI屏幕,咨询阅读其datasheet和驱动IC的技术手册。了解屏幕的特性,确定屏幕的初始化参数(分辨率, Lane数等)。屏幕的**初始化参数**可以找屏幕原厂提供。 + +**注意:** 原厂提供的初始化参数非常重要,后面mipi LCD初始化时,实际为将该参数替换成lcd.mipi_init所需要格式的参数。 + +![1686981785042](../media/hardware/display/mipi_lcd_1.png) + +从原厂给出的屏幕datasheet中可以看出其分辨率(480*854)、驱动IC(ST7701S)、接口接口(2-LINE MIPI)、触摸IC(GT911)和触摸接口(I2C)。 + + + +##### 脚本编写 + +###### 创建LCD对象 + +```python +from machine import LCD +mipi_lcd = LCD() +``` + +###### **MIPI初始化接口介绍** + +[lcd.lcd_init接口原型](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.mipi_init%3C/code%3E) + +```python +lcd.mipi_init(initbuf, **kwargs) +``` + +| 参数 | 类型 | 说明 | +| ----------- | --------- | ------------------------------------------------------------ | +| initbuf | bytearray | 必传,传入 MIPI 的配置命令 | +| width | int | 缺省值:480,屏幕的宽度,单位像素,示例:width=400 | +| hight | int | 缺省值:854,屏幕的高度,单位像素,示例:hight=800 | +| bpp | int | 缺省值:16,像素深度 | +| DataLane | int | 缺省值:2,数据通道 | +| MipiMode | int | 缺省值:0 模式: 0:DSI_VIDEO_MODE 1:DSI_CMD_MODE | +| PixelFormat | int | 缺省值:0 像素格式: 0:RGB_PIX_FMT_RGB565 16:RGB_PIX_FMT_RGB888 32:RGB_PIX_FMT_XRGB888 48:RGB_PIX_FMT_RGBX888 | +| DsiFormat | int | 缺省值:0 DSI格式: 0:DSI_FMT_RGB565 1:DSI_FMT_RGB666 2:DSI_FMT_RGB666L 3:DSI_FMT_RGB888 | +| TransMode | int | 缺省值:3 转换模式: 0:DSI_CMD 1:DSI_PULSE 2:DSI_EVENT 3:DSI_BURST | +| RgbOrder | int | 缺省值:8 RGB顺序: 0:RGB 8:BGR | +| BllpEnable | bool | 缺省值:true,blank low power 模式使能 | +| HSync | int | 缺省值:10,水平同步 | +| HBP | int | 缺省值:10,水平后肩 | +| HFP | int | 缺省值:10,水平前肩 | +| VSync | int | 缺省值:4,垂直同步 | +| VBP | int | 缺省值:10,垂直后肩 | +| VFP | int | 缺省值:14,垂直前肩 | +| FrameRate | int | 缺省值:60,帧率 | +| TESel | bool | 缺省值:false,TE选择 | +| RstPolarity | int | 缺省值:1,reset极性 | + +###### **编写初始化参数** + +该部分比较容易出错且非常重要,请**重点关注** + +参数格式如下所示: + +**cmd + delay_ms + data_len + data_list** + +| 参数 | 含义 | 说明 | +| --------- | -------- | ---------------------------------------- | +| cmd | 命令 | 初始化时命令值 | +| delay_ms | 延时 | 延时多久后发送命令,单位ms | +| data_len | 数据长度 | 命令后配置数据搭配的数据个数 | +| data_list | 数据 | 命令后配置的实际数据,个数由data_len指定 | + +注意:由于lcd_init传入的是buff,需要进行bytearray转换。 + +**示例如下(st7701s):** + +该部分参数主要由屏幕厂家提供的源码转换而来 + +```python +init_480X854 = ( +0x11,0,0, #命令为0x11, 延时0ms, 命令所接参数无 +0xFF,120,5,0x77,0x01,0x00,0x00,0x10, #命令为0xFF,延时120ms, 命令对应5个参数数据,分别为 #0x77,0x01,0x00,0x00,0x10 +0xC0,0,2,0xE9,0x03, #命令为0xC0, 延时0ms, 命令对应2个参数数据,分别为 #0xE9,0x03 +0xC1,0,2,0x11,0x02, # ... 下面依此类推 +0xC2,0,2,0x31,0x08, +0xCC,0,1,0x10, +0xB0,0,16,0x00,0x0D,0x14,0x0D,0x10,0x05,0x02,0x08,0x08,0x1E,0x05,0x13,0x11,0xA3,0x29,0x18, +0xB1,0,16,0x00,0x0C,0x14,0x0C,0x10,0x05,0x03,0x08,0x07,0x20,0x05,0x13,0x11,0xA4,0x29,0x18, +0xFF,0,5,0x77,0x01,0x00,0x00,0x11, +0xB0,0,1,0x6C, +0xB1,0,1,0x43, +0xB2,0,1,0x07, +0xB3,0,1,0x80, +0xB5,0,1,0x47, +0xB7,0,1,0x85, +0xB8,0,1,0x20, +0xB9,0,1,0x10, +0xC1,0,1,0x78, +0xC2,0,1,0x78, +0xD0,0,1,0x88, +0xE0,100,3,0x00,0x00,0x02, +0xE1,0,11,0x08,0x00,0x0A,0x00,0x07,0x00,0x09,0x00,0x00,0x33,0x33, +0xE2,0,13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xE3,0,4,0x00,0x00,0x33,0x33, +0xE4,0,2,0x44,0x44, +0xE5,0,16,0x0E,0x60,0xA0,0xA0,0x10,0x60,0xA0,0xA0,0x0A,0x60,0xA0,0xA0,0x0C,0x60,0xA0,0xA0, +0xE6,0,4,0x00,0x00,0x33,0x33, +0xE7,0,2,0x44,0x44, +0xE8,0,16,0x0D,0x60,0xA0,0xA0,0x0F,0x60,0xA0,0xA0,0x09,0x60,0xA0,0xA0,0x0B,0x60,0xA0,0xA0, +0xEB,0,7,0x02,0x01,0xE4,0xE4,0x44,0x00,0x40, +0xEC,0,2,0x02,0x01, +0xED,0,16,0xAB,0x89,0x76,0x54,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x10,0x45,0x67,0x98,0xBA, +0xFF,0,5,0x77,0x01,0x00,0x00,0x00, +0x3A,0,1,0x77, +0x36,0,1,0x00, +0x35,0,1,0x00, +0x29,0,0) +from machine import LCD +mipilcd = LCD() +mipilcd.mipi_init(initbuf=bytearray(init_480X854), TransMode=1) +``` + +效果如下: + +![1688106357858](../media/hardware/display/mipi_lcd_2.png) + +注意 :该处花屏属于正常现象,显示缓冲区中数据为随机数据,导致屏幕显示随机像素,表现即为花屏。此时清屏一下即可。 + +###### **区域写屏** + +该接口 十分重要 ,UI就是基于该接口对屏幕进行UI绘制。 + +[lcd.lcd_write 接口原型](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.lcd_write%3C/code%3E) + +注意: + +​ QuecPython屏幕数据为大端模式。 + + + +**示例如下(st7701s):** + +```python +test_buf = ( +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f,0x00,0x1f, +) +test_buf1 = bytearray(test_buf) +mipilcd.lcd_write(test_buf1,10,10,20,20) +``` + +![1688106994170](../media/hardware/display/mipi_lcd_3.png) + +###### 清屏 + +[lcd.lcd_clear 接口原型](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.lcd_clear%3C/code%3E) + +**示例如下(st7701s):** + +```python +mipilcd.lcd_clear(0xf800) #0xf800 is represented in red in RGB565 +``` + +效果如下: + +![1688107261768](../media/hardware/display/mipi_lcd_4.png) + +###### **图片显示** + +[lcd.lcd_show_jpg原型](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.LCD.html#%3Ccode%3Elcd.lcd_show_jpg%3C/code%3E) + +注意 :该接口并非所有模组都支持,具体支持情况请参考WIKI. + +**示例如下(st7701s):** + +```python +mipilcd.lcd_show_jpg("usr/background.jpeg", 0, 0) +``` + +效果如下: + +![1688123135485](../media/hardware/display/mipi_lcd_5.png) + + + +### 常见问题 + +#### 花屏 + +初始化显示屏但未写入任何数据,显示屏可能会出现所谓的“花屏”现象,主要原因可能包括: + +1. **显示缓冲区的初始状态**:当你初始化显示屏但未写入任何数据时,显示缓冲区可能包含随机数据,这可能导致在屏幕上显示随机的像素,即出现花屏现象。这是因为大多数系统并不会在初始化时自动清除显示缓冲区。 +2. **电源和信号问题**:如果显示屏的电源不稳定,或者数据信号存在干扰,也可能导致花屏。这可能需要检查电源和信号线路的质量。 +3. **显示驱动程序或硬件问题**:如果显示驱动程序存在问题,或者显示硬件有故障,也可能导致花屏。例如,驱动程序可能未正确初始化显示硬件,或者硬件存在故障。 + +为了解决这个问题,可以尝试在初始化显示屏后立即清除显示缓冲区,即将其所有像素设置为同一颜色(通常是黑色或白色)。这应该可以消除花屏现象。如果问题仍然存在,可能需要检查你的电源和信号线路,或者检查显示驱动程序和硬件是否存在问题。 + +#### 刷新率低 + + LCD刷新率低可以有多种原因,包括: + +1. **硬件性能限制**:内存或数据总线带宽的限制。目前QuecPython各个模组对应的最大SPI频率为50Mhz,且大多数屏幕无法适配这么大的频率,导致屏幕较大时,刷新帧率较小。 +2. **驱动程序或操作系统问题**:驱动程序或操作系统可能没有正确配置或优化来充分利用硬件性能。例如,驱动程序可能没有正确配置显示控制器,或者操作系统可能没有正确调度图形任务。 +3. **显示设置问题**:显示设置,如分辨率,可能过高,超出了硬件或驱动程序的性能能力。试着降低分辨率看是否可以提高刷新率。 +4. **显示控制器或面板问题**:显示控制器或LCD面板本身也可能有性能限制,不能达到更高的刷新率。 +5. **信号干扰或质量问题**:如果使用的是长数据线或信号质量差,可能会导致刷新率降低。尝试更换数据线或改善信号质量。 +6. **应用程序问题**:运行的应用程序可能占用了大量的图形资源,导致刷新率降低。尝试关闭不需要的应用程序,或者降低应用程序的图形需求。 + + + +#### 屏幕无法点亮 + +LCD屏幕无法点亮可能有很多原因,以下列出了一些常见的可能性: + +1. **电源问题**:LCD屏幕需要电源供应,如果电源供应有问题,例如供电电压过高或过低,电源线接触不良,电源适配器故障,都可能导致LCD屏幕无法点亮。 +2. **接口问题**:LCD屏幕通常通过某种接口(例如MIPI SPI等)连接到驱动电路或者主控板。如果这些接口连接不稳定或者接口线路有问题,可能会导致LCD屏幕无法正常工作。驱动MIPI时,因为频率较高,若连接线过长或者有折痕,也可能导致LCD无法工作。 +3. **驱动问题**:如果驱动程序有问题,或者主控板没有正确配置LCD屏幕的参数,也可能导致LCD屏幕无法点亮。例如分辨率、色深、帧率等参数设置不正确,或者驱动程序本身有bug。 +4. **硬件故障**:LCD屏幕自身也可能出现故障,如液晶面板、背光源、驱动IC或者其他电子元件损坏,都可能导致LCD屏幕无法点亮。 +5. **信号问题**:例如信号线损坏,或者信号质量不好(如噪声过大,信号过弱等),也可能导致LCD屏幕无法正常显示。 + + + + + +## 触摸屏 + +​ 在嵌入式设备中,Touchpad(触摸板)是一种常见的输入设备(以下简称TP),用于替代鼠标或其他指针设备,以实现对设备界面的操作和控制。它通常是一块平面表面,用户可以通过手指在其上进行触摸和滑动来实现相应的操作。 + +### 分类 + +以下是一些常见的触摸屏类型,以及它们的工作原理,特点和应用: + +1. **电阻式触摸屏**: + - **工作原理**:电阻式触摸屏包括两层电阻层,当触摸时,两层会在接触点产生电连接,电阻改变,通过测量电阻的改变,计算出触摸点的位置。 + - **特点**:成本较低,耐用性高,能应对各种环境因素(如灰尘和水)。缺点是触摸精度较低,且只能支持单点触控,透光性和色彩也不如其他类型。 + - **应用**:常见于低成本设备和工业设备,如POS系统,工业控制面板等。 +2. **电容式触摸屏**: + - **工作原理**:电容式触摸屏通过测量屏幕上的电容变化来确定触摸位置。当手指接近屏幕表面时,会引起电容的变化,从而检测到触摸事件。 + - **特点**:触摸精度高,支持多点触控,色彩和透光性好。缺点是成本相对较高,而且只能通过手指或特殊的触摸笔进行触摸。 + - **应用**:电容式触摸屏现在被广泛用于智能手机,平板电脑和笔记本电脑等个人电子设备。 +3. **红外触摸屏**: + - **工作原理**:红外触摸屏通过测量被触摸对象阻挡的红外光线来检测触摸位置。屏幕边缘装有红外发射器和接收器,当触摸事件发生,会阻挡部分红外线。 + - **特点**:可以支持多点触控,且可以通过任何物体进行触摸。缺点是精度不如电容式和电阻式触摸屏,且容易受到光线的干扰。 + - **应用**:通常应用于大型显示设备,如交互式电视,电子白板等。 +4. **声表面波(SAW)触摸屏**: + - **工作原理**:SAW触摸屏使用超声波来检测触摸位置。当你触摸屏幕时,会吸收或扰动一部分超声波,通过测量这种变化,就可以确定触摸的位置。 + - **特点**:具有很高的触摸精度和优良的图像质量,但对环境变化敏感,比如温度、湿度和液体溅落等都可能影响其性能。 + - **应用**:常见于游戏机和一些需要高精度和高质量图像的设备。 + +每种类型的触摸屏都有其优点和适用的场合,选择哪种类型主要取决于具体的应用需求。 + +### 驱动框图 + +其主控,驱动IC, 接口关系如下: + + + +![1688127951394](../media/hardware/display/touch_controller.png) + +1. **触摸屏控制器(Touch Controller)**:也被称为主控,它是触摸屏系统中的核心部分。它负责接收从触摸屏传感器发来的信号,并将这些信号转换成我们可以理解的坐标信息。触摸控制器可能是一个单独的IC,也可能是一个内置在微处理器或微控制器中的模块。 +2. **触摸屏传感器(Touch Sensor)**:这是触摸屏系统中的物理部分,它可以是一个薄膜,也可以是一个玻璃面板。当我们触摸这个传感器时,它会将我们的触摸行为转换成电信号,并发送给触摸屏控制器。 +3. **接口(Interface)**:这是触摸屏控制器与其它部分(比如微处理器或微控制器)通信的方式。常见的接口类型包括I2C、SPI和USB等。 +4. **微处理器或微控制器(Microprocessor or Microcontroller)**:它处理来自触摸屏控制器的数据,并将这些数据转换为有用的指令,比如移动光标、打开应用等。 + +因此,这个系统的工作过程可以被简化为如下几个步骤: + +触摸 -> 触摸屏传感器 -> 电信号 -> 触摸屏控制器 -> 数据 -> 接口 -> 微处理器或微控制器 -> 指令 + +### 适配说明 + +接口说明请参考[WIKI_Touch](TODO 链接) + +QuecPython系列芯片支持情况如下: + +| 名称 | 功能 | 接口 | 驱动 | 数据手册 | +| ------- | ------------ | ---- | ---------------- | ------------------ | +| xpt2046 | 电阻触控芯片 | SPI | [tm1650.py](XXX) | [xpt2046.pdf](XXX) | +| gt9xx | 电容触控芯片 | I2C | [gt9xx.py](XXX) | [gt9xx.pdf](XXX) | +| cst816 | 电容触控芯片 | I2C | [cst816.py](XXX) | [cst816.pdf](XXX) | + + + +### 校准触摸屏 + +​ 在实际应用中,电阻触摸屏必须在使用前进行校准,而电容触摸屏则一般由控制芯片完成该工作,无需额外的校准步骤。驱动中已经集成了电阻触摸屏的校准算法,校准过程屏幕会点亮四个点,依次点击来校准,采集数据进行数据比对,误差太大则点击失败,重新点击。 +​ 调用校准函数xtp2046.adjust()将会在屏幕上开始校准的过程,校准完成后,将参数保存在文件中,下次初始化xtp2046时读取校准数据,避免每次使用前的重复校准。 + + + +### 触摸屏的按下 + +​ 不论是电阻还是电容触摸屏,通常的触摸屏控制芯片会有一个用于通知触摸事件的中断引脚,可以通过该引脚中断来读取电平。但触摸控制器给出的信号往往不如程序通过寄存器判断的准确性高。创建触摸对象时,会要求传入中断引脚号。 + +​ 对于电阻触摸屏来说,判断按下的依据是 Z 方向的压力大于配置的阈值;对于电容触摸屏则是判断至少有一个触摸点存在。 + +### 触摸屏的旋转 + +触摸屏具有与显示屏一样的 8 个方向。定义在创建触摸对象的参数中(x_inv, y_inv, xy_swap)。 这里的旋转是通过软件换算来实现的 。 + +触摸屏的分辨率设置也是很重要的,因为触摸屏旋转后的换算依赖于触摸屏的宽和高分辨率大小,设置不当将无法得到正确的旋转效果。 + +**注解:** + +**在使用电阻屏时,由于电阻屏在每个方向上的电阻值不一定均匀分布,这会导致经过旋转换算后触摸位置的不准确,所以建议电阻屏校准后不再进行旋转操作。** + + + +### 示例: + +QuecPython内部已适配三款触摸驱动,可以**直接使用**。 + +以[铀开发板](TODO 链接)触摸屏(GT911)为例: + +```python +from tp import gt9xx +tp_gt911 = gt9xx(irq=40,reset=20) +tp_gt911.activate() +tp_gt911.init() +tp_gt911.read_xy() +``` + +效果如下: + +![1688129750452](../media/hardware/display/touch_resulut_show.png) + +### 常见问题 + +#### 触摸不准确 + +触摸不准确可能是由以下原因引起的: + +1. **校准问题**:如果触摸屏未经过正确的校准,或者校准过程中有误,那么触摸屏可能无法准确地识别触摸位置。此时,需要进行触摸屏校准。 +2. **驱动问题**:如果触摸屏的驱动程序存在问题,可能会导致触摸定位不准确。在这种情况下,可能需要更新或者修复触摸屏驱动程序。 +3. **硬件问题**:硬件的问题也可能会导致触摸不准确。例如,触摸屏的感应器有损坏,或者触摸屏和控制器之间的连接存在问题等。 +4. **电磁干扰**:在某些环境中,电磁干扰可能会导致触摸定位不准确。这可能需要更换设备的使用环境或者增加电磁防护措施。 +5. **软件问题**:某些情况下,应用程序的问题可能会影响到触摸的准确性。例如,某些应用程序可能没有正确地处理触摸输入,导致触摸定位偏离。 + +#### 触摸失灵 + +触摸屏失灵的原因可能有以下几种: + +1. **硬件故障**:触摸屏自身或与其连接的硬件(如控制器、数据线等)可能出现故障,包括物理损坏、老化、接触不良等。 +2. **驱动问题**:触摸屏驱动软件可能出现故障,如驱动程序存在缺陷、兼容性问题、驱动未正确安装或更新等。 +3. **软件冲突**:操作系统中,其他软件或驱动可能与触摸屏驱动产生冲突。 +4. **电源问题**:触摸屏的电源供应不稳定,或电源供应不足,都可能导致触摸屏失灵。 +5. **环境因素**:极端的环境条件,如温度、湿度、静电等,都可能影响触摸屏的正常工作。 + +#### 触摸响应延迟 + +触摸响应延迟可能是由以下几个因素导致的: + +1. **处理器过载**:如果嵌入式设备的处理器正在处理大量任务,它可能没有足够的资源来快速处理触摸输入,这会导致触摸响应延迟。 +2. **软件问题**:触摸屏的驱动程序或操作系统可能存在问题,导致触摸事件处理延迟。另外,如果运行在设备上的应用程序设计或优化不佳,也可能导致用户感觉到触摸响应延迟。 +3. **硬件问题**:如果触摸屏或其相关硬件存在问题,也可能导致触摸响应延迟。例如,触摸屏的感应器或控制器可能存在性能问题。 +4. **通信延迟**:在某些嵌入式系统中,触摸屏与处理器之间的数据传输可能存在延迟,这可能是由于接口速度较慢或数据线质量问题等原因造成的。 + + + +## 数码管 + +​ 数码管/LED 点阵是嵌入式系统中常见的显示方案,该方案比 LCD 显示屏占用更少的引脚和内存资源,实现也更加简单,比较适合计时、计数、状态显示等具有单一显示需求的应用场景。 + +数码管主要有LED数码管、LCD数码管和VFD数码管等类型。 + +1. **LED数码管** + + - **工作原理**:LED数码管是利用发光二极管(LED)的PN结正向导通时产生发光的原理制成的。数码管内部通常有7段或14段LED条组成,可以通过控制器控制这些LED条的亮暗,从而显示出各种数字或字符。 + - **特点**:LED数码管具有亮度高、视角大、寿命长、耐震性能好等优点,缺点是功耗较大。 + - **应用**:广泛应用于各种电子设备和仪表中,如电子表格、计时器、计算器和闹钟等。 + +2. **LCD数码管** + + - **工作原理**:LCD数码管利用液晶材料在电场作用下对光的传输性质产生变化的特性来显示信息。通常有7段或14段液晶条组成,通过控制器控制液晶的状态,从而显示各种数字或字符。 + - **特点**:LCD数码管具有功耗低、显示效果柔和、对环境适应性强等优点,缺点是视角较小、对温度敏感。 + - **应用**:广泛应用于各种便携式和低功耗电子设备,如手表、电子词典、移动电话等。 + +3. **VFD数码管** + + - **工作原理**:VFD数码管是利用气体放电原理来产生发光的。数码管内部通常有7段或14段荧光发射体,通过控制器控制发射体的放电状态,从而显示各种数字或字符。 + - **特点**:VFD数码管具有亮度高、显示颜色丰富、视角大、反应速度快等优点,缺点是功耗大、寿命较短。 + - **应用**:广泛应用于各种家电和娱乐设备中,如电视、DVD播放器、音响等。 + + + + # 驱动框图 + + + +![1687868024093](../media/hardware/display/nixie_tube_controller.png) + +1. **主控微处理器(MCU)**:主控微处理器是系统的核心,负责处理并执行所有的操作。它根据需要显示的信息,通过接口向显示驱动IC发送指令。 +2. **显示驱动IC**:显示驱动IC(也被称为数码管驱动IC)是一个专门用于驱动数码管显示的集成电路。它接收来自主控微处理器的指令,并根据这些指令来驱动数码管显示相应的数字或字符。 +3. **接口(SPI, I2C等)**:接口是用于连接主控微处理器和显示驱动IC的通信路径。主控微处理器通过接口向显示驱动IC发送指令。 +4. **数码管显示**:数码管是显示驱动IC驱动的显示设备。根据显示驱动IC的指令,数码管显示相应的数字或字符。 + + + +在一个基本的数码管显示系统中,这些组件的关系可以概括为以下几个步骤: + +- 主控微处理器根据需要显示的信息,生成相应的指令。 +- 主控微处理器通过接口将指令发送给显示驱动IC。 +- 显示驱动IC接收这些指令,根据指令驱动数码管显示相应的数字或字符。 + +这就是一个基本的数码管显示系统的工作原理。不同的数码管显示系统可能会有不同的具体实现,但大部分系统都会遵循这个基本模式。 + +### 适配说明 + +QuecPython系列芯片接口支持情况如下: + +| 名称 | 功能 | 接口 | 驱动 | 数据手册 | +| ------ | ------------------------------------------------- | ---- | ---------------- | ------------- | +| TM1650 | 带键盘扫描的LED驱动控制芯片,支持8段×4位和7段×4位 | I2C | [tm1650.py](XXX) | [TM1650](XXX) | + + + +#### TM1650 + +​ TM1650 是一种带键盘扫描接口的 LED(发光二极管显示器)驱动控制专用电路。内部集成有 MCU输入输出控制数字接口、数据锁存器、LED 驱动、键盘扫描、辉度调节等电路。 +​ 支持两种显示模式:8段×4位和7段×4位 +​ 可通过I2C接口与QuecPython模组进行通信 + +![typical_application_circuit](image\tm1650_typical_application_circuit.png) + +[tm1650脚本](XXX)已对TM1650的基本操作进行了封装,用户可以直接调用Tm1650.show_num Tm1650.show_str Tm1650.show_dp 等接口再数码管上进行显示 + + + +[示例](x'x'x) + +``` +from usr.tm1650 import Tm1650 +tube = Tm1650(Pin.GPIO13, Pin.GPIO12) #600U PIN60,PIN59 +tube.on() +tube.all_show() +utime.sleep(1) +tube.clear_bit(3) +utime.sleep(1) +tube.all_clear() +utime.sleep(1) +tube.show_dp(3) +utime.sleep(1) +tube.show_str("PPJ") +utime.sleep(1) +tube.show_num(-537) +utime.sleep(1) +tube.show_num(8537) +utime.sleep(1) +tube.circulate_show("AbCdEFH") +``` + + + +### 常见问题 + +- 数码管不亮:这可能是供电问题,如电源线接触不良或电源电压不足。也可能是数码管本身的问题,如内部电路损坏。此外,如果使用的是控制器驱动的数码管,驱动程序或控制器的硬件可能存在问题。 +- 数码管显示不全或显示错误:这通常是驱动电路或控制程序的问题。例如,如果驱动程序没有正确地设置或更新显示数据,可能会导致显示错误。 +- 数码管闪烁:如果数码管在没有意图的情况下闪烁,可能是供电不稳定或驱动程序问题。例如,如果供电电压不稳定,可能会导致数码管的亮度不断变化,造成闪烁现象。另外,如果驱动程序没有正确地刷新显示数据,也可能导致数码管闪烁。 +- 数码管亮度不均或颜色不一:这可能是数码管本身的质量问题,或者是驱动电路的问题。例如,如果数码管的某些LED不均匀地老化,可能会导致亮度不均或颜色不一。 + + diff --git a/docs/Application_guide/zh/network/socket/README.md b/docs/Application_guide/zh/hardware/peripheral-interfaces/README.md similarity index 100% rename from docs/Application_guide/zh/network/socket/README.md rename to docs/Application_guide/zh/hardware/peripheral-interfaces/README.md diff --git a/docs/Application_guide/zh/hardware/peripheral-interfaces/SPI.md b/docs/Application_guide/zh/hardware/peripheral-interfaces/SPI.md new file mode 100644 index 0000000000000000000000000000000000000000..04015bdbbf53df884d3c2af667c4defc8f9bb1f3 --- /dev/null +++ b/docs/Application_guide/zh/hardware/peripheral-interfaces/SPI.md @@ -0,0 +1,187 @@ +# SPI + +## SPI 概述 + +SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线。SPI使用了主设备和从设备的概念。在任何给定的SPI通信中,有一个主设备和一个或多个从设备。主设备负责启动和结束通信会话。蜂窝模组SPI默认做主设备,外部设备做从设备,若需要蜂窝模组做从设备请联系厂家。 + +通用的SPI接口一般包含4根通讯线: +- SCK:主要的作用是主设备往从设备传输时钟信号, 控制数据交换的时机以及速率; +- SS/CS:用于主设备片选从设备,使被选中的从设备能够被主设备所访问; +- SDO/MOSI:在主上面也被称为 Tx-Channel,作为数据的出口,主要用于 SPI 设备发送数据; +- SDI/MISO:在主上面也被称为 Rx-Channel,作为数据的入口,主要用于SPI 设备接收数据; + +SPI通信是全双工的,这意味着数据可以在两个方向上同事进行传输。对于SPI,这是通过MOSI(主设备输出,从设备输入)和MISO(主设备输入,从设备输出)线路实现的。 + +SPI共4种工作模式,由时钟极性(CPOL)和相位(CPHA)的不同配置决定,具体如下表所示: +|SPI Mode|CPOL|CPHA| +|-------|----|-----| +|0[00] | 0 | 0 | +|1[01] | 0 | 1 | +|2[10] | 1 | 0 | +|3[11] | 1 | 1 | + +## 不同平台下SPI支持情况 +BC25PA平台只支持SPI工作模式0、3,其他平台支持所有工作模式。 + +## Quecpython SPI API说明 + +### 构造函数 +```python +class machine.SPI(port, mode, clk) +``` +参数描述: +- port - 通道选择,int类型。选择通道前应查看WIKI,找到使用的SPI引脚对应通道。 +- mode - SPI的工作模式。选择工作模式时应查看外部SPI通信芯片数据手册,查看芯片支持哪些模式,如MCP2515只支持0、3两种模式,此时只能选择模式0或者模式3。 +- clk - 时钟频率。不同平台支持最高和最低时钟频率均有差异,如ASR平台最高支持52MHz时钟频率,最低812.kHz。选择时钟频率时,应在小于外部SPI通信芯片的最高支持时钟频率的条件下,尽量选择更高频率,提高通信速率。 + +### SPI读接口 +```python +SPI.read(recv_data, datalen) +``` +该方法用于读取数据。 +参数描述: +- recv_data - 接收读取数据的数组,bytearray类型。数据读取成功之后,读取到的数据将写入到recv_data中。 +- datalen - 读取数据的长度,int类型。使用时注意datalen不能大于recv_data的实际长度。 + +返回值描述: +成功返回整型值0,失败返回整型值-1。 + +### SPI写接口 +```python +SPI.write(data, datalen) +``` +该方法用于写入数据。 +参数描述: +- data - 写入的数据,bytes类型。 +- datalen。写入的数据长度,int类型。使用时注意datalen不能大于data的实际长度。 + +### SPI读写接口 +```python +SPI.write_read(r_data, data, datalen) +``` +该方法用于同时写入和读取数据。SPI通信本质上是主设备和从设备进行数据交换,在每个 Clock 周期内,SPI 设备都会发送并接收一个 bit 大小的数据(不管主设备好还是从设备),相当于该设备有一个 bit 大小的数据被交换了。所以SPI设备在读取数据的同时也在写入数据,同理写入数据的时候也会读取数据。上面的SPI读接口和SPI写接口只是使用时忽略另一方的数据。 +参数描述: +- r_data - 接收读取数据的数组,bytearray类型。 +- data - 发送的数据,bytes类型。 +- 写入的数据长度,int类型。使用时注意datalen不能大于data的实际长度。 + + +## SPI总线应用示例 + +### 和MCU交互 +和MCU交互时,蜂窝模组做Master(主)设备,MCU做Slave(从)设备。使用前需要MCU端提供协议,协议中应当清楚定义蜂窝模组读写数据含义。此时MCU端类似普通的SPI外设,需提供寄存器的读写含义。当蜂窝模组对寄存器写入值时,MCU可以获取蜂窝模组的指令;MCU端可以自主更新寄存器(模拟)值,等待蜂窝模组读取,从而获取MCU数据。 +示例如下: +1.MCU端虚拟寄存器表 +|寄存器号|读写属性|寄存器数据长度|含义| +|-------|--------|-----|-----| +|0 | W | 1|MCU GPIO1拉高状态,写入1时MCU拉高GPIO1| +|1 | R | 1|MCU 是否完成初始化| + +2.定义协议如下 +蜂窝模组端发送:0x7F(包头)+8位控制段(0:读指令,1:写指令)+ 8位寄存器号 + 8位数据长度 + N字节数据(读指令时为空) + +当MCU端收到蜂窝模组数据后,且判断数据合法后,MCU端回复: +0x7E(包头) + 8位寄存器号 + 8位数据长度 + N字节数据(写指令时为空) + +3.示例数据: +蜂窝模组-》MCU:0x7F 0x00 0x01 0x01(读取寄存器1数据) +MCU-》蜂窝模组:0x7E 0x01 0x01 0x01 (MCU完成初始化) + + +### SPI LCD显示屏 +使用蜂窝模组连接SPI LCD共有两种方式,一种是专门的LCD SPI接口,一种是通用SPI。 +蜂窝模组和LCD显示屏连接线如下图所示: + +- DOU引脚用于串行数据的传输。它是液晶显示屏与外部设备(如控制器或微控制器)之间的双向数据通信线路。 +- SCL/CLK引脚用于串行数据传输时的时钟同步。它提供了数据传输的时钟信号,确保数据的同步和正确传输。 +- RS/DC引脚用于指示发送给液晶显示屏的数据类型,即数据或命令。当RS/DC引脚为低电平时,表示发送的是命令;当RS引脚为高电平时,表示发送的是数据。 +- RST引脚用于复位液晶显示屏,将其恢复到初始状态。当RST引脚接收到复位信号时,显示屏将重新初始化,并清除之前的状态和数据。 +- CS引脚用于选择或激活液晶显示屏的芯片。当CS引脚为低电平时,表示选择该芯片进行通信或操作。 + +当使用LCD SPI引脚与LCD连接时,RS、RST和CS脚只能选择硬件手册中的指定引脚,如EC600U系列中CS脚为65、RST脚为64、RS脚为63。LCD SPI接口初始化LCD如下: +```python +lcd.lcd_init(lcd_init_data, lcd_width, lcd_hight, lcd_clk, data_line, line_num, lcd_type, lcd_invalid, lcd_display_on, lcd_display_off, lcd_set_brightness) + + +``` +当使用通用SPI引脚与LCD连接时,RS、RST和CS脚可更加需求任意选择合适引脚,但是引脚号需要在初始化中指定。如下: +```python +lcd.lcd_init(lcd_init_data, lcd_width, lcd_hight, lcd_clk, data_line, line_num, lcd_type, lcd_invalid, lcd_display_on, lcd_display_off, lcd_set_brightness, lcd_interface, spi_port, spi_mode, cs_pin, dc_pin, rst_pin) +``` +和LCD SPI引脚相比,初始化参数中增加了SPI模式、SPI口以及CS、DC、RST引脚。 + +具体示例可参考[WIKI-LCD](https://python.quectel.com/doc/API_reference/zh/medialib/audio.Audio.html)中的参考代码。 + + +### MCP2515 CAN控制器 +市面上大部分CAN控制器都是使用SPI总线通信,其中MCP2515是应用最为广泛的一款,所以这里详细介绍如何使用Quecpython SPI模块和MCP2515进行通信。 +模组和MCP2515连接示例图如下: + + +以下为MCP2515驱动中使用到SPI接口的代码 +```python +# 初始化SPI,使用SPI端口0、工作模式0、6.5MHz(EC00M模组) +self.spi = SPI(0, 0, 3) + +# 读单个寄存器值。INSTRUCTION.INSTRUCTION_READ:0x03(读指令) +# 参数: reg:寄存器号 +def readRegister(self, reg): + write_buf = bytearray([INSTRUCTION.INSTRUCTION_READ, reg, 0x00]) + read_buf = bytearray(len(write_buf)) + ret = self.spi.write_read(read_buf, write_buf, len(write_buf)) + if(ret != 0): + print("readRegister ret = ",ret) + return read_buf[2] + +# 读连续多个寄存器值 +# 参数: reg:起始寄存器号,n:连续读取寄存器数目 +def readRegisters(self, reg, n): + write_buf = bytearray(2+n) + write_buf[0] = INSTRUCTION.INSTRUCTION_READ + write_buf[1] = reg + read_buf = bytearray(len(write_buf)) + ret = self.spi.write_read(read_buf, write_buf, len(write_buf)) + if(ret != 0): + print("readRegisters ret = ",ret) + return read_buf[2:] + +# 写单个寄存器值 INSTRUCTION.INSTRUCTION_WRITE:0x02(写指令) +# 参数: reg:寄存器号,value:写入寄存器值 +def setRegister(self, reg, value): + write_buf = bytearray([INSTRUCTION.INSTRUCTION_WRITE, reg, value]) + ret = self.spi.write(write_buf, len(write_buf)) + if(ret != 0): + print("setRegister ret = ",ret) + +# 写连续多个寄存器值 INSTRUCTION.INSTRUCTION_WRITE:0x02(写指令) +# 参数: reg:寄存器号,values:写入寄存器值(bytearray) +def setRegisters(self, reg, values): + write_buf = bytearray([INSTRUCTION.INSTRUCTION_WRITE, reg]) + values + ret = self.spi.write(write_buf, len(write_buf)) + if(ret != 0): + print("setRegisters ret = ",ret) +``` + + +## SPI总线常见问题和故障排查 + +1.**硬件连接问题**: SPI接口需要四条线 (MISO、MOSI、SCLK和CS) 进行连接,如果这些线的连接不正确,可能导致通信失败。 + +2.**电源和地问题**: 如果SPI设备的电源和地没有正确连接,也可能导致通信问题。 + +3.**SPI模式错误**: SPI有四种模式,由时钟极性和相位决定。如果主设备和从设备的SPI模式不匹配,可能会导致通信错误。 + +4.**时钟频率过高**:如果SPI的时钟频率设置得过高,可能会超过设备的能力,导致数据传输错误。 + +5.**数据位数不匹配**: SPI的数据通常是8位或16位。如果发送和接收的数据位数不匹配,可能会导致数据错误。 + +6.**片选线控制错误**: 在多从设备的系统中,如果片选线没有被正确控制,可能会导致通信混乱。 + +7.**电平不匹配问题**: 如果主设备和从设备的电平(例如,3.3V和5V)不匹配,可能会导致通信问题或设备损坏。 + +8.**设备兼容性问题**: 由于SPI没有官方标准,不同设备的实现可能会有差异。这可能导致设备之间的兼容性问题。 + +9.**线路噪声和干扰**: 如果SPI线路过长,或者环境中有高频噪声,可能会影响SPI的信号质量导致通信错误。 + + + diff --git a/docs/Application_guide/zh/hardware/sensors/README.md b/docs/Application_guide/zh/hardware/sensors/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4e6a521461637e8675b98bd2a1e0de03f97d8781 --- /dev/null +++ b/docs/Application_guide/zh/hardware/sensors/README.md @@ -0,0 +1,9 @@ +# 概述 + +Quecpython现已提供对多种传感器的支持,包括温湿度传感器、加速度传感器和环境光传感器。这些传感器将为您的项目提供了更多的感知能力和数据采集功能,提供更智能化的功能和交互体验。 + +- [温湿度传感器](./humiture/README.md) + +- [加速度传感器](./accelerometer/README.md) + +- [环境光传感器](./light-sensor/README.md) \ No newline at end of file diff --git a/docs/Application_guide/zh/hardware/sensors/accelerometer/README.md b/docs/Application_guide/zh/hardware/sensors/accelerometer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..775f8e4532457074e46209d66a885d2acb76eb1b --- /dev/null +++ b/docs/Application_guide/zh/hardware/sensors/accelerometer/README.md @@ -0,0 +1,420 @@ +# 加速度传感器 + +加速度传感器包含线角加速度传感器(陀螺仪)、线加速度传感器等,能够实时检测设备的加速度、角速度和姿态等数据,以下是一些典型应用场景。 + +- 姿态感知:加速度传感器可以用于检测设备的姿态和方向变化。它们可以用于智能穿戴,智能安防系统等设备中,实现屏幕旋转、姿态识别和动作感应等功能。 +- 运动追踪:加速度传感器可以用于追踪和记录物体的加速度和运动轨迹。在嵌入式系统中,它们可以用于运动监测器、运动手表、智能手环等设备中,实现步数统计、睡眠监测和运动轨迹记录等功能。 +- 防抖动控制:加速度传感器可以用于控制设备的抖动和稳定性。它们可以用于相机防抖功能、电子稳定器、机器人控制等应用中,实现图像稳定和运动控制功能。 + +## 支持列表 + +**Quecpython目前已支持的加速度传感器型号表** + +| 型号 | 接口 | 规格书 | 代码链接 | +| -------- | ------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| LIS2DH12 | I2C/SPI | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/lis2dh12/LIS2DH12.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/lis2dh12/LIS2DH12.py) | +| ADXL346 | I2C/SPI | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/ADXL346/adxl346.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/ADXL346/adxl346.py) | +| BMA250 | I2C/SPI | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/bma250/BMA250-0273141121.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/bma250/bma250.py) | + +## 硬件介绍 + +本章节主要基于 LTE OPEN-EVB_V1.1和EC600U TE-A和LIS2DH12TR传感器模块,演示如何调试一款加速度传感器。 + +详见[LIS2DH12TR传感器硬件连接](./lis2dh12tr.md)。 + +## 软件设计 + +### 工作流程 + +LIS2DH12 支持的功能: + +1. 单双击检测 + +2. 自由落体检测 + +3. 倾斜角测量 + +4. 切换横屏/竖屏模式 + +我们使用其单击检测功能,出现单击事件,将其映射到INT1 引脚上面,其处理逻辑大致如下: + +![media_i2c_lis2dh_3](../../../media/hardware/sensors/gsensor_3.png) + +LIS2SH12 初始化步骤如下: + +1. 设置 CTRL_REG2 寄存器,开启高通滤波。 +2. 设置 CTRL_REG3 寄存器,将中断引到INT1 引脚上面。 +3. 设置 CTRL_REG4 寄存器,配置为满量程。 + +配置单击中断步骤如下: + +1. 配置 CLICK_CFG 寄存器,使能需要检测的感应轴,X,Y,Z。 +2. 配置 CLICK_THS 寄存器,设置阈值。 +3. 配置 TIME_LIMIT 寄存器,设置窗口限制。 +4. 配置 TIME_LATENCY 寄存器,设置延时。 + +LIS2SH12 使能传感器 + +1. 配置CTRL_REG1 寄存器,开始使能传感器。 + +### API参考 + +以下 API 实现了对lis2dh12传感器的功能抽象,用户可直接引用lis2dh12类,调用其API编写传感器应用程序。 + +> 不同加速度传感器的区别可能较大,用户若想编写自己的加速度传感器类,请先阅读对应规格书,可仿照本章源码进行开发。 + +**类引用:** + +```python +from lis2dh12 import lis2dh12 +``` + +**实例化:** + +```python +i2c_dev = I2C(I2C.I2C1, I2C.STANDARD_MODE) +g_sensor = lis2dh12(i2c_dev,14) +``` + +**参数描述:** + +- `i2c_obj` - I2C对象,必传,obj类型。 +- `int_pin` - 连接传感器int1的引脚gpio号,必传,int类型。 +- `dev_addr` - I2C从设备地址,非必传,int类型,缺省值0x19。 + +> 注意:SDO/SA0 引脚为高电平,`dev_addr`为0x19;SDO/SA0 引脚为低电平,`dev_addr`为0x18。 + +#### **`lis2dh12.sensor_reset`** + +重置传感器。 + +#### **`lis2dh12.int_enable`** + +中断使能。 + +**参数描述:** + +- `int_type` - 中断类型(可配单双击中断,自由落体中断),必传,int类型。
x轴单击:0x01,y轴单击:0x04,z轴单击:0x10,三轴单击:0x15
x轴双击:0x02,y轴双击:0x08,z轴双击:0x20,三轴双击:0x2A
自由落体:0x95。 +- `int_ths` - 中断阈值,必传,int类型。 +- `time_limit` - 时间限制(单双击事件),非必传,int类型,缺省值0x18。 +- `time_latency` - 延时(双击事件设置),非必传,int类型,缺省值0x12。 +- `time_window` - 时间窗口(双击事件设置),非必传,int类型,缺省值0x55。双击得在该段时间内完成。 +- `duration` - 事件的持续时间,非必传,int类型,缺省值0x03。 + +> `int_ths`用于设置触发中断的阈值。当传感器测量值(输入加速度)超过或低于该阈值时,会触发中断事件。 +> +> `time_limit`用于设置点击事件的时间限制。如果检测到两次输入加速度超过阈值的时间间隔小于该时间,则认为发生了点击事件。 +> +> `time_latency`用于设置双击事件的延时时间。在第一次点击事件之后,延时一段时间后开始检测第二次点击事件,如果第二次点击事件在延时时间内发生,则认为发生了双击事件。 +> +> `time_window`用于设置双击事件的时间窗口。时间窗口定义了第二次触发事件的时间范围。 +> +> `duration`用于指定某个事件的持续时间阈值。当事件的持续时间达到或超过该阈值时,可以触发相应的处理逻辑或中断。 + +#### **`lis2dh12.start_sensor`** + +启动传感器(使能`xyz`轴)。 + +#### **`lis2dh12.read_acceleration`** + +读取三轴加速度。 + +**返回值描述:** + +- `acceleration` - 三轴加速度`(x,y,z)`,tuple类型,单位:`G`。 + +#### **`lis2dh12.set_int_callback`** + +设置中断回调。(`int1`脚)。 + +#### **`lis2dh12.set_mode`** + +设置工作模式。 + +**参数描述:** + +- `mode` - 三轴加速度工作模式,必传,int类型,0:高分辨率模式,1:普通模式,2:低功耗模式。 + +### 实验设计 + +1. 使用LIS2DH12 传感器的 INT1 引脚产生中断。 +2. 轮询此引脚的状态,检测到上升沿以后, 表示中断产生,处理中断。 +3. 在中断函数里面读取三轴的状态。 + +### 实验代码 + +传感器类代码设计如下: + +```python +class lis2dh12(object): + ''' + lis2dh12 class + ''' + def __init__(self, i2c_dev, int_pin, slave_address=0x19): + ''' + :param i2c_dev: i2c object + :param int_pin: gpio of pin which is connected with int1_pin + :param slave_address: device address + ''' + self._address = slave_address + self._i2c_dev = i2c_dev + self._int_pin = int_pin + self._extint = None + self._sensor_init() + + def _read_data(self, regaddr, datalen): + ''' + i2c read data + :param regaddr: register address + :param datalen: length of reading data + :return: data + ''' + r_data = bytearray(datalen) + reg_addres = bytearray([regaddr]) + self._i2c_dev.read(self._address, reg_addres, 1, r_data, datalen, 1) + ret_data = list(r_data) + return ret_data + + def _write_data(self, regaddr, data): + ''' + i2c write data + :param regaddr: register address + :param data: data to write + ''' + addr = bytearray([regaddr]) + w_data = bytearray([data]) + self._i2c_dev.write(self._address, addr, len(addr), w_data, len(w_data)) + + def sensor_reset(self): + ''' + reset the sensor + ''' + # 重置chip + self._write_data(LIS2DH12_CTRL_REG5, 0x80) + + print('reboot already. {}'.format(self._read_data(LIS2DH12_CTRL_REG5,1))) + utime.sleep_ms(100) + r_data = self._read_data(LIS2DH12_WHO_AM_I, 1) + while r_data[0] != 0x33: + r_data = self._read_data(LIS2DH12_WHO_AM_I, 1) + utime.sleep_ms(5) + + def _sensor_init(self): + ''' + initialize the sensor + ''' + self.sensor_reset() + + self._write_data(LIS2DH12_CTRL_REG1, 0x77) # set ODR 400HZ ,enable XYZ. + utime.sleep_ms(20) # (7/ODR) = 18ms + self._write_data(LIS2DH12_CTRL_REG4, 0x08) # ±2g + + self._write_data(LIS2DH12_CLICK_CFG, 0) # clear click_cfg + self._write_data(LIS2DH12_INT1_CFG, 0) # clear int1_cfg + self._write_data(LIS2DH12_INT2_CFG, 0) # clear int2_cfg + + def int_enable(self,int_type,int_ths=0x12,time_limit=0x18,time_latency=0x12,time_window=0x55,duration=0x03): + ''' + interrupt enable + :param int_type: type of interrupt + :param int_ths: threshold + :param time_limit: click_int to send this parameter, time window limit + :param time_latency: click_int to send this parameter, set the time_latency + :param duration: + ''' + # single_click int + if int_type in (XYZ_SINGLE_CLICK_INT, X_SINGLE_CLICK_INT, Y_SINGLE_CLICK_INT, Z_SINGLE_CLICK_INT): + self._write_data(LIS2DH12_CTRL_REG2, 0x07) # Enable high pass filter for click function + self._write_data(LIS2DH12_CTRL_REG3, 0x80) # Bind interrupt to INT1 pin, default high level is valid + self._write_data(LIS2DH12_CTRL_REG5, 0x08) # INT1 latch + self._write_data(LIS2DH12_CLICK_CFG, int_type) # enable click_int + self._write_data(LIS2DH12_CLICK_THS, int_ths) # set threshold + self._write_data(LIS2DH12_TIME_LIMIT, time_limit) # set time_limit + # double_click int + elif int_type in (XYZ_DOUBLE_CLICK_INT, X_DOUBLE_CLICK_INT, Y_DOUBLE_CLICK_INT, Z_DOUBLE_CLICK_INT): + self._write_data(LIS2DH12_CTRL_REG2, 0x07) + self._write_data(LIS2DH12_CTRL_REG3, 0x80) + self._write_data(LIS2DH12_CTRL_REG5, 0x08) + self._write_data(LIS2DH12_CLICK_CFG, int_type) + self._write_data(LIS2DH12_CLICK_THS, int_ths) + self._write_data(LIS2DH12_TIME_LIMIT, time_limit) + self._write_data(LIS2DH12_TIME_LATENCY, time_latency) + self._write_data(LIS2DH12_TIME_WINDOW, time_window) + # 6d int + elif int_type in (MOVE_RECOGNIZE, X_MOVE_RECOGNIZE, Y_MOVE_RECOGNIZE, Z_MOVE_RECOGNIZE,POSI_CHANGE_RECOGNIZE, + X_POSI_CHANGE_RECOGNIZE,Y_POSI_CHANGE_RECOGNIZE,Z_POSI_CHANGE_RECOGNIZE,FF_RECOGNIZE): + self._write_data(LIS2DH12_CTRL_REG2, 0x00) # switch off the high pass filter + self._write_data(LIS2DH12_CTRL_REG3, 0x40) + self._write_data(LIS2DH12_CTRL_REG5, 0x08) + self._write_data(LIS2DH12_INT1_CFG, int_type) # enable 6d int + self._write_data(LIS2DH12_INT1_THS, int_ths) + self._write_data(LIS2DH12_INT1_DURATION, duration) # set duration + + def start_sensor(self): + ''' + start the sensor + ''' + self._write_data(LIS2DH12_CTRL_REG1, 0x77) # ODR 100HZ ,enable XYZ. + utime.sleep_ms(20) # (7/ODR) = 18ms + + def process_xyz(self): + ''' + Read registers and convert x-axis, y-axis, and z-axis data + :return: x,y,z data + ''' + data = [] + ctl4 = self._read_data(LIS2DH12_CTRL_REG4, 1)[0] + big_endian = ctl4 & 0x40 + # read xl,xh,yl,yh,zl,zh + for i in range(6): + r_data = self._read_data(LIS2DH12_OUT_X_L + i, 1) + data.append(r_data[0]) + if big_endian: + x = data[0] * 256 + data[1] + y = data[2] * 256 + data[3] + z = data[4] * 256 + data[5] + else: + x = data[1] * 256 + data[0] + y = data[3] * 256 + data[2] + z = data[5] * 256 + data[4] + return (x, y, z) + + def int_processing_data(self): + ''' + handle int_processing + :return: x,y,z-axis acceleration + ''' + acc = self.read_acceleration + int_src = self._read_data(LIS2DH12_INT1_SRC,1) # read INT1_SRC,clear interrupt request + return acc + + @property + def _resolution(self): + """ + resolution range. + :return: range_2_G, range_4_G, range_8_G,, range_16_G. + """ + ctl4 = self._read_data(LIS2DH12_CTRL_REG4,1)[0] + return (ctl4 >> 4) & 0x03 + + @property + def _acceleration(self): + """ + x,y,z-axis acceleration + :return: x,y,z-axis acceleration + """ + divider = 1 + accel_range = self._resolution + if accel_range == 3: # range_16_G + divider = 2048 + elif accel_range == 2: # range_8_G + divider = 4096 + elif accel_range == 1: # range_4_G + divider = 8192 + elif accel_range == 0: # range_2_G + divider = 16384 + x, y, z = self.process_xyz() + x = x / divider + y = y / divider + z = z / divider + if accel_range == 3: # range_16_G + print('range_16_G') + x = x if x <= 16 else x - 32 + y = y if y <= 16 else y - 32 + z = z if z <= 16 else z - 32 + elif accel_range == 2: # range_8_G + print('range_8_G') + x = x if x <= 8 else x - 16 + y = y if y <= 8 else y - 16 + z = z if z <= 8 else z - 16 + elif accel_range == 1: # range_4_G + print('range_4_G') + x = x if x <= 4 else x - 8 + y = y if y <= 4 else y - 8 + z = z if z <= 4 else z - 8 + elif accel_range == 0: # range_2_G + print('range_2_G') + x = x if x <= 2 else x - 4 + y = y if y <= 2 else y - 4 + z = z if z <= 2 else z - 4 + return (x, y, z) + + @property + def read_acceleration(self): + ''' + read acceleration + :return: x,y,z-axis acceleration + ''' + while 1: + status = self._read_data(LIS2DH12_STATUS_REG,1)[0] + xyzda = status & 0x08 # if xyz data exists, set 1 + xyzor = status & 0x80 + if not xyzda: + continue + else: + x,y,z = self._acceleration + return (x, y, z) + + + def set_mode(self,mode): + """ + set work mode + :param mode: 0: High resolution mode; 1: Normal mode; 2: Low power mode; + :return: None + """ + if mode == 0: + self._write_data(LIS2DH12_CTRL_REG1, 0x77) # ODR 400HZ ,enable XYZ. + self._write_data(LIS2DH12_CTRL_REG4, 0x08) # ±2g, High resolution mode + elif mode == 1: + self._write_data(LIS2DH12_CTRL_REG1, 0x57) # ODR 100HZ ,enable XYZ. + self._write_data(LIS2DH12_CTRL_REG4, 0x08) # ±2g, Normal mode + elif mode == 2: + self._write_data(LIS2DH12_CTRL_REG1, 0x8f) + self._write_data(LIS2DH12_CTRL_REG4, 0x08) # ±2g, Low power mode + else: + print("wrong mode.") + + def set_int_callback(self, cb): + self._extint = ExtInt(self._int_pin, ExtInt.IRQ_FALLING, ExtInt.PULL_PU, cb) + +``` + +> **注意** +> +> - 中断使能接口`int_enable`的参数需结合规格书和具体应用场景决定,比如希望中断不会经常误触发,则阈值参数`int_ths`和持续时间参数`duration`需要稍微设置较大,数值随具体测试效果而定。 +> - 三轴传感器在z轴竖直向上放置且没有外力作用的时候,`x`,`y`,`z`轴的加速度基本是(0,0,1),单位`G`。据此可以判断传感器读取计算加速度是否正常。 +> - 开发的时候尽量用[外部中断](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.ExtInt.html)的方式来处理中断,而非轮询传感器中断寄存器的方式,后者会导致进不了低功耗模式。 + +主程序代码设计如下: + +```python +def int_cb(args): + print('click just happened...') + acc = dev.int_processing_data() + print("read_acceleration result: ", acc) + +if __name__ == "__main__": + # initialize i2c + i2c_dev = I2C(I2C.I2C1, I2C.STANDARD_MODE) + # initialize the lis2dh12 object + dev = lis2dh12(i2c_dev, 14) + # enable single_click_interrupt + dev.int_enable(XYZ_SINGLE_CLICK_INT) + # set interrupt callback + dev.set_int_callback(int_cb) + # start sensor + dev.start_sensor() + # LP mode + dev.set_mode(2) + + print("interrupt detecting...") +``` + +## 效果验证 + +​ 将实验代码下载至模组中运行,单击传感器,可见交互界面出现如下图的打印,括号内打印的是触发瞬间的三个轴的加速度,单位为`G`: + +![image-20230630145011109](../../../media/hardware/sensors/gsensor_4.png) + + + diff --git a/docs/Application_guide/zh/hardware/sensors/accelerometer/lis2dh12tr.md b/docs/Application_guide/zh/hardware/sensors/accelerometer/lis2dh12tr.md new file mode 100644 index 0000000000000000000000000000000000000000..19e45d236a2c2cdae59b88d8b63334f52d5ae1ea --- /dev/null +++ b/docs/Application_guide/zh/hardware/sensors/accelerometer/lis2dh12tr.md @@ -0,0 +1,39 @@ +## 硬件介绍 + +本章节主要基于 LTE OPEN-EVB_V1.1和EC600U TE-A和LIS2DH12TR传感器模块,演示如何调试一款加速度传感器。 + + LIS2DH12TR传感器是一款高性能 3 轴线性加速度传感器,具有数字 I2C、SPI串行接口标准输出。 器件具有超低功耗工作模式,可实现高级节能、智能、睡眠唤醒以及恢复睡眠功能。 LIS2DH12TR具有 ±2g/±4g/±8g/±16g 的动态用户可选满量程,并能通过 1 Hz 到 5 kHz 的输出数据速率测量加速度。器件可配置为通过独立的惯性唤醒 / 自由落体事件以及通过器件自身的位置生成中断信号。中断发生器的阈值 和时序可由终端用户动态设定。 + +### 封装规格 + +LIS2DH12TR传感器规格图如下: + +![media_i2c_lis2dh_1](../../../media/hardware/sensors/gsensor_1.png) + +### 硬件连接 + +引脚功能介绍: + +| 引脚号 | 引脚名 | 说明 | +| ------ | --------------------- | ------------------------------------------------------------ | +| 1 | SCL
SPC | I2C/SPI时钟引脚。 | +| 2 | CS | SPI使能引脚,控制芯片通信方式。
1:I2C通信;2:SPI通信。 | +| 3 | SDO
SA0 | SPI模式下为数据输出引脚;
I2C模式为设备从地址选择引脚,1:0x19;0:0x18。 | +| 4 | SDA
SDI
SDO | I2C模式下为数据引脚;
标准SPI模式下为数据输入引脚;
也可以作为SPI的数据输出引脚。 | +| 5 | Res | 接地即可。 | +| 6 | GND | 地 | +| 7 | GND | 地 | +| 8 | GND | 地 | +| 9 | VDD | 电源 | +| 10 | VDD_IO | IO参考电平引脚 | +| 11 | INT2 | 中断引脚2 | +| 12 | INT1 | 中断引脚1 | + +本章我们采用 I2C 接口通讯,供电电压采用1.8V。具体接线为VCC接J0202 的2脚,GND接J0202的30脚,SCL接J0201的17脚,SDA接J0201的14脚,SDO/SA0接J0202的1脚,CS接J1006的9脚,INT1接J0201的2脚;LIS2DH12加速度模块与EVB板接线图如下。 + +![media_i2c_lis2dh_2](../../../media/hardware/sensors/gsensor_2.png) + +SDO/SA0 引脚为高电平, 因此可以确认加速度传感器的从机地址为0x19。 + + + diff --git a/docs/Application_guide/zh/hardware/sensors/humiture/README.md b/docs/Application_guide/zh/hardware/sensors/humiture/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7c10a1a0ed9684e6cc9e46fd8515e8f35735a4f8 --- /dev/null +++ b/docs/Application_guide/zh/hardware/sensors/humiture/README.md @@ -0,0 +1,181 @@ +# 温湿度传感器 + +温湿度传感器采用高精度传感技术,能够实时监测环境的温度和湿度变化,以下是一些典型应用场景。 + +- 智能家居:温湿度传感器可以嵌入智能家居系统中,监测各个房间的温湿度情况。系统可根据传感器数据,自动控制空调、加湿器等设备的运行,提供更舒适的居住环境。 +- 智能农业和温室:在农业领域,温湿度传感器可用于监测农作物生长环境的温度和湿度。这对于控制温室条件、提供适宜的生长环境和实现智能灌溉等都非常重要。 +- 工业自动化:在工业自动化领域,温湿度传感器可以用于监测生产线上的环境条件。它可以帮助控制和调节工业设备的运行,确保生产过程的稳定性和产品质量。 + +## 支持列表 + +**Quecpython目前已支持的温湿度传感器型号表** + +| 型号 | 接口 | 规格书 | 代码链接 | +| ------- | ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| HDC1080 | I2C | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/HDC1080/hdc1080.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/HDC1080/hdc1080.py) | +| HDC2080 | I2C | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/HDC2080/hdc2080.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/HDC2080/hdc2080.py) | +| AHT10 | I2C | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/aht10/AHT10.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/aht10/aht10.py) | +| AHT20 | I2C | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/aht20/AHT20-datasheet.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/aht20/aht20.py) | + +## 硬件介绍 + +本章节基于 LTE OPEN-EVB_V1.1和EC600U TE-A开发板和HDC1080传感器模块,演示如何调试一款温湿度传感器。 + +详见[HDC1080传感器硬件连接](./hdc1080.md)。 + +## 软件设计 + +### 工作流程 + +如下图所示为一个典型的温湿度传感器的工作流程。 + +![HDC1080_4](../../../media/hardware/sensors/htsensor_4.png) + +### API参考 + +以下 API 实现了对Hdc1080传感器的功能抽象,用户可直接引用Hdc1080类,调用其API编写传感器应用程序。 + +> 不同温湿度传感器与MCU通信接口和温湿度计算方式可能有点差异,用户可以根据规格书,调整上述两点来实现自己的传感器类。 + +**类引用:** + +```python +from hdc1080 import Hdc1080 +``` + +**实例化:** + +```python +i2c_dev = I2C(I2C.I2C1, I2C.STANDARD_MODE) +hdc = Hdc1080(i2c_dev) +``` + +**参数描述:** + +- `i2c_obj` - I2C对象,obj类型。 +- `dev_addr` - I2C从设备地址,int类型,默认0x40。 + +#### **`Hdc108.read`** + +读取湿度和温度。 + +**返回值描述:** + +- `(humidity,temperature)` - 湿度,温度,tuple类型。 + +#### **`Hdc108.read_temperature`** + +读取温度。 + +**返回值描述:** + +- `temperature` - 温度,float类型。 + +#### **`Hdc108.read_humidity`** + +读取湿度。 + +**返回值描述:** + +- `temperature` - 温度,float类型。 + +#### **`Hdc108.reset`** + +重置传感器。 + +### 实验设计 + +1. 初始化HDC1080传感器。 +2. 循环读取10次温度和湿度并打印。 + +### 实验代码 + +传感器类设计如下: + +```python +class Hdc1080(object): + ''' + HDC1080 sensor class + ''' + def __init__(self,i2c,addr=HDC1080_ADDRESS): + self._i2c = i2c + self._i2c_addr = addr + + time.sleep_ms(15) + dev_id = self._read_data(HDC1080_DEVICEID_REGISTER,2) + dev_id = (dev_id[0] << 8) | dev_id[1] + print("dev_id is 0x%4x"%dev_id) + if dev_id != 0x1050: + raise CustomError("HDC1080 manu id err.") + + self.reset() + self._write_data(HDC1080_CONFIGURATION_REGISTER,[0x10,0x00]) + time.sleep_ms(15) + print("sensor init complete.") + + def _write_data(self, reg, data): + self._i2c.write(self._i2c_addr, + bytearray([reg]), len([reg]), + bytearray(data), len(data)) + + def _read_data(self,reg, length): + r_data = [0x00 for i in range(length)] + r_data = bytearray(r_data) + ret = self._i2c.read(self._i2c_addr, + bytearray([reg]), len([reg]), + r_data, length, + 0) + return list(r_data) + + def _read_data_delay(self,reg, length): + r_data = [0x00 for i in range(length)] + r_data = bytearray(r_data) + ret = self._i2c.read(self._i2c_addr, + bytearray([reg]), len([reg]), + r_data, length, + 150) + return list(r_data) + + def reset(self): + self._write_data(HDC1080_CONFIGURATION_REGISTER, [0x10, 0x00]) + + def read_temperature(self): + tem_data = self._read_data_delay(HDC1080_TEMPERATURE_REGISTER,2) + tem_data = (tem_data[0] << 8) | tem_data[1] + tem = (tem_data / 65536) * 165 - 40 + return tem + + def read_humidity(self): + hum_data = self._read_data_delay(HDC1080_HUMIDITY_REGISTER,2) + hum_data = (hum_data[0] << 8) | hum_data[1] + hum = (hum_data / 65536) * 100 + return hum + + def read(self): + return (self.read_temperature(),self.read_humidity()) +``` + +> **注意** +> +> - 初始化过程中读取设备ID错误,可能是因为总线通信失败,或是规格书中的设备ID与代码中的不符。 +> - 如您正在调试一款支持列表中未包含的型号时,请先仔细阅读传感器的规格书,可以仿照上述代码流程进行开发。 + +实验主程序设计如下: + +```python +if __name__ == "__main__": + i2c_dev = I2C(I2C.I2C1, I2C.FAST_MODE) + hdc=Hdc1080(i2c_dev) + for i in range(10): + print("test %d begin." % (i + 1)) + tem, hum = hdc.read() + print("current humidity is {0}%RH,current temperature is {1}°C".format(hum, tem)) + print("test %d end." % (i + 1)) + time.sleep(1) +``` + +## 效果验证 + +将实验代码下载至模组中运行,可以使用不同温度和湿度的物体接触传感器,观察测量数值的变化,结果打印如下图所示。 + +![image-20230629150837260](../../../media/hardware/sensors/htsensor_5.jpg) diff --git a/docs/Application_guide/zh/hardware/sensors/humiture/hdc1080.md b/docs/Application_guide/zh/hardware/sensors/humiture/hdc1080.md new file mode 100644 index 0000000000000000000000000000000000000000..84236fced693321456b2151cae1acda5986a0935 --- /dev/null +++ b/docs/Application_guide/zh/hardware/sensors/humiture/hdc1080.md @@ -0,0 +1,206 @@ +# 温湿度传感器 + +温湿度传感器采用高精度传感技术,能够实时监测环境的温度和湿度变化,以下是一些典型应用场景。 + +- 智能家居:温湿度传感器可以嵌入智能家居系统中,监测各个房间的温湿度情况。系统可根据传感器数据,自动控制空调、加湿器等设备的运行,提供更舒适的居住环境。 +- 智能农业和温室:在农业领域,温湿度传感器可用于监测农作物生长环境的温度和湿度。这对于控制温室条件、提供适宜的生长环境和实现智能灌溉等都非常重要。 +- 工业自动化:在工业自动化领域,温湿度传感器可以用于监测生产线上的环境条件。它可以帮助控制和调节工业设备的运行,确保生产过程的稳定性和产品质量。 + +## 支持列表 + +**Quecpython目前已支持的温湿度传感器型号表** + +| 型号 | 接口 | 规格书 | 代码链接 | +| ------- | ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| HDC1080 | I2C | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/HDC1080/hdc1080.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/HDC1080/hdc1080.py) | +| HDC2080 | I2C | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/HDC2080/hdc2080.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/HDC2080/hdc2080.py) | +| AHT10 | I2C | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/aht10/AHT10.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/aht10/aht10.py) | +| AHT20 | I2C | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/aht20/AHT20-datasheet.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/aht20/aht20.py) | + +## 硬件介绍 + +本章节基于 LTE OPEN-EVB_V1.1和EC600U TE-A开发板和HDC1080传感器模块,演示如何调试一款温湿度传感器。 + +### 封装规格 + +HDC1080(6 引脚)规格图如下: + +![image-20230628141028005](../../../media/hardware/sensors/htsensor_1.png) + +**典型应用** + +![image-20230628140301750](../../../media/hardware/sensors/htsensor_2.png) + +### 硬件连接 + +引脚功能介绍: + +| 引脚号 | 引脚名 | 说明 | +| ------ | ------ | --------- | +| 1 | SDA | I2C数据线 | +| 2 | GND | 接地 | +| 3 | NC | 不接 | +| 4 | NC | 不接 | +| 5 | VDD | 电源 | +| 6 | SCL | I2C时钟线 | + +本章实验采用 I2C 接口通讯,供电电压采用3.3V。具体接线为VCC接J1003 的VDD_3.3脚,GND接J0202的30脚,SCL接J0201的17脚,SDA接J0201的14脚,HDC1080传感器模块与EVB板接线如下图所示。 + +![hdc1080_senser_3](../../../media/hardware/sensors/htsensor_3.jpg) + +## 软件设计 + +### 工作流程 + +如下图所示为一个典型的温湿度传感器的工作流程。 + +![HDC1080_4](../../../media/hardware/sensors/htsensor_4.png) + +### API参考 + +以下 API 实现了对Hdc1080传感器的功能抽象,用户可直接引用Hdc1080类,调用其API编写传感器应用程序。 + +> 不同温湿度传感器与MCU通信接口和温湿度计算方式可能有点差异,用户可以根据规格书,调整上述两点来实现自己的传感器类。 + +**类引用:** + +```python +from hdc1080 import Hdc1080 +``` + +**实例化:** + +```python +i2c_dev = I2C(I2C.I2C1, I2C.STANDARD_MODE) +hdc = Hdc1080(i2c_dev) +``` + +**参数描述:** + +- `i2c_obj` - I2C对象,obj类型。 +- `dev_addr` - I2C从设备地址,int类型,默认0x40。 + +#### **`Hdc108.read`** + +读取湿度和温度。 + +**返回值描述:** + +- `(humidity,temperature)` - 湿度,温度,tuple类型。 + +#### **`Hdc108.read_temperature`** + +读取温度。 + +**返回值描述:** + +- `temperature` - 温度,float类型。 + +#### **`Hdc108.read_humidity`** + +读取湿度。 + +**返回值描述:** + +- `temperature` - 温度,float类型。 + +#### **`Hdc108.reset`** + +重置传感器。 + +### 实验设计 + +1. 初始化HDC1080传感器。 +2. 循环读取10次温度和湿度并打印。 + +### 实验代码 + +传感器类设计如下: + +```python +class Hdc1080(object): + ''' + HDC1080 sensor class + ''' + def __init__(self,i2c,addr=HDC1080_ADDRESS): + self._i2c = i2c + self._i2c_addr = addr + + time.sleep_ms(15) + dev_id = self._read_data(HDC1080_DEVICEID_REGISTER,2) + dev_id = (dev_id[0] << 8) | dev_id[1] + print("dev_id is 0x%4x"%dev_id) + if dev_id != 0x1050: + raise CustomError("HDC1080 manu id err.") + + self.reset() + self._write_data(HDC1080_CONFIGURATION_REGISTER,[0x10,0x00]) + time.sleep_ms(15) + print("sensor init complete.") + + def _write_data(self, reg, data): + self._i2c.write(self._i2c_addr, + bytearray([reg]), len([reg]), + bytearray(data), len(data)) + + def _read_data(self,reg, length): + r_data = [0x00 for i in range(length)] + r_data = bytearray(r_data) + ret = self._i2c.read(self._i2c_addr, + bytearray([reg]), len([reg]), + r_data, length, + 0) + return list(r_data) + + def _read_data_delay(self,reg, length): + r_data = [0x00 for i in range(length)] + r_data = bytearray(r_data) + ret = self._i2c.read(self._i2c_addr, + bytearray([reg]), len([reg]), + r_data, length, + 150) + return list(r_data) + + def reset(self): + self._write_data(HDC1080_CONFIGURATION_REGISTER, [0x10, 0x00]) + + def read_temperature(self): + tem_data = self._read_data_delay(HDC1080_TEMPERATURE_REGISTER,2) + tem_data = (tem_data[0] << 8) | tem_data[1] + tem = (tem_data / 65536) * 165 - 40 + return tem + + def read_humidity(self): + hum_data = self._read_data_delay(HDC1080_HUMIDITY_REGISTER,2) + hum_data = (hum_data[0] << 8) | hum_data[1] + hum = (hum_data / 65536) * 100 + return hum + + def read(self): + return (self.read_temperature(),self.read_humidity()) +``` + +> **注意** +> +> - 初始化过程中读取设备ID错误,可能是因为总线通信失败,或是规格书中的设备ID与代码中的不符。 +> - 如您正在调试一款支持列表中未包含的型号时,请先仔细阅读传感器的规格书,可以仿照上述代码流程进行开发。 + +实验主程序设计如下: + +```python +if __name__ == "__main__": + i2c_dev = I2C(I2C.I2C1, I2C.FAST_MODE) + hdc=Hdc1080(i2c_dev) + for i in range(10): + print("test %d begin." % (i + 1)) + tem, hum = hdc.read() + print("current humidity is {0}%RH,current temperature is {1}°C".format(hum, tem)) + print("test %d end." % (i + 1)) + time.sleep(1) +``` + +## 效果验证 + +将实验代码下载至模组中运行,可以使用不同温度和湿度的物体接触传感器,观察测量数值的变化,结果打印如下图所示。 + +![image-20230629150837260](../../../media/hardware/sensors/htsensor_5.jpg) diff --git a/docs/Application_guide/zh/hardware/sensors/light-sensor/README.md b/docs/Application_guide/zh/hardware/sensors/light-sensor/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0b75b757dc501620728c6ec1a03cc067b76fefb9 --- /dev/null +++ b/docs/Application_guide/zh/hardware/sensors/light-sensor/README.md @@ -0,0 +1,185 @@ +# 环境光传感器 + +环境光传感器包含光照强度传感器、颜色传感器、紫外线传感器和兼具多种功能的传感器。它可以提供多种环境光相关的数据,包括光照强度、颜色信息和紫外线辐射等,为系统提供丰富的环境感知能力。以下是一些典型应用场景。 + +- 智能亮度调节:环境光传感器可以测量周围环境的光照强度,并根据测量结果自动调节显示屏、背光灯或照明设备的亮度。这可以实现室内和室外显示设备的舒适视觉体验,并节省能源。 +- 智能农业系统:在农业领域,光照强度传感器可以用于监测环境的光照水平。根据测量数据,触发自动调光系统或实现节能措施。 +- 健康监测设备:紫外线传感器可以测量环境中的紫外线辐射强度。应用在穿戴设备上,可在户外环境中对紫外线辐射水平进行实时监测,并提醒用户采取适当的防护措施。 + +## 支持列表 + +**Quecpython目前已支持的环境光传感器型号表** + +| 型号 | 类型 | 总线 | 规格书 | 代码链接 | +| ------- | ------------ | ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| OPT3001 | 光照度传感器 | I2C | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/OPT3001/opt3001.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/OPT3001/opt3001.py) | +| BH1750 | 光照度传感器 | I2C | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/BH1750(GY-302)/bh1750fvi-e-186247.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/BH1750(GY-302)/BH1750.py) | +| GL5516 | 光敏电阻 | ADC | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/gl5516/GL55.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/gl5516/gl5516.py) | +| GL5528 | 光敏电阻 | ADC | [规格书](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/gl5528/GL55.pdf) | [代码链接](https://github.com/QuecPython/QuecPython_lib_bundles/blob/master/libraries/gl5528/gl5528.py) | + +## 硬件介绍 + +本章节主要基于 LTE OPEN-EVB_V1.1和EC600U TE-A和OPT3001传感器模块,演示如何调试一款光照传感器。 + +详见[OPT3001传感器硬件连接](./opt3001.md)。 + +## 软件设计 + +### 工作流程 + +如下图所示为一个典型的光照度传感器的工作流程。 + +![OPT3001_1](../../../media/hardware/sensors/isensor_5.png) + +### API参考 + +以下 API 实现了对Opt3001传感器的功能抽象,用户可直接引用Opt3001类,调用其API编写传感器应用程序。 + +> 不同光照传感器通信总线和温湿度计算方式可能有点差异,用户可以根据规格书,调整这两点来实现自己的传感器类。 + +**类引用:** + +```python +from opt3001 import Opt3001 +``` + +**实例化:** + +```python +i2c_obj=I2C(I2C.I2C1,I2C.FAST_MODE) +opt = Opt3001(i2c_obj) +``` + +**参数描述:** + +- `i2c_obj` - I2C对象,obj类型。 +- `dev_addr` - I2C从设备地址,int类型,默认0x44。 + +#### **`Opt3001.set_measure_mode`** + +设置测量模式,持续测量和单次测量或休眠模式。 + +**参数描述:** + +- `mode` - 测量模式,非必填,int类型,缺省值2:连续测量模式。还可选0:休眠模式,1:单次测量模式。 + +**返回值描述:** + +- `-1` - 失败。 +- `0` - 成功。 + +#### **`Opt3001.read `** + +读取照度值。 + +> **注意:**转化时间初始化为800ms,两次读取之间间隔需大于800ms。 + +**返回值描述:** + +- `illuminance` - 照度,float类型。 + +### 实验设计 + +1.初始化EC600U的I2C 1通道,通过I2C来读取opt3001数据。 + +2.配置opt3001寄存器。 + +3.设置测量模式为连续测量模式,将读取值转化成lux值。 + +### 实验代码 + +​ 传感器类设计如下: + +```python +class Opt3001(object): + def __init__(self,i2c,dev_addr=I2C_ADDR): + self._i2c = i2c + self._i2c_addr = dev_addr + + manu_id = self._read_data(I2C_LS_REG_MANUFACTURERID, 2) + manu_id = (manu_id[0] << 8) | manu_id[1] + print(manu_id) + if manu_id != 0x5449: + raise Exception("OPT3001 manu id err.") + + self._write_data(I2C_LS_REG_CONFIG, [0xcc,0x10]) + utime.sleep_ms(15) + print("sensor init complete.") + + def _write_data(self, reg, data): + self._i2c.write(self._i2c_addr, + bytearray([reg]), len([reg]), + bytearray(data), len(data)) + + def _read_data(self, reg, length): + r_data = [0x00 for i in range(length)] + r_data = bytearray(r_data) + ret = self._i2c.read(self._i2c_addr, + bytearray([reg]), len([reg]), + r_data, length, + 0) + return list(r_data) + + def set_measure_mode(self,mode=CONTINU_MODE): + ''' + Set the measurement mode, continuous measurement and single measurement or shutdown + :param mode: measurement mode 0-off 1-single 2-continuous + :return: 0-success -1-mode wrong selection + ''' + if mode not in range(3): + return -1 + r_data = self._read_data(I2C_LS_REG_CONFIG, 2) + r_data = (r_data[0] << 8) | r_data[1] + print(r_data) + w_data = (r_data & 0xf9ff) | (mode << 9) + print(w_data) + self._write_data(I2C_LS_REG_CONFIG, [(w_data >> 8), (w_data & 0xff)]) + utime.sleep_ms(20) + return 0 + + def read(self): + ''' + The read value is converted into a lux value + please ensure that the measurement mode is not 0, otherwise it will get stuck + @return: illuminance value,Unit:lux + ''' + while 1: + r_data = self._read_data(I2C_LS_REG_CONFIG, 2)[1] + if (r_data & (1 << 7)): #converted flag + lux_ori = self._read_data(I2C_LS_REG_RESULT,2) + lux_ori = (lux_ori[0] << 8) | lux_ori[1] + #convert illuminance value + mantisse = lux_ori & 0x0fff + exponent = (lux_ori & 0xf000) >> 12 + lux = 2 ** exponent * mantisse * 0.01 + return lux + +``` + +> **注意** +> +> - 初始化过程中读取设备ID错误,可能是因为总线通信失败,或是规格书中的设备ID与代码中的不符。 +> - 如您正在调试一款支持列表中未包含的型号时,请先仔细阅读传感器的规格书,可以仿照上述代码流程进行开发。 + +主程序设计如下: + +```python +if __name__ == "__main__": + i2c_obj=I2C(I2C.I2C1,I2C.FAST_MODE) + opt = Opt3001(i2c_obj) + for i in range(20): + opt.set_measure_mode(1) + utime.sleep_ms(1000) # at least 800ms + print("measurement times:{}------------".format(i+1)) + lux = opt.read() + print("The light intensity is {0} lux".format(lux)) +``` + +## 效果验证 + +将示例代码下载进模组运行,运行期间,可以选择用手遮盖或者手电筒照射,可以观察到OPT3001光敏传感器的照度值变化 + +![media_photosensitive_sensor_5](../../../media/hardware/sensors/isensor_6.png) + + + diff --git a/docs/Application_guide/zh/hardware/sensors/light-sensor/opt3001.md b/docs/Application_guide/zh/hardware/sensors/light-sensor/opt3001.md new file mode 100644 index 0000000000000000000000000000000000000000..3fdb0770375a3faee4c15706a89fd04e72a4933d --- /dev/null +++ b/docs/Application_guide/zh/hardware/sensors/light-sensor/opt3001.md @@ -0,0 +1,35 @@ +# 硬件介绍 + +本章节主要基于 LTE OPEN-EVB_V1.1和EC600U TE-A和OPT3001传感器模块,演示如何调试一款光照传感器。 + +OPT3001是一款低功耗环境光照度传感器,用于测量人眼可见的光强度。OPT3001自动采集转换数据,但不会主动上报,我们可以通过I2C协议读写OPT3001的寄存器来控制传感器。 + +### 封装规格 + +OPT3001(6 引脚)规格图如下: + +![image-20230629194734984](../../../media/hardware/sensors/isensor_1.png) + +### 硬件连接 + +引脚功能介绍: + +| 引脚号 | 引脚名 | 说明 | +| ------ | ------ | --------- | +| 1 | VCC | 电源 | +| 2 | SCL | I2C时钟线 | +| 3 | SDA | I2C数据线 | +| 4 | GND | 接地 | +| 5 | INT | 中断输出 | +| 6 | ADDR | 地址引脚 | + +本章实验中,OPT3001模块需要接四根线到开发板上,VCC接VDD_1V8,如下图中2处。SCL和SDA我们选择接到EC600U的I2C1,如下图中的3处标注着GPIO_7、GPIO_6的位置。GND选择接地即可,INT引脚本章实验不接。 + +![media_photosensitive sensor_3](../../../media/hardware/sensors/isensor_3.jpg) + +接线完成后如下图 + +![media_photosensitive sensor_4](../../../media/hardware/sensors/isensor_4.jpg) + + + diff --git a/docs/Getting_started/zh/media/background/about-qpy/compiler-and-interpreter.png b/docs/Application_guide/zh/media/background/about-qpy/compiler-and-interpreter.png similarity index 100% rename from docs/Getting_started/zh/media/background/about-qpy/compiler-and-interpreter.png rename to docs/Application_guide/zh/media/background/about-qpy/compiler-and-interpreter.png diff --git a/docs/Getting_started/zh/media/background/about-qpy/csdk-and-qpy-dev-steps.png b/docs/Application_guide/zh/media/background/about-qpy/csdk-and-qpy-dev-steps.png similarity index 100% rename from docs/Getting_started/zh/media/background/about-qpy/csdk-and-qpy-dev-steps.png rename to docs/Application_guide/zh/media/background/about-qpy/csdk-and-qpy-dev-steps.png diff --git a/docs/Getting_started/zh/media/background/about-qpy/mpy-and-rpi-pico.png b/docs/Application_guide/zh/media/background/about-qpy/mpy-and-rpi-pico.png similarity index 100% rename from docs/Getting_started/zh/media/background/about-qpy/mpy-and-rpi-pico.png rename to docs/Application_guide/zh/media/background/about-qpy/mpy-and-rpi-pico.png diff --git a/docs/Getting_started/zh/media/background/about-qpy/python-opencv.png b/docs/Application_guide/zh/media/background/about-qpy/python-opencv.png similarity index 100% rename from docs/Getting_started/zh/media/background/about-qpy/python-opencv.png rename to docs/Application_guide/zh/media/background/about-qpy/python-opencv.png diff --git a/docs/Getting_started/zh/media/background/about-qpy/python-repl.gif b/docs/Application_guide/zh/media/background/about-qpy/python-repl.gif similarity index 100% rename from docs/Getting_started/zh/media/background/about-qpy/python-repl.gif rename to docs/Application_guide/zh/media/background/about-qpy/python-repl.gif diff --git a/docs/Getting_started/zh/media/background/about-qpy/qpy-advances.png b/docs/Application_guide/zh/media/background/about-qpy/qpy-advances.png similarity index 100% rename from docs/Getting_started/zh/media/background/about-qpy/qpy-advances.png rename to docs/Application_guide/zh/media/background/about-qpy/qpy-advances.png diff --git a/docs/Getting_started/zh/media/background/about-qpy/qpy-intro.png b/docs/Application_guide/zh/media/background/about-qpy/qpy-intro.png similarity index 100% rename from docs/Getting_started/zh/media/background/about-qpy/qpy-intro.png rename to docs/Application_guide/zh/media/background/about-qpy/qpy-intro.png diff --git a/docs/Getting_started/zh/media/background/about-qpy/qpy-solutions.png b/docs/Application_guide/zh/media/background/about-qpy/qpy-solutions.png similarity index 100% rename from docs/Getting_started/zh/media/background/about-qpy/qpy-solutions.png rename to docs/Application_guide/zh/media/background/about-qpy/qpy-solutions.png diff --git a/docs/Getting_started/zh/media/background/about-qpy/quecopen-and-quecpython.png b/docs/Application_guide/zh/media/background/about-qpy/quecopen-and-quecpython.png similarity index 100% rename from docs/Getting_started/zh/media/background/about-qpy/quecopen-and-quecpython.png rename to docs/Application_guide/zh/media/background/about-qpy/quecopen-and-quecpython.png diff --git a/docs/Application_guide/zh/media/background/hardware-platform/ASR1603.png b/docs/Application_guide/zh/media/background/hardware-platform/ASR1603.png new file mode 100644 index 0000000000000000000000000000000000000000..4950a9c53f0c07c7ba12e6ef9978268b76ba404a Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/ASR1603.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/ASR1606.png b/docs/Application_guide/zh/media/background/hardware-platform/ASR1606.png new file mode 100644 index 0000000000000000000000000000000000000000..9a7e15c572dc258bb1ea306ebb4c51a226f25225 Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/ASR1606.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/ASR1803.png b/docs/Application_guide/zh/media/background/hardware-platform/ASR1803.png new file mode 100644 index 0000000000000000000000000000000000000000..72980500e4e3afe1cf3d06dcd5f991260cedccd4 Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/ASR1803.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/EC618.png b/docs/Application_guide/zh/media/background/hardware-platform/EC618.png new file mode 100644 index 0000000000000000000000000000000000000000..3ca618ff01a61dc206729e8bc03b9f02ab24e65b Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/EC618.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/MDM9205.png b/docs/Application_guide/zh/media/background/hardware-platform/MDM9205.png new file mode 100644 index 0000000000000000000000000000000000000000..46f0f04d2ab774cd3d570423296009c8deb9574c Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/MDM9205.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/RDA8908A.png b/docs/Application_guide/zh/media/background/hardware-platform/RDA8908A.png new file mode 100644 index 0000000000000000000000000000000000000000..6f08b76b4d83303a20e2928e4e7cc14423615321 Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/RDA8908A.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/UIS8850DG.png b/docs/Application_guide/zh/media/background/hardware-platform/UIS8850DG.png new file mode 100644 index 0000000000000000000000000000000000000000..1d1589d63fe204ebf4276e58418bda12fe2dba28 Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/UIS8850DG.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/UIS8910DM.png b/docs/Application_guide/zh/media/background/hardware-platform/UIS8910DM.png new file mode 100644 index 0000000000000000000000000000000000000000..dc1a44c27b441d73592f28ee3c67794aafc8f51f Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/UIS8910DM.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/app-dev-steps.png b/docs/Application_guide/zh/media/background/hardware-platform/app-dev-steps.png new file mode 100644 index 0000000000000000000000000000000000000000..f8a7c6bd55c3bae5e601d21e22314bace0e741d4 Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/app-dev-steps.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/modules_with_asr_chip.png b/docs/Application_guide/zh/media/background/hardware-platform/modules_with_asr_chip.png new file mode 100644 index 0000000000000000000000000000000000000000..fa02c421b5364e434332a94e7d6c0b59e25ed7d3 Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/modules_with_asr_chip.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/modules_with_eigencomm_chip.png b/docs/Application_guide/zh/media/background/hardware-platform/modules_with_eigencomm_chip.png new file mode 100644 index 0000000000000000000000000000000000000000..00faa5dca452bd2a4aaf7c3c680f94007835c34c Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/modules_with_eigencomm_chip.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/modules_with_qualcomm_chip.png b/docs/Application_guide/zh/media/background/hardware-platform/modules_with_qualcomm_chip.png new file mode 100644 index 0000000000000000000000000000000000000000..c24a994d7b24e4640e5ac64e2bd72b8da3aebf21 Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/modules_with_qualcomm_chip.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/modules_with_unisoc_chip.png b/docs/Application_guide/zh/media/background/hardware-platform/modules_with_unisoc_chip.png new file mode 100644 index 0000000000000000000000000000000000000000..7f6d8031f27d39389d5a68fdd76ae9ce8ebe44e7 Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/modules_with_unisoc_chip.png differ diff --git a/docs/Application_guide/zh/media/background/hardware-platform/std_mode_vs_qpy_mode.png b/docs/Application_guide/zh/media/background/hardware-platform/std_mode_vs_qpy_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..27110a025fcffa86a979130503a186cddb67de1d Binary files /dev/null and b/docs/Application_guide/zh/media/background/hardware-platform/std_mode_vs_qpy_mode.png differ diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/configuration-software.png b/docs/Application_guide/zh/media/background/iot-and-low-code/configuration-software.png similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/configuration-software.png rename to docs/Application_guide/zh/media/background/iot-and-low-code/configuration-software.png diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/internet-of-human.png b/docs/Application_guide/zh/media/background/iot-and-low-code/internet-of-human.png similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/internet-of-human.png rename to docs/Application_guide/zh/media/background/iot-and-low-code/internet-of-human.png diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/internet-of-things.png b/docs/Application_guide/zh/media/background/iot-and-low-code/internet-of-things.png similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/internet-of-things.png rename to docs/Application_guide/zh/media/background/iot-and-low-code/internet-of-things.png diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/iot-application-structure.png b/docs/Application_guide/zh/media/background/iot-and-low-code/iot-application-structure.png similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/iot-application-structure.png rename to docs/Application_guide/zh/media/background/iot-and-low-code/iot-application-structure.png diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/mpy-oled.gif b/docs/Application_guide/zh/media/background/iot-and-low-code/mpy-oled.gif similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/mpy-oled.gif rename to docs/Application_guide/zh/media/background/iot-and-low-code/mpy-oled.gif diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/node-red.png b/docs/Application_guide/zh/media/background/iot-and-low-code/node-red.png similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/node-red.png rename to docs/Application_guide/zh/media/background/iot-and-low-code/node-red.png diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/router.png b/docs/Application_guide/zh/media/background/iot-and-low-code/router.png similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/router.png rename to docs/Application_guide/zh/media/background/iot-and-low-code/router.png diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/smart-home-light.png b/docs/Application_guide/zh/media/background/iot-and-low-code/smart-home-light.png similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/smart-home-light.png rename to docs/Application_guide/zh/media/background/iot-and-low-code/smart-home-light.png diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/smart-plug.png b/docs/Application_guide/zh/media/background/iot-and-low-code/smart-plug.png similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/smart-plug.png rename to docs/Application_guide/zh/media/background/iot-and-low-code/smart-plug.png diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/temperature-remote-control.png b/docs/Application_guide/zh/media/background/iot-and-low-code/temperature-remote-control.png similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/temperature-remote-control.png rename to docs/Application_guide/zh/media/background/iot-and-low-code/temperature-remote-control.png diff --git a/docs/Getting_started/zh/media/background/iot-and-low-code/thingsboard-architecture.png b/docs/Application_guide/zh/media/background/iot-and-low-code/thingsboard-architecture.png similarity index 100% rename from docs/Getting_started/zh/media/background/iot-and-low-code/thingsboard-architecture.png rename to docs/Application_guide/zh/media/background/iot-and-low-code/thingsboard-architecture.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/5g-evolution.png b/docs/Application_guide/zh/media/background/wireless-modules/5g-evolution.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/5g-evolution.png rename to docs/Application_guide/zh/media/background/wireless-modules/5g-evolution.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/at-command-architecture.png b/docs/Application_guide/zh/media/background/wireless-modules/at-command-architecture.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/at-command-architecture.png rename to docs/Application_guide/zh/media/background/wireless-modules/at-command-architecture.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/cellular-network-architecture.png b/docs/Application_guide/zh/media/background/wireless-modules/cellular-network-architecture.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/cellular-network-architecture.png rename to docs/Application_guide/zh/media/background/wireless-modules/cellular-network-architecture.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/device-with-ec20.png b/docs/Application_guide/zh/media/background/wireless-modules/device-with-ec20.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/device-with-ec20.png rename to docs/Application_guide/zh/media/background/wireless-modules/device-with-ec20.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/ec20-inner-components.png b/docs/Application_guide/zh/media/background/wireless-modules/ec20-inner-components.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/ec20-inner-components.png rename to docs/Application_guide/zh/media/background/wireless-modules/ec20-inner-components.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/ec20-naked-top.png b/docs/Application_guide/zh/media/background/wireless-modules/ec20-naked-top.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/ec20-naked-top.png rename to docs/Application_guide/zh/media/background/wireless-modules/ec20-naked-top.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/flag-communication.png b/docs/Application_guide/zh/media/background/wireless-modules/flag-communication.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/flag-communication.png rename to docs/Application_guide/zh/media/background/wireless-modules/flag-communication.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/help-signal-shown-flag.png b/docs/Application_guide/zh/media/background/wireless-modules/help-signal-shown-flag.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/help-signal-shown-flag.png rename to docs/Application_guide/zh/media/background/wireless-modules/help-signal-shown-flag.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/open-mode-module.png b/docs/Application_guide/zh/media/background/wireless-modules/open-mode-module.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/open-mode-module.png rename to docs/Application_guide/zh/media/background/wireless-modules/open-mode-module.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/qcx216-diagram.png b/docs/Application_guide/zh/media/background/wireless-modules/qcx216-diagram.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/qcx216-diagram.png rename to docs/Application_guide/zh/media/background/wireless-modules/qcx216-diagram.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/standard-mode-module.png b/docs/Application_guide/zh/media/background/wireless-modules/standard-mode-module.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/standard-mode-module.png rename to docs/Application_guide/zh/media/background/wireless-modules/standard-mode-module.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/two-hills.png b/docs/Application_guide/zh/media/background/wireless-modules/two-hills.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/two-hills.png rename to docs/Application_guide/zh/media/background/wireless-modules/two-hills.png diff --git a/docs/Getting_started/zh/media/background/wireless-modules/wireless-communication-route.png b/docs/Application_guide/zh/media/background/wireless-modules/wireless-communication-route.png similarity index 100% rename from docs/Getting_started/zh/media/background/wireless-modules/wireless-communication-route.png rename to docs/Application_guide/zh/media/background/wireless-modules/wireless-communication-route.png diff --git a/docs/Application_guide/zh/media/cloud/aliyun_1.png b/docs/Application_guide/zh/media/cloud/aliyun_1.png deleted file mode 100644 index e3ceac49f4f5410524c2c0e6a158fae6dcb6a8e6..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_1.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_10.png b/docs/Application_guide/zh/media/cloud/aliyun_10.png deleted file mode 100644 index 428c26ac9bc9850e016014d3264ed12dcf52e8f6..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_10.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_11.png b/docs/Application_guide/zh/media/cloud/aliyun_11.png deleted file mode 100644 index 9235374d3f46893e343fac56dd252ddbcb98319c..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_11.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_12.png b/docs/Application_guide/zh/media/cloud/aliyun_12.png deleted file mode 100644 index 9235374d3f46893e343fac56dd252ddbcb98319c..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_12.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_13.png b/docs/Application_guide/zh/media/cloud/aliyun_13.png deleted file mode 100644 index 25de8c1e7afccc7d94f26df9f0ebbe949dd5ac9c..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_13.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_14.png b/docs/Application_guide/zh/media/cloud/aliyun_14.png deleted file mode 100644 index 11a8d78f7600fb8e57a3f33ac29ddd06b4d790e3..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_14.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_15.png b/docs/Application_guide/zh/media/cloud/aliyun_15.png deleted file mode 100644 index dc600798e1d06096caac590befcbf2476e844d45..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_15.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_16.png b/docs/Application_guide/zh/media/cloud/aliyun_16.png deleted file mode 100644 index ed9d8f600f44ddb8e3e94c2102b02a1cf5a7878e..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_16.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_17.png b/docs/Application_guide/zh/media/cloud/aliyun_17.png deleted file mode 100644 index 1bb34cc23f107c924d425750b9cf96641adacf72..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_17.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_18.png b/docs/Application_guide/zh/media/cloud/aliyun_18.png deleted file mode 100644 index 2e242ed4f8be545ddf5d12436851b14fbaec32b9..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_18.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_19.png b/docs/Application_guide/zh/media/cloud/aliyun_19.png deleted file mode 100644 index f3b2f5d49defae53a04338295a4ed39d6c13556c..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_19.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_2.png b/docs/Application_guide/zh/media/cloud/aliyun_2.png deleted file mode 100644 index 1eb54084768a424f3c1b87b80c8e8ca45dc282ca..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_2.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_20.png b/docs/Application_guide/zh/media/cloud/aliyun_20.png deleted file mode 100644 index ed8bd04bd7eab1d75e633ae297e19143fa233901..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_20.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_21.png b/docs/Application_guide/zh/media/cloud/aliyun_21.png deleted file mode 100644 index cd56f038eb1be4f69839948a3f73f1f12d163f4f..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_21.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_22.png b/docs/Application_guide/zh/media/cloud/aliyun_22.png deleted file mode 100644 index cd56f038eb1be4f69839948a3f73f1f12d163f4f..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_22.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_23.png b/docs/Application_guide/zh/media/cloud/aliyun_23.png deleted file mode 100644 index 7ad84103121a31b476cd7cfc5c17c789cb9fc5ae..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_23.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_24.png b/docs/Application_guide/zh/media/cloud/aliyun_24.png deleted file mode 100644 index 4da3274e7d36478efec548f2c03d5d56d32019a7..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_24.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_25.png b/docs/Application_guide/zh/media/cloud/aliyun_25.png deleted file mode 100644 index 3aaeac5135a12400360e564fd36aa6b588f11587..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_25.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_26.png b/docs/Application_guide/zh/media/cloud/aliyun_26.png deleted file mode 100644 index 91d4a08aa3a1df2cb715afa68306d6d95c2fce91..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_26.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_27.png b/docs/Application_guide/zh/media/cloud/aliyun_27.png deleted file mode 100644 index e221f7cfc7128c9e4a88ea8db2775195372d3301..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_27.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_3.png b/docs/Application_guide/zh/media/cloud/aliyun_3.png deleted file mode 100644 index daed4119d1b5723ba3806fd4c3ea9f42fbbc3a3c..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_3.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_4.png b/docs/Application_guide/zh/media/cloud/aliyun_4.png deleted file mode 100644 index 6b348c77591d9800977880fc4242f3f69336a934..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_4.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_5.png b/docs/Application_guide/zh/media/cloud/aliyun_5.png deleted file mode 100644 index 5401fcc90366a172f62fa1e595addaed5bfc0b8f..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_5.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_6.png b/docs/Application_guide/zh/media/cloud/aliyun_6.png deleted file mode 100644 index ab74e76f2c6aadf254f2d9cbfdaf05842ec54a2d..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_6.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_7.png b/docs/Application_guide/zh/media/cloud/aliyun_7.png deleted file mode 100644 index ea5bbf6604d2221deadaa6e601b5d67c781dc3cf..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_7.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_8.png b/docs/Application_guide/zh/media/cloud/aliyun_8.png deleted file mode 100644 index cea6401f83e296e88a487c4fdbdf6f3f8e0977a9..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_8.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/cloud/aliyun_9.png b/docs/Application_guide/zh/media/cloud/aliyun_9.png deleted file mode 100644 index c78c2d310ec8f336ab0fe669d6b4ae1cd70f802f..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/cloud/aliyun_9.png and /dev/null differ diff --git a/docs/Getting_started/zh/media/qpycom/QPYcom_drag.jpg b/docs/Application_guide/zh/media/dev-tools/qpycom/QPYcom_drag.jpg similarity index 100% rename from docs/Getting_started/zh/media/qpycom/QPYcom_drag.jpg rename to docs/Application_guide/zh/media/dev-tools/qpycom/QPYcom_drag.jpg diff --git a/docs/Getting_started/zh/media/qpycom/QPYcom_ext.png b/docs/Application_guide/zh/media/dev-tools/qpycom/QPYcom_ext.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/QPYcom_ext.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/QPYcom_ext.png diff --git a/docs/Getting_started/zh/media/qpycom/QPYcom_repl_01.png b/docs/Application_guide/zh/media/dev-tools/qpycom/QPYcom_repl_01.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/QPYcom_repl_01.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/QPYcom_repl_01.png diff --git a/docs/Getting_started/zh/media/qpycom/QPYcom_sc_1.jpg b/docs/Application_guide/zh/media/dev-tools/qpycom/QPYcom_sc_1.jpg similarity index 100% rename from docs/Getting_started/zh/media/qpycom/QPYcom_sc_1.jpg rename to docs/Application_guide/zh/media/dev-tools/qpycom/QPYcom_sc_1.jpg diff --git a/docs/Getting_started/zh/media/qpycom/fota_1.png b/docs/Application_guide/zh/media/dev-tools/qpycom/fota_1.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/fota_1.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/fota_1.png diff --git a/docs/Getting_started/zh/media/qpycom/image10.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image10.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image10.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image10.png diff --git a/docs/Getting_started/zh/media/qpycom/image11.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image11.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image11.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image11.png diff --git a/docs/Getting_started/zh/media/qpycom/image12.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image12.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image12.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image12.png diff --git a/docs/Getting_started/zh/media/qpycom/image13.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image13.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image13.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image13.png diff --git a/docs/Getting_started/zh/media/qpycom/image14.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image14.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image14.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image14.png diff --git a/docs/Getting_started/zh/media/qpycom/image16.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image16.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image16.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image16.png diff --git a/docs/Getting_started/zh/media/qpycom/image17.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image17.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image17.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image17.png diff --git a/docs/Getting_started/zh/media/qpycom/image18.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image18.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image18.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image18.png diff --git a/docs/Getting_started/zh/media/qpycom/image19.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image19.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image19.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image19.png diff --git a/docs/Getting_started/zh/media/qpycom/image20.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image20.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image20.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image20.png diff --git a/docs/Getting_started/zh/media/qpycom/image21.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image21.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image21.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image21.png diff --git a/docs/Getting_started/zh/media/qpycom/image22.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image22.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image22.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image22.png diff --git a/docs/Getting_started/zh/media/qpycom/image23.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image23.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image23.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image23.png diff --git a/docs/Getting_started/zh/media/qpycom/image24.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image24.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image24.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image24.png diff --git a/docs/Getting_started/zh/media/qpycom/image25.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image25.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image25.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image25.png diff --git a/docs/Getting_started/zh/media/qpycom/image26.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image26.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image26.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image26.png diff --git a/docs/Getting_started/zh/media/qpycom/image27.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image27.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image27.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image27.png diff --git a/docs/Getting_started/zh/media/qpycom/image28.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image28.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image28.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image28.png diff --git a/docs/Getting_started/zh/media/qpycom/image29.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image29.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image29.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image29.png diff --git a/docs/Getting_started/zh/media/qpycom/image30.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image30.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image30.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image30.png diff --git a/docs/Getting_started/zh/media/qpycom/image31.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image31.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image31.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image31.png diff --git a/docs/Getting_started/zh/media/qpycom/image32.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image32.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image32.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image32.png diff --git a/docs/Getting_started/zh/media/qpycom/image33.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image33.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image33.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image33.png diff --git a/docs/Getting_started/zh/media/qpycom/image34.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image34.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image34.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image34.png diff --git a/docs/Getting_started/zh/media/qpycom/image35.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image35.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image35.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image35.png diff --git a/docs/Getting_started/zh/media/qpycom/image36.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image36.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image36.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image36.png diff --git a/docs/Getting_started/zh/media/qpycom/image37.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image37.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image37.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image37.png diff --git a/docs/Getting_started/zh/media/qpycom/image38.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image38.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image38.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image38.png diff --git a/docs/Getting_started/zh/media/qpycom/image39.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image39.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image39.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image39.png diff --git a/docs/Getting_started/zh/media/qpycom/image40.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image40.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image40.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image40.png diff --git a/docs/Getting_started/zh/media/qpycom/image41.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image41.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image41.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image41.png diff --git a/docs/Getting_started/zh/media/qpycom/image42.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image42.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image42.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image42.png diff --git a/docs/Getting_started/zh/media/qpycom/image43.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image43.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image43.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image43.png diff --git a/docs/Getting_started/zh/media/qpycom/image44.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image44.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image44.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image44.png diff --git a/docs/Getting_started/zh/media/qpycom/image45.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image45.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image45.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image45.png diff --git a/docs/Getting_started/zh/media/qpycom/image46.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image46.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image46.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image46.png diff --git a/docs/Getting_started/zh/media/qpycom/image47.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image47.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image47.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image47.png diff --git a/docs/Getting_started/zh/media/qpycom/image48.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image48.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image48.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image48.png diff --git a/docs/Getting_started/zh/media/qpycom/image9.png b/docs/Application_guide/zh/media/dev-tools/qpycom/image9.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/image9.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/image9.png diff --git a/docs/Getting_started/zh/media/qpycom/repl_py.png b/docs/Application_guide/zh/media/dev-tools/qpycom/repl_py.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/repl_py.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/repl_py.png diff --git a/docs/Getting_started/zh/media/qpycom/repl_qpycom.png b/docs/Application_guide/zh/media/dev-tools/qpycom/repl_qpycom.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/repl_qpycom.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/repl_qpycom.png diff --git a/docs/Getting_started/zh/media/qpycom/set_size.png b/docs/Application_guide/zh/media/dev-tools/qpycom/set_size.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/set_size.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/set_size.png diff --git a/docs/Getting_started/zh/media/qpycom/size_conf.png b/docs/Application_guide/zh/media/dev-tools/qpycom/size_conf.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/size_conf.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/size_conf.png diff --git a/docs/Getting_started/zh/media/qpycom/suffix.png b/docs/Application_guide/zh/media/dev-tools/qpycom/suffix.png similarity index 100% rename from docs/Getting_started/zh/media/qpycom/suffix.png rename to docs/Application_guide/zh/media/dev-tools/qpycom/suffix.png diff --git a/docs/Application_guide/zh/media/hardware/USB-interfaces/Device_Manager.png b/docs/Application_guide/zh/media/hardware/USB-interfaces/Device_Manager.png new file mode 100644 index 0000000000000000000000000000000000000000..164f4bb0b0ced737b2d1e3b67acf0bdced121f6d Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/USB-interfaces/Device_Manager.png differ diff --git a/docs/Application_guide/zh/media/hardware/USB-interfaces/Net_Manager.png b/docs/Application_guide/zh/media/hardware/USB-interfaces/Net_Manager.png new file mode 100644 index 0000000000000000000000000000000000000000..ebdf4da9e4ddea4b6acd9e9d88479b30174d3b67 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/USB-interfaces/Net_Manager.png differ diff --git a/docs/Application_guide/zh/media/hardware/USB-interfaces/REPL.png b/docs/Application_guide/zh/media/hardware/USB-interfaces/REPL.png new file mode 100644 index 0000000000000000000000000000000000000000..4e2a8f6607fc3f0c833b9caab5016f90c9a37333 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/USB-interfaces/REPL.png differ diff --git a/docs/Application_guide/zh/media/hardware/USB-interfaces/RNDIS.png b/docs/Application_guide/zh/media/hardware/USB-interfaces/RNDIS.png new file mode 100644 index 0000000000000000000000000000000000000000..61857adba0ad069ffbd71818c5a1cb3fcfcba95d Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/USB-interfaces/RNDIS.png differ diff --git a/docs/Application_guide/zh/media/hardware/USB-interfaces/drive_fail.png b/docs/Application_guide/zh/media/hardware/USB-interfaces/drive_fail.png new file mode 100644 index 0000000000000000000000000000000000000000..36102a0ff005d89cac373108ccaf08acddbce753 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/USB-interfaces/drive_fail.png differ diff --git a/docs/Getting_started/zh/media/quick-start/preparation/driver_after.jpg b/docs/Application_guide/zh/media/hardware/USB-interfaces/driver_after.jpg similarity index 100% rename from docs/Getting_started/zh/media/quick-start/preparation/driver_after.jpg rename to docs/Application_guide/zh/media/hardware/USB-interfaces/driver_after.jpg diff --git a/docs/Application_guide/zh/media/hardware/USB-interfaces/driver_installation1.png b/docs/Application_guide/zh/media/hardware/USB-interfaces/driver_installation1.png new file mode 100644 index 0000000000000000000000000000000000000000..ab1d93338fa8dffbad40eb0d1b5b44da1b6dffb4 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/USB-interfaces/driver_installation1.png differ diff --git a/docs/Application_guide/zh/media/hardware/USB-interfaces/driver_installation2.png b/docs/Application_guide/zh/media/hardware/USB-interfaces/driver_installation2.png new file mode 100644 index 0000000000000000000000000000000000000000..e78fc863d5172bd94fa0f0543d58c5e7a48da5e4 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/USB-interfaces/driver_installation2.png differ diff --git a/docs/Application_guide/zh/media/hardware/USB-interfaces/usb_solutions.png b/docs/Application_guide/zh/media/hardware/USB-interfaces/usb_solutions.png new file mode 100644 index 0000000000000000000000000000000000000000..75a330ce22cca629b4ff411598334e7b091a53a9 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/USB-interfaces/usb_solutions.png differ diff --git a/docs/Application_guide/zh/media/hardware/audio-driver/AudioCalibrator1.png b/docs/Application_guide/zh/media/hardware/audio-driver/AudioCalibrator1.png new file mode 100644 index 0000000000000000000000000000000000000000..350c5d92cb9aab6aa93df723242eb61a49255de8 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/audio-driver/AudioCalibrator1.png differ diff --git a/docs/Application_guide/zh/media/hardware/audio-driver/AudioCalibrator2.png b/docs/Application_guide/zh/media/hardware/audio-driver/AudioCalibrator2.png new file mode 100644 index 0000000000000000000000000000000000000000..f5bc6e9c9bfa154cd828c2ddf9373d8d2a1cc499 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/audio-driver/AudioCalibrator2.png differ diff --git a/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio1.png b/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio1.png new file mode 100644 index 0000000000000000000000000000000000000000..26cdb4ae1502358129d025cf325e3dfdc144dda0 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio1.png differ diff --git a/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio2.png b/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio2.png new file mode 100644 index 0000000000000000000000000000000000000000..0473559b5a6ca79a94bcc6d944d414b191e71b23 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio2.png differ diff --git a/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio3.png b/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio3.png new file mode 100644 index 0000000000000000000000000000000000000000..7f4b26318a554813f1f43a90dfa0b0a870829b51 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio3.png differ diff --git a/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio4.png b/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio4.png new file mode 100644 index 0000000000000000000000000000000000000000..ef3d23f2eddf38a1ed79f484e69fcbbd3ff03977 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio4.png differ diff --git a/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio5.png b/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio5.png new file mode 100644 index 0000000000000000000000000000000000000000..d862b20edf8c862c475943304c9d61df3d60b63b Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/audio-driver/CATStudio5.png differ diff --git a/docs/Application_guide/zh/media/hardware/audio-driver/codec1.png b/docs/Application_guide/zh/media/hardware/audio-driver/codec1.png new file mode 100644 index 0000000000000000000000000000000000000000..abc76267d16fe0bae217871d5fecd24e5da6db74 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/audio-driver/codec1.png differ diff --git a/docs/Application_guide/zh/media/hardware/audio-driver/codec2.png b/docs/Application_guide/zh/media/hardware/audio-driver/codec2.png new file mode 100644 index 0000000000000000000000000000000000000000..f881504945f22531dfa3239940783e35dfc5b819 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/audio-driver/codec2.png differ diff --git a/docs/Application_guide/zh/media/hardware/audio-driver/codec3.png b/docs/Application_guide/zh/media/hardware/audio-driver/codec3.png new file mode 100644 index 0000000000000000000000000000000000000000..ee7c88b0ab5f7bdf9412cf7e9cdd4018d8227e57 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/audio-driver/codec3.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/controller_structure.png b/docs/Application_guide/zh/media/hardware/display/controller_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..02a2a07c9d429e412603f9ebb960463d6a7a8989 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/controller_structure.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/lcd_controller.png b/docs/Application_guide/zh/media/hardware/display/lcd_controller.png new file mode 100644 index 0000000000000000000000000000000000000000..0c53ef6e99d393b0651b2b065897c70d1929ed67 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/lcd_controller.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/lcd_display_para.png b/docs/Application_guide/zh/media/hardware/display/lcd_display_para.png new file mode 100644 index 0000000000000000000000000000000000000000..5aa7cd5d0494332623235b01afb004e971594357 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/lcd_display_para.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/lcd_init_para.png b/docs/Application_guide/zh/media/hardware/display/lcd_init_para.png new file mode 100644 index 0000000000000000000000000000000000000000..00b389084ce76febaee095b21a796bd64e487308 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/lcd_init_para.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/lcd_invalid_para.png b/docs/Application_guide/zh/media/hardware/display/lcd_invalid_para.png new file mode 100644 index 0000000000000000000000000000000000000000..48dfc3356910d0f57429e9cce4a90b516995d85b Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/lcd_invalid_para.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/mipi_lcd_1.png b/docs/Application_guide/zh/media/hardware/display/mipi_lcd_1.png new file mode 100644 index 0000000000000000000000000000000000000000..624d89e9d623063c6d823638906193cb04633e12 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/mipi_lcd_1.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/mipi_lcd_2.png b/docs/Application_guide/zh/media/hardware/display/mipi_lcd_2.png new file mode 100644 index 0000000000000000000000000000000000000000..3bcf13fa7d2b9f03bd117aa173bdbe88bfe36dc1 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/mipi_lcd_2.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/mipi_lcd_3.png b/docs/Application_guide/zh/media/hardware/display/mipi_lcd_3.png new file mode 100644 index 0000000000000000000000000000000000000000..74f51f6ee2ea7bd65d89f39bc372b33ea82efd00 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/mipi_lcd_3.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/mipi_lcd_4.png b/docs/Application_guide/zh/media/hardware/display/mipi_lcd_4.png new file mode 100644 index 0000000000000000000000000000000000000000..4c8ab7b7d1ae596612dcb920425e87b100430548 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/mipi_lcd_4.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/mipi_lcd_5.png b/docs/Application_guide/zh/media/hardware/display/mipi_lcd_5.png new file mode 100644 index 0000000000000000000000000000000000000000..4cc400f0363ab3ac74614e517726cd2f021cc98d Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/mipi_lcd_5.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/mirror_x.png b/docs/Application_guide/zh/media/hardware/display/mirror_x.png new file mode 100644 index 0000000000000000000000000000000000000000..9f6c582ecc5b77a0c665d299205088740f3847a7 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/mirror_x.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/mirror_xy.png b/docs/Application_guide/zh/media/hardware/display/mirror_xy.png new file mode 100644 index 0000000000000000000000000000000000000000..fd2b5f4bab3365b75fafc5649c84503f461964a9 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/mirror_xy.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/mirror_y.png b/docs/Application_guide/zh/media/hardware/display/mirror_y.png new file mode 100644 index 0000000000000000000000000000000000000000..1f68021b2ab371667dbf511a50a4315873176930 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/mirror_y.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/nixie_tube_controller.png b/docs/Application_guide/zh/media/hardware/display/nixie_tube_controller.png new file mode 100644 index 0000000000000000000000000000000000000000..fdd842a80bd7494f5721b1271b65229b177e30ea Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/nixie_tube_controller.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/original.png b/docs/Application_guide/zh/media/hardware/display/original.png new file mode 100644 index 0000000000000000000000000000000000000000..4818bcf9aac3da497ed63e3cefd19104e406f011 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/original.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/screen_driver_RGB565.png b/docs/Application_guide/zh/media/hardware/display/screen_driver_RGB565.png new file mode 100644 index 0000000000000000000000000000000000000000..d193cd7d15d298b5f9161ce28f6cbc4fff75f2ae Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/screen_driver_RGB565.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/screen_driver_gray.png b/docs/Application_guide/zh/media/hardware/display/screen_driver_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..ed2d3df0a95cda7962fc769e8441c7d0f258093e Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/screen_driver_gray.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/screen_driver_mono.png b/docs/Application_guide/zh/media/hardware/display/screen_driver_mono.png new file mode 100644 index 0000000000000000000000000000000000000000..35ef5c41a06b2383b08cba7fdd9d1f8a518d359f Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/screen_driver_mono.png differ diff --git a/docs/Getting_started/zh/media/os/power/power_0.jpg b/docs/Application_guide/zh/media/hardware/display/spilcd_show1.png similarity index 56% rename from docs/Getting_started/zh/media/os/power/power_0.jpg rename to docs/Application_guide/zh/media/hardware/display/spilcd_show1.png index 3a15cb5f35bcae030fac8e0620ec4574c4c97ddf..54c33ee044dfecaf8104a3fc59e04c79dd88d175 100644 Binary files a/docs/Getting_started/zh/media/os/power/power_0.jpg and b/docs/Application_guide/zh/media/hardware/display/spilcd_show1.png differ diff --git a/docs/Getting_started/zh/mass-production/code/fota.zip b/docs/Application_guide/zh/media/hardware/display/spilcd_show2.png similarity index 64% rename from docs/Getting_started/zh/mass-production/code/fota.zip rename to docs/Application_guide/zh/media/hardware/display/spilcd_show2.png index c2924174271cb466ee249bb3fa085d94f32ce06a..30d72eb80726fd4ac1a57a4e5ff55638065cdf59 100644 Binary files a/docs/Getting_started/zh/mass-production/code/fota.zip and b/docs/Application_guide/zh/media/hardware/display/spilcd_show2.png differ diff --git a/docs/Getting_started/zh/media/hardware/matrix-keypad/keypad_3.png b/docs/Application_guide/zh/media/hardware/display/spilcd_show3.png similarity index 57% rename from docs/Getting_started/zh/media/hardware/matrix-keypad/keypad_3.png rename to docs/Application_guide/zh/media/hardware/display/spilcd_show3.png index 10731885e4eede0a188ce78fc092dd0d9251e2e7..d299978412fcc9685a8dd29497eef6b7a3d92446 100644 Binary files a/docs/Getting_started/zh/media/hardware/matrix-keypad/keypad_3.png and b/docs/Application_guide/zh/media/hardware/display/spilcd_show3.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/spilcd_show4.png b/docs/Application_guide/zh/media/hardware/display/spilcd_show4.png new file mode 100644 index 0000000000000000000000000000000000000000..add96dffeff19343638c08d4b395298a36f74b30 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/spilcd_show4.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/swap_xy.png b/docs/Application_guide/zh/media/hardware/display/swap_xy.png new file mode 100644 index 0000000000000000000000000000000000000000..2d0806f08fbb52641f6356c0d5fc3bbd996cd2f9 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/swap_xy.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/swap_xy_mirror_x.png b/docs/Application_guide/zh/media/hardware/display/swap_xy_mirror_x.png new file mode 100644 index 0000000000000000000000000000000000000000..d11f74b42fcafc0b722ac20f80a242333a015a19 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/swap_xy_mirror_x.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/swap_xy_mirror_xy.png b/docs/Application_guide/zh/media/hardware/display/swap_xy_mirror_xy.png new file mode 100644 index 0000000000000000000000000000000000000000..99a4c0a569b499301c8872aae41b4b0c1bfbedf1 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/swap_xy_mirror_xy.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/swap_xy_mirror_y.png b/docs/Application_guide/zh/media/hardware/display/swap_xy_mirror_y.png new file mode 100644 index 0000000000000000000000000000000000000000..0f4454cae01125b67c6a761e8b7212e101c3ffb7 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/swap_xy_mirror_y.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/tm1650_typical_application_circuit.png b/docs/Application_guide/zh/media/hardware/display/tm1650_typical_application_circuit.png new file mode 100644 index 0000000000000000000000000000000000000000..37fb59f656785b56d302366cf7ea9754df111fae Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/tm1650_typical_application_circuit.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/touch_controller.png b/docs/Application_guide/zh/media/hardware/display/touch_controller.png new file mode 100644 index 0000000000000000000000000000000000000000..a304eb551dbd65ae97e146a85f73fa8f97112f00 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/touch_controller.png differ diff --git a/docs/Application_guide/zh/media/hardware/display/touch_resulut_show.png b/docs/Application_guide/zh/media/hardware/display/touch_resulut_show.png new file mode 100644 index 0000000000000000000000000000000000000000..79d38b9a52299677e4b57c26d1d534db6f53bc66 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/display/touch_resulut_show.png differ diff --git a/docs/Application_guide/zh/media/hardware/peripheral-interfaces/SPI/LCD.png b/docs/Application_guide/zh/media/hardware/peripheral-interfaces/SPI/LCD.png new file mode 100644 index 0000000000000000000000000000000000000000..18105d19ba1b002d453430368ef2f14aa5e3e9e0 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/peripheral-interfaces/SPI/LCD.png differ diff --git a/docs/Application_guide/zh/media/hardware/peripheral-interfaces/SPI/MCP2515.png b/docs/Application_guide/zh/media/hardware/peripheral-interfaces/SPI/MCP2515.png new file mode 100644 index 0000000000000000000000000000000000000000..eabc6a8140f38af18413516ca1014369b134f382 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/peripheral-interfaces/SPI/MCP2515.png differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/gsensor_1.png b/docs/Application_guide/zh/media/hardware/sensors/gsensor_1.png new file mode 100644 index 0000000000000000000000000000000000000000..2ba09fb8acb59b10598b25c4e33b02b1a70cf931 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/gsensor_1.png differ diff --git a/docs/Getting_started/zh/media/solutions/tracker/EC600N-CNLA.jpg b/docs/Application_guide/zh/media/hardware/sensors/gsensor_2.png similarity index 32% rename from docs/Getting_started/zh/media/solutions/tracker/EC600N-CNLA.jpg rename to docs/Application_guide/zh/media/hardware/sensors/gsensor_2.png index 32ab7b11ce1b4158ea08fb6d8524352583bfbe9c..cfdf2ab4529b069ac54077e69dca4df41a48f63e 100644 Binary files a/docs/Getting_started/zh/media/solutions/tracker/EC600N-CNLA.jpg and b/docs/Application_guide/zh/media/hardware/sensors/gsensor_2.png differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/gsensor_3.png b/docs/Application_guide/zh/media/hardware/sensors/gsensor_3.png new file mode 100644 index 0000000000000000000000000000000000000000..5dda8b7688df5e85e1426f7258fa920b97f19237 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/gsensor_3.png differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/gsensor_4.png b/docs/Application_guide/zh/media/hardware/sensors/gsensor_4.png new file mode 100644 index 0000000000000000000000000000000000000000..b6186b165ee7a2ab4c6c8bf41305c3d219150e3c Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/gsensor_4.png differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/htsensor_1.png b/docs/Application_guide/zh/media/hardware/sensors/htsensor_1.png new file mode 100644 index 0000000000000000000000000000000000000000..402d7b8ffa3d13314ccc90d6195bc78017da0b5e Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/htsensor_1.png differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/htsensor_2.png b/docs/Application_guide/zh/media/hardware/sensors/htsensor_2.png new file mode 100644 index 0000000000000000000000000000000000000000..5a9dfba86fb32d84d9f43c57bb58387ebc6461dd Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/htsensor_2.png differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/htsensor_3.jpg b/docs/Application_guide/zh/media/hardware/sensors/htsensor_3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f8acbbda65e8f43bcbecdf51a86bec1d2f64ee21 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/htsensor_3.jpg differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/htsensor_4.png b/docs/Application_guide/zh/media/hardware/sensors/htsensor_4.png new file mode 100644 index 0000000000000000000000000000000000000000..8b5e87daa456e08f0a174e178bcee890faac63e7 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/htsensor_4.png differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/htsensor_5.jpg b/docs/Application_guide/zh/media/hardware/sensors/htsensor_5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8f727b85774d4afb5fde7939c967d8be0947a395 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/htsensor_5.jpg differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/isensor_1.png b/docs/Application_guide/zh/media/hardware/sensors/isensor_1.png new file mode 100644 index 0000000000000000000000000000000000000000..2923e30f4cd8ef1c649228e53f9ae825ef7772fb Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/isensor_1.png differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/isensor_2.jpg b/docs/Application_guide/zh/media/hardware/sensors/isensor_2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..30e8506e86b15d936bf1dfd61aacf9b70ae455b8 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/isensor_2.jpg differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/isensor_3.jpg b/docs/Application_guide/zh/media/hardware/sensors/isensor_3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7a0e11dc0a3d78eac7792c178499e40a8e662a16 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/isensor_3.jpg differ diff --git a/docs/Getting_started/zh/media/os/thread/_thread_1.png b/docs/Application_guide/zh/media/hardware/sensors/isensor_4.jpg similarity index 33% rename from docs/Getting_started/zh/media/os/thread/_thread_1.png rename to docs/Application_guide/zh/media/hardware/sensors/isensor_4.jpg index 4b8e53ad02038603f6b45d099d3a85be087f184c..5ee8b69e7db9bb2cca066b036f942e7c3bd245c5 100644 Binary files a/docs/Getting_started/zh/media/os/thread/_thread_1.png and b/docs/Application_guide/zh/media/hardware/sensors/isensor_4.jpg differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/isensor_5.png b/docs/Application_guide/zh/media/hardware/sensors/isensor_5.png new file mode 100644 index 0000000000000000000000000000000000000000..c0ccfe85a4649268486852b1a7c1ba59b47c70b0 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/isensor_5.png differ diff --git a/docs/Application_guide/zh/media/hardware/sensors/isensor_6.png b/docs/Application_guide/zh/media/hardware/sensors/isensor_6.png new file mode 100644 index 0000000000000000000000000000000000000000..d68406dfe0f1968240dbd9516b194e552a8efb32 Binary files /dev/null and b/docs/Application_guide/zh/media/hardware/sensors/isensor_6.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/ftp/ftp_1.png b/docs/Application_guide/zh/media/network-comm/net-protocols/ftp/ftp_1.png new file mode 100644 index 0000000000000000000000000000000000000000..9c29a851fdae57bfc9436b8b3b51f235627ff95a Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/ftp/ftp_1.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/ftp/ftp_2.png b/docs/Application_guide/zh/media/network-comm/net-protocols/ftp/ftp_2.png new file mode 100644 index 0000000000000000000000000000000000000000..63e11b65a527892a07de1daf2bd7363419a7cc50 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/ftp/ftp_2.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/ftp/ftp_3.png b/docs/Application_guide/zh/media/network-comm/net-protocols/ftp/ftp_3.png new file mode 100644 index 0000000000000000000000000000000000000000..956fb18b68fc1302191af65a534fa65980a325d8 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/ftp/ftp_3.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/http/HTTP_RequestMessageExample.png b/docs/Application_guide/zh/media/network-comm/net-protocols/http/HTTP_RequestMessageExample.png new file mode 100644 index 0000000000000000000000000000000000000000..00eab3b14c213b720f4a42894c687a79f6ac4a40 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/http/HTTP_RequestMessageExample.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/http/HTTP_ResponseMessageExample.png b/docs/Application_guide/zh/media/network-comm/net-protocols/http/HTTP_ResponseMessageExample.png new file mode 100644 index 0000000000000000000000000000000000000000..06580692aa41a4508db0bf793f0e64bf4cbea977 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/http/HTTP_ResponseMessageExample.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/http/model.png b/docs/Application_guide/zh/media/network-comm/net-protocols/http/model.png new file mode 100644 index 0000000000000000000000000000000000000000..30f175db4c2649edd1163e16a47dd4c4c96bb388 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/http/model.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/http/request-format.gif b/docs/Application_guide/zh/media/network-comm/net-protocols/http/request-format.gif new file mode 100644 index 0000000000000000000000000000000000000000..060145278db6c311659a3bb81f068623ae0cd2a2 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/http/request-format.gif differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/http/response-format.gif b/docs/Application_guide/zh/media/network-comm/net-protocols/http/response-format.gif new file mode 100644 index 0000000000000000000000000000000000000000..0bc03dff3f5f853c80a7b832c233737c3a0bd79d Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/http/response-format.gif differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711103117638.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711103117638.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f82f469e53e8fab47da23b298db62389f14a71 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711103117638.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711104530432.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711104530432.png new file mode 100644 index 0000000000000000000000000000000000000000..d9b55e36b06e7437835841c3a120a7a627465559 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711104530432.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711104602875.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711104602875.png new file mode 100644 index 0000000000000000000000000000000000000000..ba440418fb0922d8f23f4b1738ec47ac7bca61fc Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711104602875.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711104709552.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711104709552.png new file mode 100644 index 0000000000000000000000000000000000000000..fa610b531066978f819a17f1315f3639f4b5b42a Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711104709552.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711111847033.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711111847033.png new file mode 100644 index 0000000000000000000000000000000000000000..d6a8ce4212fb64802fae71c911c43072518fc952 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711111847033.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711112042927.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711112042927.png new file mode 100644 index 0000000000000000000000000000000000000000..a1ea06b652d99c88ad601df5c8550d5408475f69 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711112042927.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711112526135.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711112526135.png new file mode 100644 index 0000000000000000000000000000000000000000..a00de061433bf24b9e31a6ec4e4093915ac4c349 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711112526135.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711112722629.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711112722629.png new file mode 100644 index 0000000000000000000000000000000000000000..b6b4390563bc71965c4d2b2dff76ef6c8dc1e9da Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711112722629.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711113002143.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711113002143.png new file mode 100644 index 0000000000000000000000000000000000000000..bf0a508dfb7d72930f2035a75f20731e0dfb4a26 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711113002143.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711142733722.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711142733722.png new file mode 100644 index 0000000000000000000000000000000000000000..6e145de68be11851311db1c78c169bd86074be07 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711142733722.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711143650551.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711143650551.png new file mode 100644 index 0000000000000000000000000000000000000000..0f0bf3d110e73526f271dcce3529901b8d470ca8 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711143650551.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711143921595.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711143921595.png new file mode 100644 index 0000000000000000000000000000000000000000..1459ab38fa7bf8ee574500ed3d6842444b6ce8be Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711143921595.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711145324194.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711145324194.png new file mode 100644 index 0000000000000000000000000000000000000000..5462efd0e3f035a9ddab8d1bcbd8e137e686e393 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711145324194.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711162128332.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711162128332.png new file mode 100644 index 0000000000000000000000000000000000000000..71bf7e19fba3cc1be830233f4992f20d355dd668 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711162128332.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711162204882.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711162204882.png new file mode 100644 index 0000000000000000000000000000000000000000..15f913ea32a4cda4b05d172a0e099b6d6bc94c80 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711162204882.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711162623192.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711162623192.png new file mode 100644 index 0000000000000000000000000000000000000000..9f039ebaaab81b716f61ad8cb389e3162dcd2b9f Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711162623192.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711185611691.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711185611691.png new file mode 100644 index 0000000000000000000000000000000000000000..21dba447da9dc1e76a1825074be5a8c2d237f917 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711185611691.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711192013404.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711192013404.png new file mode 100644 index 0000000000000000000000000000000000000000..16227a263ad5782cbf1cc036f9c3a51941f3fd9a Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711192013404.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711192106915.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711192106915.png new file mode 100644 index 0000000000000000000000000000000000000000..82c752cc6697d890da043f5fccfdab6d1b1bdf69 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230711192106915.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230712145141895.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230712145141895.png new file mode 100644 index 0000000000000000000000000000000000000000..3ba5e71c9dc93e4ffd9ceb90c6a827f4ef605bab Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230712145141895.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230713142751410.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230713142751410.png new file mode 100644 index 0000000000000000000000000000000000000000..7f17ffe59d3db584d4c64afc01448bfc6c9d5807 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/image-20230713142751410.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/mqtt_1.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/mqtt_1.png new file mode 100644 index 0000000000000000000000000000000000000000..84850b0e6bd8802c95891e6f52b11eb6c7daf52d Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/mqtt_1.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/mqtt_2.png b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/mqtt_2.png new file mode 100644 index 0000000000000000000000000000000000000000..73120ec1298a9a4141863484a93adc8dda8f05eb Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/mqtt/mqtt_2.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/ip-address.png b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/ip-address.png new file mode 100644 index 0000000000000000000000000000000000000000..14ee3ceaf26d3980079d40f5733d8869f968cffe Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/ip-address.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/osi-7-layers.png b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/osi-7-layers.png new file mode 100644 index 0000000000000000000000000000000000000000..6f4aef85aacbb1b48dba303757126aab4986de14 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/osi-7-layers.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/tcp-socket-model.png b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/tcp-socket-model.png new file mode 100644 index 0000000000000000000000000000000000000000..9d76238fd540edd7fedb0d359a94d086fcd6fea7 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/tcp-socket-model.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/tcp-socket-model.vsdx b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/tcp-socket-model.vsdx new file mode 100644 index 0000000000000000000000000000000000000000..de5cfce6364bcd92dbfa840cba19ebca01e6b76a Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/tcp-socket-model.vsdx differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/tcpip-4-layers.png b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/tcpip-4-layers.png new file mode 100644 index 0000000000000000000000000000000000000000..9154ae7f98f9c49eccd9101d082bd799bbce342b Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/tcpip-4-layers.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/udp-socket-model.png b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/udp-socket-model.png new file mode 100644 index 0000000000000000000000000000000000000000..4ccf921b25c2f857548aa4bd06c59db181d61dde Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/udp-socket-model.png differ diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/udp-socket-model.vsdx b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/udp-socket-model.vsdx new file mode 100644 index 0000000000000000000000000000000000000000..1d55ce520b9a12e5be33a32252023341e1f68e67 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/net-protocols/tcp-udp/udp-socket-model.vsdx differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/API\346\237\245\350\257\242SIM\345\215\241\347\212\266\346\200\201-\346\234\252\346\217\222\345\215\241.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/API\346\237\245\350\257\242SIM\345\215\241\347\212\266\346\200\201-\346\234\252\346\217\222\345\215\241.png" new file mode 100644 index 0000000000000000000000000000000000000000..d146afccce330a328002388c2e667d6e65f6504d Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/API\346\237\245\350\257\242SIM\345\215\241\347\212\266\346\200\201-\346\234\252\346\217\222\345\215\241.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/API\346\237\245\350\257\242\346\213\250\345\217\267\347\273\223\346\236\234.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/API\346\237\245\350\257\242\346\213\250\345\217\267\347\273\223\346\236\234.png" new file mode 100644 index 0000000000000000000000000000000000000000..2bbb988a9021ee2102882a79d31f41ffe9ba5a87 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/API\346\237\245\350\257\242\346\213\250\345\217\267\347\273\223\346\236\234.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/API\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/API\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" new file mode 100644 index 0000000000000000000000000000000000000000..0acde50fca6fd6132131f6527ddde0e8f1c73400 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/API\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/AT\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/AT\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" new file mode 100644 index 0000000000000000000000000000000000000000..90b7541cf6173e92dd46a384ac4544965e3e9420 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/AT\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/cellular/Functional_Diagram.png b/docs/Application_guide/zh/media/network-comm/nic/cellular/Functional_Diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..fc038087e2e166ad053113402e68e62e1aad138b Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/cellular/Functional_Diagram.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/cellular/LTE_network_architecture.png b/docs/Application_guide/zh/media/network-comm/nic/cellular/LTE_network_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..bdc5e92e581809b56a1aa66f12cb8bb372756543 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/cellular/LTE_network_architecture.png differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/PIN\347\240\201\351\252\214\350\257\201.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/PIN\347\240\201\351\252\214\350\257\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..e48fe988bd110cae762b5f260b591439e8d8c893 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/PIN\347\240\201\351\252\214\350\257\201.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\345\210\240\351\231\244\346\213\250\345\217\267\351\205\215\347\275\256\346\226\207\344\273\266.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\345\210\240\351\231\244\346\213\250\345\217\267\351\205\215\347\275\256\346\226\207\344\273\266.png" new file mode 100644 index 0000000000000000000000000000000000000000..755bf06d4d1f4c405965ad01fb7dafec6d134bdd Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\345\210\240\351\231\244\346\213\250\345\217\267\351\205\215\347\275\256\346\226\207\344\273\266.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\346\211\223\345\274\200AT\345\217\243.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\346\211\223\345\274\200AT\345\217\243.png" new file mode 100644 index 0000000000000000000000000000000000000000..a61540b83e7e41ef06f33ae99a3aeb07113e784e Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\346\211\223\345\274\200AT\345\217\243.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\346\211\223\345\274\200REPL.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\346\211\223\345\274\200REPL.png" new file mode 100644 index 0000000000000000000000000000000000000000..245b23d28701d23c3fa4fc9eb7a2b28a3466a768 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\346\211\223\345\274\200REPL.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\346\237\245\347\234\213usr\347\233\256\345\275\225\346\230\257\345\220\246\346\234\211\346\213\250\345\217\267\351\205\215\347\275\256\346\226\207\344\273\266.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\346\237\245\347\234\213usr\347\233\256\345\275\225\346\230\257\345\220\246\346\234\211\346\213\250\345\217\267\351\205\215\347\275\256\346\226\207\344\273\266.png" new file mode 100644 index 0000000000000000000000000000000000000000..d3743ee639c668039cc717b4b39451f91e792f56 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/QPYcom\346\237\245\347\234\213usr\347\233\256\345\275\225\346\230\257\345\220\246\346\234\211\346\213\250\345\217\267\351\205\215\347\275\256\346\226\207\344\273\266.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/QuecPython\347\275\221\345\215\241\346\277\200\346\264\273\346\234\272\345\210\266.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/QuecPython\347\275\221\345\215\241\346\277\200\346\264\273\346\234\272\345\210\266.png" new file mode 100644 index 0000000000000000000000000000000000000000..8f63723b94a7363a08e0841cae7ee0f8e95a96c2 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/QuecPython\347\275\221\345\215\241\346\277\200\346\264\273\346\234\272\345\210\266.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\215\241PUK\347\240\201\344\275\215\347\275\256.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\215\241PUK\347\240\201\344\275\215\347\275\256.png" new file mode 100644 index 0000000000000000000000000000000000000000..90981886b28104d58324f021229355aeefdede83 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\215\241PUK\347\240\201\344\275\215\347\275\256.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\215\241\347\212\266\346\200\201\344\270\2721\344\275\206\346\230\257\346\263\250\347\275\221\345\244\261\350\264\245.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\215\241\347\212\266\346\200\201\344\270\2721\344\275\206\346\230\257\346\263\250\347\275\221\345\244\261\350\264\245.png" new file mode 100644 index 0000000000000000000000000000000000000000..d0ccb5567c7b9c27ccee40d69cb7e2c1c2f6b4d9 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\215\241\347\212\266\346\200\201\344\270\2721\344\275\206\346\230\257\346\263\250\347\275\221\345\244\261\350\264\245.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\215\241\350\242\253\351\224\201.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\215\241\350\242\253\351\224\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..3e77a81bb123b20e4ae12c2f030fe14f72d21af0 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\215\241\350\242\253\351\224\201.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\220\257\347\224\250PIN\347\240\201\351\252\214\350\257\201.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\220\257\347\224\250PIN\347\240\201\351\252\214\350\257\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..55b336b7fb16126d0a5ad9d95f5e9901e0270b79 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/SIM\345\220\257\347\224\250PIN\347\240\201\351\252\214\350\257\201.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/checkNet\346\234\272\345\210\266.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/checkNet\346\234\272\345\210\266.png" new file mode 100644 index 0000000000000000000000000000000000000000..d7688fcb4363bcd63455af9b7158865e1771419c Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/checkNet\346\234\272\345\210\266.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/csq\346\237\245\350\257\242.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/csq\346\237\245\350\257\242.png" new file mode 100644 index 0000000000000000000000000000000000000000..487ea8458eabc66ed18e499c073fe3badbaba103 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/csq\346\237\245\350\257\242.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/API\346\237\245\350\257\242\346\213\250\345\217\267\347\273\223\346\236\234.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/API\346\237\245\350\257\242\346\213\250\345\217\267\347\273\223\346\236\234.png" new file mode 100644 index 0000000000000000000000000000000000000000..2bbb988a9021ee2102882a79d31f41ffe9ba5a87 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/API\346\237\245\350\257\242\346\213\250\345\217\267\347\273\223\346\236\234.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/API\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/API\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" new file mode 100644 index 0000000000000000000000000000000000000000..0acde50fca6fd6132131f6527ddde0e8f1c73400 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/API\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/AT\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/AT\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" new file mode 100644 index 0000000000000000000000000000000000000000..90b7541cf6173e92dd46a384ac4544965e3e9420 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/AT\346\237\245\350\257\242\347\275\221\347\273\234\346\263\250\345\206\214\346\210\220\345\212\237.png" differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/Functional_Diagram.png b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/Functional_Diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..fc038087e2e166ad053113402e68e62e1aad138b Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/Functional_Diagram.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/LTE_network_architecture.png b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/LTE_network_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..bdc5e92e581809b56a1aa66f12cb8bb372756543 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/LTE_network_architecture.png differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/QPYcom\346\211\223\345\274\200AT\345\217\243.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/QPYcom\346\211\223\345\274\200AT\345\217\243.png" new file mode 100644 index 0000000000000000000000000000000000000000..a61540b83e7e41ef06f33ae99a3aeb07113e784e Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/QPYcom\346\211\223\345\274\200AT\345\217\243.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/QPYcom\346\211\223\345\274\200REPL.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/QPYcom\346\211\223\345\274\200REPL.png" new file mode 100644 index 0000000000000000000000000000000000000000..245b23d28701d23c3fa4fc9eb7a2b28a3466a768 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/QPYcom\346\211\223\345\274\200REPL.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/QuecPython\347\275\221\345\215\241\346\277\200\346\264\273\346\234\272\345\210\266.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/QuecPython\347\275\221\345\215\241\346\277\200\346\264\273\346\234\272\345\210\266.png" new file mode 100644 index 0000000000000000000000000000000000000000..8f63723b94a7363a08e0841cae7ee0f8e95a96c2 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/QuecPython\347\275\221\345\215\241\346\277\200\346\264\273\346\234\272\345\210\266.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/checkNet\346\234\272\345\210\266.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/checkNet\346\234\272\345\210\266.png" new file mode 100644 index 0000000000000000000000000000000000000000..d7688fcb4363bcd63455af9b7158865e1771419c Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/checkNet\346\234\272\345\210\266.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\345\244\232\350\267\257\350\234\202\347\252\235\346\227\240\347\272\277\347\275\221\345\215\241.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\345\244\232\350\267\257\350\234\202\347\252\235\346\227\240\347\272\277\347\275\221\345\215\241.png" new file mode 100644 index 0000000000000000000000000000000000000000..fb136955853bfb62eacee30a3c85faa03cb1f782 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\345\244\232\350\267\257\350\234\202\347\252\235\346\227\240\347\272\277\347\275\221\345\215\241.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\216\247\345\210\266\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\216\247\345\210\266\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" new file mode 100644 index 0000000000000000000000000000000000000000..743ed8a9997c430ad7377955d11321ec542801ec Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\216\247\345\210\266\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\225\260\346\215\256\346\265\201.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\225\260\346\215\256\346\265\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..aa5c87eb088bafd04bc7ab893ed9fc2750746851 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\225\260\346\215\256\346\265\201.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\227\240APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\227\240APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" new file mode 100644 index 0000000000000000000000000000000000000000..b24353b71a91e788a236b8ce0b4f2184d4450fc6 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\227\240APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\234\211APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\234\211APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" new file mode 100644 index 0000000000000000000000000000000000000000..6b43c09e13844f416de88ec36ed0ca5f9b5f0284 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\234\211APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\234\211APN\346\277\200\346\264\273\345\244\232\350\267\257\347\275\221\345\215\241.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\234\211APN\346\277\200\346\264\273\345\244\232\350\267\257\347\275\221\345\215\241.png" new file mode 100644 index 0000000000000000000000000000000000000000..744a7e452ededc84440241d0925a090463728cad Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\346\234\211APN\346\277\200\346\264\273\345\244\232\350\267\257\347\275\221\345\215\241.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\224\250\346\210\267\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\224\250\346\210\267\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" new file mode 100644 index 0000000000000000000000000000000000000000..b6f6698207b6b6be8261839d0baefaf6a77eb054 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\224\250\346\210\267\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\241\254\344\273\266\346\225\260\346\215\256\346\265\201.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\241\254\344\273\266\346\225\260\346\215\256\346\265\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..7898a8d35dbb2660996460b65605b67fc32c482f Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\241\254\344\273\266\346\225\260\346\215\256\346\265\201.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\345\216\206\345\217\262.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\345\216\206\345\217\262.png" new file mode 100644 index 0000000000000000000000000000000000000000..322dcbfa92b0ce660b61a4206ef4e45e804b2a94 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\345\216\206\345\217\262.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\347\256\200\345\217\262.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\347\256\200\345\217\262.png" new file mode 100644 index 0000000000000000000000000000000000000000..51647e2acfd1ed195a9e0a1b851e4d9a31c239a6 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\347\256\200\345\217\262.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\273\204\347\275\221\346\226\271\345\274\217.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\273\204\347\275\221\346\226\271\345\274\217.png" new file mode 100644 index 0000000000000000000000000000000000000000..cfd40ed826f745b219ef62554948730b9875c2ed Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\273\204\347\275\221\346\226\271\345\274\217.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\275\221\345\215\241\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\275\221\345\215\241\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.png" new file mode 100644 index 0000000000000000000000000000000000000000..f4d2d2f0d0791a31861fd212be267a7835aedc14 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\347\275\221\345\215\241\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\350\207\252\345\212\250\351\207\215\350\277\236\345\237\272\346\234\254\346\265\201\347\250\213.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\350\207\252\345\212\250\351\207\215\350\277\236\345\237\272\346\234\254\346\265\201\347\250\213.png" new file mode 100644 index 0000000000000000000000000000000000000000..1681f7620902fbee5f160fe8b1f1fa0e5573b715 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/tmp/\350\207\252\345\212\250\351\207\215\350\277\236\345\237\272\346\234\254\346\265\201\347\250\213.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\344\275\277\347\224\250\346\234\200\346\226\260\350\256\276\347\275\256\347\232\204PIN\347\240\201\350\277\233\350\241\214\351\252\214\350\257\201\345\222\214\345\205\263\351\227\255\351\252\214\350\257\201.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\344\275\277\347\224\250\346\234\200\346\226\260\350\256\276\347\275\256\347\232\204PIN\347\240\201\350\277\233\350\241\214\351\252\214\350\257\201\345\222\214\345\205\263\351\227\255\351\252\214\350\257\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..bf6f91cdf1548f96b56c744de54875ae839733cb Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\344\275\277\347\224\250\346\234\200\346\226\260\350\256\276\347\275\256\347\232\204PIN\347\240\201\350\277\233\350\241\214\351\252\214\350\257\201\345\222\214\345\205\263\351\227\255\351\252\214\350\257\201.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\345\205\263\351\227\255PIN\347\240\201\351\252\214\350\257\201.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\345\205\263\351\227\255PIN\347\240\201\351\252\214\350\257\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..c23262f37a0326dd3d89f42970088eb79d28ca37 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\345\205\263\351\227\255PIN\347\240\201\351\252\214\350\257\201.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\345\244\232\350\267\257\350\234\202\347\252\235\346\227\240\347\272\277\347\275\221\345\215\241.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\345\244\232\350\267\257\350\234\202\347\252\235\346\227\240\347\272\277\347\275\221\345\215\241.png" new file mode 100644 index 0000000000000000000000000000000000000000..fb136955853bfb62eacee30a3c85faa03cb1f782 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\345\244\232\350\267\257\350\234\202\347\252\235\346\227\240\347\272\277\347\275\221\345\215\241.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\345\274\200\345\220\257\347\254\254\344\270\200\350\267\257\347\275\221\345\215\241\350\207\252\345\212\250\346\277\200\346\264\273\345\222\214\350\207\252\345\212\250\351\207\215\350\277\236.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\345\274\200\345\220\257\347\254\254\344\270\200\350\267\257\347\275\221\345\215\241\350\207\252\345\212\250\346\277\200\346\264\273\345\222\214\350\207\252\345\212\250\351\207\215\350\277\236.png" new file mode 100644 index 0000000000000000000000000000000000000000..e0710aaca61b170435bbb34bf3ac6be27b676b36 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\345\274\200\345\220\257\347\254\254\344\270\200\350\267\257\347\275\221\345\215\241\350\207\252\345\212\250\346\277\200\346\264\273\345\222\214\350\207\252\345\212\250\351\207\215\350\277\236.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\216\247\345\210\266\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\216\247\345\210\266\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" new file mode 100644 index 0000000000000000000000000000000000000000..743ed8a9997c430ad7377955d11321ec542801ec Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\216\247\345\210\266\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\225\260\346\215\256\346\265\201.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\225\260\346\215\256\346\265\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..aa5c87eb088bafd04bc7ab893ed9fc2750746851 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\225\260\346\215\256\346\265\201.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\227\240APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\227\240APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" new file mode 100644 index 0000000000000000000000000000000000000000..b24353b71a91e788a236b8ce0b4f2184d4450fc6 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\227\240APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\227\240\347\272\277\347\275\221\345\215\241\346\277\200\346\264\273\345\244\261\350\264\245.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\227\240\347\272\277\347\275\221\345\215\241\346\277\200\346\264\273\345\244\261\350\264\245.png" new file mode 100644 index 0000000000000000000000000000000000000000..0b59ddcb5ce370927efe62448d407657ad5bdcf3 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\227\240\347\272\277\347\275\221\345\215\241\346\277\200\346\264\273\345\244\261\350\264\245.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\227\240\347\272\277\347\275\221\345\215\241\346\277\200\346\264\273\346\210\220\345\212\237.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\227\240\347\272\277\347\275\221\345\215\241\346\277\200\346\264\273\346\210\220\345\212\237.png" new file mode 100644 index 0000000000000000000000000000000000000000..62d66e511b93746e868fb180a3a80f5a78f2a9e0 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\227\240\347\272\277\347\275\221\345\215\241\346\277\200\346\264\273\346\210\220\345\212\237.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\234\211APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\234\211APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" new file mode 100644 index 0000000000000000000000000000000000000000..6b43c09e13844f416de88ec36ed0ca5f9b5f0284 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\234\211APN\346\277\200\346\264\273\344\270\200\350\267\257\347\275\221\345\215\241.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\234\211APN\346\277\200\346\264\273\345\244\232\350\267\257\347\275\221\345\215\241.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\234\211APN\346\277\200\346\264\273\345\244\232\350\267\257\347\275\221\345\215\241.png" new file mode 100644 index 0000000000000000000000000000000000000000..744a7e452ededc84440241d0925a090463728cad Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\346\234\211APN\346\277\200\346\264\273\345\244\232\350\267\257\347\275\221\345\215\241.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\224\250\346\210\267\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\224\250\346\210\267\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" new file mode 100644 index 0000000000000000000000000000000000000000..b6f6698207b6b6be8261839d0baefaf6a77eb054 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\224\250\346\210\267\351\235\242\345\210\206\345\261\202\347\273\223\346\236\204.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\241\254\344\273\266\346\225\260\346\215\256\346\265\201.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\241\254\344\273\266\346\225\260\346\215\256\346\265\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..7898a8d35dbb2660996460b65605b67fc32c482f Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\241\254\344\273\266\346\225\260\346\215\256\346\265\201.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\345\216\206\345\217\262.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\345\216\206\345\217\262.png" new file mode 100644 index 0000000000000000000000000000000000000000..322dcbfa92b0ce660b61a4206ef4e45e804b2a94 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\345\216\206\345\217\262.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\347\256\200\345\217\262.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\347\256\200\345\217\262.png" new file mode 100644 index 0000000000000000000000000000000000000000..51647e2acfd1ed195a9e0a1b851e4d9a31c239a6 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\247\273\345\212\250\351\200\232\344\277\241\345\217\221\345\261\225\347\256\200\345\217\262.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\273\204\347\275\221\346\226\271\345\274\217.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\273\204\347\275\221\346\226\271\345\274\217.png" new file mode 100644 index 0000000000000000000000000000000000000000..cfd40ed826f745b219ef62554948730b9875c2ed Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\273\204\347\275\221\346\226\271\345\274\217.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\275\221\345\215\241\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\275\221\345\215\241\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.png" new file mode 100644 index 0000000000000000000000000000000000000000..f4d2d2f0d0791a31861fd212be267a7835aedc14 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\347\275\221\345\215\241\345\210\235\345\247\213\345\214\226\346\265\201\347\250\213.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\350\207\252\345\212\250\351\207\215\350\277\236\345\237\272\346\234\254\346\265\201\347\250\213.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\350\207\252\345\212\250\351\207\215\350\277\236\345\237\272\346\234\254\346\265\201\347\250\213.png" new file mode 100644 index 0000000000000000000000000000000000000000..1681f7620902fbee5f160fe8b1f1fa0e5573b715 Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\350\207\252\345\212\250\351\207\215\350\277\236\345\237\272\346\234\254\346\265\201\347\250\213.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\350\276\223\345\205\245PUK\350\247\243\351\224\201.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\350\276\223\345\205\245PUK\350\247\243\351\224\201.png" new file mode 100644 index 0000000000000000000000000000000000000000..e95d64d5e6bd12aa33342ec0b04ec6b289bb192e Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\350\276\223\345\205\245PUK\350\247\243\351\224\201.png" differ diff --git "a/docs/Application_guide/zh/media/network-comm/nic/cellular/\351\207\215\345\220\257\345\220\216SIM\345\215\241\347\212\266\346\200\201\346\201\242\345\244\215\346\255\243\345\270\270.png" "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\351\207\215\345\220\257\345\220\216SIM\345\215\241\347\212\266\346\200\201\346\201\242\345\244\215\346\255\243\345\270\270.png" new file mode 100644 index 0000000000000000000000000000000000000000..ad96db1ca00e7c8e572042f4fb6d9f37db869f9b Binary files /dev/null and "b/docs/Application_guide/zh/media/network-comm/nic/cellular/\351\207\215\345\220\257\345\220\216SIM\345\215\241\347\212\266\346\200\201\346\201\242\345\244\215\346\255\243\345\270\270.png" differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/net_tcpip_main.png b/docs/Application_guide/zh/media/network-comm/nic/net_tcpip_main.png new file mode 100644 index 0000000000000000000000000000000000000000..b1cbcf5a1165135636799bf778c18e1e2c036583 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/net_tcpip_main.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_4g_eth_wifi.png b/docs/Application_guide/zh/media/network-comm/nic/network_4g_eth_wifi.png new file mode 100644 index 0000000000000000000000000000000000000000..04199ab418476e7263e8d2382c001df05b2e73e9 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_4g_eth_wifi.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_4g_single.png b/docs/Application_guide/zh/media/network-comm/nic/network_4g_single.png new file mode 100644 index 0000000000000000000000000000000000000000..73b983ac31ddc9d6497b3c2fa8693aab1ed126b1 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_4g_single.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_config_4g_doule.png b/docs/Application_guide/zh/media/network-comm/nic/network_config_4g_doule.png new file mode 100644 index 0000000000000000000000000000000000000000..cc10cf320d5ea98b5e7a8af664dc2560909ac233 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_config_4g_doule.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_config_ap_double_4g.png b/docs/Application_guide/zh/media/network-comm/nic/network_config_ap_double_4g.png new file mode 100644 index 0000000000000000000000000000000000000000..6e9a2166e27e8275a51e5806d95fc33596820295 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_config_ap_double_4g.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_config_cmd.png b/docs/Application_guide/zh/media/network-comm/nic/network_config_cmd.png new file mode 100644 index 0000000000000000000000000000000000000000..f8a74f369fa4909ff66e735e9d15e192067d7159 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_config_cmd.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_config_lan_double_4g.png b/docs/Application_guide/zh/media/network-comm/nic/network_config_lan_double_4g.png new file mode 100644 index 0000000000000000000000000000000000000000..2f593fbb2b6d5c069d5a40ecb5d84770b11caaf5 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_config_lan_double_4g.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_config_lan_socket.png b/docs/Application_guide/zh/media/network-comm/nic/network_config_lan_socket.png new file mode 100644 index 0000000000000000000000000000000000000000..02de5df36b2e6d43c7eb93f4a0c93fff6d511bef Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_config_lan_socket.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_config_web.png b/docs/Application_guide/zh/media/network-comm/nic/network_config_web.png new file mode 100644 index 0000000000000000000000000000000000000000..b28f22fe0a36857f559ff104c1bc49c1874a59a2 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_config_web.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_api_process.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_api_process.png new file mode 100644 index 0000000000000000000000000000000000000000..c922f6e079ec00a2be619b6dc3bcbf985ed3be7f Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_api_process.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_demo_1.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_demo_1.png new file mode 100644 index 0000000000000000000000000000000000000000..b106b90e0d4f19f076f121dfcb1c85841b9fc43f Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_demo_1.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_demo_3.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_demo_3.png new file mode 100644 index 0000000000000000000000000000000000000000..8524c8d5a4fc6a5064438f6b939011b47d6de66c Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_demo_3.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway.png new file mode 100644 index 0000000000000000000000000000000000000000..3e9ed9d62d4dddca0bba55a12537b36eab01ca85 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway_bind.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway_bind.png new file mode 100644 index 0000000000000000000000000000000000000000..186d99760a4462873c4f4379f1ebea8aee759d5a Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway_bind.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway_default.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway_default.png new file mode 100644 index 0000000000000000000000000000000000000000..428835fc14a6593cd8b8c813a4c743b83079d52d Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway_default.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway_web.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway_web.png new file mode 100644 index 0000000000000000000000000000000000000000..a1faa56dba56473d40eee975dd066a5f8c66cfb0 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_gateway_web.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_node.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_node.png new file mode 100644 index 0000000000000000000000000000000000000000..85a8556b1e962aeb0b7d22e4fc10a47eb8727959 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_node.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii.png new file mode 100644 index 0000000000000000000000000000000000000000..e3116ec1daf2da810853816aa90c64d384b41a75 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii_adapter.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii_adapter.png new file mode 100644 index 0000000000000000000000000000000000000000..72582899ba40c54b99c9ca1cf2f216750011e7f0 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii_adapter.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii_full.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii_full.png new file mode 100644 index 0000000000000000000000000000000000000000..d8efee8fc4f374d59a2cf1b7c69787af72bc1f72 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii_full.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii_init_flow.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii_init_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..6bbf0582fcdc5b33e5993e5ca434ed010fb23147 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_rmii_init_flow.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi.png new file mode 100644 index 0000000000000000000000000000000000000000..f2a259c6837ba0d4e583431a72a033d20d6e0d92 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi_adapter.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi_adapter.png new file mode 100644 index 0000000000000000000000000000000000000000..2f421bcd4c4a30543f50e4f0fdb2af5f2639ff77 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi_adapter.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi_full.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi_full.png new file mode 100644 index 0000000000000000000000000000000000000000..cf55565751383bec0e00f39104eb18d890c97328 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi_full.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi_init_flow.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi_init_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..542a8a7e399dc4f3d1f9b2e2b04baae9888feae4 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_spi_init_flow.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_eth_wifi_4g.png b/docs/Application_guide/zh/media/network-comm/nic/network_eth_wifi_4g.png new file mode 100644 index 0000000000000000000000000000000000000000..3052c7f17a29aaa1747816f34b535f31c3398876 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_eth_wifi_4g.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_modem.png b/docs/Application_guide/zh/media/network-comm/nic/network_modem.png new file mode 100644 index 0000000000000000000000000000000000000000..55026c831bf7c195c6b9608c9d993f4cd2ceaa51 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_modem.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_multi_nic.png b/docs/Application_guide/zh/media/network-comm/nic/network_multi_nic.png new file mode 100644 index 0000000000000000000000000000000000000000..590d38bbc36fedc3efe9070780d8a2ef8cbf3055 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_multi_nic.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_multi_nic_connection.png b/docs/Application_guide/zh/media/network-comm/nic/network_multi_nic_connection.png new file mode 100644 index 0000000000000000000000000000000000000000..82f5b9eeb13825d475c6bf550478f5184d5120a7 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_multi_nic_connection.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_socket_usr.png b/docs/Application_guide/zh/media/network-comm/nic/network_socket_usr.png new file mode 100644 index 0000000000000000000000000000000000000000..ab8f176ec63cd53688b13214192e79c3e418ada0 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_socket_usr.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_socket_usr_run.png b/docs/Application_guide/zh/media/network-comm/nic/network_socket_usr_run.png new file mode 100644 index 0000000000000000000000000000000000000000..67b91d6de3ca62ce44aa91b82d2e0c2f11707239 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_socket_usr_run.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_socket_usr_run_resp.png b/docs/Application_guide/zh/media/network-comm/nic/network_socket_usr_run_resp.png new file mode 100644 index 0000000000000000000000000000000000000000..07dca31ff239b8002b24dfc69f21ccf62d7b2ef3 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_socket_usr_run_resp.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_tcpip_flow.png b/docs/Application_guide/zh/media/network-comm/nic/network_tcpip_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..2c05cff7e5eb258ed85204400f4ccf338f80e55d Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_tcpip_flow.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_tcpip_nic.png b/docs/Application_guide/zh/media/network-comm/nic/network_tcpip_nic.png new file mode 100644 index 0000000000000000000000000000000000000000..87b3c1c39325b7ee9a26234588bd092180021a4e Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_tcpip_nic.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet.png new file mode 100644 index 0000000000000000000000000000000000000000..b714c8cba17e4d3e1d016706d85f02c9879698ba Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_api_process.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_api_process.png new file mode 100644 index 0000000000000000000000000000000000000000..53c90720d755aeea7fc1a08c08c4a76fbae22034 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_api_process.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_1.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_1.png new file mode 100644 index 0000000000000000000000000000000000000000..b96e55a2f66c9030a12aaa646cfdc66d212caf35 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_1.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_2.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_2.png new file mode 100644 index 0000000000000000000000000000000000000000..648b8b478f21474bc9f5caa2ffb3e5109e900e06 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_2.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_3.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_3.png new file mode 100644 index 0000000000000000000000000000000000000000..5d12c2b5e835fbced554d1621aa4cbe49396b0c1 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_3.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_4.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_4.png new file mode 100644 index 0000000000000000000000000000000000000000..78264a611ab4d3a6b97dfb25a2b008713b41987a Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_4.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_5.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_5.png new file mode 100644 index 0000000000000000000000000000000000000000..c199c04bad08182a75fdef4b47f8fd6359f114c2 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_5.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_6.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_6.png new file mode 100644 index 0000000000000000000000000000000000000000..5e6fbfa8ccf5d9c16719206f09942baae8f43dcd Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_6.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_7.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_7.png new file mode 100644 index 0000000000000000000000000000000000000000..ba80f08deac0572bd6f3e2c6720ac8b13607b7b2 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_7.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_qpycom.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_qpycom.png new file mode 100644 index 0000000000000000000000000000000000000000..e323be66a68fee418df2973be6e718a6c4999365 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_demo_qpycom.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_ubuntu_choose.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_ubuntu_choose.png new file mode 100644 index 0000000000000000000000000000000000000000..a49c78c4fa3dc97d1b05c0725bee344791bf231b Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_ubuntu_choose.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_ubuntu_nic.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_ubuntu_nic.png new file mode 100644 index 0000000000000000000000000000000000000000..46468daef6f4083b387703c164eec5fe72044609 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_ubuntu_nic.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_ubuntu_ping.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_ubuntu_ping.png new file mode 100644 index 0000000000000000000000000000000000000000..46ad9db016b72a3dd789a9454c0677a8a74de541 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_ubuntu_ping.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_usb.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_usb.png new file mode 100644 index 0000000000000000000000000000000000000000..82e802da28a37cf55180b8184c7d06f66a9f5835 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_usb.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_usb_flow.png b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_usb_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..9be721ae7a7074ef30a245a79081a31e8afa647d Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_usbnet_usb_flow.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap.png new file mode 100644 index 0000000000000000000000000000000000000000..c6547c3c91995e03117c46c476257e8da6c4f025 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap_connected.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap_connected.png new file mode 100644 index 0000000000000000000000000000000000000000..415fefab0543f8ce5fe590713abba622df99008d Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap_connected.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap_connecting.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap_connecting.png new file mode 100644 index 0000000000000000000000000000000000000000..1b5bddd7102969b8c90e03d9fa48e24d8d99362d Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap_connecting.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap_single.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap_single.png new file mode 100644 index 0000000000000000000000000000000000000000..34168b5ef290a6bf36574012cfff89d6427214ac Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_ap_single.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_api_process.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_api_process.png new file mode 100644 index 0000000000000000000000000000000000000000..5e6eb5deca7562947778bbed8a859e83b039f96b Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_api_process.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_core.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_core.png new file mode 100644 index 0000000000000000000000000000000000000000..c485e4f3ea20cb5dde4ae42649f592bd198fc270 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_core.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_esp8266.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_esp8266.png new file mode 100644 index 0000000000000000000000000000000000000000..56a3b9842373379c2a0e1494a62c780aa981dbd5 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_esp8266.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_esp8266_api_process.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_esp8266_api_process.png new file mode 100644 index 0000000000000000000000000000000000000000..9e38597823efe9c8c7b287ccbdb6d97620fbd6b0 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_esp8266_api_process.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_internal.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_internal.png new file mode 100644 index 0000000000000000000000000000000000000000..c090c93cde09f513e69f9c53529a50a523670669 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_internal.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_router_connection.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_router_connection.png new file mode 100644 index 0000000000000000000000000000000000000000..f843845886a68d929e6b585f1d12655d89e3e6eb Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_router_connection.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_sdio.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_sdio.png new file mode 100644 index 0000000000000000000000000000000000000000..5a45065544c2b8124701d7cf486ba8300d30058d Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_sdio.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_sdio_flow.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_sdio_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..7316fe9c2679c1af728c44045dd8de8368f8ac4c Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_sdio_flow.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_single.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_single.png new file mode 100644 index 0000000000000000000000000000000000000000..0d762004fe8cf44688bb648845f8bac55a8635d0 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_single.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_station.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_station.png new file mode 100644 index 0000000000000000000000000000000000000000..74aaa97557b25f6b542977bcf70ebfc02f0a78ee Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_station.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_station_single.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_station_single.png new file mode 100644 index 0000000000000000000000000000000000000000..392825a442f92a8e4c4131cce174a97e1612cc7d Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_station_single.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_station_single_1.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_station_single_1.png new file mode 100644 index 0000000000000000000000000000000000000000..5ddf55ac7fcf09e4c265f14329febc540813f1b7 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_station_single_1.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_uart.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_uart.png new file mode 100644 index 0000000000000000000000000000000000000000..9ac5cfff11e54ea608a9fb0aee235d4e6db23cb7 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_uart.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/network_wifi_uart_flow.png b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_uart_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..4c2e77c6c4e940c6946d8991f411f10f1e3865c0 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/network_wifi_uart_flow.png differ diff --git a/docs/Application_guide/zh/media/network-comm/nic/nic_tcpip_pos.png b/docs/Application_guide/zh/media/network-comm/nic/nic_tcpip_pos.png new file mode 100644 index 0000000000000000000000000000000000000000..ffd60154ff4c3bbbee5fd3d64382e5fcf21cad30 Binary files /dev/null and b/docs/Application_guide/zh/media/network-comm/nic/nic_tcpip_pos.png differ diff --git a/docs/Application_guide/zh/media/network/dataCall/network.dataCall-1.png b/docs/Application_guide/zh/media/network/dataCall/network.dataCall-1.png deleted file mode 100644 index 2492a659e58bf886766d236cda0c1f857884ad83..0000000000000000000000000000000000000000 Binary files a/docs/Application_guide/zh/media/network/dataCall/network.dataCall-1.png and /dev/null differ diff --git a/docs/Application_guide/zh/media/network/test.png b/docs/Application_guide/zh/media/network/test.png deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/media/system/power-consumption/3GPP_PSM_TIMER.png b/docs/Application_guide/zh/media/system/power-consumption/3GPP_PSM_TIMER.png new file mode 100644 index 0000000000000000000000000000000000000000..eac75f9462d2bcca46bd19562e50162b277868c7 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/3GPP_PSM_TIMER.png differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/BaseCurrent.jpg b/docs/Application_guide/zh/media/system/power-consumption/BaseCurrent.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e1cdee3eed0c4b7bcf57617f70e6bb1deac71fe5 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/BaseCurrent.jpg differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/GPIO.png b/docs/Application_guide/zh/media/system/power-consumption/GPIO.png new file mode 100644 index 0000000000000000000000000000000000000000..4183be19c3238231a01a2332ae236ab5fa17681c Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/GPIO.png differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/Networkwakeup.png b/docs/Application_guide/zh/media/system/power-consumption/Networkwakeup.png new file mode 100644 index 0000000000000000000000000000000000000000..d204e6eb0cb0287a4c30c3ed1225f57bcf103bf0 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/Networkwakeup.png differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/PSM.jpg b/docs/Application_guide/zh/media/system/power-consumption/PSM.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1cef9f00a6df2c0041c546b0d3fc67290c21a6c1 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/PSM.jpg differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/PSMCycle.jpg b/docs/Application_guide/zh/media/system/power-consumption/PSMCycle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2c81a9e5a7137374df2d7d75068849561de801e4 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/PSMCycle.jpg differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/PSMMainProcess.png b/docs/Application_guide/zh/media/system/power-consumption/PSMMainProcess.png new file mode 100644 index 0000000000000000000000000000000000000000..6b226f3967b7d945bab0cfc450970ae968d4deff Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/PSMMainProcess.png differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/PSMRequest.png b/docs/Application_guide/zh/media/system/power-consumption/PSMRequest.png new file mode 100644 index 0000000000000000000000000000000000000000..fdd78719cb266f8e94fda27e88107b65427ab023 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/PSMRequest.png differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/PSMWrongACCEPT.png b/docs/Application_guide/zh/media/system/power-consumption/PSMWrongACCEPT.png new file mode 100644 index 0000000000000000000000000000000000000000..3909b67d21713e6a70255f829e148a882b9e298f Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/PSMWrongACCEPT.png differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/Paging.jpg b/docs/Application_guide/zh/media/system/power-consumption/Paging.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ceb0a7dc85dc3b62a3eb469c6a6e295806f341be Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/Paging.jpg differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/RRC.jpg b/docs/Application_guide/zh/media/system/power-consumption/RRC.jpg new file mode 100644 index 0000000000000000000000000000000000000000..090a177ce130c3821b7b23a5cfe7314e0f6ddfb4 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/RRC.jpg differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/RTOSSleep.png b/docs/Application_guide/zh/media/system/power-consumption/RTOSSleep.png new file mode 100644 index 0000000000000000000000000000000000000000..16de973ff400a4f47d274e878bdcbd6d8cbdf617 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/RTOSSleep.png differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/RTOSWakeup.png b/docs/Application_guide/zh/media/system/power-consumption/RTOSWakeup.png new file mode 100644 index 0000000000000000000000000000000000000000..533153f66a2feed963257a804f2418a5066623ca Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/RTOSWakeup.png differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/TAU&ACT.jpg b/docs/Application_guide/zh/media/system/power-consumption/TAU&ACT.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d5a0776693196b880ab1950ebc6ce07da4467aae Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/TAU&ACT.jpg differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/TAU&ACT.png b/docs/Application_guide/zh/media/system/power-consumption/TAU&ACT.png new file mode 100644 index 0000000000000000000000000000000000000000..d5a0776693196b880ab1950ebc6ce07da4467aae Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/TAU&ACT.png differ diff --git a/docs/Application_guide/zh/media/system/power-consumption/Timer.png b/docs/Application_guide/zh/media/system/power-consumption/Timer.png new file mode 100644 index 0000000000000000000000000000000000000000..24bbc173d733183d43e823735c07b7c31e501c99 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-consumption/Timer.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/ChargerCurrent.png b/docs/Application_guide/zh/media/system/power-manager/ChargerCurrent.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d270dba9f1263550907171ccff5a4b032f7017 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/ChargerCurrent.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/Mainstartup.png b/docs/Application_guide/zh/media/system/power-manager/Mainstartup.png new file mode 100644 index 0000000000000000000000000000000000000000..d84918d7b8a869598be4f5a8650d3436c9c608a3 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/Mainstartup.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/PowerkeyOn.png b/docs/Application_guide/zh/media/system/power-manager/PowerkeyOn.png new file mode 100644 index 0000000000000000000000000000000000000000..4a010eb57b849f38260b541f87e8a87070e7065f Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/PowerkeyOn.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/Powerkeyoff.png b/docs/Application_guide/zh/media/system/power-manager/Powerkeyoff.png new file mode 100644 index 0000000000000000000000000000000000000000..f3fbe678c66c29b78d5dd85523932a798f3f6374 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/Powerkeyoff.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/PoweroffCharger.png b/docs/Application_guide/zh/media/system/power-manager/PoweroffCharger.png new file mode 100644 index 0000000000000000000000000000000000000000..09e0d5c926b0e1c2310a7650d9deffd233dd0c70 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/PoweroffCharger.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/RTOS.png b/docs/Application_guide/zh/media/system/power-manager/RTOS.png new file mode 100644 index 0000000000000000000000000000000000000000..cc16b75cc4f9fb7c5f1a0e07fff8c451ff59d74c Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/RTOS.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/Reset.png b/docs/Application_guide/zh/media/system/power-manager/Reset.png new file mode 100644 index 0000000000000000000000000000000000000000..36dd1aaf7264850b74c90c2446191b01b73aa1a2 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/Reset.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/USB_BOOTTime.png b/docs/Application_guide/zh/media/system/power-manager/USB_BOOTTime.png new file mode 100644 index 0000000000000000000000000000000000000000..c3b440f297072c4372e0e60428012303e2d04298 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/USB_BOOTTime.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/VDD_EXTTime.png b/docs/Application_guide/zh/media/system/power-manager/VDD_EXTTime.png new file mode 100644 index 0000000000000000000000000000000000000000..8de2c783ef56c6e4b11cb738bff3cac054c17e4d Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/VDD_EXTTime.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/autostart.png b/docs/Application_guide/zh/media/system/power-manager/autostart.png new file mode 100644 index 0000000000000000000000000000000000000000..6323e42022a9b221aa949bfbbf1236cc1be7a54d Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/autostart.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/bootloader.png b/docs/Application_guide/zh/media/system/power-manager/bootloader.png new file mode 100644 index 0000000000000000000000000000000000000000..7d54a07e54398c723fc3785edc9cc78c8f5f9888 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/bootloader.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/bootrom.png b/docs/Application_guide/zh/media/system/power-manager/bootrom.png new file mode 100644 index 0000000000000000000000000000000000000000..6adc29d291e6a4de677e7820d421f7b50cd10e07 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/bootrom.png differ diff --git a/docs/Application_guide/zh/media/system/power-manager/pythonVM.png b/docs/Application_guide/zh/media/system/power-manager/pythonVM.png new file mode 100644 index 0000000000000000000000000000000000000000..a9182dda31fa6fdfac29f71f56bc8cfc981321d5 Binary files /dev/null and b/docs/Application_guide/zh/media/system/power-manager/pythonVM.png differ diff --git a/docs/Application_guide/zh/peripherals/README.md b/docs/Application_guide/zh/network-comm/README.md similarity index 100% rename from docs/Application_guide/zh/peripherals/README.md rename to docs/Application_guide/zh/network-comm/README.md diff --git a/docs/Getting_started/zh/appendix/README.md b/docs/Application_guide/zh/network-comm/net-protocols/README.md similarity index 100% rename from docs/Getting_started/zh/appendix/README.md rename to docs/Application_guide/zh/network-comm/net-protocols/README.md diff --git a/docs/Application_guide/zh/network-comm/net-protocols/ftp.md b/docs/Application_guide/zh/network-comm/net-protocols/ftp.md new file mode 100644 index 0000000000000000000000000000000000000000..a0c90c2ca09d4fc1d8c2db01d4d3697dbc581ced --- /dev/null +++ b/docs/Application_guide/zh/network-comm/net-protocols/ftp.md @@ -0,0 +1,401 @@ +# FTP 应用文档 + +本文档详细介绍了如何在 QuecPython 中使用 FTP 协议进行文件传输,并提供了协议概述、环境搭建、代码示例和结果演示。 + +## 协议概述 + +FTP(File Transfer Protocol)是一种用于在计算机网络中传输文件的标准协议,它基于客户端-服务器模型,通过控制连接和数据连接实现文件的上传、下载和管理操作。 + +在FTP通信中,客户端负责发起连接并发送FTP命令,而服务器则负责接收命令并执行相应的操作,FTP使用控制连接来传输命令和响应,通过控制连接建立的数据连接用于实际的文件传输。 + +FTP的操作包括: + +- 登录认证:客户端向服务器发送用户名和密码进行身份认证,以获取访问权限。 +- 目录操作:客户端可以请求列出服务器上的文件和目录列表、切换当前工作目录、创建和删除目录等。 +- 文件传输:客户端可以上传文件到服务器或从服务器下载文件到本地系统。 +- 文件管理:客户端可以对服务器上的文件进行重命名、删除、复制等操作。 +- 被动和主动模式:客户端和服务器之间的数据传输可以采用主动模式或被动模式,具体取决于网络环境和配置。 + +## FTP 的作用 + +FTP的作用包括但不限于以下方面: + +- 文件传输:FTP允许用户在不同的计算机之间传输文件,用户可以从一个计算机(服务器)下载文件到本地计算机(客户端),也可以将文件从本地上传到服务器,这使得文件共享和传输变得非常方便。 + +- 文件管理:FTP提供了一系列的命令和操作,使用户能够对远程服务器上的文件进行管理。用户可以列出目录中的文件和子目录,创建新目录,删除目录和文件,重命名文件等。通过这些操作,用户可以对远程文件进行组织、备份和维护。 + +## 工作模式 + +FTP 有两种常见的工作模式: + +### 主动模式(Active Mode) + +在主动模式下,客户端通过控制连接告知服务器它将在哪个端口接收数据连接,并等待服务器的连接请求。服务器在数据连接上主动连接到客户端指定的端口,进行文件数据的传输。 + +![image-20230701162802597](../../media/network-comm/net-protocols/ftp/ftp_1.png) + +#### 主动模式工作过程 + +1. 客户端以随机非特权端口N,就是大于1024的端口,对server端21端口发起连接。 +2. 客户端开始监听 N+1端口。 +3. 服务端会主动以20端口连接到客户端的N+1端口。 + +#### 主动模式的优点 + +服务端配置简单,利于服务器安全管理,服务器只需要开放21端口。 + +#### 主动模式的缺点 + +如果客户端开启了防火墙,或客户端处于内网(NAT网关之后), 那么服务器对客户端端口发起的连接可能会失败。 + +### 被动模式(Passive Mode) + +在被动模式下,服务器通过控制连接告知客户端它将在哪个端口接受数据连接,客户端在数据连接上主动连接到服务器指定的端口,进行文件数据的传输。 + +![image-20230701162905210](../../media/network-comm/net-protocols/ftp/ftp_2.png) + +#### 被动模式工作过程 + +1. 客户端以随机非特权端口连接服务端的21端口。 +2. 服务端开启一个非特权端口为被动端口,并返回给客户端。 +3. 客户端以非特权端口+1的端口主动连接服务端的被动端口。 + +#### 被动模式缺点 + +服务器配置管理稍显复杂,不利于安全,服务器需要开放随机高位端口以便客户端可以连接,因此大多数FTP服务软件都可以手动配置被动端口的范围。 + +#### 被动模式的优点 + +对客户端网络环境没有要求。 + +## 通信模型 + +![image-20230701163157920](../../media/network-comm/net-protocols/ftp/ftp_3.png) + +FTP(File Transfer Protocol)通信模型基于客户端-服务器架构,其中客户端和服务器之间通过FTP协议进行通信和文件传输。在FTP通信模型中,客户端负责发起连接请求并发送各种FTP命令,而服务器则负责接收命令并执行相应的操作。 + +通信模型包括控制连接和数据连接两个关键要素,控制连接用于传输命令和服务器响应,客户端通过控制连接与服务器建立初始连接并发送FTP命令,服务器通过控制连接返回响应。数据连接用于实际的文件传输,当需要传输文件时,客户端和服务器之间会建立一个数据连接,用于在两者之间传输文件内容。通过这种模型,FTP实现了可靠的文件传输和管理,允许用户在不同计算机之间共享和传输文件,并对远程服务器上的文件进行操作和维护。 + +- **控制连接(Control Connection)**:控制连接是客户端和服务器之间的长期连接,用于传输命令、状态信息和控制信号。控制连接默认使用端口 21。 + +- **数据连接(Data Connection)**:数据连接是客户端和服务器之间进行实际文件数据传输的连接,数据连接的建立方式取决于工作模式(主动或被动模式)。 + +## FTP应用 + +QuecPython 提供了`ftplib`模块,用于FTP客户端连接使用,本节分为FTP服务端和客户端两个章节进行说明。 + +本章节将通过一个公网FTP服务端和使用QuecPython实现一个FTP客户端来进行演示,由于模组使用蜂窝数据网络只能访问公网服务器,本次演示默认有公网服务器,可以使用蜂窝数据网络直接访问,我们先通过下图初步了解下基于QuecPython完成FTP客户端的应用流程: + +![image-20230713142751410](../media/network-comm/mqtt/image-20230713142751410.png) + +上图是FTP客户端连接到服务端的应用流程,如下描述: + +1. 实例化FTP类函数,返回一个可操作的句柄,Python里面称之为对象,该对象拥有FTP所有的API方法,例如请求连接,登录服务端,上传下载文件等。 +2. 输入FTP服务端地址和端口信息,执行`ftp.connect`方法向服务端发起连接请求。 +3. 与服务端连接建立成功后需要登录FTP服务,使用`ftp.login`方法完成登录请求,这里需要用到用户名以及密码。 +4. 连接登录完成后,上传本地文件到服务端需要先从设备文件系统中打开并读取文件内容,然后通过`ftp.storlines`方法配合"STOP"命令完成上传请求。 +5. 本地客户端可以通过`ftp.retrlines`方法配合"RETR"命令从服务端下载指定文件到本地,请求下载后接收数据并保存至文件系统中。 + +### FTP客户端通信流程 + +1. 建立控制连接: 客户端通过建立TCP连接与FTP服务器建立控制连接。客户端使用服务器的IP地址和FTP端口号(通常是端口号21)来建立连接。 +2. 发起登录请求: 客户端发送用户凭证(用户名和密码)作为登录请求。这些凭证用于身份验证,以获取对服务器的访问权限。 +3. 接收服务器响应: 服务器接收到登录请求后,会进行身份验证,并发送相应的响应消息给客户端。响应消息包含一个响应码,指示登录是否成功或失败。 +4. 发送FTP命令: 客户端通过控制连接发送FTP命令给服务器,以执行各种操作。这些命令可以包括列出目录内容、改变工作目录、上传文件、下载文件、删除文件等。 +5. 接收服务器响应: 服务器接收到FTP命令后,执行相应的操作,并通过控制连接返回响应消息给客户端。响应消息包含一个响应码,指示命令执行的结果。 +6. 建立数据连接: 当需要进行文件传输时,客户端和服务器之间会建立一个数据连接。数据连接可以采用主动模式或被动模式,具体取决于服务器的配置。 +7. 进行文件传输: 在建立好的数据连接上,客户端可以通过数据连接上传或下载文件。客户端向服务器发送文件内容或请求获取文件内容,服务器通过数据连接进行传输。 +8. 关闭连接: 当文件传输完成或不再需要与服务器通信时,客户端可以发送QUIT命令来关闭控制连接,并释放相关资源。 + +### FTP客户端 + +模组端使用`ftplib`模块搭建FTP客户端,与服务端进行连接并完成文件上传和下载,需要注意的是要在 QuecPython 中使用 FTP 功能,您需要确保您的设备满足以下要求: + +1. 烧录 QuecPython固件:请根据您的模组型号找到对应的 QuecPython固件版本进行烧录。 +2. 连接到网络:确保您的设备能连接到网络。 +3. QuecPython固件中包含ftplib模块。 + +完成固件烧录后需要检测当前固件是否包含`ftplib`模块以及找网状态,可使用[Qpycom工具](https://python.quectel.com/doc/Advanced_development/zh/QuecPythonTools/QPYcom.html)进行调试,本文演示均使用该工具。在工具交互页面通过导入模块的方式来确认是否包含和使用API检测网络情况,python语法通过`import xxx`或`from xx import xxx`的方式导入API。 + +```python +# 未抛出异常则包含 +from ftplib import FTP +``` + +通过导入API`checkNet`来进行查询设备网络情况,状态值请查看[wiki](https://python.quectel.com/doc/API_reference/zh/iotlib/checkNet.html#%3Ccode%3EcheckNet.waitNetworkReady%3C/code%3E)。 + +```python +import checkNet +stage, state = checkNet.waitNetworkReady(30) +print(stage, state) # 3 1 +``` + +我们在确认设备网络正常的情况下使用`ftplib`API创建ftp对象,如下: + +```python +# 导入ftplib模块下的FTP类函数 +from ftplib import FTP + +# 创建ftp对象 +ftp_obj = FTP() +``` + +这样我们就成功创建了一个ftp客户端对象,接下来我们可以使用该对象进行后续操作。 + +创建ftp客户端对象后需要主动请求连接服务端,`connect`方法可以帮助我们完成这一步骤,使用`connect`方法需要server地址和端口两个参数: + +```python +# FTP服务端地址 +host = "" +# 默认端口为21 +port = 21 + +# 使用ftp对象发起连接请求 +ftp_obj.connect(host, port) +``` + +连接成功后进行登录操作,如果ftp服务端未设置或者为空时传入一个空字符串即可,使用`login`方法完成登录请求: + +```python +# FTP服务端用户名 +user = "xx" +# FTP服务端密码 +pwd = "xx" + +ftp_obj.login(user, pwd) +``` + +运行结果如图: + +![image-20230711142733722](../media/network-comm/mqtt/image-20230711142733722.png) + +连接与登录完成后我们就可以进行文件传输,在上传文件时需要注意待上传的文件必须是真实存在,需要根据文件路径来打开该文件,下述代码示例为文件上传: + +```python +path = 'usr/' # 模组用户分区的根目录为'usr' +filename = 'ftp_file.txt' + +# 判断文件是否存在 +if filename in uos.listdir("usr/"): + print("文件存在") + with open(path + filename, "rb") as fp: + # cmd应为STOR 命令。fp是一个文件对象 (以二进制模式打开),将使用它的 readline()方法读取它的每一行,用于提供要存的数据。 + res = ftp_obj.storlines("STOR " + filename, fp) + msg = "Upload %s to FTP Server %s." + if res.startswith("226 Transfer complete"): + print(msg % (filename, "success")) + return True + else: + print(msg % (filename, "falied")) + return False +else: + print("文件路径不存在") +``` + +运行结果: + +![image-20230711143921595](../media/network-comm/mqtt/image-20230711143921595.png) + +服务端查看上传文件: + +![image-20230711143650551](../media/network-comm/mqtt/image-20230711143650551.png) + +客户端请求下载服务端文件,我们以刚刚上传至服务端的"ftp_file.txt"文件为例来进行下载演示,ftp对象通过`retrlines`方法完成下载,首先在本地创建一个空文件并用open方法返回文件句柄,客户端与服务端下载链接建立连接后会将文件数据写入该文件中,如下: + +```python +path = 'usr/' # 模组用户分区的根目录为'usr' +filename = 'ftp_file.txt' # 待下载的文件名 + +# 打开并创建一个文件 +save_fp = open(path + filename, "wb+") +# 查询服务端当前路径 +server_path = ftp_obj.pwd() +# 建立下载连接,fp.write为写文件操作 +res = ftp_obj.retrlines("RETR " + server_path + "/" + filename, fp.write) +# 完成下载 +msg = "Down %s to device %s." +if res.startswith('226 Transfer complete'): + print(msg % (filename, "success")) + return True +else: + print(msg % (filename, "falied")) + return False +``` + +运行截图: + +![image-20230711145324194](../media/network-comm/mqtt/image-20230711145324194.png) + + + +本次演示我们通过QuecPython搭建的FTP客户端完成与服务端的交互和文件传输,最后给出完整的示例代码供参考。 + +示例代码如下: + +```python +import uos +from ftplib import FTP + + +class FtpManager(object): + """ + FTP Client + """ + def __init__(self): + self.ftp_obj = FTP() + + def connect(self, host, port): + """ + ftp connect + """ + connect_sta = self.ftp_obj.connect(host, port) + return connect_sta + + def login(self, username, password): + """ + login ftp server + """ + login_sta = self.ftp_obj.login(username, password) + return login_sta + + def check_file(self, filename): + """ + check file is usr + """ + # 判断文件是否存在 + if filename in uos.listdir("usr/"): + return True + return False + + def upload_file(self, path, filename): + """ + file_path + upload file to ftp server + """ + if not self.check_file(filename): + print("please check file") + return False + with open(path + filename, "rb") as fp: + # 以文本行模式存储文件。cmd应为恰当的 STOR 命令。fp是一个文件对象 (以二进制模式打开),将使用它的 readline()方法读取它的每一行,用于 提供要存的数据。 + res = self.ftp_obj.storlines("STOR " + filename, fp) + msg = "Upload %s to FTP Server %s." + if res.startswith("226 Transfer complete"): + print(msg % (filename, "success")) + return True + else: + print(msg % (filename, "falied")) + return False + + def download_file(self, path, filename): + """ + file_path + download file + """ + with open(path + filename, "wb+") as fp: + server_path = self.ftp_obj.pwd() + res = self.ftp_obj.retrlines("RETR " + server_path + "/" + filename, fp.write) + msg = "Down %s to device %s." + if res.startswith('226 Transfer complete'): + print(msg % (filename, "success")) + return True + else: + print(msg % (filename, "falied")) + return False +``` + +在启动模组端代码之前,需先检查网络状态,在确认网络正常的情况下,初始化`FtpManager`类函数启动客户端。 + +```python +import checkNet + +host = "" +port = 21 +user = "test" +passwd = "test" + +# 创建FTP连接对象 +ftp_cli_obj = FtpManager() + +stage, state = checkNet.waitNetworkReady(30) +if stage == 3 and state == 1: # 网络状态正常 + # 连接FTP服务器 + connect_info = ftp_cli_obj.connect(host, port) + print(connect_info) + # 登录FTP服务 + login_info = ftp_cli_obj.login(user, passwd) + print(login_info) + # 上传文件,输入本地文件路径 + upload_sta = ftp_cli_obj.upload_file("usr/", "ftp_file.txt") + print(upload_sta) +else: + print('Network connection failed, stage={}, state={}'.format(stage, state)) +``` + +### FTP客户端命令 + +以下是常用的FTP客户端命令: + +- CONNECT:建立与FTP服务器的连接。 +- USER:指定登录用户名。 +- PASS:指定登录密码。 +- LIST:列出服务器上当前目录的文件和子目录。 +- CWD:改变当前工作目录。 +- PWD:显示当前工作目录的路径。 +- RETR:从服务器下载文件到本地计算机。 +- STOR:将本地文件上传到服务器。 +- DELE:删除服务器上的文件。 +- MKD:在服务器上创建新的目录。 +- RMD:删除服务器上的目录。 +- RNFR:指定要重命名的文件或目录。 +- RNTO:指定重命名后的文件或目录名。 +- PASV:进入被动模式,用于数据传输。 +- TYPE:指定数据传输的类型,如ASCII或二进制。 +- SIZE:获取服务器上文件的大小信息。 +- SYST:获取服务器的操作系统类型。 +- NOOP:空操作,用于保持控制连接的活动状态。 +- QUIT:断开与服务器的连接。 + +这些命令用于在FTP客户端与服务器之间进行通信和执行不同的操作,包括登录认证、目录操作、文件传输、重命名、删除等。具体的命令语法和使用方式可能因FTP客户端的实现而有所不同。 + +### FTP服务端 + +基于Linux环境搭建FTP服务参考文档,[点击查看文档](https://www.geeksforgeeks.org/how-to-setup-and-configure-an-ftp-server-in-linux-2/#),如果没有公网FTP服务,请参考该文档或其他资料完成搭建。 + +FTP服务端提供了一种可靠和标准的方式来进行文件传输,广泛应用于文件共享、网站维护、备份等场景,与客户端的通信主要为以下几点: + +1. 连接建立:客户端与服务端通过TCP连接建立通信。客户端向服务端发送连接请求,并提供必要的身份验证凭据(如果需要)。 +2. 命令传输:客户端通过控制连接向服务端发送FTP命令,例如获取文件列表、上传文件、下载文件、创建目录等。 +3. 数据传输:根据命令的类型和传输模式,服务端与客户端建立数据连接并进行文件的实际传输。数据连接可以是主动模式(由服务端发起)或被动模式(由客户端发起)。 +4. 响应和状态码:服务端接收到客户端的命令后,执行相应的操作,并向客户端发送响应消息和状态码,以指示命令的执行结果或错误信息。 + +服务器端配置(以下配置默认为有公网ip的服务器): +安全组入方向映射:TCP/21端口 + +```python +# FTP服务器地址 +host = "" +# 端口 +port = 21 +# 用户名 +user = "" +# 密码 +passwd = "" +``` + +## 常见问题 + +**Q: 导入ftplib模块失败** + +A:首先确保正确烧录了QuecPython固件,且该固件包含ftplib模块,如不包含则需更换固件。 + +**Q:设备连接FTP服务器失败。** + +A:请检查是设备能够正常找网,在确保找网成功后检查FTP连接参数是否正确。 + +**Q:下载文件时空间不够怎么办?** + +A:下载指定文件前需确认默认空间足够,不然会导致下载失败。 + +**Q:下载文件提示文件550异常码** + +A:该异常码为在服务器上未找到对应的文件名,请检查服务端是否存在该文件。 \ No newline at end of file diff --git a/docs/Application_guide/zh/network-comm/net-protocols/http.md b/docs/Application_guide/zh/network-comm/net-protocols/http.md new file mode 100644 index 0000000000000000000000000000000000000000..f467bfe4519f5270cf2f23385f023b47b4e73fac --- /dev/null +++ b/docs/Application_guide/zh/network-comm/net-protocols/http.md @@ -0,0 +1,455 @@ +# HTTP通信 + +## HTTP 简介 + +### HTTP协议 + +HTTP(Hyper Text Transformer Protocol,超文本传输协议)是一种由客户端发起请求,通过 URL(Uniform Resource Locator,统一资源定位符)来访问服务器资源的一种通信协议。该协议基于 C/S 模型,以文本的形式组织请求和响应的报文。通信模型如下: + +![](../../media/network-comm/net-protocols/http/model.png) + +HTTP 协议的主要应用场景有:基于浏览器的网页获取与表单提交、文件上传与下载、移动应用、物联网设备的数据上报等。 + +### 请求和响应报文 + +#### 请求报文 + +HTTP 请求报文的格式如下: + +![](../../media/network-comm/net-protocols/http/request-format.gif) + +- `Request line`:请求行,由`method`、`URL`、`Version`字段组成。 + - `method`:请求方法,常用的方法有 GET、POST、PUT、HEAD、DELETE 等,详见[下文](#http_request_method)。 + - `URL`:统一资源定位符,用于标识服务器资源的路径。 + - `Version`:协议版本,目前常用的版本为`HTTP/1.1`。 +- `Header lines`:请求首部,由一个或多个头域组成,每个头域的格式为`header field name: value`。 + - `head field name`:头域的名称。 + - `value`:头域的值。 +- `Entity body`:消息体,即向服务器传递的消息正文。 + +> 值得注意的是,图中的`sp`表示空格,`cr` `lf`表示回车与换行,请求首部与消息体之间必须插入一行空行,即图示的`Blank line`。 + +请求报文的示例如下: + +![img](../../media/network-comm/net-protocols/http/HTTP_RequestMessageExample.png) + +#### 响应报文 + +HTTP 响应报文的格式如下: + +![](../../media/network-comm/net-protocols/http/response-format.gif) + +- `Status line`:响应状态行,由`version`、`status code`、`phrase`字段组成。 + - `version`:协议版本,目前常用的版本为`HTTP/1.1`。 + - `status code`:响应状态码,详见[下文](#http_status_code)。 + - `phrase`:原因短语。 +- `Header lines`:响应首部,由一个或多个头域组成,每个头域的格式为`header field name: value`。 + - `head field name`:头域的名称。 + - `value`:头域的值。 +- `Entity body`:消息体,即服务器响应的消息正文。 + +> 值得注意的是,图中的`sp`表示空格,`cr` `lf`表示回车与换行,响应首部与消息体之间必须插入一行空行,即图示的`Blank line`。 + +响应报文的示例如下: + +![img](../../media/network-comm/net-protocols/http/HTTP_ResponseMessageExample.png) + +## HTTP 请求方法 + +HTTP 常用的方法有 GET、POST、PUT、HEAD、DELETE 等。 + +- **GET**:HTTP 请求中最常用的方法,用于向服务器请求获取资源。 + +- **HEAD**:和 GET 方法一样,但是不返回报文实体主体部分。主要用于确认 URL 的有效性以及资源更新的日期时间等。 + +- **PUT**:用于向服务器上传资源,当资源不存在时则创建资源,否则会替换资源。 + +- **POST**:用于修改服务器资源,当资源不存在时则创建资源,否则会修改资源。 + +- **DELETE**:删除资源,与 PUT 功能相反。 + +## HTTP 状态码 + +服务器返回的响应报文中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。 + +| 状态码 | 类别 | 原因短语 | +| :----: | :------------------------------: | :------------------------: | +| 1XX | Informational(信息性状态码) | 接收的请求正在处理 | +| 2XX | Success(成功状态码) | 请求正常处理完毕 | +| 3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 | +| 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 | +| 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 | + +> - HTTP 协议的具体内容请参考国际互联网工程任务组的[官方文档](https://www.ietf.org/rfc/rfc2616.txt)。 +> +> - HTTP 协议与 TCP 协议的区别与联系: +> +> - HTTP 协议是基于 TCP 协议实现的。 +> +> - TCP 协议面向字节流,用户无法很好地确定业务数据的边界;而 HTTP 协议具有特定的报文格式,而且用户业务数据可通过`Content-Length: ${Length}`头域来指定用户数据长度,或通过`Transfer-Encoding:chunked`头域来指定用户数据编码格式,两种方式均可确定业务数据的边界。QuecPython 模组已同时支持对`Content-Length: ${Length}`和`Transfer-Encoding:chunked`两种方式的解析。 + +## HTTP 应用 + +### 连接网络 + +启动 HTTP 应用之前,首先要确保网络畅通。 + +而 QuecPython 相关模组在上电后会自动进行蜂窝网络连接。因此,一般情况下开发者只需在应用中检测网络状态即可,示例代码如下: + +```python +import checkNet + +if __name__ == '__main__': + stage, state = checkNet.waitNetworkReady(30) + if stage == 3 and state == 1: + print('Network connection successful.') + else: + print('Network connection failed, stage={}, state={}'.format(stage, state)) + +``` + +> 如果网络连接异常,可参考网络拨号相关的应用指导,重新进行网络连接。 + +### HTTP 请求代码示例 + +#### HTTP GET 请求 + +本节以 GET 和 POST 请求为例,展示了最基础的 HTTP 请求代码编写。 + +首先以 GET 请求为例,示例代码如下: + +```python +import request + +# 构建 HTTP GET 请求 +url = 'http://api.example.com/resource' + +# 设置请求头为 JSON, 不设置默认是application/json +headers = {'Content-Type': 'application/json'} + +# 发送 HTTP GET 请求 +response = request.get(url, headers=headers) + +# 获取 HTTP 响应状态 +status_code = response.status_code +print('GET status code:', status_code) + +# 获取 HTTP 响应数据 +data = response.json() +print('GET response data:', data) + +``` + +#### HTTP POST 请求 + +由于 GET 请求是向服务器请求资源,请求报文中一般无需携带消息体。 + +而 POST 请求则用于向服务器推送消息,需要携带消息体,示例代码如下: + +```python +import request +import ujson + +# 构建 HTTP POST 请求 +url = 'http://api.example.com/resource' + +# 设置请求头为 JSON +headers = {'Content-Type': 'application/json'} + +# 构建请求数据 +payload = {'key1': 'value1', 'key2': 'value2'} +json_payload = ujson.dumps(payload) + +# 发送 HTTP POST 请求 +response = request.post(url, headers=headers, data=json_payload) + +# 获取 HTTP 响应状态 +status_code = response.status_code +print('POST status code:', status_code) +# 获取 HTTP 响应数据 +data = response.json() +print('POST response data:', data) + +``` + +上述两段代码可以看出,GET 和 POST 请求的代码在编写时有两个主要区别: + +1. `request`模块后使用的方法不同,分别是`request.get()`和`request.post()`。 +2. POST 请求需要构建请求数据,而 GET 请求不需要。 + +其余编程的思路几乎一样,服务器响应后,HTTP 请求返回响应对象`response`。通过`response`提供的方法,可以获取 HTTP 的响应码、原因短语、响应消息体等数据。 + +### HTTP 请求常用接口 + +本节讲述下`request.get()`、`request.post()`和响应结果`response`对象常用方法的使用说明。 + +- `request.get()`函数原型:`request.get(url, data=None, **kwargs)` +- `request.post()`函数原型:`request.post(url, data=None, **kwargs)` +- `response`类型:`` +- 参数说明: + - `url`:位置参数,服务器资源地址。 + - `data`:默认参数,请求报文的消息体。 + - `**kwargs`:关键字参数,HTTP 请求其它的相关参数。 + - `headers`:请求头,字典类型。 + - `decode`:是否将响应结果进行 utf-8 解码,默认值为`True`。 + - `sizeof`:读取缓冲区的数据块大小,默认 255 个字节,建议 255-4096,数值越大读取的速度越快。 + - `ssl_params`:SSL 证书参数,格式为:`{"cert": certificate_content, "key": private_content}`。 +- 返回值:``类型的对象`response`。该响应对象的属性和方法如下: + - `status_code`:响应状态码,int 类型。 + - ``headers``:响应头,dict 类型。 + - `text`:响应消息体的字符串类型生成器。 + - `json`:将响应的 json 消息体转换为 dict 类型。 + - `content`:响应消息体的 bytes 类型生成器。 + +### HTTP 应用范例 + +#### 天气查询 + +高德开放天气查询接口:`http://restapi.amap.com/v3/weather/weatherInfo?key=2875b8171f67f3be3140c6779f12dcba&city=北京&extensions=base` + +> `北京`可替换为其他国内城市名称。 + +调用 HTTP GET 方法可查询指定城市的天气,示例代码如下: + +````` +import request + +# 天气查询URL +url = 'http://restapi.amap.com/v3/weather/weatherInfo?key=2875b8171f67f3be3140c6779f12dcba&city=北京&extensions=base' + +# 发送 HTTP GET 请求 +response = request.get(url) + +# 获取天气查询网站返回的原始数据 +data = '' +for i in response.text: + data += i +print('raw data responsed from website:') +print(data) +````` + +以上代码的执行结果为: + +```log +raw data responsed from website: +{"status":"1","count":"1","info":"OK","infocode":"10000","lives":[{"province":"北京","city":"北京市","adcode":"110000","weather":"阴","temperature":"36","winddirection":"东南","windpower":"≤3","humidity":"34","reporttime":"2023-07-02 18:40:39","temperature_float":"36.0","humidity_float":"34.0"}]} +``` + +想进一步处理该数据,比如美化输出,可以调用`response`的`json`方法: + +````` +import request + +# 天气查询URL +url = 'http://restapi.amap.com/v3/weather/weatherInfo?key=2875b8171f67f3be3140c6779f12dcba&city=北京&extensions=base' + +# 发送 HTTP GET 请求 +response = request.get(url) + +# 获取天气查询网站返回的原始数据 +data = response.json() +data = data['lives'][0] +for k,v in data.items(): + print('%s: %s' % (k, v)) +````` + +以上代码的执行结果为: + +```log +province: 北京 +city: 北京市 +adcode: 110000 +weather: 阴 +temperature: 36 +winddirection: 东南 +windpower: ≤3 +humidity: 34 +reporttime: 2023-07-02 18:40:39 +temperature_float: 36.0 +humidity_float: 34.0 +``` + +#### 文件下载 + +此处以下载百度搜索首页的 html 网页文件为例进行演示,发送请求的代码如下: + +```python +import request + +# http 协议的(非 https)百度URL +url = 'http://www.baidu.com' + +# 发送 HTTP GET 请求 +response = request.get(url) +``` + +在读取网页数据前,在模组的 /usr 目录下以`wb`的模式创建一个名为`baidu.html`的文件: + +```python +# 创建文件baidu.html +f = open('/usr/baidu.html', 'wb') # 'wb' 表示写入二进制数据 +``` + +网页文件可能会包含一些超出 ascii 范围的字符,在获取响应数据时,建议使用`request.content`,而不是`request.text`。 + +代码如下: + +```python +# 获取网页文件内容,并写入文件 +for i in response.content: + f.write(i) + +# 网页数据拉取完毕,关闭文件 +f.close() +``` + +至此文件下载完毕。 + +想要查看下载的文件内容,执行以下代码即可: + +```python +# 打开文件baidu.html +with open('/usr/baidu.html', 'rb') as f: # 'rb' 以二进制方式读取文件 + r = f.read() + while r: + print(r) + r = f.read() + +# 关闭文件 +f.close() +``` + +打印的文件内容如下: + +```python +b'\r\n \xe7\x99\xbe\xe5\xba\xa6\xe4\xb8\x80\xe4\xb8\x8b\xef\xbc\x8c\xe4\xbd\xa0\xe5\xb0\xb1\xe7\x9f\xa5\xe9\x81\x93
\r\n' +``` + +#### multipart/form-data 表单提交 + +HTTP 的`multipart/form-data`形式的表单提交在 HTTP 通信中很常用,可用于文件上传。需要在请求首部中增加`Content-Type: multipart/form-data;boundary=${Boundary}`头域,完整格式如下: + +```http +POST / HTTP/1.1 +host: www.example.com +Content-Type: multipart/form-data; boundary=${Boundary} + +--${Boundary} +Content-Disposition: form-data; name="text" + +title +--${Boundary} +Content-Disposition: form-data; name="file"; filename="test.png" +Content-Type: image/png + + +--${Boundary}-- +``` + +上述表单请求报文实现的是 test.png 文件的上传,同时增加了一个表单字段`text`,它的值为`title`。 + +报文格式说明如下: + +- `${Boundary}`:请求方自行设置的分隔字符串,比如`----WebKitFormBoundaryrGKCBY7qhFd3TrwA`。 + +- 请求的消息体由多个`--${Boundary}`分隔的表单项组成,每个表单项又由请求头和消息体组成,格式为: + + ```http + --${Boundary} + Content-Disposition: form-data; name="参数名" + + 参数值 + ``` + + - `--${Boundary}`后追加回车换行符`cr` `lf`。 + + - 每个请求头后也追加回车换行符`cr` `lf`。 + + - 请求头域消息体(即上述的参数值)之间也追加一行空行,即`cr` `lf`。 + +- 如果表单项中携带的是文件数据: + + - Content-Disposition 头域后需追加 filename 字段,如`Content-Disposition: form-data; name="file"; filename="test.png"`,表明表单携带的是文件内容,文件名称为 test.png。 + - 需追加 Content-Type 请求头,表明文件类型,如`Content-Type: image/png`。 + +- 在所有表单的最后,以`--${Boundary}--`结尾。 + +在 QuecPython 中实现此类型的表单,需要用户自行组织完整的表单格式消息体,示例代码如下: + +```python +import request + +url = 'http://www.example.com' +boundary = '----WebKitFormBoundaryrGKCBY7qhFd3TrwA' +headers = {'Content-Type': 'multipart/form-data; boundary=' + boundary} + +data = '' +data += '--' + boundary + '\r\n' +data += 'Content-Disposition: form-data; name="text"\r\n' +data += '\r\n' +data += 'title\r\n' +data += '--' + boundary + '\r\n' +data += 'Content-Disposition: form-data; name="file"; filename="test.png"\r\n' +data += 'Content-Type: image/png\r\n' +data += '\r\n' + +data = bytes(data.encode()) + +with open('/usr/test.png', 'rb') as f: + data += f.read() + +data += b'\r\n' +data += b'--' + bytes(boundary.encode()) + b'--' + +request.post(url, data=data) +``` + +#### application/x-www-form-urlencoded 表单提交 + +另一种表单形式` application/x-www-form-urlencoded`在 HTTP 通信中也很常用。 + +- 在请求首部中增加头域`Content-Type: application/x-www-form-urlencoded; charset=utf-8`,其中字符集编码格式按需取舍,一般网络数据传输均以 utf-8 编码,增加该字符集编码无副作用。 +- 消息体的格式为`key1=value1&key2=value2`。 + +可以看出该种形式的表单格式很简单,较为完整的报文如下: + +```http +POST / HTTP/1.1 +host: www.example.com +Content-Type: application/x-www-form-urlencoded; charset=utf-8 + +key1=value1&key2=value2 +``` + +#### json 表单提交 + +物联网设备使用 HTTP 协议上报数据时,最常见的就是以 json 格式进行上报。 + +[点此查看参考代码](#http_post_exam)。 + +### HTTP 常见问题 + +**1. 用 POST 方法向百度或其他服务器提交数据时,报如下错误怎么办?** + +```log +Traceback (most recent call last): + File "", line 1, in + File "request.py", line 272, in post + File "request.py", line 252, in request +NotImplementedError: Redirects not yet supported +``` + +​ QuecPython 的 HTTP 请求暂未支持 URL 重定向,请使用最终有效的 URL 进行数据提交。 + +**2. HTTP POST 的消息体格式与 Content-Type 头域的对应关系是怎样的?** + +| Content-Type 头域的值 | 消息体格式 | +| ------------------------------------------------ | :------------------------------------ | +| text/plain | 纯文本 | +| text/html; charset=utf-8 | html格式 | +| application/json | json格式 | +| application/xml | xml格式 | +| application/x-www-form-urlencoded; charset=utf-8 | key1=value1&key2=value2 | +| multipart/form-data; boundary=${Boundary} | [点此查看](#http_multipart_form_data) | +| image/jpeg | jpeg格式的图像数据 | +| audio/mpeg | mpeg格式的音频数据 | diff --git a/docs/Application_guide/zh/network-comm/net-protocols/mqtt.md b/docs/Application_guide/zh/network-comm/net-protocols/mqtt.md new file mode 100644 index 0000000000000000000000000000000000000000..4e0e45265b7e3033877126695de59843751539dd --- /dev/null +++ b/docs/Application_guide/zh/network-comm/net-protocols/mqtt.md @@ -0,0 +1,446 @@ +# MQTT 应用文档 + +本文档详细介绍了如何在 Quecpython 中使用 MQTT 协议进行通信,并提供了环境搭建、代码示例和结果演示。 + +## MQTT协议概述 + +MQTT是一种轻量级的消息传输协议,旨在在物联网(IoT)应用中实现设备间的可靠通信。它使用发布-订阅模式,其中包括一个MQTT服务端(代理或服务器)和多个MQTT客户端之间的通信。MQTT协议具有以下特点: + +- 轻量级:MQTT协议设计简单,协议头部开销小,适用于资源受限的设备和网络。 +- 低带宽消耗:MQTT采用二进制编码,有效地利用网络带宽。 +- 异步通信:客户端可以随时发布和订阅消息,无需等待对方的响应。 +- 发布-订阅模式:消息发布者将消息发布到特定的主题,而订阅者则订阅感兴趣的主题。这种模式支持松耦合的通信和灵活的消息传递。 + +## MQTT应用场景 + +MQTT在物联网、传感器网络、远程监测和控制、实时数据传输、消息推送和通知、车联网以及能源管理等众多领域都有重要作用。其轻量级、低带宽消耗和可靠的消息传递机制使得MQTT成为许多应用中的首选通信协议,下面列举一些常用的场景简介: + +![image-20230711185611691](../../media/network-comm/net-protocols/mqtt/image-20230711185611691.png) + +1. 物联网(IoT):MQTT是物联网应用中最常用的通信协议之一。它适用于连接大量设备的场景,提供可靠的消息传递和实时通信。MQTT的轻量级特性使其能够在资源受限的设备和网络环境下运行,同时支持发布-订阅模式和异步通信,使设备能够实时交换信息、监测和控制。 +2. 传感器网络:MQTT可用于传感器网络中的数据收集和实时监控。传感器可以发布数据到特定的主题,而订阅者可以订阅感兴趣的主题以接收传感器数据。MQTT的低带宽消耗和高效的消息传递机制使其成为传感器网络中可靠的数据传输选择。 +3. 远程监测和控制:MQTT使得远程监测和控制变得简单而可靠。通过MQTT,可以实时监测设备的状态、传感器数据,以及远程控制设备的操作。这在许多应用中都非常有用,如智能家居、智能城市、工业自动化等。 +4. 实时数据传输:MQTT的异步通信机制使其非常适用于实时数据传输场景。它能够以极低的延迟传输数据,使实时监控和通信成为可能。例如,在金融交易系统中,MQTT可用于实时传输市场行情数据给交易系统和投资者。 +5. 消息推送和通知:MQTT的发布-订阅模式使其成为消息推送和通知的理想选择。服务端可以发布消息到特定的主题,而订阅者将实时接收到这些消息。这在应用程序中的推送通知、聊天应用和实时数据更新等方面非常有用。 +6. 车联网(Connected Car):MQTT可用于车辆间的通信和车辆与云平台的通信。它可以支持车辆状态监测、车辆诊断和远程控制等功能,以提高车辆的安全性和效率。 +7. 能源管理:MQTT可用于能源监测和管理系统中的数据传输。通过MQTT,能源消耗数据可以实时传输给监测和分析系统,以便进行能源消耗优化和监控。 + +## MQTT通信机制 + +在MQTT中,通信是基于客户端与服务端之间的TCP连接完成的。客户端通过发送CONNECT消息发起连接请求,并在连接建立后发送相应的操作消息,如SUBSCRIBE(订阅)、PUBLISH(发布)、UNSUBSCRIBE(取消订阅)等。 + +发布者在向特定主题发布消息时,将消息内容和指定的主题发送给MQTT服务端,然后服务端将消息传递给订阅了该主题的所有订阅者。 + +订阅者通过发送SUBSCRIBE消息来订阅感兴趣的主题,其中指定了主题和所需的QoS级别。服务端接收到订阅请求后,会记录订阅者的订阅信息,并在有相关消息发布时将其传递给订阅者。 + +MQTT还支持保留消息(Retained Message),即发布者可以发布保留消息,并设置保留标志。保留消息将由服务端保留,以便在有订阅者订阅相关主题时发送给订阅者。这样,新的订阅者可以接收到最新的保留消息。 + +此外,MQTT还提供持久化会话(Persistent Session)的机制,持久化会话允许客户端断开连接后重新连接时保留其订阅和发布的状态信息,确保不丢失重要的消息。 + +通过这些机制,MQTT实现了可靠的消息传递、解耦和异步的实时通信,适用于物联网、传感器网络和实时数据传输等场景。它提供了灵活的通信模型和机制,使设备和应用程序能够高效地进行消息交互。 + +![image-20230701163911290](../../media/network-comm/net-protocols/mqtt/mqtt_1.png) + +- MQTT客户端:MQTT客户端是连接到MQTT服务端的设备或应用程序,每个客户端都具有唯一的客户端标识符(Client Identifier),用于在服务端中识别和区分不同的客户端。在QuecPython中我们通过`umqtt`实现MQTT客户端,通过传入初始化连接参数创建连接对象,[点击查看详情](https://python.quectel.com/doc/API_reference/zh/networklib/umqtt.html#%3Ccode%3Eumqtt.MQTTClient%3C/code%3E)。 + + ```python + from umqtt import MQTTClient + MQTTClient(client_id, server, port=0, user=None, password=None, keepalive=0, ssl=False, ssl_params={},reconn=True,version=4) + ``` + + **参数介绍:** + + - `client_id` - 客户端 ID,字符串类型,具有唯一性。 + - `server` - 服务端地址,字符串类型,可以是 IP 或者域名。 + - `port` - 服务器端口(可选),整数类型,默认为1883,请注意,MQTT over SSL/TLS的默认端口是8883。 + - `user` - (可选) 在服务器上注册的用户名,字符串类型。 + - `password` - (可选) 在服务器上注册的密码,字符串类型。 + - `keepalive` - (可选)客户端的keepalive超时值,整数类型,默认为0。 + - `ssl` - (可选)是否使能 SSL/TLS 支持,布尔值类型。 + - `ssl_params` - (可选)SSL/TLS 参数,字符串类型。 + - `reconn` - (可选)控制是否使用内部重连的标志,布尔值类型,默认开启为True。 + - `version` - (可选)选择使用mqtt版本,整数类型,version=3开启MQTTv3.1,默认version=4开启MQTTv3.1.1。 + +- MQTT服务端:MQTT服务端(也称为代理或服务器)负责接收和转发消息,管理订阅关系,并维护客户端的连接。它监听指定的端口,等待客户端的连接请求,并根据客户端的请求进行相应的处理,我们可以通过服务端主动下发主题消息,订阅该主题的客户端设备都会收到消息。 + +### 发布-订阅 + +- 发布者(Publisher):发布者是MQTT中的消息发送方,发布者将消息发布到特定的主题(Topic),并通过MQTT服务端将消息传递给订阅该主题的所有订阅者,基于`umqtt`创建客户端对象后可使用[publish](https://python.quectel.com/doc/API_reference/zh/networklib/umqtt.html#%3Ccode%3EMQTTClient.publish%3C/code%3E)方法进行消息发布: + + ```python + MQTTClient.publish(topic, msg, retain=False, qos=0) + ``` + + **参数描述:** + + - `topic` - mqtt 消息主题,字符串类型 + - `msg` - 需要发送的数据,字符串类型 + - `retain` - 布尔值类型,默认为False, 发布消息时把retain设置为true,即为保留信息。 + MQTT服务器会将最近收到的一条RETAIN标志位为True的消息保存在服务器端, 每当MQTT客户端连接到MQTT服务器并订阅了某个topic,如果该topic下有Retained消息,那么MQTT服务器会立即向客户端推送该条Retained消息。 + 特别注意:MQTT服务器只会为每一个Topic保存最近收到的一条RETAIN标志位为True的消息!也就是说,如果MQTT服务器上已经为某个Topic保存了一条Retained消息,当客户端再次发布一条新的Retained消息,那么服务器上原来的那条消息会被覆盖! | + - `qos` - 整数类型, MQTT消息服务质量(默认0,可选择0或1)0:发送者只发送一次消息,不进行重试 1:发送者最少发送一次消息,确保消息到达Broker + +- 订阅者(Subscriber):订阅者是MQTT中的消息接收方,订阅者可以订阅感兴趣的主题,以接收与该主题相关的消息。一旦订阅者订阅了某个主题,它将接收到该主题下的所有发布消息,项目中一般以不同的事件来定义Topic,设备订阅事件主题后即可接收到该主题的推送消息。 + + [点击查看示例API详情](https://python.quectel.com/doc/API_reference/zh/networklib/umqtt.html#%3Ccode%3EMQTTClient.subscribe%3C/code%3E) + + ```python + MQTTClient.subscribe(topic,qos) + ``` + + **参数描述:** + + - `topic` - mqtt topic主题,字符串类型。 + - `qos` - MQTT消息服务质量(默认0,可选择0或1),整数类型0:发送者只发送一次消息,不进行重试 1:发送者最少发送一次消息,确保消息到达Broker。 + +- 主题(Topic):主题是MQTT中消息发布和订阅的标识符,它可以是层次结构的,使用斜杠(/)分隔不同的层级,例如"Quectel/Python/temperature"。主题用于组织消息的传递,发布者将消息发布到特定的主题,而订阅者则订阅感兴趣的主题。 + +- 消息传递:MQTT服务端负责接收发布者发送的消息,并根据主题将消息传递给订阅了相应主题的订阅者。当发布者发布消息到某个主题时,所有订阅了该主题的订阅者将接收到该消息。 + + ![image-20230712145141895](../../media/network-comm/net-protocols/mqtt/image-20230712145141895.png) + +- 解耦和灵活性:发布-订阅模型使得发布者和订阅者之间解耦,它们不需要直接相互通信。发布者只需要将消息发布到特定的主题,而不需要知道谁将接收消息。同样,订阅者只需要订阅感兴趣的主题,而不需要知道消息来自哪个发布者。这种解耦和灵活性使得系统更加可扩展和灵活。 + +- 异步通信:在发布-订阅模型中,发布者和订阅者之间是异步通信的。发布者可以随时发布消息,而不需要等待订阅者的响应。订阅者可以接收到发布者发布的消息,并在需要时对消息进行处理。 + +### 服务质量(QoS)级别 + +MQTT定义了三个不同的QoS级别,用于控制消息传递的可靠性和保证。 + +- QoS 0(最多一次):消息发布者发布消息,只发送一次,没有确认机制。此级别的消息可能会丢失或重复传递,适用于不强调可靠性的场景。 +- QoS 1(最少一次):消息发布者发布消息,确保至少传递一次,但可能导致重复传递。使用发布和确认机制实现可靠传递,适用于需要至少一次传递保证的场景。 +- QoS 2(恰好一次):消息发布者发布消息,确保恰好传递一次,通过两次握手和四次握手确认机制实现。此级别提供了最高的传递可靠性,适用于对传递准确性要求很高的场景。 + +QuecPython中设置Qos等级,[详情点击查看](https://python.quectel.com/doc/API_reference/zh/networklib/umqtt.html#%3Ccode%3EMQTTClient.subscribe%3C/code%3E)。 + +```python +""" +当一个客户端以QoS 0订阅了一个主题,然后向该主题发布了QoS为1或2的消息时,该客户端将无法收到自己所发布的消息. +这是因为QoS级别是针对消息传递的可靠性而定义的。在MQTT中,消息的QoS级别是在发布时指定的,而订阅时指定的QoS级别仅适用于接收到的消息。 + +对于QoS 0,发布者发送消息后不会收到任何确认或响应,也不会对消息进行重传。这意味着即使发布者自己订阅了相同的主题,也无法保证接收到自己所发布的消息。 + +如果发布者希望确保接收到自己所发布的消息,应将发布消息的QoS级别设置为1或2,并在订阅时使用相应的QoS级别。这样,客户端将在发布消息时获得确认,并在QoS级别为1或2的情况下进行重传,以确保消息的可靠传递。 +""" +MQTTClient.subscribe("Quectel/Python/demo",1) +MQTTClient.publish("Quectel/Python/demo","Hello", qos=1) +``` + +### 遗嘱消息 + +MQTT允许客户端在连接时设置遗嘱消息(LWT),在建立连接的过程中,客户端可以设置遗嘱消息的相关参数,包括遗嘱消息的主题(Topic),遗嘱消息的内容和QoS级别。当服务端检测到客户端未在保活时间内上报心跳包,且未发送关闭连接请求,则认为客户端为异常断开,会根据客户端设置的遗嘱消息,将遗嘱消息发布到设置的遗嘱主题,这样其他订阅了该主题的订阅者就可以接收到该遗嘱消息,以得知该客户端的离线状态。 + +下面`set_last_will`为QuecPython设置遗嘱消息的API介绍,详情请[点击](https://python.quectel.com/doc/API_reference/zh/networklib/umqtt.html#%3Ccode%3EMQTTClient.set_last_will%3C/code%3E): + +```python +MQTTClient.set_last_will(topic,msg,retain=False,qos=0) +``` + +**参数描述:** + +- `topic` - mqtt遗嘱主题,字符串类型。 +- `msg` - 遗嘱的内容,字符串类型。 +- `retain` - retain = True boker会一直保留消息,默认False,布尔值类型。 +- `qos` - 整数类型,消息服务质量(0~1)。 + +### 持久化会话 + +MQTT支持持久化会话是一种机制,用于在客户端与服务端之间保持会话状态,即使客户端断开连接后重新连接,也能够保留订阅和发布的状态信息。MQTT服务端接收到客户端的连接请求后,会检查客户端标识符,如果客户端标识符是一个新的、未使用过的标识符,服务端将接受连接请求并为客户端分配一个新的会话,如果客户端标识符已经存在于服务端的会话列表中,服务端会继续使用现有的会话。也就是说客户端断开重新连接后,只要Client ID与上次连接一致,服务端将根据之前保存的会话状态,恢复该客户端的订阅信息,并将相关消息传递给客户端,这样客户端可以继续接收之前订阅的主题的消息。 + +### MQTT长连接 + +MQTT通过使用长连接(Long Connection)来保持客户端和服务端之间的持久通信。TCP保持活动机制和心跳保持机制用于保持连接的活跃状态,避免连接被中断,并检测连接的可用性,详细介绍如下: + +- 心跳保持机制:MQTT协议定义了心跳保持机制,用于在长连接期间维持客户端和服务端之间的活动状态。客户端可以定期发送PINGREQ消息给服务端,服务端会回复PINGRESP消息作为响应。通过定期的心跳保持,客户端和服务端可以互相确认连接的存活状态。 + +- 心跳间隔(Keep Alive):在建立MQTT连接时,客户端可以协商与服务端的心跳间隔(Keep Alive Interval)。心跳间隔是指客户端在该时间间隔内发送心跳请求,用于告知服务端连接的活跃性。服务端可以根据心跳间隔来检测客户端是否处于活动状态。 + +- 断开连接检测:如果服务端在一段时间内没有收到客户端的心跳请求或其他消息,它会认为客户端断开连接,并终止连接。同样地,客户端也可以检测到服务端的断开连接,并尝试重新连接。 + +MQTT初始化时配置Keep Alive不为0时默认开启保活机制,QuecPython会主动在心跳间隔时间内发送心跳包,使用的是`umqtt.ping()`方法,[点击查看详情](https://python.quectel.com/doc/API_reference/zh/networklib/umqtt.html#%3Ccode%3EMQTTClient.ping%3C/code%3E): + +```python +MQTTClient.ping() +``` + +## MQTT应用 + +QuecPython 提供了`umqtt`模块,用于MQTT协议的客户端连接。关于`umqtt`模块接口的用法,[点此查看](https://python.quectel.com/doc/API_reference/zh/networklib/umqtt.html)。 + +本章节将搭建两个MQTT客户端进行演示,为方便演示效果,客户端A在PC端使用MQTT.fx工具完成搭建并连接到服务端,客户端B使用QuecPython的`umqtt`搭建。当两个客户端连接到同一服务端后,相互向对方推送主题消息,经服务端将消息转发至对方设备。在开始介绍MQTT应用演示之前,我们先通过下图初步了解下基于QuecPython完成MQTT客户端的应用流程: + +![image-20230711162623192](../../media/network-comm/net-protocols/mqtt/image-20230711162623192.png) + +上图以两个客户端连接到同一个服务端为示例描述应用流程: + +1. 传入MQTT服务端连接参数给MQTT类函数进行实例化,返回一个可操作的句柄,Python里面称之为对象,该对象拥有mqtt所有的API方法,例如请求连接,发布订阅等。 +2. 实现一个回调函数,通过`set_callback`方法将该回调函数注册到创建的mqtt对象中,当服务端转发主题消息到客户端时通过注册的回调函数进行通知。 +3. 执行`connect`方法向服务端发起客户端连接请求。 +4. 客户端连接建立成功后通过mqtt对象的`subscribe`方法完成主题订阅,可订阅多个事件主题。 +5. 客户端主动推送主题消息,使用`publish`方法传入主题信息以及待发送消息至服务端,服务端会转发该主题消息至订阅者,即订阅该主题的所有客户端设备。 +6. 客户端开启消息监听,MQTT协议是基于TCP协议进行连接的,默认都是阻塞形式,所以我们通过开启一个线程任务的方式监听服务端下行数据,并通过回调的方式通知使用者。 + +### MQTT客户端流程介绍 + +整个MQTT应用流程基于发布-订阅模型,允许设备和应用程序之间以异步、解耦的方式进行通信。通过订阅不同的事件主题,客户端可以接收对应主题的消息,并通过发布消息将数据发送给服务端和其他订阅者,如下描述: + +1. 客户端连接建立: + - 客户端通过TCP连接向MQTT服务端发起连接请求。 + - 客户端提供唯一的客户端标识符(Client Identifier),以便在服务端中标识和区分不同的客户端。 + - 可选地,客户端可以提供用户名和密码进行身份验证,以确保连接的安全性。 +2. 订阅主题: + - 客户端可以向MQTT服务端发送订阅请求,指定要订阅的主题和所需的QoS级别(服务质量级别)。 + - 订阅者可以订阅一个或多个感兴趣的主题,以接收与这些主题相关的消息。 + - 服务端维护订阅关系,并在有消息发布到订阅的主题时将消息传递给订阅者。 +3. 发布消息: + - 客户端可以向MQTT服务端发布消息,指定要发布的主题和消息内容。 + - 消息发布者将消息发送给服务端,服务端接收到消息后根据主题转发给已订阅该主题的所有客户端。 + - 服务端可以根据消息的QoS级别,将确认消息发送给发布者,以确保消息的可靠传递。 +4. 消息传递: + - 当服务端接收到发布的消息后,根据消息的主题,查找所有已订阅该主题的客户端。 + - 服务端将消息转发给这些订阅的客户端,以便它们可以接收到相应的消息。 + - 消息传递可以根据订阅者的QoS级别进行处理,以确保消息的可靠性传递。 +5. 断开连接: + - 客户端可以随时向服务端发送断开连接请求,终止与服务端的通信。 + - 断开连接后,服务端将清理客户端的相关信息,并停止向其发送消息。 + +### MQTT客户端A + +本次演示我们在PC端使用MQTT.fx作为客户端A,填入所需的连接参数后进行服务端的连接。 + +参数填写如下图: + +![image-20230711104602875](../../media/network-comm/net-protocols/mqtt/image-20230711104602875.png) + +在成功连接上服务端后,客户端A订阅A主题,通过服务端向A主题发布消息,如下图: + +![image-20230711104709552](../../media/network-comm/net-protocols/mqtt/image-20230711104709552.png) + +### MQTT客户端B + +模组端使用QuecPython的`umqtt`模块搭建MQTT客户端B来连接服务端,并订阅主题B,配合客户端A完成消息推送与接收,需要注意的是我们要在 Quecpython 中使用 MQTT 功能,您需要确保您的设备满足以下要求: + +1. **烧录Quecpython 固件**:请根据您的模组型号,将 Quecpython 固件烧录到您的设备上。 +2. **连接到网络**:确保您的设备已正确连接到网络。 + +完成固件烧录后需要检测当前固件是否包含`umqtt`模块以及找网状态,可使用[Qpycom工具](https://python.quectel.com/doc/Advanced_development/zh/QuecPythonTools/QPYcom.html)进行调试,本文演示均使用该工具。在工具交互页面通过导入模块的方式来确认是否包含和使用API检测网络情况,python语法通过`import xxx`或`from xx import xxx`的方式导入API。 + +```python +# 未抛出异常则包含 +import umqtt +``` + +通过导入API`checkNet`来进行查询设备网络情况,状态值请查看[wiki](https://python.quectel.com/doc/API_reference/zh/iotlib/checkNet.html#%3Ccode%3EcheckNet.waitNetworkReady%3C/code%3E)。 + +```python +import checkNet +stage, state = checkNet.waitNetworkReady(30) +print(stage, state) # 3 1 +``` + +我们在确认设备网络正常的情况下使用`umqtt`API创建mqtt对象,如下: + +```python +# 导入umqtt模块下的MQTTClient类函数 +from umqtt import MQTTClient + +# 客户端唯一标识ID +client_id = "QuecPython_cli_2023" +# MQTT服务端地址 +server = "mq.tongxinmao.com" +# MQTT服务端端口 +port = 18830 +# 订阅主题 +sub_topic_b = "/public/TEST/QuecPython2023_B" +# 创建mqtt连接对象并返回 +mqtt_cli_obj = MQTTClient(client_id, server, port) +``` + +这样我们就成功创建了一个mqtt客户端对象,接下来我们可以使用该对象进行后续操作。 + +创建mqtt客户端对象后需要主动请求连接服务端,`connect`方法可以帮助我们完成这一步骤,使用`connect`方法需要server地址和端口两个参数,但我们通过mqtt对象来调用时不需要,因为我们在创建该对象时已经将这两个信息完成了传入,`connect`方法中可以直接获取到。 + +```python +# 成功返回0,失败则抛出异常 +mqtt_cli_obj.connect() +``` + +成功与服务端建立连接后尝试订阅一个主题,主题可以是自定义的,不过项目中一般以事件来划分主题,这里我们演示一个自定义主题,Qos等级为0: + +```python +mqtt_cli_obj.subscribe("/public/TEST/QuecPython2023_B", qos=0) +``` + +客户端B订阅主题成功后就要开始监听服务端的推送消息了,需要开启消息监听任务,该方法是通过`umqtt.wait_msg()`方法封装而来,因该方法是阻塞等待,所以通过线程的方式单独运行,我们可以在示例代码中找到`loop_forever`方法: + +```python +mqtt_cli_obj.loop_forever() +``` + +当前状态下如果服务端收到该主题的推送消息会直接转发给客户端,下面我们使用客户端A给`"/public/TEST/QuecPython2023_B"`这个主题发送消息来模拟,结果如下: + +PC(客户端A)向主题"/public/TEST/QuecPython2023_B"发送消息,客户端B订阅了该主题,经服务端转发后由回调通知到用户。 + +![image-20230711112722629](../../media/network-comm/net-protocols/mqtt/image-20230711112722629.png) + +上述演示了客户端订阅主题后成功接收到服务端推送的主题消息,在QuecPython中主动发布主题消息需要使用`publish`方法,如下: + +```python +mqtt_cli_obj.publish("/public/TEST/QuecPython2023_A", "Hello, PC", qos=0) +``` + +结果图示: + +![image-20230711113002143](../../media/network-comm/net-protocols/mqtt/image-20230711113002143.png) + +本次演示我们通过两个客户端完成了消息流转,以及MQTT客户端的应用流程,最后给出完整的示例代码供参考。 + +示例代码如下: + +```python +import _thread +from umqtt import MQTTClient + +class MqttClientManage(object): + """ + Quecpython client manage + """ + + def __init__(self, client_id, server, port, user=None, password=None, keepalive=60, ssl=False, ssl_params=None,reconn=True): + self.client_id = client_id + self.server = server + self.port = port + self.user = user + self.password = password + self.keepalive = keepalive + self.ssl = ssl + self.ssl_params = ssl_params + self.reconn = reconn + self.client = MQTTClient(self.client_id, self.server, self.port, self.user, self.password, + keepalive=self.keepalive, ssl=self.ssl, ssl_params=self.ssl_params, + reconn=reconn) + def connect(self): + ''' + 连接mqtt Server + ''' + return self.client.connect() + + def set_callback(self, sub_cb=None): + ''' + 设置mqtt回调消息函数 + ''' + if sub_cb is None: + sub_cb = self.mqtt_cli_cb + self.client.set_callback(sub_cb) + + def mqtt_cli_cb(self, topic, data): + print("QuecPython receive Topic={},Msg={}".format(topic.decode(), data.decode())) + + def subscribe(self, topic, qos=0): + ''' + 订阅Topic + ''' + return self.client.subscribe(topic, qos) + + def publish(self, topic, msg, qos=0): + ''' + 发布消息 + ''' + return self.client.publish(topic, msg, qos) + + def disconnect(self): + ''' + 关闭连接 + ''' + return self.client.disconnect() + + def __listen(self): + while True: + try: + self.client.wait_msg() + except Exception as err: + print("mqtt client listen error: " + str(err)) + + def loop_forever(self): + _thread.start_new_thread(self.__listen, ()) +``` + +在启动模组端代码之前,需先检查网络状态,在确认网络正常的情况下,初始化`MqttClientManage`类函数启动客户端。 + +```python +import checkNet + +client_id = "QuecPython_cli_2023" +server = "mq.tongxinmao.com" +port = 18830 +# 设备主题 +sub_topic_b = "/public/TEST/QuecPython2023_B" +# 创建mqtt连接对象 +mqtt_cli_obj = MqttClientManage(client_id, server, port) +# 注册消息回调 +mqtt_cli_obj.set_callback() + +stage, state = checkNet.waitNetworkReady(30) +if stage == 3 and state == 1: # 网络状态正常 + # 请求连接mqtt服务器 + connect_state = mqtt_cli_obj.connect() + print("mqtt connect state: ", connect_state) + # 订阅客户端A主题 + sub_state = mqtt_cli_obj.subscribe(sub_topic_b) + print("mqtt subscribe state: ", sub_state) + # 启动消息监听 + mqtt_cli_obj.loop_forever() +else: + print('Network connection failed, stage={}, state={}'.format(stage, state)) +``` + +将此代码上传至模组端后运行,成功连接至MQTT服务端,结果如下图: + +![image-20230711112042927](../../media/network-comm/net-protocols/mqtt/image-20230711112042927.png) + +### MQTT服务端 + +当服务端接收到发布的消息时,它将根据消息的主题,查找所有已订阅该主题的客户端。然后,服务端将消息转发给这些订阅的客户端,以便它们可以接收到相应的消息。消息的转发可以根据订阅者的QoS级别进行处理,以确保消息的可靠性传递。对于QoS级别为1和2的消息,服务端会发送确认消息给发布者,以确保消息已成功传递给订阅者。 + +此文档选择一个公共MQTT服务器进行演示,实际可使用自己搭建的MQTT服务以及其它支持MQTT协议的平台。 + +MQTT服务器连接信息准备: + +```python +# 公共MQTT服务器地址 +server = "mq.tongxinmao.com" +# 公共MQTT服务器端口 +port = 18830 + +# A客户端订阅topic信息 +sub_topic_a = "/public/TEST/QuecPython2023_A" +# B客户端订阅topic信息 +sub_topic_b = "/public/TEST/QuecPython2023_B" +``` + +## 常见问题 + +**Q: 连接MQTT服务器失败怎么排查原因?** + +A:首先排查设备网络状态,确认SIM卡或其他网络能正常访问远端;服务端地址以及是否需要证书认证,连接信息是否填写正确;服务端是否可以正常访问,可通过MQTT.fx工具尝试是否可以连接。 + +**Q:设备无法正常运行python代码,也无法交互。** + +A:请检查是否正确烧录QuecPython固件 + +**Q:订阅topic失败** + +A:检查该topic权限是否支持订阅 + +**Q:如果进行MQTTS加密连接?** + +A:在创建MQTT连接对象时将参数ssl置为True即可,该参数默认为False + +**Q: 为什么发布qos等级1的消息无法收到,topic已成功订阅** + +A:订阅topic时传入qos,与当前发布的消息等级保持一致 + +**Q:网络异常后如何进行异常重连?** + +A:umqtt模块内部会进行自动重连 + diff --git a/docs/Application_guide/zh/network-comm/net-protocols/tcp-udp.md b/docs/Application_guide/zh/network-comm/net-protocols/tcp-udp.md new file mode 100644 index 0000000000000000000000000000000000000000..c3b92a4688a0b149eb68692e3d22c8ab0fc81431 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/net-protocols/tcp-udp.md @@ -0,0 +1,870 @@ +# UDP/TCP 网络通信应用指导 + +## 概述 + +### IP 地址与域名 + +IP 地址是网络中的主机地址,用于两台网络主机能够互相找到彼此,这也是网络通信能够成功进行的基础。IP 地址一般以点分十进制的字符串来表示,如`192.168.1.1`。 + +![](../../media/network-comm/net-protocols/tcp-udp/ip-address.png) + +我们日常访问的网站,其所在的服务器主机都有唯一的 IP 地址,网络中的主机不计其数,靠记 IP 地址的方式来区分不同的主机显然比较困难,并且同一个网站可能有多个不同的 IP 地址,或者 IP 地址会因为某种原因而更换。 + +因此,用域名表示网站地址的方式便应运而生,如我们常见的`www.baidu.com`比 IP 地址更容易被记住。因为实际的网络通信报文中使用的仍然是 IP 地址,所以需要使用**域名解析**协议去获取域名背后所对应的 IP 地址。 + +> 下文的讲解均以 IPv4 协议为基础。 + +### OSI 七层模型 + +国际标准化组织(ISO)制定的一个用于计算机或通信系统的标准体系,一般被称为 OSI(Open System Interconnection)七层模型。它为网络通信协议的实现提供了一个标准,通信双方在相同的层使用相同的协议,即可进行通信;就同一台设备而言,下层协议为上层协议提供了调用接口,将上层协议打包为底层协议,最终发送到网络上进行传输。 + +这七层分别为:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。 + +![](../../media/network-comm/net-protocols/tcp-udp/osi-7-layers.png) + +为了简化协议实现或者方便理解,五层模型或者四层模型的概念也诞生了。四层模型一般被提及的比较多,包括:应用层、传输层、网络层、网络接口层。 + +![](../../media/network-comm/net-protocols/tcp-udp/tcpip-4-layers.png) + +上文中的 IP 地址则属于网络层。 + +> 网络层用于把该主机所有的网络数据转发到网卡,经由物理层电路发送到网络中去。 + +为了方便阐述,下文将按照四层模型来进行讲解。 + +### 传输层协议 + +IP 地址解决了网络中两台主机如何能够找到彼此,进而进行报文收发的问题。 + +试想下,一台主机上可能运行着多个应用程序,执行着不同的网络任务。这时,某台 IP 地址的主机收到了另一台主机的报文,这个报文数据要传递给哪个应用程序呢? + +为了解决这个问题,人们基于网络层协议演化出了传输层协议,传输层协议为本地的网络应用分配不同的端口。收到网络层的报文后,根据不同的端口号,将数据递交给不同的应用。 + +为了应对不同的场景,传输层协议分为 UDP 和 TCP 协议。 + +#### UDP 协议 + +UDP 协议具有以下特点: + +- 无连接 +- 支持一对一、一对多和多对多通信 +- 不保证可靠交付 +- 全双工通信 +- 面向报文 + +根据不同的需求,基于 UDP 衍生出了一些应用层协议,不同的应用会默认指定一个端口号。端口号亦可根据实际情况更换。 + +常见的基于 UDP 的应用协议及端口如下: + +| 熟知端口号 | 协议 | 说明 | +| ---------- | ---------- | ------------------- | +| 0 | -- | 保留 | +| 7 | echo | 报文回送服务器 | +| 53 | nameserver | 域名服务器 | +| 67 | bootps | BOOT 或 DHCP 服务器 | +| 68 | bootpc | BOOT 或 DHCP 客户端 | +| 69 | TFTP | 简单文件传输协议 | +| 123 | NTP | 网络时间协议 | +| 161 | SNMP | 简单网络管理协议 | + +#### TCP 协议 + +TCP 协议具有以下特点: + +- 面向连接 +- 每条连接只能有两个端点,即点对点 +- 提供可靠的数据交付 +- 全双工通信 +- 面向字节流 + +根据不同的需求,基于 TCP 衍生出了一些应用层协议,不同的应用会默认指定一个端口号。端口号亦可根据实际情况更换。 + +常见的基于 TCP 的应用协议及端口如下: + +| 熟知端口号 | 协议 | 说明 | +| ---------- | -------- | -------------------- | +| 0 | -- | 保留 | +| 7 | echo | 报文回送服务器 | +| 20 | FTP-DATA | 文件传输协议(数据) | +| 21 | FTP | 文件传输协议 | +| 23 | Telnet | 终端连接 | +| 25 | SMTP | 简单邮件传输协议 | +| 53 | DNS | 域名服务器 | +| 80 | HTTP | HTTP服务器 | +| 110 | POP3 | 邮局协议版本3 | +| 1080 | SOCKS | 代理服务器协议 | + +确定TCP连接的五元组:协议类型(TCP)、本地 IP、本地端口、远端 IP、远端端口。 + +## socket 编程 + +QuecPython 提供了`usocket`模块,用于网络通信的 socket 编程。关于`usocket`模块接口的用法,[点此查看](https://python.quectel.com/doc/API_reference/zh/stdlib/usocket.html)。 + +本节分为 TCP 网络编程、UDP 网络编程和多网卡网络编程三部分。 + +### TCP 网络编程 + +在开始 TCP 网络编程之前,我们先通过下图,初步了解下 TCP 服务器与客户端的 socket 编程模型: + +![](../../media/network-comm/net-protocols/tcp-udp/tcp-socket-model.png) + +#### TCP 客户端网络编程 + +上图的右侧是最简的 TCP 客户端编程的接口调用流程: + +1. 调用`socket()`接口创建 socket 对象。 + +2. 调用`connect()`接口连接服务器。 + +3. 调用`send()`接口向服务器发送数据。 + +4. 调用`recv()`接口接收服务器下发的数据。 + +5. 循环执行第 3 步和第 4 步,业务满足一定条件或连接断开,调用`close()`接口关闭 socket,释放资源。 + +几乎所有编程语言实现的 socket 接口,默认都是阻塞模式的,即所有涉及到网络报文收发的接口,如`connect()`、`send()`、`recv()`、`close()`等,默认都是阻塞式接口。 + +开始之前,我们先了解上图中 5 个与 TCP 客户端编程相关的接口的用法: + +- `usocket.socket(af=AF_INET, type=SOCK_STREAM, proto=IPPROTO_TCP)`:创建 socket 对象 + - `af`:地址族,取值为`usocket.AF_INET`和`usocket.AF_INET6`,分别表示 IPv4 地址与 IPv6 地址。 + - `type`:socket 类型,取值为`usocket.SOCK_STREAM`、`usocket.SOCK_DGRAM`、`usocket.SOCK_RAW`,分别表示 流套接字、数据报套接字与原始套接字。 + - `proto`:协议类型,取值为`usocket.IPPROTO_TCP`、`usocket.IPPROTO_UDP`、`IPPROTO_TCP_SER`等,分别表示 TCP 协议、 UDP 协议、TCP 服务器等。 +- `sock.connect(address)`:建立连接 + - `address`:包含 IP 地址字符串与端口号的元组或列表。 +- `sock.send(data)`:发送数据,返回实际发送的数据长度。 + - `data`:bytes 类型的数据 +- `sock.recv(size)`:接收数据,返回接收到的 bytes 类型的数据 + - `size`:读取数据的长度 +- `sock.close()`:关闭 socket。 + +> `IPPROTO_TCP_SER`是 QuecPython 特定的,非标的,在 QuecPython 中进行 TCP server 编程时,`socket()`接口的第三个参数必须是`IPPROTO_TCP_SER`。 + +我们可以基于此来实现一个最简单的 TCP 客户端功能:连接百度服务器,手动组织 HTTP 报文并发送给服务器,循环接收服务器下发的 html 页面内容,直至接收完毕。 + +代码示例如下: + +```python +import usocket + +def tcp_client(address, port): + # 创建socket对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.IPPROTO_TCP) + print('socket object created.') + + # 连接tcp服务器 + sock.connect((address, port)) + print('tcp link established: %s, %s' % (address, port)) + + # 打包用户数据 + data = 'GET / HTTP/1.1\r\n' + data += 'Host: ' + address + ':' + str(port) + '\r\n' + data += 'Connection: close\r\n' + data += '\r\n' + data = data.encode() + + # 发送数据 + sock.send(data) + print('<-- send data:') + print(data) + + #接收数据 + print('--> recv data:') + while True: + try: + data = sock.recv(1024) + print(data) + except: + # 数据接收完毕,连接断开 + print('tcp disconnected.') + sock.close() + break +``` + +上述示例中,我们组织的 HTTP 请求报文是: + +```http +GET / HTTP/1.1 +Host: : +Connection: close + +``` + +该报文中: + +- ``:回车换行。 +- ``:服务器的域名或 IP 地址。 +- ``:服务器的端口号。 + +> 需要注意的是,上述 HTTP 报文中添加了`Connection: close`头域,用来通知服务器这是一个短连接,当 HTTP 请求完成时,服务器会主动断开连接。 +> +> 这么做的好处是:当服务器主动断开连接时,网络协议栈底层会抛出异常,用户可以通过捕获该异常,得知客户端也需要断开连接了。 +> +> 这是为了快速实现 TCP 客户端测试代码的取巧做法。如果不增加`Connection: close`头域,对于 HTTP/1.1 协议来说,默认是长连接,服务器不会主动断开连接。此时就需要在客户端增加解析 HTTP 报文的代码,通过解析报文,判断数据被全部接收后,客户端才能断开连接。这会增加测试代码的复杂度,而且 HTTP 报文的解析也不在本文的阐述范围内。 + +请求报文组织完成后,通过`encode()`方法将字符串转换为 utf-8 编码的 bytes 类型,即上述代码中的`data = data.encode()`。而后调用`sock.send(data)`接口发送数据。 + +服务器下发的数据可能会很长,我们无法预知,因此需要在 while 循环中反复调用`sock.recv(1024)`接口读取数据,直到业务满足一定条件或连接异常,而后调用`sock.close()`关闭连接。 + +上文中说道,由于自行组织的 HTTP 报文中添加了`Connection: close`头域,服务器会主动断开连接,导致模组协议栈底层抛异常。所以在 while 循环中需要对`sock.recv(1024)`做`try`操作,当捕获到异常后,在`except`下断开连接,同时退出循环。 + +在启动客户端代码之前,需先检查网络状态,在确认网络正常的情况下,调用`tcp_client()`函数启动客户端。 + +```python +import checkNet + +if __name__ == '__main__': + stage, state = checkNet.waitNetworkReady(30) + if stage == 3 and state == 1: # 网络状态正常 + print('Network connection successful.') + tcp_client('36.152.44.95', 80) # 启动客户端功能 + else: + print('Network connection failed, stage={}, state={}'.format(stage, state)) +``` + +上述代码中,我们直接使用了百度服务器的 IP 地址`36.152.44.95`,运行结果如下: + +```log +Network connection successful. +socket object created. +tcp link established: 36.152.44.95, 80 +<-- send data: +b'GET / HTTP/1.1\r\nHost: 36.152.44.95:80\r\nConnection: close\r\n\r\n' +--> recv data: +b'HTTP/1.1 200 OK\r\nAccept-Ranges: bytes\r\nCache-Control: no-cache\r\nContent-Length: 9508\r\nContent-Security-Policy: frame-ancestors......Baidu " +tcp disconnected. +``` + +而一般情况下,用户更容易记住域名`www.baidu.com`。这时需要我们调用域名解析的接口,将域名转换为 IP 地址,而不是直接记住这个 IP 地址。 + +域名解析的函数原型为`usocket.getaddrinfo(host, port)`,说明如下: + +- 参数: + + - `host`:主机域名(IP 地址字符串亦可)。 + - `port`:端口号 + +- 返回值:`[(family, type, proto, canonname, sockaddr)]` + + - `family`:地址族。 + - `type` - socket 类型。 + + - `proto` - 协议类型。 + + - `canonname` - 主机域名。 + + - `sockaddr` - 包含 IP 地址和端口号的列表。 + +了解了此接口的用法后,`tcp_client()`函数实现中,直接以`sockaddr`作为`sock.connect()`函数的参数即可。 + +优化后的`tcp_client()`函数实现如下: + +```python +import usocket +import checkNet + +def tcp_client(address, port): + # 创建socket对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.IPPROTO_TCP) + print('socket object created.') + + # 域名解析 + sockaddr=usocket.getaddrinfo(address, port)[0][-1] + print('DNS for %s: %s' % (address, sockaddr[0])) + + # 连接tcp服务器 + sock.connect(sockaddr) + print('tcp link established.') + + # 打包用户数据 + data = 'GET / HTTP/1.1\r\n' + data += 'Host: ' + address + ':' + str(port) + '\r\n' + data += 'Connection: close\r\n' + data += '\r\n' + data = data.encode() + + # 发送数据 + sock.send(data) + print('<-- send data:') + print(data) + + #接收数据 + print('--> recv data:') + while True: + try: + data = sock.recv(1024) + print(data) + except: + # 数据接收完毕,连接断开 + print('tcp disconnected.') + sock.close() + break + +if __name__ == '__main__': + stage, state = checkNet.waitNetworkReady(30) + if stage == 3 and state == 1: # 网络状态正常 + print('Network connection successful.') + tcp_client('www.baidu.com', 80) # 启动客户端功能 + else: + print('Network connection failed, stage={}, state={}'.format(stage, state)) +``` + +优化的客户端代码,可以传递域名或者 IP 地址,更易使用。运行结果如下: + +```python +Network connection successful. +socket object created. +DNS for www.baidu.com: 36.152.44.95 +tcp link established. +<-- send data: +b'GET / HTTP/1.1\r\nHost: 36.152.44.95:80\r\nConnection: close\r\n\r\n' +--> recv data: +b'HTTP/1.1 200 OK\r\nAccept-Ranges: bytes\r\nCache-Control: no-cache\r\nContent-Length: 9508\r\nContent-Security-Policy: frame-ancestors......Baidu " +tcp disconnected. +``` + +#### TCP 服务端网络编程 + +对于蜂窝通信模组来说,一般情况下基站分配的 IP 地址是局域网地址,外部网络设备无法直接访问模组,这种情况下,模组做 TCP 服务器的意义不是很大。 + +但是对于专网卡来说,基站为其分配专网地址,并允许专网内的网络设备能够访问模组,此时模组是可以做 TCP 服务器的,这在电表类的产品中是比较常见的。 + +而且在Wi-Fi和以太网的网络中,是允许局域网设备访问该主机的。 + +所以我们仍有必要讲解下如何在模组中实现 TCP 服务器功能。 + +TCP 服务器与客户端的 socket 编程模型示意图的左侧展示了服务器编程的接口调用流程: + +1. 调用`socket()`接口创建 socket 对象。 + +2. 调用`bind()`接口绑定本地的地址和端口。 + +3. 调用`listen()`接口监听客户端连接请求。 + +4. 调用`accept()`接口接受客户端连接请求。 +5. 调用`recv()`接口接收客户端上行的数据。 +6. 调用`send()`接口向客户端发送数据。 +7. 每一个客户端连接中,循环执行第 5 步和第 6 步,业务满足一定条件或连接断开,调用`close()`接口关闭 socket,释放资源。 +8. 在接受客户端连接请求的线程中,循环执行第 4 步,以接受更多的客户端接入。 + +TCP 服务器编程调用的接口相比客户端,多了`bind()`、`listen()`、`accept()`三个接口,说明如下: + +- `socket.bind(address)`:绑定本地 IP 地址与端口。 + - `address`:包含 IP 地址字符串与端口号的元组或列表。因为服务器一般需要固定的 IP 地址与端口,如果不进行绑定,客户端将无从得知服务器的 IP 地址和端口号。 +- `listen(backlog)`:监听客户端连接请求。 + - `backlog`:除了正在处理的连接请求外,服务器允许后面排队等待处理的连接请求的个数。 +- `accept()`:接受客户端连接请求。 + +了解了流程和接口用法,我们这里做一个实验:在模组中分别编写一个 TCP 服务器程序和一个 TCP 客户端程序,客户端周期性向服务器发送数据,而后等待服务器回送数据。 + +TCP 服务器代码如下: + +```python +import usocket +import _thread + +def _client_conn_proc(conn, ip_addr, port): + while True: + try: + # 接收客户端发送的数据 + data = conn.recv(1024) + print('[server] [client addr: %s, %s] recv data:' % (ip_addr, port), data) + + # 向客户端回送数据 + conn.send(data) + except: + # 出现异常,连接断开 + print('[server] [client addr: %s, %s] disconnected' % (ip_addr, port)) + conn.close() + break + +def tcp_server(address, port): + # 创建 socket 对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.IPPROTO_TCP_SER) + print('[server] socket object created.') + + # 绑定服务器 IP 地址和端口 + sock.bind((address, port)) + print('[server] bind address: %s, %s' % (address, port)) + + # 监听客户端连接请求 + sock.listen(10) + print('[server] started, listening ...') + + while True: + # 接受客户端连接请求 + cli_conn, cli_ip_addr, cli_port = sock.accept() + print('[server] accept a client: %s, %s' % (cli_ip_addr, cli_port)) + + # 每接入一个客户端连接,新建一个线程,即连接并行处理 + _thread.start_new_thread(_client_conn_proc, (cli_conn, cli_ip_addr, cli_port)) +``` + +> 该段代码中,服务器每接入一个客户端,则新建一个线程,专门处理该客户端连接的相关事宜,做到并发处理。 + +TCP 客户端代码如下: + +```python +import usocket +import utime + +def tcp_client(address, port): + # 创建socket对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.IPPROTO_TCP) + print('[client] socket object created.') + + # 连接tcp服务器 + print('[client] connecting: %s, %s' % (address, port)) + sock.connect((address, port)) + print('[client] connected.') + + data = b'1234567890' + while True: + try: + # 向服务器发送数据 + sock.send(data) + print('[client] send data:', data) + + # 读取服务器回发的数据 + data = sock.recv(1024) + print('[client] recv data:', data) + print('[client] -------------------------') + + # 延时1s + utime.sleep(1) + except: + # 数据接收完毕,连接断开 + print('[client] disconnected.') + sock.close() + break +``` + +主流程代码如下: + +```python +import checkNet +import _thread +import utime +import dataCall + +if __name__ == '__main__': + stage, state = checkNet.waitNetworkReady(30) + if stage == 3 and state == 1: # 网络状态正常 + print('[net] Network connection successful.') + + # 获取模组的 IP 地址 + server_addr = dataCall.getInfo(1, 0)[2][2] + server_port = 80 + + # 启动服务器线程,一直监听客户端连接请求 + _thread.start_new_thread(tcp_server, (server_addr, server_port)) + + # 延时一段时间,确保服务器启动成功 + print('sleep 3s to ensure that the server starts successfully.') + utime.sleep(3) + + # 启动客户端功能 + tcp_client(server_addr, server_port) + else: + print('[net] Network connection failed, stage={}, state={}'.format(stage, state)) +``` + +该段代码中调用了`dataCall.getInfo()`接口获取模组的 IP 地址,该接口的返回值格式为`(profileID, ipType, [state, reconnect, addr, priDNS, secDNS])`。说明如下: + +- `profileID`:PDP 上下文场景 ID。该术语比较晦涩,简单的理解就是:蜂窝通信模组可以和基站建立多路连接,每一路对应一个不同的 IP 地址。一般情况下,一路连接就足以满足需求。如果想要获取指定某一路的 IP 地址,该参数填入对应的 ID 即可。 +- `ipType`:IP 协议类型。取值范围为 0-2,分别表示 IPv4协议、IPv6协议、IPv4与IPv6协议共存。初学者可以仅关心 IPv4 即可。 +- `state`:与基站间的网络连接状态。0 表示未连接,1 表示连接。 +- `reconnect`:重连标志。保留未用。 +- `addr`:IP 地址。 +- `priDNS`:主 DNS 服务器地址。 +- `secDNS`:辅 DNS 服务器地址。 + +代码中的`server_addr = dataCall.getInfo(1, 0)[2][2]`表示获取第 1 路连接的 IPv4 协议的 IP 地址,将其作为本地服务器的 IP 地址。 + +> - 支持了 QuecPython 的蜂窝通信模组在上电后会自动进行第一路的蜂窝数据连接。 +> - [点此查看](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html)更多与蜂窝数据连接相关的接口用法。 + +该实验代码的运行结果如下: + +```log +[net] Network connection successful. +sleep 3s to ensure that the server starts successfully. +[server] socket object created. +[server] bind address: 10.104.189.115, 80 +[server] started, listening ... +[client] socket object created. +[client] connecting: 10.104.189.115, 80 +[client] connected. +[client] send data: b'1234567890' +[server] accept a client: 10.104.189.115, 55187 +[server] [client addr: 10.104.189.115, 55187] recv data: b'1234567890' +[client] recv data: b'1234567890' +[client] ------------------------- +[client] send data: b'1234567890' +[server] [client addr: 10.104.189.115, 55187] recv data: b'1234567890' +[client] recv data: b'1234567890' +[client] ------------------------- +... +``` + +### UDP网络编程 + +在开始 UDP 网络编程之前,我们先通过下图,初步了解下 UDP 服务器与客户端的 socket 编程模型: + +![](../../media/network-comm/net-protocols/tcp-udp/udp-socket-model.png) + +从图中可以看出,UDP 服务器也需要调用`bind()`接口,绑定本地的 IP 地址和端口号,这是作为服务器所必须的接口调用。 + +同时,UDP 编程在接口调用上也有与 TCP 编程不同之处: + +- `socket()`接口参数不同: + - TCP 编程时,第二个参数`type`为`usocket.SOCK_STREAM`,而 UDP 编程时,第二个参数`type`为`usocket.SOCK_DGRAM`。 + - TCP 编程时,第三个参数`proto`为`usocket.IPPROTO_TCP`或`usocket.IPPROTO_TCP_SER`,而 UDP 编程时,第三个参数`proto`为`usocket.IPPROTO_UDP`。 +- 由于 UDP 是无连接的,客户端无需调用`connect()`接口去连接服务器。 +- 数据发送方只要直接调用`sendto()`接口将数据发送出去即可。 +- 数据接收方调用`recvfrom()`接口接收数据。 + +>`sendto()`接口是否能真正将数据发送到目的地,视网络环境而定,如果无法找到目标 IP 地址对应的主机,则数据被丢弃。 + +接下来,我们做一个实验:在模组中分别编写一个 UDP 服务器程序和一个 UDP 客户端程序,客户端周期性向服务器发送数据,而后等待服务器回送数据。 + +有了前面 TCP 编程的经验,我们直接给出实验代码: + +```python +import usocket +import _thread +import utime +import checkNet +import dataCall + +def udp_server(address, port): + # 创建 socket 对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM, usocket.IPPROTO_UDP) + print('[server] socket object created.') + + # 绑定服务器 IP 地址和端口 + sock.bind((address, port)) + print('[server] bind address: %s, %s' % (address, port)) + + while True: + # 读取客户端数据 + data, sockaddr = sock.recvfrom(1024) + print('[server] [client addr: %s] recv data: %s' % (sockaddr, data)) + + # 向客户端回送数据 + sock.sendto(data, sockaddr) + +def udp_client(address, port): + # 创建socket对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM, usocket.IPPROTO_UDP) + print('[client] socket object created.') + + data = b'1234567890' + while True: + # 向服务器发送数据 + sock.sendto(data, (address, port)) + print('[client] send data:', data) + + # 读取服务器回发的数据 + data, sockaddr = sock.recvfrom(1024) + print('[client] [server addr: %s] recv data: %s' % (sockaddr, data)) + print('[client] -------------------------') + + # 延时1s + utime.sleep(1) + +if __name__ == '__main__': + stage, state = checkNet.waitNetworkReady(30) + if stage == 3 and state == 1: # 网络状态正常 + print('[net] Network connection successful.') + + # 获取模组的 IP 地址 + server_addr = dataCall.getInfo(1, 0)[2][2] + server_port = 80 + + # 启动服务器线程 + _thread.start_new_thread(udp_server, (server_addr, server_port)) + + # 延时一段时间,确保服务器启动成功 + print('sleep 3s to ensure that the server starts successfully.') + utime.sleep(3) + + # 启动客户端功能 + udp_client(server_addr, server_port) + else: + print('[net] Network connection failed, stage={}, state={}'.format(stage, state)) +``` + +运行结果如下: + +```log +[net] Network connection successful. +sleep 3s to ensure that the server starts successfully. +[server] socket object created. +[server] bind address: 10.110.90.159, 80 +[client] socket object created. +[client] send data: b'1234567890' +[server] [client addr: ('10.104.189.115', 62104)] recv data: b'1234567890' +[client] [server addr: ('10.104.189.115', 80)] recv data: b'1234567890' +[client] ------------------------- +[client] send data: b'1234567890' +[server] [client addr: ('10.104.189.115', 62104)] recv data: b'1234567890' +[client] [server addr: ('10.104.189.115', 80)] recv data: b'1234567890' +[client] ------------------------- +... +``` + +### 多网卡网络编程 + +多网卡,顾名思义,一台设备中有多个网卡。在进行网络通信时,如何选取指定的网卡呢?有两种方法: + +- 将指定网卡设置为默认网卡。指定了默认网卡了,前述 TCP 客户端编程的流程不需要变动。 +- 获取指定网卡的 IP 地址,在创建完成 socket 对象后,并且在连接服务器之前调用`socket.bind()`接口。 + +> - `socket.bind()`仅用于将当前网络连接与指定的 IP 地址及端口号绑定,不区分客户端或服务器。 +> - 端口号写 0 表示随机生成端口号,随机端口号在客户端编程中很常用。如果端口号非 0,即固定端口号,需要通过`socket.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1)`设置端口重用,否则将会连接失败。 + +在实际应用场景中,有时会进行多路蜂窝数据连接,有时会 4G 网卡、外接的以太网卡或 Wi-Fi 网卡并存,此两种方法要如何实现呢? + +#### 蜂窝数据网络 + +前文提到,模组上电后会自动进行第 1 路的蜂窝数据连接,并且将该路网卡设置为默认网卡。 + +如果调用`dataCall`相关接口进行了多路连接,则后面的连接可通过[`dataCall.getInfo()`](#dataCall_getInfo)接口获取模组蜂窝数据连接的 IP 地址。 + +> 目前暂未提供相关接口,将后来的蜂窝数据连接的网卡设置为默认网卡。 + +#### 以太网卡 + +##### 调用`socket.bind()`接口绑定 IP 地址 + +以太网卡提供`ipconfig()`方法,用以获取包含 IP 地址等信息的网卡属性,返回值格式为:`[(mac, hostname), (iptype, ip, subnet, gateway, primary_dns,secondary_dns)]`。 + +- `mac`:以太网mac地址。 +- `hostname`:网卡名称。 +- `iptype`:IP 协议类型。4 表示 IPv4,6 表示 IPv6。 +- `ip`:IP 地址。 +- `subnet`:子网掩码。 +- `gateway`:网关地址。 +- `primary_dns`:主 DNS 服务器地址。 +- `secondary_dns`:辅 DNS 服务器地址。 + +获取以太网卡 IP 地址并且绑定的代码示例如下: + +```python +import ethernet +import usocket + +# 创建以太网卡 +eth = ethernet.W5500(b'\x12\x34\x56\x78\x9a\xbc','','','',-1,38,36,37, 0) +print('W5500 ethernet nic created.') + +# 开启 DHCP 功能 +eth.dhcp() +print('DHCP enabled.') + +# 启动网卡 +eth.set_up() +print('Ethernet nic enabled.') + +# 网卡注册成功后,查看网络配置信息 +ip_conf = eth.ipconfig() +print('get ip_conf:', ip_conf) + +def tcp_client(address, port): + # 创建socket对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.IPPROTO_TCP) + print('socket object created.') + + # 创建完成socket对象以后,并且连接服务器之前 绑定以太网卡IP地址 + local_address = ip_conf[1][1] + sock.bind((local_address, 0)) + print('bind ethernet address: %s', local_address) + + # 域名解析 + sockaddr=usocket.getaddrinfo(address, port)[0][-1] + print('DNS for %s: %s' % (address, sockaddr[0])) + + # 连接tcp服务器 + sock.connect(sockaddr) + print('tcp link established.') + + # 更多代码请参考前面关于 TCP 客户端的代码示例 +``` + +##### 设置为默认网卡 + +以太网卡提供`set_default_NIC(ip)`方法,用来将指定 IP 地址的网卡设置为默认网卡。 + +示例代码如下: + +```python +import ethernet + +# 创建以太网卡 +eth = ethernet.W5500(b'\x12\x34\x56\x78\x9a\xbc','','','',-1,38,36,37, 0) +print('W5500 ethernet nic created.') + +# 开启 DHCP 功能 +eth.dhcp() +print('DHCP enabled.') + +# 网卡注册成功后,查看网络配置信息 +ip_conf = eth.ipconfig() +print('get ip_conf:', ip_conf) + +# 将以太网卡设置为默认网卡 +eth.set_default_NIC(ip_conf[1][1]) +print('W5500 is set as default nic.') + +# 启动网卡 +eth.set_up() +print('Ethernet nic enabled.') +``` + +> - [点此查看](https://python.quectel.com/doc/API_reference/zh/peripherals/ethernet.html)以太网相关的接口用法。 +> - 关于以太网卡更多的编程应用指导,请查看相关文档。 + +#### Wi-Fi 网卡 + +##### 调用`socket.bind()`接口绑定 IP 地址 + +Wi-Fi 网卡也提供了一个名为`ipconfig()`的方法,但是该方法的返回值与以太网稍有不同,格式为`(ip, subnet, gateway, mtu, primary_dns, secondary_dns)`。 + +Wi-Fi 网卡的`ipconfig()`方法说明如下: + +- `ip`:IP 地址。 +- `subnet`:子网掩码。 +- `gateway`:网关地址。 +- `mtu`:最大传输单元。 +- `primary_dns`:主 DNS 服务器地址。 +- `secondary_dns`:辅 DNS 服务器地址。 + +获取 Wi-Fi 网卡 IP 地址并且绑定的代码示例如下: + +```python +from usr.WLAN import ESP8266 +from machine import UART +import usocket + +# 创建Wi-Fi网卡 +wifi = ESP8266(UART.UART2, ESP8266.STA) +print('Wi-Fi nic created.') + +# 配置连接的SSID和password,并连接路由器 +ssid = 'ssid' +password = 'password' +wifi.station(ssid,password) +print('Wi-Fi connected:%s, %s.' % (ssid, password)) + +# 给Wi-Fi网卡配置DNS服务器地址 +wifi.set_dns('8.8.8.8', '114.114.114.114') +print('Wi-Fi DNS server configured.') + +# 查看网络配置信息 +ip_conf = wifi.ipconfig() +print('get ip_conf:', ip_conf) + +def tcp_client(address, port): + # 创建socket对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.IPPROTO_TCP) + print('socket object created.') + + # 创建完成socket对象以后,并且连接服务器之前 绑定Wi-Fi网卡IP地址 + local_address = ip_conf[0] + sock.bind((local_address, 0)) + print('bind ethernet address: %s', local_address) + + # 域名解析 + sockaddr=usocket.getaddrinfo(address, port)[0][-1] + print('DNS for %s: %s' % (address, sockaddr[0])) + + # 连接tcp服务器 + sock.connect(sockaddr) + print('tcp link established.') + + # 更多代码请参考前面关于 TCP 客户端的代码示例 +``` + +##### 设置为默认网卡 + +Wi-Fi 网卡也提供了`set_default_NIC(ip)`方法,用来将指定 IP 地址的网卡设置为默认网卡。 + +示例代码如下: + +```python +from usr.WLAN import ESP8266 +from machine import UART + +# 创建Wi-Fi网卡 +wifi = ESP8266(UART.UART2, ESP8266.STA) +print('Wi-Fi nic created.') + +# 配置连接的SSID和password,并连接路由器 +ssid = 'ssid' +password = 'password' +wifi.station(ssid,password) +print('Wi-Fi connected:%s, %s.' % (ssid, password)) + +# 给Wi-Fi网卡配置DNS服务器地址 +wifi.set_dns('8.8.8.8', '114.114.114.114') +print('Wi-Fi DNS server configured.') + +# 查看网络配置信息 +ip_conf = wifi.ipconfig() +print('get ip_conf:', ip_conf) + +# 将Wi-Fi网卡设置为默认网卡 +wifi.set_default_NIC(ip_conf[0]) +print('Wi-Fi is set as default nic.') +``` + +> - [点此查看 ](https://python.quectel.com/doc/API_reference/zh/wifilib/index.html)Wi-Fi 网卡相关的接口用法。 +> - 关于 Wi-Fi 网卡更多的编程应用指导,请查看相关文档。 + +## 常见问题 + +**1. 为什么连接服务器会失败?** + +- 服务器必须是公网地址(连接模组本地的 server 除外)。 +- 使用 PC上 的 TCP/UDP 测试工具客户端、或者 mqtt.fx,连接服务器确认一下是否可以连接成功,排除服务器故障。 +- 2G网络不要使用中国联通卡。 +- 检查下模块信号、网络注册、网络附着、PDP激活状态。 +- 检查下SIM卡是否欠费【4G模块有一种欠费表现:无法注册4G网络,可以注册2G网络】。 + +**2. TCP 有自动重连功能吗?** + +底层没有自动重连,重连机制在应用层处理。 + +**3. DNS 解析失败怎么排查原因?** + +检查 SIM 卡是否注网成功以及检查该地址的有效性再次进行尝试。 + +**4. 为什么 4G 模块专网卡连接服务器失败?** + +- 检查 APN 参数是否设置正确。 +- 如果有其他厂家的模块,对比测试下是否连接正常。 +- 如果无法百分百保证服务器配置没问题,最好在服务器端用 wireshark 抓包,或者在服务器上安装一个第三方工具,开启一个服务器端口来对比测试。 +- 用定向 IP 的物联网卡,需要把域名或 IP 加入白名单才能使用。 + +**5. 各系列模组最多可同时创建多少路 socket?** + +- EC200A 系列模组最多可同时创建 64 路 socket。 +- ECxxxG/ECxxxU 系列模组最多可同时创建 15 路 socket。 +- BG95 系列模组最多可同时创建 20 路 socket。 + +**6. 4G 模块可以同时作为服务器和客户端使用吗?** + +- 对于 IPv4 协议来说,一般不能作为服务器使用, 模块能获取的是运营商分配的内网 IP;对于专网卡,运营商分配的 IP 地址可以在专网内进行通信,因此可以作为服务器使用。 + +- 对于 IPv6 协议来说,其全局链路地址就是公网 IP 地址,因为可以作为服务器使用。 + +- 模组内的客户端可以连接模组内的服务器,某些应用架构有需要,可以这么做。 + +**7. 为什么我一包数据只有不到 50B,一天消耗的流量要远远大于实际传输值** + +如果使用的是 TCP 协议,需要三次握手四次挥手才算完成了一次数据交互,原始数据不多但是由于 TCP 协议决定的一包数据必须要加包头包尾帧校验等,所以实际消耗的流量不止 50B,部分运营商有限制每一包数据必须 1KB 起发,不足 1KB 也会加各种校验凑足 1KB。 + +**8. 如果 KeepAlive 设置的时间长,会不会被基站断开?** + +会,一般建议使用 2 分钟,不建议超过 4 分钟,基站策略会关闭长时间没有数据传输的连接,太长时间可能会导致连接被基站关闭。 \ No newline at end of file diff --git a/docs/Application_guide/zh/network-comm/nic/README.md b/docs/Application_guide/zh/network-comm/nic/README.md new file mode 100644 index 0000000000000000000000000000000000000000..962e2c272c55ff4461324a0aa02ad7d63b04b747 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/README.md @@ -0,0 +1,24 @@ +# 网卡 + +网卡又称网络接口控制器,网络适配器,或局域网接收器,是一块被设计用来允许计算机在计算机网络上进行通讯的计算机硬件。这意味着一块独立设备,必须有网卡才能对外进行网络通信。 + +## 网卡的作用 + +网卡是局域网中连接计算机和传输介质的接口,不仅能实现与局域网传输介质之间物理连接和电信号匹配,还涉及帧的发送和接收、帧的封装和拆帧、介质的访问控制、数据的编码解码、数据缓存等。 + +## 网卡在 TCP/IP 模型中工作层次 + +网卡一般工作在 TCP/IP 模型的物理层和数据链路层。 +- 物理层定义了数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。 +- 数据链路层则提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层提供标准的数据接口等功能。 + +如下图所示,介绍网卡在 TCPIP 模型中的工作层次。 +![](../../media/network-comm/nic/network_tcpip_nic.png) + +## 网卡在 TCP/IP 模型中数据流转过程 + +在数据通信转发过程中,网卡主要处理 lwIP 协议栈(网络层)的数据,将 lwIP 协议栈(网络层)的数据交给网卡转发通过网线/电磁波发送到局域网,或者收到局域网数据转发给 lwIP 协议栈(网络层)。 +在网络通信中,网卡是主机与介质的桥梁设备,应用层协议提供了信息互通的基础。 + +如下图所示,介绍一次网络请求两台主机在 TCP/IP 模型中的传输过程。 +![](../../media/network-comm/nic/network_tcpip_flow.png) diff --git a/docs/Application_guide/zh/network-comm/nic/cellular/FAQ.md b/docs/Application_guide/zh/network-comm/nic/cellular/FAQ.md new file mode 100644 index 0000000000000000000000000000000000000000..a0f896d80a3d177f3fcc3a5a42d045a68c321a5c --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/cellular/FAQ.md @@ -0,0 +1,70 @@ +# 常见问题解答 + +**问题1:为什么要配置APN?** + +APN是终端入网时必须配置的一个参数,它决定了终端通过哪种接入方式来访问网络。所有运营商都有其特定的APN,通常运营商的基站都有APN自动纠错功能,即终端在入网时,如果没有配置APN或者配置了错误的APN,运营商的基站会自动纠正并下发一个正确的APN给终端,此时终端会选择接受并使用该APN继续入网。但是有些特殊情况,用户是必须主动配置APN,终端才能正常入网。下面列出了几种必须配置APN的情况。 + +* 有的运营商在一些区域的基站没有APN自动纠错功能,此时如果终端设备没有配置APN或者配置了错误的APN,会导致终端设备入网失败。我们没法提前知道某个基站是否具有APN自动纠错功能,因此建议用户不要依赖基站的APN自动纠错功能。 +* 对于一些特殊的SIM卡,比如专网卡,如果没有配置APN,会导致终端设备入网失败,在小区上发起附着请求(Attach Request)时就会被拒绝。这种特殊的SIM卡,必须要先配置APN。 +* 还有一类特殊的SIM卡,既可以连接公共网络(比如互联网),也可以连接专网。其连接不同网络的方式就是通过配置不同的APN来实现。 + +综上所述,我们建议用户,无论使用的是什么SIM卡,都主动去配置APN。 + + + +**问题2:如何知道使用的SIM卡的APN信息?** + +可以和运营商进行确认。 + + + +**问题3:为什么没有配置APN或者配置错误的APN也能激活成功?** + +因为有的运营商的基站有APN自动纠错功能,即终端在入网时,如果没有配置APN或者配置了错误的APN,运营商的基站会自动纠正并下发一个正确的APN给终端,此时终端会选择接受并使用该APN继续入网。 + +目前在中国境内,大部分地区的基站应该都有此功能,也有一些没有。由于我们无法提前确认哪些基站有这个功能,因此我们不建议用户依赖运营商基站的APN自动纠错功能,最好在使用时,主动配置正确的APN。 + + + +**问题4:SIM卡可以识别,但是无法注网是什么原因?** + +这种情况相对比较复杂,参考前面一个章节《网络异常处理》中的[模组网络注册失败]()部分,这里仅列出比较常见的几种原因: + +* 没接射频天线或者射频天线性能差。 +* 该SIM卡需要配置APN才能注册到网络,但是用户没有配置APN。 +* 该SIM卡存在机卡绑定的情况。需要用户联系运营商给该SIM卡解绑,或者更换新的SIM卡。 +* 测试地点没有该SIM卡对应的运营商网络覆盖。 +* 该SIM卡只支持特定频段网络。 +* 该SIM卡只支持特定的网络制式。 +* 设备硬件问题,比如硬件设计问题导致干扰大、模组射频相关器件损坏等。 + + + +**问题5:无线网卡激活成功,但是无法进行网络业务可能是什么原因?** + +一般有下面两种情况可能会导致这种问题: + +* SIM卡欠费了。有的SIM卡在欠费时,模组使用这张SIM卡也能成功注册到网络,并成功激活蜂窝无线网卡,但是运营商会限制模组进行网络通信。 + +* 用户这张SIM卡支持通过特定的APN接入到特殊的网络,并且用户配置的也是接入特殊网络的APN,此时设备接入的是特殊网络,并非互联网。用户可能在这种情况下进行了访问互联网的操作,那么就会出现访问失败的情况。 + + + +**问题6:使用蜂窝无线网卡的自动重连功能,模组正常运行过程中因为一些原因导致网络连接断开,后面自动重连成功后,Socket、HTTP和MQTT这些功能需要重新初始化吗?** + +首先我们需要知道,模组因为网络异常导致连接断开后,如果自动重连成功,那蜂窝无线网卡的IP地址已经发生了变化。此时Socket、HTTP和MQTT这些功能处理情况如下: + +* Socket + + 在创建soket对象时,系统会默认自动为其绑定蜂窝无线网卡的当前IP。当峰窝无线网卡的IP地址变化后,之前的socekt失效,需要用户先关闭之前socket,然后重新创建一个socket对象,才能正常使用。 + +* HTTP + + HTTP是基于socket实现的一种短连接服务,每一次调用HTTP的方法,其实都重新创建了一个socket对象。因此自动重连功能对HTTP功能没有影响,用户可直接使用。 + + > HTTP方法中创建的socket对象,在用户获取响应后(response.text & response.content & response.json),系统会自动关闭socket对象,如果用户没有获取响应,则需要用户主动调用接口`response.close`关闭连接,才可以关闭socket对象。 + +* MQTT + + QuecPython的MQTT具备自动重连功能,即当MQTT发现网络连接断开后,在模组的蜂窝无线网卡自动重连时,MQTT会自动重新初始化,并自动按照之前的订阅信息重新订阅相关Topic。因此在蜂窝无线网卡自动重连后,用户不需要重新初始化MQTT,可以继续正常使用。 + diff --git a/docs/Application_guide/zh/network-comm/nic/cellular/README.md b/docs/Application_guide/zh/network-comm/nic/cellular/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9f7bbed6dc310d7ffd404fe89e877ba7ac4e9127 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/cellular/README.md @@ -0,0 +1,56 @@ +# 蜂窝无线网络 + +蜂窝无线网络(Cellular Network),又称移动通信网络(mobile network),是一种广泛应用于移动通信的无线通信技术。它基于蜂窝拓扑结构,将服务区域分割为多个小区,每个小区都由一个无线基站负责覆盖,由于构成网络覆盖的各通信基站的信号覆盖呈六边形,从而使整个网络像一个蜂窝,所以叫它蜂窝无线网络。目前应用比较广泛的蜂窝网络类型有:GSM(2G)、UMTS(3G)、LTE(4G)和NR(5G)。 + + + +## 发展简史 + +在蜂窝无线通信的发展过程中,GSM、UMTS以及LTE等几个网络技术并不是唯一的移动通信技术,它们只是比较主流的移动通信技术,还有其他一些不太常见的移动通信技术。具体可以参考下面的移动通信技术演进路线图,它不仅说明了移动通信技术是如何从2G发展到4G,还说明了其他一些移动通信技术和当前主流通信技术的关系以及发展路线。 + +![移动通信发展历史](../../../media/network-comm/nic/cellular/移动通信发展历史.png) + + + +为了能更加简单明了的让读者理解,我们可以将移动通信技术的发展路线简化如下: + +![移动通信发展历史](../../../media/network-comm/nic/cellular/移动通信发展简史.png) + + + +* 1G - 模拟时代 + + 1G是指第一代移动通信技术,在1980年代初期首次推出。是以模拟技术为基础的蜂窝无线通话系统。主要提供语音通话功能,代表性的通讯手段就是大哥大。 + +* 2G - 数字时代 + + 2G是指第二代移动通信技术,在1990年代推出。开始采用数字信号技术,极大地提高了通话质量和网络容量。同时,2G还引入了一些新的服务,例如短信息服务(SMS)和低速数据传输。GSM是最广泛使用的2G标准。 + +* 3G - 宽带时代 + + 3G是指第三代移动通信技术,在21世纪初推出。3G提供了更高的数据传输速度,可以提供网页浏览、音乐等基本数据业务。UMTS是最早和最广泛使用的3G系统。 + +* 4G - 智能时代 + + 4G是指第四代移动通信技术,在2010年代初开始广泛使用,主要技术是LTE。相比3G,4G提供了更快的数据传输速度,实现了更高质量的音视频流,以及更快的移动互联网访问。 + +* 5G - 万物互联时代 + + 在2019年开始逐渐退出第五代移动通信技术,也是目前商业化应用的最新的移动通信技术。由于其更高的数据速度、更低的延迟,以及更高的网络容量,满足了万物互联的应用需求。 + + + +## 组网方式 + +蜂窝无线网络中,移动终端的组网方式,是通过无线电波和基站进行数据交互,基站将处理后的数据发送到运营商的核心网络,最后到达外部网络。 + +![LTE network architecture](../../../media/network-comm/nic/cellular/组网方式.png) + +以LTE网络为例,终端设备的组网方式如下: + +![LTE network architecture](../../../media/network-comm/nic/cellular/LTE_network_architecture.png) + + + + + diff --git a/docs/Application_guide/zh/network-comm/nic/cellular/api-instruction.md b/docs/Application_guide/zh/network-comm/nic/cellular/api-instruction.md new file mode 100644 index 0000000000000000000000000000000000000000..cc5f62728d24bd9343d058d518417252e0316e14 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/cellular/api-instruction.md @@ -0,0 +1,394 @@ +# 蜂窝网络API说明 + +本文会结合用户常用到的场景来说明需要使用哪些API,以及这些API的一些使用说明和注意事项。 + +## 设备工作模式 + +设备工作模式是指移动终端的功能模式,即我们常说的CFUN状态。QuecPython支持设置和查询模组的工作模式,相关方法的详细说明,请参考QuecPython官网wiki说明的[工作模式配置](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#%E5%B7%A5%E4%BD%9C%E6%A8%A1%E5%BC%8F%E9%85%8D%E7%BD%AE)。 + +### 查询模组工作模式 + +```python +net.getModemFun() +``` + +结合蜂窝网络基础概念介绍中的[CFUN]()部分说明,模组有3种工作模式,只有处于模式1(全功能模式)时,模组才能与蜂窝网络进行通信。 + +### 设置模组工作模式 + +``` +net.setModemFun(fun [, rst]) +``` + +第一个参数表示要设置的模式。第二个参数为可选参数,表示是否重启设备。一般我们都不需要填写第二个参数,即默认在设置工作模式时不重启设备。 + +### 应用场景说明 + +在遇到如下一些问题或者场景时,用户可以使用上述方法来查询和设置模组的工作模式: + +* 模组开机自动激活网卡失败,用户可以先查询模组当前工作模式是否为`1`,如果不是`1`,需要先设置为`1`。 + +* 用户希望模组在不需要进行网络业务时,能关闭模组的射频功能,以降低设备的功耗,此时可以将模组工作模式设置为0或者4。 + +* 模组当前网络通信质量不好,用户可以尝试先将模组工作模式设置为4(飞行模式),然后再设置为1(全功能模式),这样模组就会重新搜索网络并注册。 +* 在使用中,用户在不关机的情况下,进行了SIM卡插拔或者是换了一张卡,如果用户不想重启设备,也可以先将模组的工作模式设置为0,然后再设置为1。这样模组会重新进行SIM卡的初始化,然后重新搜索网络并注册。 + + + +## 设置网卡参数 + +网卡参数的设置,主要包括IP协议类型、APN、用户面、密码以及加密方式。QuecPython支持设置和查询蜂窝无线网卡的参数,相关方法的详细说明,请参考QuecPython官网wiki说明的[APN配置与获取功能](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html#APN%E9%85%8D%E7%BD%AE%E4%B8%8E%E8%8E%B7%E5%8F%96%E5%8A%9F%E8%83%BD)。 + +### 查询网卡参数 + +```python +dataCall.getPDPContext(profileID) +``` + +### 设置网卡参数 + +```python +dataCall.setPDPContext(profileID, ipType, apn, username, password, authType) +``` + +使用该接口设置网卡参数后,需要重启一次设备才能生效。另外,该接口设置的信息是掉电保存的。 + +### 应用场景说明 + +在遇到如下一些问题或者场景时,用户可以使用上述方法来查询和设置蜂窝无线网卡的参数: + +* 有的特殊的SIM卡需要配置APN后,网络注册才能成功。这种情况需要用户先和运营商确认这张卡应该使用什么APN以及用户面和密码,然后使用设置网卡参数的接口来将APN信息配置给蜂窝无线网卡,最后重启设备。 + +* 当用户需要模组同时激活多路蜂窝无线网卡,并且使用不同的网卡去连接访问不同的网络。比如第一路网卡用于访问公共网络,第二路网卡用于访问某个专用网络。此时用户需要使用`dataCall.setPDPContext`方法分别给第一路和第二路蜂窝无线网卡配置对应的APN信息。 + +关于如何配置APN,可以参考《场景使用说明》章节中的下面几个部分,这几部分都提供了详细的APN配置示例: + +* [开机自动激活一路网卡且配置APN]() + +* [开机自动激活多路网卡且配置APN]() + +* [手动激活一路网卡]() + +* [手动激活多路网卡]() + + + +## 激活/去激活网卡 + +QuecPython支持用户手动进行蜂窝无线网卡的激活与去激活,相关方法的详细说明,请参考QuecPython官网wiki说明的[激活与去激活功能](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html#%E6%BF%80%E6%B4%BB%E4%B8%8E%E5%8E%BB%E6%BF%80%E6%B4%BB%E5%8A%9F%E8%83%BD)。 + +### 激活网卡 + +```python +dataCall.activate(profileID) +``` + +### 去激活网卡 + +```python +dataCall.deactivate(profileID) +``` + +### 应用场景说明 + +QuecPython的模组默认开机都会自动激活蜂窝无线网卡,因此大部分情况下,用户是不需要手动进行蜂窝无线网卡的激活和去激活操作的。但是一些特殊场景或者用户的特殊需求,需要使用上述方法来手动激活或者去激活蜂窝无线网卡,比如: + +用户关闭了开机自动激活蜂窝无线网卡功能,由用户应用程序根据需要在某个时间进行网卡的激活和去激活操作。这种情况下,就需要用户在其应用程序中,根据需要来调用`dataCall.activate`和`dataCall.deactivate`方法。比如有的电表,平时不需要建立网络连接,只有在需要上报电表数据的时候,才会激活网卡,等数据上报完成,又会将网卡去激活,让网络连接断开。这样既节约了流量,也降低了设备的功耗。 + +关于如何手动激活无线网卡,可以参考《场景使用说明》章节中的下面几个部分,这几部分都提供了详细的APN配置示例: + +* [手动激活一路网卡]() + +* [手动激活多路网卡]() + + + +## DNS配置 + +模组在进行蜂窝无线网卡激活的时候,如果激活成功,核心网是会自动分配好DNS服务器地址给模组的。也就是说正常情况下,用户是不需要手动去配置DNS服务器地址的。但是有时候会遇到核心网分配的DNS服务器地址无法使用的情况,此时就需要用户手动去配置DNS服务器地址了。QuecPython支持用户手动进行DNS配置,相关方法的详细说明,请参考QuecPython官网wiki说明的[DNS配置功能](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html#DNS%E9%85%8D%E7%BD%AE%E5%8A%9F%E8%83%BD)。 + +### 配置DNS + +```python +dataCall.setDNSServer(profileID, simID, priDNS, secDNS) +``` + +### 应用场景说明 + +有时候会遇到核心网分配的DNS服务器地址无法使用的情况,此时需要用户手动去配置DNS服务器地址。 + + + +## 获取网卡状态信息 + +QuecPython支持网卡状态信息的查询,比如网卡激活状态、IP地址、DNS服务器地址等。相关方法的详细说明,请参考QuecPython官网wiki说明的[获取拨号信息功能](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html#%E8%8E%B7%E5%8F%96%E6%8B%A8%E5%8F%B7%E4%BF%A1%E6%81%AF%E5%8A%9F%E8%83%BD)。 + +### 获取网卡状态信息 + +```python +dataCall.getInfo(profileID, ipType) +``` + +### 应用场景说明 + +查询网卡状态信息这一操作,是用户必须进行的。不管是什么应用场景,只要用户需要进行网络业务操作,就必须先查询网卡激活状态,确认蜂窝无线网卡已经激活成功。具体而言,只要用户有如下需求,都需要使用`dataCall.getInfo`来查询: + +* 确认蜂窝无线网卡激活是否成功,通过`dataCall.getInfo`方法返回值的state来确定,为1表示激活成功。 + +* 用户使用QuecPython的socket功能时,需要知道模组当前的IP地址。 + +* 激活多路蜂窝无线网卡后,需要建立多路socket,不同的socket使用不同的网卡,此时需要获取每一路网卡的IP地址信息,然后与对应的socket进行绑定。 + +* 获取蜂窝无线网卡当前使用DNS地址。 + + + +## 设置网卡开机自动激活 + +QuecPython的模组默认开机都会自动激活第一路蜂窝无线网卡,并且使用的IP协议类型是IPv4,APN/用户面/密码默认都为空。用户可以通过API来配置激活任意一路或者多路蜂窝无线网卡开机自动激活。相关方法的详细说明,请参考QuecPython官网wiki说明的[开机自动拨号功能](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html#%E5%BC%80%E6%9C%BA%E8%87%AA%E5%8A%A8%E6%8B%A8%E5%8F%B7%E5%8A%9F%E8%83%BD)。 + +### 设置网卡是否开机自动激活 + +```python +dataCall.setAutoActivate(profileID, enable) +``` + +该方法设置结果是掉电保存的。设置后,需要重启才能生效。 + +### 应用场景说明 + +可能会有如下一些场景,用户需要使用上述方法来配置某一路网卡是否需要开机自动激活: + +* 用户希望关闭模组开机自动激活蜂窝无线网卡功能,此时需要使用`dataCall.setAutoActivate`来关闭。 + +* 用户希望开机自动激活多路蜂窝无线网卡,此时需要使用`dataCall.setAutoActivate`来分别设置需要激活的网卡。比如,用户希望开机自动激活第一路和第二路蜂窝无线网卡,则配置如下: + + ```python + dataCall.setAutoActivate(1, 1) # 使能第一路蜂窝无线网卡开机自动激活功能 + dataCall.setAutoActivate(2, 1) # 使能第二路蜂窝无线网卡开机自动激活功能 + ``` + + + +## 设置网卡自动重连 + +QuecPython的模组默认开机都会使能第一路蜂窝无线网卡的自动重连功能。用户可以通过API来配置任意一路或者多路蜂窝无线网卡的自动重连功能。相关方法的详细说明,请参考QuecPython官网wiki说明的[拨号自动重连功能](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html#%E6%8B%A8%E5%8F%B7%E9%87%8D%E8%BF%9E%E5%8A%9F%E8%83%BD)。 + +建议如果没有特殊需求,不要关闭网卡的自动重连功能。 + +### 设置网卡是否自动重连 + +```python +dataCall.setAutoConnect(profileID, enable) +``` + +该方法设置结果是掉电保存的。设置后,需要重启才能生效。 + +### 应用场景说明 + +如下一些场景,用户需要使用上述方法来配置某一路网卡是否需要自动重连: + +* 用户因为一些特殊需求,不需要网卡自动重连。用户可以使用`dataCall.setAutoConnect`方法来将这一路网卡的自动重连功能关闭。 + +* 用户使用`dataCall.setAutoActivate`方法设置了某一路或者多路蜂窝无线网卡开机自动激活,此时需要确认这一路或者多路蜂窝无线网卡是否需要自动重连功能,如果需要,则使用`dataCall.setAutoConnect`方法来使能这一路或者多路蜂窝无线网卡的自动重连功能;如果不需要,则使用`dataCall.setAutoConnect`方法来关闭这一路或者多路蜂窝无线网卡的自动重连功能。 + + + +## 网络事件监听 + +QuecPython提供了方法让用户监听网络状态变化事件。具体方案是让用户注册回调函数,当蜂窝无线网卡与网络的连接状态发生变化时,系统会通过用户注册的回调函数来通知当前网络连接状态。相关方法的详细说明,请参考QuecPython官网wiki说明的[回调注册功能](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html#%E5%9B%9E%E8%B0%83%E6%B3%A8%E5%86%8C%E5%8A%9F%E8%83%BD)。 + +### 注册回调函数 + +``` +dataCall.setCallback(fun) +``` + +### 应用场景说明 + +在实际使用中,因为一些原因(比如网络异常、环境干扰、信号差、SIM卡欠费等)可能会导致终端设备与网络之间的连接断开,此时终端设备将无法再继续进行网络相关业务。因此需要用户监听终端设备的网络状态,确保当网络连接断开和恢复正常时,能及时进行相应的处理。 + +关于网络事件监听具体如何使用,可以参考《网络异常处理》章节中的如下部分: + +* [网络异常事件处理示例]() + + + +## 获取信号强度 + +QuecPython提供了相关API用来获取信号强度和信号质量等参数。这些参数可以帮助用户确定当前设备所处环境的信号强度和信号质量。用户常用的几个参数有CSQ、RSSI、SINR、RSRP以及RSRQ。建议用户结合前面的蜂窝网络基础概念章节中的[信号质量]()部分一起看看。下面这些方法的详细说明,请请参考QuecPython官网wiki说明的[获取CSQ信号强度](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#%E8%8E%B7%E5%8F%96CSQ%E4%BF%A1%E5%8F%B7%E5%BC%BA%E5%BA%A6)和[获取详细信号强度](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#%E8%8E%B7%E5%8F%96%E8%AF%A6%E7%BB%86%E4%BF%A1%E5%8F%B7%E5%BC%BA%E5%BA%A6)。 + +### 查询信号强度与质量 + +查询CSQ使用如下方法: + +``` +net.csqQueryPoll() +``` + +查询RSSI、SINR以及RSRQ使用如下方法: + +``` +net.getSignal([sinrEnable]) +``` + +要注意: + +CSQ表示信号强度,是用来指示RSSI强度的参数,也就是说CSQ本质上和RSSI是同一个指标的两种表现形式。一般用户通过CSQ来看信号强度即可。取值范围为0 ~ 31,数值越大信号越好。 + +SINR是指接收到的有用信号的强度与接收到的干扰信号(噪声和干扰)的强度的比值,反映当前信道的链路质量。SINR的取值范围:0 ~ 30,比值越大越好。 + +RSRP反映了当前信道的路径损耗强度,用于小区覆盖的测量和小区选择/重选。即我们可以通过这个参数值来判断小区的覆盖情况。RSRP的取值范围:-44~-140dBm,值越大越好。 + +RSRQ表示当前信道质量的信噪比和干扰水平。RSRQ是随着网络负荷和干扰发生变化,网络负荷越大,干扰越大,RSRQ测量值越小。其取值范围是-19.5 dB ~ -3 dB,值越大越好。 + +### 应用场景说明 + +一般在如下一些问题或者场景中,用户需要查询一下信号强度和信号质量: + +* 模组网络注册失败时,用户需要使用`net.csqQueryPoll`来查询当前的CSQ信号强度,如果CSQ小于6,模组基本无法进行网络通信。 + +* 模组在网络通信过程中,网络连接经常断开,此时用户需要使用上述两个方法,将CSQ、SINR、RSRP以及RSRQ这些参数值都查询看看,而且最好能连续查看一段时间,以获取更多的数据样本。通过CSQ先看当前的信号强度如何,如果信号强度还可以,通过SINR的值确认当前的信号质量,以及结合RSRQ来判断当前通信中的干扰情况。同时也可以结合RSRP来看当前小区的信号覆盖情况综合判断。 + + + +## 获取小区信息 + +QuecPython提供了相关API用来获取小区的相关信息,包括当前服务小区和邻区。小区信息主要包括:小区类型(服务小区、邻区)、Cid、MCC/MNC、无线频道编号、物理小区标识号等。相关方法的详细说明,请参考QuecPython官网wiki说明的[获取小区信息](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#%E8%8E%B7%E5%8F%96%E5%B0%8F%E5%8C%BA%E4%BF%A1%E6%81%AF)。 + +### 查询小区小区 + +```python +net.getCellInfo() +``` + +当调用该方法查询小区信息时,模组会进行实时的扫描,该过程一般会花费几秒钟。另外,模组能扫描到哪些小区,和模组自身支持的BAND也有关系,如果有的小区的BAND模组不支持,模组是扫描不到这个小区的。 + +### 应用场景说明 + +需要查询小区信息的场景主要如下: + +* 用户使用基站定位功能时,需要获取周边小区信息。如果是使用QuecPython提供的基站定位功能,用户不需要直接获取小区信息,该操作已经由基站定位功能的代码自动完成。如果用户是自己去借助第三方平台实现基站定位功能,则需要用户使用`net.getCellInfo`方法来获取小区信息并上传相关服务器。 + +* 用户想知道当前环境中,有哪些小区,可以通过`net.getCellInfo`方法查询。 + + + +## 蜂窝网络制式 + +QuecPython支持的模组钟,有的模组仅支持LTE,有的支持GSM和LTE,也有的支持GSM、WCDMA和LTE。用户可以通过相关API来查询和配置模组的网络制式。相关方法的详细说明,请参考QuecPython官网wiki说明的[网络制式及漫游配置](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#%E7%BD%91%E7%BB%9C%E5%88%B6%E5%BC%8F%E5%8F%8A%E6%BC%AB%E6%B8%B8%E9%85%8D%E7%BD%AE)。 + +### 查询网络制式 + +```python +net.getConfig() +``` + +### 设置网络制式 + +```python +net.setConfig(mode [, roaming]) +``` + +该方法的第一个参数,就是用户需要设置的网络制式。第二个参数为是否开启漫游功能,可选参数,一般不需要设置,调用`net.setConfig`方法时,不填该参数即可。 + +通过QuecPython官网wiki说明的[网络制式及漫游配置](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#%E7%BD%91%E7%BB%9C%E5%88%B6%E5%BC%8F%E5%8F%8A%E6%BC%AB%E6%B8%B8%E9%85%8D%E7%BD%AE)章节中`net.getConfig`的返回值说明,可以看出,可以配置的网络制式比较多,有配置成单一的某一种网络制式,也有配置成几种网络制式组合模式的。这里有如下几点需要说明清楚: + +* `net.getConfig`的返回值说明中虽然列举了很多模式,但是并不是所有模组都支持这些模式。每一个型号的模组具体支持哪些网络制式,需要用户查阅对应模组的模块产品规格书来确定。 + +* 当用户配置网络制式为几种网络制式的组合模式时,比如用户配置的是GSM和LTE两种网络制式的组合。则模组开网络搜索时,就会有一个优先级,即先搜索哪种网络制式的小区,然后搜索哪种网络制式的小区。具体按照什么样的优先级顺序,取决于用户选择的是`net.getConfig`的返回值说明中网络制式表格中的哪一种。例如,用户配置GSM和LTE两种网络制式的组合时,选择的是网络制式表格中的模式8,则模组会优先搜索LTE网络的小区,如果没有搜到或者搜到了但是没有在LTE小区上附着成功,则模组会再去搜索GSM小区进行附着。 +* 如果模组支持多种网络制式,其默认配置一般都是其支持的网络制式的组合。 + +### 应用场景说明 + +一般如下一些场景中,用户需要设置模组的网络制式: + +* 如果模组支持多种网络制式,但是用户使用`net.getConfig`方法查询,发现模组当前被配置为仅支持某一种网络制式,用户希望将其配置为支持多种网络制式,则需要用户使用`net.setConfig`方法来重新配置。 + +* 模组支持多种网络制式,并且默认配置为多种网络制式的组合。但是用户使用的SIM卡仅支持其中一种网络制式,此时用户可以使用`net.setConfig`方法直接将模组配置为SIM卡支持的那一种网络制式。这样模组开机搜网时,就会直接在SIM卡支持的那种网络制式上搜索小区,可以加快模组网络注册的速度。 + +* 模组支持多种网络制式,并且默认配置为多种网络制式的组合。但是模组所处的环境中,其周边小区都仅支持一种网络制式,比如仅支持GSM。此时用户可以使用`net.setConfig`方法直接将模组配置为周边小区支持的那一种网络制式。 + + + +## 蜂窝网络技术 + +前面我们提到有的模组支持多种网络制式,那么当模组配置为多种网络制式组合的情况下,如果模组网络注册成功了,那么它到底注册的是哪一种网络呢?此时就涉及到网络技术了,通过查询模组当前网络技术就可以判断出当前接入的是哪一种网络制式。QuecPython提供了相关API用来查询模组当前的网络技术,相关方法的详细说明,请参考QuecPython官网wiki说明的[获取网络配置模式](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#%E8%8E%B7%E5%8F%96%E7%BD%91%E7%BB%9C%E9%85%8D%E7%BD%AE%E6%A8%A1%E5%BC%8F)。 + +### 查询网络技术 + +```python +net.getNetMode() +``` + +### 应用场景说明 + +主要应用场景就是,当模组支持多种网络制式,并且配置为多种网络制式组合的情况下,模组网络注册成功后,用于确认模组具体接入的是哪一种网络。 + + + +## 获取运营商信息 + +运营商信息是指模组当前接入的小区所属的运营商信息,包括运营商名称、MCC和MNC。QuecPython提供了相关API用来查询这些运营商信息,相关方法的详细说明,请参考QuecPython官网wiki说明的[获取运营商信息](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#%E8%8E%B7%E5%8F%96%E8%BF%90%E8%90%A5%E5%95%86%E4%BF%A1%E6%81%AF)。 + +### 查询运营商信息 + +```python +net.operatorName() +``` + +这里需要强调一件事,模组当前接入的小区所属的运营商,不一定就是模组当前使用的SIM卡所属的运营商。比如,如果模组是通过漫游的方式接入的某个小区,那么此时获取的就是这个小区所属的运营商,但是SIM卡却是其他运营商的。 + +### 应用场景说明 + +如上所述,该方法用来查询模组当前接入的小区所属的运营商信息。如果用户希望获取这方面信息,则可以使用`net.operatorName`方法来获取。 + + + +## 获取设备网络注册状态 + +设备的蜂窝无线网络注册状态是非常重要的参数。蜂窝无线网卡能否激活成功的前提,就是设备必须先注网成功。QuecPython提供了相关API用来查询设备的注网状态。相关方法的详细说明,请参考QuecPython官网wiki说明的[获取网络注册信息](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#%E8%8E%B7%E5%8F%96%E7%BD%91%E7%BB%9C%E6%B3%A8%E5%86%8C%E4%BF%A1%E6%81%AF)。 + +### 查询设备注网状态 + +```python +net.getState() +``` + +### 应用场景说明 + +正常情况下,只要模组网卡能激活成功,用户是不需要查询注网状态的。需要查询设备注网状态的场景主要如下: + +* 模组蜂窝无线网卡激活失败,需要依次排查SIM卡和注网状态。此时可以使用`net.getState`来查询注网状态,确认设备注网有没有成功。 + + + +## BAND设置 + +模组一般都支持多个BAND,并且默认所有支持的BAND都是开启状态。QuecPython提供了相关方法来查询和设置模组当前支持的BAND。相关方法的详细说明,请参考QuecPython官网wiki说明的[band设置与获取](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#band%E8%AE%BE%E7%BD%AE%E4%B8%8E%E8%8E%B7%E5%8F%96)。 + +> QuecPython中目前支持BAND查询和设置的API模组较少,仅BG95系列和EG912NENAA支持。 + +### BAND查询 + +```python +net.getBand(netRat) +``` + +### BAND设置 + +```python +net.setBand(netRat, gsmBand, bandTuple) +``` + +### 应用场景说明 + +一般如下场景中,用户会需要查询和设置BAND: + +* 用户想了解模组当前支持哪些BAND,此时用户可以通过`net.getState`来查询,也可以查阅模组的《模块产品规格书》。需要注意的是,模组的《模块产品规格书》中描述的是模组理论上支持的BAND;而`net.getState`方法查询的是模组实际配置的支持哪些BAND。 + +* 一些特殊的SIM卡仅支持特定的BAND,此时可以使用`net.setBand`方法来将模组锁定到特定的BAND。 + +* 模组所处的环境中有多个BAND,并且这些BAND模组都支持。但是有的BAND上信号质量较差,有的BAND上信号很好,用户可以通过`net.setBand`方法将模组锁定在这些信号很好的BAND上,这样模组就可以直接选择这些信号很好的BAND接入。 + +* 用户希望将模组锁定到某一个BAND,进行一些功能验证或者测试,可以使用`net.setBand`方法将模组锁定到指定的BAND上。 \ No newline at end of file diff --git a/docs/Application_guide/zh/network-comm/nic/cellular/application.md b/docs/Application_guide/zh/network-comm/nic/cellular/application.md new file mode 100644 index 0000000000000000000000000000000000000000..12def355769e41a166576900fa350b8e36eb8f28 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/cellular/application.md @@ -0,0 +1,683 @@ +# 场景使用说明 + +本文将根据用户的不同场景需求,来详细说明如何配置,以及在对应场景下,如何使用网卡进行网络通信,并给出示例代码供用户参考。 + + + +## 开机自动激活一路网卡且没有配置APN + +这种场景是指,用户没有为任何一路蜂窝无线网卡配置过APN,并且开机自动激活一路网卡。这里其实有2种情况: + +情况1:用户没有配置过任何一路蜂窝无线网卡开机自动激活。这种情况下,QuecPython开机时默认激活第一路蜂窝无线网卡。即下图中的NICn表示第一路蜂窝无线网卡NIC1。 + +情况2:用户通过`dataCall.setAutoActivate`方法配置了某一路蜂窝无线网卡开机自动激活。这种情况下,QuecPython开机时会激活用户指定的那一路蜂窝无线网卡。 + +不管是上面哪种情况,模组开机仅自动激活了一路蜂窝无线网卡。此时使用socket、http、mqtt等模块进行网络业务时,无需指定网卡,系统会自动选择已激活的那一路蜂窝无线网卡进行网络通信。 + +![](../../../media/network-comm/nic/cellular/无APN激活一路网卡.png) + +在这种场景下,用户应该按照下面的步骤来编写其应用代码: + +步骤1:使用checkNet等待网络就绪。 + +步骤2:进行网络通信。 + +下面以socket为例,说明这种场景下,如何编写代码进行网络通信,示例如下: + +```python +import checkNet +import usocket + + +def main(): + stage, state = checkNet.waitNetworkReady(20) + if stage == 3 and state == 1: + print('Network connected successfully.') + # 创建一个socket对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + # 解析域名 + try: + sockaddr = usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + except Exception: + print('Domain name resolution failed.') + sock.close() + return + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret = sock.send('GET /News HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send {} bytes'.format(ret)) + + # 接收服务端消息 + data = sock.recv(256) + print('recv {} bytes:'.format(len(data))) + print(data.decode()) + + # 关闭连接 + sock.close() + else: + print('Network connected failed, stage={}, state={}'.format(stage, state)) + + +if __name__ == '__main__': + main() +``` + + + +这种场景下,用户没有为任何一路蜂窝无线网卡配置过APN,模组开机后也能正常进行网络通信。但是会存在一个很大的问题:用户很可能会发现,按照上述示例编写的应用程序,在有的地方可以正常运行,有的地方不能正常运行,尤其是在中国以外的其他国家,大概率不能正常运行,具体表现就是`checkNet.waitNetworkReady`方法返回值不是`(3,1)`。原因是因为没有配置APN导致模组网络注册失败。 + +当用户没有为蜂窝无线网卡配置APN时,模组的蜂窝无线网卡能不能成功激活,取决于当前通信的基站是否有APN自动纠错功能。该功能是指当UE进行网络附着时,如果用户没有给无线网卡配置APN或者配置了错误的APN,基站会自动下发一个正确的APN给设备,并让UE附着成功。 + +上面的应用程序能正常运行的根本原因就在于此。目前中国境内,大部分运营商的基站是有APN自动纠错功能的,但是也有一些不支持。我们无法提前判断某一个基站是否支持这个功能,因此我们建议用户在使用模组时一定要配置APN。也就是下面我们要介绍的场景。 + + + +## 开机自动激活一路网卡且配置APN + +这种场景是指,用户根据使用的SIM卡,为某一路蜂窝无线网卡配置了正确的APN,并且开机自动激活的也是用户配置了APN的这一路蜂窝无线网卡。 + +这种情况下,模组使用socket、http、mqtt等模块进行网络业务时,无需指定网卡,系统会自动选择已激活的那一路蜂窝无线网卡进行网络通信。并且模组根据配置的APN不同,也可以访问不同的网络,如下图所示。需要说明的是,下图中APN1和APN2,并不是表示模组可以为某一路蜂窝无线网卡同时配置两个APN,而是表示为某一路蜂窝无线网卡配置不同的APN时,可以访问不同的网络。 + +![](../../../media/network-comm/nic/cellular/有APN激活一路网卡.png) + +在这种场景下,用户应该按照下面的步骤来编写其应用代码: + +步骤1:检查开机自动激活的那一路网卡的当前APN配置,确认是否需要配置APN。如果当前APN已经是用户需要配置的,则无需重复配置;否则需要重新配置,并重启设备。 + +步骤2:使用checkNet等待网络就绪。 + +步骤3:进行网络通信。 + +下面以开机自动激活第一路蜂窝无线网卡为例,说明这种情况下,如何编写代码,使用socket进行网络通信,示例如下: + +```python +import checkNet +import usocket +import dataCall +from misc import Power + +# 用户需要配置的APN信息,根据实际情况修改 +usrCfg = {'apn': '3gnet', 'username': '', 'password': ''} + + +def checkAPN(): + # 获取第一路网卡的APN信息,确认当前使用的是否是用户指定的APN + pdpCtx = dataCall.getPDPContext(1) + if pdpCtx != -1: + if pdpCtx[1] != usrCfg['apn']: + # 如果不是用户需要的APN,使用如下方式配置 + ret = dataCall.setPDPContext(1, 0, usrCfg['apn'], usrCfg['username'], usrCfg['password'], 0) + if ret == 0: + print('APN configuration successful. Ready to restart to make APN take effect.') + print('Please re-execute this program after restarting.') + # 重启后按照配置的信息进行拨号 + Power.powerRestart() + else: + print('APN configuration failed.') + return False + else: + print('The APN is correct and no configuration is required') + return True + else: + print('Failed to get PDP Context.') + return False + + +def main(): + checkpass = checkAPN() + if not checkpass: + return + + stage, state = checkNet.waitNetworkReady(20) + if stage == 3 and state == 1: + print('Network connected successfully.') + # 创建一个socket对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + # 解析域名 + try: + sockaddr = usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + except Exception: + print('Domain name resolution failed.') + sock.close() + return + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret = sock.send('GET /News HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send {} bytes'.format(ret)) + + # 接收服务端消息 + data = sock.recv(256) + print('recv {} bytes:'.format(len(data))) + print(data.decode()) + + # 关闭连接 + sock.close() + else: + print('Network connected failed, stage={}, state={}'.format(stage, state)) + + +if __name__ == '__main__': + main() +``` + + + +## 开机自动激活多路网卡且配置APN + +这种场景指的是,用户需要模组开机时自动激活多路蜂窝无线网卡,其应用程序可以通过不同的网卡来访问不同的网络,比如一路网卡用于访问公网,一路网卡用于访问专网。 + +这种场景中,用户需要配置开机自动激活多路蜂窝无线网卡,并且分别为这几路需要激活的网卡配置不同的APN。下图是开机自动激活第一路和第二路蜂窝无线网卡的示意图。我们以此图为例进行说明。 + +当用户配置开机自动激活第一路和第二路蜂窝无线网卡,并且为第一路蜂窝无线网卡配置的是可以访问公网的APN,为第二路蜂窝无线网卡配置的是只能访问某个专网的APN。在开机自动激活这两路蜂窝无线网卡后,情况如下: + +* http/https/mqtt这些功能默认只能使用第一路蜂窝无线网卡进行网络通信,用户无法指定使用哪一路蜂窝无线网卡。 + +* socket功能可以通过bind接口来绑定IP和端口号,决定使用哪一路蜂窝无线网卡进行网络通信。如果用户没有指定,则默认使用第一路蜂窝无线网卡。 + +![](../../../media/network-comm/nic/cellular/有APN激活多路网卡.png) + +> 在QuecPython中,当模组激活了多路蜂窝无线网卡,在使用socket功能进行网络通信时,如果用户没有指定使用哪一路蜂窝无线网卡,则系统会默认在激活的几路网卡中,选择使用网卡编号最小的那一路进行网络通信。比如模组激活了第一路和第二路网卡,则默认选择使用第一路;如果模组激活了第二路和第三路,则默认选择使用第二路。 + +这种场景下,用户的应用代码应按照下面的步骤来编写: + +步骤1:配置哪几路蜂窝无线网卡开机自动激活。 + +步骤2:分别为需要开机自动激活的蜂窝无线网卡配置APN。 + +步骤3:重启设备,使配置生效。 + +步骤4:使用checkNet等待网络就绪。 + +步骤5:进行网络通信。 + + + +下面提供一个例程,该例程中配置了第一路和第二路蜂窝无线网卡开机自动激活,并分别为第一路蜂窝无线网卡和第二路蜂窝无线网卡配置了不同的APN。然后分别创建了两个socket对象,socket1不指定网卡,即默认选择第一路网卡进行网络通信;socket2指定使用第二路蜂窝无线网卡与某个专网进行网络通信。示例代码如下: + +```python +import checkNet +import usocket +import dataCall +from misc import Power + + +# 用户需要配置的APN信息,根据实际情况修改 +usrCfg1 = {'profileID': 1, 'apn': '3gnet', 'username': '', 'password': ''} +usrCfg2 = {'profileID': 2, 'apn': '3gwap', 'username': '', 'password': ''} + + +def checkAPN(usrCfg, reboot=False): + if type(usrCfg) != dict: + print("Error: Input is not a dictionary.") + return False + + print('Check the APN configuration of the {} network card.'.format(usrCfg['profileID'])) + # 获取网卡的APN信息,确认当前使用的是否是用户指定的APN + pdpCtx = dataCall.getPDPContext(usrCfg['profileID']) + if pdpCtx != -1: + if pdpCtx[1] != usrCfg['apn']: + # 如果不是用户需要的APN,使用如下方式配置 + ret = dataCall.setPDPContext(usrCfg['profileID'], 0, usrCfg['apn'], usrCfg['username'], usrCfg['password'], 0) + if ret == 0: + print('APN configuration successful.') + # 重启后按照配置的信息进行拨号 + if reboot: + print('Ready to restart to make APN take effect.') + print('Please re-execute this program after restarting.') + Power.powerRestart() + else: + return True + else: + print('APN configuration failed.') + return False + else: + print('The APN is correct and no configuration is required') + return True + else: + print('Failed to get PDP Context.') + return False + + +def main(): + # 使能第一路网卡开机自动激活功能 + dataCall.setAutoActivate(1, 1) + # 使能第一路网卡自动重连功能 + dataCall.setAutoConnect(1, 1) + # 使能第二路网卡开机自动激活功能 + dataCall.setAutoActivate(2, 1) + # 使能第二路网卡自动重连功能 + dataCall.setAutoConnect(2, 1) + + # 检查第一路网卡的APN配置,暂时不重启 + checkpass = checkAPN(usrCfg1, reboot=False) + if not checkpass: + return + # 检查第二路网卡的APN配置,配置后重启 + checkpass = checkAPN(usrCfg2, reboot=True) + if not checkpass: + return + + stage, state = checkNet.waitNetworkReady(20) + if stage == 3 and state == 1: + print('Network connected successfully.') + # 分别获取第一路和第二路网卡的IP地址信息 + ret1 = dataCall.getInfo(usrCfg1['profileID'], 0) + ret2 = dataCall.getInfo(usrCfg2['profileID'], 0) + print('NIC{}:{}'.format(usrCfg1['profileID'], ret1)) + print('NIC{}:{}'.format(usrCfg2['profileID'], ret2)) + ip_nic1 = None + ip_nic2 = None + if ret1 == -1 or ret1[2][2] == '0.0.0.0': + print("Error: Failed to get the IP of the NIC{}.".format(usrCfg1['profileID'])) + return + else: + ip_nic1 = ret1[2][2] + if ret2 == -1 or ret2[2][2] == '0.0.0.0': + print("Error: Failed to get the IP of the NIC{}.".format(usrCfg2['profileID'])) + return + else: + ip_nic2 = ret2[2][2] + print('NIC{} IP:{}'.format(usrCfg1['profileID'], ip_nic1)) + print('NIC{} ip:{}'.format(usrCfg2['profileID'], ip_nic2)) + + print('---------------sock1 test-----------------') + # 创建socket对象 + sock1 = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + # 解析域名 + try: + sockaddr = usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + except Exception: + print('Domain name resolution failed.') + sock1.close() + return + # 建立连接 + sock1.connect(sockaddr) + # 向服务端发送消息 + ret = sock1.send('GET /News HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send {} bytes'.format(ret)) + # 接收服务端消息 + data = sock1.recv(256) + print('recv {} bytes:'.format(len(data))) + print(data.decode()) + # 关闭连接 + sock1.close() + print('---------------sock2 test-----------------') + sock2 = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + sock2.bind((ip_nic2, 0)) + sock2.settimeout(10) + # 服务器IP和端口,下面的IP和端口仅作示例参考 + server_addr = ('220.180.239.212', 8305) + # 建立连接 + sock2.connect(server_addr) + # 向服务器发送消息 + ret = sock2.send('test data.') + print('send {} bytes'.format(ret)) + # 接收服务端消息 + try: + data = sock2.recv(256) + print('recv {} bytes:'.format(len(data))) + print(data.decode()) + except Exception: + print('No reply from server.') + # 关闭连接 + sock2.close() + else: + print('Network connected failed, stage={}, state={}'.format(stage, state)) + + +if __name__ == '__main__': + main() +``` + + + +## 手动激活一路网卡 + +手动激活蜂窝无线网卡的场景一般比较少,主要是客户因为一些特殊的需求,不需要开机时自动激活蜂窝无线网卡,而是在需要的时候,由用户应用程序来主动进行网卡激活操作。需要说明的是,用户手动进行网卡激活和模组开机自动进行网卡激活,两者没有本质上的区别,主要区别在于激活的时间点。在激活成功后,使用网卡进行网络通信上,这两种激活方式是完全一样的。 + +我们先来说一下,手动激活一路网卡的场景。这种场景下,当用户手动激活蜂窝无线网卡后,使用网卡进行网络通信时,其过程示意如下图所示: + +![](../../../media/network-comm/nic/cellular/有APN激活一路网卡.png) + +可以看到,这种场景下,用户使用网卡进行网络通信的过程和“开机自动激活一路网卡且配置APN”的场景中是完全一样的。这种场景下,用户应按照如下步骤来编写代码: + +步骤1:关闭开机自动激活网卡功能。该配置重启生效。 + +步骤2:为需要手动激活的网卡配置APN。该配置重启生效。 + +步骤3:重启设备,使配置生效。 + +步骤4:在需要的时候,手动进行蜂窝无线网卡的激活。 + +步骤5:确认是否激活成功。 + +步骤6:进行网络通信。 + +步骤7:根据需要决定在通信结束后,是否需要对网卡进行去激活。 + +下面以手动激活第一路蜂窝无线网卡为例,说明这种情况下,如何编写代码,以及使用socket进行网络通信,示例如下: + +```python +import checkNet +import usocket +import dataCall +import utime +from misc import Power + +# 用户需要配置的APN信息,根据实际情况修改 +usrCfg1 = {'profileID': 1, 'apn': '3gnet', 'username': '', 'password': ''} + +''' +环境配置: +1.关闭开机自动拨号功能 +2.APN配置 +''' +def cfgEnv(usrCfg): + errcode = 0 + need_reboot1 = False + need_reboot2 = False + if "datacall_config.json" not in uos.listdir('/usr'): + # 关闭第一路网卡开机自动激活功能 + dataCall.setAutoActivate(usrCfg['profileID'], 0) + # 建议用户使能网卡自动重连功能 + dataCall.setAutoConnect(usrCfg['profileID'], 1) + need_reboot1 = True + + print('Check the APN configuration.') + # 获取网卡的APN信息,确认当前使用的是否是用户指定的APN + pdpCtx = dataCall.getPDPContext(usrCfg['profileID']) + if pdpCtx != -1: + if pdpCtx[1] != usrCfg['apn']: + # 如果不是用户需要的APN,使用如下方式配置 + ret = dataCall.setPDPContext(usrCfg['profileID'], 0, usrCfg['apn'], usrCfg['username'], usrCfg['password'], 0) + if ret == 0: + print('APN configuration successful.') + need_reboot2 = True + else: + print('APN configuration failed.') + errcode = -1 + else: + print('The APN is correct and no configuration is required') + errcode = 0 + else: + print('Failed to get PDP Context.') + errcode = -1 + # 重启使配置生效 + if need_reboot1 or need_reboot2: + print('Ready to restart.') + print('Please re-execute this program after restarting.') + Power.powerRestart() + utime.sleep(2) + return errcode + + +def main(): + if cfgEnv(usrCfg1) == -1: + return + + # 手动激活蜂窝无线网卡 + print('Prepare to activate the NIC{}.'.format(usrCfg1['profileID'])) + ret = dataCall.activate(usrCfg1['profileID']) + if ret == -1: + print('NIC activation failed.') + return + + stage, state = checkNet.waitNetworkReady(10) + if stage == 3 and state == 1: + print('Network connected successfully.') + # 获取第一路网卡的IP地址等信息 + ret = dataCall.getInfo(usrCfg1['profileID'], 0) + print('NIC{}:{}'.format(usrCfg1['profileID'], ret)) + + print('---------------sock test-----------------') + # 创建socket对象 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + # 解析域名 + try: + sockaddr = usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + except Exception: + print('Domain name resolution failed.') + sock.close() + return + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret = sock.send('GET /News HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send {} bytes'.format(ret)) + # 接收服务端消息 + data = sock.recv(256) + print('recv {} bytes:'.format(len(data))) + print(data.decode()) + # 关闭连接 + sock.close() + + # 用户根据需要决定是否需要在通信结束对网卡进行去激活 + # dataCall.deactivate(usrCfg1['profileID']) + else: + print('Network connected failed, stage={}, state={}'.format(stage, state)) + + +if __name__ == '__main__': + main() +``` + + + +## 手动激活多路网卡 + +这种场景和前面描述的“开机自动激活多路网卡且配置APN”的场景,在配置和功能使用上没有太大的区别。唯一的区别就是需要用户先把开机自动激活网卡功能关闭,然后在用户需要的时候手动进行多路网卡的激活操作。 + +下图是用户手动激活第一路和第二路蜂窝无线网卡的示意图。我们以此图为例进行说明。 + +假如用户为第一路蜂窝无线网卡配置的是可以访问公网的APN,为第二路蜂窝无线网卡配置的是只能访问某个专网的APN。在用户手动激活这两路蜂窝无线网卡后,情况如下: + +* http/https/mqtt这些功能默认只能使用第一路蜂窝无线网卡进行网络通信,用户无法指定使用哪一路蜂窝无线网卡。 + +* socket功能可以通过bind接口来绑定IP和端口号,决定使用哪一路蜂窝无线网卡进行网络通信。如果用户没有指定,则默认使用第一路蜂窝无线网卡。 + +![](../../../media/network-comm/nic/cellular/有APN激活多路网卡.png) + +这种场景下,用户的应用代码应按照下面的步骤来编写: + +步骤1:关闭开机自动激活网卡功能。该配置重启生效。 + +步骤2:为需要手动激活的网卡配置APN。该配置重启生效。 + +步骤3:重启设备,使配置生效。 + +步骤4:在需要的时候,手动进行多路蜂窝无线网卡的激活。 + +步骤5:确认是否激活成功。 + +步骤6:进行网络通信。 + +步骤7:根据需要决定在通信结束后,是否需要对网卡进行去激活。 + + + +下面提供一个例程,该例程中首先关闭了开机自动激活网卡功能,并分别为第一路蜂窝无线网卡和第二路蜂窝无线网卡配置了不同的APN。然后分别创建了两个socket对象,socket1不指定网卡,即默认选择第一路网卡进行网络通信;socket2指定使用第二路蜂窝无线网卡与某个专网进行网络通信。示例代码如下: + +```python +import checkNet +import usocket +import dataCall +import utime +from misc import Power + +# 用户需要配置的APN信息,根据实际情况修改 +usrCfg1 = {'profileID': 1, 'apn': '3gnet', 'username': '', 'password': ''} +usrCfg2 = {'profileID': 2, 'apn': '3gwap', 'username': '', 'password': ''} + +''' +关闭开机自动拨号功能 +返回值表示是否需要重启设备 +True - 需要重启 +False - 不需要重启 +''' +def disableAutoActivate(): + need_reboot = False + if "datacall_config.json" not in uos.listdir('/usr'): + # 关闭第一路网卡开机自动激活功能 + dataCall.setAutoActivate(1, 0) + # 建议用户使能网卡自动重连功能 + dataCall.setAutoConnect(1, 1) + need_reboot = True + return need_reboot + +''' +为蜂窝无线网卡配置APN参数 +返回值表示是否需要重启设备 +True - 需要重启 +False - 不需要重启 +''' +def cfgAPN(usrCfg): + need_reboot = False + print('Check the APN configuration of the NIC{}.'.format(usrCfg['profileID'])) + # 获取网卡的APN信息,确认当前使用的是否是用户指定的APN + pdpCtx = dataCall.getPDPContext(usrCfg['profileID']) + if pdpCtx != -1: + if pdpCtx[1] != usrCfg['apn']: + # 如果不是用户需要的APN,使用如下方式配置 + ret = dataCall.setPDPContext(usrCfg['profileID'], 0, usrCfg['apn'], usrCfg['username'], usrCfg['password'], 0) + if ret == 0: + print('APN configuration successful.') + need_reboot = True + else: + raise ValueError("APN configuration failed.") + else: + need_reboot = False + print('The APN is correct and no configuration is required') + else: + raise ValueError("Failed to get PDP Context.") + return need_reboot + + +def main(): + need_reboot1 = disableAutoActivate() + need_reboot2 = cfgAPN(usrCfg1) + need_reboot3 = cfgAPN(usrCfg2) + if need_reboot1 or need_reboot2 or need_reboot3: + print('Ready to restart.') + print('Please re-execute this program after restarting.') + Power.powerRestart() + utime.sleep(2) + + # 手动激活蜂窝无线网卡 + print('Prepare to activate the NIC{}.'.format(usrCfg1['profileID'])) + ret = dataCall.activate(usrCfg1['profileID']) + if ret == -1: + print('NIC activation failed.') + return + print('Prepare to activate the NIC{}.'.format(usrCfg2['profileID'])) + ret = dataCall.activate(usrCfg2['profileID']) + if ret == -1: + print('NIC activation failed.') + return + + stage, state = checkNet.waitNetworkReady(10) + if stage == 3 and state == 1: + print('Network connected successfully.') + # 分别获取第一路和第二路网卡的IP地址信息 + ret1 = dataCall.getInfo(usrCfg1['profileID'], 0) + ret2 = dataCall.getInfo(usrCfg2['profileID'], 0) + print('NIC{}:{}'.format(usrCfg1['profileID'], ret1)) + print('NIC{}:{}'.format(usrCfg2['profileID'], ret2)) + ip_nic1 = None + ip_nic2 = None + if ret1 == -1 or ret1[2][2] == '0.0.0.0': + print("Error: Failed to get the IP of the NIC{}.".format(usrCfg1['profileID'])) + return + else: + ip_nic1 = ret1[2][2] + if ret2 == -1 or ret2[2][2] == '0.0.0.0': + print("Error: Failed to get the IP of the NIC{}.".format(usrCfg2['profileID'])) + return + else: + ip_nic2 = ret2[2][2] + print('NIC{} IP:{}'.format(usrCfg1['profileID'], ip_nic1)) + print('NIC{} ip:{}'.format(usrCfg2['profileID'], ip_nic2)) + + print('---------------sock1 test-----------------') + # 创建socket对象 + sock1 = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + # 解析域名 + try: + sockaddr = usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + except Exception: + print('Domain name resolution failed.') + sock1.close() + return + # 建立连接 + sock1.connect(sockaddr) + # 向服务端发送消息 + ret = sock1.send('GET /News HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send {} bytes'.format(ret)) + # 接收服务端消息 + data = sock1.recv(256) + print('recv {} bytes:'.format(len(data))) + print(data.decode()) + # 关闭连接 + sock1.close() + print('---------------sock2 test-----------------') + sock2 = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + sock2.bind((ip_nic2, 0)) + sock2.settimeout(10) + # 服务器IP和端口,下面的IP和端口仅作示例参考 + server_addr = ('220.180.239.212', 8305) + # 建立连接 + sock2.connect(server_addr) + # 向服务器发送消息 + ret = sock2.send('test data.') + print('send {} bytes'.format(ret)) + # 接收服务端消息 + try: + data = sock2.recv(256) + print('recv {} bytes:'.format(len(data))) + print(data.decode()) + except Exception: + print('No reply from server.') + # 关闭连接 + sock2.close() + + # 用户根据需要决定是否需要在通信结束对网卡进行去激活 + # dataCall.deactivate(usrCfg1['profileID']) + # dataCall.deactivate(usrCfg2['profileID']) + else: + print('Network connected failed, stage={}, state={}'.format(stage, state)) + + +if __name__ == '__main__': + main() +``` + + + +## 配置DNS服务器地址 + +模组在激活蜂窝无线网卡时,只要使用的是公共网络(Internet)的APN,核心网一般都会分配DNS服务器地址给模组。用户正常情况下是不需要去手动配置DNS服务器地址的。但是有时候,会出现核心网分配的DNS服务器地址无法使用的情况,此时用户可以尝试手动配置DNS服务器地址。 + +可以使用如下方法来配置用户指定的DNS服务器地址: + +``` +dataCall.setDNSServer(profileID, simID, priDNS, secDNS) +``` + +关于该方法的详细描述,可以参考QuecPython官方网站中wiki文档的[DNS配置](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html#DNS%E9%85%8D%E7%BD%AE%E5%8A%9F%E8%83%BD)部分。这里说明一下`profileID`和`simID`参数的确定。 + +* `profileID` + + 在配置DNS服务器地址时,`profileID`参数选择什么值,取决于激活的是哪一路蜂窝无线网卡。一般是第一路,即`profileID`参数选择`1`。 + +* `simID` + + 默认写0即可。 + +下面是配置第一路蜂窝无线网卡的DNS服务器地址的示例: + +```python +import dataCall +dataCall.setDNSServer(1, 0, "8.8.8.8", "114.114.114.114") +``` + diff --git a/docs/Application_guide/zh/network-comm/nic/cellular/common-concepts.md b/docs/Application_guide/zh/network-comm/nic/cellular/common-concepts.md new file mode 100644 index 0000000000000000000000000000000000000000..cd7d63a2d913e120c4961f8350a1b709c019a5a3 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/cellular/common-concepts.md @@ -0,0 +1,248 @@ +# 蜂窝网络基础概念 + +## APN + +### 什么是APN + +APN(Access Point Name,接入点名称)指一种网络接入技术,是终端入网时必须配置的一个参数,它决定了终端通过哪种接入方式来访问网络。 + +对于用户来说,可以访问的外部网络类型有很多,例如:[Internet](https://baike.baidu.com/item/Internet/272794)、[WAP网站](https://baike.baidu.com/item/WAP网站/3419865)、集团企业内部网络、行业内部专用网络。而不同的接入点所能访问的范围以及接入的方式是不同的,网络侧如何知道终端激活以后要访问哪个网络从而分配哪个网段的IP呢,这就要靠APN来区分了,即APN决定了用户的终端通过哪种接入方式来访问什么样的网络。 + +所有运营商都使用特定的APN(Access Point Name,接入点名称)。这通常是您的SIM卡预先配置的,但必要时,您需手动进行调整。 + +### 确认用什么APN + +所有运营商都有自己的APN,一般普通的SIM卡(也叫公网卡),其APN都是公开的,可以在网络上查询到,或者直接联系对应运营商咨询。而对于物联网卡或者其他一些专网卡,需要用户联系对应运营商去确认应该使用什么APN。 + + + +## CFUN + +CFUN(Cellular Functionality,蜂窝功能)指移动终端的功能模式。我们在提到CFUN时,一般都是指使用`net`模块的相关方法或者`AT+CFUN`这个AT命令,来设置或者查询移动终端的功能模式。关于如何查询和设置CFUN,请参考《蜂窝网络API说明》章节的[设备工作模式]()部分。移动终端通常有如下几种功能模式: + +* 最小功能模式:该模式下,整个射频网络协议栈全部关闭,SIM卡模块停止供电。这种模式下,终端设备功耗是不关机情况下最低的。 + +* 全功能模式:该模式下,终端设备的无线射频功能全都是开启的,设备可以执行网络相关操作。 + +* 飞行模式:该模式下,会禁用终端设备的无线射频功能,即禁止设备发送和接收RF信号,但是SIM卡模块依然是正常供电,并且可以正常识别到SIM卡的。 + + + +## MCC/MNC + +MCC(Mobile Country Code)和 MNC(Mobile Network Code)是在移动通信网络中用来唯一标识一个移动网络运营商的。 + +* MCC:移动国家码。MCC是由3位数字组成的,用于标识移动设备所在的国家或地区。例如,中国的MCC是460。 + +* MNC:移动网络码。MNC是由2位或3位数字组成的,用于在一个特定国家或地区内标识具体的移动网络运营商。例如,中国移动的MNC是00或02,中国联通的MNC是01,中国电信的MNC是03或05。 + +将MCC和MNC组合在一起,就形成了一个全球唯一的代码,用于标识世界上每一个移动网络运营商。我们将由MCC和MNC组合起来的标识称为PLMN(Public Land Mobile Network)。 + + + +## 小区 + +在移动网络中,小区(Cell)代表的是基站覆盖的特定地理区域。每一个小区都由一个基站来覆盖并为其提供网络服务。各个小区的大小并不是统一的,会根据需求和环境进行调整。 + +对于UE来说,小区还分为服务小区(Serving Cell)和邻区(Neighbouring Cell)。 + +* 服务小区:是指当前为移动设备提供服务的小区。也就是说,这个设备目前正与这个小区的基站进行通信,所有的呼叫、数据传输等活动都通过这个小区进行。 + +* 邻区:是指那些与服务小区相邻,并且可能在移动设备移动时成为新的服务小区的小区。移动设备会周期性地测量邻区的信号质量,以便在必要时进行小区切换。 + + + +## 信号质量 + +### 概述 + +在蜂窝移动网络中,信号质量是由不同的测量值来确定的,并不是只看某个参数的测量值。在不同的网络制式中,用来衡量信号质量的参数以及参数范围一般也都不同。下面是一些常见的测量值: + +* RSSI + +* CSQ + +* RSRP + +* RSRQ + +* RSCP + +* SINR + + + +### 影响信号质量的因素 + +影响信号强度和信号质量的因素有很多,比如: + +* 和基站的距离:移动设备与基站之间的距离越远,信号强度就越弱,信号质量也可能下降。这是因为信号在传播过程中会衰减。 + +* 遮挡物:建筑物、树木、山丘等遮挡物可以阻碍信号的传播,导致信号强度下降和信号质量降低。这种影响在室内尤其明显。 + +* 天线性能:设备和基站的天线性能也会影响信号强度和信号质量。 + +* 干扰:其他设备发出的信号可能会与目标信号冲突,导致信号质量下降。这种情况在频繁使用无线设备的地方(例如公共WIFI区域)尤其常见。 + +* 多路径效应:信号可能会在到达接收器之前反射、折射或散射,导致多个信号路径。这些不同路径的信号可能会相互干扰,导致信号质量下降。 + +* 天气条件:某些天气条件,如雨、雾、雪等,也可能对无线信号产生影响,降低信号强度和质量。 + +* 网络拥塞:在网络使用量大的时段,如高峰时段,大量用户可能会导致网络拥塞,进而影响信号质量。 +* 基站负载:同一个基站在某时间段内承载了太多的用户或者很大的数据流量,也可能会影响到信号质量。 + +* 功率控制:移动通信系统通常采用功率控制机制来优化信号强度,避免过强的信号导致的干扰和过弱的信号导致的服务质量下降。 + +我们在衡量信号强度和信号质量时,不能片面的认为信号强度值很好或者信号质量好,就表示蜂窝移动通信数据传输就一定是非常快和稳定的。比如用户所在地方,RSSI的值很大,说明信号强度很好,但是用户使用时,由于网络拥塞或者基站负载过重,导致用户设备此时的通信质量并不是很好。 + +### RSSI + +RSSI(Received Signal Strength Indicator)指接收的所有信号的总功率(单位dBm),包括导频信号、数据信号、邻区干扰信号和底噪信号等。RSSI的参数范围在不同的网络制式中有所不同,但是值越大,表示信号强度越好。 + +RSSI通常是一个相对值,它的测量和接收设备有很大的关系。因此对于不同设备,其RSSI的衡量标准并不是完全统一的,因此下面我们提供的RSSI衡量标准仅作为一个参考: + +**衡量标准** + +| RSSI(dBm) | 信号强度 | 描述 | +| ------------------ | -------- | ------------------------------------------------------------ | +| RSSI < -100 | 差 | 信号非常弱,无法进行正常通信。 | +| -100 <= RSSI < -90 | 一般 | 信号较弱,可能会有丢包或延迟等问题。 | +| -90 <= RSSI < -80 | 良好 | 信号良好,可以支持大部分应用。 | +| -80 <= RSSI < -70 | 好 | 信号很好,适合高清视频、实时语音等应用。 | +| RSSI > -70 | 非常好 | 信号非常好,适合高速数据传输和对网络质量要求较高的应用场景。 | + +
+ +### CSQ + +CSQ表示信号强度,是用来指示RSSI强度的参数,取值范围是0~31,数值越大表示信号强度越好。若CSQ值小于6,终端基本无法进行网络通信。CSQ和RSSI之间是有对应关系的,其对应关系如下: +$$ +CSQ=(RSSI+113)/2 +$$ + + +| CSQ | RSSI(dBm) | +| ---- | ---------------------- | +| 0 | -113 | +| 1 | -111 | +| 2~30 | -109 ~ -53 | +| 31 | \>= -51 | +| 99 | 未知或者不可检测等异常 | + +
+ +### RSRP + +RSRP(Reference Signal Received Power)指参考信号接收功率。是在某个符号内承载参考信号的所有RE(资源粒子)上接收到的信号功率的平均值。反应的是当前信道的路径损耗强度,用于小区覆盖的测量和小区的选择以及重选。RSRP的取值范围是-140dBm ~ -44dBm,值越大越好。 + +需要注意的是,RSRP是在LTE中才引入的概念,也就是说RSRP是用来衡量LTE网络信号强度的参数。相当于WCDMA网络中的RSCP参数。下面提供的RSRP参数衡量标准仅作为一个参考: + +**衡量标准** + +| RSRP(dBm) | 强度级别 | 描述 | +| ------------------ | -------- | ------------------------------------------------------------ | +| RSRP <= -105 | 6 | 覆盖很差,基本无法进行业务。 | +| -105 < RSRP <= -95 | 5 | 覆盖差,室外语音业务能够起呼,但是呼叫成功率低,掉话率高;室内基本无法发起业务。 | +| -95 < RSRP <= -85 | 4 | 覆盖一般,室外能够发起各种业务,可获得低速率的数据业务;室内呼叫成功率低,掉话率高。 | +| -85 < RSRP <= -75 | 3 | 覆盖较好,室外能够发起各种业务,可获得中等速率的数据业务;室内能发起各种业务,可获得低速率数据业务。 | +| -75 -65 | 1 | 覆盖非常好。 | + +
+ +### RSRQ + +RSRQ(Reference Signal Received Quality)指参考信号接收质量。表示当前信道质量的信噪比和干扰水平。其取值范围是-19.5 dB ~ -3 dB,值越大越好。RSRQ是在LTE中才引入的概念,也就是说RSRQ是用来衡量LTE网络信号强度的参数。 + +RSRQ是RSRP和RSSI的比值,但是因为两者测量所基于的带宽可能不同,会用一个系数来调整,即 RSRQ = N*RSRP/RSSI。 + +下面提供的RSRQ参数衡量标准仅作为一个参考: + +**衡量标准** + +| RSRQ(dB) | 信号质量 | 说明 | +| ------------------- | -------- | ---------------------------------- | +| -19.5 <= RSRQ < -15 | 差 | 信号质量很差,基本不能正常通信。 | +| -15 <= RSRQ < -10 | 一般 | 信号质量一般,通信可能会收到干扰。 | +| -10 <= RSRQ < -7 | 良好 | 信号质量较好,可以正常通信。 | +| -7 <= RSRQ <= -3 | 好 | 信号质量很好,数据通信速度快。 | + +
+ +### RSCP + +RSCP(Receive Signal CodePower)指接收信号码功率。它是UMTS网络系统中的概念,表示接收器在特定物理信道上测量的功率。它在下行链路功率控制中用作信号强度的指示、切换标准以及计算路径损耗。RSCP的取值范围通常在-120 dBm到-25 dBm之间。 + +下面提供的RSCP参数衡量标准仅作为一个参考: + +| RSCP(dBm) | 信号强度 | 描述 | +| ------------------- | -------- | -------------------------------------------------- | +| RSCP < -110 | 很差 | 信号非常差,基本无法保持稳定的通话和数据传输。 | +| -110 <= RSCP < -100 | 较差 | 信号较差,很以保持稳定的通话和数据传输。 | +| -100 <= RSCP < -85 | 一般 | 信号一般,可能会影响通话和数据传输的质量。 | +| -85 <= RSCP < -75 | 良好 | 信号较好,通常能够保持高质量的通话和数据传输。 | +| RSCP >= -75 | 好 | 信号非常好,通常可以保持最高质量的通话和数据传输。 | + +
+ +### SINR + +SINR(Signal to Interference plus Noise Ratio)指信号到干扰加噪声比,即接收到的有用信号的强度和接收到的干扰信号强度的比值。它是用来表示移动网络通信中信号质量的重要参数。SINR的单位是dB,范围一般是0dB到30dB。 + +下面提供的SINR参数衡量标准仅作为一个参考: + +| SINR(dB) | 信号质量 | 说明 | +| --------------- | -------- | -------------------------------------------- | +| SINR < 3 | 非常差 | 通常无法建立连接或维持通信。 | +| 3 <= SINR < 10 | 差 | 通信不稳定,可能会出现高丢包率和低数据速率。 | +| 11 <= SINR < 15 | 一般 | 通信较为稳定,但数据速率可能受限。 | +| 16 <= SINR < 25 | 好 | 通信稳定,数据速率较高。 | +| SINR >= 25 | 非常好 | 通信非常稳定,可获得高数据传输速率。 | + +
+ +## BAND + +在移动通信中,BAND表示频段,是指无线电频谱中特定的频率范围,每个频段由一定的频率范围和带宽(频宽)组成。无线电频谱是一种有限的宝贵资源,全球的各种无线电通信服务都必须共享这一资源。为了确保各种服务之间能够和谐共存,避免相互干扰,国际电信联盟(ITU)和各国的电信管理机构会将无线电频谱划分为多个频段,每个频段被指定给一种或多种特定的服务使用。同时为了方便使用和管理,这些通信频段通常会被编号,比如Band1、Band2、Band3等。 + +不同的频段有着不同的传播特性,例如,低频段的信号能够更好地穿透建筑物,而高频段的信号更适合在开阔地区或直线视距内传播。因此运营商在获取和使用频段时需要做出平衡,决定支持哪些频段,以实现网络覆盖和容量的最优化。 + + + +## 网络制式 + +网络制式(Radio Access Technology,简称RAT),也叫无线接入技术,是指移动设备如何通过无线电信号连接到网络的技术。比如我们通常说的GSM、GPRS、WCDMA、CDMA2000、LTE等都是网络制式。 + + + +## 网络技术 + +网络技术可以理解为使得设备能够连接到移动网络并且进行通信的一系列技术。通常指的是用于在设备和网络之间建立和管理连接的一组技术或协议。 + +例如: + +COMPACT是一种针对GSM网络的优化技术,它通过改变控制通道的处理方式,来提高网络的频谱效率和容量。因此我们也将其归类为GSM网络技术的一种。 + +EMTC(Enhanced Machine-Type Communication)是指增强型机器类通信。这是一种LTE技术,旨在提高网络对大量低功耗设备的支持。因此我们也将其归类为LTE网络技术的一种。 + + + +## 默认承载 + +在介绍默认承载之前,我们先来说明什么是承载。承载(Bearer)也叫EPS承载,是LTE中才引入的术语,它是一种逻辑上的概念。指的是LTE无线网络中传递信息的通道。如果将承载比喻为一条交通道路的话,那信息就像是路上来回的车辆。 + +在LTE无线网络中,承载一般分为默认承载(Default Bearer)和专用承载(Dedicated Bearer)。 + +* 默认承载:在UE初始附着过程中按照用户签约的默认QoS等级建立一个承载,叫默认承载。在PDN连接业务存在期间会始终保持这个默认承载,给UE提供一个“永久在线”的IP连接。其实可以将其理解为一种提供尽力而为的IP连接的承载。 + +* 专用承载:连接到相同的PDN的其他EPS承载称为专用承载。也就是在默认承载基础上,为了提供某种特定的QoS传输需求而建立的,比如VoLTE功能就需要专门建立一路承载。 + + + +## 基站时间 + +基站时间通常指的是无线基站的内部时钟,这个时钟为移动通信网络提供了一个精确的时间参考。在无线通信系统中,发送和接收数据需要在准确的时间窗口内进行。例如,数据传输需要准确地与基站的时间同步,否则可能会导致数据丢失或错误。因此,基站需要有一个精确的内部时钟,以便控制这些时间敏感的操作。 + +此外,基站时间还有一个很常见的用处,即一些移动设备可能会通过基站时间来自动校准它们的本地时间,尤其是当移动设备在不同的时区之间移动时。而这个过程就是通过NITZ(Network Identity and Time Zone)来实现的。 + + diff --git a/docs/Application_guide/zh/network-comm/nic/cellular/exception-handling.md b/docs/Application_guide/zh/network-comm/nic/cellular/exception-handling.md new file mode 100644 index 0000000000000000000000000000000000000000..15ec0e6777f09e655399045224bfd3e8bf519cf2 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/cellular/exception-handling.md @@ -0,0 +1,510 @@ +# 网络异常处理 + +本文主要讲述在QuecPython中,如何排查和处理网络异常情况,并提供一个示例,演示了在设备运行过程中,出现网络异常时的处理方式。 + +## 开机时网络异常处理 + +模组开机时的网络异常,主要包括3种情况,分别是: + +* SIM卡异常 + +* 模组网络注册失败 + +* 蜂窝无线网卡自动激活失败 + +这3种情况导致的直接结果,就是模组无法连接到网络。因此我们将这些情况都称之为“网络异常”。下面我们分别来说明如何排查和处理这3种网络异常情况。 + +### SIM卡异常 + +SIM卡异常主要有下面3种情况,分别是: + +#### 设备没有检测到SIM卡 + +我们可以先通过如下方式,查看SIM卡的状态: + +步骤1:将终端设备通过USB数据线连接到电脑的USB端口上。 + +步骤2:在电脑上打开QPYcom工具,打开QuecPython的REPL命令端口,进入到交互模式。 + +![打开QPYcom](../../../media/network-comm/nic/cellular/QPYcom打开REPL.png) + +步骤3:在命令交互行,按照如下方式调用API查询注网状态。 + +``` +import sim +sim.getStatus() +``` + +![API查询SIM卡状态-未插卡](../../../media/network-comm/nic/cellular/API查询SIM卡状态-未插卡.png) + +如果`sim.getStatus()`的返回值为0,说明设备没有检测到SIM卡,此时需要确定是否插入了SIM卡。如果用户已经插入SIM卡并且重启设备后,查询状态值还是0,那可能的原因如下: + +* SIM卡没有插好,比如插反了,或者没有插紧。可以重新插入SIM卡并重启设备,开机后再次查询SIM卡状态是否为1。 + +* SIM卡本身有损坏。可以换一张可以正常使用的SIM卡插入后,重启设备,然后再次查询SIM卡状态是否为1。 + +* SIM卡卡槽有损坏。如果确认是这个问题,则需要更换新的卡槽。 + +* SIM卡的硬件电路存在问题,比如接触不良,导致设备无法正常识别SIM卡。需要硬件工程师检查电路确认问题。 + +用户可以依次排查上面几种情况来确认问题。 + +
+ +#### SIM卡状态异常 + +SIM卡状态异常是指通过`sim.getStatus()`这个API查询的值既不是0也不是1,比较常见的是2和3。 + +* 当SIM卡状态值是2时 + +当SIM卡状态值为2的时候,说明用户之前为这张SIM卡启用了PIN码验证功能。这种情况下,需要先输入正确的PIN码进行验证,然后关闭PIN码验证功能,最后重启设备,开机后再次确认SIM卡状态是否为1。 + +> 关于SIM卡应该使用什么PIN码,一般在SIM卡的卡托上有标注默认的PIN码。如果用户找不到卡托了,则需要用户与SIM卡所属运营商进行确认,请勿随意输入PIN码进行尝试。我们强烈建议用户,如果没有特殊需要,不要随便使用PIN码相关的功能,因为很可能因为误操作导致SIM卡永久无法使用。 + +比如用户现在使用的是中国联通的SIM卡,默认的PIN码是'1234',现在这张卡启用了PIN码验证,查询的状态值是2,那么可以按照如下步骤来恢复: + +![SIM卡被PIN锁定](../../../media/network-comm/nic/cellular/SIM启用PIN码验证.png) + +步骤1:先通过下面方法输入PIN码进行验证。 + +![](../../../media/network-comm/nic/cellular/PIN码验证.png) + +步骤2:关闭PIN码验证。 + +![](../../../media/network-comm/nic/cellular/关闭PIN码验证.png) + +步骤3:重启设备,再次查询SIM卡状态,确认是否恢复正常。 + +![](../../../media/network-comm/nic/cellular/重启后SIM卡状态恢复正常.png) + + + +* 当SIM卡状态值是3时 + +![](../../../media/network-comm/nic/cellular/SIM卡被锁.png) + +当SIM卡状态值为3的时候,说明这一张卡已经被锁,此时需要使用PUK码来进行解锁。PUK码一般在SIM卡的卡托上可以找到,如下图所示: + +![](../../../media/network-comm/nic/cellular/SIM卡PUK码位置.png) + +需要说明的是,每一张SIM卡都有自己的PUK,用户在解锁SIM卡时,一定要确定使用的PUK是正确的。如果多次输入PUK错误,这张SIM卡将永久锁定,再也无法使用。 + +当用户确认了PUK码后,可以按照下面的步骤来解锁: + +步骤1:输入正确的PUK解锁,并设置新的PIN码。 + +SIM卡解锁使用的是下面的方法。该方法有两个参数,第一个参数表示PUK码,第二个参数表示新设置的PIN码。一般SIM卡被锁的原因就是用户启用了PIN码验证,但是在进行PIN码验证时,连续3次输入了错误的PIN码,此时SIM卡就会被锁。所以使用PUK码进行解锁时,需要用户重新设置一个新的PIN码。 + +```python +sim.unblockPin(puk, newPin) +``` + +![](../../../media/network-comm/nic/cellular/输入PUK解锁.png) + +通过上图可以看到,SIM卡已经解锁成功,解锁后查询SIM卡状态,状态值已经变成了1。但是需要注意,此时仅仅是解锁成功了,但是SIM卡的PIN码验证功能并没有关闭,如果现在重启设备,开机后,SIM卡的状态值依然是2,表示需要用户输入PIN码进行验证。因此我们可以先将PIN码验证功能关闭,然后再重启设备。当然,也可以在解锁SIM卡并重启后,先进入PIN码验证,验证成功后,再把PIN码验证功能关闭。 + +步骤2:关闭PIN码验证功能。 + +注意,由于SIM卡解锁时,我们已经重新设置了PIN码,因此进行PIN码验证和关闭PIN码验证时,必须使用最新设置的PIN码。 + +![](../../../media/network-comm/nic/cellular/使用最新设置的PIN码进行验证和关闭验证.png) + +步骤3:重启设备,确认SIM卡状态是否为1。 + + + +#### SIM卡欠费 + +这种情况有些特殊,因为不同运营商的SIM卡情况可能不太一样,没办法用一个完全统一的标准去判断。一般如果出现下面两种情况,我们建议用户先去查询一下SIM卡是否出现欠费的情况。 + +情况1:SIM卡状态值为1,设备网络注册已经成功,并且蜂窝无线网卡也激活成功,但是无法进行网络业务,比如无法发送数据或者接收数据。 + +![](../../../media/network-comm/nic/cellular/无线网卡激活成功.png) + +情况2:SIM卡状态值为1,但是网络注册失败。 + +![](../../../media/network-comm/nic/cellular/SIM卡状态为1但是注网失败.png) + + + +> 注意,并不是说出现“SIM卡状态值为1,但是网络注册失败。”的情况时,就一定是SIM卡欠费了。SIM卡欠费仅仅是导致出现这种情况的可能原因之一。 + + + +### 模组网络注册失败 + +这种情况是指模组已经正常检测到SIM卡,即通过`sim.getStatus()`方法查询结果为1,但是通过`net.getState()`方法查询模组网络注册状态,发现网络注册状态既不是1也不是5。 + +![](../../../media/network-comm/nic/cellular/SIM卡状态为1但是注网失败.png) + +导致这种情况的原因有很多,常见的有下面这些: + +#### 射频性能不好 + +当设备的射频性能不好时,可能会导致模组发送的消息无法到达基站,或者无法接收到基站下发的消息。结果就是导致模组无法注册到网络。一般我们可以先查询模组的CSQ信号强度参数,来进行初步的判断。若CSQ值小于6,终端基本无法进行网络通信。查询CSQ方法如下: + +```python +net.csqQueryPoll() +``` + +操作如下: + +![](../../../media/network-comm/nic/cellular/csq查询.png) + + + +影响模组射频性能的因素比较多,比如设备没有接天线、天线性能不好、设备所处环境比较封闭、模组没有进行射频参数校准、设备硬件设计问题、模组射频器件有损坏等。这些原因中,用户能直接进行排查的,主要有下面两种: + +* 没有接天线/天线性能不好 + + 如果用户没有接天线,请先接上适合的天线,然后看看是否可以成功注册到网络。如果接了天线,可以尝试换一个天线,然后再次查询CSQ,确认是否有改善。 + +* 设备所处环境比较封闭 + + 如果用户的设备处于一个比较封闭的环境,也会影响到模组的射频性能,导致设备无法正常进行网络通信。用户可以尝试到室外开阔地带重新测试。 + +其他的情况,需要联系我司FAE协助处理。 + + + +#### 没有配置APN + +有的国家和地区,UE在进行网络注册时,需要携带APN信息,否则会被核心网拒绝导致网络注册失败。当用户发现设备无法注册到网络时,请先确认是否配置了正确的APN。如果没有配置或者配置错误,请按照如下步骤进行配置: + +步骤1:和运营商确认这张SIM卡应该使用什么APN。 + +步骤2:查询模组的APN配置情况,确认是否需要配置APN。如果当前APN已经是用户需要配置的,则无需重复配置;否则需要重新配置,并重启设备。 + +QuecPython默认开机自动激活第一路蜂窝无线网卡,下面以此为例,说明如何为第一路蜂窝无线网卡配置APN。假如用户现在使用的是中国联通的SIM卡,经过确认,应该使用的APN为`3gnet`,则示例代码如下: + +```python +>>> import dataCall +>>> dataCall.getPDPContext(1) +(0, '', '', '', 0) # 返回值中,APN为空,说明没有配置APN +>>> dataCall.setPDPContext(1,0,'3gnet','','',0) +0 +``` + +上述示例,是在QPYcom工具的REPL交互模式下直接执行API,如果是在用户的python脚本文件中,我们可以按照如下方式编写: + +```python +import checkNet +import usocket +import dataCall +from misc import Power + +# 用户需要配置的APN信息,根据实际情况修改 +usrCfg = {'apn': '3gnet', 'username': '', 'password': ''} + +def checkAPN(): + # 获取第一路网卡的APN信息,确认当前使用的是否是用户指定的APN + pdpCtx = dataCall.getPDPContext(1) + if pdpCtx != -1: + if pdpCtx[1] != usrCfg['apn']: + # 如果不是用户需要的APN,使用如下方式配置 + ret = dataCall.setPDPContext(1, 0, usrCfg['apn'], usrCfg['username'], usrCfg['password'], 0) + if ret == 0: + print('APN configuration successful. Ready to restart to make APN take effect.') + print('Please re-execute this program after restarting.') + # 重启后按照配置的信息进行拨号 + Power.powerRestart() + else: + print('APN configuration failed.') + return False + else: + print('The APN is correct and no configuration is required') + return True + else: + print('Failed to get PDP Context.') + return False + + +def main(): + checkpass = checkAPN() + if not checkpass: + return + + stage, state = checkNet.waitNetworkReady(20) + if stage == 3 and state == 1: + print('Network connected successfully.') + # do something + else: + print('Network connected failed, stage={}, state={}'.format(stage, state)) + + +if __name__ == '__main__': + main() +``` + + + +#### 无SIM卡所属运营商的网络覆盖 + +每个运营商在不同地方的基站部署情况可能都不相同,并且不同的基站,其信号覆盖范围可能也不相同。这就会导致设备在一些地方无法注册到网络。因为这些地方没有设备使用的SIM卡所属运营商的网络覆盖,或者覆盖很弱。设备在开机后搜索可用网络时,找不到适合的小区,导致网络注册失败。 + +> 覆盖很弱,指的是设备处于某一个小区的边缘。此时设备搜索该小区时,很可能无法搜到,或者偶尔能搜到。 + +比较常见的现象,就是用户发现同样一台设备,使用的也是同一张SIM卡,但是在地点A可以成功注册到网络上,然后到另外一个地点B后,发现设备一直无法注册到网络,或者花了很长时间才注册到网络。 + +如果客户遇到这种情况,基本可以确认是因为设备所处地点没有对应运营商网络覆盖或者是覆盖很弱导致。这种情况下,用户可以换一个地方进行测试,或者换一张其他运营商的SIM卡进行测试,进一步确认是否是该问题。 + + + +#### SIM卡只支持特定网络制式 + +有的SIM卡只支持特定的网络制式,比如一些SIM卡只支持GSM网络,但是设备所处的地点没有GSM网络覆盖,就会导致设备无法注册到网络。这种情况下用户可以进行下面的处理: + +* 和SIM卡所属运营商进行确认,确认该SIM卡是否只支持特定的网络制式。 + +* 联系我司FAE协助,抓取设备开机网络注册过程的log,提供给我司研发人员进一步确认。 + + + +#### SIM卡只支持特定频段 + +有的SIM卡比较特殊,只能注册到特定频段的网络。此时用户需要确认两件事: + +* 和SIM卡运营商确认该SIM卡是否只支持特定频段,如果是,那支持哪些频段。 + +* 查阅对应模组的规格书或者联系我司的FAE,确认该模组支持哪些频段。如果SIM卡支持的频段,模组不支持,则设备无法注册到网络。还有一种情况是,设备所处的地方,没有该频段的网络覆盖,也会导致设备无法注册到网络。 + +上述仅列出了一些比较常见的导致设备网络注册失败的原因。如果经过排查后,导致设备网络注册失败的原因依然无法确认,请联系我司FAE协助,抓取设备开机网络注册过程的log,提供给我司研发人员进行分析确认。 + + + +### 蜂窝无线网卡自动激活失败 + +蜂窝无线网卡自动激活失败,特指模组网络已经注册成功,但是无线网卡激活失败的情况。即模组网络注册成功了,但是没有获取到IP信息。如下图所示: + +![](../../../media/network-comm/nic/cellular/无线网卡激活失败.png) + +一般这种情况,可能有如下3种原因: + +#### 没有配置APN + +如果出现这种无线网卡激活失败的情况,用户首先可以去确认是否配置了APN,如果配置了,那配置的APN是否正确。用户可以通过下面的方法来查询确认: + +```python +>>> import dataCall +>>> dataCall.getPDPContext(1) # 查询第一路无线网卡的APN配置信息 +(0, '', '', '', 0) # 返回值中,APN为空,说明没有配置APN +``` + +如果上述方法的返回值中,APN为空,或者不是正确的APN,则用户需要使用下面的方法来设置,设置成功后,重启设备。这里假设需要设置的APN为`3gnet`: + +```python +>>> dataCall.setPDPContext(1,0,'3gnet','','',0) +0 +``` + +关于APN配置的更详细示例,请参考前面“模组网络注册失败”章节中“没有配置APN”部分的示例。 + + + +#### 用户关闭了开机自动激活网卡功能 + +排除APN配置的原因,用户也可以检查一下,之前是否有使用`dataCall.setAutoActivate`方法把开机自动激活无线网卡功能给关闭了,或者配置成了开机自动激活第二路或者第3路无线网卡。排查方式如下: + +步骤1:通过QPYcom工具查看usr目录下是否有`datacall_config.json`文件。 + +如果像下图中所示一样,usr目录下存在`datacall_config.json`文件,说明用户配置过QuecPython无线网卡的开机自动激活功能或者自动重连功能。如果不存在该文件,则无需再进行后面的步骤。 + +![](../../../media/network-comm/nic/cellular/QPYcom查看usr目录是否有拨号配置文件.png) + +步骤2:打开`datacall_config.json`文件,进一步确认每一路无线网卡的配置情况。 + +在命令行执行下面的代码: + +```python +>>> import ujson +>>> fd = open('/usr/datacall_config.json', 'r') +>>> cfg = ujson.load(fd) +>>> print(cfg) +{'1': {'autoConnect': 0, 'autoActivate': 0}, '3': {'autoConnect': 0, 'autoActivate': 0}, '2': {'autoConnect': 0, 'autoActivate': 0}} +``` + +我们将上述`cfg`的格式整理一下: + +``` +{ + '1': {'autoConnect': 0, 'autoActivate': 0}, + '2': {'autoConnect': 0, 'autoActivate': 0}, + '3': {'autoConnect': 0, 'autoActivate': 0} +} +``` + +此时,各路无线网卡的配置情况就非常清楚了。如果用户的结果和上述一致,说明用户将开机自动激活无线网卡的功能关闭了。如果用户的结果和上面的情况不一致,用户需要确认3路无线网卡的`autoConnect`配置项的值是什么,因为该值决定了开机是否自动激活无线网卡,为0说明开机时不激活,为1说明开机时需要激活。 + +步骤3:恢复开机自动激活无线网卡功能。 + +这里有两种方式可以恢复: + +* 方式1:直接删除usr目录下的`datacall_config.json`文件,然后重启设备。 + +![](../../../media/network-comm/nic/cellular/QPYcom删除拨号配置文件.png) + +* 方式2:使用`dataCall.setAutoActivate`方法打开某一路无线网卡开机自动激活功能,然后重启设备。 + +下面以打开第一路无线网卡开机自动激活功能为例: + +```python +>>> import dataCall +>>> dataCall.setAutoActivate(1, 1) # 开启第一路无线网卡的开机自动激活功能 +>>> dataCall.setAutoConnect(1, 1) # 开启第一路无线网卡的自动重连功能 +``` + +![](../../../media/network-comm/nic/cellular/开启第一路网卡自动激活和自动重连.png) + + + +#### 等待时间不足 + +还有一种情况,也会导致模组网络注册成功了,但是没有获取到IP信息的情况。即模组网络注册成功,正在进行无线网卡激活的时候,用户使用`dataCall.getInfo`去查询了网卡激活结果,此时也是获取不到IP信息的。这种情况并不是无线网卡激活失败,需要用户多等一段时间。 + +我们建议用户使用QuecPython的`checkNet`模块来等待网络就绪,可以将超时时间设置的长一点。可以避免这种情况。下面是一个简单的使用示例: + +```python +import checkNet + + +def main(): + stage, state = checkNet.waitNetworkReady(20) + if stage == 3 and state == 1: + print('Network connected successfully.') + # do something + else: + print('Network connected failed, stage={}, state={}'.format(stage, state)) + + +if __name__ == '__main__': + main() +``` + + + +## 设备运行过程中网络异常处理 + +蜂窝无线网卡激活成功后,用户的应用程序还需要关注一件事——设备与网络的连接状态。这是因为在设备运行过程中,可能会因为一些异常原因(如网络异常、环境干扰、信号差等)导致模组与网络的连接断开。如果用户应用程序没有关注这种网络事件,很可能导致用户应用程序中和网络相关的业务执行异常,导致出现无法预料的问题。 + +QuecPython提供了网络事件监听功能,用户应用程序可以通过注册回调函数的方式来监听网络状态变化事件。当设备与无线网络的连接状态发生变化时,系统就会自动将对应的事件通过用户注册的回调函数,推送给用户的应用程序。 + +注册网络监听回调函数的方法如下: + +```python +dataCall.setCallback(fun) +``` + +回调函数的示例如下: + +```python +def netCallback(args): + profileID = args[0] + netState = args[1] + if netState == 0: + print('### network {} disconnected.'.format(profileID)) + elif netState == 1: + print('### network {} connected.'.format(profileID)) +``` + +该回调函数的参数是一个元组,包含3个元素,目前用户只需要关注前两个元素即可。前两个参数说明如下: + +| 参数 | 类型 | 说明 | +| ------- | ---- | ------------------------------------------------------------ | +| args[0] | 整型 | 蜂窝无线网卡编号,表示当前是哪一路无线网卡的网络连接状态发生了变化。 | +| args[1] | 整型 | 网络状态,0表示网络连接断开,1表示网络连接成功。 | + +我们建议用户注册该回调函数,用于监听网络连接状态,确保当网络连接状态发生变化时,用户应用程序可以根据网络状态变化进行及时的处理。通常,我们可以参考下面的方式来处理: + +* 回调函数中收到网络连接状态发生变化的事件后,通过消息队列功能将网络事件发送给其他线程去处理。当然,用户也可以使用QuecPython的`sys_bus`功能来代替消息队列。 + +* 其他线程在收到网络事件时,判断如果是网络连接断开事件,则停止socket、mqtt等这类和网络相关的业务。同时,该线程也可以选择启动一个定时器,比如先将定时器时间设定为60s。如果60s后,网络还没有恢复,则执行CFUN0/1切换,然后看网络是否可以恢复。 + + + +> **什么是CFUN0/1切换?** +> +> 通过《蜂窝网络基础概念》章节中关于CFUN的说明,可以知道,CFUN指的是移动终端的功能模式。CFUN0/1切换是指通过`net.setModemFun(0)`方法先将设备切换到模式0(最小功能模式),然后再通过`net.setModemFun(1)`方法将设备切换到模式1(全功能模式)。当切换到模式0,设备的整个射频网络协议栈全部关闭,SIM卡模块停止供电;再次切换到模式1时,会重新恢复对SIM卡的供电并重新进行初始化,同时与射频相关的软硬件功能都会重新开启,此时设备会重新发起网络注册流程。 +> +> **为什么要进行CFUN0/1切换?** +> +> QuecPython具有自动重连功能,如果发生网络异常导致设备与网络的连接断开,异常消失后,不是应该自动恢复吗?为什么上面提到的处理方式中,还要进行CFUN0/1的切换? +> +> 我们需要搞清楚,QuecPython的自动重连功能指的是在网络异常恢复后,自动重新激活无线网卡,而不是设备的网络注册状态。设备的网络注册行为是由系统的射频网络协议栈自动控制的,理论上在网络异常因素消失后,射频网络协议栈会自动重新发起网络注册。但是不排除因为一些原因,导致设备没有及时重新进行网络注册的情况。这时因为设备网络注册尚未成功,无线网卡也无法重新激活。因此,我们主动进行了CFUN0/1的切换操作。其实就像是我们平时使用手机时,有时候遇到网络差或者没信号的时候,我们会选择先关闭手机的移动网络,然后再重新打开是一样的道理。当然,用户也可以选择进行重启。 + + + +关于这种场景下的示例,请参考下面的章节。 + + + +## 网络异常事件处理示例 + +下面提供一个模组在运行中出现网络异常的处理代码,提供给用户参考,用户也可按照自己的方案进行处理。 + +```python +import net +import dataCall +import _thread +import osTimer +import utime +import sys_bus + + +CFUN_SWITCH_TOPIC = 'cfun_switch_timer' +NETWORK_EVENT_TOPIC = 'network_event' + + +class NetworkConnectState: + DISCONNECT = 0 + CONNECT = 1 + + +class NetworkExceptionService: + def __init__(self): + self.timer_period = 1000 * 60 * 2 # 2 * 60s + self.__cfun_timer = osTimer() + + sys_bus.subscribe(CFUN_SWITCH_TOPIC, self.__cfun_switch_timer_handle) + sys_bus.subscribe(NETWORK_EVENT_TOPIC, self.__network_event_handle) + + def enable(self): + dataCall.setCallback(self.__network_event_callback) + + def __cfun_switch_timer_callback(self, args): + print('cfun switch timer.') + sys_bus.publish(CFUN_SWITCH_TOPIC, 0) + + def __cfun_switch_timer_handle(self, topic, msg): + print('recv event form cfun_switch_timer.') + net.setModemFun(0) + utime.sleep(5) + net.setModemFun(1) + + def __network_event_callback(self, args): + print('The state of the network connection has changed.') + sys_bus.publish(NETWORK_EVENT_TOPIC, args) + + def __network_event_handle(self, topic, msg): + print('recv event form network_event_callback.') + profile_id = msg[0] + conn_state = msg[1] + if conn_state == NetworkConnectState.DISCONNECT: + print('The network connection has been disconnected.') + print('start cfun_switch_timer.') + self.__cfun_timer.start(self.timer_period, 1, self.__cfun_switch_timer_callback) + elif conn_state == NetworkConnectState.CONNECT: + print('The network connection has been connected.') + self.__cfun_timer.stop() + else: + print('unknown state value:{}.'.format(conn_state)) + + +def main(): + network_monitor = NetworkExceptionService() + network_monitor.enable() + + +if __name__ == '__main__': + main() +``` + diff --git a/docs/Application_guide/zh/network-comm/nic/cellular/hardware-arch.md b/docs/Application_guide/zh/network-comm/nic/cellular/hardware-arch.md new file mode 100644 index 0000000000000000000000000000000000000000..393f6d700c78e852e05050bcc9f5155fc9878840 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/cellular/hardware-arch.md @@ -0,0 +1,99 @@ +# 蜂窝无线网卡硬件结构 + +本文主要讲述模组的硬件结构以及模组作为蜂窝无线网卡时,其模型结构。 + +## 模组硬件结构 + +模组作为蜂窝无线网卡,其硬件结构主要包括如下几个部分: + +* 电源管理 +* 基带部分 +* 射频部分 +* 存储器 +* 外围接口 + +其硬件功能框图主要如下: + +![Functional Diagram](../../../media/network-comm/nic/cellular/Functional_Diagram.png) + + + +> 1. 上图中外围接口仅为示例,不同的模组支持的硬件外设可能不同,具体情况请参考对应模组的硬件设计手册。 +> +> 2. GNSS功能是可选的,并非所有的模组都支持,具体情况请参考对应模组的硬件设计手册。 + + + +下面主要说明和蜂窝无线网卡有直接关联的几个部分: + +* 基带部分 + + 在无线网卡中,基带部分主要负责对上层网络提供的数据进行编码和解码处理,将数字数据转换成可以在物理介质(例如电磁波)上传输的信号,或者将从物理介质上接收到的信号转换成数字数据。此外,基带处理还包括其他一些功能,例如射频信号的调制和解调等。 + +* 射频部分 + + 射频部分主要负责将基带处理部分生成的电信号转换成高频电磁波(射频信号),并通过天线发送出去。同时,它也负责接收天线接收到的射频信号,并将其转换成电信号,然后交给基带部分进行处理。射频部分的主要任务就是射频信号的发射和接收。 + +* PA + + PA是指功率放大器(Power Amplifier)。主要用于增加发射信号的功率,以确保射频信号能够覆盖足够的距离,并改善信号质量。 + + + +## 无线网络分层结构 + +本文以LTE为例来说明无线网络分层结构。LTE核心网接口协议分为控制面(Control Plane,简称C-Plane)和用户面(User Plane,简称U-Plane)。 + +* 控制面:控制面主要负责信令的传输,包括控制信息的传递和网络管理。控制面通信涉及的信息包括呼叫设置、路由选择、网络连接管理、鉴权、位置更新、移动性管理等。通过控制面,网络可以对用户设备进行有效的控制和管理。 + +* 用户面:用户面主要负责用户的数据传输。比如所有的语音、视频、文本或其他形式的数据都是通过用户面进行传输的。用户面的主要任务是有效地传输用户数据,并确保数据的完整性和可靠性。 + +在实际的网络通信中,控制面和用户面的通信通常是同时进行的。例如,当用户使用手机打电话时,控制面将管理呼叫设置和路由选择,而用户面则负责传输用户的语音数据。 + +### 控制面协议栈 + +下图是LTE网络架构中,控制面的协议栈分层结构示意。 + +![](../../../media/network-comm/nic/cellular/控制面分层结构.png) + +由于控制面对用户来说是无感的,并且也不是由用户来直接控制。这里不再详述,如果用户想了解更详细的情况,可以参考3GPP TS 23401协议文档的Control Plane章节。 + +### 用户面协议栈 + +下图是LTE网络架构中,用户面的协议栈分层结构示意。该图详细的说明了,用户应用程序是如何与蜂窝无线网络实现数据通信。图中的UE表示用户的终端设备(比如手机),而最右边经过PDN GW后,为互联网中的应用服务器。 + +应用层只存在于终端设备和应用服务器中,是基于IP传输的。用户数据先经过蜂窝无线网卡的层层处理,然后通过无线接口发送到基站,再通过核心网的网关进行路由,最终达到目的地。 + +![](../../../media/network-comm/nic/cellular/用户面分层结构.png) + + + +通过控制面和用户面的协议栈分层结构图可以看出,它们都包含了如下几个功能: + +* PDCP(Packet Data Convergence Protocol,分组数据汇聚协议)层:主要作用是实现头压缩,并且实现加密和完整性保护。 + +* RLC(Radio Link Control,无线链路控制)层:提供可靠的数据传输,实现数据分段和自动重传请求机制。 + +* MAC(Medium Access Control,媒体介入控制)层:负责数据调度和快速重传。 + +* L1层:在无线通信系统中,L1层通常是指物理层(PHY)。物理层的主要作用是进行信道编码和调制,并将数据发送到无线接口。 + + + +### 硬件数据流 + +上述主要是对蜂窝无线网络的分层结构进行一个简要描述。那么用户的数据具体是怎样一步步到达核心网以及互联网的呢?我们可以通过下面两个图示来理解。 + +#### 基于硬件结构的数据流 + +如下是基于蜂窝无线网卡硬件结构的数据流向图。基于该图,可以看到,用户数据在软件上先依次经过TCP/UPD层、IP层、PDCP层、RLC层、MAC层,最终达到物理层(即蜂窝无线网卡)。当数据到达基带部分,会被编码调制,并经过射频部分处理转换为电磁波这种模拟信号,最后再经过功率放大器处理后,通过射频天线发送出去,到达基站。再结合基于蜂窝无线网络分层结构的数据流向图,数据如何从UE达到目的地的过程就很明确了。 + +![](../../../media/network-comm/nic/cellular/硬件数据流.png) + + + +#### 基于分层结构的数据流 + +下面是基于蜂窝无线网络分层结构的数据和信令流程示意。 + +![](../../../media/network-comm/nic/cellular/数据流.png) \ No newline at end of file diff --git a/docs/Application_guide/zh/network-comm/nic/cellular/initialization.md b/docs/Application_guide/zh/network-comm/nic/cellular/initialization.md new file mode 100644 index 0000000000000000000000000000000000000000..361410c4f3c3bc6705c1fa6114abc014d2bbdfe8 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/cellular/initialization.md @@ -0,0 +1,97 @@ +# 蜂窝网卡初始化流程 + +蜂窝无线网卡的初始化,需要经历如下几个阶段: + +![无线网卡初始化流程](../../../media/network-comm/nic/cellular/网卡初始化流程.png) + +## 硬件初始化 + +这个阶段会先进行SIM卡的检测,判断是否插入了SIM卡。如果检测到插入SIM卡,会进行SIM卡相关初始化操作。对应上述流程图中的`Initialize SIM Card`。如果没有检测到SIM卡,则不会再继续进行后面的步骤。 + + + +## 网络搜索 + +当SIM卡初始化完成后,UE会搜索周边可能存在的无线信号,并进行小区搜索。我们将这个过程称之为“网络搜索”,对应上述流程图中的`Search Network`。 + + + +## 网络注册 + +UE在搜网过程中,找到合适的小区后,就会在该小区上发起附着流程(Attach procedure),我们将这个过程称之为“网络注册”,对应上述流程图中的`Network Register`。模组只有网络注册成功了,才能进行无线网卡的激活操作。 + +UE的网络搜索和网络注册过程,都是由协议栈自动进行的,用户无法介入该过程。但是用户可以通过一些方法来确定UE网络注册是否成功。下面介绍两种方式来判断UE网络注册是否成功。 + +**方法1:使用AT命令查询** + +用户可以发送如下AT命令来查询UE的网络注册状态: + +``` +AT+CREG? +# 如果网络注册成功,返回值如下,即第二个参数值为1或者5;如果是其他值则说明网络注册失败。 ++CREG: 0,1 +OK +``` + +步骤1:将终端设备通过USB数据线连接到电脑的USB端口上。 + +步骤2:在电脑上打开QPYcom工具,选择AT端口,按照图中所示选择配置参数并打开端口。 + +![AT查询网络注册状态](../../../media/network-comm/nic/cellular/QPYcom打开AT口.png) + +步骤3:发送AT查询指令,确认网络注册状态。 + +![网络注册成功](../../../media/network-comm/nic/cellular/AT查询网络注册成功.png) + + + +**方法2:使用QuecPython的API查询** + +用户可以使用QuecPython的`net.getState`方法来查询UE的网络注册状态。有关该方法的详细说明,请参考QuecPython官方网站wiki文档的[获取网络注册信息](https://python.quectel.com/doc/API_reference/zh/iotlib/net.html#%E8%8E%B7%E5%8F%96%E7%BD%91%E7%BB%9C%E6%B3%A8%E5%86%8C%E4%BF%A1%E6%81%AF)部分。 + +步骤1:将终端设备通过USB数据线连接到电脑的USB端口上。 + +步骤2:在电脑上打开QPYcom工具,打开QuecPython的REPL命令端口,进入到交互模式。 + +![打开QPYcom](../../../media/network-comm/nic/cellular/QPYcom打开REPL.png) + +步骤3:在命令交互行,按照如下方式调用API查询注网状态。 + +```python +import net +net.getState() +``` + +![注网状态查询](../../../media/network-comm/nic/cellular/API查询网络注册成功.png) + +当`net.getState`的返回值中,图中红框标注的参数值为`1`或者`5`的时候,表示网络注册成功。`1`表示注册的是归属地网络,`5`表示的注册的是漫游网络。 + +> 如果模组网络注册失败,请参考《网络异常处理》章节中如下部分进行排查: +> +> * [模组网络注册失败]() + + + +## 激活网卡 + +当UE网络注册成功后,就会进行蜂窝无线网卡的激活操作。只有蜂窝无线网卡激活成功,UE才能获取到由核心网分配的IP地址等信息,并为系统的TCP/IP协议栈创建一路虚拟网卡。只有网卡创建并激活成功才能进行socket、http、mqtt等网络业务。该步骤对应上述流程图中的`Activate NIC`。 + +用户可以通过`dataCall`模块的`dataCall.getInfo`方法来确定网卡是否激活成功,有关该方法的详细说明,请参考QuecPython官方网站wiki文档的[获取拨号信息功能](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html#%E8%8E%B7%E5%8F%96%E6%8B%A8%E5%8F%B7%E4%BF%A1%E6%81%AF%E5%8A%9F%E8%83%BD)部分。 + +查询方法: + +```python +dataCall.getInfo(profileID, ipType) +``` + +第一个参数`profileID`表示哪一路网卡,第二个参数`ipType`表示IP协议类型。由于QuecPython默认开机自动激活第一路蜂窝无线网卡,并且使用的IP协议类型为IPv4,所以查询时,`profileID`取值为1,`ipType`取值为0。 + +![拨号信息查询](../../../media/network-comm/nic/cellular/API查询拨号结果.png) + +当`dataCall.getInfo`方法的返回值中,网卡激活状态为`1`,并且IP不为`0.0.0.0`,说明网卡激活成功。 + + + +> 如果模组无线网卡激活失败,请先请参考《网络异常处理》章节中如下部分进行排查: +> +> * [开机时网络异常处理]() \ No newline at end of file diff --git a/docs/Application_guide/zh/network-comm/nic/cellular/mechanism.md b/docs/Application_guide/zh/network-comm/nic/cellular/mechanism.md new file mode 100644 index 0000000000000000000000000000000000000000..5a16fc72b7f26a141c37cd778df9cfedfb0bdcc3 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/cellular/mechanism.md @@ -0,0 +1,259 @@ +# QuecPython蜂窝网卡特有机制 + +本文主要说明QuecPython蜂窝无线网卡特有的一些处理机制,并说明这些机制的基本流程与原理。帮助用户更好的使用QuecPython的蜂窝无线网卡功能。 + +## 概述 + +QuecPython蜂窝无线网卡的处理机制主要包括如下3个部分: + +* 开机自动激活蜂窝无线网卡(下文中简称自动激活)。 + +* 蜂窝无线网卡自动恢复机制(下文中简称自动重连)。 + +* 蜂窝无线网卡配置参数保存。 + +下图是QuecPython蜂窝无线网卡开机自动激活的流程。总体而言,可以分为三个阶段: + +* 阶段1:根据`datacall_config.json`文件是否存在,决定开机时使能哪一路或者多路蜂窝无线网卡的自动激活和自动重连功能。 + +* 阶段2:根据用户是否配置了APN等网卡参数,决定使用什么参数来进行蜂窝移动网卡的激活。 + +* 阶段3:前面两个阶段主要是参数配置,这个阶段是实际的网卡激活操作。 + +![](../../../media/network-comm/nic/cellular/QuecPython网卡激活机制.png) + +下面将结合上述流程图来详细介绍QuecPython蜂窝无线网卡的各种处理机制。 + + + +## 开机自动激活网卡 + +开机自动激活蜂窝无线网卡,是指模组在开机后,系统在判断模组网络注册已经成功时,会自动进行蜂窝无线网卡激活的操作。 + +结合上面的流程图,可以看到,系统会先根据`datacall_config.json`文件是否存在,决定激活哪一路或者多路蜂窝无线网卡。有如下两种处理机制: + +* `datacall_config.json`文件不存在:系统会选择激活第一路蜂窝无线网卡。 + +* `datacall_config.json`文件存在:系统会根据该文件中的配置,来决定开机时激活哪一路或者多路蜂窝无线网卡。 + +系统决定了激活哪些蜂窝无线网卡后,在执行网卡激活操作之前,系统还会进一步确认用户是否为对应的蜂窝无线网卡配置了APN等参数。有如下两种处理机制: + +* 用户没有配置APN等参数:如果没有配置,则使用默认参数进行蜂窝无线网卡的激活。默认的参数配置如下: + +| 参数 | 默认值 | 说明 | +| -------- | ------ | ------------------------------ | +| ipType | 0 | 表示IPv4协议类型。 | +| apn | "" | 表示APN为空,即没有APN | +| username | "" | 表示用户名为空,即没有用户名。 | +| password | "" | 表示密码为空,即没有密码。 | +| authType | 0 | 表示不使用加密方式。 | + +> 使用默认的参数配置进行蜂窝无线网卡的激活,可能会导致激活失败。建议用户按照实际情况,配置正确的APN参数。 + +* 用户配置了APN等参数:如果用户配置了,则使用用户配置的APN参数进行蜂窝无线网卡的激活。 + + + +## 网卡自动恢复机制 + +蜂窝无线网卡自动恢复机制,是指模组在激活蜂窝无线网卡后,因为一些异常原因(如网络异常、环境干扰、信号差等)导致模组与网络的连接断开,当异常因素消失,模组在重新注册到网络后,自动重新激活蜂窝无线网卡的行为过程。我们也将这个机制称为蜂窝无线网卡自动重连机制。 + +结合上面的流程图,可以看到,系统会先根据`datacall_config.json`文件是否存在,决定使能哪一路或者多路蜂窝无线网卡的自动重连功能。有如下两种处理机制: + +* `datacall_config.json`文件不存在:系统会选择使能第一路蜂窝无线网卡的自动重连功能。 + +* `datacall_config.json`文件存在:系统会根据该文件中的配置,来决定使能哪一路或者多路蜂窝无线网卡的自动重连功能。 + +蜂窝无线网卡自动重连机制的基本工作原理如下: + +![](../../../media/network-comm/nic/cellular/自动重连基本流程.png) + +系统会监控设备与无线网络的连接状态,当网络连接断开了,引起网络异常的因素消失后,设备会自动发起附着流程(Attach procedure)重新注册到网络上。一旦注册成功,系统会按照之前的网卡配置参数重新激活网卡。 + +下面是一些会导致设备网络连接断开的常见情况: + +* 设备在一些信号很差的环境中,容易导致设备网络连接断开。比如在隧道中。 + +* 设备所处的环境,存在同频干扰或者其他干扰,影响设备与基站的正常通信,也会导致设备与网络连接断开。 + +* 设备接入的小区因为负载过重,即接入该小区的设备过多时,也可能会导致设备与网络连接断开。 + +* 无线网络发生异常,会导致设备与网络连接断开。 + + + +## 网卡信息保存 + +在QuecPython中,系统会保存蜂窝无线网卡相关的参数,具体可以分为两个部分: + +**自动激活和自动重连配置信息保存** + +通过【概述】部分的流程图可以看出,系统会根据`datacall_config.json`文件是否存在,决定开机时使能哪一路或者多路蜂窝无线网卡的自动激活和自动重连功能。这是因为`datacall_config.json`文件保存了每一路蜂窝无线网卡是否自动激活和自动重连的配置信息。其内容和格式如下: + +```json +{ + "1": {"autoActivate": 0, "autoConnect": 0}, + "2": {"autoActivate": 0, "autoConnect": 0}, + "3": {"autoActivate": 0, "autoConnect": 0} +} +``` + +配置文件中有3组配置信息,分别用于控制3路蜂窝无线网卡的自动激活和自动重连功能。其中`autoActivate`用于控制是否自动激活,`autoConnect`则用于控制是否自动重连。`autoActivate`和`autoConnect`的值为0表示关闭对应功能,为1表示开启对应功能。 + +用户可以通过下面两个方法来修改`datacall_config.json`文件: + +配置自动激活 + +```python +dataCall.setAutoActivate(profileID, enable) +``` + +配置自动重连 + +```python +dataCall.setAutoConnect(profileID, enable) +``` + +默认是不存在`datacall_config.json`文件的,只有用户使用`dataCall.setAutoActivate`或者`dataCall.setAutoConnect`方法配置过对应的功能,系统才会在模组的`usr`目录下创建`datacall_config.json`文件并保存用户配置信息,该文件不会因为设备断电而丢失。 + +
+ +**用户配置的APN等参数保存** + +用户为每一路蜂窝无线网卡配置的APN等参数,都会被保存到系统的NVM当中,不会因为设备断电而丢失。即使用下面的方法配置的信息会被保存: + +```python +dataCall.setPDPContext(profileID, ipType, apn, username, password, authType) +``` + + + +## checkNet机制 + +`checkNet`是QuecPython提供的一个功能模块。该模块主要用于检查网络是否以及就绪。关于该`checkNet`模块API的使用说明,请参考QuecPython官网wiki说明的[checkNet](https://python.quectel.com/doc/API_reference/zh/iotlib/checkNet.html)部分。 + +**`checkNet`使用场景** + +UE开机后入网流程是一个很复杂的过程,需要终端设备与基站以及核心网进行一系列交互,进行信息确认。这个过程会受很多因素影响,比如终端设备的射频性能(硬件设计、天线等)、周边环境、小区信号覆盖情况、基站负载情况等。因此在设备开机后,用户应用程序开始运行时,蜂窝无线网卡尚未激活成功是很常见也很正常的现象。 + +如果用户在其应用程序中,一开始就进行网络相关的业务操作,很可能因为蜂窝无线网卡尚未激活的原因而导致用户网络业务失败。因此我们建议用户在进行socket、http/https以及mqtt等网络相关的业务操作之前,先判断网络是否就绪。这里我们推荐用户使用`checkNet`模块的如下方法来检测网络是否就绪: + +```python +checkNet.waitNetworkReady(timeout) +``` + +推荐用户使用这个方法主要有以下原因: + +* 该方法会完整的检测SIM卡状态、设备网络注册状态以及蜂窝无线网卡激活状态。并且还可以设置超时时间,即在该超时时间内,如果检测到蜂窝无线网卡以及激活成功,则会立即返回,否则该方法会一直阻塞到超时才会退出。 + +* 该方法在超时退出时,会返回详细的错误码,告知用户当前蜂窝无线网卡没有激活,是在哪一个阶段失败。比如是SIM卡状态异常、设备网络注册失败或者在蜂窝无线网卡激活阶段失败。这样缩小了问题范围,可以帮助更快的确认问题。 + +**`checkNet`机制说明** + +`checkNet`检查网络是否就绪的流程如下图所示。分为3个阶段: + +* 阶段1:首先检查SIM卡的状态,确认SIM卡是否已经就绪。如果SIM卡已经就绪,则进入下一个阶段;否则等待SIM卡就绪,直到超时时间到了才返回。 + +* 阶段2:则开始检查设备的网络注册状态。如果设备已经注网成功,则进入下一个阶段;否则等待设备注网成功,直到超时时间到了才返回。 + +* 阶段3:开始检查蜂窝无线网卡的激活状态。如果蜂窝无线网卡已经激活成功,则直接返回检查结果;否则等待蜂窝无线网卡激活成功,直到超时时间到了才返回。 + + + +![](../../../media/network-comm/nic/cellular/checkNet机制.png) + + + + + +## 网络事件监听 + +网络事件监听是QuecPython蜂窝无线网卡特有的机制之一,旨在以高效及时的方式通知用户的应用程序,设备网络连接状态发生了变化。用户应用程序可以根据网络状态变化来及时的做出合适的处理。 + +之所以说这种机制高效及时,是因为QuecPython提供方法让用户应用程序去注册回调函数。当设备与无线网络的连接状态发生变化时,系统就会将对应的事件推送给用户的应用程序。相比于让用户应用程序经常主动的去查询设备网络连接状态,QuecPython这种网络监听机制则更加高效并且及时了。 + +用户可以通过下面的方法来注册监听回调函数: + +```python +dataCall.setCallback(fun) +``` + +关于该方法的详细说明,请参考QuecPython官网wiki说明中`dataCall`模块的[回调注册功能](https://python.quectel.com/doc/API_reference/zh/iotlib/dataCall.html#%E5%9B%9E%E8%B0%83%E6%B3%A8%E5%86%8C%E5%8A%9F%E8%83%BD)部分,这里主要说明如何使用。 + +回调函数的示例如下: + +```python +def network_event_callback(args): + profileID = args[0] + netState = args[1] + if netState == 0: + print('### network {} disconnected.'.format(profileID)) + elif netState == 1: + print('### network {} connected.'.format(profileID)) +``` + +该回调函数的参数是一个元组,包含3个元素,目前用户只需要关注前两个元素即可。前两个参数说明如下: + +| 参数 | 类型 | 说明 | +| ------- | ---- | ------------------------------------------------------------ | +| args[0] | 整型 | 蜂窝无线网卡编号,表示当前是哪一路无线网卡的网络连接状态发生了变化。 | +| args[1] | 整型 | 网络状态,0表示网络连接断开,1表示网络连接成功。 | + +下面是一个简单的示例,在该示例代码中,我们注册了网络事件监听的回调函数,然后在回调函数中打印网络状态。并且通过将模组先切换到飞行模式,来模拟网络网络异常导致的连接断开;再切回正常模式,来模拟网络网络异常因素消失后,网络连接重新恢复的过程。 + +```python +import net +import utime +import dataCall + + +def network_event_callback(args): + profile_id = args[0] + conn_state = args[1] + ret = dataCall.getInfo(1, 0) + if conn_state == 0: + print('### NIC{} network disconnected.'.format(profile_id)) + elif conn_state == 1: + print('### NIC{} network connected.'.format(profile_id)) + else: + print('Unknown event.') + print('Current NIC information:{}'.format(ret)) + + +def main(): + # 注册网络事件监控回调函数 + dataCall.setCallback(network_event_callback) + ret = dataCall.getInfo(1, 0) + print('Current NIC information:{}'.format(ret)) + # 切换到飞行模式,会导致模组网络连接断开 + net.setModemFun(4) + utime.sleep(5) + # 切换到正常模式,此时模组会重新进行网络注册并激活无线网卡 + net.setModemFun(1) + + +if __name__ == '__main__': + main() +``` + +上面的示例仅仅是为了让用户直观的感受一下QuecPython的网络事件监控功能。如果用户需要在实际项目中使用,请参考后续《网络异常处理》章节中“网络异常事件处理示例”部分。 + + + +## 多路蜂窝无线网卡 + +多路蜂窝无线网卡指的是模组为了满足用户设备同时接入多个网络的需求,从软件上虚拟出来多张蜂窝无线网卡。 + +### 场景 + +前面我们提到,APN是终端入网时必须配置的一个参数,它决定了终端通过哪种接入方式来访问网络。而不同的接入点所能访问的范围以及接入的方式是不同的,即APN决定了终端设备通过哪种接入方式来访问什么样的网络。那么对于终端设备来说,能否配置多个不同的APN,让终端设备可以同时连接到不同的网络服务呢?答案是肯定可以的。 + +因此,当用户需要他们的产品能在连接公共网络(Internet)的同时,还需要能接入一些专用网络时,就需要使用到多路蜂窝无线网卡。此时需要给不同的网卡配置不同的APN,并激活这些网卡,即可实现用户需求。 + +![](../../../media/network-comm/nic/cellular/多路蜂窝无线网卡.png) + +### 说明 + +QuecPython各个平台的模组都可以虚拟出多路蜂窝无线网卡,为了平台的统一性,开放给用户使用的网卡有3路,即用户可以同时激活和使用3路网卡,这3路网卡没有其他特殊用途。同时,各个平台的VoLTE功能也会占用一路网卡,具体占用哪一路,各个平台不一样,但是不会占用开放给用户使用的3路网卡。 + +> BG77/BG95系列的模组,在NB网络制式下,实际只支持激活最多2路网卡。 \ No newline at end of file diff --git a/docs/Application_guide/zh/network-comm/nic/ethernet/README.md b/docs/Application_guide/zh/network-comm/nic/ethernet/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f65fcd96f6ada56c0734368fa4541d9f92a95afa --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/ethernet/README.md @@ -0,0 +1,292 @@ +## 6.5 以太网卡 + +本章主要介绍 QuecPython 下以太网卡适配情况,以及如何使用。 + +目录 +[以太网卡介绍](#click_jump_6.5.1) + [QuecPython 支持的以太网卡硬件接口](#click_jump_6.5.1.1) +  [SPI](#click_jump_6.5.1.1.1) +    [硬件结构](#click_jump_6.5.1.1.1) +    [初始化流程](#click_jump_6.5.1.1.1) +  [RMII](#click_jump_6.5.1.1.2) +    [硬件结构](#click_jump_6.5.1.1.2) +    [初始化流程](#click_jump_6.5.1.1.2) + [WAN/LAN](#click_jump_6.5.1.2) + [数据流](#click_jump_6.5.1.3) + [各平台接口支持情况](#click_jump_6.5.1.4) +[Ethernet API 说明](#click_jump_6.5.2) +[应用示例](#click_jump_6.5.3) + [模块通过以太网卡使用外部网络](#click_jump_6.5.3) + [其他设备通过以太网卡使用蜂窝无线网络](#click_jump_6.5.3) +[常见异常处理](#click_jump_6.5.4) + + + +## 以太网卡介绍 + +以太网是一种计算机局域网技术。IEEE(Institute of Electrical and Electronics Engineers,电气与电子工程师协会)组织的 802.3 标准制定了以太网的技术规范,它规定了包括物理层的连线、电子信号和介质访问控制的内容。以太网是目前应用最普遍的局域网技术。以太网在生活中随处可见,电脑上的以太网口,路由器上的以太网口等。 + +**QuecPython 上是如何支持以太网的?** + +网络通信一般基于 TCP/IP 协议进行构建的,而 QuecPython 则支持轻量级的 TCP/IP 协议栈--lwIP。根据支持 QuecPython 的模组的以太网接口特性,可以分为两种方式: + +- QuecPython 模组 + 以太网芯片 (MAC + PHY,如 W5500/CH395 等)。 +- QuecPython 模组 + PHY 芯片 (如 YT8512/JL1101 等)。 + +![](../../../media/network-comm/nic/network_eth_spi_adapter.png) + +![](../../../media/network-comm/nic/network_eth_rmii_adapter.png) + + + + +### QuecPython 支持的以太网卡硬件接口 + + + +#### SPI 接口 + +>SPI 是串行外设接口(Serial Peripheral Interface)的缩写,是美国摩托罗拉公司最先推出的一种同步串行传输规范,也是一种单片机外设芯片串行扩展接口,是一种高速、全双工、同步通信总线,所以可以在同一时间发送和接收数据,通常能达到甚至超过 10M/bps。 + +对于不支持 MAC 层的 QuecPython 模组,可以通过 SPI 硬件接口外挂支持 MAC + PHY 的芯片。当前已支持多种型号,如 W5500/DM9051/CH395。 + +**硬件结构** +SPI 接口硬件连接图如下所示: + +![](../../../media/network-comm/nic/network_eth_spi.png) + +| 引脚 | 说明 | +| ---- | ------- | +| `SCLK` | `SPI`时钟引脚 | +| `MISO` | `SPI`主机输入从机输出引脚 | +| `MOSI` | `SPI`主机输出从机输入引脚 | +| `CS` | `SPI`片选引脚,此管脚也可以自定义配置 | +| `PIN_X` | 自定义引脚,用于数据接收中断触发 | +| `PIN_Y` | 自定义引脚,用于芯片`reset`控制 | + +**初始化流程** + +网卡初始化加载流程如下图所示: + +![](../../../media/network-comm/nic/network_eth_spi_init_flow.png) + + + +#### RMII 接口 + +>RMII(Reduced Media Independant Interface)即简化媒体独立接口,是标准的以太网接口之一,比 MII 有更少的 I/O 传输。是简化的 MII 接口,在数据的收发上它比 MII 接口少了一倍的信号线(2数据位)。它包括一个数据接口,以及一个 MAC 和 PHY 之间的管理接口。和 MII 一样,RMII 支持 10Mbps 和 100Mbps 的总线接口速度。 + +对于支持 MAC 层的 QuecPython 模组,也可以通过 RMII 硬件接口外挂 PHY 芯片。当前已支持多种型号,如 YT8512/SZ18201/JL1101。 + +**硬件结构** + +RMII 接口的硬件连接图如下所示: +![](../../../media/network-comm/nic/network_eth_RMII.png) + +| 引脚 | 说明 | +| ---- | ------- | +| `RMII_RX_1` | 接收数据位1 | +| `RMII_CTL_RX` | 数据接收控制 | +| `RMII_CLK` | 时钟线 | +| `RMII_RX_0` | 接收数据位0 | +| `RMII_TX_0` | 发送数据位0 | +| `RMII_TX_1` | 发送数据位1 | +| `RMII_CTL_TX` | 发送数据控制 | +| `RMII_INT` | 突发数据中断线 | +| `RMII_MD_IO` | 管理数据线 | +| `RMII_MD_CLK` | 管理数据时钟线 | +| `RMII_RST_N` | 芯片复位线 | + +**初始化流程** + +网卡启动初始化加载流程如下图所示: + +![](../../../media/network-comm/nic/network_eth_rmii_init_flow.png) + + + +### 广域网 (WAN) 与 局域网 (LAN) + +广域网(Wide Area Network,缩写WAN)一般指外网、公网。是链接不同地区局域网或者城域网计算机通信的远程网络。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个城市或国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。 + +局域网(Local Area Network,缩写LAN)一般指内网、私网,是局部地区形成一个区域网络,其特点就是分布地区范围有限,可大可小,大到一栋建筑与相邻建筑之间的连接,小到可以是办公室之间的联系。 + +以太网根据工作方式可以分为 WAN 口和 LAN 口,WAN是外网接接入入口,对外连接网络,LAN是局域网输出接口,连接局域网设备,形成私有局域网网络,常见的设备如路由器。 + +QuecPython 支持对以太网口的工作方式进行配置,根据工作环境将切换到不同的工作方式。 + + + +### 数据流 + +QuecPython 下网口配置不同的工作方式,具有不同的工作流程,终端模式(WAN)主要是将模块通过以太网,连接外部设备接入外网,网关模式(LAN)主要为局域网设备提供 4G 网络。 + +1. 终端模式(WAN) + +将网卡配置成终端模式,此时网口作为WAN口,为模块提供网络,模块可以通过以太网对外网进行访问。 + +![](../../../media/network-comm/nic/network_eth_node.png) + +2. 网关模式(LAN) + +将网卡配置成网关模式,此时网口作为LAN口,模块为LAN局域网内设备提供网络,通过网络转发,其他模块可以通过模块 4G 网络对外进行网络访问。 + +![](../../../media/network-comm/nic/network_eth_gateway.png) + + + +### 各平台接口支持情况 + +下表是各平台型号对于 SPI 接口以太网及 RMII 接口 PHY 适配支持情况。不同平台对于支持以太网卡型号目前不一致,请根据WIKI下[ethernet]()章节介绍,进行以太网卡选型。 + +| 型号\接口类型 | SPI-ETHERNET | RMII-PHY | +| :-: | :-: | :-: | +| `EC200A` | ✕ | ✓ | +| `EC600N` | ✓ | ✕ | +| `EC600U` | ✓ | ✕ | +| `EC600M/EC800M` | ✓ | ✕ | + +>注释: +> - ✓: 支持 +> - ✕: 不支持 + + + + +## Ethernet API 说明 + +QuecPython 对于外挂 RMII 接口 PHY 设备或者外挂 SPI 接口太网卡设备提供了统一的的API接口。 + +以太网接口调用流程图如下所示: +![](../../../media/network-comm/nic/network_eth_api_process.png) + +### 选择以太网卡 + +以太网均被包含在 ethernet 模块中,不同类型网卡加载有所区别,各模块加载参数也有所差异,请参考 WIKI 下 [ethernet]() 模块各网卡初始化传参含义,各以太网卡加载方式如下所示。 + +**加载 W5500** +为了方便W5500适配,对于W5500接口设置较多自定义参数,方便灵活对接。 +```python +nic = ethernet.W5500(mac, ip='', subnet='', gateway='', spi_port=-1, spi_cs_pin=-1, extint_pin=-1, reset_pin=-1, work_mode=0) +``` + +**加载 DM9051** +```python +nic = ethernet.DM9051(mac, ip='', subnet='', gateway='', spi_port=-1, spi_cs_pin=-1, work_mode=0) +``` + +**加载 CH395** +```python +nic = ethernet.CH395(mac, ip='', subnet='', gateway='', spi_port=-1, spi_cs_pin=-1, extint_pin=-1, reset_pin=-1, work_mode=0) +``` + +**加载 YT8512/SZ18201/JL1101** +```python +nic = ethernet.YT8512H(mac, ip='', subnet='', gateway='') +``` + +对于不同的型号加载规则都是相同的,只是参数有所差异,以下各型号对于各参数适配情况介绍。 + +| 参数 | 含义 | 各型号适配情况 | 备注 | +| --- | --- | --- | --- | +| `mac` | 字节流,6字节长度的 `mac` 地址。 | 均适配该参数 | 此处 mac 自定义,需要自行根据以太网 MAC 地址规则进行匹配使用。 | +| `ip` | 以太网卡的 `ip` 地址,若值为空字符串'',表示使用默认值`192.168.1.100`。 | 均适配该参数 | 用于初始化配置网卡 ip 相关信息 | +| `subnet` | 以太网卡的子网掩码地址,若值为空字符串'',表示使用默认值`255.255.255.0`。 | 均适配该参数 | 用于初始化配置网卡 ip 相关信息 | +| `gateway` | 以太网卡的网关地址,若值为空字符串'',表示将 `ip` 地址的最后一位替换成`1`作为网关。 | 均适配该参数 | 用于初始化配置网卡 ip 相关信息 | +| `spi_port` | 连接 W5500 的[SPI端口](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.SPI.html),默认值为`-1`,表示使用上次配置的值,程序中默认配置为 `SPI1` 端口。 | W5500/DM9051/CH395均适配 | SPI-ETHERNET 外挂设备均适配 SPI 可选接口。RMII 接口使用接口是固定的,无需适配。 | +| `spi_cs_pin` | 连接 W5500 的 SPI 片选[GPIO管脚](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.Pin.html),默认值为`-1`,表示使用上次配置的值,程序中默认配置参考 WIKI 下具体型号。 | W5500/DM9051/CH395已适配 | SPI-ETHERNET 外挂设备均适配 SPI CS 可选PIN脚。RMII 使用接口是固定的,无需适配。 | +| `extint_pin` | 连接 W5500 的外部中断[GPIO管脚](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.Pin.html),默认值为`-1`,表示上次配置的值,程序中默认配置参考 WIKI 下具体型号。 | W5500/CH395已适配 | DM9051尚未适配中断方式数据处理。
YT8512/SZ18201/JL1101 RMII 接口内部固定中断脚,不可更改。| +| `reset_pin` | 连接 W5500 的重置[GPIO管脚](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.Pin.html),默认值为`-1`, 表示上次配置的值,程序中默认配置参考 WIKI 下具体型号。 | W5500/CH395 已适配 | DM9051 尚未适配。
YT8512/SZ18201/JL1101 RMII 接口内部固定 RST 脚,不可更改。 | +| `work_mode` | 以太网工作模式配置,默认为终端模式,`0`/`1` 分别表示终端模式/网关模式。终端模式表示该模块作为终端设备连接供网设备上网。网关模式表示该模块作为网关,为外部设备提供网络访问,通过`4G`上网。 | W5500/DM9051/CH395 均适配 | YT8512/SZ18201/JL1101 尚未适配,不限制工作默认配置,自行调用接口确认工作模式。 | + +### 动态获取 IP 地址 + +在大部分网络环境中都是 dhcp 的方式获取 ip 信息,当前提供 dhcp 接口,方便在终端模式(WAN)下,自动获取 ip 信息,如以太网外接到路由器进行网络连接,此时直接通过调用此接口即可获取到路由器分配的 ip 地址。 + +```python +nic.dhcp() +``` + +### 设置 IP 地址 + +不同的网络环境,需要不同的 IP 地址配置加入网络,故提供网卡静态IP配置接口,方便用户根据不同的网络环境进行配置使用,使得网络连通。比如为了固定设备 ip 或者组网环境内没有dhcp server可以分配 ip 情况下可以使用该接口进行配置。 + +```python +nic.set_addr(ip, subnet, gateway) +``` + +### 设置 DNS 地址 + +在网络中,域名解析需要使用的 DNS 服务器,将对应的域名转换成 IP 地址进行通信,提供 DNS 服务器地址配置接口,可以让用户根据自己的环境灵活配置 DNS 服务器地址。 + +```python +nic.set_dns(primary_dns, secondary_dns) +``` + +### 启动网卡 + +在网络环境中可以根据需要对网卡进行开启,此时网卡上的数据将进行处理,方便用户对网卡使用进行管理。 + +```python +nic.set_up() +``` + +### 停止网卡 + +在网络环境中可以根据需要对网卡进行禁用,此时网卡上的数据将不会进行处理,方便用户对网卡使用进行管理。 + +```python +nic.set_down() +``` + +### 获取网卡网络信息 + +在网络环境中可以根据需要对网卡进行禁用,此时网卡上的数据将不会进行处理,方便用户对网卡使用进行管理。 + +```python +nic.ipconfig() +``` + +### 配置默认网卡 + +默认网卡对于模块网络通信以及网络转发有效。 +1. 对于模块进行 socket 通信,模块会进行路由查找,匹配对应的网卡进行发送,如果不进行 bind 网卡发送,此时网络会通过默认网卡进行发送,如果默认网卡不存在则会报错退出。 +2. 对于多网卡下,网络转发进行路由查找由哪个网卡发出时,对于非本机数据,如果没有默认网卡,将直接丢弃,如果配置默认网卡将通过默认网卡进行转发。 + +```python +nic.set_default_NIC(ip) +``` + + + +## 应用示例 + +下面以 QuecPython EC600NCN_LE 开发板(下称开发板)通过 SPI1 连接 W5500 使用ethernet功能,分别以终端模式(WAN)及网关模式(LAN)介绍以太网的应用。 + +[模块通过以太网卡使用外部网络](./ethernet-wan-example.md) + +[其他设备通过以太网卡使用蜂窝无线网络](./ethernet-lan-example.md) + + + +## 常见问题 + +**1. SPI-ETHERNET 设备初始化时报错EIO 5?** + +> 此报错说明SPI接线有问题,在检查以太网设备时不存在导致,请确实以太网型号是支持型号以及SPI接线、中断脚连接是否正常。 + +**2. RMII-PHY 设备初始化时报错OS 3?** + +> 此报错是由于开机启动未初始化 MAC 层导致,MAC 层默认开机不初始化,使用 PHY 芯片时,会在第一次初始化时报该错误,重启后会初始化 MAC 层再启动网卡则正常使用。此后如果销毁网卡,则会再触发此问题。故遇到该问题直接重启设备就好。 + +**3. 支持哪些以太网芯片?** + +> 目前我们支持 SPI 接口以太网芯片 W5500/DM9051/CH395,RMII 接口 PHY 芯片 YT8512/SZ18201/JL1101。详细适配情况请参考[各平台接口支持](#click_jump_6.5.1.4)情况章节。 + +**4. 以太网速率能到多少?** + +> 不同芯片平台下均有差异,不同的芯片平台速率支持可能受限于 4G 网络速率,可能受限于硬件接口传输速率,要根据具体平台而定,就目前而言W5500/DM9051/CH395支持速率基本在2Mbps左右上下行速率,PHY芯片YT8512/SZ18201/JL1101基本保持在下行40Mbps,上行10Mbps。 + +**5. 以太网芯片是多少M的?** + +> 就以太网芯片而言都是支持 10/100M 的。 \ No newline at end of file diff --git a/docs/Application_guide/zh/network-comm/nic/ethernet/ethernet-lan-example.md b/docs/Application_guide/zh/network-comm/nic/ethernet/ethernet-lan-example.md new file mode 100644 index 0000000000000000000000000000000000000000..5a8ea1b9078ec9cf58eb445ddc70facfba6ea706 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/ethernet/ethernet-lan-example.md @@ -0,0 +1,75 @@ + +# 其他设备通过以太网卡使用蜂窝无线网络 + +W5500 作为网关使用配置时,使用 4g 连接外网。比如 w5500 网口连接电脑,电脑需要 +配置静态 ip 与 w5500 以太网卡同一网段,网关与 w5500 网卡地址一致,使得电脑能够正常通过 4g 网卡连接网络。 + +## 硬件准备工作 + +使用外挂以太网功能,需要额外准备外挂的以太网卡、网线、路由器、杜邦线。 + +通过杜邦线将模块与以太网卡连接,模块与网卡接线可以根据实际需求配置,具体接口配置参数请参考[ethernet]()章节,然后通过网线将网卡与路由器连接。 + +[W5500硬件连接实物图](../../../media/network-comm/nic/network_eth_demo_1.png) +[DM9051硬件连接实物图](../../../media/network-comm/nic/network_eth_demo_1.png) +[CH395硬件连接实物图](../../../media/network-comm/nic/network_eth_demo_1.png) +[PHY硬件连接实物图](../../../media/network-comm/nic/network_eth_demo_1.png) + +## 软件准备 + +**网卡初始化** + +交互口输入以下命令初始化以太网设备,配置网卡以网关模式(LAN)使用。 +```python +# 网关模式需要使用到拨号信息,所以多导入一个dataCall包 +>>> import ethernet +>>> import dataCall + +# 初始化网卡,配置各个参数信息,此处填写的CS脚、RST脚、INT脚均为GPIO引脚编号(GPIO引脚编号与物理引脚的映射关系请参考Wiki) +>>> eth = ethernet.W5500(b'\x12\x34\x56\x78\x9a\xbc','192.168.1.1','','',-1,38,36,37,1) + +# 启动以太网卡 +>>> eth.set_up() +0 + +``` + +**路由配置** + +路由配置功能主要是不同网卡下进行网络转发的方法,当前只是通过默认网卡接口进行配置,默认网卡作为转发对象,对于通过 LAN 口非本机数据,将通过默认网卡进行 NAT 转发。 + +交互口输入以下命令配置路由转发为 LAN 设备提供 4G 网络。 +```python +# 网关模式需要使用到拨号信息,所以多导入一个dataCall包 +>>> import dataCall + +# 获取当前 4g 拨号 ip 信息 +>>> info=dataCall.getInfo(1, 0) + +# 查看是否有获得4G的IP +>>> print(info) +(1, 0, [1, 0, '10.62.209.177', '211.138.180.4', '211.138.180.5']) + +# 设置默认网卡,网关模式下设置 4g 为默认网卡 +>>> eth.set_default_network_card(info[2][2]) +0 + +# 查询以太网卡 ip 信息 +>>> eth.ipconfig() +[('12-34-56-78-9A-BC', 'W5500'), (4, '192.168.1.100', '255.255.255.0 ', '192.168.1.1', '8.8.8.8', '114.114.114.114')] + +``` + +## 对端配置 + +以PC为例,此时PC端是无法进行网络通信的,还需对端配置静态IP,与模组静态ip配置在同一个网段方可正常通信。 +右键`以太网2`,选择属性,点击`Internet协议版本4(TCP/IPv4)`,取消自动获得IP地址和自动获得DNS服务器地址,改为如下图所示的静态IP地址,点击确定。 +> 注:此处电脑也可以通过动态ip获取信息,部分模块未内置dhcp server需要通过静态ip的方式进行网络配置。 + +![](../../../media/network-comm/nic/network_eth_demo_3.png) + +## 对端通过模组以太上网 + +此时可以通过电脑浏览器访问网页。比如浏览器输入 https://python.quectel.com 即可正常访问。 + +![](../../../media/network-comm/nic/network_eth_gateway_web.png) diff --git a/docs/Application_guide/zh/network-comm/nic/ethernet/ethernet-wan-example.md b/docs/Application_guide/zh/network-comm/nic/ethernet/ethernet-wan-example.md new file mode 100644 index 0000000000000000000000000000000000000000..c37943b0a2b95576555e0bbf09fd45d90bc7534c --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/ethernet/ethernet-wan-example.md @@ -0,0 +1,177 @@ +# 模块通过以太网卡使用外部网络 + +模块通过以太网上外网,需要将以太网配置到网关模式(LAN)。比如以太网口连接路 +由器,通过 dhcp 获取 ip 信息,从而通过以太网连接外部网络,实现板子能够正常连接网络。 + +开发环境搭建请参考[快速入门章节](),本章节主要是对以太网使用介绍。 + +## 硬件准备工作 + +使用外挂以太网功能,需要额外准备外挂的以太网卡、网线、路由器、杜邦线。 + +通过杜邦线将模块与以太网卡连接,模块与网卡接线可以根据实际需求配置,具体接口配置参数请参考[ethernet]()章节,然后通过网线将网卡与路由器连接。 + +[W5500 硬件连接实物图](../../../media/network-comm/nic/network_eth_demo_1.png) +[DM9051 硬件连接实物图](../../../media/network-comm/nic/network_eth_demo_1.png) +[CH395 硬件连接实物图](../../../media/network-comm/nic/network_eth_demo_1.png) +[PHY 硬件连接实物图](../../../media/network-comm/nic/network_eth_demo_1.png) + +## 软件准备 + +**网卡初始化** + +默认以太网不开启,需要手动进行打开,以下是加载以太网过程,通过dhcp的方式获取ip信息与外部通信。 +以下示例介绍针对 W5500 以太网卡进行介绍,其他网卡在初始化时,调用该型号类即可,具体参数配置请参考 [ethernet]() 模块下具体型号类。 + +```python +# 首先导入ethernet包 +>>> import ethernet + +# 初始化网卡,配置各个参数信息,此处填写的CS脚、RST脚、INT脚均为GPIO引脚编号(GPIO引脚编号与物理引脚的映射关系请参考Wiki) +>>> eth = ethernet.W5500(b'\x12\x34\x56\x78\x9a\xbc','','','',-1,38,36,37, 0) + +# 网卡注册成功后,查看默认的静态 IP 地址 +>>> eth.ipconfig() +[('12-34-56-78-9A-BC', 'W5500'), (4, '192.168.1.100', '255.255.255.0 ', '192.168.1.1', '8.8.8.8', '114.114.114.114')] + +# 启动以太网卡 +>>> eth.set_up() +0 + +# 此时可以通过以太网对访问外部网络。 + +``` + +**网络配置** + +网络配置可以通过两种方式配置,一种 dhcp 方式获取,或者通过提供的静态 ip 配置接口进行配置。 + +DHCP + +```python +# 首先导入ethernet包 +>>> import ethernet + +# 初始化网卡,配置各个参数信息,此处填写的CS脚、RST脚、INT脚均为GPIO引脚编号(GPIO引脚编号与物理引脚的映射关系请参考Wiki) +>>> eth = ethernet.W5500(b'\x12\x34\x56\x78\x9a\xbc','','','',-1,38,36,37, 0) + +# 网卡注册成功后,查看默认的静态 IP 地址 +>>> eth.ipconfig() +[('12-34-56-78-9A-BC', 'W5500'), (4, '192.168.1.100', '255.255.255.0 ', '192.168.1.1', '8.8.8.8', '114.114.114.114')] + +# 开启 DHCP +>>> eth.dhcp() +0 + +# DHCP 成功后,再次查看 IP 地址,可以发现IP地址已更新。 +>>> eth.ipconfig() +[('12-34-56-78-9A-BC', 'W5500'), (4, '192.168.31.203', '255.255.255. 0', '192.168.31.1', '192.168.31.1', '0.0.0.0')] + +# 启动以太网卡 +>>> eth.set_up() +0 + +# 此时可以通过以太网对访问外部网络。 +``` + +静态IP配置 + +```python +# 首先导入ethernet包 +>>> import ethernet + +# 初始化网卡,通过传参 ip 信息,启动时会使用 ip 传参而不适用默认 ip 信息。 +>>> eth = ethernet.W5500(b'\x12\x34\x56\x78\x9a\xbc','192.168.2.10','255.255.255.0','192.168.2.1',-1,38,36,37, 0) + +# 网卡注册成功后,查看默认的静态 IP 地址 +>>> eth.ipconfig() +[('12-34-56-78-9A-BC', 'W5500'), (4, '192.168.2.10', '255.255.255.0 ', '192.168.1.1', '8.8.8.8', '114.114.114.114')] + +# 设置静态ip +>>> eth.set_addr('192.168.3.10','255.255.255.0','192.168.3.1') +0 + +# 设置dns +>>> eth.set_dns('114.114.114.114','8.8.8.8') +0 + +# 配置完成后,再次查看 IP 地址,可以发现IP地址已更新。 +>>> eth.ipconfig() +[('12-34-56-78-9A-BC', 'W5500'), (4, '192.168.3.10', '255.255.255.0', '192.168.3.1', '114.114.114.114', '8.8.8.8')] + +# 启动以太网卡 +>>> eth.set_up() +0 + +# 此时可以通过以太网对访问外部网络。 + +``` + +## TCP(socket)应用示例 + +以下示例展示 TCP 通信时,通过以太网进行通信时的两种方法。 + +**TCP 客户端绑定以太网卡通信** + +此例中通过以太网进行 TCP 通信,通过 socket 模块内 bind 接口,指定通过以太网口进行发送。 + +>注:下例中 bind 接口绑定 192.168.1.100 指以太网的 ip 地址。 + +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + sock.settimeout(5) + sock.bind(("192.168.1.100", 0)) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + +**TCP 客户端非绑定以太网卡通信** + +此例中通过以太网进行 TCP 通信,不使用 socket 模块内 bind 接口,通过配置默认网卡进行 socket 通信。需要确定配置以太网卡作为默认网卡。 + +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + sock.settimeout(5) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + +## MQTT/HTTP 等应用协议应用 + +需要注意通过以太网卡进行 MQTT/HTTP 应用协议,由于内置的 MQTT/HTTP 通信模块没有使用绑定网卡操作接口,需要配置以太网卡作为默认网卡即可。具体 MQTT/HTTP 使用请参考应用层协议 WIKI 下 [umqtt]() 以及 [reqeust]() 章节。 diff --git a/docs/Application_guide/zh/network-comm/nic/network-config.md b/docs/Application_guide/zh/network-comm/nic/network-config.md new file mode 100644 index 0000000000000000000000000000000000000000..85deab3e0de6a867a59b8eb371fd2242b4207d05 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/network-config.md @@ -0,0 +1,746 @@ +# 6.7 配网 + +> 本章节主要介绍 QuecPython 下如何进行网络配置连接。当前QuecPython支持多种类型的网卡,不同的网卡有不同的使用方式,以及多网卡情况下网卡的使用切换及路由配置,本章节将逐一介绍,方便对网络配置应用了解。 + +目录 +[配网介绍](#click_jump_6.7.1) +  [蜂窝无线网卡](#click_jump_6.7.1.1) +  [Wi-Fi无线网卡](#click_jump_6.7.1.2) +  [以太网卡](#click_jump_6.7.1.3) +  [USB网卡](#click_jump_6.7.1.4) +[简单场景](#click_jump_6.7.2) +  [使用默认蜂窝网卡传输数据](#click_jump_6.7.2.1) +  [使用多张蜂窝网卡并指定网卡传输数据](#click_jump_6.7.2.2) +  [使用以太网卡传输数据](#click_jump_6.7.2.3) +  [使用 Wi-Fi 网卡传输数据](#click_jump_6.7.2.4) +[路由配置讲解](#click_jump_6.7.3) +[多网卡下网络配置](#click_jump_6.7.4) +  [Wi-Fi(AP)->LTE(单路)](#click_jump_6.7.4.1) +  [Ethernet(LAN)-> LTE(单路)](#click_jump_6.7.4.2) +  [Wi-Fi(AP)-> LTE(多路)](#click_jump_6.7.4.3) +  [Ethernet(LAN)-> LTE(多路)](#click_jump_6.7.4.4) +[复杂场景案例](#click_jump_6.7.5) +[常见问题](#click_jump_6.7.6) +  [Socket如何和网卡绑定?](#click_jump_6.7.6.1) +  [如何测试网络链路通达?](#click_jump_6.7.6.2) + + + +## 配网介绍 + +QuecPython 支持的网卡类型较多,如何配置网卡,使其能够正常地进行网络通信呢?本章节主要对于网卡配置进行简单介绍,方便快速使用网卡设备进行连接网络。 + +QuecPython 提供了两种网卡配置方式: +1. 通过 python 接口进行配置。 +2. 通过内置的 web 服务,使用网页进行网络配置。 +> 注1:只有蜂窝无线网络的模组,无法使用 web 服务。 + +![](../../media/network-comm/nic/network_config_cmd.png) + +![](../../media/network-comm/nic/network_config_web.png) + + + +### 蜂窝无线网卡 + +蜂窝无线网卡依赖于运营商,需要模组连接运营商的SIM卡,并且在运营商基站信号覆盖范围内,方可进行网络连接。支持 QuecPython 的蜂窝通信模组在开机后会自动进行蜂窝数据网络连接。 + +![](../../media/network-comm/nic/network_4g_single.png) + +**示例:** + +```python +>>> import dataCall +>>> dataCall.setPDPContext(1, 0, '', '', '', 0) # 设置网卡信息 +0 +>>> dataCall.activate(1) # 激活第一路网卡 +0 +>>> dataCall.getInfo(1, 0) # 第一路拨号信息查询 +(1, 0, [1, 0, '10.11.129.252', '211.138.180.4', '211.138.180.5']) + +# 现在模块可以通过 4G 网络正常进行网络连接。 +``` + + + +### Wi-Fi 无线网卡 + +Wi-Fi 无线网卡不同的工作模式下具有不同的应用场景,对于 station 模式下,需要连接Wi-Fi 热点(比如路由器),通过 Wi-Fi 热点给模块提供网络。对于 ap 工作模式下,Wi-Fi 无线网卡作为热点,接受其它 Wi-Fi 站点设备连接,并为其提供网络服务。 + +Wi-Fi 无线网卡的网络配置有多种方式,如直接输入热点名称和密码、一键配网、AP 配网、web 页面配置等,但最终的目的就是为了成功获取热点名称和密码。本文着重说明 Wi-Fi 网卡的加载,并通过直接访问热点的方式进行网络连接。其余的配网方式请参考 [Wi-Fi 网卡](../nic/WIFI/README.md) 章节。 + +![](../../media/network-comm/nic/network_wifi_station.png) + +![](../../media/network-comm/nic/network_wifi_ap.png) + +**示例:** + +通用 Wi-Fi 网卡配网 + +```python +>>> import network +>>> nic = network.WLAN(network.STA_IF) # 加载 Wi-Fi 网卡驱动,对于外挂设备需要确认是否还需要其他参数配置。 +0 +>>> nic.connect('ssid','password') # 连接 Wi-Fi 热点。 +0 +>>> nic.status() # 查询连接状态,5表示已连接。 +5 +>>> nic.ifconfig() # 查询 ip 信息。 +('192.168.1.4', '255.255.255.0', '192.168.1.1', '192.168.1.1') + +# 现在模块可以通过 Wi-Fi 网络正常进行网络连接。 +``` + +外挂 ESP8266 Wi-Fi 网卡配网 + +```python +>>> from usr.WLAN import ESP8266 +>>> from machine import UART +>>> esp8266 = ESP8266(UART.UART2, ESP8266.STA) # 初始化 esp8266 网卡。 +0 +>>> esp8266.station('ssid','password') # 连接 Wi-Fi 热点。 +0 +>>> esp8266.set_dns('8.8.8.8','114.114.114.114') # 配置 dns server。 +0 +>>> esp8266.status() # 查询连接状态,1表示已连接。 +1 +>>> nic.ipconfig() # 查询 ip 信息。 +('172.16.1.2', '255.255.255.0', '172.16.1.1', 1500, '8.8.8.8', '114.114.114.114') +>>> esp8266.set_default_NIC('172.16.1.2') #设置 Wi-Fi 网卡作为默认网卡。 +0 + +# 现在模块可以通过 Wi-Fi 网络正常进行网络连接。 +``` + + + +### 以太网卡 + +在不同的应用场景下,以太网卡有不同的工作模式:终端模式(WAN)、网关模式(LAN)。 + +**终端模式** + +即作为 WAN 口,通过以太网卡为模组提供外网访问能力。模型如下图所示: + +![](../../media/network-comm/nic/network_eth_node.png) + +该模式下,可通过 DHCP 协议动态获取 IP 地址,亦可设置静态 IP 地址。 +通过 DHCP 协议获取 IP 地址的示例代码如下: +```python +>>> import ethernet +>>> W5500 = ethernet.W5500(b'\x12\x34\x56\x78\x9a\xbc') # 加载以太网卡驱动,在实际使用中,需要确认硬件连接是否使用默认配置,否则请将网卡初始化接口参数不全。 +0 +>>> W5500.dhcp() # 动态获取 ip 信息。保证网络环境中有dhcp服务器,比如接的路由器。 +0 +>>> W5500.ipconfig() # 查询 ip 信息。 +[('12-34-56-78-9A-BC', 'W5500'), (4, '192.168.31.203', '255.255.255. 0', '192.168.31.1', '192.168.31.1', '0.0.0.0')] +>>> W5500.set_default_NIC('192.168.31.203') #设置以太网卡作为默认网卡。 +0 + +# 现在模块可以通过以太网正常进行网络连接。 +``` + +设置静态 IP 地址的示例代码如下: +```python +>>> import ethernet +>>> W5500 = ethernet.W5500(b'\x12\x34\x56\x78\x9a\xbc','192.168.2.100', '255.255.255.0', '192.168.2.1') # 加载以太网卡驱动,在实际使用中,需要确认硬件连接是否使用默认配置,否则请将网卡初始化接口参数补全。其中静态ip信息配置需要根据自己的网络环境调整。 +0 +>>> W5500.dhcp() # 动态获取 ip 信息。 +0 +>>> W5500.ipconfig() # 查询 ip 信息。 +[('12-34-56-78-9A-BC', 'W5500'), (4, '192.168.2.100', '255.255.255.0', '192.168.2.1', '8.8.8.8', '114.114.114.114')] +>>> W5500.set_default_NIC('192.168.2.100') #设置以太网卡作为默认网卡。 +0 + +# 现在模块可以通过以太网正常进行网络连接。 +``` + +**网关模式** + +即作为 LAN 口,与另一台以太网设备连接,借助 4G 网络为外接以太网设备外网访问能力。模型如下图所示: + +![](../../media/network-comm/nic/network_eth_gateway.png) + +在该模式下,模组会默认启用 DHCP 服务,为外接的以太网设备动态分配 IP 地址。示例代码如下: +```python +>>> import dataCall +>>> import ethernet +>>> info=dataCall.getInfo(1, 0) # 获取当前 4g 拨号 ip 信息,确认4G网络正常。 +>>> print(info) +(1, 0, [1, 0, '10.84.113.152', '211.138.180.2', '211.138.180.3']) +>>> W5500 = ethernet.W5500(b'\x12\x34\x56\x78\x9a\xbc','192.168.43.1','','',-1,-1,-1,-1, 1) # 加载以太网卡驱动,在实际使用中,需要确认硬件连接是否使用默认配置,否则请将网卡初始化接口参数不全。 +0 +>>> W5500.set_default_NIC('10.84.113.152') # 设置默认网卡,设置 4G 为默认网卡。 +0 +>>> W5500.ipconfig() # 查询 ip 信息。 +[('12-34-56-78-9A-BC', 'W5500'), (4, '192.168.43.1', '255.255.255. 0', '192.168.43.1', '8.8.8.8', '114.114.114.114')] +>>> W5500.set_up() # 启动以太网卡。 +0 + +# 现在以太网终端设备可以正常进行网络连接。 +``` + + + +### USB 网卡 + +当前 USB 网卡主要应用是使用USB网口作为 LAN 口,通过 4G 网卡进行数据转发进行网络访问。USB 网卡协议支持 ECM/RNDIS 协议,请根据需要进行选择,在 PC 下 RNDIS 可以直接加载,在 linux/android/ios 系统中 ECM 可以直接加载。 + +支持 USB 网卡的蜂窝通信模组通过 USB 口连接至支持 ECM 或 RNDIS 协议的主机设备,借助 4G 网络为其提供外网访问能力。模型如下图所示: + +![](../../media/network-comm/nic/network_usbnet.png) + +QuecPython 模组通过 set_worktype(USBNET_Type) 方法设置 USB 网卡的协议类型后,调用 open()方法即可使能 USB 网卡功能。 + +USBNET_Type 参数取值说明: +- USBNET.Type_RNDIS : 指定 RNDIS 协议; Windows 操作系统默认支持 RNDIS 协议。 +- USBNET.Type_ECM: 指定 ECM 协议; Linux、Android、IOS、MACOS等操作系统默认支持 ECM 协议。 + +示例代码如下: + +```python +>>> from misc import USBNET +>>> USBNET.get_worktype() # 获取工作模式。 +3 +>>> USBNET.set_worktype(USBNET.Type_RNDIS) # 配置工作模式,需要注意更新配置重启生效。 +0 +>>> USBNET.open() # 打开 usbnet 功能。 +0 + +# 现在 usb 终端设备可以通过 usb 网口正常进行网络连接。 +``` + + + +## 简单场景 + +主要介绍如何在各种网卡状态下进行网络通信。以下场景数据通信将使用 socket tcp 通信为例进行说明。 + + + +### 使用默认蜂窝无线网卡传输数据 + +对于单独蜂窝无线网卡情况下,可以不绑定网卡进行通信。 + +示例代码如下: +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + sock.settimeout(5) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + + + +### 使用多张蜂窝网卡并指定网卡传输数据 + +对于支持多 sim 卡或者多路拨号的情况下,具体使用哪一路或者哪一个sim卡进行通信,需要指定从哪一路进行传输。 +socket API 标准接口提供了数据通信绑定网卡操作,标准接口中 bind 接口,绑定 ip 及端口号,可以解决多路网卡数据传输指向问题。 + +![](../../media/network-comm/nic/network_config_4g_doule.png) + +如上图所示,使用NIC1网卡通信展示代码如下: +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + sock.settimeout(5) + sock.bind(("10.11.129.251", 0)) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + +如上图所示,使用NIC2网卡通信展示代码如下: +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + sock.settimeout(5) + sock.bind(("10.11.129.252", 0)) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + + + +### 使用以太网卡传输数据 + +以太网在不同的工作方式下,数传有一定差异。我们分别以网关模式(LAN)及终端模式(WAN)工作状态下进行介绍。 + +网关模式(LAN) +以太网 LAN 工作状态下,使用 4G 网络为 LAN 局域网下设备提供网络服务,此时 4G 作为默认网卡。如果模块需要通过以太网进行通信,则必须通过绑定网卡方式进行使用。 + +![](../../media/network-comm/nic/network_config_lan_socket.png) + +示例代码展示如下: +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + sock.settimeout(5) + sock.bind(("192.168.1.1", 0)) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + +终端模式(WAN) +WAN工作状态下,以太网口被配置为专门与外界通信的,默认以太网卡作为默认网卡。我们可以在不绑定的情况下直接通信即可。 + +![](../../media/network-comm/nic/network_eth_node.png) + +示例代码展示如下: +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + sock.settimeout(5) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + + + +### 使用 Wi-Fi 网卡传输数据 + +Wi-Fi 网卡在不同的工作方式下,数传有一定差异。我们分别以 Station 模式及 AP 模式工作状态下进行介绍。 + +Station 模式 +Station 模式工作状态下,Wi-Fi 网口被配置为专门与外界通信的,默认 Wi-Fi 网卡作为默认网卡。我们可以在不绑定的情况下直接通信即可。 + +![](../../media/network-comm/nic/network_wifi_station.png) +![](../../media/network-comm/nic/network_wifi_station_single.png) + +示例代码展示如下: +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + sock.settimeout(5) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + +AP 模式 +Wi-Fi 网卡在 AP 工作状态下,使用 4G 网络为 AP 局域网下设备提供网络服务,此时 4G 作为默认网卡。如果模块需要通过 Wi-Fi 网卡 进行通信,则必须通过绑定网卡方式进行使用。 +>注:对于单网卡设备可以直接通信,无需绑定操作。 + +![](../../media/network-comm/nic/network_wifi_ap.png) +![](../../media/network-comm/nic/network_wifi_single.png) + +示例代码展示如下: +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + sock.settimeout(5) + sock.bind(("192.168.1.1", 0)) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + + + +## 路由配置讲解 + +我们正在优化此项,后续提供统一标准工具。 + + + +## 多网卡下网络配置 + +如下图所示,模块集成了多种网卡设备,不同的网卡配置不同的工作模式,提供了不同的网络服务。 其中可以通过 4G 网卡、Wi-Fi 网卡 AP 模式、以太网终端模式( WAN )访问外部网络,Wi-Fi 网卡 STATION 模式、以太网网关模式( LAN )、USB 网卡均提供了局域网络,那么该如何选择网卡使用,以及如何配置网络转发,按照自己想要的方式进行网络通信? + +![](../../media/network-comm/nic/network_multi_nic_connection.png) + +针对多网卡使用情况下,QuecPython 提供如下两种方式进行操作。 + +1. 指定网卡数据发送 + +多网卡情况下,当进行网络通信时,无法确定走哪个网卡进行发出,但是有时网络服务在不同的局域网内,需要从指定网卡发出,此时可以通过标准的socket接口,通过 bind 网卡的 ip,来固定 tcp/udp 通信从哪个网口发出。如下图所示,当绑定`10.11.129.252`时走4G网卡交互,当绑定`192.168.1.100`时走以太网卡交互。 + +![](../../media/network-comm/nic/network_eth_gateway_bind.png) + +2. 网卡转发配置 + +使用提供的默认网卡配置接口,当配置默认网卡后,将该网卡作为对外访问网络网卡,启用 NAT 功能,对其他网卡数据进行转发。如下图所示,使用`nic.set_default_NIC('10.11.129.252')`配置立即生效,然后会对来自以太网卡的非本机数据从 4G 网卡转发处理。 + +> 注1:默认网卡接口开出,主要由用户维护默认网卡,便于切换网络转发。 +> 注2:其中 4G 网卡第一路固定配置为默认网卡,第一路拨号成功后会触发重新配置成默认网卡。目前使用场景基本都是4G网卡作为默认网卡对外转发,防止用户使用的不确定性,导致网络不可用,为了网络可靠性增加此操作。 + +![](../../media/network-comm/nic/network_eth_gateway_default.png) + + + + +### Wi-Fi(AP)-> LTE(单路) + +在 Wi-Fi 网卡和蜂窝无线网卡(LTE)同时存在时,通过 Wi-Fi 网络使用 4G 网络连通外网,该如何配置,工作方式见下图: +![](../../media/network-comm/nic/network_wifi_ap.png) + +对于多种网卡,配置转发关系,当前 QuecPython 尚未提供复杂详细的路由表转发配置功能,我们只需要配置默认网卡即可。再多的网卡,也只有唯一的一个默认网卡,通过默认网卡配置,都会从默认网卡进行 NAT 转发。 + +示例代码如下展示: +```python + +# 此处展示只是配置转发方向,具体 Wi-Fi 网卡初始化配置请参考 Wi-Fi 网卡应用指导章节。 +# nic 指具体网卡的网卡对象,当前我们默认网卡配置接口在具体网卡功能内部,后续会作为基础接口放置在网络配置模块下。 + +nic.set_default_NIC('10.11.129.251') + +# 此时 AP 网络下的终端设备可以通过 LTE 连接外部网络 + +``` + + + +### Ethernet(LAN)-> LTE(单路) + +在以太网卡和蜂窝无线网卡(LTE)同时存在时,通过以太网络使用 4G 网络连通外网,该如何配置,工作方式见下图: +![](../../media/network-comm/nic/network_config_lan_socket.png) + +对于多种网卡,配置转发关系,当前 QuecPython 尚未提供复杂详细的路由表转发配置功能,我们只需要配置默认网卡即可。再多的网卡,也只有唯一的一个默认网卡,通过默认网卡配置,都会从默认网卡进行 NAT 转发。 + +示例代码如下展示: +```python + +# 此处展示只是配置转发方向,具体以太网卡初始化配置请参考以太网卡应用指导章节 。 +# nic 指具体网卡的网卡对象,当前我们默认网卡配置接口在具体网卡功能内部,后续会作为基础接口放置在网络配置模块下。 + +nic.set_default_NIC('10.11.129.252') + +# 此时 LAN 网络下的终端设备可以通过 LTE 连接外部网络。 + +``` + + + +### Wi-Fi(AP)-> LTE(多路) + +在 Wi-Fi 网卡和蜂窝无线网卡(LTE)同时存在时,并且 LTE 进行多路拨号,一路为 Wi-Fi 网络提供 LTE 网络服务,一路为模组内部通信,该如何配置?工作方式见下图: +![](../../media/network-comm/nic/network_config_ap_double_4g.png) + +对于多种网卡,配置转发关系,当前 QuecPython 尚未提供复杂详细的路由表转发配置功能,我们只需要配置默认网卡即可。再多的网卡,也只有唯一的一个默认网卡,通过默认网卡配置,都会从默认网卡进行 NAT 转发。 + +如上图所示,蜂窝无线网卡第一路拨号 ip 为 10.11.129.251 ,第二路拨号 ip 为 10.11.129.252 ,我们使用第一路作为模组内部通信,第二路为 Wi-Fi 网络提供 LTE 网络服务。 + + +第一路作为内部通信网络服务,使用第一路进行 TCP 通信示例代码如下展示: +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + sock.settimeout(5) + sock.bind(("10.11.129.251", 0)) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + +第二路为 Wi-Fi 网络提供 LTE 网络服务,配置示例代码如下展示: +```python + +# 此处展示只是配置转发方向,具体 Wi-Fi 网卡初始化配置请参考 Wi-Fi 网卡应用指导章节。 +# nic 指具体的网卡对象,当前我们默认网卡配置接口在具体网卡功能内部,后续会作为基础接口放置在网络配置模块下。 + +nic.set_default_NIC('10.11.129.252') + +# 此时 LAN 网络下的终端设备可以通过 LTE 连接外部网络。 + +``` + + + +### Ethernet(WAN)-> LTE(多路) + +在以太网卡和蜂窝无线网卡(LTE)同时存在时,并且 LTE 进行多路拨号,一路为以太网络提供 LTE 网络服务,一路为模组内部通信,,该如何配置,工作方式见下图: +![](../../media/network-comm/nic/network_config_lan_double_4g.png) + +对于多种网卡,配置转发关系,当前 QuecPython 尚未提供复杂详细的路由表转发配置功能,我们只需要配置默认网卡即可。再多的网卡,也只有唯一的一个默认网卡,通过默认网卡配置,都会从默认网卡进行 NAT 转发。 + +如上图所示,蜂窝无线网卡第一路拨号 ip 为 10.11.129.251 ,第二路拨号 ip 为 10.11.129.252 ,我们使用第一路作为模组内部通信,第二路为 Wi-Fi 网络提供 LTE 网络服务。 + +第一路作为内部通信网络服务,使用第一路进行 TCP 通信示例代码如下展示: +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + sock.settimeout(5) + sock.bind(("10.11.129.251", 0)) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + +第二路为以太网络提供 LTE 网络服务,配置示例代码如下展示: +```python + +# 此处展示只是配置转发方向,具体以太网卡初始化配置请参考以太网卡应用指导章节。 +# nic 指具体网卡的网卡对象,当前我们默认网卡配置接口在具体网卡功能内部,后续会作为基础接口放置在网络配置模块下。 + +nic.set_default_NIC('10.11.129.252') + +# 此时 LAN 网络下的终端设备可以通过 LTE 连接外部网络。 + +``` + + + +## 复杂场景案例 + +本章节旨在展示多网卡下的典型应用场景,以帮助用户理解多网卡环境下的网卡应用,并实现快速上手。 + +**案例1** + +4G + ethernet + Wi-Fi 共用情况下网络应用。根据使用需求,将不同网卡配置不同的工作模式,进入正常的工作状态后,通过改变默认网卡,来确定转发规则。 + +> 对于以太、Wi-Fi、蜂窝网络的使用,我们没有固定使用规则,由用户进行选择,我们建议对于多网络状态,模块使用网络优先级 以太 > Wi-Fi > 蜂窝。 + +1. 使用 4G 作为默认网卡,Wi-Fi 与以太网络通过 4G 转发进行网络访问。 + +![](../../media/network-comm/nic/network_4g_eth_wifi.png) + +代码如下图所示: +```python +# 导入usocket模块 +import dataCall +import network +import ethernet + +if __name__ == '__main__': + # 默认网卡配置 + defalut_nic = None + + # 获取 4G 信息 + lte = dataCall.getInfo(1, 0) + print(lte) + + # Wi-Fi 初始化 + WiFi = network.ASR5803W(network.AP_IF) + WiFi.config('ssid'='QuecPython', 'key'='quecpython') + WiFi.active(True) + print('Wi-Fi init success') + + # 以太网初始化。 + eth = ethernet.YT8512H(b'\x12\x34\x56\x78\x9a\xbc','192.168.1.1') + eth.set_up() + print('ethernet init success') + + # 设置 4G 作为默认网卡 + defalut_nic = lte[2][2] + eth.set_default_NIC(defalut_nic) +``` + +2. 使用以太网作为默认网卡,Wi-Fi 网络通过以太网转发进行网络访问。 + +![](../../media/network-comm/nic/network_eth_wifi_4g.png) + +代码如下图所示: +```python +# 导入usocket模块 +import dataCall +import network +import ethernet + +if __name__ == '__main__': + # 默认网卡配置 + defalut_nic = None + + # 获取 4G 信息 + lte = dataCall.getInfo(1, 0) + print(lte) + + # Wi-Fi 初始化 + WiFi = network.ASR5803W(network.AP_IF) + WiFi.config('ssid'='QuecPython', 'key'='quecpython') + WiFi.active(True) + print('Wi-Fi init success') + + # 以太网初始化,以太网卡作为 WAN,通过 dhcp 的方式获取入网。 + eth = ethernet.YT8512H(b'\x12\x34\x56\x78\x9a\xbc') + eth.dhcp() + eth.set_up() + print('ethernet init success') + + # 设置以太网卡作为默认网卡 + defalut_nic = eth.ipconfig()[1][1] + eth.set_default_NIC(defalut_nic) +``` + + + +### 6.7.6 常见问题 + + + +### Socket 如何和网卡绑定? + +请查看 [TCP-UDP 章节]()。 + + + +### 如何测试网络链路通达? + +提供 uping 模块检查网络,通过该模块 ping 各个网络,可以判断链路状态。比如以太网卡可以获取到ip,但是无法进行网络连接,此时需要检查整个网络环境,可能是上层路由器 wan 口未连接外网,也可能是外部网络配置限制导致网络不可用,就需要此功能进行整个链路检查。 + +```python +>>> import uping +>>> uping.ping('python.quectel.com') +uping.ping('python.quectel.com') + +PING python.quectel.com (47.107.246.213): 64 data bytes + +72 bytes from 47.107.246.213: icmp_seq=1, ttl=52, time=50.546000 ms +72 bytes from 47.107.246.213: icmp_seq=2, ttl=52, time=38.424000 ms + +72 bytes from 47.107.246.213: icmp_seq=3, ttl=52, time=52.606000 ms + +72 bytes from 47.107.246.213: icmp_seq=4, ttl=52, time=38.940000 ms +4 packets transmitted, 4 packets received, 0 packet loss +round-trip min/avg/max = 38.424/45.129/52.606 ms + +``` + diff --git a/docs/Application_guide/zh/network-comm/nic/support/README.md b/docs/Application_guide/zh/network-comm/nic/support/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3b5b4f3268d07500bc695cdd5faebe2d1a41e950 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/support/README.md @@ -0,0 +1,81 @@ + +# QuecPython 支持网卡介绍 + +本章节主要针对当前 QuecPython 下支持的网卡(蜂窝无线网卡、Wi-Fi 网卡、以太网卡、USB 网卡)进行简单介绍。 + +## 支持网卡简介 + +### 蜂窝无线网卡 +QuecPython 大部分都是基于蜂窝无线网卡芯片开发,对于蜂窝无线网卡支持有着天然的优势,具有强大的完善的功能接口及底层知识储备。 +QuecPython 支持的模组,绝大多数都是蜂窝通信模组,因此 QuecPython 对于蜂窝无线网卡的支持有着天然的优势,其具有完善的功能接口和稳健的底层技术支撑。 + +### Wi-Fi 网卡 +QuecPython 提供了 Wi-Fi 设备管理功能,通过硬件接口外挂 Wi-Fi 芯片,或者移植 QuecPython 到 Wi-Fi 芯片的方式进行控制。 +外置 Wi-Fi 网卡,针对模组芯片本身不自带 Wi-Fi 网卡,分别支持通过串口或 SDIO 硬件接口连接外部 Wi-Fi 网卡,如 ESP8266/FC41D/ASR5803。 +内置 Wi-Fi 网卡,直接在 Wi-Fi 芯片上移植 QuecPython,如 FCM360W/FC41D。 + +### 以太网卡 +QuecPython 提供了以太网卡/PHY芯片管理功能,QuecPython 内置 lwIP 协议栈以及 EMAC 层,使得能够驱动以太网 / PHY 芯片。现已支持多种 SPI/RMII 接口以太网芯片。 + +### USB 网卡 +QuecPython 提供了 USB 网卡管理功能,QuecPython 在 USB 接口集成 RNDIS/ECM/MBIM 协议,能够在不同系统下(如 windows/linux/android/ios 等)识别成网卡设备,直接进行网络连接。 + +## 网卡支持列表 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
网卡类型支持型号
蜂窝网络除 Wi-Fi 芯片平台外均支持
Wi-Fi网卡ESP8266EC600N系列、EC600M系列、EC600E/EC800E系列
FC41DEC600N系列、EC600M系列、EC600E/EC800E系列
ASR5803EC200A系列
内置WLANFCM360W、FC41D
以太网卡W5500EC600N系列、EC600M/EC800M系列
DM9051EC600N系列
CH395EC600U系列
YT8512/SZ18201/JL1101EC200A系列
USB网卡RNDIS除高通芯片平台及 Wi-Fi 芯片平台外均支持
ECM除高通芯片平台及 Wi-Fi 芯片平台外均支持
MBIM仅高通芯片平台支持
\ No newline at end of file diff --git a/docs/Application_guide/zh/network-comm/nic/usbnet/README.md b/docs/Application_guide/zh/network-comm/nic/usbnet/README.md new file mode 100644 index 0000000000000000000000000000000000000000..408ca532f1316b1b42f5326b6fcd006e08dd2560 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/usbnet/README.md @@ -0,0 +1,181 @@ +# USB 网卡 + +> 本章主要介绍 QuecPython 下 USB 网卡适配情况,以及如何应用。 + +目录 +[USB网卡介绍](#click_jump_6.6.1) + [硬件结构](#click_jump_6.6.1.1) + [初始化流程](#click_jump_6.6.1.2) + [数据流](#click_jump_6.6.1.3) + [工作模式](#click_jump_6.6.1.4) + [各平台接口支持情况](#click_jump_6.6.1.5) +[USBNET API 说明](#click_jump_6.6.2) +[应用示例](#click_jump_6.6.3) + [windows 电脑下加载 USB 网卡](#click_jump_6.6.3) + [android 手机下加载 USB 网卡](#click_jump_6.6.3) + [linux 下加载 USB 网卡](#click_jump_6.6.3) +[常见异常处理](#click_jump_6.6.4) + + + + +## USB 网卡介绍 + +USB网卡(USB Network Adapter)是一种外部设备,用于在计算机或其他设备上提供以太网连接。它通过USB接口连接到计算机,并通过USB总线进行数据传输。USB网卡通常用于为计算机添加或扩展网络连接功能,特别是在没有内置以太网接口或需要额外网络连接的情况下。 + +**QuecPython 上是如何支持 USB 网卡的?** + +USB 协议定义了设备类的类别码信息,可以用来识别设备并且加载设备驱动。这种代码信息有包含Base Class([基类])、SubClass([子类])、Protocol([协议])一共占有3个字节。常见的支持USB网卡的协议ECM/RNDIS/MBIM(USB 协议下的子类)。 +- ECM(Ethernet Networking Control Model,以太网网络控制模型)用于在设备和主机之间交换以太网帧数据。ECM设备的一般用例是LAN/WLAN的点对点以太网适配器。 + +- RNDIS(Remote network Driver Interface Specification,远程网络驱动接口规范) 协议是微软对于 ECM 的变种实现,主要用于简化windows平台中usb网络设备的驱动开发。 + +- MBIM(Mobile Broadband Interface Model,移动宽带接口模型),专门用于3G/4G/5G模块的。它定义了移动宽带设备与 PC 端的接口标准。 + +QuecPython 通过支持USB协议下 ECM/RNDIS/MBIM 子类协议实现对 usb 网卡适配,工作场景基本都是通过 USB 口给其他设备供网。 + + + +### 硬件结构 + +![USBNET硬件结构图](../../../media/network-comm/nic/network_usbnet_usb.png) + +| 引脚 | 说明 | +| ---- | ------- | +| `VCC` | 提供电源供电给连接的USB设备 | +| `D-` | 数据线负极 | +| `D+` | 数据线正极 | +| `GND` | 提供电路的接地 | + + + +### 初始化流程 + +网卡初始化加载流程如下图所示: + +![](../../../media/network-comm/nic/network_usbnet_usb_flow.png) + + + +### 数据流 + +![](../../../media/network-comm/nic/network_usbnet.png) + + + +### 工作模式 + +USB网卡是类以太网的,其工作方式与以太网相似,可以根据工作方式分为LAN/WAN(请参考[以太网章节介绍](../ethernet/README.md))。 + +QuecPython在使用中以LAN的方式使用,通过4G给USB网卡供网。 + + + +### 各平台支持情况 + +下表是 USB 网卡在各平台适配支持情况。 + + + + + + + + + + + + + + + + + + + +
网卡类型支持型号
USB网卡RNDIS除 BGxx 系列模组和 Wi-Fi 模组外,其余模组均支持
ECM除 BGxx 系列模组和 Wi-Fi 模组外,其余模组均支持
MBIMBGxx 系列模组支持
+ + + +## USBNET API 说明 + +USBNET 功能较为固定,所以操作上较简单,无网络相关配置。 + +![](../../../media/network-comm/nic/network_usbnet_api_process.png) + +### 打开 usb 网卡 + +打开 USB 网卡,默认网卡接口是存在的,操作此接口后,USB 网卡可以正常连接网络使用。 + +```python +USBNET.open() +``` + +### 配置 usb 网卡工作模式 + +USB 网卡支持 ECM/RNDIS 协议,当前可以选择两种 Type_ECM/Type_RNDIS,需要根据自己使用的环境进行配置使用。 + +```python +USBNET.set_worktype(USBNET.Type_RNDIS) +``` + +### 查询 usb 网卡工作模式 + +查询 USB 网卡工作模式,主要在复杂情况下,判断网卡工作在什么模式下进行什么操作,也在调试时判断是否和预期一样。 + +```python +USBNET.get_worktype() +``` + +### 查询 usb 网卡工作状态 + +查询 USB 网卡工作状态,方便处理各种异常情况以及调试。 + +```python +USBNET.get_status() +``` + +### 关闭 usb 网卡 + +与打开 USB 网卡逆向操作,操作后会让网卡不可用,方便根据需要对网卡进行控制。 + +```python +USBNET.close() +``` + + + +## 应用示例 + +[Windows 电脑下加载 USB 网卡](./usbnet-windows-example.md) + +[Android 手机下加载 USB 网卡](./usbnet-android-example.md) + +[Linux 下加载 USB 网卡](./usbnet-linux-example.md) + + + + +## 常见异常处理 + +**1. 为什么同一台 USB 网卡设备连接手机后有的手机正常有的手机没有网络?** + +> a. 检查网卡工作模式是否设置为 ECM 模式,RNDIS 模式不是所有的手机都支持。 +> b. 查看手机是否需要开启 OTG 功能,对于 OPPO 及其旗下品牌手机(如一加、IQOO等)需要在系统设置中打开 OTG 模式。 + +**2. 使用展锐 8910 的 USB 网卡功能,首次以默认 ECM 模式 open 后,电脑连接不上网络?** + +> 展锐 8910 的 USB 网卡功能需要在 NAT 功能支持,默认开机不启动NAT,需要启动NAT后才能正常使用 USB 网卡功能。请参考 WIKI 上对应 [USBNET]() 章节。 + +**3. 展锐8850使用 USB 网卡设置 RNDIS 后打开正常,关闭后再打开返回 -1** + +> 需要进行 NAT 模式的设置,具体用法请参考 WIKI 上对应 [USBNET]() 章节。 + +**4. 为什么我的手机状态栏没有`<···>`样图标,但网络可以使用?** + +> 这是正常情况,由于手机厂商差异,某些型号的手机(如一加 9RT、红米 Note 8等)是没有`<···>`图标的。 + +**5. 电脑使用正常,但是手机却无法识别?** + +> a. 由于模块 MAC 地址导致手机无法正常识别,可以尝试使用 MAC 地址设置接口,更新 MAC 地址。 +> b. 由于手机厂商差异,无法识别 USB 网卡。 diff --git a/docs/Application_guide/zh/network-comm/nic/usbnet/usbnet-android-example.md b/docs/Application_guide/zh/network-comm/nic/usbnet/usbnet-android-example.md new file mode 100644 index 0000000000000000000000000000000000000000..e1a568b950b32de988ecacf581f3ecbb0457170e --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/usbnet/usbnet-android-example.md @@ -0,0 +1,70 @@ +# Android 手机下加载 USB 网卡 + +下面以 QuecPython EC600ECN_LF 开发板为例,在 Android 手机环境下使用 USB 网卡功能进行演示。 + +> 注1:RNDIS 模式在 Windows 下使用无须驱动可直接加载,ECM 模式在 Linux/Android/IOS 系统中无需驱动,我们建议在使用时请根据平台进行模式选择,当然我们也提供了在 Windows 下 ECM 驱动适配。 +> 注2:ECM 模式下部分型号模块 MAC 地址手机无法识别,请根据 WIKI 上对应 [USBNET]() 章节,使用 MAC 配置接口更新可用 MAC 即可。 + +1. 开发环境准备 + + 请参考[快速入门章节]()(如已熟悉请跳过此步骤),搭建开发使用环境。 + 另需要手机一部,以及能够转换成手机接口的转换线。 + +2. 打开 QPYCOM 工具连接设备 + + 将模块上电,通过USB口连接电脑,使用 QPYCOM 工具连接`USB串行设备`交互口。 + ![](../../../media/network-comm/nic/network_usbnet_demo_qpycom.png) + +3. 编写 USB 网卡开机启动脚本 + + 具体 USB 网卡功能接口请参考 Wiki 上对应的 [USBNET章节]()。 + 新建一个 main.py 文件,输入以下内容: + ```python + # 导入所需包 + from misc import USBNET + from misc import Power + import sim + import dataCall + import utime + + def usbnet_start(): + #判断 USBNET 工作模式为 ECM,若不是,则设置为 ECM 并重启模组。 + saved_type = USBNET.get_worktype() + if saved_type != USBNET.Type_ECM : + USBNET.set_worktype(USBNET.Type_ECM) + Power.powerRestart() + utime.sleep(2) + #判断 SIM 卡状态,没有插卡则退出。 + sim_info = sim.getStatus() + if sim_info != 1: + return + #循环查询注网状态,得到注网信息则开启 USBNET。 + while True: + lte = dataCall.getInfo(1,0) + if type(lte) == tuple and lte[2][0] == 1: + if lte[2][2] != '0.0.0.0': + break + utime.sleep(1) + #开启USBNET功能。 + USBNET.open() + + usbnet_start() + ``` + +4. 导入开机启动文件 + + 通过 QPYCom 文件栏把编写的脚本拖入模组文件系统 usr 分区,开机启动会执行main.py文件。 + + ![](../../../media/network-comm/nic/network_usbnet_demo_5.png) + +5. usb连接手机 + + 通过电脑配置好开机启动后,将 usb 口接到手机上的 Type-C 口,并按 PWRKEY 键给模组上电。请自行准备转接线。 + + ![](../../../media/network-comm/nic/network_usbnet_demo_6.png) + +6. 手机网络连接 + + 模块连接收手机上电后,手机可在状态栏看到 <···> 样图标,表示手机已识别到USB网卡,此时打开浏览器可以正常访问网络。 + + ![](../../../media/network-comm/nic/network_usbnet_demo_7.png) diff --git a/docs/Application_guide/zh/network-comm/nic/usbnet/usbnet-linux-example.md b/docs/Application_guide/zh/network-comm/nic/usbnet/usbnet-linux-example.md new file mode 100644 index 0000000000000000000000000000000000000000..b91c18b7dda6e9086f4a54bdf8208b2dffec2cda --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/usbnet/usbnet-linux-example.md @@ -0,0 +1,75 @@ +# Linux 下加载 USB 网卡 + +下面以 QuecPython EC600ECN_LF 开发板为例,在 Linux 环境下使用 USB 网卡功能进行演示。 + +> 注1:RNDIS 模式在 Windows 下使用无须驱动可直接加载,ECM 模式在 Linux/Android/IOS 系统中无需驱动,我们建议在使用时请根据平台进行模式选择,当然我们也提供了在 Windows 下 ECM 驱动适配。 + +1. 开发环境准备 + + 请参考[快速入门章节]()(如已熟悉请跳过此步骤),搭建开发使用环境。 + 此处 Linux 系统使用的是 ubuntu 版本,在电脑下搭建 Linux 环境测试。 + +2. 打开 QPYCOM 工具连接设备 + + 将模块上电,通过USB口连接电脑,使用QPYCOM工具连接`USB串行设备`交互口。 + ![](../../../media/network-comm/nic/network_usbnet_demo_qpycom.png) + +3. 编写 USB 网卡开机启动脚本 + + 具体 USB 网卡功能接口请参考 WIKI 上对应的 [USBNET章节]()。 + 新建一个 main.py 文件,输入以下内容: + ```python + # 导入所需包 + from misc import USBNET + from misc import Power + import sim + import dataCall + import utime + + def usbnet_start(): + #判断 USBNET 工作模式为 ECM,若不是,则设置为 ECM 并重启模组。 + saved_type = USBNET.get_worktype() + if saved_type != USBNET.Type_ECM : + USBNET.set_worktype(USBNET.Type_ECM) + Power.powerRestart() + utime.sleep(2) + #判断 SIM 卡状态,没有插卡则退出。 + sim_info = sim.getStatus() + if sim_info != 1: + return + #循环查询注网状态,得到注网信息则开启 USBNET。 + while True: + lte = dataCall.getInfo(1,0) + if type(lte) == tuple and lte[2][0] == 1: + if lte[2][2] != '0.0.0.0': + break + utime.sleep(1) + #开启USBNET功能。 + USBNET.open() + + usbnet_start() + ``` + +4. 导入开机启动文件 + + 通过 QPYCom 文件栏把编写的脚本拖入模组文件系统 usr 分区,开机启动会执行 main.py 文件。 + + ![](../../../media/network-comm/nic/network_usbnet_demo_5.png) + +5. USB 连接ubuntu + + 将设备 USB 口接到 ubuntu 系统下。 + + ![](../../../media/network-comm/nic/network_usbnet_ubuntu_choose.png) + +6. 确认网卡连接正常 + + 网卡加载后,通过 ifconfig 命令查看网卡状态,可以看到新增 usb0 网卡。如下图所示: + + ![](../../../media/network-comm/nic/network_usbnet_ubuntu_nic.png) + +7. 确认网络连接 + + 此处通过 ping python.quectel.com 确认是否连接正常。如下图所示: + + ![](../../../media/network-comm/nic/network_usbnet_ubuntu_ping.png) diff --git a/docs/Application_guide/zh/network-comm/nic/usbnet/usbnet-windows-example.md b/docs/Application_guide/zh/network-comm/nic/usbnet/usbnet-windows-example.md new file mode 100644 index 0000000000000000000000000000000000000000..19c6b5e28a2019846074e99145b72134f0c6c722 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/usbnet/usbnet-windows-example.md @@ -0,0 +1,67 @@ +# Windows 电脑下加载 USB 网卡 + +下面以 QuecPython EC600ECN_LF 开发板为例,在 Windows 环境下使用 USB 网卡功能进行演示。 + +> 注1:RNDIS 模式在 Windows 下使用无须驱动可直接加载,ECM 模式在 Linux/Android/IOS 系统中无需驱动,我们建议在使用时请根据平台进行模式选择,当然我们也提供了在 Windows 下 ECM 驱动适配。 + +1. 开发环境准备 + + 请参考[快速入门章节]()(如已熟悉请跳过此步骤),搭建开发使用环境。 + +2. 打开 QPYCOM 工具连接设备 + + 将模块上电,通过USB口连接电脑,使用QPYCOM工具连接`USB串行设备`交互口。 + ![](../../../media/network-comm/nic/network_usbnet_demo_qpycom.png) + +3. 通过 QPYCOM 工具输入交互命令开启 USB 网卡 + + 具体USBNET功能接口请参考 Wiki 上对应的 [USBNET章节]()。 + + 交互口输入以下命令。 + ```python + # 首先从misc中导入USBNET包 + >>> from misc import USBNET + >>> from misc import Power + + # 查询当前USBNET的工作模式,1 表示ECM模式, 3 表示 RNDIS模式。 + >>> USBNET.get_worktype() + 1 + + # 设置为RNDIS模式 + >>> USBNET.set_worktype(USBNET.Type_RNDIS) + 0 + + # 重启模组,用以生效之前配置的模式 + >>> Power.powerRestart() + + # 重启模组后,导入所需的包 + >>> from misc import USBNET + >>> import dataCall + + >>> USBNET.get_worktype() + 3 + + # 查询模组是否拨号成功 + >>> dataCall.getInfo(1,0) + (1, 0, [1, 0, '10.62.213.130', '211.138.180.4', '211.138.180.5']) + + # 有拨号信息后打开USB网卡 + >>> USBNET.open() + 0 + ``` + + 4. 查看 USB 网卡连接 + + 可以看到增加了一个 RNDIS 类型的网络接口。 + + ![](../../../media/network-comm/nic/network_usbnet_demo_3.png) + + 打开 cmd,输入 ipconfig,也可以看到基本网络信息都获取到了,此时关闭其余网络接口只保留 USB 网卡的网络接口电脑也可以正常上网。 + + ![](../../../media/network-comm/nic/network_usbnet_demo_4.png) + + 5. 查看网络连接 + +此时可以通过电脑浏览器访问网页。比如浏览器输入 https://python.quectel.com 即可正常访问。 + +![](../../../media/network-comm/nic/network_eth_gateway_web.png) diff --git a/docs/Application_guide/zh/network-comm/nic/wifi/README.md b/docs/Application_guide/zh/network-comm/nic/wifi/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a0c45392cd7b8ab1870daea68b5ba85a2d2d5ff7 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/wifi/README.md @@ -0,0 +1,459 @@ +# Wi-Fi 网卡 + +> 本章节主要介绍 QuecPython 下 Wi-Fi 网卡适配情况,并详细介绍其在软件及硬件方面对接方法,方便用户对网卡进行开发及自定义适配。 + +目录 +[Wi-Fi 无线网卡介绍](#click_jump_6.4.1) + [QuecPython 支持的 Wi-Fi 网卡硬件接口](#click_jump_6.4.1.1) +  [UART](#click_jump_6.4.1.1.1) +    [硬件结构](#click_jump_6.4.1.1.1) +    [初始化流程](#click_jump_6.4.1.1.1) +  [SDIO](#click_jump_6.4.1.1.2 ) +    [硬件结构](#click_jump_6.4.1.1.2 ) +    [初始化流程](#click_jump_6.4.1.1.2) +  [内置 Wi-Fi](#click_jump_6.4.1.1.3) +    [硬件结构](#click_jump_6.4.1.1.3) +    [初始化流程](#click_jump_6.4.1.1.3) + [工作模式介绍](#click_jump_6.4.1.2) + [频段介绍](#click_jump_6.4.1.3) + [数据流](#click_jump_6.4.1.4) + [各平台接口支持情况](#click_jump_6.4.1.5) +[Wi-Fi API 说明](#click_jump_6.4.2) + [外置 ESP8266 Wi-Fi 网卡 API 说明](#click_jump_6.4.2.1) + [通用 Wi-Fi 网卡 API 说明](#click_jump_6.4.2.2) +[应用示例](#click_jump_6.4.3) + [Station 模式](#click_jump_6.4.3) + [AP 模式](#click_jump_6.4.3) +[常见异常处理](#click_jump_6.4.4) + + + + +## Wi-Fi 无线网卡介绍 + +Wi-Fi 无线网卡是一种用于连接终端设备到无线局域网(Wireless Local Area Network,WLAN)的设备,允许设备通过无线信号进行数据通信,使设备能够无线访问互联网、共享文件和打印机等。Wi-Fi 无线网卡的工作原理是以无线电波为传输媒介,通过接收和发送无线信号来实现无线数据传输。它主要使用2.4 GHz或5 GHz频段,并基于IEEE 802.11b/g/n/ac/ax协议。 + +**QuecPython 上是如何支持 Wi-Fi 的?** + +正常网络通信是基于 TCP/IP 模型工作的,QuecPython 支持 LwIP 协议栈(轻量开源的 TCP/IP 协议栈),因此挂载 Wi-Fi 可以选择 QuecPython + MAC + PHY 的方式(通过串口/SDIO硬件接口外挂集成MAC和PHY的芯片,如 ESP8266/ASR5803),或者直接根据 Wi-Fi 平台资源大小,移植 QuecPython 到对应芯片平台上如 FCM360W。 + +![](../../../media/network-comm/nic/network_eth_spi_adapter.png) + +![](../../../media/network-comm/nic/network_wifi_core.png) + + + +### QuecPython 支持的 Wi-Fi 网卡硬件接口 + +QuecPython 支持的 Wi-Fi 网卡有三种硬件连接方式:蜂窝无线通信模组通过 UART 或 SDIO 接口外接 Wi-Fi 网卡,或者在移远的 Wi-Fi 芯片中移植 QuecPython。 + + + +#### UART 接口 + +UART是一种异步的串行通信接口协议,用于在设备之间传输数据,通过发送和接收线路实现设备之间的双向异步传输。UART接口的传输速度相对较慢,通常在几千字节每秒(B/s)到几十千字节每秒(KB/s)之间。 + +通过 UART 接口与蜂窝通信模组连接的 Wi-Fi 网卡,其与蜂窝通信模组之间使用 SLIP 协议进行链路通信。 + +SLIP 协议是指串行线路网际协议(Serial Line Internet Protocol),是在串行通信线路上支持 TCP/IP 协议的一种点对点式的链路层通信协议。 + +QuecPython 通过 UART 接口外接的 Wi-Fi 网卡,目前支持乐鑫的 ESP8266 与 ESP8285。 + +- 乐鑫 Wi-Fi 模组不支持 SLIP 协议,QuecPython 团队基于 ESP8266_RTOS_SDK 开发集成了 SLIP 协议,用户需要烧录 QuecPython 团队定制后的固件。 +- 受限于串口波特率,该种连接方式仅适用于低速率场景。 + +**硬件连接** + +串口有较简单的硬件连接,方便设备集成。 +UART 硬件连接如下所示: +![](../../../media/network-comm/nic/network_wifi_uart.png) + +| 引脚 | 说明 | +| ---- | ------- | +| TX | 串口数据发送引脚 | +| RX | 串口数据接收引脚 | +| GND | 接地引脚 | + +**初始化流程** + +串口连接具有较简单的初始化使用流程,只需要保证串口可用即可。QuecPython 初始化串口及 SLIP 不会对外部设备进行校验,只要对端设备集成 SLIP 协议即可使用,不限制设备类型。你也可以根据需要尝试其他设备集成 SLIP 进行对接。 + +![](../../../media/network-comm/nic/network_wifi_uart_flow.png) + + + +#### SDIO 接口 + +SDIO 是一种用于存储卡和其他外部设备的串行通信接口协议。它是SD卡协会制定的标准,允许设备通过 SDIO 接口与主机进行双向数据传输。SDIO 协议定义了物理接口、电气特性和通信协议,支持高速数据传输和多功能设备的连接,如无线网卡、摄像头等。SDIO 接口可以支持高速传输,速度可达到几十兆字节每秒(MB/s)。 + +**硬件连接** + +SDIO 硬件连接如下所示: +![](../../../media/network-comm/nic/network_wifi_sdio.png) + +| 引脚 | 说明 | +| ---- | ------- | +| WLAN_SLP_CLK | WLAN 睡眠时钟 | +| WLAN_PWR_EN | WLAN 电源使能控制 | +| WLAN_SDIO_DATA3 | WLAN SDIO数据位3 | +| WLAN_SDIO_DATA2 | WLAN SDIO数据位2 | +| WLAN_SDIO_DATA1 | WLAN SDIO数据位1 | +| WLAN_SDIO_DATA0 | WLAN SDIO数据位0 | +| WLAN_SDIO_CLK | WLAN SDIO 时钟 | +| WLAN_SDIO_CMD | WLAN SDIO 命令 | +| WLAN_WAKE | WLAN 唤醒模块 | +| WLAN_EN | WLAN 使能控制 | +| WLAN_RST | WLAN 重启控制 | + +**初始化流程** + +![](../../../media/network-comm/nic/network_wifi_sdio_flow.png) + + + +#### 内置 Wi-Fi + +QuecPython 内置 Wi-Fi,是指直接将 QuecPython 移植到 Wi-Fi 芯片上,适配 Wi-Fi 的接口,直接对 Wi-Fi 芯片进行控制。 + +**硬件结构** + +如下图所示,QuecPython 移植到 Wi-Fi 芯片,同时会适配 Wi-Fi 平台的硬件接口,对 Wi-Fi 硬件资源充分利用。 + +![](../../../media/network-comm/nic/network_wifi_internal.png) + + + +### 工作模式介绍 + +Wi-Fi 支持多种工作模式,当前 QuecPython 主要支持基础功能 AP 和 STATION 模式,即 Wi-Fi 作为终端连接到其他接入点入网(如路由器),或者 Wi-Fi 本身作为接入点给其他设备入网。 + +>AP 模式(Access Point Mode): +- AP 模式也被称为热点模式或基础设施模式。 +- 在 AP 模式下,Wi-Fi 设备充当访问点,负责创建和管理无线网络。 +- AP模式的设备通常连接到有线网络,并通过无线信号将网络连接提供给其他设备,这些设备可以是 STA 模式下的客户端设备或其他 Wi-Fi 设备。 +- AP 模式允许多个客户端设备同时连接到它,提供网络连接和资源共享的能力。 +- 在 AP 模式下,设备通常具有固定的IP地址,可以设置网络的安全性参数,如密码和加密方式。 + +STA模式(Station Mode): +- STA 模式也被称为客户端模式或无线工作站模式。 +- 在 STA 模式下,Wi-Fi 设备充当客户端,用于连接到无线网络的访问点(AP)。 +- STA 模式的设备可以通过扫描可用的无线网络并选择一个AP进行连接。 +- 一旦连接建立,STA 模式的设备可以与 AP 进行通信,通过无线网络访问 Internet 或其他网络资源。 +- STA 模式的设备通常获取动态分配的 IP 地址,可以使用 DHCP 协议自动获取网络配置。 + + + +### 频段介绍 + +Wi-Fi 技术在无线通信中使用的频段主要包括 2.4 GHz 和 5 GHz 两个频段。不同的频段无法兼容,比如使用只支持 2.4G 的网卡去连接5G的热点网络,下面对这两个频段进行详细介绍: + +2.4 GHz频段: +- 2.4 GHz频段是最常用的Wi-Fi频段之一。 +- 在 2.4 GHz频段,可用的无线信道有 13 个,从频率 2412 MHz 到 2484 MHz,每个信道之间相隔 5 MHz。 +- 2.4 GHz 频段的优点是信号传播距离较远,穿透能力较强,可以在较长的距离内提供较好的覆盖范围。 +- 然而,由于 2.4 GHz 频段被许多其他设备(如蓝牙设备、微波炉等)使用,可能存在较多的干扰和拥塞问题。 +- 在 2.4 GHz 频段中,常用的 Wi-Fi 标准包括 802.11b、802.11g 和 802.11n。 + +5 GHz频段: +- 5 GHz 频段是较新的 Wi-Fi 频段,也被称为高速频段。 +- 在 5 GHz 频段,可用的无线信道较多,具体可用信道数量因地区而异,通常有 24 个或更多。 +- 5 GHz 频段的优点是拥有更大的频谱带宽,可以提供更高的数据传输速率和更低的延迟。 +- 此外,5 GHz 频段通常较少受到干扰,因为许多其他设备更倾向于在 2.4 GHz 频段中工作。 +- 5 GHz 频段中,常用的 Wi-Fi 标准包括 802.11a、802.11n、802.11ac 和802.11ax(Wi-Fi 6)。 + + + +### 数据流 + +QuecPython 下 Wi-Fi 配置不同的工作方式,具有不同的工作流程,Station 模式主要是将模块连接外部设备接入外网,AP 模式主要为局域网设备提供 4G 网络。 +> 注:对于内置 Wi-Fi 网卡设备,模块只存在 Wi-Fi 网卡,无法进行 4G 网络转发。 + +1. STATION + +将 Wi-Fi 网卡配置成 STATION 模式,此时通过 Wi-Fi 网卡连接外部热点,为模块提供网络,模块可以通过外部热点(如路由器)对外网进行访问。 + +![](../../../media/network-comm/nic/network_wifi_station.png) +![](../../../media/network-comm/nic/network_wifi_station_single_1.png) + +2. AP + +将 Wi-Fi 网卡配置成 AP 模式,此时 Wi-Fi 作为接入点,模块为其他接入该接入点设备提供网络,通过网络转发,其他模块可以通过模块 4G 网络对外进行网络访问。 + +![](../../../media/network-comm/nic/network_wifi_ap.png) +![](../../../media/network-comm/nic/network_wifi_ap_single.png) + + + + +### 各平台接口支持情况 + +下表是各平台型号对于 UART 接口 Wi-Fi 及 SDIO 接口 Wi-Fi 适配支持情况。 + +| 型号\接口类型 | UART-WiFi | SDIO-WiFi | 内置-WiFi | +| :-: | :-: | :-: | :-: | +| `EC200A` | ✕ | ✓ | ✕ | +| `EC600N` | ✓ | ✕ | ✕ | +| `EC600U` | ✓ | ✕ | ✕ | +| `EC600M/EC800M` | ✓ | ✕ | ✕ | +| `EC600E/EC800E` | ✓ | ✕ | ✕ | +| `FCM360W` | ✕ | ✕ | ✓ | +>注释: +> - ✓: 支持 +> - ✕: 不支持 + + + +## Wi-Fi API 说明 + +QuecPython 支持多种型号 Wi-Fi,其中包括外挂或者内置 Wi-Fi,由于历史原因,当前 ESP8266/ESP8285 尚未支持mpy接口,我们后续将对所有 Wi-Fi 设备接口进行统一。详细各型号接口说明请参考 WIKI 下 [WLAN]() 章节。 + + + +### 外置 ESP8266 Wi-Fi 网卡 API 说明 + +由于 ESP8266 是在串口 SLIP 协议上进行支持的,为了方便对接使用拓展,我们只是提供了 WLAN.py 脚本,内置 ESP8266 控制类,方便用户根据自己需求进行接口定义并开发。 + +详细接口信息请参考 WIKI 下[ESP8266]()章节介绍。ESP8266 控制脚本内容查看[WLAN.py](./WLAN.py)。 + +接口调用关系如下图所示: +![](../../../media/network-comm/nic/network_wifi_esp8266_api_process.png) + +#### 初始化 Wi-Fi 网卡工作模式 + +此处初始化 Wi-Fi 网卡,确认外挂 Wi-Fi 网卡连接的串口通道,以及 Wi-Fi 网卡的工作模式。 + +```python +# 初始化 Wi-Fi 网卡 STA 工作模式配置。 +esp8266=ESP8266(UART.UART2,ESP8266.STA) +``` + +#### 使能AP模式 + +开启AP模式,用户可以配置用户明密码建立相应的热点。需要注意,此处配置需要跟网卡初始化接口模式保持一致。 + +```python +# 初始化 Wi-Fi 网卡 AP 工作模式配置。 +esp8266=ESP8266(UART.UART2,ESP8266.AP) +# 配置建立热点的用户名/密码 +esp8266.ap(user, password) +``` + +#### STATION模式 + +连接热点,根据热点的用户名密码,连接对应的 Wi-Fi 接入点。 + +```python +# 初始化 Wi-Fi 网卡 STA 工作模式配置。 +esp8266=ESP8266(UART.UART2,ESP8266.STA) +# 连接热点的用户名/密码 +esp8266.station(user, password) +``` + +#### WEB 配网 + +开启 WEB 配网,主要在 STATION 模式下,通过 WEB 配置连接 Wi-Fi 热点。 + +```python +# 初始化 Wi-Fi 网卡 STA 工作模式配置。 +esp8266=ESP8266(UART.UART2,ESP8266.STA) +# 开启web配网热点的用户名/密码 +esp8266.web_config(user, password) +``` + +#### SMARTCONFIG配网 + +根据乐鑫官方提供的智能配网方案。主要应用在 STATION 模式下,通过 SMARTCONFIG 配置连接到 Wi-Fi 热点。 + +```python +# 初始化 Wi-Fi 网卡 STA 工作模式配置。 +esp8266=ESP8266(UART.UART2,ESP8266.STA) +# 开启smartconfig,mode可配置ESPTOUCH模式 / AIRKISS / ESPTOUCH模式和AIRKISS / ESPTOUCH-V2模式。 + +esp8266.smartconfig(mode) +``` + +#### 路由配置 + +提供了 Wi-Fi 网卡数据通过 4G 网卡转发的方法,需要进行两步,通过配置默认网卡接口确认由哪个网卡转发,另外需要配置路由转发设置,通过指定转发 IP 规则,只有热点的网段内数据才进行转发。一般在 Wi-Fi 网卡工作在 AP 模式下,由 4G 网络为 Wi-Fi 终端提供网络服务。 + +> 1. 对于模块进行 socket 通信,模块会进行路由查找,匹配对应的网卡进行发送,如果不进行 bind 网卡发送,此时网络会通过默认网卡进行发送,如果默认网卡不存在则会报错退出。 +> 2. 对于多网卡下,网络转发进行路由查找由哪个网卡发出时,对于非本机数据,如果没有默认网卡,将直接丢弃,如果配置默认网卡将通过默认网卡进行转发。对于ESP8266需要判断转发规则。 + +```python +# 配置默认网卡,通过默认网卡进行网络转发。 +esp8266.set_default_NIC(ip) + +#添加路由规则,只有该网段ip才进行转发。 +esp8266.router_add(ip, mask) +``` + +#### 网络配置 + +此处网络配置信息,并非是对 ESP8266 侧网络进行配置,此处只是针对串口 SLIP 网卡进行配置,需要保证通信可靠性,不提供 IP 配置,只提供了 DNS 配置方法。 + +```python +# 配置网卡dns服务器。 +esp8266.set_dns(pri_dns, sec_dns) + +# 网卡ip信息查询。 +esp8266.ipconfig() +``` + +#### 状态查询 + +通过该接口获取网卡的当前状态,正常使用中,需要对网卡的状态进行对应的判断,然后进行操作。 + +```python +# 获取网卡当前工作状态。 +esp8266.status() +``` + +#### OTA 升级 + +此处 OTA 升级只是针对 Wi-Fi 网卡,触发网卡进行 OTA 升级。 + +>注: 升级过程中只可查询当前状态,其他操作均不会执行。升级过程中根据回调进行通知。 + +```python +# esp8266 OTA升级接口,通过传入固件下载地址,实现 Wi-Fi 网卡固件升级,此功能需要 Wi-Fi 网卡配置成STATION模式使用。 +esp8266.ota(url) +``` + + + +### 通用 Wi-Fi 网卡 API 说明 + +此套接口是完全按照 MicroPython 官方接口进行开发适配。适用于 SDIO 外挂 Wi-Fi 网卡以及内置 Wi-Fi 网卡。 + +当前信息只是对于通用 Wi-Fi 接口使用说明,部分型号适配根据实际情况实现有所差异,详细接口信息请参考 WIKI 下 [network]() 章节介绍。 + +![](../../../media/network-comm/nic/network_wifi_api_process.png) + +#### 初始化 Wi-Fi 网卡工作模式 + +初始化 Wi-Fi 网卡,确认 Wi-Fi 网卡工作模式。 + +**加载 FCM360W/FC41D** + +```python +nic = network.WLAN(network.STA_IF) +``` + +**加载ASR5803** + +```python +nic = network.ASR5803W(network.STA_IF) +``` + +#### 使能 AP 模式 + +开启AP模式,用户可以配置用户明密码建立相应的热点。需要注意,此处配置需要跟网卡初始化接口模式保持一致。 + +```python +# 初始化 Wi-Fi 网卡 AP 工作模式。 +nic = network.WLAN(network.AP_IF) +# 配置建立热点的用户名/密码 +nic.config('ssid'='user', 'key'='password') +# 激活 Wi-Fi 网卡 +nic.active(True) +``` + +#### STATION 模式 + +连接热点,根据热点的用户名密码,连接对应的 Wi-Fi 接入点。 + +方式1: +```python +# 初始化 Wi-Fi 网卡 STATION 工作模式。 +nic = network.WLAN(network.STA_IF) +# 连接热点的用户名/密码。 +nic.connect(user, password) +``` + +方式2: +```python +# 初始化 Wi-Fi 网卡 STATION 工作模式。 +nic = network.WLAN(network.STA_IF) +# 配置连接热点的用户名/密码。 +nic.config('ssid'='user', 'key'='password') +# 激活 Wi-Fi 网卡。 +nic.active(True) +``` + +#### 配网 + +当前只能通过 python 接口配置网路,其他方式正在完善中。 + +#### 网卡配置 + +提供了网络配置方法,方便用户根据需要进行网络配置。比如需要网卡在组网环境中使用静态ip,这可以通过该接口进行配置,或者AP模式下,默认网段与其他网段有冲突,可以修改到其他网段进行使用。 + +```python +# 网卡静态ip配置。 +nic.ifconfig((ip, subnet, gateway, primary_dns, secondary_dns)) + +# 网卡ip信息获取。 +nic.ifconfig() +``` + +#### 参数配置及查询 + +网卡提供了一系列配置及查询方法,方便用户调试或者应用 Wi-Fi 网卡。 + +```python +# 网卡配置与查询,可以配置ssid、bssid、channel、RSSI、security、hidden等信息。具体支持情况请查看WIKI下对应型号进行确认。 +nic.config('param') +nic.config(param=value) + +# 网卡激活与去激活查询与配置接口。 +nic.active([param]) + +# 网卡STA模式下,与路由器断开连接。 +nic.disconnect() + +# 网卡STA模式下,表示是否连接到路由器,网卡 AP 模式下,表示是否有客户端连接。 +nic.isconnected() +``` + +#### 状态查询 + +通过该接口获取网卡的当前状态及一些属性值,正常使用中,需要对网卡的状态进行对应的判断。 + + +```python +#网卡状态查询,当不进行传参是只是查询网卡当前状态,对于传参时查询具体的参数信息。 +nic.status([param]) +``` + + + + +## 应用示例 + +下面以 QuecPython EC600NCN_LE 开发板(下称开发板)通过 UART2 连接 ESP8266 使用 Wi-Fi 功能为例进行演示: + +[6.4.3.1 Station 模式](./wifi-station-example.md) + +[6.4.3.2 AP 模式](./wifi-ap-example.md) + + + + +## 常见问题 + +**1. 为什么 ASR5803 启动后搜索不到 Wi-Fi 信号?** +> a. 硬件 GPIO 设置是否正确。 +> b. mac地址是否合法(符合 IEEE 中对于 Wi-Fi AP模式 mac 设置的规定)。 +> c. Wi-Fi 距离过远或信号质量差。 +> d. 其他因素:其他因素,如无线干扰、安全设置、网络配置等。 + + +**2. Wi-Fi 连接不上** + +> a. 热点频段与 Wi-Fi 网卡是否在一个频段,比如2.4G/5G。 +> b. 热点信息是否存在乱码,Wi-Fi 不支持特殊字符。 + + +**3. 支持哪些 Wi-Fi 芯片?** + +当前通过串口外挂 Wi-Fi 芯片支持 ESP8266/ESP8285/FC41D,SDIO 外挂 Wi-Fi 芯片支持 ASR5803,内置 Wi-Fi 芯片支持 FCM360W。 diff --git a/docs/Application_guide/zh/network-comm/nic/wifi/WLAN.py b/docs/Application_guide/zh/network-comm/nic/wifi/WLAN.py new file mode 100644 index 0000000000000000000000000000000000000000..f49dfc517dd248a9464ed5cbadebe5674785b666 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/wifi/WLAN.py @@ -0,0 +1,230 @@ +import usocket +import log +import utime +import slip +from machine import UART +import _thread +from queue import Queue + +# 设置日志输出级别 +log.basicConfig(level=log.INFO) +ESP8266_log = log.getLogger("ESP8266") + + +class ESP8266: + AP = 0 # SLIP_INNER + STA = 1 # SLIP_OUTER + + def __init__(self, uart=UART.UART2, mode=STA, callback=None): + self.__uart = uart + self.__mode = mode + self.__message = '' + self.__err = 0 + self.__callback = callback + self.__value = None + self.__wait_resp = 0 + slip.destroy() + + ret = slip.construct(self.__uart, self.__mode, 0) + if ret != 0: + #print('slip netif construct fail') + return None + #print('slip network card create success') + self.__queue = Queue(1) + self.__sock = self.__socket_init() + self.__threadid = _thread.start_new_thread(self.__Socket_Thread, ()) + return None + + def __socket_init(self): + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM, usocket.TCP_CUSTOMIZE_PORT) + sock.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1) + # 绑定IP + bind_addr = ('172.16.1.2', 5000) + sock.bind(bind_addr) + sock.settimeout(10) + return sock + + def __socket_reinit(self): + return self.__socket_init() + + # station模式 + def station(self, user, password): + self.clear_remain() + __head = 'F3' + self.user = str(user) + self.password = str(password) + if self.__mode == self.STA: + self.__message = self.user + ',' + self.password + else: + #print('Not match station mode') + return -1 + self.__Socket_UDP(__head, self.__message) + if self.__err == 1 or self.__value[2] != 'OK': + return -1 + return 0 + + #ap 模式 + def ap(self, user, password): + self.clear_remain() + __head = 'F2' + self.user = str(user) + self.password = str(password) + + if self.__mode == self.AP: + self.__message = self.user + ',' + self.password + else: + #print('Not match ap mode') + return -1 + self.__Socket_UDP(__head, self.__message) + if self.__err == 1 or self.__value[2] != 'OK': + return -1 + return 0 + + # web配网模式 + def web_config(self, user, password): + self.clear_remain() + __head = 'F0' + if user == '' and password == '': + self.__message = '0' + else: + self.user = str(user) + self.password = str(password) + self.__message = self.user + ',' + self.password + self.__Socket_UDP(__head, self.__message) + if self.__err == 1 or self.__value[2] != 'OK': + return -1 + return 0 + + def clear_remain(self): + if self.__queue.empty() == False: + self.__queue.get() + self.__err = 0 + self.__value = None + + # 查询网卡状态 + def status(self): + self.clear_remain() + self.__Socket_UDP('F1', '0') + if self.__err == 1: + return 0 + return int(self.__value[2]) + + # 查询网卡版本信息 + def version(self): + self.clear_remain() + self.__Socket_UDP('F5', '0') + if self.__err == 1: + return -1 + return self.__value[2] + + # ota升级 + def ota(self, url): + self.clear_remain() + __head = 'F4' + self.__message = str(url) + self.__Socket_UDP(__head, self.__message) + if self.__err == 1 or self.__value[2] != 'OK': + return -1 + return 0 + # smartconfig配置 + def smartconfig(self, mode): + self.clear_remain() + __head = 'F6' + self.__message = str(mode) + self.__Socket_UDP(__head, self.__message) + if self.__err == 1 or self.__value[2] != 'OK': + return -1 + return 0 + + #添加路由信息 + def router_add(self, ip='192.168.4.1', mask='255.255.255.0'): + self.ip = ip + self.mask = mask + return slip.router_add('192.168.4.1', '255.255.255.0') + + # 获取slip的网络配置 + def ipconfig(self): + return slip.ipconfig() + + # 设置dns服务器 + def set_dns(self, pri_dns='8.8.8.8', sec_dns='114.114.114.114'): + self.pri_dns = pri_dns + self.sec_dns = sec_dns + return slip.set_dns(self.pri_dns,self.sec_dns) + + # 设置默认网卡 + def set_default_NIC(self, ip_str): + self.ip_str = ip_str + return slip.set_default_netif(self.ip_str) + + # 释放slip网卡 + def stop(self): + slip.destroy() + return 0 + + + # 封装tlv数据包 + def __pack_tlv_format(self, head, content): + self.head = head + self.content = content + if len(self.content) == 0 or len(self.content) > 9999 or len(self.head) != 2: + #print('illegal tlv content') + return -1 + len_str ='%04d' % len(self.content) + tlv_pack = self.head+len_str+self.content + return tlv_pack + + # 解码tlv数据包 + def __unpack_tlv_format(self, msg): + self.msg = str(msg) + tag = self.msg[0:2] + length = self.msg[2:6] + value = self.msg[6:] + len_str ='%04d' % len(value) + if len(value) == 0 or len(value) > 9999 or len(tag) != 2 or len_str != self.msg[2:6]: + #print('illegal tlv content') + return -1 + unpack = (tag,length,value) + return unpack + + # socket通信(UDP)模块 + def __Socket_UDP(self, head, content): + self.head = head + self.content = content + + # 消息处理 + msg = self.__pack_tlv_format(self.head, self.content) + + # 向服务端发送消息 + server_addr = ('172.16.1.5',1000) + #print(msg) + self.__err = 0 + self.__sock.sendto(msg,server_addr) + self.__wait_resp = 1 + data = self.__queue.get() + self.__wait_resp = 0 + return data + + # socket通信(UDP)模块 + def __Socket_Thread(self): + while True: + try: + data=self.__sock.recv(1024) + except Exception as recverr: + if "110" in str(recverr): + self.__err = 1 + if self.__wait_resp == 1: + self.__queue.put('0') + else: + utime.sleep(1) + self.__sock = self.__socket_reinit() + else : + value = self.__unpack_tlv_format(data.decode()) + #print('mode: {0}, recv {1} bytes, Data: {2}'.format (value[0],len(data), value[2])) + if value[0] == 'FF': + if self.__callback != None: + self.__callback(value[2]) + else: + self.__value = value + self.__queue.put('1') diff --git a/docs/Application_guide/zh/network-comm/nic/wifi/wifi-ap-example.md b/docs/Application_guide/zh/network-comm/nic/wifi/wifi-ap-example.md new file mode 100644 index 0000000000000000000000000000000000000000..8aafb002d90e768cdab0c3c27214a90201827f87 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/wifi/wifi-ap-example.md @@ -0,0 +1,75 @@ +# AP 模式 + +## 硬件准备工作 + +使用外挂 WIFI 网卡功能,需要额外准备外挂的 WIFI 网卡、路由器、杜邦线。 + +通过杜邦线将模块与 WIFI 网卡连接,模块与网卡接线可以根据实际需求配置,具体接口配置参数请参考 WIKI 下[WIFI]()章节。 + +[ESP8266硬件连接实物图](../../../media/network-comm/nic/network_wifi_esp8266.png) +[ASR5803硬件连接实物图](../../../media/network-comm/nic/network_wifi_esp8266.png) +[FCM360W硬件实物图]() +[FC41D硬件实物图]() + +## 软件准备 + +**网卡初始化** + +网卡初始化需要初始化 WIFI 网卡 AP 模式下工作环境,保证 WIFI 网卡的互通性。以及配置WIFI网卡热点名称及密码。以下示例介绍针对 ESP8266 WIFI 网卡进行介绍。 + +```python + +# ESP8266无线网卡包含在WLAN包中,从 WLAN 包导入ESP8266,然后继续从machine中导入需要的UART模块 +>>> from usr.WLAN import ESP8266 +>>> from machine import UART + +# 初始化网卡,串口参数选择UART2口,模式参数选择AP模式 +>>> wifi = ESP8266(UART.UART2, ESP8266.AP) + +# 设置无线网卡以ap模式启动,并配置ap热点的名称及密码 +>>> wifi.ap('test_8266','12345678') +0 + +# 获取当前网卡状态 返回 4 说明无线网卡已启用 ap 热点模式 +>>> wifi.status() +4 + +``` + +**路由配置** + +路由配置功能主要是不同网卡下进行网络转发的方法,当前只是通过默认网卡接口进行配置,默认网卡作为转发对象,对于通过 WIFI 网卡的非本机数据,将通过默认网卡进行 NAT 转发。 +交互口输入以下命令配置路由转发为 WIFI 终端设备提供 4G 网络。 +```python + +>>> import dataCall + +# 获取拨号信息 +>>> Info = dataCall.getInfo(1,0) + +# 查看是否有获得4G的IP +>>> Info +(1, 0, [1, 0, '10.62.209.177', '211.138.180.4', '211.138.180.5']) + +# 设置默认网卡,ap 模式下设置 4g 为默认网卡 +>>> wifi.set_default_NIC(Info[2][2]) +0 + +# 添加路由信息,设置网卡转发规则 +>>> wifi.router_add('192.168.4.0', '255.255.255.0') +0 + +``` + +## 手机连接 WIFI 热点 + +打开手机WLAN界面,找到名称为`test_8266`的WiFi,输入密码,进行连接。 + +![](../../../media/network-comm/nic/network_wifi_ap_connecting.png) + +## 查看网络连接 + +可以看到,手机已经成功连接到WiFi热点,此时关闭手机其他网络连接,只保留此WiFi连接,打开浏览器也可以正常访问网站。 +>注:ESP8266是通过串口外挂到模块,通信速率较低,建议手机只能浏览新闻,无法提供流畅视频通信。 + +![](../../../media/network-comm/nic/network_wifi_ap_connected.png) diff --git a/docs/Application_guide/zh/network-comm/nic/wifi/wifi-station-example.md b/docs/Application_guide/zh/network-comm/nic/wifi/wifi-station-example.md new file mode 100644 index 0000000000000000000000000000000000000000..488b55ad9c64b81f555c88ee07a8b3c202f22d08 --- /dev/null +++ b/docs/Application_guide/zh/network-comm/nic/wifi/wifi-station-example.md @@ -0,0 +1,190 @@ +# Station 模式 + +WIFI网卡通过连接热点,为模块提供网络。 + +开发环境搭建请参考[快速入门章节](),本章节主要是对 ESP8266/ESP8285 使用介绍。 + +## 硬件准备工作 + +使用外挂 WIFI 网卡功能,需要额外准备外挂的 WIFI 网卡、路由器、杜邦线。 + +通过杜邦线将模块与 WIFI 网卡连接,模块与网卡接线可以根据实际需求配置,具体接口配置参数请参考 WIKI 下 [WIFI]() 章节。 + +[ESP8266硬件连接实物图](../../../media/network-comm/nic/network_wifi_esp8266.png) +[ASR5803硬件连接实物图](../../../media/network-comm/nic/network_wifi_esp8266.png) +[FCM360W硬件实物图]() +[FC41D硬件实物图]() + +## 软件准备 + +**网卡初始化** + +网卡初始化不对网卡进行配置,只是初始化 WIFI 网卡工作环境,保证 WIFI 网卡的互通性。以下示例介绍针对 ESP8266/ESP8285 WIFI 网卡进行介绍。 + +```python + +# ESP8266无线网卡包含在WLAN包中,从WLAN包导入ESP8266,然后继续从machine中导入需要的UART模块 +>>> from usr.WLAN import ESP8266 +>>> from machine import UART + +# 初始化网卡,串口参数选择 UART2 口,模式参数选择 STA 模式 +>>> wifi = ESP8266(UART.UART2, ESP8266.STA) + +``` + +**WIFI 网卡配网** + +ESP8266/ESP8285 提供多种配网方式,分别通过命令行配网、WEB配网、SMARTCONFIG配网。 + +命令行配网 + +```python +# ESP8266无线网卡包含在WLAN包中,从WLAN包导入ESP8266,然后继续从machine中导入需要的UART包 +>>> from usr.WLAN import ESP8266 +>>> from machine import UART + +# 初始化网卡,串口参数选择UART2口,模式参数选择STA模式 +>>> wifi = ESP8266(UART.UART2, ESP8266.STA) + +# 配置无线网卡以station模式启动时所要连接的WiFi名称及密码 +>>> wifi.station('wifiname','wifipassword') +0 + +``` + +WEB配网 + +```python +# ESP8266无线网卡包含在WLAN包中,从WLAN包导入ESP8266,然后继续从machine中导入需要的UART包 +>>> from usr.WLAN import ESP8266 +>>> from machine import UART + +# 初始化网卡,串口参数选择UART2口,模式参数选择STA模式 +>>> wifi = ESP8266(UART.UART2, ESP8266.STA) + +# 配置无线网卡以station模式启动时所要连接的WiFi名称及密码 +>>> wifi.web_config('admin','admin123456') +0 + +``` + +SMARTCONFIG配网 + +```python +# ESP8266无线网卡包含在WLAN包中,从WLAN包导入ESP8266,然后继续从machine中导入需要的UART包 +>>> from usr.WLAN import ESP8266 +>>> from machine import UART + +# 初始化网卡,串口参数选择UART2口,模式参数选择STA模式 +>>> wifi = ESP8266(UART.UART2, ESP8266.STA) + +# 配置无线网卡以station模式启动时所要连接的WiFi名称及密码 +>>> wifi.smartconfig('wifiname','wifipassword') +0 + +``` + +**网络转发配置** + +ESP8266/ESP8285 是一个独立完整的设备,内部包括独立的配网方式,ESP8266/ESP8285 配网完成后,QuecPython 需要在模块侧进行网络配置才能进行正常的网络转发。 + +```python +# ESP8266无线网卡包含在WLAN包中,从WLAN包导入ESP8266,然后继续从machine中导入需要的UART包 +>>> from usr.WLAN import ESP8266 +>>> from machine import UART + +# 初始化网卡,串口参数选择UART2口,模式参数选择STA模式 +>>> wifi = ESP8266(UART.UART2, ESP8266.STA) + +# 配置无线网卡以station模式启动时所要连接的WiFi名称及密码 +>>> wifi.station('wifiname','wifipassword') +0 + +# 获取当前网卡状态 返回 1 说明无线网卡已连接 WiFi,返回 2 说明未连接 WiFi +>>> wifi.status() +1 + +# 给无线网卡配置dns服务器 +>>> wifi.set_dns('8.8.8.8','114.114.114.114') +0 + +# 再次查看无线网卡的 IP 信息,此处需要注意172.16.1.2是虚拟 IP,不是具体WIFI终端的IP。 +>>> wifi.ipconfig() +('172.16.1.2', '255.255.255.0', '172.16.1.1', 1500, '8.8.8.8', '114.114.114.114') + +# 设置无线网卡作为默认网卡,使用无线网卡进行网络通信 +>>> wifi.set_default_NIC('172.16.1.2') +0 + +# 此时可以通过WIFI网卡访问外部网络。 + +``` + +## TCP(socket)应用示例 + +以下示例展示 TCP 通信时,通过 WIFI 网络进行通信时的两种方法。 + +**TCP 客户端绑定 WIFI 网卡通信** + +此例中通过WIFI网卡进行 TCP 通信,通过 socket 模块内 bind 接口,指定通过WIFI网口进行发送。 + +>注:下例中bind 接口绑定 172.16.1.2 指 WIFI 网卡的 ip 地址。网卡ip地址通过网卡对象下 ipconfig 接口进行查询,详细见 WIFI 接口 WIKI。 + +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM, usocket.TCP_CUSTOMIZE_PORT) + sock.settimeout(5) + sock.bind(("172.16.1.2", 0)) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + +**TCP 客户端非绑定 WIFI 网卡通信** + +此例中通过 WIFI 网络进行 TCP 通信,不使用 socket 模块内 bind 接口,通过配置默认网卡进行 socket 通信。需要确定配置 WIFI 网卡作为默认网卡。 + +```python +# 导入usocket模块 +import usocket + +if __name__ == '__main__': + # 创建一个socket实例 + sock = usocket.socket(usocket.AF_INET, usocket.SOCK_STREAM) + sock.settimeout(5) + # 解析域名 + sockaddr=usocket.getaddrinfo('python.quectel.com', 80)[0][-1] + print('start connect') + # 建立连接 + sock.connect(sockaddr) + # 向服务端发送消息 + ret=sock.send('GET /NEWS HTTP/1.1\r\nHost: python.quectel.com\r\nAccept-Encoding: deflate\r\nConnection: keep-alive\r\n\r\n') + print('send %d bytes' % ret) + #接收服务端消息 + data=sock.recv(1024) + print('recv %s bytes:' % len(data)) + print(data.decode()) + + # 关闭连接 + sock.close() +``` + +## MQTT/HTTP 等应用协议应用 + +需要注意通过 WIFI 网卡进行 MQTT/HTTP 应用协议,由于内置的 MQTT/HTTP 通信模块没有使用绑定网卡操作接口,需要配置以太网卡作为默认网卡即可。具体 MQTT/HTTP 使用请参考应用层协议 WIKI 下 [umqtt]() 以及 [reqeust]() 章节。 diff --git a/docs/Application_guide/zh/network/cellLocator.md b/docs/Application_guide/zh/network/cellLocator.md deleted file mode 100644 index 5bf561a459371f8d7978938f16aa20da6d9e4763..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/network/cellLocator.md +++ /dev/null @@ -1,49 +0,0 @@ -# 基站定位 - -## 什么是基站定位 - -基站定位是一种利用移动通信基站的信号信息确定设备位置的技术。在移动通信网络中,基站是无线信号的发射和接收站点,每个基站都有一个唯一标识码,同时可以测量设备与基站之间的信号强度和时间延迟等信息。通过对设备与多个基站之间的信号信息进行比对和计算,就可以确定设备大致的位置。基站定位技术广泛应用于车辆追踪、物流管理、紧急救援等领域。与GPS等卫星定位技术相比,基站定位更适用于城市等密集建筑区域,但精度相对较低。 - -#### 基站定位分类 - -基站定位可以分为以下几类: - -1、单纯的基站定位。 - -2、基站和WiFi混合定位。 - -本文档主要介绍如何 QuecPython 单纯的基站定位功能,WiFi定位可查看WiFi定位应用文档,通过本文你将了解到 QuecPython 基站定位使用方法。 - - - -## 怎么使用基站定位功能 - -### 硬件设计 - -基站定位主要是数据通道向服务器获取位置信息实现的,除前提条件模块需要组网成功外无需其他外围硬件支持。 - -### 软件应用 - -#### 使用流程 - -在使用基站定位服务之前,首先需要建立数据通道(拨号成功,能通过dataCall.getInfo(1,0)查询到ip),基站定位服务基于这个数据通道向服务器获取位置信息。若在其他任务中已经建立了数据通道,可以跳过建立数据通道的步骤。 - -建立好数据通道之后,调用cellLocator.getLocation可查询当前UE的经纬度坐标。 - - - -> 1、 token需要向相关部门提交申请获取。 -> -> 2、基站定位服务访问不可太过频繁,两次定位间隔最好保持10s以上。 - - - -#### 软件实现示例 - -```Python ->>> import cellLocator ->>> cellLocator.getLocation("www.queclocator.com", 80, "xxxxxxxxxxxxxxxx", 8, 1) -(117.1138, 31.82279, 550) -# 上面使用的密钥"xxxxxxxxxxxxxxxx"指代token,具体需要向移远申请 -``` - diff --git a/docs/Application_guide/zh/network/datacall.md b/docs/Application_guide/zh/network/datacall.md deleted file mode 100644 index 08d7737501bdc39aaf435064cb7208758d93cf39..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/network/datacall.md +++ /dev/null @@ -1,385 +0,0 @@ -# 数据拨号 - -数据拨号是指通过调用通信模块的拨号接口,实现将嵌入式设备连接到互联网的过程。在进行数据拨号之前,需要先配置通信模块的网络参数,如APN、用户名、密码等,以便建立连接时使用。 - -## 引言 - -移远通信LTE Standard 模块支持QuecPython方案,本文档主要描述QuecPython模块无线数据业务的建立过程,即数据拨号的过程。本文主要介绍数据拨号功能的实际应用,包含开机自动拨号、拨号重连以及手动拨号的基础的用法和注意事项。同时介绍了拨号功能在常见场景中的使用。 - -### 适用模块 - -| **模块系列** | -| ------------------------------------------------------ | -| EC100Y/EC200N/EC600N/EC600S/EC800N/EG912N/EG915N 系列 | -| EC600M/EC800M/EG810M/EC200A系列 | -| EC200U/EC600U/EG912U/EG915U/EC600G/EC800G系列 | -| BC25系列 | -| EC600E/EC800E 系列 | -| BG77/BG95系列 | - -### 设备检查 - -本文档介绍的内容是基于Python SDK中包含的API来实现的,本文中使用的相关API具体详情见[dataCall](../../../API_reference/zh/iotlib/dataCall.md)。 - -拨号之前需做一系列基本检查,判断模块是否处于基本的正常工作状态,具体步骤如下: - -1. 通过USB转串口线连接PC至模块的主串口或者USB端口。 - -2. 插入(U)SIM卡和天线,并上电。 - -3. 通过如下API函数,检查设备状态。 - - a) 检测(U)SIM卡:sim.getStatus() - - b) 检测信号强度:net.getSignal() - - c) 检测模块注网:net.getState() - - d) 查询第一路拨号服务状态:dataCall.getInfo(1,0) - -4. 使用QPYcom工具和模组进行交互,检查如下操作: - -![image-20230320192624802](media/network/dataCall/network.dataCall-1.png) - - - -## 开机自动拨号 - -所谓开机自动拨号,是指模组上电运行时,自动进行拨号的行为,无需用户再去执行拨号。用户只需要在模组开机后确认已经拨号成功,即可开始正常使用网络功能。 - -### 功能实现 - -根据profileID来设置对应那一路是否开机自动拨号。profileID为1的那一路初始默认使能开机自动拨号。 - -1. 查询是否拨号成功。使用API和示例代码如下: - - dataCall.getInfo(profileID, ipType) - -```python -import dataCall -dataCall.getInfo(1,0) #查看第一路是否拨号成功 -#返回结果 (1, 0, [1, 0, '10.146.83.168', '211.138.180.2', '211.138.180.3']) -dataCall.getInfo(2,0) #查看第二路是否拨号成功 -dataCall.getInfo(3,0) #查看第三路是否拨号成功 -``` - -2. 设置为自动拨号。使用API和示例代码如下: - - dataCall.setAutoActivate(profileID, enable) - -```python -import dataCall -# 第一路默认为自动拨号 -dataCall.setAutoActivate(2, 1) #设置第二路自动拨号 -dataCall.setAutoActivate(3, 1) #设置第三路自动拨号 -``` - -2. 取消自动拨号。示例代码如下: - -```python -import dataCall -dataCall.setAutoActivate(1, 0) #取消第一路自动拨号 -dataCall.setAutoActivate(2, 0) #取消第二路自动拨号 -dataCall.setAutoActivate(3, 0) #取消第三路自动拨号 -``` - -### 注意事项 - -> 1. 第一路(profileID为1)初始默认开机自动拨号。 -> 2. 拨号前请先通过 setPDPContext配置APN相关信息。 -> 3. 如没有配置对应profileID的APN相关信息,默认使用空字符串进行拨号。 -> 4. 当设置多路自动拨号时,会依照profileID从小到大依次进行拨号。 -> 5. 如全部取消自动拨号,则模组上电运行时不会进行拨号,需要进行手动拨号。 - - - -## 手动拨号 - -拨号,指的是PDP Context的激活操作,激活成功后,核心网的PDN网关才会分配一个IP地址给模组。手动拨号是指用户自己使用拨号接口去进行拨号,手动拨号前需要先配置PDP Context相关信息(APN,用户名,密码以及鉴权参数等信息)。 - -### 配置APN - -配置APN相关信息,拨号时使用该方法配置的相关信息进行PDP Context激活。 - -1. 配置PDP Context信息。使用API和示例代码如下: - -​ dataCall.setPDPContext(profileID, ipType, apn, username, password, authType) - -```python -import dataCall -#配置第一路PDP Context相关信息 -dataCall.setPDPContext(1, 0, "3gnet", "", "", 0) -#配置第二路PDP Context相关信息 -dataCall.setPDPContext(2, 0, "3gnet.mnc001.mcc460.gprs", "", "", 0) -#配置第三路PDP Context相关信息 -dataCall.setPDPContext(3, 0, "3gnet.mnc001.mcc460.gprs", "", "", 0) -``` - -​2. 查询PDP Context信息。使用API和示例代码如下: - -​ dataCall.getPDPContext(profileID) - -```python -import dataCall -dataCall.getPDPContext(1) #查询第一路PDP Context相关信息 -#返回结果 (0, '3gnet', '', '', 0) -dataCall.getPDPContext(2) #查询第一路PDP Context相关信息 -#返回结果 (0, '3gnet.mnc001.mcc460.gprs', '', '', 0) -dataCall.getPDPContext(3) #查询第一路PDP Context相关信息 -``` - -### 拨号实现 - -手动拨号,激活profileID指定的那一路PDP Context。 - -1. 手动激活,进行拨号。使用API和示例代码如下: - - dataCall.activate(profileID) - -```Python -import dataCall -# 激活之前,应先配置APN -dataCall.setPDPContext(1, 0, '3gnet', '', '', 0) # 这里配置第一路的APN -dataCall.setPDPContext(2, 0, '3gnet', '', '', 0) # 这里配置第二路的APN -dataCall.setPDPContext(3, 0, '3gnet', '', '', 0) # 这里配置第三路的APN -dataCall.activate(1) # 激活第一路 -dataCall.activate(2) # 激活第二路 -dataCall.activate(3) # 激活第三路 -``` - -2. 去激活,取消拨号。使用API和示例代码如下: - - dataCall.deactivate(profileID) - -```Python -import dataCall -dataCall.deactivate(1) # 去激活第一路 -dataCall.deactivate(2) # 去激活第二路 -dataCall.deactivate(3) # 去激活第三路 -``` - -### 注意事项 - -> 1. 模组会在开机时自动拨号,一般不需要用户执行激活操作。 -> 2. 如用户关闭了开机自动拨号功能,则需要用户调用此方法来进行手动拨号。 -> 3. 手动拨号之前,应该先配置对应profileID的APN相关信息。 -> 4. 如没有配置对应profileID的APN相关信息,默认使用空字符串进行拨号。 - - - -## 拨号重连 - -自动重连功能是指,因网络异常、信号差等异常场景导致模组与网络断开连接,当异常场景恢复正常后,模组自动进行拨号重连的行为。 - -### 功能实现 - -根据profileID来设置对应那一路是否使能自动重连。profileID为1的那一路默认使能自动重连功能。 - -1. 设置开启自动重连。使用API和示例代码如下: - - dataCall.setAutoConnect(profileID, enable) - -```python -import dataCall -# 第一路默认开启自动重连 -dataCall.setAutoConnect(2, 1) #开启二路为自动重连 -dataCall.setAutoConnect(3, 1) #开启三路为自动重连 -``` - -2. 关闭自动重连。示例代码如下: - - 如用户关闭了自动重连功能,网络状态发生变化时,如网络断开等情况下,则需要用户进行手动拨号。 - -```python -import dataCall -dataCall.setAutoConnect(1, 0) #关闭第一路自动重连 -dataCall.setAutoConnect(2, 0) #关闭第二路自动重连 -dataCall.setAutoConnect(3, 0) #关闭第三路自动重连 -``` - -### 注册回调函数 - -当网络状态发生变化时,如网络断开、拨号重连成功时,会触发注册的回调函数,告知用户网络状态。 - -1. 定义回调函数并注册。使用API和示例代码如下: - - dataCall.setCallback(fun) - -```python -import dataCall -#定义回调函数 -def netCallback(args): - pdp = args[0] - datacallState = args[1] - if datacallState == 0: - print('*** network {} disconnected.'.format(pdp)) - elif datacallState == 1: - print('*** network {} connected.'.format(pdp)) -#注册回调函数 -dataCall.setCallback(netCallback) - -#此时第一路网络断开 -*** network 1 disconnected. -#此时第一路自动重连成功 -*** network 1 connected -``` - -### 注意事项 - -> 1. 第一路(profileID为1)初始默认为自动重连。 -> 2. 如用户关闭了自动重连功能,则在网路断开恢复时需要用户进行手动拨号。 -> 3. 当注册了回调函数后,在网络断开、拨号重连成功时,会触发注册的回调函数的运行。 - - - -## 不同场景下的使用 - -### 场景一:第一次使用 - -第一次插入sim卡,之前没有配置过PDP Context相关信息,文件系统也没有保存过PDP Context相关信息。 - -模组烧录QuecPython的固件,开机后,默认会自动执行第一路拨号,使用默认APN进行拨号。可以直接查询拨号信息。 - -1. 拨号成功--能够查询到拨号信息 - - 对于大部分公网卡和可以连上公网的专网卡而言,一般利用核心网下发的APN,进行拨号,并保存在文件系统里面,可以通过dataCall.getPDPContext(1)查询PDP Context相关信息。 - -```python -import dataCall -dataCall.getInfo(1, 0) #查询拨号结果信息 -#返回结果 (1, 0, [1, 0, '10.91.44.177', '58.242.2.2', '218.104.78.2']) -dataCall.getPDPContext(1) #查询第一路PDP Context信息 -#返回结果 (0, '3gnet', '', '', 0) -``` - -2. 拨号失败--查询不到拨号信息 - -对于少部分公网卡,不是所有核心网都支持不设置APN;大部分专网卡必须提前设置APN,导致拨号失败。需要进行正常单路拨号,见场景二。 - -```python -import dataCall -dataCall.getInfo(1, 0) #查询拨号结果信息 -#返回结果 (1, 0, [0, 0, '', '', '']) -dataCall.getPDPContext(1) #查询第一路PDP Context信息 -#返回结果 (0, '', '', '', 0) -``` - -### 场景二:正常单路拨号 - -在场景一和场景三拨号失败或不使用默认APN拨号的情况下,使用用户自己配置对应运营商的APN信息的进行拨号。 - -示例一如下: - -```python -import dataCall -from misc import Power -# 用户需要配置的APN信息,根据实际情况修改 -usrConfig = {'apn': '3gnet', 'username': '', 'password': ''} -# 获取第一路的APN信息,确认当前使用的是否是用户指定的APN -pdpCtx = dataCall.getPDPContext(1) -if pdpCtx != -1: - if pdpCtx[1] != pdpConfig['apn']: - # 如果不是用户需要的APN,使用如下方式配置 - ret = dataCall.setPDPContext(1, 0, usrConfig['apn'], usrConfig['username'], usrConfig['password'], 0) - if ret == 0: - print('APN 配置成功。') - # 重启后按照配置的信息进行拨号 - Power.powerRestart() - else: - print('APN 配置失败。') - else: - print('APN 已经配置过了。') -else: - print('获取PDP Context失败。') - -``` - -示例二 如下: - -```python ->>> import dataCall ->>> dataCall.getPDPContext(1) #查询APN -(0, 'cmnet.mnc001.mcc460.gprs', '', '', 0) - #设置成用户指定的APN ->>> dataCall.setPDPContext(1, 0, '3gnet', '', '', 0) - -0 ->>> dataCall.getPDPContext(1) #查询是否配置成功 -(0, '3gnet', '', '', 0) - ->>> from misc import Power ->>> Power.powerDown() #重启 -``` - -重启后: - -1. 拨号成功--能够查询到正确的拨号信息 - -正常情况下使用正确的APN进行拨号,都能够拨号成功,并保存在文件系统里面,可以通过dataCall.getPDPContext(1)查询PDP Context相关信息。 - -```python -import dataCall -dataCall.getInfo(1, 0) #查询第一路拨号信息 -#返回结果 (1, 0, [1, 0, '10.91.44.177', '58.242.2.2', '218.104.78.2']) -dataCall.getPDPContext(1) -#返回结果(0, '3gnet', '', '', 0) -``` - -2. 拨号失败--查询不到拨号信息 - -部分为APN配置错误造成的,如没有配置对应运营商的APN,配置错专网卡特有的APN。需要先确认APN的正确性。 - -部分为网络、SIM卡或模块自身问题造成的,如SIM卡欠费、限制,网络断开,信号质量差等其它原因,需要和运营商或研发人员确认。 - -### 场景三:中途换卡 - -非第一次插入卡,中途换卡(公网换公网、公网换专网、专网换专网,专网换公网),因为之前卡拨号成功过,所以无论有没有配置过PDP Context,文件系统都保存过PDP Context相关信息。 - -如删除过文件系统的PDP Context相关信息,拨号场景见场景一。 - -开机后,默认会自动执行第一路拨号,使用文件系统中配置的APN进行拨号。可以直接查询拨号信息。 - -1. 拨号成功--能够查询到拨号信息 - -对于部分同运营商内换卡或部分公网换公网,APN信息是一样的能够拨号成功; - -对于部分公网换公网或专网换公网,因为核心支持APN纠错,能够拨号成功,但是不建议使用,不是所有核心网都支持这个纠错功能。 - -2. 拨号失败--查询不到拨号信息 - -对于中途换卡场景拨号失败情况基本上都是APN错误导致的,需要进行正常单路拨号,见场景二。 - -### 场景四:多路拨号 - -用户除了使用第一路数据拨号外还需要使用其它路进行数据拨号。 - -1. 由于除了第一路外,其他路没有自带自动拨号,自动重连功能,可以使用手动拨号。 - -```python -import dataCall -#查询第二路拨号信息;由于没有拨号,返回空 -dataCall.getInfo(2, 0) -#返回结果 (2, 0, [0, 0, '', '', '']) -#设置第二路拨号的PDP Context相关信息,如不设置,拨号时使用空字符串进行拨号 -dataCall.setPDPContext(2, 0, '3gnet', '', '', 0) -# 开启第二路开机自动拨号功能 -dataCall.setAutoActivate(2, 1) -# 开启第二路自动重连功能 -dataCall.setAutoConnect(2, 1) -#手动激活第二路PDP Context -dataCall.activate(2) -#返回结果 0 拨号成功 -dataCall.getInfo(2, 0) #查询第二路拨号信息 -#返回结果 (2, 0, [1, 0, '10.91.44.177', '58.242.2.2', '218.104.78.2']) -dataCall.getPDPContext(2) -#返回结果(0, '3gwap', '', '', 0) -``` - -2. 注意事项 - - 在配置其它路为自动拨号,自动重连后,拨号场景同场景一、场景二、场景三。 - - 多路手动拨号成功与失败原因,同场景一、场景二、场景三。 - - 如设置第一路为取消自动拨号,取消自动重连,同场景四。 - diff --git a/docs/Application_guide/zh/network/esim.md b/docs/Application_guide/zh/network/esim.md deleted file mode 100644 index b72415985a4d1df5413eba3eee27df0ea7d443b3..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/network/esim.md +++ /dev/null @@ -1,197 +0,0 @@ -# eSIM LPA - -## 什么是eSIM LPA - -eSIM即嵌入式SIM,也叫eUICC,支持空中写号,可远程动态切换码号;它是SIM卡技术的升级,是由GSMA开发的一个新标准。 -与传统可插拔的SIM卡不同,eSIM一般没有实体SIM卡,而是通过一个嵌入到设备中的芯片(eUICC),下载运营商配置文件并激活运营商服务。详细点说,eSIM是通过使用远程SIM卡配置来激活的,即通过使用远程SIM卡配置平台,通过使用标准化、安全和远程的“空中传送”过程,来完成eSIM配置文件的下载、安装和激活。 -eSIM这种虚拟化和设备集成化的特性,让用户不必来回插拔传统SIM卡,SIM卡号码的切换也将引来全新的用户体验,用户直接通过与终端交互,通过APP或者云端,即可在全球范围内将终端智能设备连接到所选择的当地网络,且可动态切换,使设备可以始终处于优质网速中。 - -对于终端用户而言,Quecpython eSIM方案整体架构可以理解为由DP+服务器,设备提供的LPA以及eSIM组成,我们UE在此充当的就是LPA的功能。 - -## 怎么使用eSIM - -### 硬件设计 - -eSIM功能主要为DP+服务器与eSIM通过UE来进行的内部交互,除前提条件模块需要组网成功外无需其他外围硬件支持。 - -### 软件应用 - -目前GSMA制定了两套eSIM标准方案,分别是针对物联网行业的M2M方案——M2M eSIM;以及直接面向最终用户的消费者解决方案——Consumer eSIM。 -M2M方案需要依赖短信功能进行profile的下载,而消费者解决方案依赖于HTTPS下载。故M2M方案的实施eSIM芯片中必须预置有种子号;消费者解决方案的实施只需要设备能连接网络即可,但目前QuecPython支持的模组型号只能通过蜂窝数据连接网络,所以使用消费者解决方案时也需要预置种子号保证能正常连接网络。 - -下面我们分:`eSIM空中写号流程`;`OTA`;`软件设计`三个部分来分别介绍Quecpython eSIM LPA方案。 - -#### eSIM空中写号流程 - -1、通常MCU/End user通过二维码获取activation code并提供给终端 - -2、模块使用esim中的种子号的网络与DP+服务器进行交互 - -3、从服务器下载profile到esim中(OTA) - -#### OTA - -1、ESIM和DP+服务器互相认证 - -2、从DP+ 服务器下载profile安装包 - -3、安装profile到eSIM并上报结果给DP+ 服务器 - -#### 软件设计 - -注意:执行该脚本之前请关闭开机自动拨号逻辑 - -```Python -import utime -import net -import dataCall -import sim -from sim import esim -from machine import UART -from queue import Queue - -OTAResult_q = None - -def check_sim(): - repeat_count = 10 - while repeat_count >= 0: - sim_state = sim.getStatus() - if sim_state == 1: - print('sim is ready!') - break; - else: - utime.sleep_ms(500) - repeat_count -= 1 - - if repeat_count < 0: - return -1 - #else: - #return 0 - - iccid = sim.getIccid() - print('iccid:{}'.format(iccid)) - - eid = esim.getEid() - print("eUICC id:{}".format(eid)) - if eid == '' or eid == None: - print('get euicc id failed!') - return -1 - - profile_list = esim.getProfileInfo(1) - print(profile_list) - if profile_list[0] == 0: - print('there is no profileinfo in this esim!') - return -1 - return 0 - -def init_esim(): - # - print('Please input the ipVersion of this esim:') - ipVersion=input() - print('Please input the APN of this esim:') - apn=input() - print('Please input the userName of this esim:') - userName=input() - print('Please input the passWord of this esim:') - passWord=input() - print('Please input the authType of this esim:') - authType=input() - net.setApn(1,int(ipVersion),apn,userName,passWord,int(authType),0) - # - print('Please input the net config:') - config=input() - net.setConfig(int(config)) - # - net.setModemFun(0) - repeat_count = 5 - while repeat_count >= 0: - net_state = net.getState() #([11, 26909, 232301323, 7, 0, 466], [0, 26909, 232301323, 7, 0, 0]) - if net_state == -1 or (net_state[1][0] != 1 and net_state[1][0] != 5): - net.setModemFun(1) - repeat_count = 1200 - while repeat_count >= 0: - net_state = net.getState() - if net_state[1][0] == 1 or net_state[1][0] == 5: - print('success to reg network...') - break; - else: - utime.sleep_ms(500) - repeat_count -= 1 - print('wait reg network...') - - if repeat_count >= 0: - return 0 - else: - print('failed to reg network,please check pdn info and netconfig!') - return -1 - else: - utime.sleep_ms(100) - repeat_count -= 1 - if repeat_count < 0: - return -1 - return 0 - -def cb(args): - global OTAResult_q - print('OTA result:{}'.format(args)) - OTAResult_q.put(args) - -def esim_lpa(): - global OTAResult_q - esim.setCallback(cb) - print('please input the activationCode:') - activationCode = input() - print('please input the confirmationCode:') - confirmationCode = input() - ret = esim.profileOTA(activationCode, confirmationCode) - if ret == 0: - result = OTAResult_q.get() - if result == 0: - print('success to download and install the profile!') - while True: - print('Please select the action you want<0:enable profile 1:exit>') - handlerType = input() - if int(handlerType) == 1: - return 0 - elif int(handlerType) == 0: - print('please input the iccid:') - iccid = input() - ret = esim.profileHandle(int(handlerType), iccid) - if ret != 0: - print('enable profile failed!') - return -1 - print('enable profile succeed!') - sim_state = sim.getStatus() - print('sim state:{}'.format(sim_state)) - iccid = sim.getIccid() - print('iccid:{}'.format(iccid)) - profile_list = esim.getProfileInfo(1) - print('profile_list:{}'.format(profile_list)) - break - else: - print('invalid input') - utime.sleep_ms(100) - else: - print('failed to download and install the profile!') - return -1 - else: - print('failed to download and install the profile!') - return -1 - -if __name__ == '__main__': - OTAResult_q = Queue(maxsize=100) - ret = check_sim() - if ret == 0: - ret = init_esim() - if ret == 0: - pdninfo = net.getApn(1,0) - print(pdninfo) - dataCall.setAsynMode(0) - ret = dataCall.start(1,int(pdninfo[0]), pdninfo[1], pdninfo[2], pdninfo[3], int(pdninfo[4])) - if ret == 0: - esim_lpa() -``` - -## eSIM OTA测试 - -成功执行以上脚本之后,默认已经成功从DP+服务器下载并安装profile到eSIM中,并且使能了该profile,如果该profile是可用的,我们可以重启模块,查询当前注网状态,以及iccid,验证是否写号成功 diff --git a/docs/Application_guide/zh/network/http/http_get.md b/docs/Application_guide/zh/network/http/http_get.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/network/http/http_post.md b/docs/Application_guide/zh/network/http/http_post.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/network/mqtt.md b/docs/Application_guide/zh/network/mqtt.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/network/sim.md b/docs/Application_guide/zh/network/sim.md deleted file mode 100644 index 9005a5ba871d49c4d3334bc0554aa3afcfd97c68..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/network/sim.md +++ /dev/null @@ -1,92 +0,0 @@ -# SIM卡 - -## 什么是SIM卡 - -Subscriber Identity Module(简称SIM卡)是一种智能卡,主要用于存储移动用户的身份信息和相关密钥,并实现对移动通信网络的认证和授权。SIM卡通常由芯片、封装材料和金属接点等组成,可以插入手机或其他移动终端中使用。 SIM卡的主要功能是存储移动用户的身份信息和相关密钥,包括手机号码、用户身份标识、IMSI号码、PIN码、PUK码、加密密钥等。通过这些信息,SIM卡可以向移动通信网络提供用户身份认证和授权,以保证通信网络的安全性。 此外,SIM卡还可以存储联系人信息、短信、通话记录、语音信箱号码、网络参数等相关信息,为用户提供各种通信和管理功能,方便用户的日常生活和工作。 - -本文档主要介绍如何使用 QuecPython SIM卡功能,通过本文你将了解到 QuecPython SIM卡的设置及使用方法。 - -## 怎么使用SIM卡功能 - -### 硬件设计 - -SIM卡功能主要是实体SIM卡通过UE来进行交互,除前提条件模块需要组网成功外无需其他外围硬件支持。 - -### 软件应用 - -#### 已实现功能 - -SIM卡是具有以下功能和应用: - -1. 身份认证:SIM卡中存储了用户的身份信息和网络访问权限,可以对用户进行身份认证和授权,以保证通信网络的安全性。 -2. 存储联系人:SIM卡可以存储联系人信息,包括姓名、电话号码等,方便用户进行通信和联系。 -3. 短信功能:SIM卡可以接收和发送短信,方便用户进行信息交流和通信。 -4. 存储通话记录:SIM卡可以存储用户的通话记录,包括通话时间、通话时长等,方便用户进行通话管理和统计。 -5. 存储语音信箱号码:SIM卡可以存储语音信箱号码,方便用户使用语音信箱服务。 -6. 存储网络参数:SIM卡可以存储网络参数,包括APN、用户名、密码等,方便用户进行数据拨号和网络连接。 -7. 存储安全信息:SIM卡可以存储加密密钥、PIN码等安全信息,以保证通信网络的安全性。 - -在QuecPython中这些功能的接口主要在[sim](/../../../API_reference/zh/QuecPython类库/sim.html),[sms](/../../../API_reference/zh/QuecPython类库/sms.html)和[ voiceCall](/../../../API_reference/zh/QuecPython类库/ voiceCall.html)中。 - - 1. SIM卡信息和状态查询:包括获取sim卡的IMSI,ICCID,电话号码和SIM当前状态。 - 2. 电话簿功能:可以存储联系人信息,包括姓名、电话号码等。 - 3. 短信功能:具体见[sms](./sms.md)短信应用指导。 - 4. 电话功能:具体见[ voiceCall](./sms.md)电话应用指导。 - 5. 存储安全信息功能:包括PIN码验证,解锁等。 - 6. 其它功能:如切卡、热插拔和注册回调功能。 - -#### 软件实现示例 - -```python ->>> import sim ->>> sim.getImsi() # 获取sim卡的IMSI -'460105466870381' ->>> sim.getIccid() # 获取sim卡的ICCID -'89860390845513443049' ->>> sim.getPhoneNumber() # 获取sim卡的电话号码 -'+8618166328752' ->>> sim.getStatus() # 查询当前SIM卡状态 0:SIM卡不存在/被移除,1:SIM已经准备好,2:SIM卡已锁定 -1 ->>> sim.writePhonebook(9, 1, 'Tom', '18144786859') # 写电话簿 -0 ->>> sim.readPhonebook(9, 1, 4, "") # 读电话簿 -(4,[(1,'Tom','15544272539'),(2,'Pony','15544272539'),(3,'Jay','18144786859'),(4,'Pondy','15544282538')]) ->>> sim.readPhonebook(9, 0, 0, "Tom") -(1, [(1, 'Tom', '18144786859')]) ->>> sim.readPhonebook(9, 0, 0, "Pony") -(1, [(2, 'Pony', '17744444444')]) ->>> sim.readPhonebook(9, 0, 0, "Pon") # 关键字查询电话簿 -(2, [(2, 'Pony', '17744444444'),(4,'Pondy','15544282538')]) ->>> sim.enablePin("1234") # 开启PIN码验证 -0 ->>> sim.verifyPin("1234") # PIN码验证 -0 ->>> sim.disablePin("1234") # 取消PIN码验证。 -0 ->>> sim.changePin("1234", "4321") # 于更改sim卡PIN码。 -0 ->>> sim.unblockPin("12345678", "0000") # SIM卡解锁:当多次输入PIN码错误需要用PUK码解锁 -0 ->>> sim.setSimDet(1, 0) # 开启SIM卡热插拔, 1表示开启 -0 ->>> sim.getSimDet() # 查询热插拔相关配置 -(1, 0) ->>> sim.getCurSimid() # 获取当前卡,当前是卡1 -0 ->>> sim.switchCard(1) # 切到卡2 -0 ->>> sim.getCurSimid() # 获取当前卡,成功切到卡2 -1 -# 热插拔 注册监听回调函数 -def usrCallback(args): - simstates = args - print('sim states:{}'.format(simstates)) -sim.setCallback(usrCallback) - -# SIM卡切卡状态 注册监听回调函数 -def usrCallback(args): - switchcard_state = args - print('sim switchcard states:{}'.format(switchcard_state)) -sim.setSwitchcardCallback(usrCallback) -``` - diff --git a/docs/Application_guide/zh/network/sms.md b/docs/Application_guide/zh/network/sms.md deleted file mode 100644 index ad245b26384d1e489194fd509e15be937c6ea58c..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/network/sms.md +++ /dev/null @@ -1,92 +0,0 @@ -# 短信 - -## 什么是短信 - -短信(Short Message Service)是一种通过移动通信网络传输文字信息的服务。短信可以用于发送文字、数字、符号等信息,每条短信的长度一般为160个字符。短信可以用于个人通信、商业宣传、政府通知等多种场合。短信服务通常需要用户购买短信套餐或者按条计费。 - -本文档主要介绍如何使用 QuecPython 短信功能,通过本文你将了解到 QuecPython 短信的设置及使用方法。 - -#### 什么是PUD短信 - -PDU(Protocol Data Unit)短信是一种短信传输的编码格式,常用于GSM网络和3G网络中。相对于常见的文本格式短信(也称为ASCII短信),PDU短信可以在同样长度的情况下携带更多的信息。PDU短信包含多个字段,例如消息中心号码、目标号码、短信编码方式、短信内容等。 - -QuecPython 短信功能支持PDU短信的生成和解析。 - -## 怎么使用短信功能 - -### 硬件设计 - -短信功能主要是实体SIM卡通过UE来进行交互,除前提条件模块需要组网成功外无需其他外围硬件支持。 - -### 软件应用 - -#### 已实现功能 - -短信功能是移动通信网络中的一种基本服务,它可以实现文字、图片、音频等多种信息的快速传输和交流。 - -在QuecPython中短信功能的接口主要在[sms](/../../../API_reference/zh/QuecPython类库/sms.html)中。 - -下面是QuecPython短信功能的应用说明: - - 1. 短信发送:用户可以通过UE,向其他用户发送文字信息,支持发送TEXT类型的消息和PDU类型的消息。 - 2. 短信接收:用户可以接收来自其他用户或服务提供商发送的短信信息,包括广告信息、验证码等,支持PDU方式和TEXT方式。 - 3. 短信存储:UE可以将短信保存在本地,方便用户进行查看和管理,包括设置短信存储位置,获取短信数量;查看发送、删除和接收的短信。 - 4. 其它功能:如删除短信、PDU短信解析、短信中心号码配置、注册监听回调函数等 - -#### 软件实现示例 - -```Python ->>> import sms -# 发送TEXT短信 ->>> sms.sendTextMsg('18158626517', '这是一条中文测试短信!', 'UCS2') -0 ->>> sms.sendTextMsg('18158626517', 'Hello, world.', 'GSM') -0 ->>> sms.sendTextMsg('18158626517', '这是一条夹杂中文与英文的测试短信,hello world!', 'UCS2') -0 -# 发送PDU短信 ->>> sms.sendPduMsg('18158626517', 'send pdu msg by GSM mode.', 'GSM') -0 ->>> sms.sendPduMsg('18158626517', 'send pdu msg by UCS2 mode.', 'UCS2') -0 ->>> sms.sendPduMsg('18158626517', '这是一条中文测试短信!通过PDU-UCS2模式', 'UCS2') -0 -# 删除短信 ->>> sms.deleteMsg(2) #删除索引号为2的那一条短信 -0 ->>> sms.deleteMsg(1,4) #删除所有短信 -0 -# 获取短信数量 ->>> sms.getMsgNums() # 执行本行前,先发送一条短信到模块 -1 -# 查询短信存储位置 ->>> sms.getSaveLoc() -(['SM', 2, 50], ['SM', 2, 50], ['SM', 2, 50]) -# 设置短信存储位置 ->>> sms.setSaveLoc('SM','ME','MT') -0 ->>> sms.getSaveLoc() -(['SM', 2, 50], ['ME', 14, 180], ['MT', 2, 50]) ->>> sms.sendPduMsg('+8618226172342', '123456789aa', 'GSM') # 自己给自己发送一条短信 -# 以TEXT方式获取短信内容 ->>> sms.searchTextMsg(0) -('+8618226172342', '123456789aa', 22) -# 以PDU方式获取短信内容 ->>> sms.searchPduMsg(0) # 下面PDU格式短信需要解码后才能正常显示短信内容 -'0891683110305005F0240BA19169256015F70000022141013044230B31D98C56B3DD70B97018' ->>> sms.getPduLength(sms.searchPduMsg(0)) #注意,是获取PDU短信长度,不是上面字符串的长度 -20 -# 解析PDU短信内容 ->>>sms.decodePdu('0891683110305005F0240BA19169256015F70000022141013044230B31D98C56B3DD70B97018',20) -('+8618226172342', '123456789aa', '2021-07-13 09:34:44', 40) -# 获取短信中心号码 ->>> sms.getCenterAddr() -'+8613800551500' -# 注册监听收到短信回调函数 -def cb(args): - index = args[1] - storage = args[2] - print('New message! storage:{},index:{}'.format(storage, index)) -sms.setCallback(cb) -``` - diff --git a/docs/Application_guide/zh/network/socket/tcp_client.md b/docs/Application_guide/zh/network/socket/tcp_client.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/network/socket/tcp_server.md b/docs/Application_guide/zh/network/socket/tcp_server.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/network/socket/udp.md b/docs/Application_guide/zh/network/socket/udp.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/docs/Application_guide/zh/sidebar.yaml b/docs/Application_guide/zh/sidebar.yaml index 351fc4ec14a81cc764e443d32d44bd9ff050fdfc..7e84277e9997f634a4937fe806a8d549b8847504 100644 --- a/docs/Application_guide/zh/sidebar.yaml +++ b/docs/Application_guide/zh/sidebar.yaml @@ -1,11 +1,220 @@ items: -- label: 音频 - file: audio/README.md -- label: 蓝牙 - file: bluetooth/README.md -- label: GNSS定位 +- label: 背景知识 + file: background/README.md + items: + - label: 物联网与低代码开发 + file: background/iot-and-low-code.md + - label: 无线通信模块简介 + file: background/wireless-modules.md + - label: QuecPython 简介 + file: background/about-qpy.md + - label: 硬件平台支持 + file: background/hardware-platform.md + - label: 项目开发流程 + file: background/project-dev.md +- label: 系统功能 + file: system/README.md + items: + - label: 设备信息 + file: system/info.md + - label: 电源管理 + file: system/power-manager.md + - label: 存储设备 + file: system/memory-device.md + - label: 文件系统 + file: system/fs.md + - label: 多线程 + file: system/thread.md + - label: Core dump + file: system/core-dump.md + - label: 系统功耗 + file: system/power-consumption.md + - label: 异常处理 + file: system/exception-handling.md + - label: 系统时间 + file: system/sys-time.md + - label: 加密与安全 + file: system/encryption-and-security.md + - label: 内存管理 + file: system/memory-manager.md + - label: 数据格式转换 + file: system/data-formatting.md + - label: 看门狗 + file: system/watch-dog-timer.md + - label: 日志输出 + file: system/log.md +- label: 硬件功能 + file: hardware/README.md + items: + - label: 外设接口 + file: hardware/peripheral-interfaces/README.md + items: + - label: GPIO + file: hardware/peripheral-interfaces/GPIO.md + - label: UART + file: hardware/peripheral-interfaces/UART.md + - label: SPI + file: hardware/peripheral-interfaces/SPI.md + - label: IIC + file: hardware/peripheral-interfaces/IIC.md + - label: PWM + file: hardware/peripheral-interfaces/PWM.md + - label: ADC + file: hardware/peripheral-interfaces/ADC.md + - label: USB 接口 + file: hardware/USB-interfaces.md + - label: 显示设备 + file: hardware/display.md + - label: 传感器 + file: hardware/sensors/README.md + items: + - label: 加速度传感器 + file: hardware/sensors/accelerometer/README.md + items: + - label: LIS2DH12TR 传感器 + file: hardware/sensors/accelerometer/lis2dh12tr.md + - label: 温湿度传感器 + file: hardware/sensors/humiture/README.md + items: + - label: HDC1080 传感器 + file: hardware/sensors/humiture/hdc1080.md + - label: 照度传感器 + file: hardware/sensors/light-sensor/README.md + items: + - label: OPT3001 传感器 + file: hardware/sensors/light-sensor/opt3001.md + - label: 音频驱动 + file: hardware/audio-driver.md + - label: 摄像头 + file: hardware/camera.md +- label: 多媒体 + file: multi-media/README.md + items: + - label: GUI + file: multi-media/GUI.md + - label: 二维码 + file: multi-media/qrcode.md + - label: 音频播放 + file: multi-media/audio-play.md +- label: 网络与通信 + file: network-comm/README.md + items: + - label: 网卡 + file: network-comm/nic/README.md + items: + - label: 支持的网卡 + file: network-comm/nic/support/README.md + - label: 蜂窝无线网卡 + file: network-comm/nic/cellular/README.md + items: + - label: 基础概念 + file: network-comm/nic/cellular/common-concepts.md + - label: 硬件结构 + file: network-comm/nic/cellular/hardware-arch.md + - label: 网卡初始化流程 + file: network-comm/nic/cellular/initialization.md + - label: API 说明 + file: network-comm/nic/cellular/api-instruction.md + - label: 原理机制 + file: network-comm/nic/cellular/mechanism.md + - label: 应用场景 + file: network-comm/nic/cellular/application.md + - label: 异常处理 + file: network-comm/nic/cellular/exception-handling.md + - label: 常见问题 + file: network-comm/nic/cellular/FAQ.md + - label: 以太网卡 + file: network-comm/nic/ethernet/README.md + items: + - label: 模组通过以太网卡访问网络 + file: network-comm/nic/ethernet/ethernet-wan-example.md + - label: 模组通过 4G 转以太网为外部设备提供网络 + file: network-comm/nic/ethernet/ethernet-lan-example.md + - label: Wi-Fi 无线网卡 + file: network-comm/nic/wifi/README.md + items: + - label: Station 模式 + file: network-comm/nic/wifi/wifi-station-example.md + - label: AP 模式 + file: network-comm/nic/wifi/wifi-ap-example.md + - label: USB 网卡 + file: network-comm/nic/usbnet/README.md + items: + - label: Windows 系统使用 USB 网卡 + file: network-comm/nic/usbnet/usbnet-windows-example.md + - label: Linux 系统使用 USB 网卡 + file: network-comm/nic/usbnet/usbnet-linux-example.md + - label: Android 系统使用 USB 网卡 + file: network-comm/nic/usbnet/usbnet-android-example.md + - label: 网络配置 + file: network-comm/nic/network-config.md + - label: 网络协议 + file: network-comm/net-protocols/README.md + items: + - label: TCP-UDP 协议 + file: network-comm/net-protocols/tcp-udp.md + - label: HTTP 协议 + file: network-comm/net-protocols/http.md + - label: FTP 协议 + file: network-comm/net-protocols/ftp.md + - label: MQTT 协议 + file: network-comm/net-protocols/mqtt.md + - label: NTP 协议 + file: network-comm/net-protocols/ntp.md + - label: LWM2M 协议 + file: network-comm/net-protocols/lwm2m.md + - label: RTMP 协议 + file: network-comm/net-protocols/rtmp.md + - label: 网络异常处理 + file: network-comm/exception-handling.md + - label: 云平台 + file: network-comm/clouds.md + - label: SIM卡 + file: network-comm/sim.md + - label: 电话 + file: network-comm/phone.md + - label: 短信 + file: network-comm/phone.md + - label: Wi-Fi Scan + file: network-comm/wifi-scan.md + - label: 蓝牙 + file: network-comm/bluetooth.md +- label: 固件升级 + file: firmware-upgrade/README.md + items: + - label: 固件烧录 + file: firmware-upgrade/firmware-burning.md + - label: OTA 升级 + file: firmware-upgrade/firmware-ota.md + - label: APP 升级 + file: firmware-upgrade/app-ota.md +- label: GNSS 应用 file: gnss/README.md -- label: USB网卡 - file: usbnet/README.md -- label: WiFi扫描 - file: wifiscan/README.md \ No newline at end of file +- label: 开发工具 + file: dev-tools/README.md + items: + - label: QPYcom 使用教程 + file: dev-tools/QPYcom/README.md + items: + - label: 界面介绍 + file: dev-tools/QPYcom/qpycom-gui.md + - label: 固件下载 + file: dev-tools/QPYcom/qpycom-dw.md + - label: 命令交互 + file: dev-tools/QPYcom/qpycom-repl.md + - label: 固件合并 + file: dev-tools/QPYcom/qpycom-merge.md + - label: 扩展功能 + file: dev-tools/QPYcom/qpycom-ext.md + - label: Thonny 使用教程 + file: dev-tools/Thonny/README.md +- label: 生产与测试 + file: production-and-testing/README.md +- label: 开源组件 + file: open-source-components/README.md +- label: 应用框架 + file: app-framework/README.md +- label: 产品方案 + file: solutions/README.md +- label: Helios 组件 + file: helios-components/README.md diff --git a/docs/Getting_started/zh/high-level-component/README.md b/docs/Application_guide/zh/system/README.md similarity index 100% rename from docs/Getting_started/zh/high-level-component/README.md rename to docs/Application_guide/zh/system/README.md diff --git a/docs/Getting_started/zh/os/data-formattng.md b/docs/Application_guide/zh/system/data-formatting.md similarity index 100% rename from docs/Getting_started/zh/os/data-formattng.md rename to docs/Application_guide/zh/system/data-formatting.md diff --git a/docs/Getting_started/zh/os/os-info.md b/docs/Application_guide/zh/system/info.md similarity index 81% rename from docs/Getting_started/zh/os/os-info.md rename to docs/Application_guide/zh/system/info.md index 8f23eafed3c31915bd8aa773b8a20cd3d870259c..6fb77d050c80cc78287132532beeb181fff3948d 100644 --- a/docs/Getting_started/zh/os/os-info.md +++ b/docs/Application_guide/zh/system/info.md @@ -1,134 +1,134 @@ -# 系统信息 - -本文主要介绍如何使用 uos、usys、modem 等模块查询模组的固件信息、模组剩余内存大小等用户经常关注的信息,本文将不断补充,以方便用户了解模组的基本信息。 - -## 查询固件版本信息 - -```python ->>> import uos -# 查询QuecPython固件版本信息(QuecPython独有命名规则) ->>> uos.uname() -# 返回值(EC600U型号为例) -# ('sysname=EC600U-CNLB', 'nodename=EC600U', 'release=1.13.0', 'version=v1.12 on Sat_Nov_19_2022_5:29:48_PM', 'machine=EC600U with QUECTEL', 'qpyver=V0002') -``` - -如上所示我们可以查询到模组型号 machine = EC600U with QUECTEL,即 EC600U,但固件中对型号的区分是 sysname = EC600U-CNLB,即这个固件可以在 EC600UCNLB 这个子型号中使用,一般严格遵循字母数字一一对应原则,但是也有例外情况存在,固件具体适用的模组型号以下载区的固件描述和移远官方技术人员描述为准。还可以查询到固件的编译日期和 microPython 版本 version = v1.12 on Sat_Nov_19_2022_5: 29: 48_PM,一般用于判断 BETA 版本(仅用于测试严禁量产的版本)新旧,由于 BETA 版本仅仅用于测试,所以 QuecPython 版本并非是正式发布的,故版本号信息不会变更,只能通过编译时间来确认版本,建议用户进行版本控制时也使用这种方式判断版本,拿到测试版本进行测试时也可以方便的进行版本控制。qpyver = V0002 这个字段即为 QuecPython 固件官网发布的正式版本号,在此不过多赘述。 - -为什么要查询固件版本? - -在开发的过程中难免会遇到一些问题,并找到官方人员或技术前辈咨询,那么此时需要提供的信息中,固件版本就是必不可少的,除此之外使用 uos 库查询固件版本还有如下方法: - -```python ->>> import uos ->>> uos.uname2() -# 返回值(以EC600U型号为例,需要注意此方法较老的固件版本不支持) -# (sysname='EC600U-CNLB', nodename='EC600U', release='1.13.0', version='v1.12 on Sat_Nov_19_2022_5:29:48_PM', machine='EC600U with QUECTEL', qpyver='V0002') -``` - -如上所示,此接口和 uos.uname()返回的信息是一样的,只是返回值兼容了 microPython 的用法,更方便用户在脚本中访问返回值中“=”右边的信息,具体用法参考 [uos - 基本系统服务](../../../API_reference/zh/QuecPython标准库/uos.html)。 - -除此之外我们还可以使用如下方法获取固件版本信息: - -```python ->>> import modem -# 查询Quectel固件版本信息(Quectel通用命名规则) ->>> modem.getDevFwVersion() -# 常见返回值类型(以EC600U型号为例) -# 'EC600UCNLBR03A01M08_OCPU_QPY_BETA1207' -# 'EC600UCNLBR03A02M08_OCPU_QPY' -``` - -如上所示,常见的两种返回值主要区别为是否包含 BETA 字段,包含 BETA 字段的固件为非正式发布版本固件,仅能用于测试,不用于项目量产,BETA 后为编译固件的日期,12 月 7 日。我们一般仅需关注是否包含 BETA 字段和 BETA 后的日期。 - -此版本号除了使用 QuecPython 脚本查询外还可以使用 AT+GMR 命令进行查询,在这里不是我们的重点。 - -AT 命令仅需一行即可查询,脚本需要两行,怎么解决? - -```python ->>> import modem;modem.getDevFwVersion() # 两行合为一行,交互界面回车即可返回结果 -``` - -## 查询模组运行内存和文件系统剩余空间 - -```python -import gc -import uos - -usr = uos.statvfs("/usr") - -print('获取usr目录状态信息:', usr) -print('f_bsize – 文件系统块大小,单位字节:', usr[0]) -print('f_bfree – 可用块数:', usr[3]) -print('usr剩下总空间 {} 字节'.format(usr[0] * usr[3])) -print('usr剩下总空间 {} KB'.format((usr[0] * usr[3])/1024)) -print('usr剩下总空间 {} MB'.format((usr[0] * usr[3]) / 1024 / 1024)) - -bak = uos.statvfs("/bak") - -print('获取bak目录状态信息:', bak) -print('f_bsize – 文件系统块大小,单位字节:', bak[0]) -print('f_bfree – 可用块数:', bak[3]) -print('bak剩下总空间 {} 字节'.format(bak[0] * bak[3])) -print('bak剩下总空间 {} KB'.format((bak[0] * bak[3])/1024)) -print('bak剩下总空间 {} MB'.format((bak[0] * bak[3]) / 1024 / 1024)) - -mem = gc.mem_free() -print('剩余可用RAM空间:{}KB'.format(mem / 1024)) -``` - -如上所示我们使用 uos.statvfs 这个函数查询了根目录下'usr'和'bak'两个文件夹的状态信息,可以获取到文件夹的剩余空间大小。关于根目录和这两个文件夹做如下简介,根目录:对于用户来说是不允许操作的,所以对根目录做的任何操作都会导致报 OSerror 异常。'usr'目录:此目录是允许客户做文件读写操作的,通常客户代码等文件均是主要存放在这里,如需扩展请看 [外扩存储](./../hardware-advanced/ext-storage.html) 章节。'bak'目录:此目录是用于量产时存放客户需要备份的重要文件,可读不可写,存放重要文件请看 [备份分区和数据安全区的使用](./../mass-production/data-backup.html) 章节。 - -其他 uos 相关使用请查看 [uos - 基本系统服务](../../../API_reference/zh/QuecPython标准库/uos.html)。 - -## 查询 microPython 虚拟机版本 - -```python ->>> try:import usys as sys -... except ImportError:import sys ->>> sys.implementation -# 返回值 -# (name='micropython', version=(1, 13, 0), mpy=10245) -``` - -如上所示可以直接查询到 microPython 虚拟机版本是 1.13.0 版本,虽然可以查询到 microPython 虚拟机版本,但是不少用法仍和 microPython 不同,需要注意。以上示例使用 try-except 语句进行 import 的原因就是不同时期的固件 microPython 虚拟机版本不同,部分模块出现了更名的情况,为避免出现异常导致程序退出运行,使用了 python 的异常处理语法。后续 QuecPython 将为避免出现此类情况,在移植 microPython 新虚拟机时即将模块命名同历史版本进行兼容,方便用户的使用。 - -## 查询 microPython 语言版本 - -```python ->>> try:import usys as sys -... except ImportError:import sys ->>> sys.version -# 返回值 -# '3.4.0' ->>> sys.version_info -# 返回值 -# (3, 4, 0) -``` - -如上所示,使用了两个 API 进行的查询,两个 API 的主要差异为返回值形式不同。当前查询的 microPython 语言版本是 3.4.0,在语法上是兼容电脑端 CPython 的 3.4.0 版本,后续是否有变更可以使用此接口查询。 - -## 查询设备的 IMEI - -```python ->>> import modem ->>> modem.getDevImei() -# 返回值 -# '866327040830317' -``` - -如上所示,获取设备的 IMEI 号虽然十分简单,但是又特别的常用,所以在这里介绍一下如何查询。那么,IMEI 是什么呢? - -物联网模块的 IMEI 是国际移动设备身份码(International Mobile Equipment Identity)的缩写,它是用于识别物联网模块的唯一标识符。在物联网场景中,IMEI 可以用于以下几个方面: - -1. 设备识别和管理:通过 IMEI,可以唯一地识别和管理物联网设备,包括设备的制造商、型号和版本等信息。这些信息对于设备的维护和升级非常重要。 -2. 安全性和防盗:IMEI 可以用于防止设备被盗或丢失。如果设备的 IMEI 被注册到一个中央数据库中,就可以通过该数据库来追踪设备的位置和使用情况。 -3. 远程管理和控制:IMEI 可以用于远程管理和控制物联网设备。例如,如果设备出现故障或需要更新固件,就可以通过 IMEI 来远程诊断和修复设备。 -4. 数据统计和分析:IMEI 可以用于统计和分析物联网设备的使用情况。例如,可以根据 IMEI 来确定设备的使用时间、位置、频率和使用模式等信息,以便更好地了解设备的使用情况和优化设备的性能。 - -总之,IMEI 是物联网设备非常重要的标识符,可以用于设备管理、安全性、远程管理和数据分析等方面。 - -使用 modem 库查询其他设备信息不再赘述,请查看 [modem - 设备相关](./../API_reference/zh/QuecPython类库/modem.html)。 - -## 总结 - -用户经常关注的模块相关的,可以通过 microPython 脚本查询到的信息均在此做了介绍,如有疑问或更好的建议欢迎联系我们,也可以直接向我们提交文档贡献,后续本文将继续完善和补充。 +# 系统信息 + +本文主要介绍如何使用 `uos`、`usys`、`modem` 等模块查询模组的固件信息、模组剩余内存大小等用户经常关注的信息,本文将不断补充,以方便用户了解模组的基本信息。 + +## 查询固件版本信息 + +```python +# 查询QuecPython固件版本信息(QuecPython独有命名规则) +>>> import uos +>>> uos.uname() +# 返回值(EC600U型号为例) +# ('sysname=EC600U-CNLB', 'nodename=EC600U', 'release=1.13.0', 'version=v1.12 on Sat_Nov_19_2022_5:29:48_PM', 'machine=EC600U with QUECTEL', 'qpyver=V0002') +``` + +如上所示,我们可以查询到模组型号 `machine = EC600U with QUECTEL`,即 EC600U,但固件中对型号的区分是 sysname = EC600U-CNLB,即这个固件可以在 EC600UCNLB 这个子型号中使用,一般严格遵循字母数字一一对应原则,但是也有例外情况存在,固件具体适用的模组型号以下载区的固件描述和移远官方技术人员描述为准。还可以查询到固件的编译日期和 microPython 版本 version = v1.12 on Sat_Nov_19_2022_5: 29: 48_PM,一般用于判断 BETA 版本(仅用于测试严禁量产的版本)新旧,由于 BETA 版本仅仅用于测试,所以 QuecPython 版本并非是正式发布的,故版本号信息不会变更,只能通过编译时间来确认版本,建议用户进行版本控制时也使用这种方式判断版本,拿到测试版本进行测试时也可以方便的进行版本控制。qpyver = V0002 这个字段即为 QuecPython 固件官网发布的正式版本号,在此不过多赘述。 + +为什么要查询固件版本? + +在开发的过程中难免会遇到一些问题,并找到官方人员或技术前辈咨询,那么此时需要提供的信息中,固件版本就是必不可少的,除此之外使用 uos 库查询固件版本还有如下方法: + +```python +>>> import uos +>>> uos.uname2() +# 返回值(以EC600U型号为例,需要注意此方法较老的固件版本不支持) +# (sysname='EC600U-CNLB', nodename='EC600U', release='1.13.0', version='v1.12 on Sat_Nov_19_2022_5:29:48_PM', machine='EC600U with QUECTEL', qpyver='V0002') +``` + +如上所示,此接口和 uos.uname()返回的信息是一样的,只是返回值兼容了 microPython 的用法,更方便用户在脚本中访问返回值中“=”右边的信息,具体用法参考 [uos - 基本系统服务](../../../API_reference/zh/QuecPython标准库/uos.html)。 + +除此之外我们还可以使用如下方法获取固件版本信息: + +```python +>>> import modem +# 查询Quectel固件版本信息(Quectel通用命名规则) +>>> modem.getDevFwVersion() +# 常见返回值类型(以EC600U型号为例) +# 'EC600UCNLBR03A01M08_OCPU_QPY_BETA1207' +# 'EC600UCNLBR03A02M08_OCPU_QPY' +``` + +如上所示,常见的两种返回值主要区别为是否包含 BETA 字段,包含 BETA 字段的固件为非正式发布版本固件,仅能用于测试,不用于项目量产,BETA 后为编译固件的日期,12 月 7 日。我们一般仅需关注是否包含 BETA 字段和 BETA 后的日期。 + +此版本号除了使用 QuecPython 脚本查询外还可以使用 AT+GMR 命令进行查询,在这里不是我们的重点。 + +AT 命令仅需一行即可查询,脚本需要两行,怎么解决? + +```python +>>> import modem;modem.getDevFwVersion() # 两行合为一行,交互界面回车即可返回结果 +``` + +## 查询模组运行内存和文件系统剩余空间 + +```python +import gc +import uos + +usr = uos.statvfs("/usr") + +print('获取usr目录状态信息:', usr) +print('f_bsize – 文件系统块大小,单位字节:', usr[0]) +print('f_bfree – 可用块数:', usr[3]) +print('usr剩下总空间 {} 字节'.format(usr[0] * usr[3])) +print('usr剩下总空间 {} KB'.format((usr[0] * usr[3])/1024)) +print('usr剩下总空间 {} MB'.format((usr[0] * usr[3]) / 1024 / 1024)) + +bak = uos.statvfs("/bak") + +print('获取bak目录状态信息:', bak) +print('f_bsize – 文件系统块大小,单位字节:', bak[0]) +print('f_bfree – 可用块数:', bak[3]) +print('bak剩下总空间 {} 字节'.format(bak[0] * bak[3])) +print('bak剩下总空间 {} KB'.format((bak[0] * bak[3])/1024)) +print('bak剩下总空间 {} MB'.format((bak[0] * bak[3]) / 1024 / 1024)) + +mem = gc.mem_free() +print('剩余可用RAM空间:{}KB'.format(mem / 1024)) +``` + +如上所示我们使用 uos.statvfs 这个函数查询了根目录下'usr'和'bak'两个文件夹的状态信息,可以获取到文件夹的剩余空间大小。关于根目录和这两个文件夹做如下简介,根目录:对于用户来说是不允许操作的,所以对根目录做的任何操作都会导致报 OSerror 异常。'usr'目录:此目录是允许客户做文件读写操作的,通常客户代码等文件均是主要存放在这里,如需扩展请看 [外扩存储](./../hardware-advanced/ext-storage.html) 章节。'bak'目录:此目录是用于量产时存放客户需要备份的重要文件,可读不可写,存放重要文件请看 [备份分区和数据安全区的使用](./../mass-production/data-backup.html) 章节。 + +其他 uos 相关使用请查看 [uos - 基本系统服务](https://python.quectel.com/doc/API_reference/zh/stdlib/uos.html)。 + +## 查询 microPython 虚拟机版本 + +```python +>>> try:import usys as sys +... except ImportError:import sys +>>> sys.implementation +# 返回值 +# (name='micropython', version=(1, 13, 0), mpy=10245) +``` + +如上所示可以直接查询到 microPython 虚拟机版本是 1.13.0 版本,虽然可以查询到 microPython 虚拟机版本,但是不少用法仍和 microPython 不同,需要注意。以上示例使用 try-except 语句进行 import 的原因就是不同时期的固件 microPython 虚拟机版本不同,部分模块出现了更名的情况,为避免出现异常导致程序退出运行,使用了 python 的异常处理语法。后续 QuecPython 将为避免出现此类情况,在移植 microPython 新虚拟机时即将模块命名同历史版本进行兼容,方便用户的使用。 + +## 查询 microPython 语言版本 + +```python +>>> try:import usys as sys +... except ImportError:import sys +>>> sys.version +# 返回值 +# '3.4.0' +>>> sys.version_info +# 返回值 +# (3, 4, 0) +``` + +如上所示,使用了两个 API 进行的查询,两个 API 的主要差异为返回值形式不同。当前查询的 microPython 语言版本是 3.4.0,在语法上是兼容电脑端 CPython 的 3.4.0 版本,后续是否有变更可以使用此接口查询。 + +## 查询设备的 IMEI + +```python +>>> import modem +>>> modem.getDevImei() +# 返回值 +# '866327040830317' +``` + +如上所示,获取设备的 IMEI 号虽然十分简单,但是又特别的常用,所以在这里介绍一下如何查询。那么,IMEI 是什么呢? + +物联网模块的 IMEI 是国际移动设备身份码(International Mobile Equipment Identity)的缩写,它是用于识别物联网模块的唯一标识符。在物联网场景中,IMEI 可以用于以下几个方面: + +1. 设备识别和管理:通过 IMEI,可以唯一地识别和管理物联网设备,包括设备的制造商、型号和版本等信息。这些信息对于设备的维护和升级非常重要。 +2. 安全性和防盗:IMEI 可以用于防止设备被盗或丢失。如果设备的 IMEI 被注册到一个中央数据库中,就可以通过该数据库来追踪设备的位置和使用情况。 +3. 远程管理和控制:IMEI 可以用于远程管理和控制物联网设备。例如,如果设备出现故障或需要更新固件,就可以通过 IMEI 来远程诊断和修复设备。 +4. 数据统计和分析:IMEI 可以用于统计和分析物联网设备的使用情况。例如,可以根据 IMEI 来确定设备的使用时间、位置、频率和使用模式等信息,以便更好地了解设备的使用情况和优化设备的性能。 + +总之,IMEI 是物联网设备非常重要的标识符,可以用于设备管理、安全性、远程管理和数据分析等方面。 + +使用 modem 库查询其他设备信息不再赘述,请查看 [modem - 设备相关](./../API_reference/zh/QuecPython类库/modem.html)。 + +## 总结 + +用户经常关注的模块相关的,可以通过 microPython 脚本查询到的信息均在此做了介绍,如有疑问或更好的建议欢迎联系我们,也可以直接向我们提交文档贡献,后续本文将继续完善和补充。 diff --git a/docs/Getting_started/zh/os/log.md b/docs/Application_guide/zh/system/log.md similarity index 55% rename from docs/Getting_started/zh/os/log.md rename to docs/Application_guide/zh/system/log.md index 494909cfbcbd6ddc24b5e4a0e3283271bed3a264..899c03214bc892d73849d987c664d0157b7a7428 100644 --- a/docs/Getting_started/zh/os/log.md +++ b/docs/Application_guide/zh/system/log.md @@ -1,36 +1,34 @@ # 日志功能 -当程序运行出现问题时,日志记录是一种非常有用的工具,它可以帮助我们追踪和定位问题。在MicroPython中,可以使用log模块来记录程序运行中的信息。本文将介绍log模块的使用方法和常见功能。 - - +当程序运行出现问题时,日志记录是一种非常有用的工具,它可以帮助我们追踪和定位问题。在 MicroPython 中,可以使用 log 模块来记录程序运行中的信息。本文将介绍 log 模块的使用方法和常见功能。 ## 日志级别 ### `log.DEBUG` -常量,用来标识LOG等级,最详细的日志信息,通常只在开发和调试时使用。 +常量,用来标识 LOG 等级,最详细的日志信息,通常只在开发和调试时使用。 ### `log.INFO` -常量,用来标识LOG等级,确认一切按预期运行。 +常量,用来标识 LOG 等级,确认一切按预期运行。 ### `log.WARNING` -常量,用来标识LOG等级,表明发生了一些意外,或者指示可能出现问题的情况,但仍然可以继续执行。 +常量,用来标识 LOG 等级,表明发生了一些意外,或者指示可能出现问题的情况,但仍然可以继续执行。 ### `log.ERROR` -常量,用来标识LOG等级,由于更严重的问题,应用程序已不能执行某些功能。 +常量,用来标识 LOG 等级,由于更严重的问题,应用程序已不能执行某些功能。 ### `log.CRITICAL` -常量,用来标识LOG等级,指出应用程序中的严重错误,可能导致应用程序停止运行。 +常量,用来标识 LOG 等级,指出应用程序中的严重错误,可能导致应用程序停止运行。 ## 日志设置 ### `log.basicConfig` -设置日志输出级别,默认为log.INFO,系统只会输出 level 数值大于或等于该 level 的的日志结果。 +设置日志输出级别,默认为 log.INFO,系统只会输出 level 数值大于或等于该 level 的的日志结果。 ```python log.basicConfig(level) @@ -38,7 +36,7 @@ log.basicConfig(level) **参数描述:** -* `level`-日志等级 +- `level` - 日志等级 **返回值描述:** @@ -53,7 +51,7 @@ log.basicConfig(level=log.INFO) ### `log.set_output` -设置日志输出的位置,目前只支持uart和usys.stdout +设置日志输出的位置,目前只支持 uart 和 usys.stdout ```python log.set_output(out) @@ -61,7 +59,7 @@ log.set_output(out) **参数描述:** -* `out` - 日志输出位置,输出到指定串口或者交互口,默认不设置为交互口输出,类型参考示例 +- `out` - 日志输出位置,输出到指定串口或者交互口,默认不设置为交互口输出,类型参考示例 **返回值描述:** @@ -93,7 +91,7 @@ Testlog.info("this is a Test log") # 会输出到交互口 ### `log.getLogger` -获取log对象,对象支持输出不同等级的log信息。 +获取 log 对象,对象支持输出不同等级的 log 信息。 ```python Testlog = log.getLogger(name) @@ -101,15 +99,15 @@ Testlog = log.getLogger(name) **参数描述:** -* `name` - 当前log对象的主题信息,字符串类型 +- `name` - 当前 log 对象的主题信息,字符串类型 **返回值描述:** -* log操作句柄,也可理解成log对象,拥有log输出的方法。 +- log 操作句柄,也可理解成 log 对象,拥有 log 输出的方法。 ### `log.debug` -输出DEBUG级别的日志。 +输出 DEBUG 级别的日志。 ```python Testlog.debug(msg) @@ -117,11 +115,11 @@ Testlog.debug(msg) **参数描述:** -* `msg` - 日志内容,字符串类型 +- `msg` - 日志内容,字符串类型 ### `log.info` -输出INFO级别的日志。 +输出 INFO 级别的日志。 ```python Testlog.info(msg) @@ -129,11 +127,11 @@ Testlog.info(msg) **参数描述:** -* `msg` - 日志内容,字符串类型 +- `msg` - 日志内容,字符串类型 ### `log.warning` -输出WARNING级别的日志。 +输出 WARNING 级别的日志。 ```python Testlog.warning(msg) @@ -141,11 +139,11 @@ Testlog.warning(msg) **参数描述:** -* `msg` - 日志内容,字符串类型 +- `msg` - 日志内容,字符串类型 ### `log.error` -输出ERROR级别的日志。 +输出 ERROR 级别的日志。 ```python Testlog.error(msg) @@ -153,11 +151,11 @@ Testlog.error(msg) **参数描述:** -* `msg` - 日志内容,字符串类型 +- `msg` - 日志内容,字符串类型 ### `log.critical` -输出CRITICAL级别的日志。 +输出 CRITICAL 级别的日志。 ```python Testlog.critical(msg) @@ -165,8 +163,7 @@ Testlog.critical(msg) **参数描述:** -* `msg` - 日志内容,字符串类型 - +- `msg` - 日志内容,字符串类型 **示例:** @@ -184,6 +181,3 @@ Testlog.critical("Test critical message!!") Testlog.info("Test info message!!") Testlog.warning("Test warning message!!") ``` - - - diff --git a/docs/Application_guide/zh/system/power-consumption.md b/docs/Application_guide/zh/system/power-consumption.md new file mode 100644 index 0000000000000000000000000000000000000000..33deb57498da7892e25ce15ad44f6b06c41404ae --- /dev/null +++ b/docs/Application_guide/zh/system/power-consumption.md @@ -0,0 +1,522 @@ +# 功耗管理 + +## 模组支持的功耗模式 + +蜂窝通信模组支持多种工作模式,每种模式的功耗各不相同,常见的工作模式有以下几种: + +**ACTIVE**:模组进行 LTE 数传、GSM 通话或 RTOS 在运行逻辑时的状态,耗流受到具体业务和网络通信制式的影响,CPU 本身耗流和网络射频功率都有所不同,故实际功耗在不同工况下会有较大差异。 + +**IDEL**:此时模组处于空闲状态,硬件正常在电,RTOS 保持运行,但没有任何业务。有业务启动或网络业务呼入时,会立即恢复运行。ECX00U 平台会在 IDEL 模式下降低时钟频率,进入轻睡眠状态。 + +**休眠**:休眠模式的前提是模组处于空闲状态且使能 autosleep,进入休眠模式后,RTOS 暂停运行,模组的时钟频率会变慢,部分外设控制器(UART、SPI 等)下电,同时只保留部分中断控制器,达到减小耗流的目的。 + +**PSM**:PSM 模式是 3GPP 协议所规定的一种低功耗模式,这种模式下,模组只会周期性的唤醒并执行业务,其余时间都处于 PSM 休眠中。PSM 休眠时,模组行为和耗流都近似于关机。 + +**关机**:模组完全下电的状态,此时 BB 芯片和外设控制器完全关闭,但 PMIC 仍是在电的。一般可由 Powerkey 或者 RTC 唤醒。 + +**各平台工作模式支持情况和耗流:** + +| | ECX00U | ECX00G | ECX00N | ECX00M | ECX00E | BG95/BG77 | EC21 | +| :------------------- | -------- | ------- | -------- | -------- | ------- | ------------------- | ------- | +| IDLE(LTE FDD@64ms) | 12.34 mA | 10.1 mA | 17.36 mA | 16.72 mA | 4 mA | 18.9 mA(catm@128ms) | 16.9 mA | +| SLEEP(LTE FDD@64ms) | 2.05mA | 1.99 mA | 1.64 mA | 1.37 mA | 0.64 mA | 1.89 mA(catm@128ms) | 2.74 mA | +| SLEEP(CFUN0) | 1.29mA | 1.03 mA | 0.96 mA | 0.8 mA | 60 μA | 0.575mA | 1.00 mA | +| PSM | 15 μA | 15 μA | 不支持 | 不支持 | 5 μA | 5.10 μA | | +| 关机 | 33 μA | 15 μA | 20 μA | 38 μA | 3 μA | 14.87 μA | 7 μA | + +## 休眠 + +休眠是通信模组最常用的一种低功耗模式。模组处于空闲状态时,若 autosleep 被使能,则模组会进入休眠状态。此时,模组会关闭部分 IP 核(如外设控制器和中断控制器等)并且降低时钟频率,从而实现耗流的降低。 + +### RTOS 休眠原理 + +以 freertos + ARM Cortex-M3 为例: + +**休眠检测机制:** +freertos 在上电初始化时,会默认建立一个名为 IDLE 的 task,优先级为 0,也就是说,这个 task 只在 RTOS 不运行其它 task 时才会被调度到。休眠相关处理就在 IDLE TASK 中被执行。freertos 在 IDLE TASK 中,会检测模组的外设是否正在使用,网络是否有数据等休眠条件等,满足休眠条件且 autosleep 使能时,控制模组进入休眠模式。 + +**休眠机制:** +freertos 的休眠使用\_WFI 指令进入 arm 的休眠模式,此时,高速时钟将会关闭,外设会在保存上下文后断电,arm 的内核会停止运行,但 NVIC(中断控制器)和 sram 仍可保持运行,任意中断都可将内核唤醒。 + +**唤醒机制:** +任意中断均可将内核唤醒,唤醒后内核立即恢复运行,高速时钟恢复输出,为断电的外设恢复上下文,最后恢复应用程序的运行。这种休眠模式不仅实现了耗流的降低,还能较快恢复应用程序的运行。 + +**休眠流程图例:** + +![Sleep](../media/system/power-consumption/RTOSsleep.png) + +### 通信模组休眠 + +根据前一章节可知,设备空闲时保持 IDLE 状态还是进入休眠状态,完全取决于是否使能 autosleep。换言之,设备空闲时进入休眠时没有任何硬性阻碍,而是完全由用户控制。那么,为什么不选择默认进入休眠呢? + +原因是,某些外设需要一直刷新(如 LCD)。然而当模组休眠时,这些外设便不能连续工作,而是不断进行掉电->恢复的过程,严重降低了外设的刷新效率和响应速度。同时,由于休眠时采用低速时钟,任何依赖高速时钟的设备即使不掉电也无法正常工作。故而,模组空闲时会默认保持在 IDLE 状态,维持所有设备的正常工作。 + +#### Autosleep 机制 + +autosleep 本质上是操作 RTOS 休眠检测机制中的一个 flag,不使能 autosleep 时,模组的检测机制就会指令模组保持在 IDLE 状态。autosleep 被使能时,检测机制才认为模组处于允许休眠的状态,从而进入休眠的逻辑。 + +使用方法参见:[autosleep 相关 API 说明](https://python.quectel.com/doc/API_reference/zh/syslib/pm.html#自动休眠模式控制) + +#### 休眠锁机制 + +在某些场景下,我们既要使模组能够进入休眠,又需要在特定代码段保护某些外设的正常工作,这时,我们就要引入休眠锁机制。 + +休眠锁本质上也是一个 flag,允许创建多个。生效机制是:只要有任意一个休眠锁处于 lock 状态,模组就不会进入休眠。 + +使用方法参见:[功耗锁相关 API 说明](https://python.quectel.com/doc/API_reference/zh/syslib/pm.html#创建wake_lock锁) + +#### 影响蜂窝模组休眠的因素 + +##### 底电流 + +影响底电流的功耗的原因主要是系统的主频、打开的 IP 核的功耗,这块和 PMIC 相关。 +底电流的影响因素主要来源于硬件,包括模组本身和外设两部分: +模组本身的耗流因素主要包括:系统主频、打开的 IP 核。 +外设耗电:部分外设使用模组 PMIC 进行供电,此时其耗流就会由模组负担。 + +一般来说,测量底电流时,需要将网络置为 CFUN0,使射频停止工作,此时测试出的电流即为模组底电流,正常情况下其特征一般近似直线,如下图: + +![BaseCurrent](../media/system/power-consumption/BaseCurrent.jpg) + +##### DRX 周期 & Paging + +基站寻呼(paging):当网络状态产生变化/需要对 UE 进行呼叫/ETWS 或 CMAS 系统发送预警时,基站会对模组发起寻呼,通知处于 IDLE 状态的模组开始进行通信。 + +DRX 周期:模组网络在没有 RRC 连接的情况下,会周期性的监听基站寻呼,保证基站寻呼到来时能及时响应。这个监听的周期就叫做 DRX 周期。DRX 周期一般有 32ms、64ms、128ms 几种,DRX 周期越短,同样时间内监听基站信号的次数就越多,功耗就越高,但响应寻呼就更及时。反之,DRX 周期越长,功耗越低,但响应寻呼的速度越慢。 + +附上 Sleep(CFUN1)模式下耗流图,图中规律性的凸起即为 paging,其周期即 DRX 周期: + +![Paging](../media/system/power-consumption/Paging.jpg) + +##### 信号质量 + +信号质量差的时候,在完成相同的网络行为时,模组需要更大的发射功率。这会在两个场景下影响模组功耗: 1.进行网络业务时,功耗会上升,除上文描述的发射功率外,如果网络业务出现了因通信质量差导致的重连或重传,整体的业务时间会被拉长,导致功耗进一步上升。 2.模组休眠时,每次寻呼的峰值耗流会上升,导致整体休眠电流上升。 + +##### 业务数据 + +模组在进行网络通信时,射频和 CPU 都会工作,从而产生较大的耗流。一般实际业务中,不会一直进行业务数据传输,常见的业务模式是心跳包。 +由于在使用长连接时,如果长期不进行通信,可能会被对端认为已经离线,从而断开连接。所以业务中一般会以固定周期向对端发送一包数据,用来保活长连接。 +各心跳周期下的功耗数据(LTE-FDD@64): + +| ECX00U | ECX00G | ECX00M | ECX00E | EC21 | BG95/BG77 | +| ------ | ------- | ------- | ------- | ------- | --------- | +| 5min | 2.16 mA | 2.13 mA | 1.32 mA | 4.41 mA | 5 mA | +| 10min | 1.84 mA | 1.76 mA | 1.03 mA | 3.39 mA | 4.43 mA | + +##### RRC 释放时间 + +无线资源控制(Radio Resource Control,RRC),又称为无线资源管理(RRM)或者无线资源分配(RRA),是包括呼叫准入控制、切换、功率控制、信道分配、分组调度、端到端 QoS 保障等各自独立的调配和管理算法。模组要进行业务时,需要和基站建立 RRC 连接,只有当 RRC 连接被释放时,模组才能进入休眠。 + +但如下图所示,这个 RRC 连接的生命周期中,只有最开始数秒在进行真正的数据业务。从业务完成到 RRC 连接释放之间,有一段耗流近似 IDLE,却无法休眠的时间,这段耗流的形成和 RRC 的运营商策略有关: + +![Paging](../media/system/power-consumption/RRC.jpg) + +为了避免乒乓效应,基站并不会在网络业务完成后立即释放 RRC 连接,而是会等待一段时间,保证短时间内再次进行业务时无需重新建立 RRC 连接。RRC 连接的时长会因网络环境和运营商策略而不同产生差别,在没有网络数据而 RRC 未释放的条件下,模组并不会进入休眠。因此 RRC 连接的时长会显著影响模组功耗。 + +### 休眠模式唤醒源 + +蜂窝模组进入休眠模式后,有多种模式可以唤醒模组,包括网络数据、电话、GPIO 中断、Timer 等,各种方式实际上都是通过中断来唤醒系统。 + +#### SoC 唤醒机制 + +模组的休眠状态可以由任意中断唤醒(前提是有效的,部分中断控制器处于功耗考虑也会被关闭),唤醒后会在临界区中恢复设备上下文。此时中断控制器中存有此中断的标志,但是不会运行。等待外设恢复完毕,CPU 会退出中断区,此时中断的 ISR 会立即运行。 + +#### 网络数据&电话唤醒 + +网络数据&电话:基站通过 paging 通知模组有呼叫请求,之后 cp(或 DSP)通过回调唤醒 CPU,并通知 CPU 有网络数据到来,CPU 开始将空口数据搬运进协议栈的 buffer,此时上层应用(socket 或电话)可从协议栈中获取到网络数据。 + +![Network](../media/system/power-consumption/Networkwakeup.png) + +#### GPIO 中断唤醒 + +GPIO 可用的前提:GPIO 在模组休眠时保持供电,且其连接的中断的控制器也未被关闭。 +GPIO 硬件被触发时,其连接的中断控制器会立即响应并唤醒 CPU,CPU 会将外设的上下文恢复,然后退出临界区。此时 ISR 检测到 GPIO 的中断已经触发,会立即执行 GPIO 的中断函数(一般是发消息触发 GPIO 绑定的回调),最后触发该 GPIO 绑定的回调函数。 + +![GPIO](../media/system/power-consumption/GPIO.png) + +#### timer 唤醒 + +Timer 超时会触发系统定时器绑定的中断,中断控制器会立即响应并唤醒 CPU,CPU 会将外设的上下文恢复,然后退出临界区。最后通过 ISR 触发 timer 绑定的回调。 + +![Timer](../media/system/power-consumption/timer.png) + +#### 唤醒源获取 + +实际上,从上面三条可以看到,凡是涉及到唤醒行为,都要通过中断控制器进行,这正是休眠模式的特征:由任意中断唤醒。由此,我们可以得出结论:不需要刻意去获取唤醒源,唤醒行为必然对应着特定中断的触发。 + +#### 唤醒后业务处理 + +上一条已经做出结论:不需要刻意去获取唤醒源,唤醒行为必然对应着特定中断的触发。那么业务处理的原则也就很简单了:模组出休眠逻辑中,设备的上下文恢复后退出临界区,唤醒中断的 ISR 就会立即去执行,我们需要的业务逻辑直接由此触发即可,一般都是采用在 ISR 发送消息或者信号量,来触发对应业务的执行。 + +#### 弱信号休眠方案 + +弱信号会带来一系列造成功耗上升的改变:发射功率上升、频繁触发小区切换、业务中出现重传和重连、DRX 周期降低等等……以下介绍部分弱信号休眠方案: 1.控制 DRX 周期使之尽量长,减少网络发射的次数。在发射功率较高时,减少发射次数,可以减少耗流。 2.使用 RRC 快速释放,降低业务完成后 rrc 连接的保持时间。 3.提高小区测量阈值,避免频繁触发小区检测。 + +#### 典型应用 + +**网络例程:网络主动发送心跳包&网络接收寻呼唤醒** + +```python +#利用MQTT来实现心跳包和接受寻呼 +from umqtt import MQTTClient +import utime +import net +import sim +import sms +import _thread +import gc + +sub_path = '/a1A5W32fexl/test2/user/Text' +pub_path = '/a1A5W32fexl/test2/user/Text' +state = 0 +a = 0 +global c + +def sub_cb(topic, msg): + global state + print("subscribe recv:") + print(topic, msg) + +def err_cb(err): + print("thread err:") + print(err) + +def wait_msg(): + global c + while True: + try: + c.wait_msg() + except OSError as e: + print("wait_msg thread err: %s"%str(e)) + break + +global c +c = MQTTClient( + client_id="a1A5W32fexl.test2|securemode=2,signmethod=hmacsha256,timestamp=1675836667378|", + server="a1A5W32fexl.iot-as-mqtt.cn-shanghai.aliyuncs.com", + port=1883, + user="test2&a1A5W32fexl", + password="a5882fb77108cbd93f1413a403b31ed06d0c0e97c0ebca4b0b2f8dffe286da77", + ssl=False, + keepalive=600) #keepalive:MQTT的保活机制,此处为每10min发送一次 +c.error_register_cb(err_cb) +c.set_callback(sub_cb) +print('set_callback') +c.connect() +print('connect') + +print("MQTT is connecting") +c.subscribe(sub_path) +print("Connected to mq.tongxinmao.com, subscribed to %s" % sub_path) +c.publish(pub_path, b"hello") +print("Publish topic: %s, msg: hello" % pub_path) + +_thread.start_new_thread(wait_msg, ())#监听MQTT,等待数据到来,本质上就是等待寻呼 + +``` + +**硬件例程 1:GPIO/keypad 等外部硬件中断唤醒** + +```python +import pm +pm.autosleep(1)#开启休眠 + +# 创建ExtInt对象 +from machine import ExtInt +def fun(args): + f = open("/usr/log.txt", "a+") #以追加模式打开一个文件,低功耗时无法连接交互口,在文件中保存调试信息 + f.write('### interrupt {} ###'.format(args)) # args[0]:gpio号 args[1]:上升沿或下降沿,写入文件 + f.close()#关闭文件,保存写入信息 + +extint = ExtInt(ExtInt.GPIO1, ExtInt.IRQ_FALLING, ExtInt.PULL_PU, fun)#给GPIO1绑定回调fuction + +extint.enable()#使能GPIO中断 + +#进入低功耗后测试触发GPIO1,然后连接USB,查看文件是否写入了正确信息。 +#若文件中写入了正确的信息,说明低功耗模式下GPIO中断有效唤醒了模组,并且执行了其绑定的中断。 +``` + +**硬件例程 2:UART 唤醒和接收** + +```python +""" +运行本例程,需要通过串口线连接开发板的 MAIN 口和PC,在PC上通过串口工具 +打开 MAIN 口,并向该端口发送数据,即可看到 PC 发送过来的消息。 +""" +import _thread +import utime +import pm +from machine import UART + +''' +将主串口接到串口小板上,连接到PC + * 参数1:端口 + 注:选择主串口,所有平台的主串口都支持低功耗唤醒机制,其它串口具有不确定性 + UART2 – MAIN PORT + * 参数2:波特率 + * 参数3:data bits (5~8) + * 参数4:Parity (0:NONE 1:EVEN 2:ODD) + * 参数5:stop bits (1~2) + * 参数6:flow control (0: FC_NONE 1:FC_HW) +''' + + +class Example_uart(object): + def __init__(self, no=UART.UART2, bate=115200, data_bits=8, parity=0, stop_bits=1, flow_control=0): + self.uart = UART(no, bate, data_bits, parity, stop_bits, flow_control) + self.uart.set_callback(self.callback) + + + def callback(self, para): + if(0 == para[0]): + self.uart.write("UART WAKRUP!")#在串口RX回调中发送特定数据 + + + def uartWrite(self, msg): + self.uart.write(msg) + + def uartRead(self, len): + msg = self.uart.read(len) + utf8_msg = msg.decode() + return utf8_msg + + +if __name__ == "__main__": + uart_test = Example_uart() + pm.autosleep(1) + + +# 进入低功耗后向主串口发送数据,如果返回了"UART WAKRUP!",则串口唤醒低功耗成功,并执行了发送 +# 注:部分平台会丢一包数据 + + +``` + +**硬件例程 3:利用功耗锁在休眠唤醒时保护硬件时序和状态** + +```python +from machine import SPI +import utime +import pm + +spi_obj = SPI(0, 0, 1) + +if __name__ == '__main__': + pm.autosleep(1) + + lpm_fd = pm.create_wakelock("test", len("test"))#锁住功耗锁,保护SPI读写 + r_data = bytearray(5) # 创建接收数据的buff + data = b"world" # 测试数据 + + ret = spi_obj.write_read(r_data, data, 5) # 写入数据并接收 + spi_log.info(r_data) + + lpm_fd = pm.create_wakelock("test", len("test"))#释放功耗锁,允许在SPI不进行读写时进入低功耗 +``` + +#### 常见问题 + +**1.无法正常进入休眠:排查唤醒源** + +| 常见唤醒源 | 备注 | +| ---------- | --------------------------------------------- | +| 功耗锁 | 任一把锁未释放都无法休眠,需要全部释放 | +| UART | UART 有数据时不可休眠 | +| USB | USB 插入不可休眠 | +| Thread | Thread 运行时不会进入 IDLE,无法休眠 | +| SPI | SPI 有数据时无法休眠 | +| LVGL | LVGL 会不断刷新,休眠使应使 LVGL 先进入 sleep | + +**2.正常进入休眠后底电流高,检测硬件和网络环境,需要检查的部分包括:** + +| 耗流源 | 备注 | +| ------- | --------------------------------------------- | +| PWM | PWM 控制器可在休眠时使用低速时钟进行输出 | +| GPIO | GPIO 休眠时能够保持对外输出,需要检查是否漏电 | +| VDD_EXT | 常开电源,休眠时需要检查漏电 | + +**3.网络环境导致功耗高** + +| 影响因素 | 备注 | +| -------- | --------------------------------- | +| 射频功耗 | 检查 paging 时耗流峰值和网络质量 | +| RRC 周期 | 检查业务完成到 RRC 链接释放的时间 | + +## PSM 模式 + +### 什么是 PSM 模式 + +PSM 模式是在 UE 空闲一定时间后关闭信号的发送和接收,允许与 AS(接入层)相关的功能,这相当于部分关闭,降低了天线、射频、信令处理等功耗。 + +PSM 模式的优点是可以长时间睡眠,但缺点是无法及时应对终端接收(移动终端、MT)服务。主要应用在远程抄表和一些对实时性要求不高的场景中。 + +### PSM 模式原理 + +PSM 是一种比常规休眠功耗功耗更低的模式,但其运行需要网络侧支持。 + +启用 PSM 是,需要先向基站发送请求(通过 ATTACH 或 TA_UPDATER 携带定时器信息)。基站若支持进入 PSM,则会在对应的 REQUEST 中下发定时器信息,需要注意的是,实际 PSM 的参数会使用基站下发的值,并不一定和我们请求的值相同。 + +模组会以基站下发的值来配置定时器时间,当模组进入 IDLE 且 ACT 定时器超时,模组会关闭 CPU 和一切射频,此时相当于部分关机,只是保留了比关机更多的唤醒源,一般包括 ACT、TAU 定时器和 PSM_INT。功耗一般下降到微安级别。此时由于基站已经记录 UE 使用 PSM,不会断开此 UE 的连接,下次如果在同一小区内重新开机,就无需拨号,直接用原有的 IP 等信息,进一步减少了功耗。 + +当 PSM TAU 定时器超时、RTC_alarm 到期或 PSM_INT 等唤醒源被触发时,模组会被唤醒。由于硬件上 CPU(包括 SRAM)和 PA 均被下电,此时并不能恢复休眠前运行状态,而是要重新走开机流程。开机后可进行网络业务,网络业务完成且 RRC 被释放后 ACT 定时器编开始计时,超时便进入 PSM 中。 + +**整体流程如下:** + +![PSMProcess](../media/system/power-consumption/PSMMainProcess.png) + +最终,在两个定时器的作用下,模组会进行周期性的休眠,唤醒切换,但只有在唤醒的时候,才能进行网络业务,休眠时是无法响应网络寻呼的。 + +**耗流特征如下(本示例每 1min 唤醒一次):** + +![Timer](../media/system/power-consumption/PSMCycle.jpg) + +#### ACT 和 TAU 定时器 + +ACT,又称 T3324,当模组完成网络业务(即 RRC 连接释放时)开始计时,超时后立即进入 PSM。 + +TAU,又称 T3412,当模组完成网络业务(即 RRC 连接释放时)开始计时,超时后,若模组在 PSM 模式,会将模组唤醒。 + +它们的时序关系如下图: + +![Timer](../media/system/power-consumption/3GPP_PSM_TIMER.png) + +### PSM &RTC 模式下的功耗以及各平台支持情况 + +| | ECX00U | ECX00G | ECX00M | ECX00E | BG95/BG77 | +| -------- | ------ | ------ | ------ | ------------------- | --------- | +| PSM | 15 uA | 15 uA | 不支持 | 5uA | 5.10 uA | +| RTC+关机 | 33 uA | 15 uA | 38 uA | 5uA(with hibernate) | 14.87 uA | + +### PSM 唤醒源 + +#### PSM INT + +PSM_INT 一般是模组 PMIC 预留的引脚,其固定作用就是唤醒 PSM。不需要额外的配置,进入 PSM 休眠后按照指定方式触发即可唤醒模组。 + +但对于 ECX00E 模组,没有专门的 PSM_INT,而是由 wakeup 引脚实现此功能,使用方法参见 wakeup 引脚 + +#### RTC 闹钟 + +除关机之外,RTC 闹钟也能在 PSM 模式下生效。其使用方法与关机 RTC 闹钟相同。 + +参见:[RTC 相关 API 说明](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.RTC.html) + +#### Powerkey + +Powerkey 也能唤醒 PSM 模式,作用原理和触发开机一致。 + +#### TAU Timer + +上文描述过,TAU 定时器超时会唤醒模组。 + +> 注意:BG95/BG77 的 TAU Timer 和 RTC alarm 复用了同一个 PMIC 唤醒源,因此 PSM 和 RTC 不能同时使用 + +### PSM 典型应用 + +PSM 功耗虽低,但有以下缺点: + +1.PSM 启动时需要走重启逻辑,应用恢复时间远远长于 autosleep; + +2.产生小区切换时,无法拿到原有的网络信息,导致出现掉网和重新注网问题,反而会使耗流上升; + +3.PSM 休眠时不响应寻呼 + +所以 PSM 的应用场景一般满足以下三个条件: + +1.定时任务间隔较长,可忽略启动时间 + +2.较少进行移动,不会频繁切换小区 + +3.对数据实时性要求不高 + +#### 如何进入 PSM + +需要在联网,且确认运营商支持 PSM 的前提下使用。根据业务需求决定 ACT 和 TAU 的周期,通过 API 设置即可: + +参见:[PSM 相关 API 说明](https://python.quectel.com/doc/API_reference/zh/syslib/pm.html#设置PSM模式的控制时间) + +#### PSM INT 应用 + +EC600U 和 BG95 支持此引脚,上升沿触发,进入 PSM 时模组 PMIC 会默认配置此引脚,确保触发方式正确即可在 PSM 模式下唤醒模组。 + +> ECX00E 使用 wakeup 引脚,需要额外进行配置 + +#### 弱信号 PSM 方案 + +弱信号下 PSM 处理机制如下: + +1.基站下发定时器后,模组即会按定时器设置进入 PSM。因此只要成功接收到基站下发的定时器,即使在 PSM 过程中网络通信断开,在下一次联网前,仍能保持正确的周期。 + +2.如果出现了连不上网络或者小区切换的情况,模组会主动放弃原有的连接,重新触发驻网和拨号,保证网络可用,并且在注网时会再次请求 PSM。 + +3.网络质量过差导致 PSM 无法正常请求和下发:使用关机和 RTC 闹钟唤醒来代替 PSM 模式 + +#### 不支持 PSM 的运营商的方案 + +如果运营商不支持 PSM,基站会设法阻碍模组进入 PSM。常见的行为有三种: + +1. 在模组向基站请求定时器时,直接 REJECT 请求(此情况很少,绝大部分为后两种)。 +2. 下发不成对的 ACT 和 TAU 定时器,不成对的定时器无法使模组进入 PSM。 +3. 不下发定时器 + +此时,只能通过 RTC 闹钟定时唤醒关机来实现类似的业务需求,这种方式也能达到在无需数据交互时减少耗流。但从关机状态重新开机相比 PSM 多了一个注网和拨号的动作,在每次唤醒注网时都会产生更多射频功耗。 + +> 此外,ECX00E 在不支持 PSM 时需要通过 pm.forcehib()方法强制进入 hibernate 休眠,因为此平台关机时 RTC 无法维持运行。 + +### 常见问题排查 + +**1.PSM 设置返回 True,但无法进入** + +基站可能不支持 PSM,确认这一点的方法是从 CPlog 中查看基站下发的信息,需要查看的条目为: + +**ATTACH_REQUEST** + +**ATTACH_ACCEPT** + +**TA_UPDATE_REQUEST** + +**TA_UPDATE_ACCEPT** + +如图,模组设置成功后,REQUEST 一定会带有成对的定时器信息,我们需要关注 ACCEPT 中基站是否正确下发了定时器信息。 + +![Timer](../media/system/power-consumption/PSMRequest.png) + +如图,网络环境不支持 PSM,基站未下发成对的定时器,只下发了一个 ACT 定时器,这时候是不能进入 PSM 的: + +![Timer](../media/system/power-consumption/PSMWrongACCEPT.png) + +**2.PSM 成功进入,但休眠-唤醒周期与设置的不同** + +同样的,查看以上 CPlog 条目,基站下发的定时器周期可能不等于设置的值,且模组最终以基站下发的值为准。 + +## 常见应用场景 + +**对讲机低功耗方案:** + +RRC 快速释放:对讲机的网络业务是不固定的,随机触发的。快速释放 RRC 能够保证在任何一个随机时间点,都能在对讲后快速释放 RRC,从而进入休眠。 + +**tracker 低功耗方案:** + +RRC 快速释放:tracker 是一个定位器,位置是不固定的,定期进行一次定位。可使用 RRC 快速释放减少 RRC 连接保持的时长,从而降低整体耗流。 + +**表计、门磁低功耗方案:** + +PSM,此类产品的唤醒间隔较长,且一般不会经常移动,对数据的实时性要求不高,可采用 PSM。若需要在无网络或运营商不支持的情况下使用,可采用关机+RTC 闹钟定时唤醒的方案。 + +**根据功耗需求选型:** + +uA 级耗流:必须选择支持 PSM 或关机 RTC 唤醒的型号 + +mA 级耗流:全平台支持 autosleep,根据其它需求评估适合型号 + +**待机时间推算方法:** + +1.首先确定电池选型,得出电池容量。 + +2.确定模组选型,根据规格书和实际业务计算平均待机电流。如果需要准确的待机电流,应保持整机按照业务流程运行,然后使用功耗仪测试出平均待机电流。 + +3.从待机时间的计算公式:待机时间 = 电池容量/ 平均待机电流 + +例如:已知电池容量 800mah,给 ECX00E 供电。ECX00E 在 autosleep 模式下保持网络连接(LTE-FDD@64)时平均待机电流约 0.64mA。 + +则实际待机时间 T=800mah/0.64mA=1250h diff --git a/docs/Application_guide/zh/system/power-manager.md b/docs/Application_guide/zh/system/power-manager.md new file mode 100644 index 0000000000000000000000000000000000000000..158194b490130dc877e496f68b795f3021e68237 --- /dev/null +++ b/docs/Application_guide/zh/system/power-manager.md @@ -0,0 +1,401 @@ +# 电源管理 + +## 电源管理概述 + +为什么要进行电源管理: + +- **节能和延长电池寿命**:可以根据不同的应用场景和负载要求,实现电源的高效管理和控制。例如,可以通过动态调整电源电压和电流等参数,来适应不同的负载要求,从而提高电池使用效率和寿命。 +- **保护系统安全和稳定性**:可以监测电池电量、电源温度、电压和电流等关键参数,以保证系统的安全和稳定性。例如,在电池电量过低或过高、电流过大或过小等情况下,电源管理 IC 芯片会自动控制和保护电源系统,防止系统因电源问题而损坏或出现故障。 +- **提供多种功能和接口**:如 ADC、Codec、PWM、温度监测、LED 驱动、RTC 等,可以为不同的应用提供灵活和多样化的电源管理解决方案。 +- **提高系统效率和性能**:通过使用高效的 DC-DC 转换器和低功耗的睡眠模式等技术,可以减少系统的能量损耗和功耗,从而提高系统的效率和性能。 + +### 电源管理芯片(PMIC) + +PMIC 是 Power Management IC 的缩写,中文是电源管理集成电路,主要特点是高集成度,将传统的多路输出电源封装在一颗芯片内,使得多电源应用场景高效率更高,体积更小。蜂窝模组为了在减小体积同时实现多电源,大量采用 PMIC。 + +### 电源管理单元(PMU) + +PMU 就是电源管理单元,一种高集成的、针对便携式应用的电源管理方案。将传统分立的若干类电源管理芯片,如低压差线性稳压器(LDO)、直流直流转换器(DC-DC)等集成到电源管理单元(PMU)中。这样可实现更高的电源转换效率和更低功耗,及更少的组件数以适应缩小的板级空间,成本更低。 + +> 很多地方,PMU 等同于 PMIC,但是这里讨论的 SoC 外部的供电芯片是 PMIC,SOC 内部的电源管理单元被称为 PMU。 + +### DC-DC + +DC-DC 指直流转直流电路,广义上包括所有输入输出都是直流的电源,下文将要提到的 LDO,也是一种 DC-DC。这里我们所说的 DC-DC 实际上指的是 DC-DC 最典型的一种实现形式:开关恒流源。 + +蜂窝基带的 DC-DC 一般都是 BUCK 电路,根据用途的不同会附带一些特殊功能,例如: + +- DVC_BUCK_DC/DC 电路给 CPU 供电:负载能力强,带 DVC(动态调节电压)功能,允许动态调整电压,达到节能目的。 +- APT_BUCK_DC/DC 电路给射频以及 PA 供电:负载能力强,带 APT(平均功率追踪)功能,减少射频耗电。 + +从用途上可以很明显看出来,DC-DC 的优势是负载大。除了负载,它还有输入范围宽、转换效率高的优点。相对于 LDO,它的缺点是结构复杂,负载响应差,纹波较大,不适合给对噪声敏感的电路供电。 + +### LDO + +LDO 是一种线性电源,只能做降压,负载能力较弱。但它结构简单,成本低,适合大规模铺设。而且噪声小,负载响应快,在对噪声敏感的模拟电路内用 LDO 是最好的选择。 +蜂窝基带内一般用来给各个分立器件或外设供电(如 RTC 电路、GPIO、UART 等),这些器件对负载的要求不高。利用 LDO 可以方便的实现多电压域的低噪声电路。 + +### 动态调整电压(DVFS/DVC) + +DVFS 即动态电压频率调整,动态技术则是根据芯片所运行的应用程序对计算能力的不同需要,动态调节芯片的运行频率和电压(对于同一芯片,频率越高,需要的电压也越高),从而达到节能的目的。 + +DVFS 本质上是一种低功耗技术,目的是根据芯片当时的实际功耗需要设定工作电压和时钟频率,这样可以保证提供的功率既满足要求又不会性能过剩,从而可以降低功耗。 + +我们的通信模组中给 CPU 供电的 BUCK 电路一般都带有电压调整功能,根据芯片实际的时钟频率来调整输出电压。 + +### RTC 电路 + +通信模组使用的 PMIC 通常集成 RTC 电路(一般包括一路常开的 LDO 和硬件定时器),以内置/外置晶振作为时钟源计数,可以在模组的 CPU 下电时保持计数,并在 PMIC 上产生唤醒源,从而实现定时开机功能。 + +### 模拟电路 + +PMIC 一般集成 ADC 和温度传感器,部分型号(ECX00U)集成了音频 Codec,模组所需的模拟电路功能实际上大部分由 PMIC 实现。 + +### 充电管理电路 + +一般当 USB_VBUS 连接且电压在合法范围内时启动,其功能包括两点:1.从 VBUS 上取电,经转换后经 VBAT 向电池充电 2.预留了部分充电组件接口,如:充电指示灯、电池的温度传感器,电池 ID 检测等。 + +### 其它外设 + +#### PowerKey + +模组的 PowerKey 在 PMIC 的定义上一般叫做 exton,其作用就是触发 PMIC 给 CPU 供电。开机状态下也可以控制模组关机。 + +#### 晶振 + +给模组内提供震荡源,一般是休眠时模组 32K 低速时钟的源。 + +#### GPIO + +部分资源比较丰富的 PMIC 上还集成了 GPIO,与连接在 CPU 的 GPIO 相比,这些 GPIO 在关机时仍可保持电平。 + +### 输入电路 + +PMIC 一般由一路或几路电压相对较高的直流做输入,经过 VIN 电路稳压后供给 PMIC 各个部分。VIN 一般连接在模组的 VBAT 上。需要注意的是,部分 PMIC 功能较简单的模组(如 ECX00M 和 ECX00E),无法通过 PMIC 给射频供电,需要额外增加射频供电单元(但一般保留了控制射频电源通断的功能)。 + +## Powerkey 和 Reset + +### Powerkey 的硬件连接和开关机原理 + +Powerkey,可以控制模组开关机。其硬件连接 PMIC,模组通电时,PMIC 处于在电状态,但并不给 CPU 供电。此时若 powerkey 被按下,则开始向 CPU 供电,CPU 从 bootrom 开始运行。但经过 bootloader 阶段的消抖,实际上 PWRKEY 必须长按才能让模组真正开机。 +**开关机时序图:** + +![PowerkeyOn](../media/system/power-manager/PowerkeyOn.png) + +![PowerkeyOff](../media/system/power-manager/Powerkeyoff.png) + +### Powerkey 长按开机的实现 + +在 bootloader 中对 Powerkey 进行电平检测和消抖,由于 bootloader 中一般不能使用 ISR,实际的消抖大部分是轮询 powerkey 状态,保持低电平的时间低于消抖阈值则操作 PMIC 关机。 + +### RESET 的硬件连接和重启原理 + +RESET 一般是直接连接在 CPU 上的,其触发的动作并不涉及 PMIC,只是 CPU 复位,中断向量表回到 bootrom,然后从 bootrom 开始执行。 +RESET 时序图: + +![PowerkeyOff](../media/system/power-manager/Reset.png) + +### 自定义 Powerkey 短按长按功能 + +一般是在能够双边触发的 powerkey 上实现。实现方法是在按下时的中断里启动一个定时器,在抬起的中断里关闭这个定时器。如果定时器尚未到期,就被 pwrkey 抬起的中断关闭,则判定为短按。如果直到超时都没有抬起的中断触发,则判定为长按。定时器的时间就是界定短按和长按的阈值。 + +### 上电自启动功能实现 + +如需上电自动开机功能且不考虑关机(ECX00M 在这种场景下无法关机),则可以把 PWRKEY 直接下拉到地,下拉电阻建议不超过 1 kΩ。 + +![autostart](../media/system/power-manager/autostart.png) + +## USB_BOOT + +### USB_BOOT 概述 + +模组固定的一个 IO 引脚,bootrom 阶段检测到此引脚处于某种特定的电平时,直接跳转到强制下载/紧急下载模式。开机后可当作普通 IO 使用。 + +### 强制下载的实现原理 + +模组固件被破坏或未烧录时,USB_BOOT 仍能够被正常触发。USB_BOOT 的引脚号、触发电平和下载模式跳转全部固化在硬件的 ROM 中,且此 ROM 的代码入口就是 CPU 的复位向量,即使可擦写存储介质中的代码损坏,也不影响此处 USB_BOOT 的行为。 + +### 使用方法 + +使用场景:首次烧录、救砖、不方便使用软件控制进入下载模式的场景 +使用方法:将 usb_boot 短接到地(部分模组,如 EC600U/EC600E 是上拉到 1.8V,具体触发电平可在硬件规格书中获取),然后上电。此时 USB 不再和正常启动时一样枚举多个口,上位机只能看到一个下载口 + +> 可能的下载口名称: +> Quectel Download Port(ECX00N, ECX00M) +> Quectel QDLoader 9008(BG95/BG77) +> Quectel QDLoader Port(ECX00E) +> SPRD U2S Port(ECX00U ECX00G) + +此时在上位机启动烧录软件,烧录软件操作如下: +**QPYCom:**载入固件后,根据型号选中模组对应下载口,然后点击“固件下载”进行烧录 +**QFLASH**:载入固件后,根据型号选中模组对应下载口,然后点击“Start”进行烧录 +**原厂工具**(仅了解,客户侧禁止使用):载入固件后,点击开始烧录,原厂工具都会自动检测下载口,然后进行下载协议握手和烧录 + +**USB_BOOT 开机时序图:** + +![USB_BOOTTime](../media/system/power-manager/USB_BOOTTime.png) + +> 注意事项:不期望进行下载时,注意在开机流程结束前保护此引脚电平,防止模组检测到此引脚被触发从而进入下载模式 + +## 设备启动流程 + +### 启动流程概述 + +总体来说,启动流程可分为以下四个部分: +1.bootrom,上电立即运行的部分,一般固化在硬件的 ROM 内 +2.bootloader,初始化必要外设并检查内置存储中的代码,满足启动条件后跳转到 RTOS 启动部分 +3.RTOS,此处会完成 RTOS 的初始化,并且启动系统必要的 task,此流程的最后会创建 QuecPython 虚拟机线程 +4.QuecPython 虚拟机启动,完成交互口、虚拟文件系统、网络环境的初始化后,在 usr 分区中寻找 main.py 并执行 +总流程图如下: + +![Mainstartup](../media/system/power-manager/Mainstartup.png) + +### bootrom + +bootrom 是固化在模组硬件中的代码,复位向量代码即指向此处。当模组上电或复位时会立即执行此处的逻辑,一般包含强制下载检测、基本初始化等功能。运行结束前会检测并引导 bootloader。 + +![bootrom](../media/system/power-manager/bootrom.png) + +### bootloader + +bootloader 一般存储在内置存储中,作为第二级 boot,完成一些不确定的,相对复杂的,不方便在 bootrom 阶段完成的功能。 +不同平台的 bootloader 功能不尽相同,以下介绍其典型流程。 + +![bootloader](../media/system/power-manager/bootloader.png) + +#### 基本 BSP 外设初始化 + +bootloader 中需要操作一些外设,如 GPIO、UART 等,此时会把这些外设进行初始化 + +#### powerkey 消抖 + +bootloader 中一般不能使用 ISR,powerkey 消抖即是在阈值时间内轮询 powerkey 的电平,如果其电平保持时间超过阈值时间,则继续进行开机流程。反之,则对此次 powerkey 触发进行消抖,操作 PMIC 关机。 + +#### 判断是否进行 FOTA + +大部分模组的 FOTA 依赖 bootloader 内进行跳转。一般上一次关机前会操作某个寄存器作为 flag,标识需要进行 fota。检测到 FOTA 的 flag 时,bootloader 便不会跳转到 RTOS 系统,而是跳转到 updater(一般是一块可以独立运行的代码,会将 fota 包内容写到指定的 flash 地址,完成固件的升级)。 + +#### 相关外设去初始化,跳转 RTOS 系统启动 + +将 bootloader 中使用过的外设去初始化,恢复到操作前的状态。校验 RTOS 代码区域的完整性,校验通过则跳转到 RTOS 入口函数,不通过一般会认为镜像损坏,跳进下载模式。 + +(ECX00N/M 跳转到 LOGO 阶段,读取文件系统中的图片资源,并初始化 LCD 驱动,完成开机 LOGO 的显示,LOGO 结束后跳转到 RTOS) + +### RTOS 启动 + +RTOS 是模组运行的主体程序,绝大部分通信模组此时会初始化两套 RTOS,分别处理 CP 和 AP 任务。一般启动流程如下: + +![RTOS](../media/system/power-manager/RTOS.png) + +#### 时钟和外设进行初始化,为 RTOS 启动做好准备 + +为了获得准确的 sys_tick,CPU 的 tick 会被重新初始化(即 tick 恢复到 0)。所有外设也会被置为上电初始状态。此时的 BSP 状态应和模组默认电平相对齐。 + +#### RTOS 初始化 + +初始化任务堆栈、任务调度器,当任务调度器初始化完成,rtos 就已经接管了底层,我们调用的函数开始全部运行在 RTOS 的任务中。 + +#### CP core 启动搜网和 SIM 卡相关任务 + +CP 侧开始启动搜网和 SIM 卡相关任务,原厂的 CP core 一般不开源,此处不再赘述 + +#### AP core 启动必要的内部 task + +WDT、FS、协议栈等必要的内部 task 启动,完善 APP 运行的条件。 + +#### 启动 QuecPython 虚拟机 + +RTOS 内部环境初始化完成,启动 APP task。APP 一般是一个优先级相对较低的 task,用来处理第三方业务。相对于底层而言,我们的 python 虚拟机就是一个 APP,在 RTOS 初始化完成后启动。 + +## 开机 APP 自动运行 + +QuecPython 虚拟机是 python 运行的基础,虚拟机在启动时会完成交互命令行、文件系统和网络环境的初始化,并在最后默认执行名为 main.py 的 pyscript + +大致流程如下: + +![pythonVM](../media/system/power-manager/pythonVM.png) + +### 虚拟机本身的初始化 + +包括栈、GC、python 解释器等虚拟机组件初始化,初始化完成后已经能运行 pyscript,此时进入\_boot.py 进行下一步初始化. + +### 虚拟文件系统初始化 + +要在 python 中访问文件系统,就要把模组上实体的文件系统挂载到 python 的 VFS 上。部分模组底层没有初始化我们需要的 liitleFS 文件系统,此时就要将实体文件系统一并初始化。 + +当实体文件系统没有初始化成功、SPI 通信失败等情况导致此步骤执行失败,模组会继续开机流程。但会直接跳过 app_fota 和备份还原,这两个功能和文件系统是强依赖,文件系统未能初始化成功时执行必定失败,没有意义。 + +### 备份还原 + +启用备份还原机制时,若 usr 中文件出现丢失或者更改,会从 bak 分区将备份文件恢复进 usr。若未开启备份还原机制,或者 usr 内文件没有变更时,则直接跳过备份还原流程。 + +### app_fota + +检测是否进行 APP_FOTA。APP_FOTA 在下载完成后会在文件系统中写入 fota 标志,如果在文件系统中读到这个标志,就会进行 app_fota 升级流程。将下载的内容更新到 usr 分区。 + +### 交互命令行初始化 + +从此环节开始,是一定会执行到的流程,repl 命令行启动,如果 system_config.json 中开启了交互保护,则会打开交互保护功能。 + +### 自动拨号 + +网络环境初始化,根据 system_config.json 中的配置决定是否进行开机自动拨号(默认开启)。 + +### pyscript 运行 + +以上步骤均结束后,检测 usr 文件系统内是否有名为 main.py 的脚本,有则立即运行此脚本。因此,main.py 是 APP 任务的固定入口点。 + +## 开机 LOGO 显示 + +### 开机 LOGO 的使用场景 + +开机 LOGO 一般需求尽量快的显示,由于虚拟机初始化和 APP 载入需要一定时间,因此要在此虚拟机启动前先显示 LOGO + +### 方案 1:bootloader(ECX00M/N 方案) + +在 bootloader 中引入 LCD 和文件系统部分的代码,在 bootloader 阶段完成 LCD 显示,适用于 boot 时间较长的场景。 + +ASR 模组 bootloader 的最后会进入名为 LOGO 的代码块,这里我们适配了 littleFS 文件系统和 SPI LCD 驱动,可以直接访问 usr 分区的数据,并驱动 LCD 显示 LOGO。由于 usr 分区在上层可以直接修改,客户可以方便的更换 LOGO 图像。 + +### 方案 2:RTOS(ECX00U 展锐方案) + +在 RTOS 初始化完成,但 APP 尚未启动时,先刷新屏幕,显示 LOGO,然后再进行 python 虚拟机的初始化,适用于 boot 较快的场景。 + +在 python 还没启动时提前将 lfs 文件系统挂载到 ECX00U 底层的 vfs 上,就可以在虚拟机初始化之前访问文件系统并驱动 LCD 显示 LOGO,同样的,LOGO 图片报此马在 usr 分区,客户可以直接在 python 层进行修改。 + +### 自定义显示 + +以上两种方案,其显示内容都存储在我们的文件系统 usr 分区内,用户在 python 层修改 usr 分区内的 logo 内容即可。 + +## 设备充电 + +### 充电方案概述 + +通信模组的充电方案一般是借由 PMIC 的充电管理单元来实现的,部分模组(ECX00U)可以在模组开机的情况下,在软件中介入充电控制。 + +#### PMIC 充电管理单元原理 + +PMIC 充电管理单元的实现形式一般是一个 linear charger(线性充电器),只要检测到 VBUS 电压在合法范围,就会开始工作。 + +充电单元的传感器一般至少包括两个 ADC,一路监控 VBAT 电压,另一路监控电池内阻(实际上通过内阻的变化来监控电池温度)。 + +#### 充电阶段控制 + +根据 VBUS 电压高低,PMIC 会控制充电管理单元进入不同的充电管理阶段,典型划分如下(不同平台称谓不同,原理大致相似): + +涓流模式/激活模式:电池电压低于某个阈值时,电池管理系统会禁止向电池中充电,此时需要用小电流激活电池,使电池电压恢复到可充电状态。 + +预充电模式/小电流模式:当电池电压较低时,如果进行全功率充电,由于电池的化学特性,会给电池带来高温和过压等安全隐患。因此,当电压小于全电流充电阈值时,充电管理电源会用较小电流和电压对 VBAT 进行充电。 + +全电流模式/快速充电模式:此时电池已经到达能够安全进行全功率充电的电压,保持一个较大的恒定电流,快速对电池进行充电。 + +恒压充电:电池较接近满载电压,此时保持恒压,充电电流随 VBAT 和充电单元间压差减小逐渐下降,直到到达满电,充电停止。 + +复充:充电完成后,轮询 VBAT 电压,发现 VBAT 滑落到复充触发电压以下时,则进行恒压充电。 + +典型的充电电流、电压曲线图如下: + +![ChargerCurrent](../media/system/power-manager/ChargerCurrent.png) + +#### 充电的软件控制 + +允许通过软件调节充电行为,如停止/启动充电,控制恒流模式时的输出占空比,或者获取电池电压和温度等(仅 ECX00U 支持)。 + +### 关机充电方案 + +#### 关机充电方案概述 + +关机充电,即在关机状态下插入电源进行充电时,在不开机的情况下,实现对充电状态的监控。 + +#### 实现方案原理 + +充电单元本身含有控制充电阶段的 FSM,可以在 CPU 不运行的情况下控制充电行为,所以我们要实现的即是在不开机的情况下,监控充电状态(如 VBAT 电压、电池温度等,可能还需要进行显示)。 + +此时,可在 VBUS 被电源拉高时通过硬件联动 powerkey(如果 PMIC 支持 charger 插入触发开机,则无需触发 powerkey,使能 charger 触发开机即可),CPU 上电。 + +关机充电的逻辑停留在 boot 中实现,主要包括轮询电池状态和 LCD 显示等业务,但并不从 boot 中跳转进 RTOS 阶段,如果此时检测到 powerkey 再次被触发,就进入正常的开机流程。 + +![PoweroffCharger](../media/system/power-manager/PoweroffCharger.png) + +### 平台支持情况 + +当前 EC100Y 和 ECX00U 内置充电管理,其它平台可外置充电管理芯片。 + +## PSM_INT + +### PSM_INT 概述 + +PSM_INT 是 PMIC 上的一个引脚,它的特征是在 PSM 模式下仍能响应,并触发模组唤醒。但需要注意的是,在关机模式下,这个引脚一般是失效的。也就是说,PSM_INT 是一个仅在 PSM 模式下生效的唤醒源。 + +### PSM_INT 的原理 + +ECX00U&BG95/BG77:PMIC 上的引脚,进入 PSM 模式时,PMIC 默认使能其中断,触发时会将模组从 PSM 中唤醒。 +ECX00E:与之类似的是六个 wakeup 引脚,需要在模组进入 PSM 前使能这几个 wakeup 引脚和中断,触发时会将模组从 PSM 中唤醒。 + +### 使用方法 + +ECX00U&BG95/BG77:进入 PSM 时默认开启使能,具有固定的电平状态和触发条件,进入 PSM 模式时直接触发即可。 +ECX00E:需要配置使能哪几路 wakeup,可设定自身电平状态和触发条件。 + +## 休眠 GPIO 电平控制 + +### GPIO 电源供应概述 + +GPIO 从 BB 芯片引出。状态由 BB 芯片控制,供电多来自 PMIC 的 LDO。GPIO 的电平状态和 PMIC 行为息息相关,休眠时其电平变化取决于 PMU 行为。 + +### 休眠时 GPIO 电平变化 + +sleep1:保持电平状态不变,中断可以触发。(ECX00E AGPIO 可保持输出电平,其余 GPIO 由于 LDO 被拉低,全部下电,GPIO 中断不可触发) +PSM&关机:GPIO 全部变成低电平(EC21 部分 GPIO 可保持电平,因为这些 GPIO 是连接在 PMU 上的,PMU 仍然在电) + +### 休眠 GPIO 电平控制原理 + +休眠时,模组并不会去操作 GPIO 状态相关的寄存器,PMU 也不会切断 GPIO 的供电,所以 GPIO 的电平一般保持不变。 +反之,关机或 PSM 时 BB 芯片失电,与之相连的 GPIO 自然不能保持电平,但 PMIC 仍在电,若有 GPIO 自 PMIC 引出,则有可能保持电平。 + +同样的,ECX00E 为了降低休眠功耗,关闭了 GPIO 的供电,所以这个型号的 GPIO 在休眠时会全部掉电。 + +## VDD_EXT + +### VDD_EXT 概述 + +一路直接连接到 LDO 或者常开 IO 的引脚,BB 芯片上电时 LDO 开始供电,此引脚立刻拉高,并在开机时一直保持高电平。可用来标识 CPU 上电时间,可以作为常开的电源或上拉,部分连接在常开 LDO 上的 VDD_EXT 在 BB 芯片下电后仍能保持高电平。 + +### VDD_EXT 电平-时序图 + +![VDD_EXTTime](../media/system/power-manager/VDD_EXTTime.png) + +### 典型应用 + +标识 BB 芯片上电时间 +作为模组开机时常开的电源或上拉(做电源时,注意驱动能力是否满足需求)。 + +## RTC 闹钟 + +### RTC 闹钟概述 + +RTC(Real Time Clock)是 PMIC 上的一个单元,一般由一路关机时也可保持供电的 LDO 来驱动,其核心部件是数目不等的计数器,通过 32K 时钟源进行计数。RTC alarm 到期时可以产生中断,大部分型号可唤醒模组。 + +### RTC 闹钟原理 + +RTC 闹钟原理:提供专门的 RTC_alarm 寄存器,使能 alarm 时,向该寄存器中存储 alarm 触发的时间。当 RTC 时钟到达 RTC_alarm 寄存器中存储的时间(一般称此事件为 Expire),即产生中断。 + +大部分模组(ECX00N 和 ECX00E 不支持此功能)都可在关机时保持 RTC 工作,并且被 RTC_alarm 的中断唤醒。 + +> 注意事项: +> 移芯没有 RTC 电路,其所谓 RTC 闹钟是硬件定时器实现的,可以在任何休眠等级下产生有效唤醒,但关机时不可用。 +> 高通 MDM9x05 的 RTC alarm 和 PSM T3412 复用一个 alarm 寄存器,因此不能共存。 +> 定时精度,BG95 平台定时精度需要专门校准调整 +> 唤醒频率,移芯在特定 sleep 时会将 RTC_alarm 寄存器写入 flash,会涉及到擦写 flash 的操作不宜太频繁 + +### 典型应用 + +应用方法参考:[class RTC – 实时时钟](https://python.quectel.com/doc/API_reference/zh/peripherals/machine.RTC.html) + +#### 开机情况下的使用 + +开机时可作为定时器使用,实现定时任务。 + +#### 关机情况下唤醒 + +设置 alarm 时间后将模组关机(但不可完全断电)。关机时若 RTC 闹钟超时,会触发开机。可利用此特性实现低功耗,在不需要处理业务时将设备关机,需要处理业务时再唤醒设备。 diff --git a/docs/Application_guide/zh/usbnet/README.md b/docs/Application_guide/zh/usbnet/README.md deleted file mode 100644 index 1317d196ae6253d95a34005ad1f24b900ed7168f..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/usbnet/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# USBNET应用指导说明 - -本文中对USBNET一些基础概念进行了简要说明,描述了如何使用 QuecPython 的`USBNET`模块的功能,包含USB网卡使用的前提条件。 - - - -## 1. 基础概念说明 - -### 1.1 什么是USBNET - -USBNET(Universal Serial Bus Network),是一种USB网络技术,可以通过USB接口在计算机之间传输数据。它通常用于连接嵌入式设备,例如智能手机或网络路由器,与计算机进行通信。USBNET技术使嵌入式设备可以像普通计算机一样直接连接到网络,从而方便了设备的管理和控制。因此,USBNET被广泛应用于各种计算机和网络设备中。 - - - -### 1.3 `ECM`模式和`RNDIS`模式的区别 -ECM和RNDIS是USBNET中的两种网络模式。具体区别如下: - -* `ECM` - 全称是"Ethernet Control Model",它是一种通过USB连接来模拟Ethernet网络连接的技术。ECM能够在device和host之间交换ethernet frame, 符合ECM规范的设备,认为自己是一个虚拟的网络接口, 可以被分配MAC和IP。ECM可以被用于连接任何支持TCP/IP协议的设备,例如计算机、网络路由器、智能手机等,从而使它们可以直接访问网络。 - -* `RNDIS` - 全称是"Remote Network Driver Interface Specification",它是一种使嵌入式设备通过USB接口来实现远程网络连接的技术,实际上就是TCP/IP over USB。RNDIS可以让嵌入式设备直接连接到计算机的网络,从而方便了设备的管理和控制。 - -因此,ECM和RNDIS都是USBNET中常见的网络模式,它们可以使嵌入式设备通过USB连接实现网络连接和通信。 - - - -### 1.2 什么是PID - -PID是"Product ID"的缩写,指的是USB设备的产品标识号。每一个USB设备都必须拥有唯一的PID,以便计算机能够识别和区分不同的USB设备。PID通常由USB设备制造商进行分配,并写入设备的固件中。 -在USB连接建立时,计算机会读取USB设备的PID信息,并根据PID来确定设备的厂商和型号等信息。这些信息可以用来识别设备,并为其加载正确的驱动程序,以确保设备可以正常工作。因此,PID是USB设备中重要的标识之一。 - - - -## 2. USBNET应用指导文档列表 - -* [USBNET功能应用指导](./usbnet.md) - - diff --git a/docs/Application_guide/zh/usbnet/usbnet.md b/docs/Application_guide/zh/usbnet/usbnet.md deleted file mode 100644 index 8b2624f4e4bdb8f56d7b7c6e167ae7666a3388b3..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/usbnet/usbnet.md +++ /dev/null @@ -1,145 +0,0 @@ -# 1. 简介 - -QuecPython提供了`USBNET`功能模块来为用户提供USB网卡功能。该功能模块使得用户只需调用几个接口即可开启USBNET功能,提高了开发效率。 - -目前支持USBNET功能的模组系列:EC600S/EC600N/EC800N/EC200U/EC600U/EC600M。 - -> 本文档中示例代码前面有 `>>> `字符串的,表示在QuecPython的命令交互界面输入的代码。 - - - -# 2. 使用说明 - -`USBNET`模块接口的详细说明,请参考QuecPython官网的Wiki文档中相关部分的说明。下面将说明如何使用`USBNET`模块的相关功能。 - -## 2.1 USB网卡相关设置 - -下面将详细描述使用USBNET模块接口的步骤,同时说明在使用过程中一些注意事项。 - -### 2.1.1 使用步骤 - -步骤1:从misc中导入USBNET包 - -```python ->>> from misc import USBNET -``` - - -步骤2:获取USB网卡工作模式 - -对于一块全新的模组,使用USBNET功能之前要先获取其工作模式,确认是用户想要使用的模式。 - -```python ->>> USBNET.get_worktype() -1 -``` - - - -步骤3:设置USB网卡工作模式 - -用户需要调用如下接口,设置自己需要的网卡工作模式(以RNDIS模式为例): - -```python ->>> USBNET.set_worktype(USBNET.Type_RNDIS) -0 -``` - - -步骤4:重启模组 - -设置完USB网卡工作模式,需要重启模组使其生效: - -```python ->>> from misc import Power ->>> Power.powerRestart() -``` - - - -步骤5:打开USBNET功能 - -重启后之前设置的工作模式已生效,直接打开USBNET即可: - -```python ->>> from misc import USBNET ->>> USBNET.open() -``` - - - -### 2.1.2 示例代码 - -如下代码是一个完整的使用`USBNET`模块的例程: - -```python - -from misc import USBNET -from misc import Power - -#work on ECM mode default -USBNET.open() - -USBNET.set_worktype(USBNET.Type_RNDIS) - -#reset the module -Power.powerRestart() - -#after restart -from misc import USBNET - -#work on RNDIS mode -USBNET.open() -``` - - - -## 2.2 NAT相关设置 - -`USBNET.set_worktype()`接口调用的时候会使对应的`nat`值变为`1`,使得该`pid`无法`IPV6`拨号,所以在`close USBnet`后,可以使用该接口关闭`NAT`,使`IPV6`功能正常。 - -> 目前仅EC200U/EC600U系列支持此功能 - -### 2.2.1 获取NAT使能情况 - -获取某一路网卡的NAT使能情况(是否支持ipv6拨号)。命令如下: - -```python -USBNET.getNat(simid, pid) -``` - -参数说明: - -- `imid` - 整型值,范围0/1,目前仅支持`0`。 -- `pid` - 整型值,PDP索引,范围`1-7`。 - -返回值说明: - -成功:返回NAT使能情况,整型0/1,`0`:使能,支持ipv6拨号;`1`:未使能,不支持ipv6拨号。 -失败:返回整型`-1`。 - -### 2.2.2 NAT设置 - -NAT设置,设置成功后重启生效。命令为: - -```python -USBNET.setNat(simid, pid, nat) -``` - -参数说明: - -- `simid` - 整型值,范围0/1,目前仅支持0。 -- `pid` - 整型值,PDP索引, 范围1-7。 -- `Nat` - 整型值,范围:0/1,0:支持ipv6拨号;1:不支持ipv6拨号。 - -返回值说明: - -`0`表示设置成功,`-1`表示设置失败。 - -```python -USBNET.setNat(0, 1, 0) -0 -``` - - - diff --git a/docs/Application_guide/zh/wifiscan/README.md b/docs/Application_guide/zh/wifiscan/README.md deleted file mode 100644 index 625a2aead014ee8f2acf8efa615747cdffd2dfed..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/wifiscan/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# wifiScan应用指导说明 - -本文描述了如何使用QuecPython的wifiScan模块的功能,包含相关API的使用以及一些注意事项。 - - - -## wifiScan应用指导文档列表 - -* [wifiScan功能应用指导](./wifiscan.md) - - - diff --git a/docs/Application_guide/zh/wifiscan/wifiscan.md b/docs/Application_guide/zh/wifiscan/wifiscan.md deleted file mode 100644 index 4cc374b60a0de2a500ead6e7b0aa7fb8fb6a87f7..0000000000000000000000000000000000000000 --- a/docs/Application_guide/zh/wifiscan/wifiscan.md +++ /dev/null @@ -1,240 +0,0 @@ -# 1. 简介 - -QuecPython提供了`wifiScan`功能模块来扫描周边的WiFi热点信息,包括WiFi热点的MAC地址和RSSI信号强度。 - -> 本文档中示例代码前面有 `>>> `字符串的,表示在QuecPython的命令交互界面输入的代码。 - - - -# 2. 使用说明 - -`wifiScan`有同步扫描和异步扫描两种扫描方式。同步扫描是一直等待扫描结束才返回扫描结果,因此扫描接口会阻塞一段时间;而异步扫描则是将扫描结果通过回调来返回给用户,因此扫描接口本身并不会阻塞。具体使用哪一种扫描方式,取决于用户的实际需求。下面分别说明这两种扫描方式的用法。 - -## 2.1 同步扫描 - -如前述所,同步扫描接口会阻塞一段时间,具体阻塞时间的长短取决于扫描配置参数,因此并没有一个固定的时间值。如果用户允许相关线程中使用阻塞性质接口,则可按照如下步骤来使用`wifiScan`的同步扫描功能。 - -### 2.1.1 使用步骤 - -步骤1:确认状态 - -先通过如下接口获取`wifiScan`的状态,确认是否已开启。返回值为`True`表示功能已开启,为`False`表示功能未开启。 - -```python ->>> wifiScan.getState() -False -``` - -步骤2:打开`wifiScan`功能 - -如果`wifiScan.getState()`返回`False`,则使用如下接口来开启`wifiScan`功能。 - -```python ->>> wifiScan.control(1) -0 -``` - -步骤3:扫描参数设置 - -`wifiScan`模块提供了配置接口来设置扫描的相关参数,比如超时时间、扫描轮数、最大扫描热点数量以及扫描优先级。用户可根据需要来设置对应参数。这里设置超时时间为10s、扫描1次、最大扫描数量20个。由于优先级参数是可选参数,对于不支持的模组,我们可以不写该参数。 - -```python ->>> wifiScan.getCfgParam() -(5, 1, 10, 0) ->>> wifiScan.setCfgParam(10, 1, 20) -0 ->>> wifiScan.getCfgParam() -(10, 1, 20, 0) -``` - - - -> * 并不是所有的模组都支持优先级参数,上述示例是基于EC600U系列模组,不支持优先级参数,因此没有配置。不支持优先级参数的模组有:EC200U/EC600U/EG912U/EG915U/EC600G/EC800G系列。 -> -> * 设置的扫描参数掉电不保存。 - - - -步骤4:开始扫描 - -```python ->>> wifiScan.start() -(8, [('34:CE:00:09:E5:A8', -38), ('50:D2:F5:B4:70:BF', -40), ('00:60:92:57:0A:F4', -47), ('00:03:7F:12:06:06', -53), ('F0:2F:74:2A:41:78', -54), ('00:03:7F:12:15:15', -68), ('08:4F:0A:05:22:8B', -73), ('08:4F:0A:05:22:8F', -76)]) -``` - - - -### 2.1.2 示例代码 - -```python -""" -本例程示范了如何使用wifiScan模块的同步扫描功能 -""" -import utime -import wifiScan - - -def main(): - isOpen = wifiScan.getState() - if not isOpen: - ret = wifiScan.control(1) - if ret == 0: - print('wifi scan 打开成功') - else: - print('wifi scan 打开失败') - return -1 - else: - print('wifi scan 已经开启') - - ret = wifiScan.setCfgParam(5, 1, 20, 0) - if ret == 0: - print('扫描参数设置成功') - else: - print('扫描参数配置出错') - return -1 - curCfg = wifiScan.getCfgParam() - if curCfg != -1: - print('当前扫描参数为:') - print('超时时间:{}'.format(curCfg[0])) - print('扫描轮数:{}'.format(curCfg[1])) - print('最大扫描数量:{}'.format(curCfg[2])) - else: - print('获取扫描配置出错') - return -1 - count = 0 - while True: - count += 1 - scanInfo = wifiScan.start() - if scanInfo != -1: - scanNums = scanInfo[0] - wifiInfo = scanInfo[1] - print('扫描到{}个热点:'.format(scanNums)) - for i in wifiInfo: - print(i) - utime.sleep(2) - if count >= 3: - break - else: - print('扫描出错') - return -1 - - ret = wifiScan.control(0) - if ret == 0: - print('wifi scan 关闭成功') - return 0 - else: - print('wifi scan 关闭失败') - return -1 - - -if __name__ == '__main__': - main() - -``` - - - -## 2.2 异步扫描 - -异步扫描方式在使用上和同步扫描区别不大,大部分步骤和同步扫描一致。 - -### 2.2.1 使用步骤 - -步骤1:确认状态 - -步骤2:打开`wifiScan`功能 - -步骤3:扫描参数设置 - -步骤4:注册异步扫描回调函数 - -步骤5:开始扫描 - - - -### 2.2.2 示例代码 - -关于如下例程有两点需要说明: - -* 例程中,`wifiscanCallback`回调中使用消息队列将扫描数据发到`main`中显示处理了。这是因为,在回调中,尽量只做一些耗时短的操作,一些耗时较长、处理较为复杂的操作一般放到其他任务中进行处理,不一定是放到主任务`main`中处理,也可以是其他子任务中。 - -* 消息队列的`get`方法,在没有消息时,会一直阻塞。如果应用代码中某个线程中不能阻塞,则不应该在其中使用该方法。 - -```python -""" -本例程示范了如何使用wifiScan模块的异步扫描功能 -""" -import utime -import wifiScan -from queue import Queue - -msgq = Queue(5) - - -def wifiscanCallback(args): - global msgq - msgq.put(args) - - -def main(): - global msgq - isOpen = wifiScan.getState() - if not isOpen: - ret = wifiScan.control(1) - if ret == 0: - print('wifi scan 打开成功') - else: - print('wifi scan 打开失败') - return -1 - else: - print('wifi scan 已经开启') - - ret = wifiScan.setCfgParam(5, 1, 20, 0) - if ret == 0: - print('扫描参数设置成功') - else: - print('扫描参数配置出错') - return -1 - curCfg = wifiScan.getCfgParam() - if curCfg != -1: - print('当前扫描参数为:') - print('超时时间:{}'.format(curCfg[0])) - print('扫描轮数:{}'.format(curCfg[1])) - print('最大扫描数量:{}'.format(curCfg[2])) - else: - print('获取扫描配置出错') - return -1 - # 注册回调函数 - wifiScan.setCallback(wifiscanCallback) - - count = 0 - while True: - count += 1 - if wifiScan.asyncStart() == 0: - scanInfo = msgq.get() # 没有消息时会阻塞在这里 - scanNums = scanInfo[0] - wifiInfo = scanInfo[1] - print('扫描到{}个热点:'.format(scanNums)) - for i in wifiInfo: - print(i) - utime.sleep(2) - if count >= 3: - break - else: - print('扫描出错') - return -1 - - ret = wifiScan.control(0) - if ret == 0: - print('wifi scan 关闭成功') - return 0 - else: - print('wifi scan 关闭失败') - return -1 - - -if __name__ == '__main__': - main() - -``` - diff --git a/docs/FAQ/zh/mp/hd-design.md b/docs/FAQ/zh/mp/hd-design.md index 0c6c0d7a89e665fd5a1caaf55d74d8e6cb0d73f6..aadd99a49444fba4bc0f7d0f660aa898571ebf47 100644 --- a/docs/FAQ/zh/mp/hd-design.md +++ b/docs/FAQ/zh/mp/hd-design.md @@ -29,3 +29,6 @@ - 如已开机,开发评估板可以正常识别,只有自行制作的PCB无法识别,需要检查自己的USB电路设计是否符合硬件设计的要求,如阻抗匹配要求、走线要求,TVS管是否结电容过大等。排查此问题比较直接的方法是直接连接USB的四根通信线到模块USB引脚,去掉电路板上原先设计的电路。 - 如经过以上排查仍无法识别,需要考虑是否模块损坏,换一块PCB板进行对比测试。 +### 硬件如何设计可以让模组上电自动开机? + +绝大部分模组直接将POWERKEY引脚通过下拉电阻接地即可,但是部分型号不允许一直接地,如BG95系列模组,具体可参考硬件设计手册。除了BG95系列,还有部分应用场景不能一直接地,如需要使用定时开关机降低功耗时,如一直接地,进行软件关机后又会自动开机,此时将需要使用BG95系列类似电路,开机完成后将POWERKEY引脚拉高,将不再影响软件关机功能。 \ No newline at end of file diff --git a/docs/Getting_started/zh/Note.md b/docs/Getting_started/zh/Note.md deleted file mode 100644 index 19973e3f7b18ac09c874c14eb48bbf306d2b4705..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/Note.md +++ /dev/null @@ -1,72 +0,0 @@ -# QuecPython 开发指南编写注意事项 - -## 目录结构 - -开发指南根目录下存在一个名为`README.md`的文件,作为开发指南的首页入口。 - -> 文档中心使用的[teedoc](https://gitee.com/teedoc)架构要求每个版块的首页文件都要是名为`README.md`的文件。 - -每个功能板块在根目录下均对应一个文件夹,在该文件夹内,按照功能板块,拆分为多个对应的md文件。 - -所有md文件中引用的图片,均存放于media目录中: -- 开发指南文件夹下有多少文件夹,在media也按照目录关系建立相应的文件夹。 -- 图片统一为png格式,命名请见名知意,如`BSP.xxx.png`,其中`xxx`表示用简短英文编写的图片作用描述。 - -目录结构示例如下: - -``` -QuecPython官网文档中心/开发指南 -├── BSP应用开发 -│   ├── BSP-Audio应用开发.md -│   ├── BSP-GPIO应用开发.md -│   ├── BSP-UART应用开发.md -│   └── README.md -├── Note.md -├── OTA升级 -│   ├── OTA升级-固件.md -│   ├── OTA升级-文件.md -│   └── README.md -├── README.md -├── media -│   ├── BSP应用开发 -│   │   └── BSP.xxx.png -│   └── OTA升级 -│   └── OTA.xxx.png -├── 外设应用开发 -│   ├── 外设-LCD应用开发.md -│   ├── 外设-摄像头应用开发.md -│   └── README.md -├── 多线程应用开发 -│   ├── 多线程-互斥锁应用.md -│   ├── 多线程-创建线程.md -│   ├── 多线程-消息队列应用.md -│   └── README.md -├── 快速入门.md -└── 网络通信应用开发 - ├── 网络通信-HTTP通信.md - ├── 网络通信-MQTT通信.md - ├── 网络通信-SNMP通信.md - ├── 网络通信-TCP与UDP通信.md - ├── 网络通信-WebSocket通信.md - ├── 网络通信-数据拨号.md - └── README.md -``` - -## README.md - -README.md作为板块的首页入口,需要对该板块做综合性描述,并且在最后提供板块的所有二级目录链接。 - -## 正文内容格式 - -参照以下格式: - -```markdown -# BSP 应用开发 - -正文 - -``` - -## QuecPython 开发指南 - -[点此查看](./README.md) diff --git a/docs/Getting_started/zh/README.md b/docs/Getting_started/zh/README.md index 11514b8c941a199eb3e6268b361edc5ed41ca5c1..b932502c7b15566bfea82655eba74603354612ca 100644 --- a/docs/Getting_started/zh/README.md +++ b/docs/Getting_started/zh/README.md @@ -1,268 +1,318 @@ -# 目录 - -
- 1 背景知识 - -- [1.1 物联网和低代码开发](./background/iot-and-low-code.md) - -- [1.2 无线通信模块简介](./background/wireless-modules.md) - -- [1.3 QuecPython 简介](./background/about-qpy.md) - -- [1.4 硬件选型](./background/selection-guide.md) - -
- -
- 2 快速入门 - -- [2.1 准备工作](./quick-start/preparation.md) - -- [2.2 上电与连接](./quick-start/boot-and-connect.md) - -- [2.3 固件烧录](./quick-start/burn-firmware.md) - -- [2.4 QPYcom 基本操作](./quick-start/qpycom-basic.md) - -
- -
- 3 MicroPython 语法基础 - -- [3.1 基本语法规则](./mpy-syntax/rules.md) - -- [3.2 变量和运算](./mpy-syntax/vars-and-ops.md) - -- [3.3 程序流程控制](./mpy-syntax/flow-control.md) - -- [3.4 常用容器类型](./mpy-syntax/containers.md) - -- [3.5 函数](./mpy-syntax/functions.md) - -
- -
- 4 系统功能 - -- [4.1 系统信息](./os/os-info.md) - -- [4.2 日志功能](./os/log.md) - -- [4.3 文件管理](./os/files.md) - -- [4.4 时间功能](./os/time.md) - -- [4.5 数据格式转换](./os/data-formattng.md) - -- [4.6 电源管理](./os/power.md) - -- [4.7 功耗管理](./os/pm.md) - -- [4.8 内存管理](./os/ram.md) - -- [4.9 多线程](./os/threads.md) - -
- -
- 5 硬件功能 - -- [5.1 GPIO](./hardware/gpio.md) - -- [5.2 外部中断](./hardware/extint.md) - -- [5.3 UART](./hardware/uart.md) - -- [5.4 I2C](./hardware/i2c.md) - -- [5.5 SPI](./hardware/spi.md) - -- [5.6 ADC](./hardware/adc.md) - -- [5.7 Timer](./hardware/timer.md) - -- [5.8 PWM](./hardware/pwm.md) - -- [5.9 看门狗](./hardware/wdt.md) - -- [5.10 屏幕显示](./hardware/screen.md) - -- [5.11 LVGL](./hardware/lvgl.md) - -- [5.12 摄像头](./hardware/camera.md) - -- [5.13 数码管](./hardware/nixie-tube.md) - -- [5.14 音频和TTS](./hardware/Audio_and_TTS.md) - -- [5.15 存储器扩展](./hardware/ext-storage.md) - -- [5.16 矩阵键盘](./hardware/matrix-keypad.md) - -- [5.17 BT 和 BLE](./hardware/bt/README.md) - -- [5.18 USB 网卡](./hardware/usb-wireless-card.md) - -- [5.19 以太网卡](./hardware/ext-ethernet.md) - -- [5.20 温湿度传感器](./hardware/humiture-sensor.md) - -- [5.21 光敏传感器](./hardware/light-sensor.md) - -- [5.22 加速度传感器](./hardware/acceleration-sensor.md) - -- [5.23 步进电机驱动](./hardware/step-motor.md) - -- [5.24 流水灯](./hardware/water-lamp.md) - -- [5.25 蜂鸣器](./hardware/buzzer.md) - -
- -
- 6 网络与通信 - -- [6.1 天线、SIM 卡和网络注册](./network-comm/preparation.md) - -- [6.2 APN与数据拨号](./network-comm/apn-and-datacall.md) - -- [6.3 TCP 与 UDP 通信](./network-comm/tcp-and-udp.md) - -- [6.4 HTTP 通信](./network-comm/http.md) - -- [6.5 MQTT 通信](./network-comm/mqtt.md) - -- [6.6 WebSocket 通信](./network-comm/websocket.md) - -- [6.7 NTP时间同步](./network-comm/ntp.md) - -- [6.8 语音通话](./network-comm/voicecall.md) - -- [6.9 短信](./network-comm/sms.md) - -- [6.10 通信异常处理](./network-comm/exceptions.md) - -
- -
- 7 云平台对接 - -- [7.1 阿里云](./clouds/aliyun.md) - -- [7.2 腾讯云](./clouds/tencent.md) - -- [7.3 华为云](./clouds/huawei.md) - -- [7.4 移动云](./clouds/onenet.md) - -- [7.5 电信云](./clouds/ctyun.md) - -- [7.6 亚马逊云](./clouds/aws.md) - -- [7.7 微软云](./clouds/azure.md) - -
- -
- 8 定位应用 - -- [8.1 内置GNSS定位](./location/internal_gnss.md) - -- [8.2 外置GNSS定位](./location/external_gnss.md) - -- [8.3 基站定位](./location/cell-location.md) - -- [8.4 WiFi定位](./location/wifi-location.md) - -
- -
- 9 高级组件 - -- [9.1 modbus](./high-level-component/modbus.md) - -- [9.2 asyncio](./high-level-component/asyncio.md) - -- [9.3 blinker](./high-level-component/blinker.md) - -
- -
- 10 应用框架 - -- [10.1 Helios service](./app-framework/helios-service.md) - -- [10.2 sys_bus](./app-framework/sys_bus.md) - -- [10.3 event_mesh](./app-framework/EventMesh.md) - -
- -
- 11 FOTA 应用 - -- [11.1 脚本文件升级](./fota/fota-file.md) - -- [11.2 M-N-A系列模组固件升级](./fota/fota-fw-m_n_a.md) - -- [11.3 U-G系列模组固件升级](./fota/fota-fw-u_g.md) - -- [11.4 E系列模组固件升级](./fota/fota-fw-e.md) - -- [11.5 BG系列模组固件升级](./fota/fota-fw-bg.md) - -
- -
- 12 Helios SDK 开发指南 - -- [12.1 入门](./helios-sdk/quick-start.md) - -- [12.2 进阶](./helios-sdk/junior.md) - -- [12.3 高级](./helios-sdk/advanced.md) - -
- -
- 13 量产指导 - -- [13.1 脚本加密和固件打包](./mass-production/encryption-and-packaging.md) - -- [13.2 备份分区和数据安全区](./mass-production/data-backup.md) - -- [13.3 量产工具](./mass-production/production-tools.md) - -- [13.4 产测工具](./mass-production/testing-tools.md) - -- [13.5 硬件设计和生产注意事项](./mass-production/considerations.md) - -
- -
- 14 产品方案介绍 - -- [14.1 DTU](./solutions/dtu.md) - -- [14.2 定位器](./solutions/tracker.md) - -- [14.3 对讲机](./solutions/poc.md) - -- [14.4 电表](./solutions/smart-meter.md) - -- [14.5 云喇叭](./solutions/payment-speaker.md) - -- [14.6 学生卡](./solutions/student-card.md) - -- [14.7 充电桩](./solutions/charging-pile.md) - -
- -
- 15 附录 - -- [15.1 QuecPython 开发板板载资源列表](./appendix/evb-resources.md) - -- [15.2 QuecPython 异常处理流程](./appendix/exception-handling.md) - -
+# 快速入门 + +本文档旨在指导用户搭建QuecPython硬件开发的软件环境,通过一个简单的示例展示如何使用官方的开发板进行开发调试,并使用QuecPython进行固件下载、代码开发和调试等步骤。 + +## 概述 + +QuecPython模组具有以下优势 + +- 支持Cat 1、Cat 4、Cat M、NB等多种网络制式,具有超高性价比 +- 持IEEE 802.11b/g/n/ax和蓝牙5.1协议 +- 内置丰富的网络协议,集成多个工业标准接口 +- 支持多种驱动和软件功能(适用于Windows 7/ 8/ 8.1/ 10/ 11、Linux 和 Android 等操作系统下的 USB 驱动) +- 使用Python语言开发,大大提升了传统物联网开发的效率 + +移远提供了手把手的入门文档、教学文档和教学视频,用户可以从零入手学习QuecPython的使用,同时丰富的技术支撑全程为用户开发提供服务。可以满足如POS、POC、ETC、共享设备、数据卡、能源控制、安防以及工业级 PDA 等多方面的行业需求。 + +### 当前支持的模组 + +| 区域/网络制式 | Cat 1 | Cat 4 | Cat M | NB | WIFI | +| ------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | --------------------------------------------------------- | +| 国内 | [EG810M-CN](https://python.quectel.com/products/eg810m-cn)
[EC800E-CN](https://python.quectel.com/products/ec800e-cn)
[EC600E-CN](https://python.quectel.com/products/ec600e-cn)
[EC600G-CN](https://python.quectel.com/products/ec600g-cn)
[EC800G-CN](https://python.quectel.com/products/ec800g-cn)
[EC200N-CN](https://python.quectel.com/products/ec200n-cn)
[EC800M-CN](https://python.quectel.com/products/ec800m-cn)
[EC600M-CN](https://python.quectel.com/products/ec600m-cn)
[EC200U-CN](https://python.quectel.com/products/ec200u-cn)
[EC600U-CN](https://python.quectel.com/products/ec600u-cn)
[EC600N-CN](https://python.quectel.com/products/ec600n-cn) | [EC200A-CN](https://python.quectel.com/products/ec200a-cn)
| / | [BC25](https://python.quectel.com/products/bc25) | [FCM360W](https://python.quectel.com/cn/products/fcm360w) | +| 海外 | [EG915U-EU](https://python.quectel.com/en/products/eg915u-eu)
[EG915N-EU](https://python.quectel.com/en/products/eg915n-eu)
[EG912U-GL](https://python.quectel.com/en/products/eg912u-gl)
[EG912N-EN](https://python.quectel.com/en/products/eg912n-en)
[EC200U-EU](https://python.quectel.com/en/products/ec200u-eu)
[EC600U-EU](https://python.quectel.com/en/products/ec600u-eu) | [EC200A-EU](https://python.quectel.com/en/products/ec200a-eu)
[EC200A-AU](https://python.quectel.com/en/products/ec200a-au) | [BG77](https://python.quectel.com/en/products/bg77)
[BG95-M1](https://python.quectel.com/en/products/bg95m1)
[BG95-M3
](https://python.quectel.com/en/products/bg95m3)[BG95-M8](https://python.quectel.com/en/products/bg95m3) | [BG77](https://python.quectel.com/en/products/bg77)
[BG95-M3
](https://python.quectel.com/en/products/bg95m3)[BG95-M8](https://python.quectel.com/en/products/bg95m3) | [FCM360W](https://python.quectel.com/en/products/fcm360w) | + + +## 准备工作 + +硬件: + +- 一块 [**QuecPython_EC2X_EVB**](EC2X_EVB.md) 开发板 (开发板介绍参见下文开发板列表) +- **USB 数据线** (USB-A TO USB-C) +- **PC** (Windows7 & Windows10 & Windows11) + +开发板列表: + +- [EC2X_EVB](EC2X_BOARD.md) +- [FCM360W-TE-B](FCM360W-TE-B.md) + +软件: + +- 下载安装 **USB驱动**,用于开发调试QuecPython模组 +- 下载**调试工具** —— QuecPython全栈式开发调试工具 +- 获取下载 **QuecPython** 固件和相关软件资源 +- 安装**Python语言**的 **文本编辑器**,例如 [VSCode](https://code.visualstudio.com/) + +## 详细步骤 + +请根据下方详细步骤,完成开发环境搭建和第一个脚本的开发。 + +- **第一步:硬件准备** +- **第二步:驱动准备** +- **第三步:获取工具** +- **第四步:安装VScode插件** +- **第五步:获取固件** +- **第六步:烧录固件** +- **第七步:REPL调试** +- **第八步:编写第一个脚本** + +## 硬件准备 + +- [EC2X_EVB 上手准备](https://python.quectel.com/doc/Quick_start/zh/EC2X_BOARD.html#上手准备) +- [FCM360W 上手准备](https://python.quectel.com/doc/Quick_start/zh/FCM360W-TE-B.html#上手准备) + +## 驱动准备 + +- **Step1:驱动下载** + +驱动程序(device driver)全称为“设备驱动程序”,是一种可以使计算机和设备通信的特殊程序,操作系统只能通过这个接口,才能控制硬件设备的工作。 + +- 打开[QuecPython官网驱动下载链接](https://python.quectel.com/download) + +- 先选择驱动栏,然后在驱动栏选择与自己模组型号和电脑系统匹配的驱动,不同平台的模组所需要的驱动程序不一致,需要根据模组型号去下载对应的驱动包 + +- 模组型号可以通过模组上的镭雕获取,也可以通过AT指令获取,镭雕如下图所示,镭雕上型号为EG912U + + + +- 在搜索框输入`EG912U`,找到EG912U需要适用的驱动QuecPython_USB Driver Win10UG 点下下载按钮下载驱动: + + + +| 模组型号 | 平台 | 驱动名称 | +| ------------------------------------------------------------ | ----- | ---------------------------------------------- | +| [EC200A-CN](https://python.quectel.com/products/ec200a-cn) | Win10 | **QuecPython_USB_Driver_Win10_A** | +| [BG77](https://python.quectel.com/en/products/bg77)
[BG95-M3](https://python.quectel.com/en/products/bg95m3)
[BG95-M8](https://python.quectel.com/en/products/bg95m8) | Win10 | **QuecPython_USB_Driver_Win10_BG** | +| [EC800E-CN](https://python.quectel.com/products/ec800e-cn)
[EC600E-CN](https://python.quectel.com/products/ec600e-cn) | Win10 | **QuecPython_USB_Driver_Win10_E** | +| [EC200N-CN](https://python.quectel.com/products/ec200n-cn)
[EC800M-CN](https://python.quectel.com/products/ec800m-cn)
[EC600N-CN](https://python.quectel.com/products/ec600n-cn)
[EC600M-CN](https://python.quectel.com/products/ec600m-cn) | Win10 | **QuecPython_USB_Driver_Win10_M_N** | +| [EC600U-CN](https://python.quectel.com/products/ec600u-cn)
[EC600G-CN](https://python.quectel.com/products/ec600g-cn)
[EC800G-CN](https://python.quectel.com/products/ec800g-cn) | Win10 | **QuecPython_USB_Driver_Win10_U_G** | + + +- **Step2:驱动安装** + +下载后解压驱动压缩包,找到**"setup.exe"** 或者是**"setup.bat"**,双击运行即可,安装完之后打开设备管理器就可以看到设备管理器中端口的黄色感叹号消失了,说明安装成功,能够正常通信。 + +*出现 Mobile ECM Network Adapter CDC Ethernet Control Modle (ECM) 等设备未被识别属于正常现象,不影响固件烧录和后续开发,无需理会。* + +右键打开【**我的电脑**】——选择【**管理**】——选择【**设备管理器**】,然后在设备管理器中选择 【**端口**】 + +按照步骤打开页面后如图所示能刷新出**Quectel USB** 名称开头的串口则USB驱动安装成功 + +**安装驱动后:** + + + +## 获取工具 + +使用QuecPython进行开发需要用到专用的开发调试工具——**QPYcom**,包括但不限于调试代码、分析日志、文件传输、烧录固件、合并固件等。 + +[QPYcom](hhttps://python.quectel.com/download) : 适用于QuecPython开发的一站式调试工具 + +*注意:该工具无需安装,解压即用,建议提前手动关闭系统中安装的防病毒软件,以避免潜在的误报导致工具被误删或者无法使用。* + +## 安装VSCode插件 + +编辑Python代码一般会用到专用于Python或者兼容多种语言的IDE,可以有效提升开发效率,这里推荐VSCode + +针对VScode,QuecPython推出专用插件实现代码提示、代码补全和串口调试等功能,安装方法见下图 + +- 在VSCode中点击侧边栏插件市场,在插件市场中搜索 “QuecPython”,根据搜索结果下载该插件即可 + +![](media/readme/VScode_1.png) + +- 在[VSCode插件商店网站](https://marketplace.visualstudio.com/vscode) 搜索“QuecPython”,根据搜索结果下载该插件后会自动打开VSCode并安装 + +![](media/readme/VScode_2.png) + +## 获取固件 + +模组在出厂时通常烧录有标准 AT 固件或 QuecOpen 固件,如需基于 QuecPython 对模块进行开发,需要手动为其重新烧录专门的 QuecPython 固件。 + +官网固件下载地址:**** + +![](media/readme/Firmware.png) + +面对官网种类众多的固件,如何选择合适的固件包,首先需要知道使用的模组的型号,模组型号可以通过模组的镭雕或者发送AT指令来获得。 + +在获取到模组型号之后根据模组的型号去官网下载该模组对应的固件即可。 + +![](media/readme/firmware_zip.png) + +> 从官网下载的固件包为压缩包格式,固件压缩包下载到本地后,需进行解压。解压后可获得两个文件,其中 .bin 、.lod或 .pac 格式的是 QuecPython 固件本体,.md 格式的是更新日志。 + +> 请务必将压缩包内容解压至一个**不包含中文、空格和其他特殊字符**的路径下,否则下载工具可能无法正常识别到固件,同时下载工具路径也**不可包含中文、空格和其他特殊字符**。 + +## 烧录固件 + +- **Step1:创建项目** + +首先确保模组连接正常并已开机,打开工具进入下载页面,点击**"创建"**项目,新建要下载的固件项目 + +- **Step2:选择固件** + +选择要下载到模组的固件(根据要下载的模组型号选择对应的固件) + +- **Step3:设置下载模式** + +单击**“Download script”**右侧的下拉选择箭头,选择**"Download FW"** + +- **Step4:开始烧录固件** + +点击**"Download FW"**,开始下载固件,下载过程会有进度条和进度百分比显示,等待下载完毕会有弹窗提示下载成功 + +![](media/readme/firmware.gif) + +## REPL调试 + +REPL全称为**Read-Eval-Print-Loop (交互式解释器)**,可以在REPL中进行QuecPython程序的调试 + +运行 **QPYcom** 工具后,选择正确的串口(波特率无需指定)并打开,即可开始 Python 命令行交互。 + +- **Step1:进入交互页面** + +打开QPYcom工具,端口选择连接**“Quectel USB MI05 COM Port”**,选择“交互”界面 + +- **Step2:打开串口** + +点击“打开串口”按钮,在交互界面输入**print(‘hello world’)**,按回车后可以看到执行的结果信息 + +```python +>>> print('hello world') +hello world +``` + +![](media/readme/hello_world.jpg) + +*注意:工具交互页面输入时需要输入英文字符,中文字符将会被屏蔽* + +## 开发第一个脚本 + +### 编写第一个脚本文件 + +创建*helloworld.py*文件输出“hello world”,打印“hello world”,编写脚本如下所示: + +```python +print("hello world") +``` + +通过QPYcom将上面编辑好的文件下载到模组中去并运行 + +![](media/readme/QPYcom_hello.jpg) + +### PC与模组间的文件传输 + +**下载方法一:** + +image-2021081301 + +- **Step1:打开串口** + +首先选择模组的交互口,点击"打开串口"按钮 + +- **Step2:通过工具按钮下载** + +可以通过文件页面右侧上面的 "**+**","**-**" 按钮来上传和删除文件 + +- **Step3:通过拖拽形式下载** + +也可以通过拖拽的方式将文件页面左侧显示的本地文件直接拖拽到右侧模组中去(也可以拖拽文件夹) + +- **Step4:下载进度和结果** + +下载过程中会在状态栏显示下载文件名和下载进度 + +**下载方法二:** + +image-2021081301 + +- **Step1:创建项目** + +根据需求,创建用户项目(点击"创建"按钮),步骤同上文烧录固件 + +- **Step2:配置要下载的文件** + +选择需要下载到模块的用户脚本(在"用户脚本"区域通过右键菜单添加) + +- **Step3:设置下载模式** + +左击下拉选择箭头,选择"下载脚本",即"Download Script" + +- **Step4:开始下载脚本** + +点击"下载脚本"开始下载脚本,下载过程中有进度条提示 + +### 执行脚本文件 + +- **Step1:下载脚本** + +将要执行的脚本文件下载到模组中去,具体步骤参考上文 + +- **Step2:通过repl执行脚本** + +打开串口之后,在QPYcom交互页面输入以下代码执行脚本文件 + +```python +import example +example.exec("/usr/helloworld.py") # filePath为要执行的脚本文件路径 +``` + +- **Step3:通过GUI工具执行脚本** + +或者通过QPYcom文件页面 **执行** 按钮执行脚本文件,在工具的文件页面选择要执行的脚本文件然后点击 "**▷**"按钮 + +执行结果如图 + +![](media/readme/QPYcom_repl_hello.jpg) + + +### 停止程序运行 + +如何停止正在运行的程序,根据运行的脚本文件类型有以下方法: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
程序名是否
为main.py
程序中是否
包含了死循环
程序中是否
使用了多线程
停止步骤
(1)按 Ctrl + A 键进入 RAW 模式
(2)按 Ctrl + D 键重启 QuecPython 虚拟机
(3)按 Ctrl + B 键回到普通交互模式
(4)若以上方法无效,请重新烧录固件
(1)按 Ctrl + C 键打断程序运行
(2)若以上方法无效,请重新烧录固件
(1)按 Ctrl + A 键进入 RAW 模式
(2)按 Ctrl + D 键重启 QuecPython 虚拟机
(3)按 Ctrl + B 键回到普通交互模式
(4)若以上方法无效,请耐心等待程序运行结束
(1)按 Ctrl + C 键打断程序运行
(2)若以上方法无效,请重新烧录固件
(1)按 Ctrl + D 键重启 QuecPython 虚拟机
(2)若以上方法无效,请直接重启模块
(1)按 Ctrl + D 键重启 QuecPython 虚拟机
(2)若以上方法无效,请直接重启模块
(1)按 Ctrl + C 键打断程序运行
(2)若以上方法无效,请重新烧录固件或直接重启模块
(1)按 Ctrl + C 键打断程序运行
(2)若以上方法无效,请重新烧录固件或直接重启模块
\ No newline at end of file diff --git a/docs/Getting_started/zh/app-framework/EventMesh.md b/docs/Getting_started/zh/app-framework/EventMesh.md deleted file mode 100644 index f71f9d6a71389b34c12a6b44fab468585f63deba..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/app-framework/EventMesh.md +++ /dev/null @@ -1,157 +0,0 @@ -# EventMesh - - - -`获取地址`: [源代码](https://github.com/QuecPython/EventMesh) - - - -EventMesh是一个轻量级的事件驱动框架,主要用于在微控制器等资源受限的环境中构建高效的消息传递和事件处理系统。它允许应用程序在运行时订阅和发布事件,并且可以异步或同步地处理它们。 - - - -## 1. API使用说明 - - - -### 1.1 导入模块 - -使用EventMesh模块之前,需要将该模块安装到MicroPython设备中。可以将EventMesh.py文件上传到MicroPython设备中,并通过`import`命令导入该模块: - -```python -from usr import EventMesh -``` - - - - - -### 1.2 `EventMesh.subscribe(event, cb)` - -订阅一个事件。此函数接受两个参数:事件名称以及对应的回调函数。每个事件只能对应一个回调函数。 - -**参数:** - -- `event`:事件名称,字符串类型。 -- `cb`:回调函数,当事件被触发时,此函数将会被调用。 - -**示例:** - -```python -def my_callback(event, msg): - print(f'Event: {event}, Message: {msg}') - -EventMesh.subscribe('my_event', my_callback) -``` - - - -### 1.3 `EventMesh.publish(event, msg=None)` - -发布一个事件。此函数接受两个参数:事件名称以及可选的消息。此函数将同步调用事件对应的回调函数。 - -**参数:** - -- `event`:事件名称,字符串类型。 -- `msg`:可选的消息,任意类型。 - -**示例:** - -```python -EventMesh.publish('my_event', 'Hello, World!') -``` - - - -### 1.4 `publish_async(event, msg=None)` - -异步发布一个事件。此函数接受两个参数:事件名称以及可选的消息。此函数将在新的线程中调用事件对应的回调函数。 - -**参数:** - -- `event`:事件名称,字符串类型。 -- `msg`:可选的消息,任意类型。 - -**示例:** - -```python -EventMesh.publish_async('my_event', 'Hello, World!') -``` - - - -### 1.5 `set_log(log_adapter)` - -设置日志记录器。此函数接受一个参数:日志记录器对象。 - -**参数:** - -- `log_adapter`:日志记录器对象。 - -**示例:** - -```python -import log -my_logger = log.getLogger("T1") -EventMesh.set_log(my_logger) -``` - - - -### 1.6 `add_filter(flt)` - -添加一个过滤器。此函数接受一个参数:过滤器对象。过滤器可以用于过滤出不需要记录的事件日志。 - -**参数:** - -- `flt`:过滤器对象。 - -**示例:** - -```python -add_filter("my_event") -``` - - - -## 2. 架构原理 - -EventMesh是一个基于事件驱动的轻量级消息框架,它采用了发布-订阅模式,将事件发布者和订阅者解耦,使系统更具灵活性和可扩展性。 - -不同于传统的消息框架,EventMesh的特点在于,每个事件(topic)只对应一个订阅函数,即一对一的模型。这样可以避免事件处理的复杂性和不确定性,提升系统的稳定性。 - -架构图如下: - -```lua - +-------------+ +-------------+ +-------------+ - | Publisher | -- publish --> | EventStore | -- notify --> | Subscriber | - +-------------+ +-------------+ +-------------+ - -``` - -对比原先未解耦的情况的架构图: - -```lua - +-------------+ +-------------+ - | Publisher | -- direct call -->| Subscriber | - +-------------+ +-------------+ - -``` - -在未解耦的情况下,发布者直接调用订阅者的方法,这种方式会导致系统的组件高度耦合,一旦某个组件需要更改,可能会影响到其他的组件。而在EventMesh中,发布者只需要发布事件,不需要关心谁会处理这个事件,这样就降低了系统各组件之间的耦合度。 - - - -## 3. 优缺点 - -### 优点: - -1. **解耦**:发布者和订阅者完全解耦,互不依赖,只需要关注自己的业务逻辑。 -2. **异步处理**:支持异步事件处理,能够提高程序的性能和响应速度。 -3. **一对一模型**:每个事件只有一个订阅者,避免了处理函数的冲突和竞态条件。 - -### 缺点: - -1. **缺乏事件监控**:由于事件的处理是异步的,因此很难对事件的处理进行监控和调试。(默认是非异步模式除非主动调用publis_async) -2. **过于依赖事件**:由于发布者和订阅者是通过事件进行通信的,因此如果事件的处理出现问题,可能会影响到整个系统的运行。 -3. **一对一模型限制**:由于每个事件只能有一个订阅者,这可能限制了系统的扩展性。如果需要对一个事件进行多种处理,就需要发布多个事件,这可能会导致事件的管理变得复杂。 \ No newline at end of file diff --git a/docs/Getting_started/zh/app-framework/README.md b/docs/Getting_started/zh/app-framework/README.md deleted file mode 100644 index 4be2d265eec6a8609850168277fbe7ac601c96c5..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/app-framework/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# 14 应用框架 - -
目录 - - - -- [14.1 HeliosService](./helios-service.md) -- [14.2 sys_bus](./sys_bus.md) -- [14.3 EventMesh](./EventMesh.md) - - 
- diff --git a/docs/Getting_started/zh/app-framework/helios-service.md b/docs/Getting_started/zh/app-framework/helios-service.md deleted file mode 100644 index d485caa9b6d19cd795848688afa1822c645bcd98..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/app-framework/helios-service.md +++ /dev/null @@ -1,765 +0,0 @@ -# HeliosServices - -1.背景 - -2.新框架解决那些问题/特性/优势 - -3.能帮助客户解决那些问题 可举例 - -4.简单说明框架如何使用,目录结构解析,使用教程 - -5.一个简单的例子 举例论证新框架的易用性和为什么要用新框架 - -​ 用新框架连阿里云,设备端喂狗、拉GPIO、组包发数据 - -描述一下开发过程中的痛点 ---> 为解决这些痛点开发了新框架来解决这些问题-->如何解决问题/解决了那些问题 - -(比如支持日志服务、解决线程通信问题、支持看门狗组件、支持腾讯云组件、阿里云组件) - - - -`获取地址`: [源码](https://gitee.com/quecpython/helios-service) - - - -## 入门 - -### 背景 - -QuecPython目前一直没有一个应用层框架来实现统筹的消息注册,消息监听,事件管理, 组件通信,会话等功能, 解决一直存在的重复工作量和安全稳定性问题,为了迎合QuecPython的发展, 简化用户对底层网络, 日志, 移远云服务, 定位, 媒体等复杂的数据处理和数据保护等操作,特此我们专门开发了一套应用编程框架来简化复杂的创建、维护的等操作。 - -我们想通过数据驱动来改善我们目前复杂的编码过程, 顺着这个出发点QuecPython应用编程框架--HeliosService 正式面世! - -### 优势 - -尽管python开发已经相当便捷且功能强大,而QuecPython又在python的基础上极大的拉低了嵌入式开发的门槛,但是仍然会有很多新手开发者在想要开发自己的项目时不知从何入手亦或是在搭建项目框架、编写项目代码时遇到形形色色的问题(以上种种俗称踩坑),为了避免开发者重复踩坑,提升开发者开发效率,于是集结了众多前人的踩坑经验以及官方开发者的满腔心血而开发出的HeliosService顺应天时而生。 - -那么HeliosService新框架给QuecPython带来了哪些变革呢? - -HeliosService将常用的功能整合成服务,并提供统一的API来供客户调用,用户在实现网络、日志、异常、线程通信、队列等功能时更加便捷,另外提供了丰富的第三方组件,例如中断拦截器、GPIO读写拦截器、定时拦截器、看门狗拦截器,极大的减轻了用户的开发难度。 - -**设计的初衷** - -- 将用户和操作一些主动行为变为被动行为的 -- 业务之间的解耦, 所以我们特此讲系统的一些组件设计成发布订阅的模式 -- 开发者订阅相关只需要关注的服务, 即可获取相关的状态 -- 提供消息队列,客户可以通过消息队列来定制自己的组件 -- 提供会话中心, 脱离系统服务外支持客户自定义TOPIC发布订阅的服务 -- 容器化, 我们提供启动器, 所有的服务和组件获取都通过容器来获取,而不再逐个依赖进行加载 -- 简化客户的操作, 和解耦业务之间的依赖使我们的和兴目标 - -而新框架又解决了哪些问题呢?让我们走进下一章。 - -### 解决的问题 - -**携带发布订阅BUS总线** - - - 支持消息和处理的绑定 - - 异步的消息处理机制 - - 用户不需关心对应的处理函数,只管向对应的topic发消息即可 - -**携带消息队列广播机制** - - - 可自定义消息类型,执行异步和同步模式,支持事件派发和事件管理, 支持标准队列和观察者模式) - - 解决了两个线程之间传递消息,以及多个事件广播的问题 - - 给模块进行了标准化的解耦过程, 提供了统一稳定的事件管理中心来保证通信的质量 - -**NET(网络)** - - - 提供网络服务(解决了客户网络的依赖, 客户只需要注册回调接口, 就可以发现注网的状态, 也支持主动查询, 设置APN等操作) - - 支持断网通知, 断网重连通知等,支持订阅消息 - - 支持网络主动查询 - -**LOG(日志)** - - - 提供标准化的日志系统(解决日志标准化的问题, 提供日志标准化的输出并携带时间,同样支持异步和同步的配置, 支持云端发送, 落盘等行为) - - 支持不同uart口输出, 落盘输出等 - - 支持订阅消息发布消息 - -**CLOUD(移远云服务)** - - - 提供基于移远云的OTA升级和日志发送接口(临终遗言日志上报, OTA的升级, OTA组件等 - - 同样支持异步和同步模式) - - 支持自动化固件升级 - - 支持自动化文件升级 - - 支持提交云端日志可配合我们日志系统使用 - -**MEDIA(媒体服务)** - - - 提供媒体功能(支持发布异步的媒体消息, 统一媒体消息管理等等) - - 支持异步tts, audio播报 - - 支持tts,audio消息发布,消息广播,消息订阅 - - 支持tts, audio播报的自动管理,保证消息播报的可靠性 - -**EXCEPTION(异常服务)** - - - 提供异常服务支持 - - 支持同步异步消息 - - 支持异常消息订阅, 异常消息发送 - -**PM(低功耗服务)** - - - 可选的功耗管理 - - 默认低功耗模式 - - 支持高刷唤醒 - -**SYS_BUS(发布订阅总线)** - - - 支持自定义topic实现发布订阅 - - 支持发布订阅, 通过自动化的线程渠道去分配线程处理业务 - - 支持高并发的处理 - -**Queue(标准队列)** - - - 支持不同线程中消息的传递 - - 阻塞实现, 支持多线程的put ,get 原子操作 - -### 使用说明 - -**代码目录结构** - -```python -usr # 用户分区 -├─main.py # 用户代码(程序入口) -├─bin # 脚本和启动器(下面放着一些共有服务和一些公有的组件) -│ ├─cloud_service.py # 云服务组件 -│ ├─exception_service.py # 异常服务组件 -│ ├─guard.py # 全局监控和配置文件的启动容器 -│ ├─log_service.py # 日志服务组件 -│ ├─media_service.py # 媒体服务组件 -│ ├─net_service.py # 网络服务组件 -│ ├─pm_service.py # 低功耗服务组件 -│ └─components # 该目录下存放一些公共组件 -│ │ ├─abstract_service.py # 服务抽象类 -│ │ ├─blinker_min.py -│ │ ├─monitor.py -│ │ ├─OTA.py # OTA升级组件 -│ │ └─service_models.py # 服务模型基类 -│ └─third_party -│ ├─ql_interrupter.py # 第三方服务组件,外部中断/看门狗等 -│ └─ ... -├─etc # 配置文件目录(非必须存在) -│ ├─app_config # app业务服务配置文件目录 -│ └─system_config # 系统服务配置文件目录 -├─log # 日志存储服务(非必须存在,选择日志存储为本地时自动创建) -└─utils # 通用工具目录 - ├─JsonParserUtils.py # josn处理utils - ├─resolver.py # 时间格式化处理器 - └─service_utils.py # 单例模式类实现 -``` - -> -> -> 第三方的组件会持续更新增加新内容 - -#### 搭配工具使用说明 - -> 新框架需要搭配QPYcom_V1.7版本使用 - -以上所有目录和文件可通过QPYcom下载到模块中,用户编辑好自己的代码之后下载到模块中即可进行调试,下载方式参考下图 - -第一种方式,下载页面配置项目,优势是一键导入目录结构之后再次下载调试点击下载脚本按钮即可,方便后续调试 - -![t21fkamlv5](../media/app-framework/heliosServices/HeliosService_1_01.gif) - -第二种方式,文件页面从左侧本地文件树拖拽到右侧模块文件树,等待下载完成即可,优势是直接拖拽操作简单便捷 - -![ZLLVygvK5o](../media/app-framework/heliosServices/HeliosService_1_02.gif) - -> 上述两种方案均可下载框架代码至模块中 - -#### api使用说明 - -参见api说明文档 - -### 举个栗子 - -#### 读取配置文件示例 - -```python -# 初始化全局的guard环境 -from usr.bin.guard import GuardContext -guard_context = GuardContext() -# 刷新容器并启动的所有服务 -guard_context.refresh() -# 读取配置文件示例 -config = guard_context.service_config["cloud"] # 读取/usr/etc/app_config/cloud/config.json 配置文件内容返回的字典对象 -``` - -网络服务和日志服务示例 - -```python -from usr.bin.guard import GuardContext - -def net(*args, **kwargs): - """网络回调函数""" - app_log.debug("net_callback,args:{},kwargs:{}".format(args, kwargs)) - -if __name__ == '__main__': - # 初始化全局容器 - guard_context = GuardContext() - guard_context.refresh() - # 获取网络服务&日志服务 - net_service = guard_context.get_server('net') - log_service = guard_context.get_server('log') - # 订阅网络服务 - net_service.subscribe(net) - - # 获取app_log - app_log = guard_context.get_logger("app_log") - app_log.debug("net status: {}".format(net_service.get_net_status())) -``` - -#### 第三方组件示例 - -GPIO读写拦截器 - -```python -from ql_interrupter import PinInterrupter -from machine import Pin, ExtInt -pin_inter = PinInterrupter(Pin.GPIO1, trige_mode=Pin.OUT, pull_mode=Pin.PULL_DISABLE, mode=0) # 初始化 -pin_inter.write(1) # 设置PIN脚电平输出为高 -``` - -看门狗示例 - -```python -from ql_interrupter import WatchDog -from machine import Pin, ExtInt -import sys_bus -# 初始化watch dog -wd = WatchDog(Pin.GPIO15, 1, 10000) -wd.start() -def topic1_cb(topic, msg): - pass -def topic2_cb(topic, msg): - pass -# 订阅喂狗后,硬件狗拉GPIO 告知模块喂狗成功 -sys_bus.subscribe("WDT_KICK_TOPIC", topic1_cb) -# 订阅喂狗后的回调 -sys_bus.subscribe("WDT_KICK_TOPIC_FEED", topic2_cb) -``` - -> 更多示例代码和演示参考API说明 - - - -## 进阶 - -### 概述 - -进阶篇,我们主要描述各个功能是如何使用的, 从入门篇我们已经帮环境搭好了, 从现在起, 我们将正式开始和使用上述功能, 并展示使用成果 - -### 使用文档 - -#### 容器 - -主要是包裹和集成所有服务和配置的集中化管理的东西, 下面演示起核心使用, 其他功能请查看API文档 - -##### 启动容器 - -- [OK] 为启动成功 -- [FAILED]为服务启动失败 -- sys 和 app 代表服务所属的分区,sys是系统级别的, app是用户级别的 -- 启动成功的服务我们就可以调用了, 我们也可以查看所有启动成功的服务 - -![QPYcom_GniGshUtTV](../media/app-framework/heliosServices/QPYcom_GniGshUtTV.png) - -##### 网络服务 - -###### 应用场景 - -- 当拨号状态异常或者从异常中恢复时, 会触发网络服务的主动通知 -- 当空口状态异常或从异常中恢复时, 会触发网络通知 - -###### 基本使用 - -- 我们可以获取到sim, net, dataCall对象无需导入 -- 我们可以订阅网络状态 - -![image-20210827100748287](../media/app-framework/heliosServices/image-20210827100748287.png) - -###### 输出订阅者的数据 - -当我们订阅网络服务的时候, 网络的波动我们会收到相关通知, 通知为我们订阅的函数 - -```python -from usr.bin.guard import GuardContext - -guard_context = GuardContext() - -guard_context.refresh() - -net_ser = guard_context.get_server('net') - -# 触发事件为用户网络异常, 网络重新上线, 服务异常 - -def func(*args, **kwargs) - """ - args: ('anonymous',)无需关注 - kwargs: - { - 'message': { - 'message_id': 2, # 消息id - 'sender': 'anonymous', # 发送人 - 'message': { - 'IPv6_DNS1': '::', # IPV6的DNS1 - 'IPv6_DNS2': '::', # IPV6的DNS2 - 'ip_type': 0, # 只需关注0是ipv4 * - 'sim_status': 1, # sim卡状态1是存在0是sim卡不存在 * - 'net_status': 0, # 空口网络状态, 良好 1 网络不成功失败0 * - 'datacall_status': 0, # 拨号状态, * - 'profile_id': 1, # PDP INDEX - 'IPv4': '0.0.0.0', # IPV4地址 - 'IPv4_DNS1': '0.0.0.0', # IPV4dns1 - 'IPv4_DNS2': '0.0.0.0', # IPV4dns2 - 'IPv6': '7f0:e6b8:8948:4069:7047:8849:20:4861' # IPV6地址 - }, - 'from_event': 'NET', # 从哪个事件来的 - 'msg_type': 1 # 消息类型 - } - } - 加*号的需要用户去关注 - """ - - print("args = {} kwargs = {}".format(args, kwargs)) -# 订阅网络服务 -net_ser.subscribe(func) -``` - -###### 检测联网 - -- 开启后可检查网络状态, 返回值请参考`HeliosService API文档` - -![image-20210827101129580](../media/app-framework/heliosServices/image-20210827101129580.png) - -###### 其他功能 - -请参考AP文档进行调用 - -##### 日志服务 - -###### 基本使用 - -- 获取一个带命名的日志客户端 -- 输出日志 -- 设置日志输出级别 - -![QPYcom_PAk2AjLkiN](../media/app-framework/heliosServices/QPYcom_PAk2AjLkiN.png) - -- 订阅日志 - -![QPYcom_AZHnngQa4K](../media/app-framework/heliosServices/QPYcom_AZHnngQa4K.png) - -###### 输出订阅者的数据 - -当我们订阅日志服务的时候我们, 当我们打印日志的时候会通知为我们订阅的函数 - -```python -from usr.bin.guard import GuardContext - -guard_context = GuardContext() - -guard_context.refresh() - -log_ser = guard_context.get_server('log') - -def func(*args, **kwargs) - """ - args: ('anonymous',)无需关注 - kwargs: - { - 'message': { - 'message_id': 4, # 消息id - 'sender': 'anonymous', # 消息发送人 - 'message': , # LogFormatUtil对象 - 'from_event': 'LOG', # LOG模块来的事件 - 'msg_type': 255 # 消息类型 - } - } - """ - """ - ## LogFormatUtil拥有以下属性, - 获取日志信息 - log_data = kwargs['message']['message'] # 查看log message数据 - log_data.time # 查看log time时间 - log_data.tag # 查看日志标签例如 NET... - log_data.level # 查看日志输出等级 - log_data.content # 查看日志的内容 - log_data.log_format # 获取我们的标准输出 - """ - print("args = {} kwargs = {}".format(args, kwargs)) - - - -# 订阅日志服务 -log_ser.subscribe(func) - -logger = guard_context.get_logger("test") - -logger.debug("this is a test msg") - -``` - - - -###### 其他功能 - -请参考API文档进行调用 - - - -##### 媒体服务 - -###### 基本使用 - -- 获取原生tts,audio -- 订阅消息 -- 设置模式 -- 设置pa `参考API文档` - -![QPYcom_BlN4sYhUOK](../media/app-framework/heliosServices/QPYcom_BlN4sYhUOK.png) - - - -###### 发布tts消息 - -tts播放媒体格式 - -> **media_ser.tts_play(priority=4, breakin=0, mode=2, play_data="")** - -- 参数 - -| 参数 | 参数类型 | 参数说明 | -| --------- | -------- | ------------------------------------------------------------ | -| priority | int | 播放优先级,支持优先级0~4,数值越大优先级越高 | -| breakin | int | 打断模式,0表示不允许被打断,1表示允许被打断 | -| mode | int | 编码模式,1 - UNICODE16(Size end conversion),2 - UTF-8,3 - UNICODE16(Don't convert) | -| play_data | string | 待播放字符串 | - -- 返回值 - - 无 - - - -###### 发布audio消息 - -音频文件播放,支持mp3、amr和wav格式文件播放。支持优先级0~4,数字越大优先级越高,每个优先级组可同时最多加入10个播放任务,与TTS播放共用同一个播放队列, - -会默认播报, - -订阅的消息也会同时收到,播报的事件和数据 - -> **media_ser.audio_play(priority=4, breakin=0, play_data="")** - -- 参数 - -| 参数 | 参数类型 | 参数说明 | -| --------- | -------- | --------------------------------------------- | -| priority | int | 播放优先级,支持优先级0~4,数值越大优先级越高 | -| breakin | int | 打断模式,0表示不允许被打断,1表示允许被打断 | -| play_data | string | 待播放的文件名称,包含文件存放路径 | - -返回值 - -- 无 - - - -###### 输出订阅者的数据 - -当订阅者想拿到发布的tts数据时,可以通过订阅媒体服务,会拿到以下相关参数, 当然我们系统已经有默认播放的动作了 - -```python -from usr.bin.guard import GuardContext - -guard_context = GuardContext() - -guard_context.refresh() - -media = guard_context.get_server('media') - -def func(*args, **kwargs): - """ - args: ('anonymous',)无需关注 - kwargs: - { - 'message': { - 'message_id': 2, # message_id消息id - 'sender': 'anonymous', # 发送人 - 'message': { - 'play_data': 'test', # 数据内容 - 'breakin': 0, # 是否可以被打断[注意audio播放无此属性, 只有tts播放时候才会有此属性] - 'mode': 2, # 播放模式 - 'priority': 4 # 优先级别 - }, - 'from_event': 'MEDIA', # 事件类型 - 'msg_type': 1 # 消息类型 1为tts消息 0为audio消息 - } - } - """ - print("args {} kwargs {}".format(args, kwargs)) -media.subscribe(func) -# 发布tts消息 media订阅者将收到此消息 -media.tts_play(play_data="test") -``` - - - -###### 其他功能 - -- ###### 请查看API文档 - -##### 低功耗服务 - -###### 基本使用 - -- 默认是开启低功耗模式 -- 存在投票机制 -- 查看偷拍不为0即无法进入休眠 -- 可以取消投票, 每一张投票都需要一次取消 - -![image-20210827131630704](../media/app-framework/heliosServices/image-20210827131630704.png) - -###### 其他功能 - -请查看API相关文档 - -##### 移远云服务 - -###### 基本使用 - -- 提供日志上传至移远云 -- OTA单文件升级 - -![image-20210827132925708](../media/app-framework/heliosServices/image-20210827132925708.png) - -创建cloud服务的config.json cloud服务是在app_config下面的 - -![image-20210827134613132](../media/app-framework/heliosServices/image-20210827134613132.png) - -cloud服务config.json的内容 - -![image-20210827135011406](../media/app-framework/heliosServices/image-20210827135011406.png) - -由于当时设置的level是3即使这里创建成功也是不能使用的, 所以我们需要, reload服务才会开启自动升级服务, 设计的初衷是让一些服务的启动等级变低, 客户去控制器启动顺序 - -![image-20210827134636763](../media/app-framework/heliosServices/image-20210827134636763.png) - -![image-20210827141310248](../media/app-framework/heliosServices/image-20210827141310248.png) - -- 提交日志 - -![image-20210827141503388](../media/app-framework/heliosServices/image-20210827141503388.png) - -###### 其他功能 - -请查看API文档 - - - -##### 异常服务 - -###### 基本使用 - -- 订阅异常服务 -- 发布异常 - -![image-20210827175123097](../media/app-framework/heliosServices/image-20210827175123097.png) - -###### 输出订阅者的数据 - -当我们订阅了异常服务, 输出给订阅者的数据 - -```python -from usr.bin.guard import GuardContext - -guard_context = GuardContext() - -guard_context.refresh() - -ex= guard_context.get_server('exception') - -def func(*args, **kwargs): - """ - args: ('anonymous',)无需关注 - kwargs: - { - 'message': { - 'message_id': 2, # message_id消息id - 'sender': 'anonymous', # 发送人 - 'message': 'this is a error msg', # 异常的消息 - 'from_event': 'EXCEPTION', # 事件类型 - 'msg_type': 255 # 消息类型无需关心 - } - } - """ - print("args {} kwargs {}".format(args, kwargs)) - -ex.subscribe(func) -# 发送异常信息 -ex.publish("this is a error msg") -``` - - - - - -#### 三方组件 - -主要有gpio, 中断, 定时器, 看门狗的封装, 还有安全组件, 断点续传的组件(网络挂掉的时候暂存到本地,恢复之后重新发)等 - -请查看相关API文档 - - - -#### sys_bus使用 - -- 订阅topic的函数 -- 发布数据 - -![image-20210827152347130](../media/app-framework/heliosServices/image-20210827152347130.png) - -#### 裁剪说明 - -- 圈出来的, 带_service下坠的都是可以裁剪的, 如果不想用此服务, 删除即可 - -![image-20210827175241263](../media/app-framework/heliosServices/image-20210827175241263.png) - -## 高级 - -### 概述 - -此篇主要说的是, helios services的软件设计的思考, 和后续将要支持的功能, 以及通过我们软件设计知道内部的通信原理, 了解和理解内部的机制等等... - - - -### 软件架构设计 - -#### 架构设计 - -![image-20210825131423890](../media/app-framework/heliosServices/image-20210825131423890.png) - -名词解释: - -- `Services`: 服务组件 -- `Config`: 配置存储 - - `app_config`: 存储用户配置文件的目录 - - `system_config`: 系统配置文件的目录 -- `monitos`: 监控器,监控各个服务的状态 -- `sys_bus`: 会话的处理总线主要处理, 生产者和消费者模型, 目前支持异步模式 -- `queue:` 普通队列, 用于线程间通信 -- `event_message`: 异步的带有命名空间的高级消息队列 - -- `GuardContext`: 上下文容器, 统一管理所有的服务和配置, 还有监控等组件, 提供服务, 配置, 监控等获取的接口 -- `third_party`: 三方组件目录, 拥有我们提供和封装的一些组件以便客户敏捷开发和二次开发 - -#### 设计原理 - -##### 服务设计原理 - -![image-20210825134134474](../media/app-framework/heliosServices/image-20210825134134474.png) - -1. 我们将写好的一些针对系统级别的提供了一些服务, 这些服务的发布者可能来自可以接受系统底层的回调, 和用户主动发布给不同的订阅者获取等 -2. 例如用户只需要订阅网络服务, 当网络信号异常或者网络信号不好时或者网络断开重连时, 网络服务会发起自动重连, 并且通知所有订阅了网络服务函数, 通知当前网络的状态相关信息, 订阅者即可收到相关信息进行处理 -3. 例如日志服务等是需要用户自己承当发布者的作用的 -4. 发布者和订阅者是毫无关系的, 发布者无需关注订阅者, 只用把发布的数据给用户即可 -5. 发布者发布到服务, 服务再到订阅者是异步的, 我们同时也提供了同步的配置和支持 -6. 服务的中间本质实现是一个, `带命名空间的高级的消息队列`+其他一些组件来组成的 - -优点: - -- 我们替客户实现好了一些系统级别的服务, 并支持后续迭代, 解决了客户客户还需要维护系统接别的代码问题, 只提供对外的API供客户调用 -- 降低了业务之间的耦合, 订阅者和发布者之间的解耦 -- 订阅者,只需要订阅服务, 或者使用服务, 既能使用我们维护并提供的功能 - -缺点: - -- 我们限定死了服务的类型, 客户无法的到定制, 比如我现在有个需求是, 想自己业务上有两个或者多个业务解耦, 客户无法实现自己的服务, 这些服务都是我们写好的, 解决方案`sys_bus` -- 首先我们是由一个高级消息队列组成, 所以有可能存在服务还在, 但是消息队列崩溃的现象, 所以我们在服务崩溃的时候给他拉起来, 解决方案`monitor` - - - -**示例:** - -订阅服务 - -![QPYcom_vOTKl9QLSz](../media/app-framework/heliosServices/QPYcom_vOTKl9QLSz.png) - -##### sys_bus实现原理 - -![image-20210826140649441](../media/app-framework/heliosServices/image-20210826140649441.png) - -上面服务是我们通过服务的形式来给用户体现的, 缺点中我们也明确了, 用户无法定制服务, 反之sys_bus即可实现用户定制服务的需求 - -- 异步的模式, 用户可以订阅和发布,还有解绑服务 -- 将发布和订阅分离, 模块化分离, 支持多个发布者, 和服务的区别是,这里发布者和订阅者都是用户去维护的 -- 用户只需要关注topic而不用关注sys_bus等本身和订阅者本身 -- 优点: - - 客户可以自己定制和发布相关的数据等 - - 我们帮客户维护了sys_bus的稳定性 -- 缺点: - - 过于灵活可能会导致, 用户的一些订阅代码需要用户自己去维护 - -示例: - -![QPYcom_NKUsHGzKV3](../media/app-framework/heliosServices/QPYcom_NKUsHGzKV3.png) - -##### monitor设计原理 - -- 负责监控每个服务在每隔15秒给服务一个心跳, 如果, 服务, 收到这个心跳包, 会将这个发给的指定的接收人的, monitor接收到心跳包, 即认为, 服务运行正常 -- 可以设置, 当服务几次运行失败后执行什么样的行为, 例如:设置当服务心跳连续多次失败后, 重启设备或者停止该服务等行为, [目前默认底层做了处理, 用户无需设置] - -**示例** - -![QPYcom_T38gWTvOPr](../media/app-framework/heliosServices/QPYcom_T38gWTvOPr.png) - -##### config设计原理 - -为了方便配置文件的读取和设置等, 将配置文件路径统一放到了config下面设置按照下列规则的config.json文件 - -![](../media/app-framework/heliosServices/image-20210827134613132.png) - -- app_config [用户配置文件夹] - - 下面对应的是用户配置的配置文件, 如上图所示, app_config下面存在 - - 例如:cloud文件夹, cloud文件夹下面存在一个config.json, 我们将映射文件自动读取文件给cloud服务装载它config.json中的内容, 当cloud服务不存在, 我们将自动保存配置文件和服务的映射关系, 通过`guard_context.service_config`获取服务和映射的config.json的内容 -- system_config [系统配置文件夹] - - 配置系统级别的服务, 我们会按照系统级别的配置文件作为提供对外, 我们会读取系统服务的配置文件, 当服务存在时, 我们可以自动装配, 服务不存在是我们会保存相关映射关系, 用户可以通过`guard_context.system_config`获取映射内容 - -**示例:** - -![RtP8rx7WtN](../media/app-framework/heliosServices/RtP8rx7WtN.png) - -- 我们可以看出上面app_config下面存在 - - lexin - - config.json - - abc - - config.json - - 我们可以看到读取的service_config下面 - - {'abc': {'a': 1}, 'lexin': {'a': 1}} - - abc和lexin就是我们的文件夹名字, 对应的value就是我们config.json中的内容 -- system_config也是通app_config的对应关系一致 -- 注意: 如若想读取必须按照此类调教key是文件夹名,value读取的是config.json中的内容书写 - -##### queue设计原理 - -普通队列用作消息通信, 创建队列后获取数据的时候会阻塞, 当有数据信号的时候将会被唤醒 - -![QPYcom_zcAf9dvHik](../media/app-framework/heliosServices/QPYcom_zcAf9dvHik.png) - -#### 三方组件和容器设计 - - - -##### GuardContext设计原理 - -上面说了那么多组件设计后, 我们说下, 我们说下容器设计 - -- 因为目前存在很多服务, 很多配置, 为了方便客户不用这导入一下那导入一下, 并且做统一化管理, 特此容器化了所有组件 - -- 用户只需要关注容器设计即 - -##### ThirdParty设计 - -请查看第三方组件API文档 - diff --git a/docs/Getting_started/zh/app-framework/sys_bus.md b/docs/Getting_started/zh/app-framework/sys_bus.md deleted file mode 100644 index 8abfb6dd5f421e4aec9c72a2ca75dff64318e06e..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/app-framework/sys_bus.md +++ /dev/null @@ -1,135 +0,0 @@ -# sys_bus - -sys_bus是一个基于MicroPython的事件总线模块,可以在MicroPython设备之间进行消息发布和订阅。它提供了订阅、发布、取消订阅等功能,并支持异步和同步发布。本文将介绍sys_bus的使用方法及其架构原理。 - - - -## 1. 导入sys_bus模块 - -使用sys_bus模块之前,需要将该模块安装到MicroPython设备中。可以将sys_bus.py文件上传到MicroPython设备中,并通过`import`命令导入该模块: - -```python -import sys_bus -``` - - - -## 2. sys_bus模块的基本用法 - -sys_bus模块提供了订阅、发布、取消订阅等功能,并支持异步和同步发布。 - - - -### 2.1 订阅 - -使用`subscribe()`函数订阅一个主题,可以接收该主题下的所有消息。 - -```python -def callback(topic, msg): - print('Received message:', msg) - -sys_bus.subscribe('topic1', callback) -``` - -在这个示例中,我们定义了一个名为callback的回调函数,并使用`subscribe()`函数订阅了名为`topic1`的主题。当`topic1`主题下有新消息时,callback函数会被自动调用。 - - - -### 2.2 发布 - -使用`publish()`函数发布一条消息,该消息将被发送到指定的主题下。 - -```python -sys_bus.publish('topic1', 'Hello, World!') -``` - -在这个示例中,我们使用`publish()`函数发布了一条消息`Hello, World!`,该消息将被发送到`topic1`主题下。 - - - -### 2.3 取消订阅 - -使用`unsubscribe()`函数取消订阅一个主题。 - -```python -sys_bus.unsubscribe('topic1', callback) -``` - -在这个示例中,我们使用`unsubscribe()`函数取消了对`topic1`主题的订阅。 - - - -### 2.4 异步发布 - -使用`publish_async()`函数异步发布一条消息,该消息将在新的线程中被发送到指定的主题下。 - -```python -sys_bus.publish_async('topic1', 'Hello, World!') -``` - -在这个示例中,我们使用`publish_async()`函数异步发布了一条消息`Hello, World!`,该消息将在新的线程中被发送到`topic1`主题下。 - - - -### 2.5 同步发布 - -使用`publish_sync()`函数同步发布一条消息,该消息将在当前线程中被发送到指定的主题下。 - -```python -sys_bus.publish_sync('topic1', 'Hello, World!') -``` - -在这个示例中,我们使用`publish_sync()`函数同步发布了一条消息`Hello, World!`,该消息将在当前线程中被发送到`topic1`主题下。 - - - -## 3. 完整示例 - -下面是一个使用sys_bus模块的完整示例,它实现了一个简单的消息发布和订阅系统。该系统由一个 - -Publisher和Subscriber两个类组成,Publisher用于发布消息,Subscriber用于订阅消息。 - -```python -import sys_bus - -class Publisher: - def __init__(self, topic): - self.topic = topic - - def publish(self, msg): - sys_bus.publish(self.topic, msg) - -class Subscriber: - def __init__(self, topic, callback): - self.topic = topic - self.callback = callback - sys_bus.subscribe(self.topic, self.callback) - - def unsubscribe(self): - sys_bus.unsubscribe(self.topic, self.callback) - - -def callback(topic, msg): - print('Received message:', msg) - -p = Publisher('topic1') -s = Subscriber('topic1', callback) - -p.publish('Hello, World!') - -s.unsubscribe() - -``` - - - -在这个示例中,我们定义了一个名为Publisher的类,用于发布消息。它的构造函数需要一个主题作为参数,用于指定发布的主题。`publish()`方法用于发布一条消息到指定主题。 - -我们还定义了一个名为Subscriber的类,用于订阅消息。它的构造函数需要两个参数,一个是主题,一个是回调函数。回调函数用于处理接收到的消息。`unsubscribe()`方法用于取消订阅。 - -我们还定义了一个名为callback的回调函数,并创建了一个Publisher和一个Subscriber对象。在Publisher对象中,我们使用`publish()`函数发布了一条消息`Hello, World!`,该消息将被发送到`topic1`主题下。在Subscriber对象中,我们使用`unsubscribe()`函数取消了对`topic1`主题的订阅。 - -通过这个简单的示例,我们可以看到sys_bus模块是如何实现消息发布和订阅的。我们可以使用Publisher对象发布消息,并使用Subscriber对象订阅消息,以实现设备之间的通信。同时,sys_bus模块还提供了异步和同步发布的功能,以满足不同应用场景的需求。 - - - diff --git a/docs/Getting_started/zh/appendix/error-code.md b/docs/Getting_started/zh/appendix/error-code.md deleted file mode 100644 index 2f47cd38a41f329c3b0ff9e2103037414ab05395..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/appendix/error-code.md +++ /dev/null @@ -1,115 +0,0 @@ -# QuecPython错误码汇总 - -**QuecPython中定义的各种错误代码常量** - -| 错误码常量 | 错误码 | 释义 | -| ----------------------- | ------ | ----------------------------------------------- | -| QUEC_PY_FAIL | -1 | Generic failure codes | -| QUEC_PY_OK | 0 | Quec_py value indicating success (no error) | -| QUEC_PY_EPERM | 1 | Operation not permitted | -| QUEC_PY_ENOENT | 2 | No such file or directory | -| QUEC_PY_ESRCH | 3 | No such process | -| QUEC_PY_EINTR | 4 | Interrupted system call | -| QUEC_PY_EIO | 5 | I/O error | -| QUEC_PY_ENXIO | 6 | No such device or address | -| QUEC_PY_E2BIG | 7 | Argument list too long | -| QUEC_PY_ENOEXEC | 8 | Exec format error | -| QUEC_PY_EBADF | 9 | Bad file number | -| QUEC_PY_ECHILD | 10 | No child processes | -| QUEC_PY_EAGAIN | 11 | Try again | -| QUEC_PY_ENOMEM | 12 | Out of memory | -| QUEC_PY_EACCES | 13 | Permission denied | -| QUEC_PY_EFAULT | 14 | Bad address | -| QUEC_PY_ENOTBLK | 15 | Block device required | -| QUEC_PY_EBUSY | 16 | Device or resource busy | -| QUEC_PY_EEXIST | 17 | File exists | -| QUEC_PY_EXDEV | 18 | Cross-device link | -| QUEC_PY_ENODEV | 19 | No such device | -| QUEC_PY_ENOTDIR | 20 | Not a directory | -| QUEC_PY_EISDIR | 21 | Is a directory | -| QUEC_PY_EINVAL | 22 | Invalid argument | -| QUEC_PY_ENFILE | 23 | File table overflow | -| QUEC_PY_EMFILE | 24 | Too many open files | -| QUEC_PY_ENOTTY | 25 | Not a typewriter | -| QUEC_PY_ETXTBSY | 26 | Text file busy | -| QUEC_PY_EFBIG | 27 | File too large | -| QUEC_PY_ENOSPC | 28 | No space left on device | -| QUEC_PY_ESPIPE | 29 | Illegal seek | -| QUEC_PY_EROFS | 30 | Read-only file system | -| QUEC_PY_EMLINK | 31 | Too many links | -| QUEC_PY_EPIPE | 32 | Broken pipe | -| QUEC_PY_EDOM | 33 | Math argument out of domain of func | -| QUEC_PY_ERANGE | 34 | Math result not representable | -| QUEC_PY_EDEADLK | 35 | Resource deadlock would occur | -| QUEC_PY_ENAMETOOLONG | 36 | File name too long | -| QUEC_PY_ENOLCK | 37 | No record locks available | -| QUEC_PY_ENOSYS | 38 | Function not implemented | -| QUEC_PY_ENOTEMPTY | 39 | Directory not empty | -| QUEC_PY_ELOOP | 40 | Too many symbolic links encountered | -| QUEC_PY_EWOULDBLOCK | 41 | Operation would block | -| QUEC_PY_ENOMSG | 42 | No message of desired type | -| QUEC_PY_EIDRM | 43 | Identifier removed | -| QUEC_PY_ECHRNG | 44 | Channel number out of range | -| QUEC_PY_EL2NSYNC | 45 | Level 2 not synchronized | -| QUEC_PY_EL3HLT | 46 | Level 3 halted | -| QUEC_PY_EL3RST | 47 | Level 3 reset | -| QUEC_PY_ELNRNG | 48 | Link number out of range | -| QUEC_PY_EUNATCH | 49 | Protocol driver not attached | -| QUEC_PY_ENOCSI | 50 | No CSI structure available | -| QUEC_PY_EL2HLT | 51 | Level 2 halted | -| QUEC_PY_EBADE | 52 | Invalid exchange | -| QUEC_PY_EBADR | 53 | Invalid request descriptor | -| QUEC_PY_EXFULL | 54 | Exchange full | -| QUEC_PY_ENOANO | 55 | No anode | -| QUEC_PY_EBADRQC | 56 | Invalid request code | -| QUEC_PY_EBADSLT | 57 | Invalid slot | -| QUEC_PY_EDEADLOCK | 58 | Deadlock | -| QUEC_PY_EBFONT | 59 | Bad font file format | -| QUEC_PY_ENOSTR | 60 | Device not a stream | -| QUEC_PY_ENODATA | 61 | No data available | -| QUEC_PY_ETIME | 62 | Timer expired | -| QUEC_PY_ENOSR | 63 | Out of streams resources | -| QUEC_PY_ENONET | 64 | Machine is not on the network | -| QUEC_PY_ENOPKG | 65 | Package not installed | -| QUEC_PY_EREMOTE | 66 | Object is remote | -| QUEC_PY_ENOLINK | 67 | Link has been severed | -| QUEC_PY_EADV | 68 | Advertise error | -| QUEC_PY_ESRMNT | 69 | Srmount error | -| QUEC_PY_ECOMM | 70 | Communication error on send | -| QUEC_PY_EPROTO | 71 | Protocol error | -| QUEC_PY_EMULTIHOP | 72 | Multihop attempted | -| QUEC_PY_EDOTDOT | 73 | RFS specific error | -| QUEC_PY_EBADMSG | 74 | Not a data message | -| QUEC_PY_EOVERFLOW | 75 | Value too large for defined data type | -| QUEC_PY_ENOTUNIQ | 76 | Name not unique on network | -| QUEC_PY_EBADFD | 77 | File descriptor in bad state | -| QUEC_PY_EREMCHG | 78 | Remote address changed | -| QUEC_PY_ELIBACC | 79 | Can not access a needed shared library | -| QUEC_PY_ELIBBAD | 80 | Accessing a corrupted shared library | -| QUEC_PY_ELIBSCN | 81 | .lib section in a.out corrupted | -| QUEC_PY_ELIBMAX | 82 | Attempting to link in too many shared libraries | -| QUEC_PY_ELIBEXEC | 83 | Cannot exec a shared library directly | -| QUEC_PY_EILSEQ | 84 | Illegal byte sequence | -| QUEC_PY_ERESTART | 85 | Interrupted system call should be restarted | -| QUEC_PY_ESTRPIPE | 86 | Streams pipe error | -| QUEC_PY_EUSERS | 87 | Too many users | -| QUEC_PY_ENOTSOCK | 88 | Socket operation on non-socket | -| QUEC_PY_EDESTADDRREQ | 89 | Destination address required | -| QUEC_PY_EMSGSIZE | 90 | Message too long | -| QUEC_PY_EPROTOTYPE | 91 | Protocol wrong type for socket | -| QUEC_PY_ENOPROTOOPT | 92 | Protocol not available | -| QUEC_PY_EPROTONOSUPPORT | 93 | Protocol not supported | -| QUEC_PY_ESOCKTNOSUPPORT | 94 | Socket type not supported | -| QUEC_PY_EOPNOTSUPP | 95 | Operation not supported on transport endpoint | -| QUEC_PY_EAFNOSUPPORT | 97 | Address family not supported by protocol | -| QUEC_PY_EADDRINUSE | 98 | Address already in use | -| QUEC_PY_ECONNABORTED | 103 | Software caused connection abort | -| QUEC_PY_ECONNRESET | 104 | Connection reset by peer | -| QUEC_PY_ENOBUFS | 105 | No buffer space available | -| QUEC_PY_EISCONN | 106 | Transport endpoint is already connected | -| QUEC_PY_ENOTCONN | 107 | Transport endpoint is not connected | -| QUEC_PY_ETIMEDOUT | 110 | Connection timed out | -| QUEC_PY_ECONNREFUSED | 111 | Connection refused | -| QUEC_PY_EHOSTUNREACH | 113 | No route to host | -| QUEC_PY_EALREADY | 114 | Operation already in progress | -| QUEC_PY_EINPROGRESS | 115 | Operation now in progress | \ No newline at end of file diff --git a/docs/Getting_started/zh/appendix/exception-handling.md b/docs/Getting_started/zh/appendix/exception-handling.md deleted file mode 100644 index a03a14073025d95304c9dffe049540488cf4f052..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/appendix/exception-handling.md +++ /dev/null @@ -1,73 +0,0 @@ -# QuecPython异常处理流程 - -## 模组网络异常处理 - -使用QuecPython开发时,如遇到网络异常情况产生,如MQTT连接异常,request请求失败等 - -可以先检查SIM状态是否正常 - -```python -import sim -sim.getStatus() # 返回1为SIM卡状态正常,具体状态枚举值参考API手册 -``` - -如果SIM状态异常,请检查SIM卡是否插入或者状态是否正常,如果SIM卡插入但是未检测到,在模块可以正常开机并且可以正常串口通信的前提下,建议尝试以下排查操作: - -1. 检查 SIM 卡有没有插反,市面上 SIM 卡座种类繁多,部分 SIM 卡座没有防呆标识,正反插都可以放置在卡座内。 -2. 更换 SIM 卡,SIM 卡可能长期插拔使用过程中损坏。 -3. 按压 SIM 卡座,防止 SIM 卡座弹片和 SIM 卡接触不良。 -4. 把 SIM 卡电路部分电容和 ESD 器件全部去掉,防止焊接电容容值不对和 ESD 器件焊接导致模组 SIM 卡不识别,这两种类型器件模组识卡是不影响。如果器件是手动焊接的,尤其关注这两点。 -5. 尤其注意部分客户是手动焊接,检查 SIM 卡座各个引脚焊接是否存在短路问题。 -6. 检查 SIM 卡座封装设计,确保封装正确。 -7. 发送 AT 指令:AT+QSIMDET=0,0 关闭 SIM 卡热插拔功能 ,模块重新开机尝试能否识别卡。主要由于 SIM 卡座结构可能与开启的热插拔识别电平不一致导致无法识卡。 -8. 如以上方法无法解决SIM异常问题,可联系技术支持人员寻求技术支持 - -如果SIM卡状态正常,可以使用模组提供的 **checkNet - 网络检测** 库来检测网络状态 - -``` -import checkNet -checkNet.waitNetworkReady(timeout) -``` - -等待模组网络就绪。该方法会依次检测SIM卡状态、模组网络注册状态和PDP Context激活状态;在设定的超时时间之内,如果检测到PDP Context激活成功,会立即返回,否则直到超时才会退出。 - -如果超时时间内一直无法成功注册网络,可检查一下SIM卡是否有欠费停机、模组天线是否插好、查询信号值等来确认原因。 - -## python代码报错处理流程 - -开发中遇到代码运行报错可根据代码报错类型来出处理,比如 - -``` -ImportError: no module named "***" -``` - -ImportError为导入错误,一般是导入的库或者库文件不存在,根据后面错误提示内容也有可能是库文件代码中有语法错误等,根据错误提示信息处理即可 - -类如(举例常见的几种) - -- TypeError:类型错误,**对象用来表示值的类型非预期类型时发生的错误** -- AttributeError:属性错误,**特性引用和赋值失败时会引发属性错误** -- NameError:**试图访问的变量名不存在** -- SyntaxError: invalid syntax,语法错,**错误使用标点符号** -- KeyError:**在读取字典中的key和value时,如果key不存在,就会触发KeyError错误** -- IndexError: list index out of range, **索引错误,列表索引超出了范围** -- IndentationError: expected an indented block,**代码缩进错误** - -其他错误码参考标准python处理即可 - -如有错误码可参考 [QuecPython错误码汇总](error-code.md) - -## 模组异常重启处理 - -代码运行过程中如遇到设备异常重启,可以通过API接口查询异常关机原因 - -``` -from misc import Power -Power.powerDownReason() -``` - - - - - - diff --git a/docs/Getting_started/zh/background/selection-guide.md b/docs/Getting_started/zh/background/selection-guide.md deleted file mode 100644 index dc44d675c03a83656f99b23a27156c133da377c7..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/background/selection-guide.md +++ /dev/null @@ -1,73 +0,0 @@ -# 硬件选型 - -## 硬件支持 - -目前重点支持: - -ECx00U:U代表展锐8910平台 - -ECx00M:M代表ASR1606平台 - -ECx00N:N代表ASR1603平台 - -ECx00A:S代表ASR1803S平台 - -ECx00E:E代表移芯EC618平台 - -BG95/BG77:高通MDM9x05平台 - -## 详细信息 - -| 型号 | 网络制式 | 频段 | CPU | Flash size (Kbytes) | RAM size (Kbytes) | GPIO | ExtInt | Timer | RTC | WDT | ADC | UART | I2C | SPI | SD | LCD | LVGL | Camera | Volte | POC | WIFI | BLE | FOTA | Supply voltage (V) | Maximum operating temperature range (°C) | -| --------- | -------------------------------------------------------- | ------------------------------------------------------------ | ------ | ----------------------- | ----------------- | ---- | ------ | ----- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ------ | ----- | ---- | ---- | ---- | ---- | ------------------ | ---------------------------------------- | -| BC25 PA | 全网通,移动,联通,电信 | LTE-FDD: B3/B5/B8 | | free:≈0.105M | free:≈450K | 16 | 17 | 4 | √ | × | 1 | 2 | 2 | 1 | * | × | * | * | * | * | × | * | √ | 2.1-3.6V | -40 to +85°C | -| EC800N CN | 全网通,移动4G,联通4G,电信4G | LTE-FDD: B1/B3/B5/B8
LTE-TDD: B34/B38/B39/B40/B41
| 624MHz | total:8M free:≈0.5M | free:≈450K | 37 | 38 | 4 | √ | √ | 4 | 3 | 1 | 2 | √ | √ | √ | √ | * | * | √ | × | √ | 3.4-4.5V | -35 to +85°C | -| EC200U CN | 全网通,移动4G,联通4G,电信4G | LTE-FDD: B1/B3/B5/B8
LTE-TDD: B34/B38/B39/B40/B41
| 624MHz | total:8M free:≈0.3M | free:≈450K | 17 | 17 | 4 | √ | √ | 3 | 2 | 2 | 2 | * | √ | √ | √ | * | √ | √ | √ | √ | 3.4-4.3V | -40 to +105°C | -| EC200U EU | 全网通,移动4G,联通4G,电信4G,移动2G,联通2G,电信2G, | LTE-FDD: B1/B3/B5/B8
LTE-TDD: B34/B38/B39/B40/B41
GSM: 900/1800 MHz | 624MHz | total:8M free:≈0.3M | free:≈450K | 17 | 17 | 4 | √ | √ | 3 | 2 | 2 | 2 | * | √ | √ | √ | * | √ | √ | √ | √ | 3.4-4.3V | -40 to +105°C | -| EC600U CN | 全网通,移动4G,联通4G,电信4G | FDD B1/B3/B5/B8
TDD B34/B38/B39/B40/B41 | 624MHz | total:8M free:≈0.3M | free:≈450K | 16 | 16 | 4 | √ | √ | 4 | 2 | 2 | 2 | * | √ | √ | √ | * | √ | √ | √ | √ | 3.4-4.3V | -40 to +105°C | -| EC600U EU | 全网通,移动4G,联通4G,电信4G | FDD B1/B3/B5/B7/B8/B20/B28
TDD B38/B40/B41
GSM B2/B3/B5/B8 | 624MHz | total:8M free:≈0.3M | free:≈450K | 16 | 16 | 4 | √ | √ | 4 | 2 | 2 | 2 | * | √ | √ | √ | √ | √ | √ | √ | √ | 3.4-4.3V | -40 to +105°C | -| EC600N CN | 全网通,移动4G,联通4G,电信4G | FDD B1/B3/B5/B8
TDD B34/B38/B39/B40/B41 | 624MHz | total:16M free:≈1M | free:≈450K | 29 | 29 | 4 | √ | √ | 1 | 3 | 1 | 2 | √ | √ | √ | √ | √ | * | √ | × | √ | 3.4-4.3V | -40 to +105°C | -| EC600S CN | 全网通,移动4G,联通4G,电信4G | FDD B1/B3/B5/B8
TDD B34/B38/B39/B40/B41 | 624MHz | total:16M free:≈1M | free:≈450K | 29 | 29 | 4 | √ | √ | 1 | 3 | 1 | 2 | × | √ | × | √ | √ | × | √ | × | √ | 3.4-4.3V | -40 to +105°C | - -标*号表示:正在开发中 - -标√号表示:支持 - -其他:不支持 - - - -**支持常用的传感器、执行单元和输入设备** - -- 传感器: - - 照度传感器(BH1750、OPT3001、GL5516、GL5528) - - 三轴加速度传感器(ADXL346、BMA250、LIS2DH12TR) - - 温湿度传感器(HDC1080、HDC2080、AHT10、DHT11) - - 可燃气体传感器 - - CO2气体传感器 - - GNSS定位模块 - - ... - -- 执行单元: - - - 功放 - - 电机 - - LED - - LCD(ILI9225、ST7735、ST7789、SSD1306、UC1628) - - ... - -- 输入设备: - - - 麦克风 - - 摄像头(GC032A、BF3901) - - 矩阵键盘 - - ... - - - -## 模组封装尺寸 - -![Hardware_Support_01](../media/background/selection-guide/Hardware_Support_01.png) - - - diff --git a/docs/Getting_started/zh/clouds/README.md b/docs/Getting_started/zh/clouds/README.md deleted file mode 100644 index 82942f77518730689a1b6b08958aa7052554da4b..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/clouds/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# 7. 云平台功能 - -本章主要介绍当前使用较多的几个物联网云平台,内容包含平台介绍,官方操作文档地址,如何在物联网平台上创建自己的产品,配置产品信息和设备管理等功能介绍。指导使用QuecPython进行开发,完成设备快速上云,且模拟设备与云平台之间的数据通讯,连接过程中遇到的常见问题如何解决,一些注意事项。 - - -- [7.1 移远云](./quectel.md) -- [7.2 阿里云](./aliyun.md) -- [7.3 腾讯云](./tencent.md) -- [7.4 华为云](./huawei.md) -- [7.5 亚马逊云](./aws.md) -- [7.6 移动云](./onenet.md) -- [7.7 电信云](./ctyun.md) -- [7.8 微软云](./azure.md) - -  - diff --git a/docs/Getting_started/zh/clouds/aliyun.md b/docs/Getting_started/zh/clouds/aliyun.md deleted file mode 100644 index 1bdf332b6e2dd63b73463806b94e625e586afcc2..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/clouds/aliyun.md +++ /dev/null @@ -1,240 +0,0 @@ -# QuecPython接入阿里云操作文档 - -## 简介 - -基于MQTT协议连接到阿里云物联网平台,设备快速连云,支持“一机一密和“一型一密”两种认证方式。 - -## 应用场景说明 - -通过阿里云物联网平台对同一产品下的设备进行管理,处理设备事件,完成消息转发,OTA升级等应用功能。 - -## 功能应用流程 - -### 阿里云物联网平台 - -详细文档请查看:https://help.aliyun.com/document_detail/145493.html - -#### 名称解释 - -连接三元组:三元组指的是ProductKey(产品标识)DeviceName(设备名)DeviceSecret(设备密钥),是设备与物联网平台建立连接时的认证信息。 - -一机一密:每个设备烧录其唯一的设备证书(ProductKey、DeviceName 和 DeviceSecret),当设备与物联网平台建立连接时,物联网平台对其携带的设备证书信息进行认证。 - -一型一密:同一产品下所有设备可以烧录相同产品证书(即 ProductKey 和 ProductSecret ),设备发送激活请求时,物联网平台进行产品身份确认,认证通过,下发该设备对应的 DeviceSecret - -#### 平台地址 - -- 阿里云:https://www.aliyun.com - - ![aliyun_1](../media/coulds/aliyun/aliyun_1.png) - - 注册个人或者企业账号进行账号登录 - -#### 创建实例 - -- 进入物联网平台 - - ![aliyun_2.png](../media/coulds/aliyun/aliyun_3.png) - -- 选择实例,根据需求选择,试用选择公共实例即可 - - ![image-20230322101737405](../media/coulds/aliyun/aliyun_2.png) - -- 实例概览 - - ![image-20230322102046889](../media/coulds/aliyun/aliyun_4.png) - -#### 创建产品 - -- 点击实例进行产品创建 - - ![image-20230322102235862](../media/coulds/aliyun/aliyun_5.png) - -- 按需填写产品创建信息 - - ![image-20230322102434008](../media/coulds/aliyun/aliyun_6.png) - -- 产品列表展示 - - ![image-20230322102516271](../media/coulds/aliyun/aliyun_7.png) - -#### 查看产品信息 - -- 产品信息包含ProductKey 和 ProductSecret - - ![image-20230322102731999](../media/coulds/aliyun/aliyun_14.png) - -#### 创建设备 - -- 选择产品进行设备创建 - - ![image-20230322102731999](../media/coulds/aliyun/aliyun_8.png) - -- 添加设备 - - ![image-20230322102805164](../media/coulds/aliyun/aliyun_9.png) - -- 手动编辑设备信息 - - ![image-20230322102932866](../media/coulds/aliyun/aliyun_10.png) - -- 设备创建完成后处于未激活状态 - - ![image-20230322103049475](../media/coulds/aliyun/aliyun_11.png) - -#### 查看设备信息 - -- 查看设备信息,设备信息包含ProductKey(产品标识)DeviceName(设备名)DeviceSecret(设备密钥) - - ![image-20230322103634903](../media/coulds/aliyun/aliyun_13.png) - -### QuecPython连接阿里云 - -QuecPython 官网地址:https://python.quectel.com - -#### 开发环境搭建 - -- 驱动安装 - - 驱动下载地址:https://python.quectel.com/download - - 选择对应平台的USB驱动进行安装 - - ![image-20230322110809659](../media/coulds/aliyun/aliyun_15.png) - -- QPYcom 图形化工具下载 - - 应用调试基于此工具,下载地址:https://python.quectel.com/download - - ![image-20230322111017197](../media/coulds/aliyun/aliyun_16.png) - -- 模组固件下载 - - 根据所用的模组型号选择固件下载后烧录,此文档调试选择EC600N CNLE进行演示。 - - ![image-20230322111312207](../media/coulds/aliyun/aliyun_17.png) - -#### 设备调试 - -- 打开电脑设备管理器,查看端口 - - ![image-20230322114434665](../media/coulds/aliyun/aliyun_18.png) - -- 选择 Quectel USB MI05 COM Port串口,使用QPYcom工具打开该串口 - - ![image-20230322114711697](../media/coulds/aliyun/aliyun_19.png) - -- 查询SIM卡状态和拨号状态 - - API 使用以及说明查阅Wiki文档:https://python.quectel.com/doc/API_reference/zh - - ```python - >>> import sim - >>> sim.getStatus() # 返回1表示SIM状态正常 - 1 - >>> import dataCall - >>> dataCall.getInfo(1,2) # 成功返回拨号信息 - (1, 0, [1, 0, '10.145.246.10', '211.138.180.2', '211.138.180.3'], [1, 0, '::', '::', '::']) - >>> - ``` - -#### 设备连云 - -阿里云API 使用以及说明查阅Wiki文档:https://python.quectel.com/doc/API_reference/zh - -- 导入阿里云API - - ```python - >>> from aLiYun import aLiYun - ``` - -- 创建aliyun连接对象 - - ```python - >>> from aLiYun import aLiYun - - >>> productKey = "a1YoiRfLSbV" - >>> DeviceName = "QuecPyhon_Dev" - >>> DeviceSecret = "a86aadbbe78de7b8248adfc09e458bb9" - >>> productSecre = None - >>> - >>> ali_obj = aLiYun(productKey, productSecre, DeviceName, DeviceSecret) - ``` - -- 注册事件回调函数 - - ```python - >>> def event_callback(topic, data): - ... print("aliyun callback recv: {}".format(data)) - ... - ... - ... - >>> ali_obj.setCallback(event_callback) - ``` - -- 设置连接参数并连接平台,state为0时表示连接成功,连接成功后调用start方法 - - ```python - >>> clientID = "Quecpython" - >>> clean_session = False - >>> keepAlive = 300 - >>> reconn = True - - >>> state = ali_obj.setMqtt(clientID, clean_session=False, keepAlive=300,reconn=True) - >>> state - 0 - >>> ali_obj.start() - ``` - - 云端查看设备状态,由创建时未激活状态变成在线状态 - - ![image-20230322133745959](../media/coulds/aliyun/aliyun_20.png) - -#### 订阅Topic - -- 根据平台订阅规则选择操作权限为订阅的Topic - - /a1YoiRfLSbV/${deviceName}/user/get:$ {deviceName}替换成我们的设备名称即可,例如"/a1YoiRfLSbV/QuecPyhon_Dev/user/get" - - ![image-20230322134239010](../media/coulds/aliyun/aliyun_21.png) - - ```python - >>> ali_obj.subscribe("/a1YoiRfLSbV/QuecPyhon_Dev/user/get") - 0 - >>> - ``` - -#### 数据上行 - -- 发布主题消息到平台,Topic选择操作权限为发布的进行数据上报 - - ```python - >>> ali_obj.publish("/a1YoiRfLSbV/QuecPyhon_Dev/user/update", "Hello, Aliyun!") - True - >>> - ``` - - 云端查看设备上行消息 - - ![image-20230322135259133](../media/coulds/aliyun/aliyun_23.png) - - ![image-20230322135342205](../media/coulds/aliyun/aliyun_24.png) - -#### 数据下行 - -- 找到设备订阅的Topic,选择发布消息 - - ![image-20230322135552300](../media/coulds/aliyun/aliyun_25.png) - - ![image-20230322135630333](../media/coulds/aliyun/aliyun_26.png) - - 设备查看平台下行数据,通过我们注册的回调函数接收数据 - - ![image-20230322135957158](../media/coulds/aliyun/aliyun_27.png) - -## 注意事项 - -- 设备进行云连接时需确认网络状态,例如SIM卡是否能够注网,设备是否拨号成功 -- 确保所用模组包含阿里云连接API可供使用 -- Topic注意操作权限 - diff --git a/docs/Getting_started/zh/clouds/aws.md b/docs/Getting_started/zh/clouds/aws.md deleted file mode 100644 index 4052c0dbc7346b6e22adc6d3cd0f35fc943dc076..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/clouds/aws.md +++ /dev/null @@ -1,203 +0,0 @@ -# 亚马逊云应用指导文档 - -## 简介 - -基于MQTT协议连接到亚马逊云物联网平台,设备快速连云,支持“一机一密和“一型一密”两种认证方式。 - -## 应用场景说明 - -通过亚马逊物联网平台对同一产品下的设备进行管理,处理设备事件,完成消息转发,OTA升级等应用功能。 - -## 功能应用流程 - -### 亚马逊云物联网平台 - -详细文档请查看:https://docs.aws.amazon.com/iot/latest/developerguide/mqtt.html - -#### 名称解释 - -连接三元组:三元组指的是ProductKey(产品标识)DeviceName(设备名)DeviceSecret(设备密钥),是设备与物联网平台建立连接时的认证信息。 - -一机一密:每个设备烧录其唯一的设备证书(ProductKey、DeviceName 和 DeviceSecret),当设备与物联网平台建立连接时,物联网平台对其携带的设备证书信息进行认证。 - -一型一密:同一产品下所有设备可以烧录相同产品证书(即 ProductKey 和 ProductSecret ),设备发送激活请求时,物联网平台进行产品身份确认,认证通过,下发该设备对应的 DeviceSecret - -#### 平台地址 - -- 亚马逊云:https://aws.amazon.com/cn - - ![image-20230324112751334](../media/coulds/aws/aws_1.png) - - 登录亚马逊云的物联网IoT Core(如果没有开通相关功能请自行开通)。 - - ![image-20230324115441486](../media/coulds/aws/aws_2.png) - -#### 创建策略 - -- 进入物联网平台 - - ![image-20230324133323449](../media/coulds/aws/aws_3.png) - - ![image-20230324140038240](../media/coulds/aws/aws_4.png) - -- 查看策略列表 - - ![image-20230324140354230](../media/coulds/aws/aws_5.png) - -#### 创建物品 - -- ioT 物品是云中的物理设备的表示形式 - - ![image-20230324140620857](../media/coulds/aws/aws_6.png) - -- 按需填写产品创建信息,选择自动生新证书 - - ![image-20230324140755797](../media/coulds/aws/aws_7.png) - - ![image-20230324141445827](../media/coulds/aws/aws_8.png) - -- 选择策略后下载设备证书,完成“创建物品,用证书连接AWS IoT平台,同一个证书可以用于多个设备,不同设备的Client ID保持唯一 - - ![image-20230324142502553](../media/coulds/aws/aws_9.png) - - ![image-20230324142417642](../media/coulds/aws/aws_10.png) - -#### 查看终端地址 - -- 亚马逊云服务地址,用于mqtt客户端连接,端口固定8883 - - ![image-20230324150030882](../media/coulds/aws/aws_11.png) - -### QuecPython连接亚马逊云 - -QuecPython 官网地址:https://python.quectel.com - -#### 开发环境搭建 - -- 驱动安装 - - 驱动下载地址:https://python.quectel.com/download - - 选择对应平台的USB驱动进行安装 - - ![image-20230322110809659](../media/coulds/aws/aws_12.png) - -- QPYcom 图形化工具下载 - - 应用调试基于此工具,下载地址:https://python.quectel.com/download - - ![image-20230322111017197](../media/coulds/aws/aws_13.png) - -- 模组固件下载 - - 根据所用的模组型号选择固件下载后烧录,此文档调试选择EC600N CNLE进行演示。 - - ![image-20230322111312207](../media/coulds/aws/aws_14.png) - -#### 设备调试 - -- 打开电脑设备管理器,查看端口 - - ![image-20230322114434665](../media/coulds/aws/aws_15.png) - -- 选择 Quectel USB MI05 COM Port串口,使用QPYcom工具打开该串口 - - ![image-20230322114711697](../media/coulds/aws/aws_16.png) - -- 查询SIM卡状态和拨号状态 - - API 使用以及说明查阅Wiki文档:https://python.quectel.com/doc/API_reference/zh - - ```python - >>> import sim - >>> sim.getStatus() # 返回1表示SIM状态正常 - 1 - >>> import dataCall - >>> dataCall.getInfo(1,2) # 成功返回拨号信息 - (1, 0, [1, 0, '10.145.246.10', '211.138.180.2', '211.138.180.3'], [1, 0, '::', '::', '::']) - >>> - ``` - -#### 设备连云 - -MQTT API 使用以及说明查阅Wiki文档:https://python.quectel.com/doc/API_reference/zh - -- 导入MQTT API - - ```python - >>> from umqtt import MQTTClient - ``` - -- 创建MQTT连接对象 - - ```python - >>> from umqtt import MQTTClient - - >>> CLIENT_ID = 'Quecpython_dev' - >>> SERVER = 'ambzd54j67b7h-ats.iot.us-west-2.amazonaws.com' - >>> PORT = 8883 - >>> USER = None - >>> PASSWORD = None - >>> certificate_content = "" # 数字证书 (certificate.crt) - >>> private_content = "" # 私钥 (private.pem) - >>> mqtt_obj = MQTTClient(client_id=CLIENT_ID, server=SERVER, port=PORT,user=USER,password=PASSWORD,keepalive=30,ssl=True,ssl_params={"cert": certificate_content,"key": private_content}) - ``` - -- 注册事件回调函数 - - ```python - >>> def event_callback(topic, data): - ... print("Subscribe Recv: Topic={},Msg={}".format(topic.decode(),msg.decode())) - ... - ... - ... - >>> mqtt_obj.set_callback(event_callback) - ``` - -- 发起连接 - - ```python - >>> mqtt_obj.connect() - ``` - - -#### 订阅Topic - -- 自定义Topic,这里测试以设备IMEI为Topic - - ![image-20230324153501761](../media/coulds/aws/aws_17.png) - - ```python - >>> mqtt_obj.subscribe("865061058985009") - 0 - ``` -#### 数据上行 - -- 发布主题消息到平台 - - ```python - >>> mqtt_obj.publish("865061058985009", "Hello, AWS Cloud!") - True - >>> - ``` - - 云端查看设备上行消息 - - ![image-20230324153808575](../media/coulds/aws/aws_18.png) - - -#### 数据下行 - -- 找到设备订阅的Topic,选择发布消息 - - ![image-20230324153844293](../media/coulds/aws/aws_19.png) - - 设备查看平台下行数据,通过我们注册的回调函数接收数据 - - ![image-20230324153921363](../media/coulds/aws/aws_20.png) - -## 注意事项 - -- 设备进行云连接时需确认网络状态,例如SIM卡是否能够注网,设备是否拨号成功 -- 确保所用模组包含MQTT连接API可供使用 - diff --git a/docs/Getting_started/zh/clouds/azure.md b/docs/Getting_started/zh/clouds/azure.md deleted file mode 100644 index ecf528a3835e03da7655b25a7ff9f983dcef2486..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/clouds/azure.md +++ /dev/null @@ -1,215 +0,0 @@ -# QuecPython接入微软云操作文档 - -## 简介 - -基于MQTT协议连接到微软云物联网平台,设备快速连云,支持“一机一密和“一型一密”两种认证方式。 - -## 应用场景说明 - -通过微软物联网平台对同一产品下的设备进行管理,处理设备事件,完成消息转发,OTA升级等应用功能。 - -## 功能应用流程 - -### 微软云物联网平台 - -详细文档请查看:https://learn.microsoft.com/zh-cn/azure/iot-hub/iot-hub-mqtt-support - -#### 名称解释 - -连接三元组:三元组指的是ProductKey(产品标识)DeviceName(设备名)DeviceSecret(设备密钥),是设备与物联网平台建立连接时的认证信息。 - -一机一密:每个设备烧录其唯一的设备证书(ProductKey、DeviceName 和 DeviceSecret),当设备与物联网平台建立连接时,物联网平台对其携带的设备证书信息进行认证。 - -一型一密:同一产品下所有设备可以烧录相同产品证书(即 ProductKey 和 ProductSecret ),设备发送激活请求时,物联网平台进行产品身份确认,认证通过,下发该设备对应的 DeviceSecret - -#### 平台地址 - -- 微软云:https://azure.microsoft.com/zh-cn - - ![image-20230324162357942](../media/coulds/azure/azure_1.png) - - 创建账号登陆到微软云平台 - - - -#### 创建资源组 - -- 进入物联网平台 - - ![image-20230324162639056](../media/coulds/azure/azure_2.png) - -- 创建资源组 - - ![image-20230324164432886](../media/coulds/azure/azure_3.png) - - ![image-20230324164613813](../media/coulds/azure/azure_4.png) - - - -- 填写资源组信息 - - ![image-20230324164830041](../media/coulds/azure/azure_5.png) - - ![image-20230324164947486](../media/coulds/azure/azure_6.png) - - ![image-20230324165043984](../media/coulds/azure/azure_7.png) - -#### 添加IOT中心 - -- 当前创建资源组添加IOT中心 - - ![image-20230324165204952](../media/coulds/azure/azure_8.png) - - ![image-20230324165251176](../media/coulds/azure/azure_9.png) - -- 搜索选择iot中心 - - ![image-20230324165831564](../media/coulds/azure/azure_10.png) - -- 按需填写IOT中心 - - ![image-20230325152257611](../media/coulds/azure/azure_11.png) - -- 设置共享访问策略,查看连接密钥 - - ![image-20230325153306752](../media/coulds/azure/azure_12.png) - - - -#### 创建IOT设备 - -- 基于创建的IOT中心添加设备 - - ![image-20230325152924099](../media/coulds/azure/azure_13.png) - -- 按需填写设备创建信息 - - ![image-20230325153039240](../media/coulds/azure/azure_14.png) - -- 设备列表展示 - - ![image-20230325153114323](../media/coulds/azure/azure_15.png) - -#### 查看设备信息 - -- 设备信息包含连接连接密钥 - - ![image-20230325153423439](../media/coulds/azure/azure_16.png) - -### QuecPython连接微软云 - -QuecPython 官网地址:https://python.quectel.com - -#### 开发环境搭建 - -- 驱动安装 - - 驱动下载地址:https://python.quectel.com/download - - 选择对应平台的USB驱动进行安装 - - ![image-20230322110809659](../media/coulds/azure/azure_17.png) - -- QPYcom 图形化工具下载 - - 应用调试基于此工具,下载地址:https://python.quectel.com/download - - ![image-20230322111017197](../media/coulds/azure/azure_18.png) - -- 模组固件下载 - - 根据所用的模组型号选择固件下载后烧录,此文档调试选择EC600N CNLE进行演示。 - - ![image-20230322111312207](../media/coulds/azure/azure_19.png) - -#### 设备调试 - -- 打开电脑设备管理器,查看端口 - - ![image-20230322114434665](../media/coulds/azure/azure_20.png) - -- 选择 Quectel USB MI05 COM Port串口,使用QPYcom工具打开该串口 - - ![image-20230322114711697](../media/coulds/azure/azure_21.png) - -- 查询SIM卡状态和拨号状态 - - API 使用以及说明查阅Wiki文档:https://python.quectel.com/doc/API_reference/zh - - ```python - >>> import sim - >>> sim.getStatus() # 返回1表示SIM状态正常 - 1 - >>> import dataCall - >>> dataCall.getInfo(1,2) # 成功返回拨号信息 - (1, 0, [1, 0, '10.145.246.10', '211.138.180.2', '211.138.180.3'], [1, 0, '::', '::', '::']) - >>> - ``` - -#### 设备连云 - -- 连接Token生成,使用Device Explorer Twin工具生成token(Device Explorer Twin工具可以通过 visual studio生成) - - 获取云端设备信息 - - ![image-20230325155706669](../media/coulds/azure/azure_22.png) - - 生成SAS Token - - ![image-20230325155919843](../media/coulds/azure/azure_23.png) - - 从生成的SAS令牌中提取user与password - - ```python - UserName: {iothubhostname}/{device_id}/api-version=2018-06-30 - Quecpython.azure-devices.net/QuecPython_dev/api-version=2018-06-30 - password:生成的密钥中取大括号中的为password; - SharedAccessSignature={SharedAccessSignature sr=Quecpython.azure-devices.net%2Fdevices%2FQuecPython_dev&sig=9EGbbDlOHbqF1iHjZiUBrFldqdVBL4bdRadP6OBg%2BMQ%3D&se=1679817565} - ``` - - - -MQTT API 使用以及说明查阅Wiki文档:https://python.quectel.com/doc/API_reference/zh - -- 导入MQTT API - - ```python - >>> from umqtt import MQTTClient - ``` - -- 创建MQTT连接对象 - - ```python - >>> from umqtt import MQTTClient - - >>> CLIENT_ID = 'QuecPython_dev' # 设备名称 - >>> SERVER = 'Quecpython.azure-devices.net' - >>> PORT = 8883 - >>> USER = "Quecpython.azure-devices.net/QuecPython_dev/api-version=2018-06-30" - >>> PASSWORD = "SharedAccessSignature sr=Quecpython.azure-devices.net%2Fdevices%2FQuecPython_dev&sig=9EGbbDlOHbqF1iHjZiUBrFldqdVBL4bdRadP6OBg%2BMQ%3D&se=1679817565" - - >>> mqtt_obj = MQTTClient(client_id=CLIENT_ID, server=SERVER, port=PORT,user=USER,password=PASSWORD,keepalive=60,ssl=True) - ``` - -- 注册事件回调函数 - - ```python - >>> def event_callback(topic, data): - ... print("Subscribe Recv: Topic={},Msg={}".format(topic.decode(),msg.decode())) - ... - ... - ... - >>> mqtt_obj.set_callback(event_callback) - ``` - -- 发起连接 - - ```python - >>> mqtt_obj.connect() - ``` - -## 注意事项 - -- 设备进行云连接时需确认网络状态,例如SIM卡是否能够注网,设备是否拨号成功 -- 确保所用模组包含MQTT连接API可供使用 - diff --git a/docs/Getting_started/zh/clouds/ctyun.md b/docs/Getting_started/zh/clouds/ctyun.md deleted file mode 100644 index b3fee0ba14489020a892050518c9a2be5db3924a..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/clouds/ctyun.md +++ /dev/null @@ -1,212 +0,0 @@ -# 电信云应用指导文档 - -## 简介 - -基于MQTT协议连接到电信云物联网平台,设备快速连云,支持“一机一密和“一型一密”两种认证方式。 - -## 应用场景说明 - -通过电信云物联网平台对同一产品下的设备进行管理,处理设备事件,完成消息转发,OTA升级等应用功能。 - -## 功能应用流程 - -### 电信云物联网平台 - -详细文档请查看:https://www.ctwing.cn/czlks/11#/callback - -#### 名称解释 - -连接三元组:三元组指的是ProductKey(产品标识)DeviceName(设备名)DeviceSecret(设备密钥),是设备与物联网平台建立连接时的认证信息。 - -一机一密:每个设备烧录其唯一的设备证书(ProductKey、DeviceName 和 DeviceSecret),当设备与物联网平台建立连接时,物联网平台对其携带的设备证书信息进行认证。 - -一型一密:同一产品下所有设备可以烧录相同产品证书(即 ProductKey 和 ProductSecret ),设备发送激活请求时,物联网平台进行产品身份确认,认证通过,下发该设备对应的 DeviceSecret - -#### 平台地址 - -- 电信云:https://www.ctwing.cn/ - - ![image-20230510101211663](../media/coulds/ctyun/ctyun_1.png) - - 注册个人或者企业账号进行账号登录 - -#### 创建产品 - -- 进入控制台 - - ![image-20230510102329107](../media/coulds/ctyun/ctyun_2.png) - - 在开发向导中点击开通服务 - - ![image-20230510102418482](../media/coulds/ctyun/ctyun_3.png) - - ![image-20230510101927041](../media/coulds/ctyun/ctyun_4.png) - - 开通通用组件服务 - - ![image-20230510102019916](../media/coulds/ctyun/ctyun_5.png) - - ![image-20230510102549841](../media/coulds/ctyun/ctyun_6.png) - -- 按需填写产品信息 - - ![image-20230510102731811](../media/coulds/ctyun/ctyun_7.png) - -- 产品列表展示 - - ![image-20230510102850990](../media/coulds/ctyun/ctyun_8.png) - - -#### 查看产品信息 - -- 产品信息包含产品ID和MasterKey - - ![image-20230510103058826](../media/coulds/ctyun/ctyun_9.png) - -#### 创建设备 - -- 选择产品进行设备创建 - - ![image-20230510103535734](../media/coulds/ctyun/ctyun_10.png) - -- 设备创建完成,初始状态为已注册,登陆成功后则显示已激活 - - ![image-20230510103634560](../media/coulds/ctyun/ctyun_11.png) - -#### 查看设备信息 - -- 查看设备信息,设备信息包含设备ID,DeviceSecret(特征串) - - ![image-20230510103813380](../media/coulds/ctyun/ctyun_12.png) - -### QuecPython连接电信云 - -QuecPython 官网地址:https://python.quectel.com - -![image-20230510105341208](../media/coulds/ctyun/ctyun_13.png) - -#### 开发环境搭建 - -- 驱动安装 - - 驱动下载地址:https://python.quectel.com/download - - 选择对应平台的USB驱动进行安装 - - ![image-20230322110809659](../media/coulds/ctyun/ctyun_14.png) - -- QPYcom 图形化工具下载 - - 应用调试基于此工具,下载地址:https://python.quectel.com/download - - ![image-20230322111017197](../media/coulds/ctyun/ctyun_15.png) - -- 模组固件下载 - - 根据所用的模组型号选择固件下载后烧录,此文档调试选择EC600N CNLE进行演示。 - - ![image-20230322111312207](../media/coulds/ctyun/ctyun_16.png) - -#### 设备调试 - -- 打开电脑设备管理器,查看端口 - - ![image-20230322114434665](../media/coulds/ctyun/ctyun_17.png) - -- 选择 Quectel USB MI05 COM Port串口,使用QPYcom工具打开该串口 - - ![image-20230322114711697](../media/coulds/ctyun/ctyun_18.png) - -- 查询SIM卡状态和拨号状态 - - API 使用以及说明查阅Wiki文档:https://python.quectel.com/doc/API_reference/zh - - ```python - >>> import sim - >>> sim.getStatus() # 返回1表示SIM状态正常 - 1 - >>> import dataCall - >>> dataCall.getInfo(1,2) # 成功返回拨号信息 - (1, 0, [1, 0, '10.145.246.10', '211.138.180.2', '211.138.180.3'], [1, 0, '::', '::', '::']) - >>> - ``` - -#### 设备连云 - -MQTT API 使用以及说明查阅Wiki文档:https://python.quectel.com/doc/API_reference/zh - -- 导入MQTT API - - ```python - >>> from umqtt import MQTTClient - ``` - -- 创建MQTT连接对象 - - 设备需将平台添加设备时提供的设备ID写入MQTT报文的ClientId字段,特征串写入设备Password字段 - - ```python - >>> from umqtt import MQTTClient - - >>> CLIENT_ID = '167229012023' - >>> SERVER = '167229012023.non-nb.ctwing.cn' - >>> PORT = 1883 - >>> USER = "Quecpython_dev" - >>> PASSWORD = "j0XmVK84hCs7fakLzTLI2htAngyVLivTAQzFNQbuiYI" - - >>> mqtt_obj = MQTTClient(client_id=CLIENT_ID, server=SERVER, port=PORT,user=USER,password=PASSWORD,keepalive=30) - ``` - -- 注册事件回调函数 - - ```python - >>> def event_callback(topic, data): - ... print("Subscribe Recv: Topic={},Msg={}".format(topic.decode(),msg.decode())) - ... - ... - ... - >>> mqtt_obj.set_callback(event_callback) - ``` - -- 发起连接 - - ```python - >>> mqtt_obj.connect() - ``` - -- 云端查看设备状态,由创建时已注册状态变成激活状态 - - ![image-20230510110513645](../media/coulds/ctyun/ctyun_19.png) - -#### 订阅Topic - -- 订阅统一主题, - - ![image-20230510112211696](../media/coulds/ctyun/ctyun_20.png) - - ```python - >>> mqtt_obj.subscribe("device_control") - 0 - ``` -#### 数据上行 - -- 发布主题消息到平台,Topic字段填写任意主题 - - ```python - >>> import ujson - >>> mqtt_obj.publish("device_test", ujson.dumps({"quec": "Hello, Ctwing cloud!"})) - True - >>> - ``` - - -云端查看设备上行消息 - - ![image-20230510112121530](../media/coulds/ctyun/ctyun_21.png) - - - -## 注意事项 - -- 设备进行云连接时需确认网络状态,例如SIM卡是否能够注网,设备是否拨号成功 -- 确保所用模组包含MQTT连接API可供使用 \ No newline at end of file diff --git a/docs/Getting_started/zh/clouds/huawei.md b/docs/Getting_started/zh/clouds/huawei.md deleted file mode 100644 index 521d04f81f18542729097cb33e789097be07f458..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/clouds/huawei.md +++ /dev/null @@ -1,280 +0,0 @@ -# QuecPython 接入华为云 - -## 简介 - -文档主要介绍如何使用HuaweiCloud华为云,HuaweiCloud设备接入服务(IoTDA)是华为云的物联网平台,提供海量设备连接上云、设备和云端双向消息通信、批量设备管理、远程控制和监控、OTA升级、设备联动规则等能力,并可将设备数据灵活流转到华为云其他服务。 - -下面主要从QuecPython的MQTT连接方式讲解,通过阅读本文,您将了解到QuecPython连接HuaweiCloud的过程操作和验证实验理论。 - -## 应用场景说明 - -通过华为云物联网平台对同一产品下的设备进行管理,处理设备事件,完成消息转发,OTA升级等应用功能。 - -## 功能应用流程 - -### 华为云物联网平台 - -打开华为云连接 [www.huaweicloud.com](http://www.huaweicloud.com) - -#### 名称解释 - -连接三元组:三元组指的是ProductKey(产品标识)DeviceName(设备名)DeviceSecret(设备密钥),是设备与物联网平台建立连接时的认证信息。 - -一机一密:每个设备烧录其唯一的设备证书(ProductKey、DeviceName 和 DeviceSecret),当设备与物联网平台建立连接时,物联网平台对其携带的设备证书信息进行认证。 - -一型一密:同一产品下所有设备可以烧录相同产品证书(即 ProductKey 和 ProductSecret ),设备发送激活请求时,物联网平台进行产品身份确认,认证通过,下发该设备对应的 DeviceSecret - -#### 进入平台 - -在首页点击【产品】---【IoT物联网】---【设备接入IoTDA】 - -![184cc27df7aaf8e51ff5fba33b354abe](../media/coulds/huawei/huawei_1.png) - -#### 创建产品 - -点击【产品】---【创建产品】 - -![437eefd54847b5780adc6b9c2c496544](../media/coulds/huawei/huawei_2.png) - -产品的“协议类型”选择MQTT - -![58e05991a4eddf591effea2642579f4a](../media/coulds/huawei/huawei_3.png) - -产品【模型定义】 - -![image-20220216154517352](../media/coulds/huawei/huawei_4.png) - -#### 添加设备 - -点击【设备】---【所有设备】 - -> [https://console.huaweicloud.com/iotdm/?region=cn-north-4\#/dm-portal/device/all-device](https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/device/all-device) - -![ee306a4acca2ec6a25d90a026d6cff22](../media/coulds/huawei/huawei_5.png) - -注册设备后自动生成的【设备ID】和【设备密钥】,记下来用于下一步骤生成连接平台的信息。 - -![178b3243bd5399776eaac723d81ce845](../media/coulds/huawei/huawei_6.png) - -#### 生成连接信息 - -访问[这里](https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/),填写[注册设备](https://support.huaweicloud.com/qs-iothub/iot_05_0006.html#ZH-CN_TOPIC_0251997880__li1261073102011)后生成的设备ID(DeviceId)和密钥(DeviceSecret),生成连接信息(ClientId、Username、Password)。 - - - -![f51db8dc51ba6ae73375cbfe7f8b0063](../media/coulds/huawei/huawei_7.png) - -```python -CLIENT_ID = b'5fbbb784b4ec2202e982e643_868540050954037_0_0_2021011909' - -SERVER = b'a15fbbd7ce.iot-mqtts.cn-north-4.myhuaweicloud.com' - -PORT = 1883 - -USER = b'5fbbb784b4ec2202e982e643_868540050954037' - -PASSWORD =b'8001a12405743199b3396943a2ed397286117a9ebab4f5dfda8dd6fafe341d94' -``` - -![image-20210929163551641](../media/coulds/huawei/huawei_8.png) - -![image-20210929163737178](../media/coulds/huawei/huawei_9.png) - -![ad674210c976e30f24d800bede396808](../media/coulds/huawei/huawei_10.png)说明: - -针对不支持用域名接入的设备,通过在cmd命令框中执行“ping -域名”获取IP地址,用IP地址接入平台。由于IP地址不固定,您需要将IP地址做成可配置项。 - -a15fbbd7ce.iot-mqtts.cn-north-4.myhuaweicloud.com - -1883 - -使用生成工具生成的clientId格式,默认不校验时间戳:设备ID_0_0\_时间戳。 - -- 当采用“HMACSHA256”校验时间戳方式时,会先校验消息时间戳与平台时间是否一致,再判断密码是否正确。 - -- 当采用“HMACSHA256”不校验时间戳方式时,鉴权消息也必须带时间戳,但不检验时间是否准确,仅判断密码是否正确。 - -#### 上报数据 - -填写接口地址,此处以“\$oc/devices/{device_id}/sys/properties/report”为例,如“\$oc/devices/5e4e2e92ac-164aefa8fouquan1/sys/properties/report”。 - -![bf41a9ad7dcf2bc0718496712c30dd72](../media/coulds/huawei/huawei_11.png) - -\$oc/devices/{device_id}/sys/properties/report - -\$oc/devices/5fbbb784b4ec2202e982e643_868540050954037/sys/properties/report - -![image-20220216153531106](../media/coulds/huawei/huawei_12.png) - -#### 下发数据 - -下发数据只能通过应用端的API请求。 - -URI - -请求方法 POST - -URI /v5/iot/{project_id}/devices/{device_id}/messages - -传输协议 HTTPS - -下发数据说明: - - - -API调试页面: - - - -![6928f54528bf7a8c7f9ad115db1520e4](../media/coulds/huawei/huawei_13.png) - -Topic: \$oc/devices/{device_id}/sys/messages/down - -\$oc/devices/5fbbb784b4ec2202e982e643_1234567890123456789/sys/messages/down - -![8b01f010e317cfd3129333709d60a514](../media/coulds/huawei/huawei_14.png) - -#### 下发命令 - -$oc/devices/{device_id}/sys/commands/request_id={request_id} - -- 设备侧订阅带{request_id}参数的topic时,可以使用通配#。如设备侧订阅命令下发请求的topic $oc/devices/{device_id}/sys/commands/request_id={request_id}时,可以用$oc/devices/{device_id}/sys/commands/# - -![image-20220216154203735](../media/coulds/huawei/huawei_15.png) - -![image-20220216154010392](../media/coulds/huawei/huawei_16.png) - -$oc/devices/{device_id}/sys/commands/request_id={request_id} - -$oc/devices/5fbbb784b4ec2202e982e643_869537055157730/sys/commands/# - -![image-20220216154918490](../media/coulds/huawei/huawei_17.png) - -#### 响应命令 - -下行: $oc/devices/{device_id}/sys/commands/request_id={request_id} - -上行:$oc/devices/{device_id}/sys/commands/response/request_id={request_id} - -平台下发命令后,需要设备及时将命令的执行结果返回给平台,如果设备没回响应,平台会认为命令执行超时。 - -![image-20220223145214105](../media/coulds/huawei/huawei_18.png) - -## 软件设计 - -在HuaweiCloud平台取得ClientId、Username、Password之后,即可通过QuecPython的umqtt连接云平台,连接云平台的地址和端口固定不变,接下来就可以编写代码实验了。 - -示例代码: - -```python -import checkNet -from umqtt import MQTTClient -import modem -from machine import UART -import sim -import hmac -from hashlib import sha256 -import utime - -IMEI = modem.getDevImei() -E_SN = modem.getDevSN() -ESIM = sim.getImsi() - -DeviceName = IMEI -DeviceSecret = 'ac4263b86eaf6442997a' -DeviceID = "5fbbb784b4ec2202e982e643" - -CLIENT_ID = DeviceID + "_" + DeviceName + "_0_0_2022010507" -SERVER = "a15fbbd7ce.iot-mqtts.cn-north-4.myhuaweicloud.com" -PORT = 1883 -USER = DeviceID + "_" + DeviceName -PASSWORD = hmac.new( - "2022010507".encode('utf-8'), - DeviceSecret.encode('utf-8'), - digestmod=sha256).hexdigest() -DEVICE_ID = DeviceID + "_" + DeviceName - -state = 0 - -def sub_cb(topic, msg): - global state - global c - print( - "Subscribe Recv: Topic={},Msg={}".format( - topic.decode(), - msg.decode())) - topic = topic.decode() - msg = b'''{ - "result_code": 0 - }''' - if r'/sys/commands/request_id=' in topic: - c.publish('$oc/devices/{}/sys/commands/response/request_id={}'.format(DEVICE_ID,(topic.split("=")[-1])),msg) - state = 1 - - -def MQTT_Init(): - global c - c = MQTTClient( - client_id=CLIENT_ID, - server=SERVER, - port=PORT, - user=USER, - password=PASSWORD, - keepalive=30) - - c.set_callback(sub_cb) - try: - c.connect() - c.subscribe('$oc/devices/{}/sys/commands/#'.format(DEVICE_ID)) - - msg = b'''{ - "services": [{ - "service_id": "WaterMeterControl", - "properties": { - "state": "T:15c, H: 85% " - } - } - ] - }''' - c.publish('$oc/devices/{}/sys/properties/report'.format(DEVICE_ID), msg) - except BaseException: - print('except') - print('Waiting command') - while True: - c.wait_msg() - if state == 1: - utime.sleep_ms(300) - break - - c.disconnect() - - -def main(): - MQTT_Init() - - -if __name__ == "__main__": - main() - -``` - -接下来就可以下载验证了,python代码不需要编译,直接通过QPYcom工具把.py文件下载到模块中运行。 - -## 下载验证 - -下载.py文件到模组运行: - -![image-20220216161638266](../media/coulds/huawei/huawei_19.png) - -下载之后,手动让脚本运行起来。 - -![image-20220216161801912](../media/coulds/huawei/huawei_20.png) - -发送数据后,看到实验结果: - -![image-20220216161915875](../media/coulds/huawei/huawei_21.png) - - - - diff --git a/docs/Getting_started/zh/clouds/onenet.md b/docs/Getting_started/zh/clouds/onenet.md deleted file mode 100644 index a0ec9f7f0ff11c566d7c43e41f06f50da2b4b4a1..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/clouds/onenet.md +++ /dev/null @@ -1,311 +0,0 @@ -# QuecPython 接入移动云 - -## 简介 - -文档主要介绍如何使用“MQTT物联网套件”,MQTT物联网套件为开发者提供高效、稳定、安全的设备接入服务,具有海量接入、数据存储、设备管理、设备直接命令、设备状态同步、消息分发等功能,支持用户通过规 则引擎对接OneNET增值服务,灵活地实现服务的扩展。 - -## 应用场景说明 - -通过移动云物联网平台对同一产品下的设备进行管理,处理设备事件,完成消息转发,OTA升级等应用功能。 - -## 功能应用流程 - -### 移动云物联网平台 - -移动云首页:[https://open.iot.10086.cn](https://open.iot.10086.cn) - -#### 进入平台 - -点击【产品服务】---【MQTT物联网套件】 - -![OneNET_MQTT_01](../media/coulds/onenet/OneNET_MQTT_01.png) - - - -#### 添加产品 - -![OneNET_MQTT_02](../media/coulds/onenet/OneNET_MQTT_02.png) - -![OneNET_MQTT_03](../media/coulds/onenet/OneNET_MQTT_03.png) - -![OneNET_MQTT_04](../media/coulds/onenet/OneNET_MQTT_04.png) - -#### 添加设备 - -点击“Chic_演示产品”进入产品概况 - -![OneNET_MQTT_05](../media/coulds/onenet/OneNET_MQTT_05.png) - -左侧栏目点击【设备列表】,再点击右边的“添加设备” - -![OneNET_MQTT_06](../media/coulds/onenet/OneNET_MQTT_06.png) - -设置设备名称: - -![OneNET_MQTT_07](../media/coulds/onenet/OneNET_MQTT_07.png) - -添加后,设备列表显示所有设备 - -![OneNET_MQTT_08](../media/coulds/onenet/OneNET_MQTT_08.png) - -#### 获取设备信息 - -“设备名称”、“access_key”、“产品ID”三个参数用于下个步骤生成连接Password; - -在“设备列表”中的设备,点击“详情” - -![OneNET_MQTT_09](../media/coulds/onenet/OneNET_MQTT_09.png) - -可以看到“设备名称”、“access_key” - -![OneNET_MQTT_10](../media/coulds/onenet/OneNET_MQTT_10.png) - -在“产品”中的设备,点击“详情” - -左侧栏目点击【产品概况】可看到“产品ID” - -![OneNET_MQTT_11](../media/coulds/onenet/OneNET_MQTT_11.png) - -#### 生成Password - -直接使用官方提供Python语言的token算法生成Password - -[https://open.iot.10086.cn/doc/mqtt/book/manual/auth/python.html](http://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/assets/attach/189223/cn_zh/1605168543507/MQTT_Password.7z?spm=a2c4g.11186623.2.19.373573a8XfigN5&file=MQTT_Password.7z) - -token分两种:一型一密、一机一密 - -```python -import base64 -import hmac -import time -from urllib.parse import quote - -# 场景 res参数格式 示例 说明 -# API访问 products/{pid} products/123123 -# 设备连接 products/{pid}/devices/{device_name} products/123123/devices/mydev 需使用设备级密钥 -product = 0 -device = 1 -TYPE = device -# TYPE 用于选择一型一密,还是一机一密 - -def token(id, access_key, deviceName=None): - global product - global device - global TYPE - - version = '2018-10-31' - - if TYPE == product: - res = 'products/%s' % id # 通过产品ID访问产品API - elif TYPE == device: - res = ('products/%s/devices/%s' % (id, deviceName)) - - # 用户自定义token过期时间 - et = str(int(time.time()) + 36000000) - - # 签名方法,支持md5、sha1、sha256 - method = 'sha1' - - # 对access_key进行decode - key = base64.b64decode(access_key) - - # 计算sign - org = et + '\n' + method + '\n' + res + '\n' + version - sign_b = hmac.new(key=key, msg=org.encode(), digestmod=method) - sign = base64.b64encode(sign_b.digest()).decode() - - # value 部分进行url编码,method/res/version值较为简单无需编码 - sign = quote(sign, safe='') - res = quote(res, safe='') - - # token参数拼接 - token = 'version=%s&res=%s&et=%s&method=%s&sign=%s' % ( - version, res, et, method, sign) - - return token - - -if __name__ == '__main__': - # MQTT - id = '469948' # 产品ID - if TYPE == product: - access_key = 'kKsubaG7FzMgzR6N3eUD53319Qu+K4MCvw6KzOYc5eI=' # 产品access_key - elif TYPE == device: - access_key = 'rs2FaE5BuygJZkWKDA5okKt1RFpwOx53hbu6hwyCS4U=' # 设备access_key - deviceName = 'Chic_D' - print(token(id, access_key, deviceName)) -``` - -#### mqttfx连接平台 - -[开发指南_开发者文档_OneNET (10086.cn)](https://open.iot.10086.cn/doc/mqtt/book/device-develop/manual.html) - -获取平台服务器IP地址、端口: - -![OneNET_MQTT_12](../media/coulds/onenet/OneNET_MQTT_12.png) - -![OneNET_MQTT_13](../media/coulds/onenet/OneNET_MQTT_13.png) - -连接成功! - -![OneNET_MQTT_14](../media/coulds/onenet/OneNET_MQTT_14.png) - -#### 上报和下发数据 - -[上传数据点_开发者文档_OneNET (10086.cn)](https://open.iot.10086.cn/doc/mqtt/book/example/datapoints.html) - -topic 命名规则如下: - -$sys/{pid}/{device-name}/dp/post/json/+ - -本例中,订阅topic为: - -`$sys/469948/Chic_D/dp/post/json/+` - -点击Subscribe,完成topic订阅 - -![OneNET_MQTT_15](../media/coulds/onenet/OneNET_MQTT_15.png) - -topic 命名规则如下: - -$sys/{pid}/{device-name}/dp/post/json - -本例中,发布 topic 名称为: - -`$sys/469948/Chic_D/dp/post/json` - -payload示例如下: - -```json -{ - "id": 123, - "dp": { - "temperatrue": [{ - "v": 30, - }], - "power": [{ - "v": 4.5, - }] - } -} -``` - -![OneNET_MQTT_16](../media/coulds/onenet/OneNET_MQTT_16.png) - -点击Publish,发布消息,已订阅的设备立即收到消息 - -![OneNET_MQTT_17](../media/coulds/onenet/OneNET_MQTT_17.png) - - - -## 软件设计 - -示例代码: - -```python -from umqtt import MQTTClient - -# 连接协议 证书 地址 端口 说明 -# MQTT 证书下载 mqttstls.heclouds.com 8883 加密接口 -# MQTT - mqtts.heclouds.com 1883 非加密接口 - -# 参数 是否必须 参数说明 -# clientId 是 设备名称 -# username 是 平台分配的产品ID -# password 是 填写经过 key 计算的 token - -SERVER = b'mqtts.heclouds.com' -PORT = 1883 -CLIENT_ID = b'Chic_D' -USER = b'469948' -PASSWORD = b'version=2018-10-31&res=products%2F469948%2Fdevices%2FChic_D&et=1673053248&method=sha1&sign=prIMDQ23WFI6PMj2IWpaRJJL4eE%3D' - -IMEI = None # modem.getDevImei() -SUB_TOPIC = '$sys/469948/Chic_D/dp/post/json/+' -PUB_TOPIC = '$sys/469948/Chic_D/dp/post/json' - -def GetDevImei(): - global IMEI - # IMEI = modem.getDevImei() - IMEI = '001' - print(IMEI) - -state = 0 - -def sub_cb(topic, msg): - global state - print( - "Subscribe Recv: Topic={},Msg={}".format( - topic.decode(), - msg.decode())) - state = 1 - - -def MQTT_Init(): - # 创建一个mqtt实例 - c = MQTTClient( - client_id=CLIENT_ID, - server=SERVER, - port=PORT, - user=USER, - password=PASSWORD, - keepalive=30) # 必须要 keepalive=30 ,否则连接不上 - # 设置消息回调 - c.set_callback(sub_cb) - # 建立连接 - try: - c.connect() # c.connect(clean_session=True) - except Exception as e: - print('!!!,e=%s' % e) - return - print('connected') - - # 订阅主题 - c.subscribe(SUB_TOPIC.format(IMEI)) - # 发布消息 - Payload = ''' - { - "id": 123, - "dp": { - "temperatrue": [{ - "v": 30, - }], - "power": [{ - "v": 4.5, - }] - } - }''' - c.publish(PUB_TOPIC.format(IMEI), Payload) - - while True: - c.wait_msg() - if state == 1: - break - - # 关闭连接 - c.disconnect() - - -def main(): - # GetDevImei() - MQTT_Init() - - -if __name__ == "__main__": - main() -``` - -接下来就可以下载验证了,python代码不需要编译,直接通过QPYcom工具把.py文件下载到模块中运行。 - -## 下载验证 - -下载.py文件到模组运行: - -![OneNET_MQTT_18](../media/coulds/onenet/OneNET_MQTT_18.png) - -下载之后,手动让脚本运行起来。 - -![OneNET_MQTT_19](../media/coulds/onenet/OneNET_MQTT_19.png) - - - diff --git a/docs/Getting_started/zh/clouds/tencent.md b/docs/Getting_started/zh/clouds/tencent.md deleted file mode 100644 index d6e8bd7d7f8266be728e513b8a5e163d63432b01..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/clouds/tencent.md +++ /dev/null @@ -1,222 +0,0 @@ -# 腾讯云应用指导文档 - -## 简介 - -基于MQTT协议连接到腾讯云物联网平台,设备快速连云,支持“一机一密和“一型一密”两种认证方式。 - -## 应用场景说明 - -通过腾讯云物联网平台对同一产品下的设备进行管理,处理设备事件,完成消息转发,OTA升级等应用功能。 - -## 功能应用流程 - -### 腾讯云物联网平台 - -详细文档请查看:https://cloud.tencent.com/document/product/1081 - -#### 名称解释 - -连接三元组:三元组指的是ProductKey(产品标识)DeviceName(设备名)DeviceSecret(设备密钥),是设备与物联网平台建立连接时的认证信息。 - -一机一密:每个设备烧录其唯一的设备证书(ProductKey、DeviceName 和 DeviceSecret),当设备与物联网平台建立连接时,物联网平台对其携带的设备证书信息进行认证。 - -一型一密:同一产品下所有设备可以烧录相同产品证书(即 ProductKey 和 ProductSecret ),设备发送激活请求时,物联网平台进行产品身份确认,认证通过,下发该设备对应的 DeviceSecret - -#### 平台地址 - -- 腾讯云:https://cloud.tencent.com - - ![image-20230323101526661](../media/coulds/tencent/tencent_1.png) - - 注册个人或者企业账号进行账号登录 - -#### 创建产品 - -- 进入物联网平台 - - ![image-20230323103213453](../media/coulds/tencent/tencent_2.png) - - ![image-20230323103327396](../media/coulds/tencent/tencent_3.png) - - 点击开发平台进入iot Hub查看产品,没有则创建 - - ![image-20230324092855021](../media/coulds/tencent/tencent_4.png) - - ![image-20230324093536745](../media/coulds/tencent/tencent_5.png) - -- 按需填写产品信息 - - ![image-20230324093639577](../media/coulds/tencent/tencent_6.png) - -- 产品列表展示 - - ![image-20230324093754781](../media/coulds/tencent/tencent_7.png) - - -#### 查看产品信息 - -- 产品信息包含ProductKey 和 ProductSecret - - ![image-20230324094117753](../media/coulds/tencent/tencent_8.png) - -#### 创建设备 - -- 选择产品进行设备创建 - - ![image-20230324094303909](../media/coulds/tencent/tencent_9.png) - -- 添加设备 - - ![image-20230324094251444](../media/coulds/tencent/tencent_10.png) - -- 手动编辑设备信息 - - ![image-20230324094402831](../media/coulds/tencent/tencent_11.png) - -- 设备创建完成后处于未激活状态 - - ![image-20230324094423342](../media/coulds/tencent/tencent_12.png) - -#### 查看设备信息 - -- 查看设备信息,设备信息包含DeviceName(设备名称)DeviceSecret(设备密钥) - - ![image-20230324094519889](../media/coulds/tencent/tencent_13.png) - -### QuecPython连接腾讯云 - -QuecPython 官网地址:https://python.quectel.com - -#### 开发环境搭建 - -- 驱动安装 - - 驱动下载地址:https://python.quectel.com/download - - 选择对应平台的USB驱动进行安装 - - ![image-20230322110809659](../media/coulds/tencent/tencent_14.png) - -- QPYcom 图形化工具下载 - - 应用调试基于此工具,下载地址:https://python.quectel.com/download - - ![image-20230322111017197](../media/coulds/tencent/tencent_15.png) - -- 模组固件下载 - - 根据所用的模组型号选择固件下载后烧录,此文档调试选择EC600N CNLE进行演示。 - - ![image-20230322111312207](../media/coulds/tencent/tencent_16.png) - -#### 设备调试 - -- 打开电脑设备管理器,查看端口 - - ![image-20230322114434665](../media/coulds/tencent/tencent_17.png) - -- 选择 Quectel USB MI05 COM Port串口,使用QPYcom工具打开该串口 - - ![image-20230322114711697](../media/coulds/tencent/tencent_18.png) - -- 查询SIM卡状态和拨号状态 - - API 使用以及说明查阅Wiki文档:https://python.quectel.com/doc/API_reference/zh - - ```python - >>> import sim - >>> sim.getStatus() # 返回1表示SIM状态正常 - 1 - >>> import dataCall - >>> dataCall.getInfo(1,2) # 成功返回拨号信息 - (1, 0, [1, 0, '10.145.246.10', '211.138.180.2', '211.138.180.3'], [1, 0, '::', '::', '::']) - >>> - ``` - -#### 设备连云 - -腾讯云API 使用以及说明查阅Wiki文档:https://python.quectel.com/doc/API_reference/zh - -- 导入腾讯云API - - ```python - >>> from TenCentYun import TXyun - ``` - -- 创建腾讯云连接对象 - - ```python - >>> from TenCentYun import TXyun - - >>> productID = "UMNSX09J5Y" - >>> DeviceName = "Quecpython_dev" - >>> DeviceSecret = "osj/SKJsa9cjyuM1KeHr8Q==" - >>> productSecre = None - >>> - >>> txy_obj = TXyun(productID, DeviceName, DeviceSecret, productSecre) - ``` - -- 注册事件回调函数 - - ```python - >>> def event_callback(topic, data): - ... print("tencent callback recv: {}".format(data)) - ... - ... - ... - >>> txy_obj.setCallback(event_callback) - ``` - -- 设置连接参数并连接平台,state为0时表示连接成功,连接成功后调用start方法 - - ```python - >>> clean_session = False - >>> keepAlive = 300 - >>> reconn = True - - >>> state = txy_obj.setMqtt(clean_session=False, keepAlive=300,reconn=True) - >>> state - 0 - >>> txy_obj.start() - ``` - -- 云端查看设备状态,由创建时未激活状态变成在线状态 - - ![image-20230324095106916](../media/coulds/tencent/tencent_19.png) - -#### 订阅Topic - -- 发布主题消息到平台,Topic选择操作权限为发布的进行数据上报 - - ${productID}/${deviceName}/event:${productID}替换成我们的产品标识,$ {deviceName}替换成我们的设备名称即可,例如"UMNSX09J5Y/Quecpython_dev/event" - - ![image-20230324095333860](../media/coulds/tencent/tencent_20.png) - - ```python - >>> txy_obj.subscribe("UMNSX09J5Y/Quecpython_dev/data") - 0 - >>> - ``` - -#### 数据上行 - -- 发布主题消息到平台,Topic选择在规则引擎中配置的data或其它自定义主题 - - ```python - >>> txy_obj.publish("EY293KGSLY/Quecpython_dev/data", "Hello, TenCent cloud!") - True - >>> - ``` - - 云端查看设备上行消息 - - ![image-20230324102143144](../media/coulds/tencent/tencent_21.png) - - - -## 注意事项 - -- 设备进行云连接时需确认网络状态,例如SIM卡是否能够注网,设备是否拨号成功 -- 确保所用模组包含腾讯云连接API可供使用 -- Topic注意操作权限 - diff --git a/docs/Getting_started/zh/evb/ec2x-evb.md b/docs/Getting_started/zh/evb/ec2x-evb.md new file mode 100644 index 0000000000000000000000000000000000000000..e6c0c2546d1a3de95ec7727f6d76e02138b82b1f --- /dev/null +++ b/docs/Getting_started/zh/evb/ec2x-evb.md @@ -0,0 +1,137 @@ +# EC2X开发板介绍 + + + +## 支持的模组列表 + +- [EC200A-CN](https://python.quectel.com/products/ec200a-cn) +- [EC200U-CN](https://python.quectel.com/products/ec200u-cn) +- [EC200A-EU](https://python.quectel.com/en/products/ec200a-eu) +- [EC200A-AU](https://python.quectel.com/en/products/ec200a-au) + +## 功能列表 + +### 基本概述 + +QuecPython_EC2X_EVB_Vx.x 开发板是专门针对QuecPython制造,是一款小巧便携的“口袋型”开发板。体型虽小,但是功能丰富,拥有温湿度传感器、加速度传感器、SIM卡座、SD卡座、LCD接口、光敏电阻、MIC等元件。 + +开发板搭载Type-C接口,开发者仅需一条USB Type-C 数据线即可轻松玩转开发板。 + +开发板搭载EC200U模组,详细资料参考 [EC200U-EU](https://python.quectel.com/en/products/ec200u-eu) + +### 功能说明 + +开发板的主要组件、接口布局见下图 + +image-2021081200 + +## 资料下载 + +- [EC200U系列模组硬件设计手册(PDF)](https://images.quectel.com/python/2023/04/Quectel_EC200U%E7%B3%BB%E5%88%97_QuecOpen_%E7%A1%AC%E4%BB%B6%E8%AE%BE%E8%AE%A1%E6%89%8B%E5%86%8C_V1.1.pdf) +- [EC200U系列模组产品规格书(PDF)](https://images.quectel.com/python/2023/04/Quectel_EC200U%E7%B3%BB%E5%88%97_LTE_Standard_%E6%A8%A1%E5%9D%97%E4%BA%A7%E5%93%81%E8%A7%84%E6%A0%BC%E4%B9%A6_V1.2.pdf) +- [EC200U系列模组封装(ZIP)](https://images.quectel.com/python/2023/05/Quectel_EC200U_Series_FootprintPart_V1.4.zip) +- [EC200U系列模组参考设计手册(PDF)](https://images.quectel.com/python/2023/05/Quectel_EC200U%E7%B3%BB%E5%88%97_%E5%8F%82%E8%80%83%E8%AE%BE%E8%AE%A1%E6%89%8B%E5%86%8C_V1.2.pdf) + +## 模组资源 + +### 开发板接口 + +**J5排针管脚分配表** + +| 排针 | 编号 | 名称 | 引脚 | 功能 | +| ---- | ---- | ------- | ---- | ----- | +| J5 | 1 | VCC_5V | - | 5V | +| J5 | 2 | VCC_5V | - | 5V | +| J5 | 3 | GND | - | 接地 | +| J5 | 4 | IO3 | 24 | GPIO3 | +| J5 | 5 | IO4 | 25 | GPIO4 | +| J5 | 6 | IO2 | 26 | GPIO2 | +| J5 | 7 | IO1 | 27 | GPIO1 | +| J5 | 8 | ADC1_IN | 44 | ADC1 | +| J5 | 9 | VDD_EXT | - | 1.8V | + +**J6排针管脚分配表** + +| 排针 | 编号 | 名称 | 引脚 | 功能 | +| ---- | ---- | ------- | ---- | -------- | +| J6 | 1 | V3.3 | - | 3.3V | +| J6 | 2 | GND | - | 接地 | +| J6 | 3 | GND | - | 接地 | +| J6 | 4 | IO9 | 3 | GPIO9 | +| J6 | 5 | SD_EN | 119 | SD卡使能 | +| J6 | 6 | SDA | 142 | I2C1 | +| J6 | 7 | SCL | 141 | I2C1 | +| J6 | 8 | ADC2_IN | 43 | ADC2 | +| J6 | 9 | ADC0_IN | 45 | ADC0 | + +**J7排针管脚分配表** + +| 排针 | 编号 | 名称 | 引脚 | 功能 | +| ---- | ---- | ---- | ---- | ------ | +| J7 | 1 | GND | - | 接地 | +| J7 | 2 | RX1 | 138 | UART1 | +| J7 | 3 | TX1 | 137 | UART1 | +| J7 | 4 | TX2 | 67 | UART2 | +| J7 | 5 | RX2 | 68 | UART2 | +| J7 | 6 | IO18 | 65 | GPIO18 | +| J7 | 7 | IO19 | 64 | GPIO19 | +| J7 | 8 | D_RX | 11 | UART | +| J7 | 9 | D_TX | 12 | UART | + +**J10排针管脚分配表** + +| 排针 | 编号 | 名称 | 引脚 | 功能 | +| ---- | ---- | ---- | ---- | ------ | +| J10 | 1 | IO7 | 136 | GPIO7 | +| J10 | 2 | IO6 | 135 | GPIO6 | +| J10 | 3 | IO22 | 127 | GPIO22 | +| J10 | 4 | IO10 | 40 | GPIO10 | +| J10 | 5 | IO13 | 39 | GPIO13 | +| J10 | 6 | IO12 | 38 | GPIO12 | +| J10 | 7 | IO11 | 37 | GPIO11 | +| J10 | 8 | GND | - | 接地 | +| J10 | 9 | V3.8 | - | 3.8V | + +开发板主要管脚布局见下图 + +image-2021081200 + +| 小提示 | +| ------------------------------------------------------------ | +| 开发板的更多资料,请访问 | + +### 开发板配置 + +开发板配备了多种传感器,以及其他外设。外设资源管脚分配表明细如下: + +| 序号 | 名称 | 型号 | 是否支持 | 接口类型 | 引脚 | +| ---- | ---------------------------- | ------------- | -------- | -------- | ------- | +| 1 | 温湿度传感器 | AHT20 | 是 | I2C | 41,42 | +| 2 | 光敏电阻 | GT36528 | 是 | ADC | 45 | +| 3 | G-Sensor | QMA7981 | 是 | I2C | 41,42 | +| 4 | 麦克风 | GMI6050P-66DB | 是 | SPK | 75,77 | +| 5 | 功放芯片 | NS4160 | 是 | SPK | 73,74 | +| 6 | LCD 显示屏(需选择含屏套餐) | ST7789 | 是 | SPI | 122~125 | +| 7 | SD_CARD | XKTF-NO2-N | 是 | SPI | 28~34 | + +## 上手准备 + +> 首先需要有一台运行有 Windows 10 以上 操作系统的电脑 + +- **Step1:天线安装** + +安装开发板配套的天线,安装位置为LTE天线座位置,并将SIM卡插入开发板上的SIM卡座,如需使用GNSS或者BTWIFI功能,则需在对应的天线座安装天线 + +- **Step2:开发板连接** + +使用USB Type-C数据线连接开发板的Type-C接口和电脑USB口即可完成供电 + +- **Step3:开发板电源设置** + +开发板上USB和DC的电源选择开关拨到USB处,开发板上的PWK_ON跳帽短接AUTO(上电自动开机) + +- **Step4:开发板开机** + +按住PWK直至主板上电源指示灯亮(主板上丝印为POW的灯),如果上一步短接PWK_ON则无需长按PWK自动开机 + +**执行以上操作后POW灯常亮即开机成功** \ No newline at end of file diff --git a/docs/Getting_started/zh/evb/fcm360w-evb.md b/docs/Getting_started/zh/evb/fcm360w-evb.md new file mode 100644 index 0000000000000000000000000000000000000000..9dbe3e87e99079a30b450c8befcfd91931e6d435 --- /dev/null +++ b/docs/Getting_started/zh/evb/fcm360w-evb.md @@ -0,0 +1,101 @@ +# FCM360W开发板介绍 + +## 支持的模组列表 + +- [FCM360W](https://python.quectel.com/en/products/fcm360w) + + +## 功能列表 + +### 基本概述 + +QuecPython_FCM360W-TE-B_Vx.x开发板是专门针对QuecPython设计的,是一款小巧便携的“口袋型”开发板。开发板搭载Type-C接口,开发者仅需一条USB Type-C 数据线即可轻松玩转开发板。 + +### 功能说明 + +开发板的主要组件、接口布局见下图 + +![img](../media/evb/fcm360w-evb/peripheral-interfaces.png) + +## 资料下载 + +- [FCM360W系列模组产品规格书](https://images.quectel.com/python/2023/06/Quectel_FCM360W_Wi-FiBluetooth_%E6%A8%A1%E5%9D%97%E4%BA%A7%E5%93%81%E8%A7%84%E6%A0%BC%E4%B9%A6_V1.0.pdf) + +- [FCM360W系列模组封装](https://images.quectel.com/python/2023/06/Quectel_FCM360W_FootprintPart_V1.0.zip) + +- [FCM360W系列模组3维PCB封装](https://images.quectel.com/python/2023/06/Quectel_FCM360W_3D_Dimensions_V1.0.zip) + +- [FCM360W系列模组2维PCB封装](https://images.quectel.com/python/2023/06/Quectel_FCM360W_2D_Dimensions_V1.0.zip) + + +## 模组资源 + +### 开发板接口 + +**J1排针管脚分配表** + +| **排针** | **编号** | **名称** | **引脚** | **功能** | +| -------- | -------- | -------- | -------- | -------- | +| J1 | 1 | VCC_3V3 | - | 3.3V | +| J1 | 2 | MAIN_RXD | 26 | UART0 | +| J1 | 3 | MAIN_TXD | 27 | UART0 | +| J1 | 4 | GND | - | 接地 | +| J1 | 5 | CEN | 5 | 模组使能 | +| J1 | 6 | IO22 | 13 | GPIO22 | +| J1 | 7 | IO21 | 12 | GPIO21 | +| J1 | 8 | IO16 | 10 | GPIO16 | +| J1 | 9 | IO4 | 9 | GPIO4 | +| J1 | 10 | IO14 | 8 | GPIO14 | +| J1 | 11 | IO15 | 7 | GPIO15 | +| J1 | 12 | IO20 | 6 | GPIO20 | + +**J2排针管脚分配表** + +| **排针** | **编号** | **名称** | **引脚** | **功能** | +| -------- | -------- | -------- | -------- | ---------------- | +| J2 | 1 | IO25 | 16 | GPIO25 | +| J2 | 2 | IO24 | 15 | GPIO24 | +| J2 | 3 | IO23 | 14 | GPIO23 | +| J2 | 4 | IO17 | 19 | GPIO17 | +| J2 | 5 | IO13 | 20 | GPIO13 | +| J2 | 6 | IO1 | 21 | GPIO1 | +| J2 | 7 | IO0 | 22 | GPIO0 | +| J2 | 8 | IO3 | 23 | GPIO3 | +| J2 | 9 | IO2 | 29 | GPIO2 | +| J2 | 10 | RESET | 11 | 模组复位 | +| J2 | 11 | BOOT | 17 | 模组启动方式选择 | +| J2 | 12 | GND | - | 接地 | + +开发板主要管脚布局见下图 + +![img](../media/evb/fcm360w-evb/pin-layout.png) + +| 小提示 | +| ------------------------------------------------------------ | +| 开发板的更多资料,请访问 | + + + +### 开发板配置 + +外设资源管脚分配表明细如下: + +| 序号 | 名称 | 型号 | 是否支持 | 接口类型 | 引脚 | +| ---- | --------- | ------ | -------- | -------- | ----- | +| 1 | USB转串口 | CH340N | 是 | USB | 26,27 | +| 2 | 按键 | - | 是 | GPIO | 15,16 | + + +## 上手准备 + +> 首先需要有一台运行有 Windows 10 以上 操作系统的电脑 + +- **Step1:开发板连接** + +使用USB Type-C数据线连接开发板的Type-C接口和电脑USB口即可完成供电 + +- **Step2:开发板开机** + +上电后会自动开机,等待主板上电源指示灯亮(主板上丝印为POWER LED的灯) + +**执行以上操作后POW灯常亮即开机成功** \ No newline at end of file diff --git a/docs/Getting_started/zh/fota/README.md b/docs/Getting_started/zh/fota/README.md deleted file mode 100644 index 7045a4ecef0949853a3a59b46838261e569449bf..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/fota/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# 11: QuecPython FOTA应用 - -本章主要介绍 QuecPython FOTA应用,包含脚本文件升级和固件升级,具体可展开目录查看。 - -
目录 - -- [11.1 脚本文件升级](./fota-file.md) - -- [11.2 M-N-A系列模组固件升级](./fota-fw-m_n_a.md) - -- [11.3 U-G系列模组固件升级](./fota-fw-u_g.md) - -- [11.4 E系列模组固件升级](./fota-fw-e.md) - -- [11.5 BG系列模组固件升级](./fota-fw-bg.md) - - 
- diff --git a/docs/Getting_started/zh/fota/fota-file.md b/docs/Getting_started/zh/fota/fota-file.md deleted file mode 100644 index b2c112292276d0713f406261ccd39d6169dfbe95..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/fota/fota-file.md +++ /dev/null @@ -1,2 +0,0 @@ -# 脚本文件升级 - diff --git a/docs/Getting_started/zh/fota/fota-fw-bg.md b/docs/Getting_started/zh/fota/fota-fw-bg.md deleted file mode 100644 index 34dead74a26acb63c9e62dd0fc997dcef0965705..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/fota/fota-fw-bg.md +++ /dev/null @@ -1,58 +0,0 @@ -# 固件升级 - -本文主要介绍BG95&BG77&BG600L系列固件升级的操作步骤,通过本文您将了解到这些型号设备固件升级的使用方法。 - -## 制作升级包 - -特定的制作工具,该工具不开放。客户可向Quectel申请制作对应版本的升级包。 - -## 上传升级包文件放到服务器 - -将升级包文件上传到服务器,本次实验下载地址为: - -URL_BG95="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/FotaFile_BG95.bin" - -## 示例代码 - -```Python -import fota - -URL_BG95="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/FotaFile_BG95.bin" - -def result(args): - print('download status:', args[0], 'download process:', args[1]) - -def run(): - fota_obj = fota() - print("fota download......") - res = fota_obj.httpDownload(url1=URL_BG95, callback=result) - if res != 0: - return - -run() -``` - -## 验证 - -将上述代码保存为fota_test.py文件,通过QPYCOM下载到模组内: - -1、点击添加按钮。 - -2、双击选中要下载的py文件。 - -![](../media/fota/package/OTA6.png) - -运行fota_test.py脚本: - -1、点击选中要执行的py脚本。 - -2、点击运行按钮运行脚本。 - -![](../media/fota/package/OTA7.png) - -等待大概几分钟,升级完成后可查询版本号: - -![](../media/fota/package/OTA11.png) - -验证升级成功! - diff --git a/docs/Getting_started/zh/fota/fota-fw-e.md b/docs/Getting_started/zh/fota/fota-fw-e.md deleted file mode 100644 index b624e1a56d9ced3207a0c4057445d05201c820b6..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/fota/fota-fw-e.md +++ /dev/null @@ -1,58 +0,0 @@ -# 固件升级 - -本文主要介绍ECx00E系列固件升级的操作步骤,通过本文您将了解到该型号设备固件升级的使用方法。 - -## 制作升级包 - -待QPYCOM工具支持后补充。 - -## 上传升级包文件放到服务器 - -将升级包文件上传到服务器,本次实验下载地址为: - -URL_EC800E="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/old_to_new.par" - -## 示例代码 - -```Python -import fota - -URL_EC800E="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/old_to_new.par" - -def result(args): - print('download status:', args[0], 'download process:', args[1]) - -def run(): - fota_obj = fota() - print("fota download......") - res = fota_obj.httpDownload(url1=URL_EC800E, callback=result) - if res != 0: - return - -run() -``` - -## 验证 - -将上述代码保存为fota_test.py文件,通过QPYCOM下载到模组内: - -1、点击添加按钮。 - -2、双击选中要下载的py文件。 - -![](../media/fota/package/OTA6.png) - -运行fota_test.py脚本: - -1、点击选中要执行的py脚本。 - -2、点击运行按钮运行脚本。 - -![](../media/fota/package/OTA7.png) - -等待大概几分钟,升级完成后可查询版本号: - -![](../media/fota/package/OTA10.png) - -验证升级成功! - diff --git a/docs/Getting_started/zh/fota/fota-fw-m_n_a.md b/docs/Getting_started/zh/fota/fota-fw-m_n_a.md deleted file mode 100644 index c14b1e194e37830411d31eb5efe67c9be3b92ddb..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/fota/fota-fw-m_n_a.md +++ /dev/null @@ -1,127 +0,0 @@ -# 固件升级 - -本文主要介绍ECx00N&EGx00N&ECx00M&EGx00M&EC200A系列固件升级的操作步骤,通过本文您将了解到这些型号设备固件升级的使用方法。 - -## 制作升级包 - - 使用QPYCOM工具制作,不同型号制作方式略有差异。这里以EC600NCNLF、EC600MCNLA、EC200A型号为例。其中EC600NCNLF、EC200A采用差分升级方式,生成1个升级包文件。EC600MCNLA采用MiniFOTA升级方式,生成2个升级包文件。 - -### EC600NCNLF - -1、点击升级按钮,选择ASR Fota。 - -2、弹出对话框,选择DFota。 - -3、选择当前版本固件。 - -4、选择目标版本固件。 - -5、选择升级包输出位置. - -6、点击OK生成升级包文件。 - -![](../media/fota/package/OTA3.png) - -### EC200A - -1、点击升级按钮,选择200A Fota,弹出对话框。 - -2、选择当前版本固件中的blf文件。 - -3、选择目标版本固件中的blf文件。 - -4、选择升级包输出位置。 - -5、点击OK生成升级包。 - -![](../media/fota/package/OTA4.png) - -### EC600MCNLA - -1、点击升级按钮,选择ASR Fota。 - -2、弹出对话框,选择Minimum system Fota。 - -3、选择当前版本固件。 - -4、选择目标版本固件。 - -5、选择文件系统bin文件,即存放用户文件的分区。需要选择合并用户文件之后的固件包中的customer_fs.bin,如何获取参照下述步骤。(可选) - -6、选择升级包输出位置。 - -7、点击OK生成升级包。 - -![](../media/fota/package/OTA1.png) - -固件包中文件系统bin文件获取方法: - -1、如果合并后的固件是.bin后缀,则需要将其手动改成.zip后缀。如果本来就是.zip后缀则跳过此步骤。 - -2、使用解压缩软件解压缩.zip后缀的固件包。 - -3、获取固件包中customer_fs.bin文件,即为文件系统bin文件。 - -![](../media/fota/package/OTA5.png) - -## 上传升级包文件放到服务器 - -将上述步骤生成的升级包文件上传到服务器,本次实验下载地址为: - -URL_EC600N="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/FotaFile_EC600N.bin" - -URL_EC200A="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/FotaFile_EC200A.bin" - -URL_EC600M_1="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/FotaFile_EC600M_1.bin" - -URL_EC600M_2="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/FotaFile_EC600M_2.bin" - -## 示例代码 - -```Python -import fota - -URL_EC600N="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/FotaFile_EC600N.bin" -URL_EC200A="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/FotaFile_EC200A.bin" -URL_EC600M_1="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/FotaFile_EC600M_1.bin" -URL_EC600M_2="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/FotaFile_EC600M_2.bin" - -def result(args): - print('download status:', args[0], 'download process:', args[1]) - -def run(): - fota_obj = fota() - print("fota download......") - res = fota_obj.httpDownload(url1=URL_EC600N, callback=result) # EC600N - #res = fota_obj.httpDownload(url1=URL_EC200A, callback=result) # EC200A - #res = fota_obj.httpDownload(url1=URL_EC600M_1, url2=URL_EC600M_2) # EC600M - if res != 0: - return - -run() -``` - -## 验证 - -将上述代码保存为fota_test.py文件,通过QPYCOM下载到模组内: - -1、点击添加按钮。 - -2、双击选中要下载的py文件。 - -![](../media/fota/package/OTA6.png) - -运行fota_test.py脚本: - -1、点击选中要执行的py脚本。 - -2、点击运行按钮运行脚本。 - -![](../media/fota/package/OTA7.png) - -等待大概几分钟,升级完成后可查询版本号: - -![](../media/fota/package/OTA8.png) - -验证升级成功! - diff --git a/docs/Getting_started/zh/fota/fota-fw-u_g.md b/docs/Getting_started/zh/fota/fota-fw-u_g.md deleted file mode 100644 index ea85893a62eb76d2bede8d7aa5cb743b9fa38795..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/fota/fota-fw-u_g.md +++ /dev/null @@ -1,70 +0,0 @@ -# 固件升级 - -本文主要介绍ECx00U&EGx00U&ECx00G系列固件升级的操作步骤,通过本文您将了解到这些型号设备固件升级的使用方法。 - -## 制作升级包 - - 使用QPYCOM工具制作,这里以EC600UCNLB型号为例。 - -1、点击升级按钮,选择Unisoc Fota,弹出对话框。 - -2、选择当前版本固件中的.pac文件。 - -3、选择目标版本固件中的.pac文件。 - -4、选择升级包输出位置。 - -5、点击OK生成升级包。 - -![](../media/fota/package/OTA2.png) - -## 上传升级包文件放到服务器 - -将上述步骤生成的升级包文件上传到服务器,本次实验下载地址为: - -URL_EC600U="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/output.pack" - -## 示例代码 - -```Python -import fota - -URL_EC600U="http://forrest-qpy-test.oss-cn-hangzhou.aliyuncs.com/output.pack" - -def result(args): - print('download status:', args[0], 'download process:', args[1]) - -def run(): - fota_obj = fota() - print("fota download......") - res = fota_obj.httpDownload(url1=URL_EC600U, callback=result) - if res != 0: - return - -run() -``` - -## 验证 - -将上述代码保存为fota_test.py文件,通过QPYCOM下载到模组内: - -1、点击添加按钮。 - -2、双击选中要下载的py文件。 - -![](../media/fota/package/OTA6.png) - -运行fota_test.py脚本: - -1、点击选中要执行的py脚本。 - -2、点击运行按钮运行脚本。 - -![](../media/fota/package/OTA7.png) - -等待大概几分钟,升级完成后可查询版本号: - -![](../media/fota/package/OTA9.png) - -验证升级成功! - diff --git a/docs/Getting_started/zh/fota/ota.md b/docs/Getting_started/zh/fota/ota.md deleted file mode 100644 index d6f845d481b9cb435d11509a380057c0c27634e6..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/fota/ota.md +++ /dev/null @@ -1,255 +0,0 @@ -# OTA升级指导说明 - -本文主要介绍基于QuecPython模组的固件升级与软件升级方法, 描述了`fota`与`app_fota`模块的功能如何使用进行设备升级, 以及一些注意事项。 - -QuecPython模组的固件升级与软件升级支持不同平台, 本文以阿里云物联网平台为例介绍如何进行OTA升级操作, 后续会更新其他平台的OTA操作流程。 - -## 制作升级包 - -### 固件升级包 - -设备固件升级建议使用差分升级, 不建议使用全量升级, 目前只有 **EC600NCNLC/LF** 支持全量升级, 其他型号的模组只支持差分升级, 差分升级包制作方法请联系移远售后客服。 - -### 软件升级包 - -软件直接使用Python脚本进行升级即可, 无需进行特殊转换。 - -## 阿里云物联网平台 - -以下为阿里云物联网平台创建OTA升级计划的操作步骤说明, 阿里云物联网平台OTA升级功能详细说明, 可参考官网文档[阿里云物联网平台-运维监控-OTA升级](https://help.aliyun.com/document_detail/130990.html?spm=a2c4g.58328.0.0.1f0e3ed6i3ApkS) - -### 创建OTA升级计划 - -同一产品可以根据升级的功能模块的不同, 创建不同升级模块名称, 通常分为两类 - -- 固件模块, 又称为FOTA升级, 建议以模组的版本命名, 如: EC800E-CNLC -- 软件升级(Python脚本升级), 又称为SOTA升级, 可自行命名, 如: QuecPython-XXX - -### 创建FOTA(固件)升级计划 - -#### 添加升级模块 - -![](../media/fota/aliyun/aliyun-fota-create-module.png) - -#### 添加升级包 - -![](../media/fota/aliyun/aliyun-fota-create-package.png) - -![](../media/fota/aliyun/aliyun-fota-create-package-info.png) - -#### 创建升级计划 - -![](../media/fota/aliyun/aliyun-fota-create-plan.png) - -![](../media/fota/aliyun/aliyun-fota-create-plan-info-1.png) - -![](../media/fota/aliyun/aliyun-fota-create-plan-info-2.png) - -#### 查看升级结果 - -![](../media/fota/aliyun/aliyun-fota-create-plan-over.png) - -![](../media/fota/aliyun/aliyun-fota-plan-result-view.png) - -![](../media/fota/aliyun/aliyun-fota-plan-result-batches.png) - -![](../media/fota/aliyun/aliyun-fota-plan-result-devices.png) - -### 创建SOTA(软件)升级计划 - -#### 添加升级模块 - -![](../media/fota/aliyun/aliyun-sota-create-module.png) - -#### 添加升级包 - -**注意:** *阿里云使用Python脚本创建升级包, 需要将脚本后缀名`.py`改为`.bin`之后才可上传, 可以上传多个文件* - -![](../media/fota/aliyun/aliyun-fota-create-package.png) - -![](../media/fota/aliyun/aliyun-sota-create-package-info.png) - -#### 创建升级计划 - -![](../media/fota/aliyun/aliyun-sota-create-plan.png) - -![](../media/fota/aliyun/aliyun-sota-create-plan-info-1.png) - -![](../media/fota/aliyun/aliyun-sota-create-plan-info-2.png) - -![](../media/fota/aliyun/aliyun-sota-create-plan-over.png) - -#### 查看升级结果 - -![](../media/fota/aliyun/aliyun-sota-plan-result-view.png) - -![](../media/fota/aliyun/aliyun-sota-plan-result-batches.png) - -![](../media/fota/aliyun/aliyun-sota-plan-result-devices.png) - -### 设备端升级功能开发 - -阿里云物联网平台对接可以使用我们现成的`aliYun`功能模块进行开发即可, 该模块基于MQTT协议进行通信, 无需自行封装阿里云的功能接口。 - -以下示例中用到的相关模块功能可在[QuecPython API 参考手册](https://python.quectel.com/doc/API_reference/zh/index.html)中查看详细说明。 - -阿里云MQTT协议下发的OTA升级数据格式与说明, 详见[阿里云物联网平台-基于Alink协议开发-OTA升级](https://help.aliyun.com/document_detail/89307.html?spm=a2c4g.89307.0.0.12272110cm6wc6) - -**示例:** - -```python -import uos -import fota -import app_fota -import modem -import ujson -from misc import Power -from aliYun import aLiYun - -# 定义软件名称 -PROJECT_NAME = "QuecPython-XXX" -# 定义软件版本 -PROJECT_VERSION = "1.0.0" -# 获取固件型号 -FIRMWARE_NAME = uos.uname()[0].split("=")[1] -# 获取固件版本号 -FIRMWARE_VERSION = modem.getDevFwVersion() - -# 初始化aLiYun功能 -ProductKey = "xxx" -ProductSecret = "xxx" -DeviceName = "xxx" -DeviceSecret = "xxx" -MqttServer = "xxx" -cloud = aLiYun(ProductKey, ProductSecret, DeviceName, DeviceSecret, MqttServer) - -# 初始化OTA相关Topic -# 设备模块版本信息上报topic -ota_topic_device_inform = "/ota/device/inform/%s/%s" % (ProductKey, DeviceName) -# 设备OTA升级计划下发topic -ota_topic_device_upgrade = "/ota/device/upgrade/%s/%s" % (ProductKey, DeviceName) -# 设备升级进度上报topic -ota_topic_device_progress = "/ota/device/progress/%s/%s" % (ProductKey, DeviceName) -# 设备OTA升级计划查询topic -ota_topic_firmware_get = "/sys/%s/%s/thing/ota/firmware/get" % (ProductKey, DeviceName) -# 设备OTA升级计划查询应答topic -ota_topic_firmware_get_reply = "/sys/%s/%s/thing/ota/firmware/get_reply" % (ProductKey, DeviceName) - -# 设置MQTT连接 -client_id = modem.getDevImei() -clean_session = True -cloud.setMqtt(client_id, clean_session) - - -ota_module = None -# 订阅Topic回调函数 -def sub_cb(topic, data): - if topic in (ota_topic_device_upgrade, ota_topic_firmware_get_reply): - # OTA升级 - global ota_module - data = ujson.loads(data) - ota_module = data["module"] - ota_version = data["version"] - if ota_module == FIRMWARE_NAME: - # FOTA升级 - _fota = fota() - _fota.httpDownload(url1=data.get("url"), callback=fota_callback) - elif ota_module == PROJECT_NAME: - # SOTA升级 - ota_data = [{"url": i["fileUrl"], "filename": "/usr/" + i["fileName"].replace(".bin", ".py")} for i in data.get("files", [])] - _app_fota = app_fota.new() - res = _app_fota.bulk_download(ota_data) - # SOTA上报升级结果 - ota_process = 100 if not res else -1 - if ota_process == 100: - _app_fota.set_update_flag() - process_data = { - "id": 5, - "params": { - "step": ota_process, - "desc": "desc", - "module": ota_module, - } - } - cloud.publish(ota_topic_device_progress, ujson.dumps(process_data), qos=1) - # 升级完成之后需要重启设备 - Power.powerRestart() - - - -# FOTA升级进度回调函数 -def fota_callback(args): - print("Download status: %s, Download process: %s" % tuple(args)) - ota_process = None - if args[0] in (0, 1, 2) and args[1] == 100: - ota_process = 100 - else: - ota_process = -1 - if ota_process is not None: - # FOTA上报升级结果 - process_data = { - "id": 5, - "params": { - "step": ota_process, - "desc": "success", - "module": ota_module, - } - } - cloud.publish(ota_topic_device_progress, ujson.dumps(process_data), qos=1) - # 升级成功后会自动重启, 此处可无需手动重启 - Power.powerRestart() - - -# 设置订阅Topic回调函数 -cloud.setCallBack(sub_cb) - -# 订阅Topic -qos = 1 -cloud.subscribe(ota_topic_device_upgrade, qos) -cloud.subscribe(ota_topic_firmware_get_reply￿, qos) - -# 阿里云功能启动 -cloud.start() - -# 上报软件版本信息 -sota_data = { - "id": 1, - "params": { - "version": PROJECT_VERSION, - "module": PROJECT_NAME, - } -} -cloud.publish(ota_topic_device_inform, ujson.dumps(sota_data), qos=1) - -# 上报固件版本信息 -fota_data = { - "id": 2, - "params": { - "version": FIRMWARE_VERSION, - "module": FIRMWARE_NAME, - } -} -cloud.publish(ota_topic_device_inform, ujson.dumps(fota_data), qos=1) - -# 查询软件升级计划 -sota_query = { - "id": 3, - "version": "1.0", - "params": { - "module": PROJECT_NAME, - }, - "method": "thing.ota.firmware.get" -} -cloud.publish(ota_topic_firmware_get, ujson.dumps(sota_query), qos=1) - -# 查询固件升级计划 -sota_query = { - "id": 4, - "version": "1.0", - "params": { - "module": FIRMWARE_NAME, - }, - "method": "thing.ota.firmware.get" -} -cloud.publish(ota_topic_firmware_get, ujson.dumps(sota_query), qos=1) -``` diff --git a/docs/Getting_started/zh/hardware/Audio_and_TTS.md b/docs/Getting_started/zh/hardware/Audio_and_TTS.md deleted file mode 100644 index 5a22516ba769ec9e671e283ba85825383bca4765..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/Audio_and_TTS.md +++ /dev/null @@ -1,183 +0,0 @@ - -# 音频和TTS - -文档主要基于EC600N介绍如何使用QuecPython来进行Audio、Record、TTS功能使用,**其他模组操作类同**。Audio:音频播放功能,支持mp3、amr和wav格式音频文件及音频流播放;Record:录音功能;TTS:Text-To-Speech 即文本到语音播放。 - -## 如何使用音频和TTS功能 - -### 硬件准备 -在使用Audio、Record、TTS功能之前,需要准备以下硬件: -- 开发板 -- 电话听筒 - -目前音频输出通道最多支持3种:听筒、耳机、喇叭。不同模块支持输出通道可能不同,具体模块所支持通道详见[Audio](../../../API_reference/zh/peripherals/audio.Audio.md)。目前开发板硬件只引出了听筒这一路,只是简单测试功能已经完全够用。开发板与听筒连接方式见下图: - - - - -### 软件设计 - -软件相关API介绍参考链接:[Audio](../../../API_reference/zh/peripherals/audio.Audio.md) - -### 示例代码 - -#### Audio -```python -# -*- coding: UTF-8 -*- -import audio -from machine import Pin -import utime - -def audio_cb(event): - if event == 0: - print('audio-play start.') - elif event == 7: - print('audio-play finish.') - -aud = audio.Audio(0) -aud.setCallback(audio_cb) -# 设置pa -aud.set_pa(Pin.GPIO15,2) -# 播放MP3 -aud.play(2, 1, 'U:/music.mp3') -# 等待播放完成 -utime.sleep_ms(5000) -aud.stop() - -``` - -#### Record -```python -# -*- coding: UTF-8 -*- -import utime -import audio -from machine import Pin - - -flag = 1 -''' -使用听筒播放录音文件,参数选择0 -''' -aud = audio.Audio(0) -tts = audio.TTS(0) - -aud.setVolume(11) - -def record_callback(args): - global flag - print('file_name:{}'.format(args[0])) - print('file_size:{}'.format(args[1])) - print('record_sta:{}'.format(args[2])) - - record_sta = args[2] - if record_sta == 3: - print('The recording is over, play it') - tts.play(1, 0, 2, '录音结束,准备播放录音文件') - aud.play(1, 0, record.getFilePath('recordfile.wav')) - flag = 0 - elif record_sta == -1: - print('The recording failure.') - tts.play(1, 0, 2, '录音失败') - flag = 0 - -record = audio.Record() -record.end_callback(record_callback) -record.start('recordfile.wav', 10) - -while 1: - if flag: - utime.sleep_ms(200) - else: - break - -``` - - -#### TTS -```python -# -*- coding: UTF-8 -*- -''' -@Description: example for class TTS -@FilePath: example_tts_file.py -''' -import log -from audio import TTS -import utime - - -''' -下面两个全局变量是必须有的,用户可以根据自己的实际项目修改下面两个全局变量的值 -''' -PROJECT_NAME = "QuecPython_TTS_example" -PROJECT_VERSION = "1.0.0" - -# 设置日志输出级别 -log.basicConfig(level=log.INFO) -tts_Log = log.getLogger("TTS") - -#定义回调函数 -def UsrFunc(event): - if event == 2: - print("开始播放") - elif event == 3: - print("停止播放") - elif event == 4: - print("播放完成") - -if __name__ == '__main__': - # 参数1:device (0:听筒,1:耳机,2:喇叭) - tts = TTS(0) - - #注册用户回调函数 - tts.setCallback(UsrFunc) - - # 获取当前播放音量大小 - volume_num = tts.getVolume() - tts_Log.info("Current TTS volume is %d" %volume_num) - - # 设置音量为6 - volume_num = 6 - tts.setVolume(volume_num) - - # 参数1:优先级 (0-4) - # 参数2:打断模式,0表示不允许被打断,1表示允许被打断 - # 参数3:模式 低四位:(1:UNICODE16(Size end conversion) 2:UTF-8 3:UNICODE16(Don't convert)),高四位:wtts_enable,wtts_ul_enable, wtts_dl_enable - # 参数4:数据字符串 (待播放字符串) - tts.play(1, 1, 2, 'QuecPython') # 执行播放 - tts.play(1,1,tts.wtts_enable|tts.wtts_ul_enable|2, '12345') - tts.close() # 关闭TTS功能 - -``` - -### 运行效果 -#### Audio -1. 将上面的Audio的示例代码保存在audio_test.py文件中保存,打开QPYcom导入audio_test.py、music.mp3。运行audio_test.py,如下图: - - - - -2. 在听筒中可听到music.mp3内容,QPYcom交互界面查看输出结果如下: - - - -#### Record -1. 将上面的Record的示例代码保存在record_test.py文件中保存,打开QPYcom导入record_test.py。运行record_test.py,如下图: - - - - -2. QPYcom交互界面查看输出结果如下,当录音结束后会自动播放录音: - - - -#### TTS -1. 将上面的TTS的示例代码保存在tts_test.py文件中保存,打开QPYcom导入tts_test.py。运行tts_test.py,如下图: - - - - -1. 在听筒中可听到文本的读音,QPYcom交互界面查看输出结果如下: - - - - diff --git a/docs/Getting_started/zh/hardware/LCD display.md b/docs/Getting_started/zh/hardware/LCD display.md deleted file mode 100644 index fa1c60d57b261be0a3565b6022dbd95eaba56d92..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/LCD display.md +++ /dev/null @@ -1,89 +0,0 @@ -# 屏幕显示 - -本章节介绍使用QuecPython进行屏幕点亮显示,目前QuecPython开发支持的屏幕接口有SPI、MIPI、I2C(段码屏)等,具体模组支持的接口详情在下表8-1所示;下文以QuecPython开发常用的LCD进行开发介绍。 - -| 模组型号 | LCD接口 | -| --------------------------- | -------------- | -| EC200U\EC600U | MIPI、SPI、I2C | -| EC600N\EC600M\EC800M\EC800M | SPI、I2C | - -表8-1 - -## LCD介绍 - -LCD(Liquid Crystal Display),液晶显示器,为平面超薄的显示设备,它由一定数量的彩色或者黑白像素组成,放置于光源或者反射面前方。它的主要原理是以电流刺激液晶分子产生点、线、面配合背部灯管构成画面。 - -QuecPYthon开发常中,LCD的几个重要的参数有驱动IC、屏幕尺寸以及分辨率等,在选型中,可根据模组支持的接口去选择对应的LCD。目前公版固件中,SPI接口的LCD最大分辨率支持240×320,MIPI接口支持最大的分辨率为480×854(开发板“铀”)。 - -ST7789V LCD为官方天猫旗舰店售卖的一款屏幕,可搭配QuecPython开发板进行开发,该屏幕的驱动IC为ST7789V,SPI接口,分辨率为240×240,接口定义如下表8-2所示。 - -| 序号 | LCD引脚 | 引脚定义 | -| ---- | ------- | ------------------------------ | -| 1 | GND | 电源地 | -| 2 | VCC | 电源3.3V | -| 3 | SCL | SPI总线时钟信号 | -| 4 | SDA | SPI总线写数据信号 | -| 5 | RES | 液晶屏复位控制信号,低电平复位 | -| 6 | DC | 写寄存器/写数据控制信号 | -| 7 | BLK | 液晶屏背光控制信号 | - -表8-2 - -## 硬件连接 - -### SPI类型的屏幕 - -模组有LCD显示驱动的接口LCM(LCD Module),可用于SPI LCD的屏幕,当然也可以选择模组其他SPI接口。具体的硬件连接可参考下表,需要注意的是,模组引脚的电压域为1.8V,需经过电平转换之后方可使用。 - -| ST7789V LCD | 模组LCM接口 | 模组SPI接口 | -| :---------- | ------------ | ----------- | -| SCL | LCD_SPI_CLK | SPI_SCLK | -| SDA | LCD_SPI_DOUT | SPI_MOSI | -| RES | LCD_RST | gpio复用 | -| DC | LCD_SPI_RS | gpio复用 | -| | LCD_CS | SPI_CS | - -### MIPI类型的屏幕 - -目前支持MIPI的模组有EC200U/EC600U系列,需要搭配6线FLASH使用,可参考QuecPython开发板“铀“的电路,具体硬件连接请联系移远技术支持进行确认。 - -## 驱动代码编写 - -选择一款屏幕后正常情况下屏幕厂商会提供C的驱动代码,需要将C代码转成python脚本,下面介绍部分参数的格式,详情文档看《基于QUECPYTHON实现SPI类型屏幕驱动》、《基于QuecPython-MIPI大屏使用指导》。 - -SPI类型格式参数如下: - -type + len + value - -type:0:cmd; 1:data; 2:delay - -len: 若 type 为 cmd: len 表示后面接多少个 data ;若 type 为 data: len 表示 data 的长度 ;若 type 为 delay: len 无实际意义。为 0 即可 - -value: 若 type 为 delay:表示延时的时长,单位为 ms。 - -MIPI类型格式参数如下: - -cmd + delay_ms + data_len + data_list - -cmd: 命令 - -delay_ms:延时多久,单位为ms - -data_len: 对应命令后接多个数据 - -data_list:实际数据 - -## 实验测试 - -用开发板连接LCD屏,插入USB上电后能看到LCD屏幕有背光亮起,但此时的LCD还未进行初始化,无法进行其他显示操作,需执行初始化程序之后就能进行图片文字显示已经gui显示等。 - -执行初始化之前 - -![](E:\QuecPython网站搬移\V3\teedoc_with_qpydoc_1\docs\Getting_started\zh\media\hardware-advanced\lcd_display\lcd_display_1.jpg) - -执行初始化之后 - -![](E:\QuecPython网站搬移\V3\teedoc_with_qpydoc_1\docs\Getting_started\zh\media\hardware-advanced\lcd_display\lcd_display_2.jpg) - - - diff --git a/docs/Getting_started/zh/hardware/README.md b/docs/Getting_started/zh/hardware/README.md deleted file mode 100644 index db2ab206fb0a2b2ccf52c33ab06982e890950af7..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# 5. 硬件功能 - -
目录 - - -- [8.1 屏幕显示]() -- [8.2 LVGL]() -- [8.3 摄像头]() -- [8.4 低功耗]() -- [8.5 音频和 TTS]() -- [8.6 外接存]() -- [8.7 以太网卡]() -- [8.8 矩阵键盘]() -- [8.9 BT 和 BLE](./bt/README.md) -- [8.10 USB 网卡]() -- [8.10 USB 网卡]() -- [8.11 外接 WiFi]() - - 
\ No newline at end of file diff --git a/docs/Getting_started/zh/hardware/adc.md b/docs/Getting_started/zh/hardware/adc.md deleted file mode 100644 index 089dd75e02a54892e84048a60e9c3428211ff481..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/adc.md +++ /dev/null @@ -1,140 +0,0 @@ -# ADC - 电压采集功能 - -本文主要介绍什么是 ADC、怎么使用 ADC、ADC 可以做什么等相关问题。 - -## ADC 简介 - -### 什么是 ADC - -ADC(Analog to Digital Converter) 即模拟数字转换器即 [A/D 转换器](https://baike.baidu.com/item/A%2FD转换器/4883848?fromModule=lemma_inlink),对于嵌入式应用开发来说,可以简单理解为:在单片机或我们的模组上有一个引脚可以读取电路中的电压值。一般软件开发人员主要关注如何读出 ADC 引脚的电压值,并将电压值做一些运算从而间接得出外部传感器当前所测量的环境值,如温度值、亮度值等。 - -### 怎么使用 ADC - -#### 硬件设计 - -对于嵌入式产品开发来说,可靠的硬件电路是软件开发的前提条件。 - -ADC 相关电路设计需要参考每个型号模组的硬件设计手册和参考设计手册,可以在主页的 [下载区](/download/) 检索下载,但具体功能引脚仍需以 QuecPython 的 [ADC 功能 API](../../../API_reference/zh/peripherals/misc.ADC.md) 介绍的引脚号为准。关于 ADC 功能常见的应用电路也可以通过搜素引擎了解更多信息。简单应用电路也可参考 QuecPython 的学习开发板,开发板可以通过天猫旗舰店、京东旗舰店等移远通信官方销售渠道获取,开发板的硬件设计同样是参考的上述硬件文档,开发板的原理图也可以在主页的 [下载区](/download/) 检索下载。 - -#### 软件应用 - -使用 QuecPython 开发的模组,想要读取 ADC 引脚的电压需要先完成 QuecPython 开发环境的搭建,再参考 [ADC 功能的 API 介绍文档](../../../../API_reference/zh/peripherals/misc.ADC.md) 进行如下命令行测试即可打印出 ADC 通道 0 引脚当前的电压值,单位为毫安 (mA)。 - -```python ->>>from misc import ADC ->>>adc = ADC() ->>>adc.open() ->>>voltage = adc.read(ADC.ADC0) ->>>print("ADC channel 0 voltage = {}mA".format(voltage)) -``` - -### ADC 功能测试 - -使用 QPYcom 工具和模组进行交互,下面实例是基于 ADC0。 - - - -## ADC 功能应用实例 - -亮度传感器是一种能够检测周围环境亮度的传感器。在 QuecPython 平台上,您可以使用光敏电阻或光敏二极管作为亮度传感器,并使用相应的代码来读取其值。在本文档中,我们将讨论如何在 QuecPython 平台上使用光敏电阻作为亮度传感器。 - -### 所需材料 - -- QuecPython 模块,选择支持 ADC 的模块即可 -- 光敏电阻 -- 高精度电阻(具体以硬件设计手册为准) -- 充足的导线(杜邦线即可) -- 如有自带光敏电阻的 QuecPython 开发板上述材料均包含 - -### 电路连接 - -以 QuecPython 开发板为例,以下是 EC600X_QuecPython_EVB_V3.1 版本开发板光敏电阻电路连接图: - -
- -如上两图所示,左边图中的 R22 即为光敏电阻,型号为 GT36528,右图为连接到 QuecPython 模块 ADC 引脚的电路,模块型号为 EC600U。 - -上图电路可等效为下图所示电路: - - - -### 原理分析 - -想要得到当前光照度,首先我们需要知道光敏电阻在当前光照条件下的电阻值,再根据电阻值与光照度之间的关系计算得出光照度,电阻值我们不能直接通过 API 接口读出,但我们可以使用 ADC 的 API 读出电路中光敏电阻的电压值,再通过欧姆定律我们就可以计算得出光敏电阻的阻值,又由于 ADC 的测量电压范围有限,所以我们设计了电子电路进行分压,将电路中某一点的电压限制在 ADC 的测量范围内,再测量电路中这一点的电压,间接换算出光敏电阻的阻值,具体分析和计算原理如下。 - -如上图所示等效电路, R3 和 R4 为串联关系,R22 与 R3、R4 组合的整体为并联关系,R14 与 R22、R3、R4 组成的整体为串联关系,根据欧姆定律及并联分流的特点我们可以得出,A 点的电流为通过 R22 支路的电流和通过 R3、R4 支路的电流之和,则可以列出如下表达式。 - -$$ -\frac{V_{3.3}-V_A}{R_{14}}=\frac{V_A}{R_{22}}+\frac{V_A}{R_{3}+R_{4}} -$$ - -再经过换算我们可以得出光敏电阻 R22 的数学表达式如下: - -$$ -R_{22}=\frac{(R_3+R_4)R_{14}V_{A}}{(R_3+R_4)(V_{3.3}-V_A)-(R_{14}V_A)} -$$ - -到这里,也许有些人会有疑问,R81 为什么没有被计算到?因为 R81 接到的是 ADC 引脚,我们可以认为只是接到了一个电压表上,所以并不会分压,无需加入计算中。即 ADC0 测得的电压即为 B 点的电压,那么 B 点电压对于我们来说是可以通过 API 变为已知的。但是在上述表达式中又并未用到 B 点电压,是怎么回事呢? - -从上述表达式中我们可以看出,表达式右侧的所有变量只有 A 点电压是未知量,左侧是待求量,一个表达式中有两个未知量是求不出结果的,所以我们需要将未知量 A 点电压变为已知量,此时就用到了已知量 B 点电压。我们再根据欧姆定律和串并联电路的知识可以知道,R22 所在支路的电压和 R3、R4 所在支路的电压是相同的,并且是 A 点电压。又由于整个支路是串联的,则支路上每一点电流是相同的,我们根据欧姆定律可以得出如下表达式: - -$$ -\frac{V_A}{R_3+R_4}=\frac{V_B}{R_4} -$$ - -由上述表达式我们可以换算得出 A 点电压和 B 点电压的关系: - -$$ -V_A =\frac{R_3+R_4}{R4}V_B -$$ - -将上述表达式整理再统一单位后代入真实数值我们可以得到表达式: - -$$ -V_A = 5.02V_B -$$ - -再将这一结果和真实数值代入 R22 的表达式可以得到如下表达式: - -
- -$$ -\begin{aligned} -R_{22}&=\frac{(R_3+R_4)R_{14}V_{A}}{(R_3+R_4)(V_{3.3}-V_A)-(R_{14}V_A)}\\ -&=\frac{((40.2+10)*1000)* 40.2 *1000* 5.02 *V_B}{((40.2+10)* 1000)*(3.3-5.02* V_B)-(40.2 *1000* 5.02*V_B)}\\ -&=\frac{10130560800 *V_B}{165660-453808* V_B} -\end{aligned} -$$ - -### 代码实现 - -在写代码之前我们需要了解到光敏电阻阻值和光照亮度之间的数学关系,一般需要通过实验测试得到,此示例仅展示我们如何获取光敏电阻的阻值,单位为欧姆,在代码中计算时一定记得统一为国际单位制再进行计算。 - -```python -from misc import ADC -import utime - -class lightSensor(): - def __init__(self, ADC_channel): - self.adc = ADC() - self.adc_channel = ADC_channel - self.adc.open() - - def readResistanceValue(self): - Vb = self.adc.read(self.adc_channel)/1000 - R22 = (10130560800*Vb) / (165660-(453808*Vb)) - return R22 - -if __name__ == "__main__": - - gt36528 = lightSensor(ADC.ADC0) - - while True: - resistanceValue = gt36528.readResistanceValue() - print("resistance value = {} Ohm".format(resistanceValue)) - utime.sleep(1) -``` - -## 总结 - -ADC 功能的一般常见用法在此做了详细的介绍,如有疑问或更好的建议欢迎联系我们,也可以直接向我们提交文档贡献,后续本文将继续完善和补充更多应用案例。 diff --git a/docs/Getting_started/zh/hardware/bt-and-ble.md b/docs/Getting_started/zh/hardware/bt-and-ble.md deleted file mode 100644 index 5effcf73515b40baf9599eccfe892249ef9883ff..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/bt-and-ble.md +++ /dev/null @@ -1,28 +0,0 @@ -# BT和BLE - -本文将主要介绍支持QuecPython开发的模块自带的[BT(经典蓝牙)](../../../API_reference/zh/wifibtgnss/bt.md)和[BLE(低功耗蓝牙)](../../../API_reference/zh/wifibtgnss/ble.md)是什么,怎么使用等基础知识和如何获取学习资料。 - -## BT和BLE分别是什么? - -经典蓝牙(BT)是一种短距离无线通信技术,广泛应用于音频设备、手机、电脑、车载系统等设备之间的数据传输。经典蓝牙的传输速度较快,可达到2.1Mbps,但功耗较高,适用于数据传输频繁的场景。 - -低功耗蓝牙(BLE)是一种低功耗的无线通信技术,适用于物联网、健康监测、智能家居等设备的连接。BLE的传输速度较慢,最高可达1Mbps,但功耗非常低,可实现长期待机和低功耗数据传输。 - -## BT和BLE的主要区别是什么? - -两者的主要区别在于功耗和传输速度。经典蓝牙速度快,但功耗大,而BLE功耗低,但速度慢。另外,BLE的连接距离比经典蓝牙更远,但传输速度也更慢。 - -## BT和BLE的主要应用场景有什么不同? - -经典蓝牙适用于需要高速数据传输的场景,如音频传输、文件传输等。BLE适用于需要低功耗和长期待机的场景,如智能家居、健康监测、物联网等。 - -## 怎么使用BT和BLE? - -关于如何使用BLE本文主要介绍如何进行基本的测试,详细资料仍需要参考对应的API文档:[BT(经典蓝牙)](../../../API_reference/zh/wifibtgnss/bt.m)和[BLE(低功耗蓝牙)](../../../API_reference/zh/wifibtgnss/ble.md),以及对应的[蓝牙应用指导文档](),[蓝牙官网文档](https://www.bluetooth.com/bluetooth-resources/?types=study-guide)等。 - -### 1.准备工作 - -在使用蓝牙功能之前请先确认您使用的固件发布时间是最新的,并且确认固件中包含BT或BLE功能,可以通过直接import尝试,如报错则不支持。需要注意,不支持的主要原因为,蓝牙协议栈占用较多资源,确需使用才会编译进固件,避免过多占用资源,导致其他常用功能无法支持。确需使用可以在下载区下载 - - - diff --git a/docs/Getting_started/zh/hardware/bt/README.md b/docs/Getting_started/zh/hardware/bt/README.md deleted file mode 100644 index 70cd16761903c43c79c29af38c33b5c3b9d4e45c..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/bt/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# 蓝牙功能使用说明 - -本文主要介绍QuecPython功能组件中的蓝牙功能,包括蓝牙基础概念、蓝牙功能的使用说明等。 - -## 概述 - -蓝牙技术是一种无线通信技术,可以让设备之间通过短距离的无线信号实现相互通信。如今蓝牙技术广泛应用各种电子设备,比如智能手机、蓝牙耳机、车载系统、音响等等。 - -目前,蓝牙技术主要有两种:经典蓝牙(BT)和低功耗蓝牙(BLE)。经典蓝牙技术是最早被开发出来,因此现在也经常会将经典蓝牙称为传统蓝牙。低功耗蓝牙则是一种相对较新的技术,设计初衷就是为了用于低功耗设备之间的通信,如智能手表、手环等设备。 - - - -## BT和BLE的区别 - -BT和BLE虽然都是蓝牙技术,但是两者在使用场景、功耗、传输速率等方面还是存在较大区别的。 - -| | BT | BLE | -| -------- | ------------------------------------------------------ | ------------------------------------------------------------ | -| 使用场景 | BT主要用于大数据量传输,如音乐、视频、图像文件等传输。 | BLE主要用于小数据量传输,常用于可穿戴设备,例如智能手环上,用于传输人体健康数据等。 | -| 传输速率 | BT传输速率较高,最高可以达到24Mbps。 | BLE传输速度相对较低,最高可达2Mbps。 | -| 功耗 | BT功耗较高。 | BLE功耗较低,适合需要长时间运行的设备,如智能手表、手环等。 | - - - -> 关于蓝牙的传输速率说明 -> -> 不同蓝牙协议版本的传输速率是不同的,比如: -> -> * 蓝牙1.x版本:最高传输速率为1 Mbps。 -> * 蓝牙2.x + EDR版本:最高传输速率为3 Mbps。 -> * 蓝牙3.x + HS版本:通过与802.11 Wi-Fi协议结合,理论上最高传输速率可以达到24 Mbps。 -> * 蓝牙4.x版本:该版本引入了低功耗蓝牙技术。BT模式下最高传输速率为3 Mbps,BLE模式下最高传输速率约为1 Mbps。 -> * 蓝牙5.x版本:最高传输速率为2 Mbps。 -> -> 需要注意,上述最高传输速率都是理论值,实际中受设备硬件限制、距离、环境干扰等因素影响,传输速率一般都是低于理论速度。 - - - -## 常见协议 - -蓝牙技术中包含了很多不同的协议和配置规范(profile),下面仅列出一些常见的协议规范: - -* GAP(Generic Access Profile):通用接入配置协议,主要用于设备之间的发现、配对和连接等功能。 -* GATT(Generic Attribute Protocol):通用属性配置协议,主要用于BLE设备之间的数据交换和通信。它定义了数据以服务和特征的形式进行组织的结构。 -* HFP(Hands-Free Profile):蓝牙免提配置协议,主要用于车载蓝牙和蓝牙耳机等设备的通话控制。如接听、挂断、拒接、语音拨号等。 -* A2DP(Advanced Audio Distribution Profile):高级音频分发配置协议,主要用于无线音频设备之间的高质量音频流传输。 -* AVRCP(Audio/Video Remote Control Profile):音频/视频远程控制配置协议,主要用于远程控制音频和视频的播放、暂停等操作。 -* SPP(Serial Port Profile):串行端口配置协议,用于在经典蓝牙设备之间建立虚拟串行端口进行通信。 - - - -## 常见概念 - -### 服务器和客户端 - -服务器(Server)和客户端(Client)指的是设备在通信过程中的角色。服务器负责存储和提供数据,而客户端则负责连接到服务器并请求访问其数据。在蓝牙通信中,服务器和客户端的角色是可以互相切换的。比如在BLE中,设备可以作为GATT服务器来提供数据,也可以作为GATT客户端来访问其他设备的数据服务。 - -### 主机和从机 - -主机(Master)和从机(Slave)指的是设备在连接过程中的角色。主机通常负责发起连接请求、管理连接参数并维护设备的连接状态。而从机则是被连接的设备,负责响应主机的请求。在蓝牙通信中,主机和从机的角色可以在不同连接过程中互相切换。 - -> 在蓝牙通信中,服务器/客户端与主机/从机是两个相互独立的概念,它们并没有直接的对应关系。具体的主从关系以及服务器和客户端关系,取决于设备间的连接和通信需求以及使用的是BT还是BLE。 - -## 蓝牙简介 - -QuecPython蓝牙功能组件包括`bt`和`ble`,使用的蓝牙协议是4.2版本。 - -### 低功耗蓝牙 - -QuecPython的`ble`支持做服务器和客户端,但是同一个时刻,只能工作在一种角色,不能同时作为服务器和客户端。 - -### 经典蓝牙 - -QuecPython的`bt`支持HFP、A2DP、AVRCP和SPP协议。当前仅支持做从机。 - -> 当前QuecPython经典蓝牙和低功耗蓝牙,不能同时使用,即同一个时刻,只能工作在其中一种模式。 - -## 蓝牙使用指导 - -- [BLE 使用说明](./ble.md) -- [BT 使用说明](./bt.md) - diff --git a/docs/Getting_started/zh/hardware/bt/ble.md b/docs/Getting_started/zh/hardware/bt/ble.md deleted file mode 100644 index 0156d660e3c08bf6fbd4d3baf55f85e2af0d9a8d..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/bt/ble.md +++ /dev/null @@ -1,1687 +0,0 @@ -# BLE 使用说明 - -本文主要介绍如何使用`QuecPython`的BLE功能,并提供一个基本的BLE的`Server`和`Client`示例来作为用户开发的参考。 - -## 1 BLE API使用说明 - -请参考`QuecPython`官方网站上的Wiki说明:[BLE API 说明](../../../../API_reference/zh/wifibtgnss/ble.md) - -## 2 GATT协议框架 - -GATT协议框架部分可以参考蓝牙官方协议文档中的如下框架图,通过该图可以清晰的看出profile、services、characteristics等之间的关系。 - -![GATT 协议框架](../../media/hardware/bt/GATT协议框架.png) - -通过上图可以看出,一个profile是由一个或多个服务(service)组成,一个服务(service)由一个或者多个特征(characteristic)组成。而每个特征(characteristic)又包含属性(property)、特征值(value)和特征描述(descriptor)。其中,用虚线框起来的部分,表示该部分内容是可选的,比如特征描述符。而一个服务至少需要包含一个特征,一个特征至少需要包含特征属性和特征值。 - - - -## 3 属性 - -在BLE GATT中,其实服务(Service)、特征(Characteristic)、特征值(Characteristic Value)以及特征描述符(Characteristic Descriptor)等,都可以称之为属性(Attribute)。而属性由四个部分构成,分别是句柄(Handle)、类型(Type)、属性值(Value)、权限(Permissions)。这部分可以参考蓝牙官方协议如下部分: - -![属性构成](../../media/hardware/bt/属性构成.png) - -下面分别针对服务(Service)、特征(Characteristic)、特征值(Characteristic Value)以及特征描述符(Descriptor)这些属性进行说明。 - -### 3.1 服务 - -服务(Service)是完成特定功能或特性的数据和相关行为的集合。在GATT中,服务是由服务定义(Service Defintion)来定义的。而服务定义应该包括服务声明(Service Declaration),也可能包括引用定义和特征定义。 - -服务声明(Service Declaration)如下 : - -![服务声明](../../media/hardware/bt/服务声明.png) - -在QuecPython BLE中,上述4个字段,用户可以配置的是`Attribute Type`字段和`Attribute Value`字段。QuecPython官网Wiki中有说明添加服务的接口如下: - -```python -ble.addService(primary, server_id, uuid_type, uuid_s, uuid_l) -``` - -* `primary`参数用来控制`Attribute Type`的值是0x2800还是0x2801。 - -* `uuid_type`、`uuid_s`和`uuid_l`参数即用来配置`Attribute Value`字段。 - -### 3.2 特征 - -特征(Characteristic)是服务中使用的一个值,同时还包含了关于如何访问该值以及如何显示或表示该值的属性和配置信息。在GATT中,特征是由特征定义(Characteristic Definition )来定义的。特征定义应该包含特征声明(Characteristic Declaration)、特征值声明(Characteristic Value Declaration ),可能包含特征描述符声明(Characteristic Descriptor Declaration )。 - -特征声明(Characteristic Declaration)如下: - -![特征声明](../../media/hardware/bt/特征声明.png) - -通过上图可知,特征的`Attribute Type`字段固定为0x2803。而用户可配置的是`Attribute Value`字段,针对该字段,蓝牙官方协议文档中描述如下: - -![属性值字段说明](../../media/hardware/bt/特征声明属性值字段说明.png) - -QuecPython官网Wiki中有说明添加特征的接口如下: - -```python -ble.addChara(server_id, chara_id, chara_prop, uuid_type, uuid_s, uuid_l) -``` - -* `chara_prop`参数配置的就是上图中的`Characteristic Properties`字段。 - -* `uuid_type`、`uuid_s`和`uuid_l`参数配置的就是上图中的`Characteristic UUID`字段。 - -* `Characteristic Value Handle`字段无需用户配置。 - -### 3.3 特征值 - -特征值(Characteristic Value)是特征的实际数据。一个特征必须包含一个特征值,即特征值是属于特征的一部分。 - -上文提到,特征定义应该包含特征声明(Characteristic Declaration)、特征值声明(Characteristic Value Declaration ),而特征值声明如下: - -![特征值声明](../../media/hardware/bt/特征值声明.png) - -QuecPython官网Wiki中有说明添加特征的接口如下: - -```python -ble.addCharaValue(server_id, chara_id, permission, uuid_type, uuid_s, uuid_l, value) -``` - -* `permission`参数配置的就是上图中的`Attribute Permissions`字段。 - -* `uuid_type`、`uuid_s`和`uuid_l`参数配置的就是上图中的`Attribute Type`字段。 - -* `value`参数配置的就是上图中的``Attribute Value`字段。 - -### 3.4 特征描述符 - -在GATT协议中,描述符是用于进一步描述特征值,比如特征值的单位、分辨率、精度、权限等。一个特征定义可以包含一个或者多个特征描述符声明。一些常见的特征描述符包括:特征扩展属性描述符、特征用户描述符、客户端特征配置描述符、服务端特征配置描述符、特征描述格式描述符以及特征聚合格式描述符。具体内容可以参考蓝牙官方协议文档中的[Vol 3, Part G, Characteristic Descriptor Declarations ] 部分的内容,这里不在详述。 - - - -## 4 BLE Server - -BLE Server指设备作为服务器,提供数据服务给其他设备来连接访问。 - -### 4.1 流程 - -要使设备作为BLE服务器,需要按照下面的流程来初始化并设置相关参数。 - -![BLE Server 流程](../../media/hardware/bt/BLE_Server流程.png) - -### 4.2 示例 - -下面提供一个BLE Server示例,该示例中设置了蓝牙名称为`Quectel_ble`,添加了一个电池相关的服务,并基于该服务添加了电池电量特征,同时启用了电池电量特征值的通知和指示功能。在支持蓝牙功能的模组上运行该例程后,可以通过手机端BLE相关的APP去搜索名为`Quectel_ble`的设备,并查看该设备提供的服务。 - -```python -# BLE Server - -import ble -import utime -import _thread -from queue import Queue - -BLE_GATT_SYS_SERVICE = 0 # 0-删除系统默认的GAP和GATT服务 1-保留系统默认的GAP和GATT服务 -BLE_SERVER_HANDLE = 0 -CONNECT_ID = 0 -BLE_IS_RUNNING = 1 - -_BLE_NAME = "Quectel_ble" - - -event_dict = { - 'BLE_START_STATUS_IND': 0, # ble start - 'BLE_STOP_STATUS_IND': 1, # ble stop - 'BLE_CONNECT_IND': 16, # ble connect - 'BLE_DISCONNECT_IND': 17, # ble disconnect - 'BLE_UPDATE_CONN_PARAM_IND': 18, # ble update connection parameter - 'BLE_SCAN_REPORT_IND': 19, # ble gatt client scan and report other devices - 'BLE_GATT_MTU': 20, # ble connection mtu - 'BLE_GATT_RECV_WRITE_IND': 21, # when ble client write characteristic value or descriptor,server get the notice - 'BLE_GATT_RECV_READ_IND': 22, # when ble client read characteristic value or descriptor,server get the notice - 'BLE_GATT_RECV_NOTIFICATION_IND': 23, # client receive notification - 'BLE_GATT_RECV_INDICATION_IND': 24, # client receive indication - 'BLE_GATT_SEND_END': 25, # server send notification,and receive send end notice -} - -class EVENT(dict): - def __getattr__(self, item): - return self[item] - - def __setattr__(self, key, value): - raise ValueError("{} is read-only.".format(key)) - - -event = EVENT(event_dict) -msg_queue = Queue(20) - - -def ble_callback(args): - global msg_queue - msg_queue.put(args) - - -def ble_gatt_server_event_handler(): - global BLE_GATT_SYS_SERVICE - global BLE_SERVER_HANDLE - global CONNECT_ID - global BLE_IS_RUNNING - global msg_queue - - while True: - msg = msg_queue.get() # 没有消息时会阻塞在这 - event_id = msg[0] - status = msg[1] - - if event_id == event.BLE_START_STATUS_IND: # ble start - print('[ble_callback]: event_id=BLE_START_STATUS_IND, status={}'.format(status)) - if status == 0: - print('[callback] BLE start success.') - mac = ble.getPublicAddr() - if mac != -1 and len(mac) == 6: - addr = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]) - print('BLE public addr : {}'.format(addr)) - ret = ble_gatt_set_name() - if ret != 0: - ble_gatt_close() - break - ret = ble_gatt_set_param() - if ret != 0: - ble_gatt_close() - break - ret = ble_gatt_set_data() - if ret != 0: - ble_gatt_close() - break - ret = ble_gatt_set_rsp_data() - if ret != 0: - ble_gatt_close() - break - ret = ble_gatt_add_service() - if ret != 0: - ble_gatt_close() - break - ret = ble_gatt_add_characteristic() - if ret != 0: - ble_gatt_close() - break - ret = ble_gatt_add_characteristic_value() - if ret != 0: - ble_gatt_close() - break - ret = ble_gatt_add_characteristic_desc() - if ret != 0: - ble_gatt_close() - break - ret = ble_gatt_add_service_complete() - if ret != 0: - ble_gatt_close() - break - ''' - 当BLE_GATT_SYS_SERVICE为1时,即保留系统默认的GAP和GATT服务,一些较低的句柄值通常被保留给系统默认的 - GAP(Generic Access Profile)和GATT(Generic Attribute Profile)服务;所以在保留系统默认的GAP - 和GATT服务的情况下,自定义服务和特征的句柄通常从较高的值开始分配,这样可以确保系统服务和自定义服务之间的 - 句柄不会发生冲突。 - ''' - if BLE_GATT_SYS_SERVICE == 0: - BLE_SERVER_HANDLE = 1 # 不可以修改该值 - else: - BLE_SERVER_HANDLE = 16 # 不可以修改该值 - - ret = ble_adv_start() - if ret != 0: - ble_gatt_close() - break - BLE_IS_RUNNING = 1 - else: - print('[callback] BLE start failed.') - elif event_id == event.BLE_STOP_STATUS_IND: # ble stop - print('[ble_callback]: event_id=BLE_STOP_STATUS_IND, status={}'.format(status)) - if status == 0: - print('[callback] ble stop successful.') - ble_status = ble.getStatus() - print('ble status is {}'.format(ble_status)) - ble_gatt_server_release() - else: - print('[callback] ble stop failed.') - elif event_id == event.BLE_CONNECT_IND: # ble connect - print('[ble_callback]: event_id=BLE_CONNECT_IND, status={}'.format(status)) - if status == 0: - print('[callback] ble connect successful.') - connect_id = msg[2] - addr = msg[3] - addr_str = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]) - print('[callback] connect_id = {}, addr = {}'.format(connect_id, addr_str)) - CONNECT_ID = connect_id - ret = ble_gatt_send_notification() - if ret == 0: - print('[callback] ble_gatt_send_notification successful.') - else: - print('[callback] ble_gatt_send_notification failed.') - ble_gatt_close() - break - else: - print('[callback] ble connect failed.') - elif event_id == event.BLE_DISCONNECT_IND: # ble disconnect - print('[ble_callback]: event_id=BLE_DISCONNECT_IND, status={}'.format(status)) - if status == 0: - print('[callback] ble disconnect successful.') - connect_id = msg[2] - addr = msg[3] - addr_str = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]) - ble_gatt_close() - print('[callback] connect_id = {}, addr = {}'.format(connect_id, addr_str)) - else: - print('[callback] ble disconnect failed.') - ble_gatt_close() - break - elif event_id == event.BLE_UPDATE_CONN_PARAM_IND: # ble update connection parameter - print('[ble_callback]: event_id=BLE_UPDATE_CONN_PARAM_IND, status={}'.format(status)) - if status == 0: - print('[callback] ble update parameter successful.') - connect_id = msg[2] - max_interval = msg[3] - min_interval = msg[4] - latency = msg[5] - timeout = msg[6] - print('[callback] connect_id={},max_interval={},min_interval={},latency={},timeout={}'.format(connect_id, max_interval, min_interval, latency, timeout)) - else: - print('[callback] ble update parameter failed.') - ble_gatt_close() - break - elif event_id == event.BLE_GATT_MTU: # ble connection mtu - print('[ble_callback]: event_id=BLE_GATT_MTU, status={}'.format(status)) - if status == 0: - print('[callback] ble connect mtu successful.') - handle = msg[2] - ble_mtu = msg[3] - print('[callback] handle = {:#06x}, ble_mtu = {}'.format(handle, ble_mtu)) - else: - print('[callback] ble connect mtu failed.') - ble_gatt_close() - break - elif event_id == event.BLE_GATT_RECV_WRITE_IND: - print('[ble_callback]: event_id=BLE_GATT_RECV_WRITE_IND, status={}'.format(status)) - if status == 0: - print('[callback] ble recv successful.') - data_len = msg[2] - data = msg[3] # 这是一个bytearray - attr_handle = msg[4] - short_uuid = msg[5] - long_uuid = msg[6] # 这是一个bytearray - print('len={}, data:{}'.format(data_len, data)) - print('attr_handle = {:#06x}'.format(attr_handle)) - print('short uuid = {:#06x}'.format(short_uuid)) - print('long uuid = {}'.format(long_uuid)) - else: - print('[callback] ble recv failed.') - ble_gatt_close() - break - elif event_id == event.BLE_GATT_RECV_READ_IND: - print('[ble_callback]: event_id=BLE_GATT_RECV_READ_IND, status={}'.format(status)) - if status == 0: - print('[callback] ble recv read successful.') - data_len = msg[2] - data = msg[3] # 这是一个bytearray - attr_handle = msg[4] - short_uuid = msg[5] - long_uuid = msg[6] # 这是一个bytearray - print('len={}, data:{}'.format(data_len, data)) - print('attr_handle = {:#06x}'.format(attr_handle)) - print('short uuid = {:#06x}'.format(short_uuid)) - print('long uuid = {}'.format(long_uuid)) - else: - print('[callback] ble recv read failed.') - ble_gatt_close() - break - elif event_id == event.BLE_GATT_SEND_END: - print('[ble_callback]: event_id=BLE_GATT_SEND_END, status={}'.format(status)) - if status == 0: - print('[callback] ble send data successful.') - else: - print('[callback] ble send data failed.') - else: - print('unknown event id.') - - -def ble_gatt_server_init(cb): - ret = ble.serverInit(cb) - if ret != 0: - print('ble_gatt_server_init failed.') - return -1 - print('ble_gatt_server_init success.') - return 0 - - -def ble_gatt_server_release(): - ret = ble.serverRelease() - if ret != 0: - print('ble_gatt_server_release failed.') - return -1 - print('ble_gatt_server_release success.') - return 0 - - -def ble_gatt_open(): - ret = ble.gattStart() - if ret != 0: - print('ble_gatt_open failed.') - return -1 - print('ble_gatt_open success.') - return 0 - - -def ble_gatt_close(): - global BLE_IS_RUNNING - - ret = ble.gattStop() - if ret != 0: - print('ble_gatt_close failed.') - return -1 - print('ble_gatt_close success.') - BLE_IS_RUNNING = 0 - return 0 - - -def ble_gatt_set_name(): - code = 0 # utf8 - name = _BLE_NAME - ret = ble.setLocalName(code, name) - if ret != 0: - print('ble_gatt_set_name failed.') - return -1 - print('ble_gatt_set_name success.') - return 0 - - -def ble_gatt_set_param(): - min_adv = 0x300 - max_adv = 0x320 - adv_type = 0 # 可连接的非定向广播,默认选择 - addr_type = 0 # 公共地址 - channel = 0x07 - filter_strategy = 0 # 处理所有设备的扫描和连接请求 - discov_mode = 2 # 该参数实际不起作用,需要在广播数据中设置 - no_br_edr = 1 # 该参数实际不起作用,需要在广播数据中设置 - enable_adv = 1 - ret = ble.setAdvParam(min_adv, max_adv, adv_type, addr_type, channel, filter_strategy, discov_mode, no_br_edr, enable_adv) - if ret != 0: - print('ble_gatt_set_param failed.') - return -1 - print('ble_gatt_set_param success.') - return 0 - -''' -如果我们希望其他设备扫描时,能在扫描结果中看到该设备的名称,就需要在广播数据中包含设备名称。 -''' -def ble_gatt_set_data(): - adv_data = [0x02, 0x01, 0x05] - ble_name = _BLE_NAME - length = len(ble_name) + 1 - adv_data.append(length) - adv_data.append(0x09) - name_encode = ble_name.encode('UTF-8') - for i in range(0, len(name_encode)): - adv_data.append(name_encode[i]) - print('set adv_data:{}'.format(adv_data)) - data = bytearray(adv_data) - ret = ble.setAdvData(data) - if ret != 0: - print('ble_gatt_set_data failed.') - return -1 - print('ble_gatt_set_data success.') - return 0 - - -def ble_gatt_set_rsp_data(): - adv_data = [] - ble_name = _BLE_NAME - length = len(ble_name) + 1 - adv_data.append(length) - adv_data.append(0x09) - name_encode = ble_name.encode('UTF-8') - for i in range(0, len(name_encode)): - adv_data.append(name_encode[i]) - print('set adv_rsp_data:{}'.format(adv_data)) - data = bytearray(adv_data) - ret = ble.setAdvRspData(data) - if ret != 0: - print('ble_gatt_set_rsp_data failed.') - return -1 - print('ble_gatt_set_rsp_data success.') - return 0 - - -def ble_gatt_add_service(): - primary = 1 - server_id = 0x01 - uuid_type = 1 # 短UUID - uuid_s = 0x180F # Battery Service 电池数据 - uuid_l = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) - ret = ble.addService(primary, server_id, uuid_type, uuid_s, uuid_l) - if ret != 0: - print('ble_gatt_add_service failed.') - return -1 - print('ble_gatt_add_service success.') - return 0 - - -def ble_gatt_add_characteristic(): - server_id = 0x01 - chara_id = 0x01 - chara_prop = 0x02 | 0x10 | 0x20 # 0x02-可读 0x10-通知 0x20-指示 - uuid_type = 1 # 短UUID - uuid_s = 0x2A19 # Battery Level 电池电量 - uuid_l = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) - ret = ble.addChara(server_id, chara_id, chara_prop, uuid_type, uuid_s, uuid_l) - if ret != 0: - print('ble_gatt_add_characteristic failed.') - return -1 - print('ble_gatt_add_characteristic success.') - return 0 - - -def ble_gatt_add_characteristic_value(): - data = [0x16] # 测试数据 - server_id = 0x01 - chara_id = 0x01 - permission = 0x0001 | 0x0002 - uuid_type = 1 # 短UUID - uuid_s = 0x2A19 # Battery Level 电池电量 - uuid_l = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) - value = bytearray(data) - ret = ble.addCharaValue(server_id, chara_id, permission, uuid_type, uuid_s, uuid_l, value) - if ret != 0: - print('ble_gatt_add_characteristic_value failed.') - return -1 - print('ble_gatt_add_characteristic_value success.') - return 0 - - -def ble_gatt_add_characteristic_desc(): - data = [0x00, 0x00] - server_id = 0x01 - chara_id = 0x01 - permission = 0x0001 | 0x0002 - uuid_type = 1 # 短UUID - ''' - 在BLE服务器中,UUID为0x2902的特征描述符是Client Characteristic Configuration Descriptor(CCCD), - 它用于管理特征值的通知和指示功能。如果某个Characteristic需要支持通知(notifications)或指示(indicatons), - 则它必须添加CCCD。 - 为特征添加UUID为0x2902的特征描述符后,客户端可以通过写入该描述符来启用或禁用特征的通知或指示功能。 - 例如,客户端可以写入0x0001来启用通知,写入0x0002来启用指示,或写入0x0000来禁用这两种功能。 - ''' - uuid_s = 0x2902 - uuid_l = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) - value = bytearray(data) - ret = ble.addCharaDesc(server_id, chara_id, permission, uuid_type, uuid_s, uuid_l, value) - if ret != 0: - print('ble_gatt_add_characteristic_desc failed.') - return -1 - print('ble_gatt_add_characteristic_desc success.') - return 0 - -''' -通知是通过特征值的句柄来发送的,因此发送之前需要先确定句柄值 -''' -def ble_gatt_send_notification(): - global BLE_SERVER_HANDLE - global CONNECT_ID - data = [0x20] # 测试数据 - conn_id = CONNECT_ID - ''' - 这里attr_handle之所以取值 BLE_SERVER_HANDLE + 2,是因为: - BLE_SERVER_HANDLE是定义的起始句柄,第一个添加的服务的句柄即为BLE_SERVER_HANDLE; - 后面每添加一个特征、特征值、特征描述符时,句柄值都会自动加1; - 通过前面代码可以看到,我们依次添加了一个服务、一个特征、一个特征值、一个特征描述符。 - 添加了一个服务(UUID是0x180F),对应服务句柄就是BLE_SERVER_HANDLE; - 添加了一个特征(UUID是0x2A19),对应的特征句柄是BLE_SERVER_HANDLE+1 - 添加了一个特征值(UUID是0x2A19),对应的特征值句柄是BLE_SERVER_HANDLE+2 - 添加了一个特征描述符(UUID是0x2902),对应的特征描述符句柄是BLE_SERVER_HANDLE+3,这个是CCCD的句柄。 - 而通知是通过特征值句柄来发送,在本例程中,即通过UUID为0x2A19(电池电量)这个特征值句柄来发送,因此attr_handle = BLE_SERVER_HANDLE + 2 - ''' - attr_handle = BLE_SERVER_HANDLE + 2 - value = bytearray(data) - ret = ble.sendNotification(conn_id, attr_handle, value) - if ret != 0: - print('sendNotification failed.') - return -1 - print('sendNotification success.') - return 0 - - -def ble_gatt_add_service_complete(): - global BLE_GATT_SYS_SERVICE - ret = ble.addOrClearService(1, BLE_GATT_SYS_SERVICE) - if ret != 0: - print('ble_gatt_add_service_complete failed.') - return -1 - print('ble_gatt_add_service_complete success.') - return 0 - - -def ble_gatt_clear_service_complete(): - global BLE_GATT_SYS_SERVICE - ret = ble.addOrClearService(0, BLE_GATT_SYS_SERVICE) - if ret != 0: - print('ble_gatt_clear_service_complete failed.') - return -1 - print('ble_gatt_clear_service_complete success.') - return 0 - - -def ble_adv_start(): - ret = ble.advStart() - if ret != 0: - print('ble_adv_start failed.') - return -1 - print('ble_adv_start success.') - return 0 - - -def ble_adv_stop(): - ret = ble.advStop() - if ret != 0: - print('ble_adv_stop failed.') - return -1 - print('ble_adv_stop success.') - return 0 - - -def main(): - global BLE_IS_RUNNING - _thread.start_new_thread(ble_gatt_server_event_handler, ()) - ret = ble_gatt_server_init(ble_callback) - if ret == 0: - ret = ble_gatt_open() - if ret != 0: - return -1 - else: - return -1 - count = 0 - while True: - utime.sleep(1) - count += 1 - if count % 5 == 0: - print('##### BLE running, count = {}......'.format(count)) - - if BLE_IS_RUNNING == 0: - count = 0 - print('!!!!! Ready to exit !!!!!') - return 0 - - -if __name__ == '__main__': - main() - -``` - - - -### 4.3 说明 - -下面就QuecPython的BLE和示例代码做一些说明。 - -#### 4.3.1 服务句柄取值说明 - -示例代码中可以看到`BLE_SERVER_HANDLE`的值根据`BLE_GATT_SYS_SERVICE`不同,可能是`1`,也可能是`16`。这里有如下两点需要说明: - -* QuecPython的`BLE Server`,句柄是在C代码中自动管理的,根据`BLE_GATT_SYS_SERVICE`取值不同,C代码中初始值为`1`或`16`;因此示例代码中`BLE_SERVER_HANDLE`的初始值也必须是`1`或`16`,与C代码中保持一致。 -* 当`BLE_GATT_SYS_SERVICE`为`1`时,即保留系统默认的`GAP`和`GATT`服务,一些较低的句柄值通常被保留给系统默认的`GAP(Generic Access Profile)`和`GATT(Generic Attribute Profile)`服务。所以在保留系统默认的`GAP`和`GATT`服务的情况下,自定义服务和特征的句柄通常从较高的值开始分配,这样的设计可以确保系统服务和自定义服务之间的句柄不会发生冲突。 - - - -#### 4.3.2 UUID为0x2902的特征描述符 - -在BLE服务器中,UUID为`0x2902`的特征描述符是指客户端特征配置描述符(Client Characteristic Configuration Descriptor),简称`CCCD`,它用于管理特征值的通知和指示功能。当需要向客户端设备发送特征值的实时更新时,可以使用通知(Notifications)和指示(Indications)这两种机制。如果某个`Characteristic`需要支持通知或指示,则它必须添加`CCCD`。 - - - -#### 4.3.3 关于通知和指示 - -通知和指示是通过特征值的句柄来发送的,因此发送之前需要先确定特征值句柄。特征值句柄是在定义`GATT`服务和特征时自动分配的,起始句柄是`BLE_SERVER_HANDLE`,每添加一个服务(service),分配的对应句柄值自动加1;给该服务添加一个特征时,分配的对应句柄值自动加1;给该特征添加一个特征值时,分配的对应句柄值自动加1;给该特征添加一个特征描述符时,分配的对应句柄值自动加1。可根据该原则计算出某个特征值句柄。 - -如果`BLE Server`发送通知或者指示失败,可能有如下原因: - -* 发送时,使用的特征值句柄不对,按照上述计算方法进一步确认是否计算有误。 -* 发送的数据长度不对,发送的数据长度大于添加特征值时的数据长度导致,须保证发送的数据长度不超过添加特征值时的数据长度。 - - - -#### 4.3.4 BLE广播数据 - -广播数据是指设备在广播状态下发送出去的数据包,通常一条广播数据最大长度限制为31个字节,用于通知周边设备自己的存在以及提供一些基本的设备信息。比如设备名称、设备类型、服务UUID等。 - -BLE的广播数据是有固定的数据结构要求的。具体结构为:长度+类型+数据。 - -* 长度:固定占一个字节,表示接下来的广播数据段的长度,包含“类型”字段。 -* 类型:固定占一个字节,表示广播数据段的类型,如设备名称、设备类型等。 -* 数据:长度由“长度”字段指定,包含实际的广播数据类容,根据“类型”字段不同,数据的结构和内容也会有所不同。 - -需要注意的是,设备可以在一条广播数据包中包含多个上述的数据结构。 - - - -#### 4.3.5 广播数据类型 - -完整的广播数据类型说明,请参考蓝牙官方的文档《Generic Access Profile》部分。下面仅列出一些常见的广播数据类型。 - -| Type值 | Type名称 | 说明 | -| ------ | ---------------------------------------------- | ------------------------------------------------------------ | -| 0x01 | Flags | 标识设备支持的通用属性,比如是否支持LE(低功耗)模式、是否支持广播等。 | -| 0x02 | Incomplete List of 16-bit Service Class UUIDs | 列出设备支持的 16 位服务 UUID(唯一标识符)列表。Incomplete 表示列表未完整列出,即只列出了部分服务 UUID。 | -| 0x03 | Complete List of 16-bit Service Class UUIDs | 列出设备支持的 16 位服务 UUID 列表。Complete 表示列表完整列出,即列出了设备支持的所有服务 UUID。 | -| 0x04 | Incomplete List of 32-bit Service Class UUIDs | 列出设备支持的 32 位服务 UUID 列表。Incomplete 表示列表未完整列出,即只列出了部分服务 UUID。 | -| 0x05 | Complete List of 32-bit Service Class UUIDs | 列出设备支持的 32 位服务 UUID 列表。Complete 表示列表完整列出,即列出了设备支持的所有服务 UUID。 | -| 0x06 | Incomplete List of 128-bit Service Class UUIDs | 列出设备支持的 128 位服务 UUID 列表。Incomplete 表示列表未完整列出,即只列出了部分服务 UUID。 | -| 0x07 | Complete List of 128-bit Service Class UUIDs | 列出设备支持的 128 位服务 UUID 列表。Complete 表示列表完整列出,即列出了设备支持的所有服务 UUID。 | -| 0x08 | Shortened Local Name | 设备的名称,以字符串形式表示。如果名称过长,可能会被截断,只列出了名称的一部分。 | -| 0x09 | Complete Local Name | 设备的完整名称,以字符串形式表示。 | - - - -#### 4.3.6 设置发现模式和是否支持BR/EDR - -在设置广播参数接口`ble.setAdvParam`中提供了这两个功能的参数设置项,实际该接口中关于发现模式和是否支持BR/EDR的参数并没有真正使用,目前仅作保留。真正设置这两个参数的方式是在设置广播数据接口`ble.setAdvData`中设置,具体如下: - -看示例代码中,设置广播数据的代码如下: - -```python -def ble_gatt_set_data(): - adv_data = [0x02, 0x01, 0x05] - ...... # 其他代码省略 -``` - -注意到广播数据中,我们给的第一组数据是`[0x02, 0x01, 0x05]`,其中`0x02`表示数据长度,表示后面有两个字节数据,第二个`0x01`表示类型,而第三个数据`0x05`则表示:有限发现模式且不支持BR/EDR。如果我们需要设置发现模式和是否支持BR/EDR,则需要修改该值,具体为: - -| 参数值 | 说明 | -| ------ | ------------------------------ | -| 0x01 | 有限发现模式,并且支持BR/EDR | -| 0x02 | 一般发现模式,并且支持BR/EDR | -| 0x05 | 有限发现模式,并且不支持BR/EDR | -| 0x06 | 一般发现模式,并且不支持BR/EDR | - - - -## 5 BLE Client - -BLE Client指设备作为客户端,主动去连接服务器并访问其数据。 - -### 5.1 流程 - -要使设备作为BLE客户端,需要按照下面的流程来初始化并设置相关参数。 - -![BLE Client 流程](../../media/hardware/bt/BLE_Client流程.png) - - - -### 5.2 示例 - -下面提供一个BLE Client示例,该示例中将设备作为客户端角色,主动去扫描周边的BLE设备,如果扫描到名称为`Quectel_ble`的设备就停止扫描,并向该设备发起连接,然后去发现该设备提供了哪些服务和服务特征等。 - - - -> 实际应用中,为了降低功耗,一般是根据UUID去发起服务发现请求,而不是发现所有的服务。 - - - -```python -# BLE Client - -import ble -import utime -import _thread -from queue import Queue - - -event_dict = { - 'BLE_START_STATUS_IND': 0, # ble start - 'BLE_STOP_STATUS_IND': 1, # ble stop - 'BLE_CONNECT_IND': 16, # ble connect - 'BLE_DISCONNECT_IND': 17, # ble disconnect - 'BLE_UPDATE_CONN_PARAM_IND': 18, # ble update connection parameter - 'BLE_SCAN_REPORT_IND': 19, # ble gatt client scan and report other devices - 'BLE_GATT_MTU': 20, # ble connection mtu - 'BLE_GATT_RECV_NOTIFICATION_IND': 23, # client receive notification - 'BLE_GATT_RECV_INDICATION_IND': 24, # client receive indication - 'BLE_GATT_START_DISCOVER_SERVICE_IND': 26, # start discover service - 'BLE_GATT_DISCOVER_SERVICE_IND': 27, # discover service - 'BLE_GATT_DISCOVER_CHARACTERISTIC_DATA_IND': 28, # discover characteristic - 'BLE_GATT_DISCOVER_CHARA_DESC_IND': 29, # discover characteristic descriptor - 'BLE_GATT_CHARA_WRITE_WITH_RSP_IND': 30, # write characteristic value with response - 'BLE_GATT_CHARA_WRITE_WITHOUT_RSP_IND': 31, # write characteristic value without response - 'BLE_GATT_CHARA_READ_IND': 32, # read characteristic value by handle - 'BLE_GATT_CHARA_READ_BY_UUID_IND': 33, # read characteristic value by uuid - 'BLE_GATT_CHARA_MULTI_READ_IND': 34, # read multiple characteristic value - 'BLE_GATT_DESC_WRITE_WITH_RSP_IND': 35, # write characteristic descriptor - 'BLE_GATT_DESC_READ_IND': 36, # read characteristic descriptor - 'BLE_GATT_ATT_ERROR_IND': 37, # attribute error -} - -gatt_status_dict = { - 'BLE_GATT_IDLE' : 0, - 'BLE_GATT_DISCOVER_SERVICE': 1, - 'BLE_GATT_DISCOVER_INCLUDES': 2, - 'BLE_GATT_DISCOVER_CHARACTERISTIC': 3, - 'BLE_GATT_WRITE_CHARA_VALUE': 4, - 'BLE_GATT_WRITE_CHARA_DESC': 5, - 'BLE_GATT_READ_CHARA_VALUE': 6, - 'BLE_GATT_READ_CHARA_DESC': 7, -} - - -class EVENT(dict): - def __getattr__(self, item): - return self[item] - - def __setattr__(self, key, value): - raise ValueError("{} is read-only.".format(key)) - - -class BleClient(object): - def __init__(self): - self.ble_server_name = 'Quectel_ble' # 目标设备ble名称 - self.connect_id = 0 - self.connect_addr = 0 - self.gatt_statue = 0 - self.discover_service_mode = 0 # 0-discover all service, 1-discover service by uuid - - self.scan_param = { - 'scan_mode': 1, # 积极扫描 - 'interval': 0x100, - 'scan_window': 0x50, - 'filter_policy': 0, - 'local_addr_type': 0, - } - - self.scan_report_info = { - 'event_type': 0, - 'name': '', - 'addr_type': 0, - 'addr': bytearray(0), - 'rssi': 0, - 'data_len': 0, - 'raw_data': 0, - } - - self.target_service = { - 'start_handle': 0, - 'end_handle': 0, - 'uuid_type': 1, # 短uuid - 'short_uuid': 0x180F, # 电池电量服务 - 'long_uuid': bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) - } - - self.characteristic_list = [] - self.descriptor_list = [] - self.characteristic_count = 0 # ql_ble_gatt_chara_count - self.chara_descriptor_count = 0 # ql_ble_gatt_chara_desc_count - self.characteristic_index = 0 # ql_ble_gatt_chara_desc_index - self.current_chara_index = 0 # ql_ble_gatt_cur_chara - self.current_desc_index = 0 # ql_ble_gatt_chara_cur_desc - self.ble_short_uuid_pair_len = 7 - self.ble_long_uuid_pair_len = 21 - - ret = ble.clientInit(self.ble_client_callback) - if ret != 0: - print('ble client initialize failed.') - raise ValueError("BLE Client Init failed.") - else: - print('ble client initialize successful.') - print('') - - @staticmethod - def gatt_open(): - ret = ble.gattStart() - if ret != 0: - print('ble open failed.') - else: - print('ble open successful.') - print('') - return ret - - @staticmethod - def gatt_close(): - ret = ble.gattStop() - if ret != 0: - print('ble close failed.') - else: - print('ble close successful.') - print('') - return ret - - @staticmethod - def gatt_get_status(): - return ble.getStatus() - - @staticmethod - def release(): - ret = ble.clientRelease() - if ret != 0: - print('ble client release failed.') - else: - print('ble client release successful.') - print('') - return ret - - def set_scan_param(self): - scan_mode = self.scan_param['scan_mode'] - interval = self.scan_param['interval'] - scan_time = self.scan_param['scan_window'] - filter_policy = self.scan_param['filter_policy'] - local_addr_type = self.scan_param['local_addr_type'] - ret = ble.setScanParam(scan_mode, interval, scan_time, filter_policy, local_addr_type) - if ret != 0: - print('ble client set scan-parameters failed.') - else: - print('ble client set scan-parameters successful.') - print('') - return ret - - @staticmethod - def start_scan(): - ret = ble.scanStart() - if ret != 0: - print('ble client scan failed.') - else: - print('ble client scan successful.') - print('') - return ret - - @staticmethod - def stop_scan(): - ret = ble.scanStop() - if ret != 0: - print('ble client failed to stop scanning.') - else: - print('ble client scan stopped successfully.') - print('') - return ret - - def connect(self): - print('start to connect.....') - addr_type = self.scan_report_info['addr_type'] - addr = self.scan_report_info['addr'] - if addr != 0 and len(addr) == 6: - addr_str = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]) - print('addr_type : {}, addr : {}'.format(addr_type, addr_str)) - ret = ble.connect(addr_type, addr) - if ret != 0: - print('ble client connect failed.') - else: - print('ble client connect successful.') - print('') - return ret - - def cancel_connect(self): - ret = ble.cancelConnect(self.scan_report_info['addr']) - if ret != 0: - print('ble client cancel connect failed.') - else: - print('ble client cancel connect successful.') - print('') - return ret - - def disconnect(self): - ret = ble.disconnect(self.connect_id) - if ret != 0: - print('ble client disconnect failed.') - else: - print('ble client disconnect successful.') - print('') - return ret - - def discover_all_service(self): - ret = ble.discoverAllService(self.connect_id) - if ret != 0: - print('ble client discover all service failed.') - else: - print('ble client discover all service successful.') - print('') - return ret - - def discover_service_by_uuid(self): - connect_id = self.connect_id - uuid_type = self.target_service['uuid_type'] - short_uuid = self.target_service['short_uuid'] - long_uuid = self.target_service['long_uuid'] - ret = ble.discoverByUUID(connect_id, uuid_type, short_uuid, long_uuid) - if ret != 0: - print('ble client discover service by uuid failed.') - else: - print('ble client discover service by uuid successful.') - print('') - return ret - - def discover_all_includes(self): - connect_id = self.connect_id - start_handle = self.target_service['start_handle'] - end_handle = self.target_service['end_handle'] - ret = ble.discoverAllIncludes(connect_id, start_handle, end_handle) - if ret != 0: - print('ble client discover all includes failed.') - else: - print('ble client discover all includes successful.') - print('') - return ret - - def discover_all_characteristic(self): - connect_id = self.connect_id - start_handle = self.target_service['start_handle'] - end_handle = self.target_service['end_handle'] - ret = ble.discoverAllChara(connect_id, start_handle, end_handle) - if ret != 0: - print('ble client discover all characteristic failed.') - else: - print('ble client discover all characteristic successful.') - print('') - return ret - - def discover_all_characteristic_descriptor(self): - connect_id = self.connect_id - index = self.characteristic_index - start_handle = self.characteristic_list[index]['value_handle'] + 1 - - if self.characteristic_index == (self.characteristic_count - 1): - end_handle = self.target_service['end_handle'] - print('[1]start_handle = {:#06x}, end_handle = {:#06x}'.format(start_handle - 1, end_handle)) - ret = ble.discoverAllCharaDesc(connect_id, start_handle, end_handle) - else: - end_handle = self.characteristic_list[index+1]['handle'] - 1 - print('[2]start_handle = {:#06x}, end_handle = {:#06x}'.format(start_handle - 1, end_handle)) - ret = ble.discoverAllCharaDesc(connect_id, start_handle, end_handle) - self.characteristic_index += 1 - if ret != 0: - print('ble client discover all characteristic descriptor failed.') - else: - print('ble client discover all characteristic descriptor successful.') - print('') - return ret - - def read_characteristic_by_uuid(self): - connect_id = self.connect_id - index = self.current_chara_index # 根据需要改变该值 - start_handle = self.characteristic_list[index]['handle'] - end_handle = self.characteristic_list[index]['value_handle'] - uuid_type = 1 - short_uuid = self.characteristic_list[index]['short_uuid'] - long_uuid = bytearray([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]) - - ret = ble.readCharaByUUID(connect_id, start_handle, end_handle, uuid_type, short_uuid, long_uuid) - if ret != 0: - print('ble client read characteristic by uuid failed.') - else: - print('ble client read characteristic by uuid successful.') - print('') - return ret - - def read_characteristic_by_handle(self): - connect_id = self.connect_id - index = self.current_chara_index # 根据需要改变该值 - handle = self.characteristic_list[index]['value_handle'] - offset = 0 - is_long = 0 - - ret = ble.readCharaByHandle(connect_id, handle, offset, is_long) - if ret != 0: - print('ble client read characteristic by handle failed.') - else: - print('ble client read characteristic by handle successful.') - print('') - return ret - - def read_characteristic_descriptor(self): - connect_id = self.connect_id - index = self.current_desc_index # 根据需要改变该值 - handle = self.descriptor_list[index]['handle'] - print('handle = {:#06x}'.format(handle)) - is_long = 0 - ret = ble.readCharaDesc(connect_id, handle, is_long) - if ret != 0: - print('ble client read characteristic descriptor failed.') - else: - print('ble client read characteristic descriptor successful.') - print('') - return ret - - def write_characteristic(self): - connect_id = self.connect_id - index = self.current_chara_index # 根据需要改变该值 - handle = self.characteristic_list[index]['value_handle'] - offset = 0 - is_long = 0 - data = bytearray([0x40, 0x00]) - print('value_handle = {:#06x}, uuid = {:#06x}'.format(handle, self.characteristic_list[index]['short_uuid'])) - ret = ble.writeChara(connect_id, handle, offset, is_long, data) - if ret != 0: - print('ble client write characteristic failed.') - else: - print('ble client read characteristic successful.') - print('') - return ret - - def write_characteristic_no_rsp(self): - connect_id = self.connect_id - index = self.current_chara_index # 根据需要改变该值 - handle = self.characteristic_list[index]['value_handle'] - data = bytearray([0x20, 0x00]) - print('value_handle = {:#06x}, uuid = {:#06x}'.format(handle, self.characteristic_list[index]['short_uuid'])) - ret = ble.writeCharaNoRsp(connect_id, handle, data) - if ret != 0: - print('ble client write characteristic no rsp failed.') - else: - print('ble client read characteristic no rsp successful.') - print('') - return ret - - def write_characteristic_descriptor(self): - connect_id = self.connect_id - index = self.current_desc_index # 根据需要改变该值 - handle = self.descriptor_list[index]['handle'] - data = bytearray([0x01, 0x02]) - print('handle = {:#06x}'.format(handle)) - - ret = ble.writeCharaDesc(connect_id, handle, data) - if ret != 0: - print('ble client write characteristic descriptor failed.') - else: - print('ble client read characteristic descriptor successful.') - print('') - return ret - - @staticmethod - def ble_client_callback(args): - global msg_queue - msg_queue.put(args) - - -def ble_gatt_client_event_handler(): - global msg_queue - old_time = 0 - - while True: - cur_time = utime.localtime() - timestamp = "{:02d}:{:02d}:{:02d}".format(cur_time[3], cur_time[4], cur_time[5]) - if cur_time[5] != old_time and cur_time[5] % 5 == 0: - old_time = cur_time[5] - print('[{}]event handler running.....'.format(timestamp)) - print('') - msg = msg_queue.get() # 没有消息时会阻塞在这 - # print('msg : {}'.format(msg)) - event_id = msg[0] - status = msg[1] - - if event_id == event.BLE_START_STATUS_IND: - print('') - print('event_id : BLE_START_STATUS_IND, status = {}'.format(status)) - if status == 0: - print('BLE start successful.') - ble_status = ble_client.gatt_get_status() - if ble_status == 0: - print('BLE Status : stopped.') - break - elif ble_status == 1: - print('BLE Status : started.') - else: - print('get ble status error.') - ble_client.gatt_close() - break - - ret = ble_client.set_scan_param() - if ret != 0: - ble_client.gatt_close() - break - ret = ble_client.start_scan() - if ret != 0: - ble_client.gatt_close() - break - else: - print('BLE start failed.') - break - elif event_id == event.BLE_STOP_STATUS_IND: - print('') - print('event_id : BLE_STOP_STATUS_IND, status = {}'.format(status)) - if status == 0: - print('ble stop successful.') - else: - print('ble stop failed.') - break - elif event_id == event.BLE_CONNECT_IND: - print('') - print('event_id : BLE_CONNECT_IND, status = {}'.format(status)) - if status == 0: - ble_client.connect_id = msg[2] - ble_client.connect_addr = msg[3] - addr = ble_client.connect_addr - addr_str = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]) - print('connect_id : {:#x}, connect_addr : {}'.format(ble_client.connect_id, addr_str)) - else: - print('ble connect failed.') - break - elif event_id == event.BLE_DISCONNECT_IND: - print('') - print('event_id : BLE_DISCONNECT_IND, status = {}'.format(status)) - if status == 0: - ble_client.connect_id = msg[2] - ble_client.connect_addr = msg[3] - addr = ble_client.connect_addr - addr_str = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]) - print('connect_id : {:#x}, connect_addr : {}'.format(ble_client.connect_id, addr_str)) - else: - print('ble disconnect failed.') - ble_client.gatt_close() - break - elif event_id == event.BLE_UPDATE_CONN_PARAM_IND: - print('') - print('event_id : BLE_UPDATE_CONN_PARAM_IND, status = {}'.format(status)) - if status == 0: - connect_id = msg[2] - max_interval = msg[3] - min_interval = msg[4] - latency = msg[5] - timeout = msg[6] - print('connect_id={},max_interval={},min_interval={},latency={},timeout={}'.format(connect_id,max_interval,min_interval,latency,timeout)) - else: - print('ble update parameter failed.') - ble_client.gatt_close() - break - elif event_id == event.BLE_SCAN_REPORT_IND: - if status == 0: - # print(' ble scan successful.') - - ble_client.scan_report_info['event_type'] = msg[2] - ble_client.scan_report_info['name'] = msg[3] - ble_client.scan_report_info['addr_type'] = msg[4] - ble_client.scan_report_info['addr'] = msg[5] - ble_client.scan_report_info['rssi'] = msg[6] - ble_client.scan_report_info['data_len'] = msg[7] - ble_client.scan_report_info['raw_data'] = msg[8] - - device_name = ble_client.scan_report_info['name'] - addr = ble_client.scan_report_info['addr'] - rssi = ble_client.scan_report_info['rssi'] - addr_type = ble_client.scan_report_info['addr_type'] - addr_str = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]) - if device_name != '' and rssi != 0: - print('name: {}, addr: {}, rssi: {}, addr_type: {}'.format(device_name, addr_str, rssi, addr_type)) - print('raw_data: {}'.format(ble_client.scan_report_info['raw_data'])) - - if device_name == ble_client.ble_server_name: # 扫描到目标设备后就停止扫描 - ret = ble_client.stop_scan() - if ret != 0: - ble_client.gatt_close() - break - - ret = ble_client.connect() - if ret != 0: - ble_client.gatt_close() - break - else: - print('ble scan failed.') - ret = ble_client.stop_scan() - if ret != 0: - ble_client.gatt_close() - break - elif event_id == event.BLE_GATT_MTU: - print('') - print('event_id : BLE_GATT_MTU, status = {}'.format(status)) - if status == 0: - handle = msg[2] - ble_mtu = msg[3] - print('handle = {:#06x}, ble_mtu = {}'.format(handle, ble_mtu)) - else: - print('ble connect mtu failed.') - ble_client.gatt_close() - break - elif event_id == event.BLE_GATT_RECV_NOTIFICATION_IND: - print('') - print('event_id : BLE_GATT_RECV_NOTIFICATION_IND, status = {}'.format(status)) - if status == 0: - data_len = msg[2] - data = msg[3] - print('len={}, data:{}'.format(data_len, data)) - handle = (data[1] << 8) | data[0] - print('handle = {:#06x}'.format(handle)) - else: - print('ble receive notification failed.') - break - elif event_id == event.BLE_GATT_RECV_INDICATION_IND: - print('') - print('event_id : BLE_GATT_RECV_INDICATION_IND, status = {}'.format(status)) - if status == 0: - data_len = msg[2] - data = msg[3] - print('len={}, data:{}'.format(data_len, data)) - else: - print('ble receive indication failed.') - break - elif event_id == event.BLE_GATT_START_DISCOVER_SERVICE_IND: - ''' - 收到BLE_GATT_START_DISCOVER_SERVICE_IND事件之后,即可开始发现Server端的服务services - ''' - print('') - print('event_id : BLE_GATT_START_DISCOVER_SERVICE_IND, status = {}'.format(status)) - if status == 0: - ble_client.characteristic_count = 0 - ble_client.chara_descriptor_count = 0 - ble_client.characteristic_index = 0 - ble_client.gatt_statue = gatt_status.BLE_GATT_DISCOVER_SERVICE - ''' - 如果知道服务service的UUID,则可以直接通过UUID来查找特定的服务;也可以直接查找所有的服务; - 本示例中,默认直接查找所有服务,因此设置discover_service_mode为0 - ''' - if ble_client.discover_service_mode == 0: - print('execute the function discover_all_service.') - ret = ble_client.discover_all_service() - else: - print('execute the function discover_service_by_uuid.') - ret = ble_client.discover_service_by_uuid() - if ret != 0: - print('Execution result: Failed.') - ble_client.gatt_close() - break - else: - print('ble start discover service failed.') - ble_client.gatt_close() - break - elif event_id == event.BLE_GATT_DISCOVER_SERVICE_IND: - print('') - print('event_id : BLE_GATT_DISCOVER_SERVICE_IND, status = {}'.format(status)) - ''' - 每发现一个service,都会上报一次BLE_GATT_DISCOVER_SERVICE_IND事件,当end_handle为0xFFFF的时候, - 说明已经发现了所有的services - ''' - if status == 0: - start_handle = msg[2] - end_handle = msg[3] - short_uuid = msg[4] - print('start_handle = {:#06x}, end_handle = {:#06x}, short_uuid = {:#06x}'.format(start_handle, end_handle, short_uuid)) - if ble_client.discover_service_mode == 0: # discover service all - if ble_client.target_service['short_uuid'] == short_uuid: # 查找到所有服务后, 按指定uuid查找特征值 - ble_client.target_service['start_handle'] = start_handle - ble_client.target_service['end_handle'] = end_handle - ble_client.gatt_statue = gatt_status.BLE_GATT_DISCOVER_CHARACTERISTIC - print('execute the function discover_all_characteristic.') - ret = ble_client.discover_all_characteristic() - if ret != 0: - print('Execution result: Failed.') - ble_client.gatt_close() - break - else: - ble_client.target_service['start_handle'] = start_handle - ble_client.target_service['end_handle'] = end_handle - ble_client.gatt_statue = gatt_status.BLE_GATT_DISCOVER_CHARACTERISTIC - print('execute the function discover_all_characteristic.') - ret = ble_client.discover_all_characteristic() - if ret != 0: - print('Execution result: Failed.') - ble_client.gatt_close() - break - else: - print('ble discover service failed.') - ble_client.gatt_close() - break - elif event_id == event.BLE_GATT_DISCOVER_CHARACTERISTIC_DATA_IND: - print('') - print('event_id : BLE_GATT_DISCOVER_CHARACTERISTIC_DATA_IND, status = {}'.format(status)) - if status == 0: - data_len = msg[2] - payload = msg[3] - pair_len = payload[0] - print('data_len={}, pair_len={}, payload:{}'.format(data_len, pair_len, payload)) - ''' - payload的数据结构:pair_len+[特征句柄+特征属性+特征值句柄+特征UUID]+...+[特征句柄+特征属性+特征值句柄+特征UUID] - ''' - if data_len > 0: - if ble_client.gatt_statue == gatt_status.BLE_GATT_DISCOVER_CHARACTERISTIC: - i = 0 - while i < (data_len - 1) / pair_len: - chara_dict = { - 'handle': (payload[i * pair_len + 2] << 8) | payload[i * pair_len + 1], - 'properties': payload[i * pair_len + 3], - 'value_handle': (payload[i * pair_len + 5] << 8) | payload[i * pair_len + 4], - 'uuid_type': 0, - 'short_uuid': 0x0000, - 'long_uuid': bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) - } - print('handle={:#06x}, properties={:#x}, value_handle={:#06x}'.format(chara_dict['handle'], chara_dict['properties'], chara_dict['value_handle'])) - if pair_len == ble_client.ble_short_uuid_pair_len: - chara_dict['uuid_type'] = 1 - chara_dict['short_uuid'] = (payload[i * pair_len + 7] << 8) | payload[i * pair_len + 6] - print('short_uuid:{:#06x}'.format(chara_dict['short_uuid'])) - elif pair_len == ble_client.ble_long_uuid_pair_len: - start_index = i * pair_len + 6 - end_index = start_index + 16 - chara_dict['uuid_type'] = 0 - chara_dict['long_uuid'] = payload[start_index : end_index] - print('long_uuid:{}'.format(chara_dict['long_uuid'])) - i += 1 - if ble_client.characteristic_count < 5: - ble_client.characteristic_list.append(chara_dict) - ble_client.characteristic_count = len(ble_client.characteristic_list) - print('characteristic_list len = {}'.format(ble_client.characteristic_count)) - elif ble_client.gatt_statue == gatt_status.BLE_GATT_READ_CHARA_VALUE: - print('data_len = {}'.format(data_len)) - print('payload = {:02x},{:02x},{:02x},{:02x}'.format(payload[0], payload[1], payload[2], payload[3])) - else: - print('ble discover characteristic failed.') - ble_client.gatt_close() - break - elif event_id == event.BLE_GATT_DISCOVER_CHARA_DESC_IND: - print('') - print('event_id : BLE_GATT_DISCOVER_CHARA_DESC_IND, status = {}'.format(status)) - if status == 0: - data_len = msg[2] - data = msg[3] - fmt = data[0] - print('fmt={}, len={}, data:{}'.format(fmt, data_len, data)) - if data_len > 0: - i = 0 - if fmt == 1: # 16 bit uuid - while i < (data_len - 1) / 4: - descriptor_dict = { - 'handle': (data[i * 4 + 2] << 8) | data[i * 4 + 1], - 'short_uuid': (data[i * 4 + 4] << 8) | data[i * 4 + 3], - } - print('handle={:#06x}, uuid={:#06x}'.format(descriptor_dict['handle'], descriptor_dict['short_uuid'])) - i += 1 - if ble_client.chara_descriptor_count < 5: - ble_client.descriptor_list.append(descriptor_dict) - ble_client.chara_descriptor_count = len(ble_client.descriptor_list) - print('descriptor_list len = {}'.format(ble_client.chara_descriptor_count)) - if ble_client.characteristic_index == ble_client.characteristic_count: - print('execute the function read_characteristic_by_uuid.') - # ble_client.gatt_statue = gatt_status.BLE_GATT_WRITE_CHARA_VALUE - # ret = ble_client.write_characteristic() - # ret = ble_client.write_characteristic_no_rsp() - - ble_client.gatt_statue = gatt_status.BLE_GATT_READ_CHARA_VALUE - ret = ble_client.read_characteristic_by_uuid() - # ret = ble_client.read_characteristic_by_handle() - - # ble_client.gatt_statue = gatt_status.BLE_GATT_READ_CHARA_DESC - # ret = ble_client.read_characteristic_descriptor() - - # ble_client.gatt_statue = gatt_status.BLE_GATT_WRITE_CHARA_DESC - # ret = ble_client.write_characteristic_descriptor() - else: - print('execute the function discover_all_characteristic_descriptor.') - ret = ble_client.discover_all_characteristic_descriptor() - if ret != 0: - print('Execution result: Failed.') - ble_client.gatt_close() - break - else: - print('ble discover characteristic descriptor failed.') - ble_client.gatt_close() - break - elif event_id == event.BLE_GATT_CHARA_WRITE_WITH_RSP_IND: - print('') - print('event_id : BLE_GATT_CHARA_WRITE_WITH_RSP_IND, status = {}'.format(status)) - if status == 0: - if ble_client.gatt_statue == gatt_status.BLE_GATT_WRITE_CHARA_VALUE: - pass - elif ble_client.gatt_statue == gatt_status.BLE_GATT_WRITE_CHARA_DESC: - pass - else: - print('ble write characteristic with response failed.') - break - elif event_id == event.BLE_GATT_CHARA_WRITE_WITHOUT_RSP_IND: - print('') - print('event_id : BLE_GATT_CHARA_WRITE_WITHOUT_RSP_IND, status = {}'.format(status)) - if status == 0: - print('write characteristic value without response successful.') - else: - print('write characteristic value without response failed.') - break - elif event_id == event.BLE_GATT_CHARA_READ_IND: - print('') - # read characteristic value by handle - print('event_id : BLE_GATT_CHARA_READ_IND, status = {}'.format(status)) - if status == 0: - data_len = msg[2] - data = msg[3] - print('data_len = {}, data : {}'.format(data_len, data)) - if ble_client.gatt_statue == gatt_status.BLE_GATT_READ_CHARA_VALUE: - # print('read characteristic value by handle.') - pass - else: - print('ble read characteristic failed.') - break - elif event_id == event.BLE_GATT_CHARA_READ_BY_UUID_IND: - print('') - # read characteristic value by uuid - print('event_id : BLE_GATT_CHARA_READ_BY_UUID_IND, status = {}'.format(status)) - if status == 0: - data_len = msg[2] - data = msg[3] - print('data_len = {}, data : {}'.format(data_len, data)) - handle = (data[2] << 8) | data[1] - print('handle = {:#06x}'.format(handle)) - else: - print('ble read characteristic by uuid failed.') - break - elif event_id == event.BLE_GATT_CHARA_MULTI_READ_IND: - print('') - # read multiple characteristic value - print('event_id : BLE_GATT_CHARA_MULTI_READ_IND, status = {}'.format(status)) - if status == 0: - data_len = msg[2] - data = msg[3] - print('data_len = {}, data : {}'.format(data_len, data)) - else: - print('ble read multiple characteristic by uuid failed.') - break - elif event_id == event.BLE_GATT_DESC_WRITE_WITH_RSP_IND: - print('') - print('event_id : BLE_GATT_DESC_WRITE_WITH_RSP_IND, status = {}'.format(status)) - if status == 0: - if ble_client.gatt_statue == gatt_status.BLE_GATT_WRITE_CHARA_VALUE: - pass - elif ble_client.gatt_statue == gatt_status.BLE_GATT_WRITE_CHARA_DESC: - pass - else: - print('ble write characteristic descriptor failed.') - break - elif event_id == event.BLE_GATT_DESC_READ_IND: - print('') - # read characteristic descriptor - print('event_id : BLE_GATT_DESC_READ_IND, status = {}'.format(status)) - if status == 0: - data_len = msg[2] - data = msg[3] - print('data_len = {}, data : {}'.format(data_len, data)) - if ble_client.gatt_statue == gatt_status.BLE_GATT_READ_CHARA_DESC: - # print('read characteristic descriptor.') - pass - else: - print('ble read characteristic descriptor failed.') - break - elif event_id == event.BLE_GATT_ATT_ERROR_IND: - print('') - print('event_id : BLE_GATT_ATT_ERROR_IND, status = {}'.format(status)) - if status == 0: - errcode = msg[2] - print('errcode = {:#06x}'.format(errcode)) - if ble_client.gatt_statue == gatt_status.BLE_GATT_DISCOVER_INCLUDES: - ble_client.gatt_statue = gatt_status.BLE_GATT_DISCOVER_CHARACTERISTIC - print('execute the function discover_all_characteristic.') - ret = ble_client.discover_all_characteristic() - if ret != 0: - print('Execution result: Failed.') - ble_client.gatt_close() - break - elif ble_client.gatt_statue == gatt_status.BLE_GATT_DISCOVER_CHARACTERISTIC: - ble_client.gatt_statue = gatt_status.BLE_GATT_IDLE - print('execute the function discover_all_characteristic_descriptor.') - ret = ble_client.discover_all_characteristic_descriptor() - if ret != 0: - print('Execution result: Failed.') - ble_client.gatt_close() - break - else: - print('ble attribute error.') - ble_client.gatt_close() - break - else: - print('unknown event id : {}.'.format(event_id)) - - # ble_client.release() - - -event = EVENT(event_dict) -gatt_status = EVENT(gatt_status_dict) -msg_queue = Queue(50) -ble_client = BleClient() - - -def main(): - print('create client event handler task.') - _thread.start_new_thread(ble_gatt_client_event_handler, ()) - # ble.setScanFilter(0) # 关闭扫描过滤功能 - ret = ble_client.gatt_open() - if ret != 0: - return -1 - - count = 0 - while True: - utime.sleep(1) - count += 1 - cur_time = utime.localtime() - timestamp = "{:02d}:{:02d}:{:02d}".format(cur_time[3], cur_time[4], cur_time[5]) - if count % 5 == 0: - print('[{}] BLE Client running, count = {}......'.format(timestamp, count)) - print('') - if count > 130: - count = 0 - print('!!!!! stop BLE Client now !!!!!') - ble_status = ble_client.gatt_get_status() - if ble_status == 1: - ble_client.gatt_close() - ble_client.release() - break - else: - ble_status = ble_client.gatt_get_status() - if ble_status == 0: # stopped - print('BLE connection has been disconnected.') - ble_client.release() - break - -if __name__ == '__main__': - main() - - -``` - - - -### 5.3 说明 - -#### 5.3.1 `event_id`为26的事件说明 - -该事件是在BLE建立连接后,C层代码自动返回的一个消息,作用是通知用户,可以开始查找服务了。 - -#### 5.3.2 `event_id`为27的事件说明 - -当用户使用`ble.discoverAllService`或`ble.discoverByUUID`方法发起查找服务,在发现服务后,会通过该事件来通知用户,并将发现的服务信息给到用户。每发现一个服务,都会上报一次该事件。注意和event_id为26的事件的区别。 - - - -> 上述提到,每发现一个服务,都会上报一次`event_id`为27的事件,那么如何判断已经发现对端设备的所有服务了? -> -> `event_id`为27的事件上报时,会将每个服务的起始句柄、结束句柄以及UUID给到用户,而当某个服务的结束句柄值为0xFFFF时,说明这个服务就是最后一个服务了。可以通过这个来判断是否已经发现了所有的服务。 - - - -#### 5.3.3 `event_id`为28的事件说明 - -该事件表示的是发现了某个服务的所有特征,同时将这些特征数据上报给用户。这里重点说一下该事件中携带的消息的数据结构,方便用户去解析。 - -通过wiki文档说明,可以知道该事件携带的消息有4个参数,前3个参数在wiki上已经有明确说明,这里重点说明第4个参数`data`的数据结构,`data`中包含了某个服务的所有特征信息,比如特征句柄、属性、特征值句柄和UUID。具体的数据结构分两种情况。 - -第一种,服务端添加特征时用的都是蓝牙定义的标准UUID。这些UUID都可以用2个字节的短UUID来表示。这种情况下,`data`数据结构可以参考蓝牙官方协议文档中《BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] 部分的CHARACTERISTIC DISCOVERY 章节》,如下: - -![第4个参数数据结构](../../media/hardware/bt/Read_By_Type_Respone.png) - -`data`数据结构如上图中`Read By Type Response`部分。第一个参数是0x07,表示每个特征数据项的长度。而每个特征数据项结构为: - -`特征句柄(2字节)+ 特征属性(1字节)+ 特征值句柄(2字节)+ UUID(2字节)` - -示例: - -假如data数据如下: - -``` -data = (0x07,0x02,0x00,0x32,0x03,0x00,0x19,0x2a,0x05,0x00,0x32,0x06,0x00,0x1a,0x2a) -``` - -数据解析如下: - -| | 数据 | 说明 | -| ----------------- | --------- | ------------------------------------------------- | -| data[0] | 0x07 | 表示每个特征数据项的长度为7个字节。 | -| data[1]~data[2] | 0x02,0x00 | 表示第1个特征的句柄为0x0002。 | -| data[3] | 0x32 | 表示第1个特征的属性为0x32,表示可读、通知和指示。 | -| data[4]~data[5] | 0x03,0x00 | 表示第1个特征值的句柄为0x0003。 | -| data[6]~data[7] | 0x19,0x2a | 表示第1个特征的UUID为0x2a19。 | -| data[8]~data[9] | 0x05,0x00 | 表示第2个特征的句柄为0x0002。 | -| data[10] | 0x32 | 表示第2个特征的属性为0x32,表示可读、通知和指示。 | -| data[11]~data[12] | 0x06,0x00 | 表示第2个特征值的句柄为0x0003。 | -| data[13]~data[14] | 0x1a,0x2a | 表示第2个特征的UUID为0x2a19。 | - - - -第二种,服务端添加特征时用的自定义的UUID或者不在蓝牙SIG定义的范围内,那么UUID将会是16字节的长UUID。这种情况下,data的数据结构同样由1个或者多个特征项构成,特征项的长度为0x15,表示每个特征项长度为21字节,每个特征项的数据结构如下: - -` 特征句柄(2字节)+ 特征属性(1字节)+ 特征值句柄(2字节)+ UUID(16字节)` - -示例: - -假如data数据如下: - -``` -data = (0x15,0x02,0x00,0x32,0x03,0x00,0x00,0x00,0x20,0x11,0x00,0x00,0x10,0x00,0x80,0x00,0x00,0x80,0x5f,0x9b,0x34,0xfb) -``` - -数据解析如下: - -| | 数据 | 说明 | -| --------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| data[0] | 0x15 | 表示每个特征数据项的长度为21个字节。 | -| data[1]~data[2] | 0x02,0x00 | 表示第1个特征的句柄为0x0002。 | -| data[3] | 0x32 | 表示第1个特征的属性为0x32,表示可读、通知和指示。 | -| data[4]~data[5] | 0x03,0x00 | 表示第1个特征值的句柄为0x0003。 | -| data[6]~data[7] | 0x00,0x00,0x20,0x11,0x00,0x00,0x10,0x00,
0x80,0x00,0x00,0x80,0x5f,0x9b,0x34,0xfb | 表示第1个特征的UUID为`00002011-0000-1000-8000-00805F9B34FB`。 | - - - -> 自定义的16字节长UUID,并不是随便定义的,它是基于蓝牙规范中16字节的UUID模板扩展出来的。规范中的长UUID模板通常为:`0000xxxx-0000-1000-8000-00805F9B34FB ` -> -> 将`xxxx`部分替换为我们自定义的2字节UUID,就得到了一个16字节的长UUID。 - - - -#### 5.3.4 `event_id`为29的事件说明 - -该事件表示的是发现了某个特征的描述符,同时将这些特征描述符相关数据上报给用户。这里重点说一下该事件中携带消息的数据结构,方便用户去解析。 - -通过wiki文档说明,可以知道该事件携带的消息有4个参数,前3个参数在wiki上已经有明确说明,这里重点说明第4个参数`data`的数据结构,`data`中包含了一个或多个属性句柄和UUID对。数据结构如下: - -`格式字段format(1字节)+ 属性句柄(2字节)+ UUID(2字节或16字节)` - -其中,格式字段用来表示返回的属性句柄和UUID对的格式。其中format有如下两种取值: - -| format | 说明 | -| ------ | ------------------------------------------------------------ | -| 0x01 | 表示每个属性句柄和UUID对中的UUID是2字节的。这通常对应于Bluetooth SIG定义的标准UUID。 | -| 0x02 | 表示每个属性句柄和UUID对中的UUID是16字节的。这通常对应于自定义UUID或不在Bluetooth SIG定义范围内的UUID。 | - -示例: - -假如data数据如下: - -``` -data = 0x01,0x04,0x00,0x02,0x29 -``` - -数据解析如下: - -| | 数据 | 说明 | -| --------------- | --------- | ----------------------------------------- | -| data[0] | 0x01 | 表示每个属性句柄和UUID对的UUID是2字节的。 | -| data[1]~data[2] | 0x04,0x00 | 表示属性句柄是0x0004 | -| data[3]~data[4] | 0x02,0x29 | 表述属性的UUID是0x2902 | - diff --git a/docs/Getting_started/zh/hardware/bt/bt.md b/docs/Getting_started/zh/hardware/bt/bt.md deleted file mode 100644 index e69b2006548fbf08d5abae7b460335035fd79fff..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/bt/bt.md +++ /dev/null @@ -1,951 +0,0 @@ -# BT 使用说明 - -本文主要介绍如何使用QuecPython的BT功能,包含对HFP、A2DP、AVRCP和SPP的介绍和使用,并提供相关示例说明。 - -## 1 BT API使用说明 - -请参考QuecPython官方网站上的Wiki说明:[BT API 说明](../../../../API_reference/zh/wifibtgnss/bt.md) - -## 2 BT HFP 功能 - -HFP(Hands-Free Profile)是一种蓝牙通信协议,常用于手机和车载系统或蓝牙耳机之间进行语音通信。它定义了车载系统或蓝牙耳机和手机之间进行语音通信的规范,使用户可以直接使用车载系统或蓝牙耳机进行电话呼叫、接听、挂断、拒接以及控制通话音量,而无需去操作手机。 - -HFP协议中有两种角色,分别是音频网关(Audio Gateway,简称AG)和免提设备(Hands-Free Unit,简称HF)。当手机连接到蓝牙耳机时,手机充当的就是AG的角色,而蓝牙耳机充当的就是HF的角色。HFP协议框架如下: - -![HFP协议框架](../../media/hardware/bt/HFP协议框架.png) - - - -> QuecPython 经典蓝牙的HFP功能,目前仅支持做HF端,不支持做AG端。 - - - -### 2.1 AG - -AG是作为音频网关的设备,它负责处理音频信号并将其传输到HF端。AG的主要功能如下: - -* 接收和发送音频信号:比如将来自电话的语音通过蓝牙连接发送到HF设备,并接收来自HF设备的音频信号。 - -* 执行控制命令:AG可以接收来自HF设备的控制命令,比如拨打电话、接听电话、挂断电话以及调整音量等命令,并根据这些命令执行相应的操作。 -* 提供一些状态信息:AG可以将一些设备的状态信息发送给HF,比如电池电量、网络注册状态、信号强度等。 - -### 2.2 HF - -HF是连接到AG并接收其音频信号的设备。而HF设备的主要功能如下: - -* 接收和发送音频信号:HF设备接收来自AG的音频信号并将其播放出来,同时HF设备也可以捕获音频信号并将其发给AG。 - -* 控制AG端行为:HF设备可以发送一些控制命令给AG,比如拨打电话、接听电话、挂断电话、控制通话音量等。 - -* 显示状态:HF设备可以接收来自AG设备的一些状态信息,并将其显示给用户。 - - - -### 2.3 流程 - -QuecPython的HFP功能使用流程如下: - -![BT HFP 流程](../../media/hardware/bt/BT_HFP流程.png) - -> 上述流程中,需要注意: -> -> “建立HFP连接”和“断开HFP连接”的操作,可以由AG端发起,也可以由HF端发起,具体由哪一端发起,通常取决于具体的使用场景。 - - - -### 2.4 示例 - -下面提供了一个关于HFP功能的使用示例,该示例中,模组是作为HF端(相当于蓝牙耳机),手机作为AG端,模组等待手机主动发起连接。要运行该例程,需要准备2部手机,手机A主动连接模组,建立HFP连接后,使用手机B拨打电话给手机A,模组在收到电话响铃事件后,接听电话。 - -```python -# -*- coding: UTF-8 -*- - -""" -示例说明:本例程提供一个通过HFP自动接听电话的功能 -运行平台:EC600UCN_LB 铀开发板 -运行本例程后,通过手机A搜索到设备名并点击连接;然后通过手机B拨打电话给手机A, -当手机A开始响铃震动时,设备会自动接听电话 -""" -import bt -import utime -import _thread -from queue import Queue -from machine import Pin - -# 如果对应播放通道外置了PA,且需要引脚控制PA开启,则需要下面步骤 -# 具体使用哪个GPIO取决于实际使用的引脚 -gpio11 = Pin(Pin.GPIO11, Pin.OUT, Pin.PULL_DISABLE, 0) -gpio11.write(1) - -BT_NAME = 'QuecPython-hfp' - -BT_EVENT = { - 'BT_START_STATUS_IND': 0, # bt/ble start - 'BT_STOP_STATUS_IND': 1, # bt/ble stop - 'BT_HFP_CONNECT_IND': 40, # bt hfp connected - 'BT_HFP_DISCONNECT_IND': 41, # bt hfp disconnected - 'BT_HFP_CALL_IND': 42, # bt hfp call state - 'BT_HFP_CALL_SETUP_IND': 43, # bt hfp call setup state - 'BT_HFP_NETWORK_IND': 44, # bt hfp network state - 'BT_HFP_NETWORK_SIGNAL_IND': 45, # bt hfp network signal - 'BT_HFP_BATTERY_IND': 46, # bt hfp battery level - 'BT_HFP_CALLHELD_IND': 47, # bt hfp callheld state - 'BT_HFP_AUDIO_IND': 48, # bt hfp audio state - 'BT_HFP_VOLUME_IND': 49, # bt hfp volume type - 'BT_HFP_NETWORK_TYPE': 50, # bt hfp network type - 'BT_HFP_RING_IND': 51, # bt hfp ring indication - 'BT_HFP_CODEC_IND': 52, # bt hfp codec type -} - -HFP_CONN_STATUS = 0 -HFP_CONN_STATUS_DICT = { - 'HFP_DISCONNECTED': 0, - 'HFP_CONNECTING': 1, - 'HFP_CONNECTED': 2, - 'HFP_DISCONNECTING': 3, -} -HFP_CALL_STATUS = 0 -HFP_CALL_STATUS_DICT = { - 'HFP_NO_CALL_IN_PROGRESS': 0, - 'HFP_CALL_IN_PROGRESS': 1, -} - -BT_IS_RUN = 0 - -msg_queue = Queue(30) - - -def get_key_by_value(val, d): - for key, value in d.items(): - if val == value: - return key - return None - -def bt_callback(args): - global msg_queue - msg_queue.put(args) - -def bt_event_proc_task(): - global msg_queue - global BT_IS_RUN - global BT_EVENT - global HFP_CONN_STATUS - global HFP_CONN_STATUS_DICT - global HFP_CALL_STATUS - global HFP_CALL_STATUS_DICT - - while True: - print('wait msg...') - msg = msg_queue.get() # 没有消息时会阻塞在这 - event_id = msg[0] - status = msg[1] - - if event_id == BT_EVENT['BT_START_STATUS_IND']: - print('event: BT_START_STATUS_IND') - if status == 0: - print('BT start successfully.') - BT_IS_RUN = 1 - bt_status = bt.getStatus() - if bt_status == 1: - print('BT status is 1, normal status.') - else: - print('BT status is {}, abnormal status.'.format(bt_status)) - bt.stop() - break - - retval = bt.getLocalName() - if retval != -1: - print('The current BT name is : {}'.format(retval[1])) - else: - print('Failed to get BT name.') - bt.stop() - break - - print('Set BT name to {}'.format(BT_NAME)) - retval = bt.setLocalName(0, BT_NAME) - if retval != -1: - print('BT name set successfully.') - else: - print('BT name set failed.') - bt.stop() - break - - retval = bt.getLocalName() - if retval != -1: - print('The new BT name is : {}'.format(retval[1])) - else: - print('Failed to get new BT name.') - bt.stop() - break - - # 设置蓝牙可见模式为:可以被发现并且可以被连接 - retval = bt.setVisibleMode(3) - if retval == 0: - mode = bt.getVisibleMode() - if mode == 3: - print('BT visible mode set successfully.') - else: - print('BT visible mode set failed.') - bt.stop() - break - else: - print('BT visible mode set failed.') - bt.stop() - break - else: - print('BT start failed.') - bt.stop() - break - elif event_id == BT_EVENT['BT_STOP_STATUS_IND']: - print('event: BT_STOP_STATUS_IND') - if status == 0: - BT_IS_RUN = 0 - print('BT stop successfully.') - else: - print('BT stop failed.') - break - elif event_id == BT_EVENT['BT_HFP_CONNECT_IND']: - HFP_CONN_STATUS = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_CONNECT_IND, {}, hfp_conn_status:{}, mac:{}'.format(status, get_key_by_value(msg[2], HFP_CONN_STATUS_DICT), mac)) - if status != 0: - print('BT HFP connect failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_HFP_DISCONNECT_IND']: - HFP_CONN_STATUS = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_DISCONNECT_IND, {}, hfp_conn_status:{}, mac:{}'.format(status, get_key_by_value(msg[2], HFP_CONN_STATUS_DICT), mac)) - if status != 0: - print('BT HFP disconnect failed.') - bt.stop() - elif event_id == BT_EVENT['BT_HFP_CALL_IND']: - call_sta = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_CALL_IND, {}, hfp_call_status:{}, mac:{}'.format(status, get_key_by_value(msg[2], HFP_CALL_STATUS_DICT), mac)) - if status != 0: - print('BT HFP call failed.') - bt.stop() - continue - - if call_sta == HFP_CALL_STATUS_DICT['HFP_NO_CALL_IN_PROGRESS']: - if HFP_CALL_STATUS == HFP_CALL_STATUS_DICT['HFP_CALL_IN_PROGRESS']: - HFP_CALL_STATUS = call_sta - if HFP_CONN_STATUS == HFP_CONN_STATUS_DICT['HFP_CONNECTED']: - print('call ended, ready to disconnect hfp.') - retval = bt.hfpDisconnect(addr) - if retval == 0: - HFP_CONN_STATUS = HFP_CONN_STATUS_DICT['HFP_DISCONNECTING'] - else: - print('Failed to disconnect hfp connection.') - bt.stop() - continue - else: - if HFP_CALL_STATUS == HFP_CALL_STATUS_DICT['HFP_NO_CALL_IN_PROGRESS']: - HFP_CALL_STATUS = call_sta - print('set audio output channel to 2.') - bt.setChannel(2) - print('set volume to 7.') - retval = bt.hfpSetVolume(addr, 7) - if retval != 0: - print('set volume failed.') - elif event_id == BT_EVENT['BT_HFP_CALL_SETUP_IND']: - call_setup_status = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_CALL_SETUP_IND, {}, hfp_call_setup_status:{}, mac:{}'.format(status, call_setup_status, mac)) - if status != 0: - print('BT HFP call setup failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_HFP_CALLHELD_IND']: - callheld_status = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_CALLHELD_IND, {}, callheld_status:{}, mac:{}'.format(status, callheld_status, mac)) - if status != 0: - print('BT HFP callheld failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_HFP_NETWORK_IND']: - network_status = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_NETWORK_IND, {}, network_status:{}, mac:{}'.format(status, network_status, mac)) - if status != 0: - print('BT HFP network status failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_HFP_NETWORK_SIGNAL_IND']: - network_signal = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_NETWORK_SIGNAL_IND, {}, signal:{}, mac:{}'.format(status, network_signal, mac)) - if status != 0: - print('BT HFP network signal failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_HFP_BATTERY_IND']: - battery_level = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_BATTERY_IND, {}, battery_level:{}, mac:{}'.format(status, battery_level, mac)) - if status != 0: - print('BT HFP battery level failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_HFP_AUDIO_IND']: - audio_status = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_AUDIO_IND, {}, audio_status:{}, mac:{}'.format(status, audio_status, mac)) - if status != 0: - print('BT HFP audio failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_HFP_VOLUME_IND']: - volume_type = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_VOLUME_IND, {}, volume_type:{}, mac:{}'.format(status, volume_type, mac)) - if status != 0: - print('BT HFP volume failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_HFP_NETWORK_TYPE']: - service_type = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_NETWORK_TYPE, {}, service_type:{}, mac:{}'.format(status, service_type, mac)) - if status != 0: - print('BT HFP network service type failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_HFP_RING_IND']: - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_RING_IND, {}, mac:{}'.format(status, mac)) - if status != 0: - print('BT HFP ring failed.') - bt.stop() - continue - retval = bt.hfpAnswerCall(addr) - if retval == 0: - print('The call was answered successfully.') - else: - print('Failed to answer the call.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_HFP_CODEC_IND']: - codec_type = msg[2] - addr = msg[3] # BT 主机端mac地址 - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('BT_HFP_CODEC_IND, {}, codec_type:{}, mac:{}'.format(status, codec_type, mac)) - if status != 0: - print('BT HFP codec failed.') - bt.stop() - continue - print('Ready to release hfp.') - bt.hfpRelease() - bt.release() - - -def main(): - global BT_IS_RUN - - _thread.start_new_thread(bt_event_proc_task, ()) - - retval = bt.init(bt_callback) - if retval == 0: - print('BT init successful.') - else: - print('BT init failed.') - return -1 - retval = bt.hfpInit() - if retval == 0: - print('HFP init successful.') - else: - print('HFP init failed.') - return -1 - retval = bt.start() - if retval == 0: - print('BT start successful.') - else: - print('BT start failed.') - retval = bt.hfpRelease() - if retval == 0: - print('HFP release successful.') - else: - print('HFP release failed.') - retval = bt.release() - if retval == 0: - print('BT release successful.') - else: - print('BT release failed.') - return -1 - - count = 0 - while True: - utime.sleep(1) - count += 1 - cur_time = utime.localtime() - timestamp = "{:02d}:{:02d}:{:02d}".format(cur_time[3], cur_time[4], cur_time[5]) - - if count % 5 == 0: - if BT_IS_RUN == 1: - print('[{}] BT HFP is running, count = {}......'.format(timestamp, count)) - print('') - else: - print('BT HFP has stopped running, ready to exit.') - break - - -if __name__ == '__main__': - main() - -``` - - - -## 3 BT A2DP/AVRCP 功能 - -A2DP和AVRCP是两种蓝牙音频设备中常用的协议。并且这两种协议是密切相关的,一般都放在一起使用,以提供完整的音频服务。因此我们将这两种协议放在一起进行说明。 - -### 3.1 A2DP - -A2DP (Advanced Audio Distribution Profile) ,是一种用于将高质量音频流从一个设备传输到另一个设备的协议。比如将手机上的音频流数据传输到蓝牙耳机上,由蓝牙耳机进行音频播放。 - -A2DP协议中,有两种角色,分别是Audio Source Side和Audio Sink Side。这两种角色在音频传输过程中负责不同的职责: - -* Audio Source Side:表示音频数据的来源端,即音频数据的发送方。比如使用蓝牙耳机播放手机端音乐时,手机就是Audio Source Side。 -* Audio Sink Side:表示音频数据的接收端。比如使用蓝牙耳机播放手机端音乐时,耳机就是Audio Sink Side。 - -A2DP协议框架可参考官方文档中的如下部分: - -![A2DP协议框架](../../media/hardware/bt/A2DP协议框架.png) - - - -> QuecPython 经典蓝牙的A2DP功能,目前仅支持做Audio Sink Side,不支持做Audio Source Side。 -> -> A2DP是单向传输协议,即数据只能从Audio Source Side传输到Audio Sink Side,不能反向传输。 - - - -### 3.2 AVRCP - -AVRCP (Audio/Video Remote Control Profile),是一种控制协议,它提供一种机制,允许一个设备去控制另一个设备的音频或视频播放,比如蓝牙耳机控制手机端音乐播放行为,进行播放、暂停、上一首、下一首等操作。 - -AVRCP协议中,有两种角色,分别是Controller Side(简称CT)和Target Side(简称TG)。两种角色说明如下: - -* Controller Side:表示远程控制的发送方,即发送控制命令的设备。比如使用蓝牙耳机来控制手机音乐的播放时,蓝牙耳机就是Controller Side,耳机可以发送播放、暂停、停止、上一首、下一首等控制命令给手机。 -* Target Side:表示远程控制的接收方,即被控制的设备。比如使用蓝牙耳机来控制手机音乐播放时,手机就是Target Side,手机接收来自于蓝牙耳机的控制命令并进行相应的操作。 - -AVRCP协议框架可参考官方文档中的如下部分: - -AVRCP协议框架 - - - -> QuecPython 经典蓝牙的AVRCP功能,目前仅支持做Controller Side,不支持做Target Side。 - - - -### 3.3 流程 - -QuecPython的HFP功能使用流程如下: - -![BT A2DP-AVRCP 流程](../../media/hardware/bt/BT_A2DP_AVRCP流程.png) - - - -### 3.4 示例 - -下面提供了一个关于A2DP和AVRCP功能的使用示例,该示例演示的是使用模组来控制手机进行音乐播放、暂停、上一首、下一首还有音量设置的功能。该例程中,在建立蓝牙连接后,会弹出一个简易的菜单,菜单中描述了不同的数字对应的功能,用户输入对应数字后敲回车键,即可触发对应的功能。 - -```python -#A2DP/AVRCP 示例程序 - -""" -示例说明:本例程提供一个通过A2DP/AVRCP实现的简易蓝牙音乐播放控制功能 -运行平台:EC600UCN_LB 铀开发板 -运行本例程后,通过手机搜索到设备名并点击连接;然后打开手机上的音乐播放软件, -回到例程运行界面,根据提示菜单输入对应的控制命令来实现音乐的播放,暂停,上一首, -下一首以及设置音量的功能 -""" -import bt -import utime -import _thread -from queue import Queue -from machine import Pin - -BT_STATUS_DICT = { - 'BT_NOT_RUNNING': 0, - 'BT_IS_RUNNING': 1 -} - -A2DP_AVRCP_CONNECT_STATUS = { - 'DISCONNECTED': 0, - 'CONNECTING': 1, - 'CONNECTED': 2, - 'DISCONNECTING': 3 -} - -host_addr = 0 -msg_queue = Queue(10) - -# 如果对应播放通道外置了PA,且需要引脚控制PA开启,则需要下面步骤 -# 具体使用哪个GPIO取决于实际使用的引脚 -gpio11 = Pin(Pin.GPIO11, Pin.OUT, Pin.PULL_DISABLE, 0) -gpio11.write(1) - - -def cmd_proc(cmd): - cmds = ('1', '2', '3', '4', '5') - vols = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11') - - if cmd in cmds: - if cmd == '5': - while True: - tmp = input('Please input volume: ') - if len(tmp) != 1: - vol = tmp.split('Please input volume: ')[1] - else: - vol = tmp - if vol in vols: - return cmd, int(vol) - else: - print('Volume should be in [0,11], try again.') - else: - return cmd, 0 - else: - print('Command {} is not supported!'.format(cmd)) - return -1 - -def avrcp_play(args): - return bt.avrcpStart() - -def avrcp_pause(args): - return bt.avrcpPause() - -def avrcp_prev(args): - return bt.avrcpPrev() - -def avrcp_next(args): - return bt.avrcpNext() - -def avrcp_set_volume(vol): - return bt.avrcpSetVolume(vol) - -def bt_callback(args): - pass - -def bt_a2dp_avrcp_proc_task(): - global msg_queue - - cmd_handler = { - '1': avrcp_play, - '2': avrcp_pause, - '3': avrcp_prev, - '4': avrcp_next, - '5': avrcp_set_volume, - } - while True: - # print('wait msg...') - msg = msg_queue.get() - print('recv msg: {}'.format(msg)) - cmd_handler.get(msg[0])(msg[1]) - - -def main(): - global host_addr - global msg_queue - - _thread.start_new_thread(bt_a2dp_avrcp_proc_task, ()) - bt.init(bt_callback) - bt.setChannel(2) # 音频输出通道切换,需要在bt.start()之前设置 - retval = bt.a2dpavrcpInit() - if retval == 0: - print('BT A2DP/AVRCP initialization succeeded.') - else: - print('BT A2DP/AVRCP initialization failed.') - return -1 - - retval = bt.start() - if retval != 0: - print('BT start failed.') - return -1 - - utime.sleep_ms(1500) - - old_name = bt.getLocalName() - if old_name == -1: - print('Get BT name error.') - return -1 - print('The current BT name is {}'.format(old_name[1])) - new_name = 'QuecPython-a2dp' - print('Set new BT name to {}'.format(new_name)) - retval = bt.setLocalName(0, new_name) - if retval == -1: - print('Set BT name failed.') - return -1 - cur_name = bt.getLocalName() - if cur_name == -1: - print('Get new BT name error.') - return -1 - else: - if cur_name[1] == new_name: - print('BT name changed successfully.') - else: - print('BT name changed failed.') - - visible_mode = bt.getVisibleMode() - if visible_mode != -1: - print('The current BT visible mode is {}'.format(visible_mode)) - else: - print('Get BT visible mode error.') - return -1 - - print('Set BT visible mode to 3.') - retval = bt.setVisibleMode(3) - if retval == -1: - print('Set BT visible mode error.') - return -1 - - print('BT reconnect check start......') - bt.reconnect_set(25, 2) - bt.reconnect() - - count = 0 - while True: - count += 1 - if count % 5 == 0: - print('waiting to be connected...') - if count >= 10000: - count = 0 - a2dp_status = bt.a2dpGetConnStatus() - avrcp_status = bt.avrcpGetConnStatus() - if a2dp_status == A2DP_AVRCP_CONNECT_STATUS['CONNECTED'] and avrcp_status == A2DP_AVRCP_CONNECT_STATUS['CONNECTED']: - print('========== BT connected! =========') - addr = bt.a2dpGetAddr() - if addr != -1: - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('The BT address on the host side: {}'.format(mac)) - host_addr = addr - else: - print('Get BT addr error.') - return -1 - print('Please open the music player software on your phone first.') - print('Please enter the following options to select a function:') - print('========================================================') - print('1 : play') - print('2 : pause') - print('3 : prev') - print('4 : next') - print('5 : set volume') - print('6 : exit') - print('========================================================') - while True: - tmp = input('> ') - if len(tmp) != 1: - cmd = tmp.split('> ')[1] - else: - cmd = tmp - if cmd == '6': - break - retval = cmd_proc(cmd) - if retval != -1: - msg_queue.put(retval) - break - else: - utime.sleep_ms(1000) - print('Ready to disconnect a2dp.') - retval = bt.a2dpDisconnect(host_addr) - if retval == 0: - print('a2dp connection disconnected successfully') - else: - print('Disconnect a2dp error.') - print('Ready to stop BT.') - retval = bt.stop() - if retval == 0: - print('BT has stopped.') - else: - print('BT stop error.') - bt.a2dpavrcpRelease() - bt.release() - - -if __name__ == '__main__': - main() - -``` - - - -## 4 BT SPP 功能 - -SPP(Serial Port Profile)即串行端口配置协议。它模拟了RS-232串行端口的全双工通信特性,可以在两个蓝牙设备之间建立一种类似于串行口的无线数据连接,使得两个设备能够通过蓝牙进行数据传输。 - -SPP协议框架可参考官方文档中的如下部分: - -![SPP协议框架](../../media/hardware/bt/SPP协议框架.png) - - - - - -### 4.1 流程 - -![BT SPP 流程](../../media/hardware/bt/BT_SPP流程.png) - - - -> SPP协议中,需要使用SPP进行数据传输的设备A和设备B,都可以主动发起SPP连接。 - - - -### 4.2 示例 - -下面提供了一个SPP功能的例程,该例程是模组与手机通过SPP进行数据通信,且由模组主动发起SPP连接。需要注意的是,运行如下程序要求手机端安装了蓝牙SPP相关的APP。如果手机端没有安装SPP相关的应用程序,那么当模组向手机端发起SPP连接请求时,手机的蓝牙功能可能无法找到合适的服务来响应处理这个SPP连接请求,因此手机可能会忽略这个连接请求,并且不会弹出配对窗口。 - -```python -# -*- coding: UTF-8 -*- - -""" -示例说明:本例程提供一个通过SPP实现与手机端进行数据传输的功能 -(1)运行之前,需要先在手机端(安卓)安装SPP相关的APP,然后打开该软件; -(2)修改本例程中的目标设备的蓝牙名称,即 DST_DEVICE_INFO['dev_name'] 的值改为用户准备连接的手机的蓝牙名称; -(3)运行本例程,例程中会先发起搜索周边设备的操作,直到搜索到目标设备,就会结束搜索,然后向目标设备发起SPP连接请求; -(4)用户注意查看手机界面是否弹出蓝牙配对请求的界面,当出现时,点击配对; -(5)配对成功后,用户即可进入到蓝牙串口界面,发送数据给设备,设备在收到数据后会回复"I have received the data you sent." -(6)手机端APP中点击断开连接,即可结束例程; -""" -import bt -import utime -import _thread -from queue import Queue - - -BT_NAME = 'QuecPython-SPP' - -BT_EVENT = { - 'BT_START_STATUS_IND': 0, # bt/ble start - 'BT_STOP_STATUS_IND': 1, # bt/ble stop - 'BT_SPP_INQUIRY_IND': 6, # bt spp inquiry ind - 'BT_SPP_INQUIRY_END_IND': 7, # bt spp inquiry end ind - 'BT_SPP_RECV_DATA_IND': 14, # bt spp recv data ind - 'BT_SPP_CONNECT_IND': 61, # bt spp connect ind - 'BT_SPP_DISCONNECT_IND': 62, # bt spp disconnect ind -} - -DST_DEVICE_INFO = { - 'dev_name':'HUAWEI Mate40 Pro',# 要连接设备的蓝牙名称 - 'bt_addr': None -} - -BT_IS_RUN = 0 -msg_queue = Queue(30) - - -def bt_callback(args): - global msg_queue - msg_queue.put(args) - - -def bt_event_proc_task(): - global msg_queue - global BT_IS_RUN - global DST_DEVICE_INFO - - while True: - print('wait msg...') - msg = msg_queue.get() # 没有消息时会阻塞在这 - event_id = msg[0] - status = msg[1] - - if event_id == BT_EVENT['BT_START_STATUS_IND']: - print('event: BT_START_STATUS_IND') - if status == 0: - print('BT start successfully.') - BT_IS_RUN = 1 - - print('Set BT name to {}'.format(BT_NAME)) - retval = bt.setLocalName(0, BT_NAME) - if retval != -1: - print('BT name set successfully.') - else: - print('BT name set failed.') - bt.stop() - continue - - retval = bt.setVisibleMode(3) - if retval == 0: - mode = bt.getVisibleMode() - if mode == 3: - print('BT visible mode set successfully.') - else: - print('BT visible mode set failed.') - bt.stop() - continue - else: - print('BT visible mode set failed.') - bt.stop() - continue - - retval = bt.startInquiry(15) - if retval != 0: - print('Inquiry error.') - bt.stop() - continue - else: - print('BT start failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_STOP_STATUS_IND']: - print('event: BT_STOP_STATUS_IND') - if status == 0: - BT_IS_RUN = 0 - print('BT stop successfully.') - else: - print('BT stop failed.') - - retval = bt.sppRelease() - if retval == 0: - print('SPP release successfully.') - else: - print('SPP release failed.') - retval = bt.release() - if retval == 0: - print('BT release successfully.') - else: - print('BT release failed.') - break - elif event_id == BT_EVENT['BT_SPP_INQUIRY_IND']: - print('event: BT_SPP_INQUIRY_IND') - if status == 0: - rssi = msg[2] - name = msg[4] - addr = msg[5] - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('name: {}, addr: {}, rssi: {}'.format(name, mac, rssi)) - - if name == DST_DEVICE_INFO['dev_name']: - print('The target device is found, device name {}'.format(name)) - DST_DEVICE_INFO['bt_addr'] = addr - retval = bt.cancelInquiry() - if retval != 0: - print('cancel inquiry failed.') - continue - else: - print('BT inquiry failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_SPP_INQUIRY_END_IND']: - print('event: BT_SPP_INQUIRY_END_IND') - if status == 0: - print('BT inquiry has ended.') - inquiry_sta = msg[2] - if inquiry_sta == 0: - if DST_DEVICE_INFO['bt_addr'] is not None: - print('Ready to connect to the target device : {}'.format(DST_DEVICE_INFO['dev_name'])) - retval = bt.sppConnect(DST_DEVICE_INFO['bt_addr']) - if retval != 0: - print('SPP connect failed.') - bt.stop() - continue - else: - print('Not found device [{}], continue to inquiry.'.format(DST_DEVICE_INFO['dev_name'])) - bt.cancelInquiry() - bt.startInquiry(15) - else: - print('Inquiry end failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_SPP_RECV_DATA_IND']: - print('event: BT_SPP_RECV_DATA_IND') - if status == 0: - datalen = msg[2] - data = msg[3] - print('recv {} bytes data: {}'.format(datalen, data)) - send_data = 'I have received the data you sent.' - print('send data: {}'.format(send_data)) - retval = bt.sppSend(send_data) - if retval != 0: - print('send data faied.') - else: - print('Recv data failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_SPP_CONNECT_IND']: - print('event: BT_SPP_CONNECT_IND') - if status == 0: - conn_sta = msg[2] - addr = msg[3] - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('SPP connect successful, conn_sta = {}, addr {}'.format(conn_sta, mac)) - else: - print('Connect failed.') - bt.stop() - continue - elif event_id == BT_EVENT['BT_SPP_DISCONNECT_IND']: - print('event: BT_SPP_DISCONNECT_IND') - conn_sta = msg[2] - addr = msg[3] - mac = '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'.format(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]) - print('SPP disconnect successful, conn_sta = {}, addr {}'.format(conn_sta, mac)) - bt.stop() - continue - - -def main(): - global BT_IS_RUN - - _thread.start_new_thread(bt_event_proc_task, ()) - retval = bt.init(bt_callback) - if retval == 0: - print('BT init successful.') - else: - print('BT init failed.') - return -1 - retval = bt.sppInit() - if retval == 0: - print('SPP init successful.') - else: - print('SPP init failed.') - return -1 - retval = bt.start() - if retval == 0: - print('BT start successful.') - else: - print('BT start failed.') - retval = bt.sppRelease() - if retval == 0: - print('SPP release successful.') - else: - print('SPP release failed.') - return -1 - - count = 0 - while True: - utime.sleep(1) - count += 1 - cur_time = utime.localtime() - timestamp = "{:02d}:{:02d}:{:02d}".format(cur_time[3], cur_time[4], cur_time[5]) - - if count % 5 == 0: - if BT_IS_RUN == 1: - print('[{}] BT SPP is running, count = {}......'.format(timestamp, count)) - print('') - else: - print('BT SPP has stopped running, ready to exit.') - break - - -if __name__ == '__main__': - main() - -``` - diff --git a/docs/Getting_started/zh/hardware/matrix-keypad.md b/docs/Getting_started/zh/hardware/matrix-keypad.md deleted file mode 100644 index a26ffd2881169c3a15372c9d32141adf0fa4f6ae..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/matrix-keypad.md +++ /dev/null @@ -1,162 +0,0 @@ -# KeyPad - 矩阵键盘 - -本文将介绍什么是矩阵键盘,以及如何在QuecPython平台上使用keypad矩阵键盘功能。 - -## 什么是矩阵键盘 - -矩阵键盘是一种常见的输入设备,通常用于电子设备和计算机中。它由多个按键组成,按键排列成一个矩阵形式,并通过连接线与控制器相连。 - -其基本工作原理是通过行列扫描技术来检测按键的状态。具体来说,矩阵键盘中的按键被分为若干行和列,行列交叉部分形成了一个二维矩阵。控制器会依次扫描每一行或每一列,当检测到某一个按键被按下时,在该按键所处的行和列之间建立联系,从而确定该按键的位置和状态。 - -这种工作原理使得矩阵键盘具有很高的灵活性和可编程性,因为可以通过编程设置哪些按键被映射到哪些行和列上。此外,矩阵键盘还具有体积小、成本低、反应速度快等优点,因此被广泛应用于各种类型的电子设备中。 - -## 如何使用矩阵键盘 - -### 1. 硬件准备 - -在使用keypad矩阵键盘功能之前,需要准备以下硬件: - -- QuecPython开发板 -- keypad矩阵键盘模块 - -将keypad矩阵键盘模块连接到开发板的矩阵键盘接口上,具体支持的模块和引脚对应关系请查看[矩阵键盘](../../../API_reference/zh/QuecPython类库/machine.KeyPad.html)API文档,同时需要注意,并不是所有开发板均引出了矩阵键盘引脚,请自行查看开发板原理图或资源列表,本文以QuecPython_EC2X_EVB_V1.0为例,开发板上搭载EC200UCNAA型号模块。如自行设计PCB则需要API文档和模块硬件设计手册同时参考,但引脚定义以API文档为准,电路特性以硬件设计手册为准。 - -### 2.硬件电路 - -本文使用的搭载EC200U的开发板中矩阵键盘相关原理图接线: - - - - - - - - - -通过API文档和上图我们可以知道EC200U系列模块支持4行3列的矩阵键盘,其中KEYOUT对应的是软件中的行号,从0开始,KEYIN对应的是软件中的列号,从1开始。 - -由于开发板只是引出了引脚,所以我们还需要自行购买矩阵键盘模块,使用杜邦线进行连接,连接的实物图如图所示: - - - -### 3. 示例代码 - -下面是一个完整的示例代码: - -```python -import machine # 导入 'machine' 模块以便于使用硬件组件 -import utime # 导入 'utime' 模块以便于进行时间相关的任务 - -keypad = machine.KeyPad() # 初始化一个名为 'keypad' 的 KeyPad 类对象。 - -keypad.init() # 初始化 keypad 对象 - -# 定义回调函数,此函数将在按下或释放按键时被触发 -def keypadCallback(return_list): - ''' - return_list[0]: 1表示按下,0表示抬起 - return_list[1] : row 行 - return_list[2] : col 列 - ''' - if return_list[0] == 1: - print("按下的按键位置在第 {} 行 {}列".format(return_list[1], return_list[2])) - elif return_list[0] == 0: - print("释放的按键位置在第 {} 行 {}列".format(return_list[1], return_list[2])) - - -# 将回调函数设置为先前定义的 'keypadCallback' 函数 -keypad.set_callback(keypadCallback) -print('开始测试,请按下按键') - -# 程序等待60秒钟 -utime.sleep(60) - -keypad.deinit() # 取消初始化 keypad 对象 -print('已退出!') # 打印一条消息表示程序已经退出。 -``` - -以上代码会在按键按下或释放时进行打印,关于API的详细说明参见[矩阵键盘](../../../API_reference/zh/QuecPython类库/machine.KeyPad.html)。 - -### 5.典型问题解答 - -买到的EVB没有引出引脚怎么进行测试? - -EVB没有引出是不方便测试的,可以自行设计PCB板进行测试验证。 - -按键工作一会后失灵了是怎么回事? - -检查创建的矩阵键盘对象是否是全局变量,如是局部变量请修改为全局变量。 - -KEYOUT一定是代表行,KEYIN一定是代表列吗? - -矩阵键盘的KEYOUT不一定是行,它可以是列或其他任何方向。矩阵键盘的KEYIN和KEYOUT可以组成一个矩阵,其中KEYIN是行,KEYOUT是列,也可以反过来,KEYIN是列,KEYOUT是行。因此,KEYOUT的方向取决于键盘的设计和实现。 - -设计EC600N模块的矩阵键盘硬件电路时,55号引脚需要接地,但是会导致通电开机模块就进入下载模式怎么办? - -严格按照硬件设计手册说明选择下拉电阻,仍不能解决可以采用软件扫描方式实现矩阵键盘功能,此方法将不受限于硬件引脚功能,可以在多种模块上兼容使用,其他模块类似处理。 - -如下是软件实现矩阵键盘的示例代码: - -```python -# 导入所需的模块 -from machine import Pin -from utime import sleep_ms - -# 定义键盘矩阵的行列数及按键对应的字符 -NUM_ROWS = 3 -NUM_COLS = 3 - -key_matrix = [ -['1', '2', '3'], -['4', '5', '6'], -['7', '8', '9'], -] - -# 定义行列引脚 -row_pins = [Pin(Pin.GPIO11, Pin.OUT, Pin.PULL_DISABLE, 0), Pin(Pin.GPIO12, Pin.OUT, Pin.PULL_DISABLE, 0), Pin(Pin.GPIO13, Pin.OUT, Pin.PULL_DISABLE, 0)] -col_pins = [Pin(Pin.GPIO9, Pin.IN, Pin.PULL_PU, 1), Pin(Pin.GPIO8, Pin.IN, Pin.PULL_PU, 1), Pin(Pin.GPIO14, Pin.IN, Pin.PULL_PU, 1)] - -# 定义全局变量,用于判断按键是否被按下或释放 -key_pressed = False -key_released = False - -# 扫描键盘矩阵的函数 -def scan_keys(): - - global key_pressed, key_released - - # 循环扫描每一列 - for i in range(NUM_COLS): - row_pins[i].write(0) - - # 循环扫描每一行 - for j in range(NUM_ROWS): - # 如果检测到按键被按下且之前未被按下,则将全局变量设置为按下状态,并返回按键对应的字符 - if not col_pins[j].read() and not key_pressed: - key_pressed = True - key_released = False - return key_matrix[j][i] - - row_pins[i].write(1) - - # 如果按键被按下且之前未被释放,则将全局变量设置为释放状态,并返回 None - if key_pressed and not key_released: - key_released = True - key_pressed = False - - return None - -# 循环检测键盘矩阵 -while True: - key = scan_keys() - # 如果检测到按键被按下,则打印出按键对应的字符 - if key is not None: - print('Key pressed:', key) - sleep_ms(50) -``` - -此示例代码仅供参考,实际使用还请自行完善逻辑。 - -# 总结 - -本文介绍了如何在QuecPython平台上使用keypad矩阵键盘功能,并列举了比较常见的问题类型,以及自行实现矩阵按键功能的示例,如有疑问或更好的建议欢迎联系我们,也可以直接向我们提交文档贡献,后续本文将继续完善和补充更多应用案例。 \ No newline at end of file diff --git a/docs/Getting_started/zh/hardware/pwm.md b/docs/Getting_started/zh/hardware/pwm.md deleted file mode 100644 index 25aa39a52ced8f30bbee0f19610162ac9cbe1634..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/pwm.md +++ /dev/null @@ -1,112 +0,0 @@ -# PWM - 脉宽调制 - -## PWM 简介 - -### 什么是 PWM - -[PWM](https://baike.baidu.com/item/脉冲宽度调制/10813756?fromtitle=PWM&fromid=3034961&fr=aladdin) 代表脉冲宽度调制(Pulse Width Modulation),是一种常见的电子信号调制技术。它通过改变信号的脉冲宽度来控制电路或设备的输出功率或速度。PWM 信号是一个周期性的信号,包含一个固定频率的基准信号和一个可变的占空比信号。占空比指的是脉冲宽度与周期之间的比率,它控制着信号的平均电平,从而控制电路或设备的输出功率或速度。 - -PWM 技术在电子工程、控制系统和机械工程等领域得到广泛应用。例如,在电机控制中,PWM 信号可以用来控制电机的转速和转向。在电源管理中,PWM 信号可以用来调节直流-直流转换器的输出电压和电流。在 LED 灯控制中,PWM 信号可以用来控制 LED 的亮度和颜色。 - -需要注意的是,在使用 PWM 技术时,需要选择适当的频率和占空比,以满足具体应用的需求。同时,还需要考虑 PWM 信号的稳定性和噪声等因素,以确保电路或设备的正常运行和可靠性。 - -### 怎么使用 PWM - -使用 PWM 技术需要通过特定的电路或芯片来生成 PWM 信号,支持 QuecPython 开发的模组大部分都具有此功能,具体可见 [PWM 功能 API 文档](../../../API_reference/zh/QuecPython类库/misc.PWM.html) 确认是否支持此功能,可以按照以下步骤进行: - -#### 硬件设计 - -根据上述 [PWM 功能 API 文档](../../../API_reference/zh/QuecPython类库/misc.PWM.html) 介绍的引脚号和模组的硬件设计文档(从 [下载区](/download/) 获取)设计好硬件电路即可。设计硬件电路注意需要注意电平转换,以及电平转换电路的最大输出频率。 - -如模组本身不支持或支持的数量不够用,则可以使用模组支持的任意一个 [GPIO](../../../API_reference/zh/QuecPython类库/machine.Pin.html) 结合 [定时器](../../../API_reference/zh/QuecPython类库/machine.Timer.html) 来粗略模拟输出 PWM 信号,硬件设计相同,软件实现可见下文。 - -#### 软件应用 - -当使用模组自带 PWM 功能时: - -```python ->>>from misc import PWM ->>>pwm = PWM(PWM.PWM0, PWM.ABOVE_MS, 1, 5) ->>>pwm.open() -``` - -当自行模拟 PWM 功能时需要注意 QuecPython 对于时序控制并不是很精确,请自行根据需求实现相关代码。 - -#### PWM 功能测试 - -在命令行中执行上面三行代码即可让模组的 PWM0 引脚输出一个周期为 5ms,占空比为 20%的方波,借助 [逻辑分析仪](https://baike.baidu.com/item/逻辑分析仪/2364388?fr=aladdin) 我们可以看到输出的波形。交互界面及逻辑分析仪获取到的波形如下图。 - - - - - -如上图所示,填写的参数和实际抓取到的波形是能够对应上的,有关 API 具体参数含义可以仔细阅读 API 介绍文档进行理解。 - -对于 QuecPython 的 PWM API 需要注意的是,填的参数不能超出填写范围,否则将会出现报错,这是常见问题。 - -一般使用 PWM 功能主要步骤是: - -1. 设置 PWM 参数:根据具体应用需求,设置 PWM 信号的频率和占空比等参数。频率决定信号的周期,占空比决定信号的平均电平和电路或设备的输出功率或速度。对于 QuecPython API,则需要填写高电平时间和周期来决定占空比和频率。即占空比 = 高电平时间/周期,频率 = 1 秒/周期(单位:秒)。 -2. 连接 PWM 输出:将 PWM 信号的输出端口连接到需要控制的电路或设备,如电机、LED 等。通常需要使用适当的电路元件,如电容、电感、MOSFET 或三极管等,来控制 PWM 信号的幅值和波形。 -3. 调试和优化:根据实际情况,调试 PWM 电路或程序,以确保 PWM 信号的稳定性和准确性。可以通过示波器、数字万用表等仪器来监测 PWM 信号的波形和电平等参数,进行优化和调整。 - -需要注意的是,在使用 PWM 技术时,需要根据具体应用需求选择合适的 PWM 芯片或电路,即选择合适的模组,并合理设置参数和连接电路。同时,还需要注意 PWM 信号的稳定性、可靠性和噪声等因素,以确保电路或设备的正常运行和安全性。 - -## PWM 功能应用实例 - -在本文中,我们将使用 QuecPython 板载的 PWM 功能,实现一个呼吸灯的效果。呼吸灯是一种灯光效果,类似于人类呼吸时的变化。它可以在各种电子设备中使用,例如嵌入式系统、智能家居、玩具等。 - -### 准备工作 - -我们需要使用 QuecPython 开发板、以及一个直插式 LED 灯(部分板载贴片式 LED 即可测试,详情请看电路原理图确认)。将 LED 灯连接到 QuecPython 开发板上。将 LED 的长脚连接到开发板的 PWM0 引脚,将短脚连接到开发板的 GND 引脚。 - -### 代码实现 - -以下是实现呼吸灯效果的 QuecPython 代码。代码中的注释将帮助您理解代码的工作原理。 - -```python -# 导入所需模块 -from misc import PWM -from utime import sleep_ms - -# 设置 PWM 通道和频率 -PWM_CHANNEL = PWM.PWM0 -PWM_FREQ_Hz = 50 - -# 初始化 PWM 占空比和周期时长 -pwm_duty = 100 # PWM 占空比初始值为 100% -cycleTime = 1000 // PWM_FREQ_Hz # 周期时长(单位:ms) - -while True: - # 根据当前 PWM 占空比计算高电平时间 - highTime = cycleTime * pwm_duty // 100 - - # 创建 PWM 对象并打开 - pwm = PWM(PWM_CHANNEL, PWM.ABOVE_MS, highTime, cycleTime) - pwm.open() - - # 根据 PWM 占空比的变化情况更新 PWM 占空比值 - if pwm_duty >= 100: - pwm_duty_change_step = -10 # 如果 PWM 占空比达到了 100%,则向负方向降低 - if pwm_duty <= 0: - pwm_duty_change_step = 10 # 如果 PWM 占空比达到了 0%,则向正方向增加 - pwm_duty += pwm_duty_change_step # 更新 PWM 占空比 - - # 等待一段时间,再进行下一次循环操作 - sleep_ms(100) -``` - -这段代码通过使用 `misc` 模块和 `utime` 模块中的函数实现对 PWM(脉冲宽度调制)信号的控制。PWM 信号的特点是占空比可以调整,因此可以通过占空比来控制输出的平均电压。在这段代码中,先选择一个 PWM 通道(`PWM_CHANNEL = PWM.PWM0`)和设定 PWM 信号的频率(`PWM_FREQ_Hz = 50`)。然后通过循环语句不断改变 PWM 信号的占空比,从而使输出的电平产生周期性的变化。 - -具体来说,代码循环中的每个步骤如下: - -1. 根据当前的 PWM 占空比值计算出 PWM 信号的高电平时间(`highTime = cycleTime * pwm_duty // 100`)。这里 `cycleTime` 表示一个 PWM 信号的周期时长,单位为毫秒,由设定的 PWM 频率和公式 `cycleTime = 1000 / PWM_FREQ_Hz` 计算得出。`pwm_duty` 表示当前的 PWM 占空比,范围在 0%~100%之间。因此,当 `pwm_duty` 为 100%时,高电平时间等于一个周期的时长;当 `pwm_duty` 为 0%时,高电平时间为 0。 -2. 创建一个 PWM 对象,并指定输出方式为“高电平持续时间大于等于给定时间”(`PWM.ABOVE_MS`),同时将高电平时间和周期时长设置为计算得到的值。然后通过 `open()` 方法打开 PWM 输出。 -3. 根据当前 PWM 占空比的变化趋势更新 PWM 占空比值。当 PWM 占空比达到 100%时,向负方向(即减小 PWM 占空比)变化;当 PWM 占空比达到 0%时,向正方向(即增加 PWM 占空比)变化。 -4. 等待一段时间(这里是 100 毫秒),然后进入下一次循环。 - -这样,即可实现对 PWM 信号的控制,从而产生周期性变化的平均电压输出。 - -## 总结 - -PWM 功能在此做了详细的介绍,如有疑问或更好的建议欢迎联系我们,也可以直接向我们提交文档贡献,后续本文将继续完善和补充更多应用案例。 diff --git a/docs/Getting_started/zh/hardware/timer.md b/docs/Getting_started/zh/hardware/timer.md deleted file mode 100644 index 88592d33a6bfd014ef778b1da51e79301594f788..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/timer.md +++ /dev/null @@ -1,158 +0,0 @@ -# Timer - 定时器 - -文档主要介绍如何使用QuecPython\_Timer。 - -## 定时器简介 - -定时器最常用的就是定时与计数,即可以作为精准延时处理,也可以在接通一个时钟源的前提下,统计总共有多少脉冲。通过本文你将了解到machine.Timer和osTimer的所有设置参数及使用方法。 - -### 硬件定时器简介 - -一般由专门的硬件电路来实现,数量和硬件资源相关,设置时写入到期时间,置位使能寄存器即可开始计时,爆发时触发硬件中断,从而执行注册到硬件中断上的回调。 - -### osTimer简介 - -一般由RTOS创建,通过系统tick来计时,数量几乎没有硬性限制,但由于会占用RAM空间,实际上可用数量与RAM大小挂钩。爆发时在RTOS内产生系统中断,从而执行注册到系统中断上的回调。 - -### 硬件描述 - -目前machine.Timer开放共4个定时器,没有硬件定时器的型号底层由RTOS定时器实现,用法保持一致,具体请参阅硬件支持文档进行确认:[硬件支持](https://python.quectel.com/doc/doc/Quecpython_intro/zh/Qp_Product_intro/Hardware_Support.html) - -osTimer统一使用RTOS定时器 - -### 软件设计 - -参阅[machine.Timer相关API介绍](..\..\..\API_reference\zh\QuecPython_classlib\machine.Timer.md)和[osTimer相关API介绍](..\..\..\API_reference\zh\QuecPython_classlib\osTimer.md) - - - -## 定时器功能应用示例 - -### machine.Timer应用示例 - -#### 常量说明 - -| 常量 | 说明 | -|----------------|----------------------------| -| Timer.Timer0 | 定时器0 | -| Timer.Timer1 | 定时器1 | -| Timer.Timer2 | 定时器2 | -| Timer.Timer3 | 定时器3 | -| Timer.ONE_SHOT | 单次模式,定时器只执行一次 | -| Timer.PERIODIC | 周期模式,定时器循环执行 | - -#### 创建Timer对象 - -timer = Timer(Timer)。创建Timer对象接口参数介绍如下: - -| 参数 | 类型 | 说明 | -| ----- | ---- | ---------------------------------------- | -| Timer | int | 定时器号。EC600S支持定时器Timer0\~Timer3 | - -#### 启动定时器 - -timer.start(period, mode, callback):启动对应的定时器,接口参数介绍如下: - -| 参数 | 类型 | 说明 | -|----------|----------|--------------------------------------------------------------------------------------| -| period | int | 中断周期,单位毫秒 | -| mode | int | 运行模式 Timer.ONE_SHOT 单次模式,定时器只执行一次 Timer.PERIODIC 周期模式,循环执行 | -| callback | function | 定时器执行函数 | - -返回值:启动成功返回整型值0,失败返回整型值-1。 - -#### 关闭定时器 - -timer.stop():关闭对应的定时器,无参数。 - -返回值:成功返回整型0,失败返回整型-1 - -#### 交互操作 - -使用QPYcom工具和模组进行交互,下面实例是基于Timer0和Timer1。Timer2和Timer3配置类似。 - - -![](./../media/hardware/Timer/Timer_1.png) - -
- -注意: - -1. from machine import Timer即为让Timer模块在当前空间可见。 -2. 只有from machine import Timer模块,才能使用Timer内的函数和变量。 -3. 上述操作没有连接任何外设,仅作为熟悉指令参考。 - - - -#### 软件实现 - -配套demo的参考代码为文档同目录下的timer_file.py文件。下载.py文件到模组运行,代码如下: 点击下载代码 - -```python -import log -from machine import Timer -log.basicConfig(level=log.INFO) # 设置日志输出级别 -Timer_Log = log.getLogger("Quectel") # 获取logger对象 -log_print_num = 5 -state = 1 -timer0 = Timer(Timer.Timer1) -# 创建一个执行函数,并将timer实例传入 -def timer_test(t): - global log_print_num - global state - Timer_Log.info('log_print_num is %d' % log_print_num) - log_print_num -= 1 - if log_print_num <= 0: - Timer_Log.info('timer exit') - state = 0 - timer0.stop() # 结束该定时器实例 -timer0.start(period=1000, mode=timer0.PERIODIC, callback=timer_test) # 启动定时器 -while state: - pass - -``` - - - -#### 运行效果 - -1. 打开QPYcom运行timer\_file.py,如下图: - - ![](./../media/hardware/Timer/Timer_2.png) - - -2. 在QPYcom交互界面查看输出结果如下: - - -![](./../media/hardware/Timer/Timer_3.png) - - - -### osTimer应用示例 - -osTimer使用比machine.Timer简单的多,只需要start和stop,代码实现如下: - -```python -import osTimer - -#osTimer回调原型,参数无意义,仅在形式上存在,不使用 -def test_cb(args): - global run_time - run_time = run_time + 1 - print('run_time' + str(run_time)) - if run_time >= 10: - print('timer stop') - timer.stop()#定时器循环10次停止 - -run_time = 0 -timer = osTimer() #创建定时器 -timer.start(1000, 1, test_cb) #第一个参数为时间(单位ms);第二个参数0代表单次执行;1代表循环执行;第三个代表定时器回调 -``` - -执行结果: - -![](./../media/hardware/Timer/Timer_0.png) - -## 总结 - -定时器功能在此做了详细的介绍,如有疑问或更好的建议欢迎联系我们,也可以直接向我们提交文档贡献,后续本文将继续完善和补充更多应用案例。 \ No newline at end of file diff --git a/docs/Getting_started/zh/hardware/wdt.md b/docs/Getting_started/zh/hardware/wdt.md deleted file mode 100644 index 1fdd953897d5bcee0dc235476405691ccc72893e..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/hardware/wdt.md +++ /dev/null @@ -1,50 +0,0 @@ -# WDT - 看门狗 - -本文主要介绍看门狗功能的使用 - -## 看门狗简介 - -### 什么是看门狗 - -看门狗,全称WatchDog Timer,即看门狗计时器。看门狗是一种电子或软件定时器,用于检测计算机故障,并从计算机故障中恢复。在正常操作期间,计算机会定期重置看门狗计时器,以防止其丢失或“计时”。 如果由于硬件故障或程序错误而导致计算机无法重置看门狗,则计时器将丢失并生成超时信号, 超时信号会被用于启动一个或多个纠正措施。 纠正措施通常包括将计算机系统置于安全状态并恢复系统的正常运行。 - -### 看门狗工作原理 - -看门狗是一个计数器,其基本原理是先给计数器设定一个数值(溢出值),程序开始运行后,看门狗定时器开始计数,程序正常运行时,会周期发出指令将计数器置零(喂狗),重新开始计数,而如果长时间没有清零,计数器增加到设定值(定时器溢出),计数器会认为程序出现了异常,强制系统复位。 - -## 看门狗应用 - -示例代码 - -```py -from machine import WDT -import utime - - -if __name__ == '__main__': - wdt = WDT(5) # 启动看门狗,间隔时长5s - for i in range(0, 4): - wdt.feed() - print("喂狗: {}".format(i)) - utime.sleep(4) - print("循环喂狗结束 接下来将触发看门狗复位") - -``` - -运行结果 - -```py ->>> example.exec("/usr/test.py") - -喂狗: 0 - -喂狗: 1 - -喂狗: 2 - -喂狗: 3 - -循环喂狗结束 接下来将触发看门狗复位 ->>> -``` - diff --git a/docs/Getting_started/zh/helios-sdk/README.md b/docs/Getting_started/zh/helios-sdk/README.md deleted file mode 100644 index c7fbe67ed229836a12856bebeb3898dcad0e285f..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/helios-sdk/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# 关于 Helios SDK - -Helios SDK 具备以下几个特点: - -- Helios SDK 是一个提供了QuecPython运行环境的、基于C语言实现的SDK。 -- 它允许用户调用系统接口,实现与Quectel模组相关的绝大多数功能。 -- 同时它允许用户在QuecPython虚拟机中进行功能扩展。 -- Helios SDK 提供 menuconfig 功能,以便用户进行功能裁剪。 - -Helios SDK 开发指南包含以下内容: - -- [Helios SDK开发指南(1)__入门](./quick-start.md) -- [Helios SDK开发指南(2)__进阶](./junior.md) -- [Helios SDK开发指南(3)__高级](./advanced.md) diff --git a/docs/Getting_started/zh/helios-sdk/advanced.md b/docs/Getting_started/zh/helios-sdk/advanced.md deleted file mode 100644 index ba82f037fe264928a843507dc2101684f3b5588a..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/helios-sdk/advanced.md +++ /dev/null @@ -1,790 +0,0 @@ -# Helios SDK开发指南(3)__高级 - -## 1. 从quecPython文件系统执行脚本文件 - -本节向大家展示如何将自己编写的Python脚本文件导入模块中运行。 - -### 1.1 安装QPYcom工具 - -进入[官方下载页面](https://python.quectel.com/download),下载`QPYcom图像化工具`,如下图所示: - -![](../media/helios-sdk/advanced/image_1f5agql651ru01icc90t1af91ggc9.png) - -下载完成后解压即可使用。 - -> 详细的使用说明,参考工具根目录下的`docs\Quectel QuecPython_QPYcom工具使用说明.docx`。 - -### 1.2 编写Python脚本文件 - -在PC中编写名为`quecPython_demo.py`的脚本文件,内容如下: -``` -import uos - -print ('Hello, quecPython!') - -root_list = uos.listdir('') # show file list under root dir -print ('root_list:', root_list) - -usr_list = uos.listdir('usr') # show file list under /usr -print ('usr_list:', usr_list) -``` - -### 1.3 将Python脚本文件导入quecPython文件系统 - -**步骤一:打开QPYcom工具** - -双击工具根目录下的`QPYcom.exe`打开工具。 - -**步骤二:打开USB串行设备端口** - -根据《Helios SDK指南(2)--进阶》第3节步骤三,在QPYcom工具的`选择串口`下拉菜单中,选中对应的端口,本例为COM5。 -点击`打开串口`即可,默认进入命令交互界面。 -效果如下图所示: - -![](../media/helios-sdk/advanced/image_1f5ajhmingrtucv1jn367v1c3s9.png) - -**步骤三:进入文件传输界面** - -点击`文件`标签,进入文件传输界面,如下图所示: - -![](../media/helios-sdk/advanced/image_1f5ajqjmoaln1a971anfgd916t4m.png) - -上图可见,界面的左侧是PC本地的文件列表,右侧是模块端文件列表。 -模块端quecPython启动后,会自动生成一个名为`system_config.json`的文件,用户可暂时先不了解该文件的作用。 - -**步骤四:导入Python脚本文件** - -点击模块端文件列表上方的`+`按钮,向模块传输文件。 - -如下图所示,按照1、2、3的顺序,即可向文件系统导入`quecPython_demo.py`文件。 - -![](../media/helios-sdk/advanced/image_1f5akck9b1mro11bp1smc11me10qf1g.png) - -导入完成后,可见模块端文件列表中新增了`quecPython_demo.py`文件,如下图所示: - -![](../media/helios-sdk/advanced/image_1f5akj3l77091lc5qq9144a1t6g1t.png) - -### 1.4 执行导入的脚本文件 - -点击`交互`标签,进入之前的命令交互界面。 - -在命令交互界面先后键入以下两行命令: -``` -import example -example.exec('/usr/quecPython_demo.py') -``` - -命令执行完成后,可在界面上看到以下输出内容: - -![](../media/helios-sdk/advanced/image_1f5akvkki10re169nua1njrrl2a.png) - -至此,`quecPython_demo.py`被成功执行,`usr_list`中也显示了新导入的脚本文件。 - -### 1.5 从main.py自动启动应用脚本 - -通过上面的学习,已经知道如何向quecPython文件系统导入脚本文件,并且通过命令触发脚本的执行。 -但是quecPython可以通过在开机后自动执行`main.py`的方式,自动启动应用脚本。 - -**步骤一:编写main.py文件** - -在PC中编写一个名为`main.py`的脚本文件,内容如下: -``` -import utime - -if __name__ == '__main__': - while True: - print('Hello, quecPython!') - utime.sleep(1) -``` - -**步骤二:导入main.py文件至模块文件系统** - -使用本章1.3节的方法,将`main.py`文件导入至模块文件系统。 - -**步骤三:重启模块** - -重新模块,以期在启动后能够自动执行`main.py`。 - -**步骤四:重新连接USB串行设备端口** - -由于模块重启后,原先使用QPYcom打开的USB串行设备端口会和PC断开连接。 -参照本章1.3节步骤二,重新连接该口。 - -**步骤五:观察执行结果** - -重新连接USB串行设备端口后,命令交互界面会每间隔1s钟,周期性打印`Hello, quecPython!`。 -说明main.py脚本已经成功自启动。 - -![](../media/helios-sdk/advanced/image_1f5amm4omau7k60fka1tvg10612n.png) - -## 2. 基于Python语言编写py模块 - -quecPython基于Python语言编写的模块统一存放于`services\microPython\ports\quectel\core`路径下。 - -下面以编写一个名为`hello.py`的模块为例演示如何在quecPython中基于Python语言编写py模块。 - -**步骤一:新建hello.py文件** - -在`services\microPython\ports\quectel\core`目录下新建`hello.py`文件。 - -**步骤二:编写文件内容** - -在新建的`hello.py`文件中,编写以下内容: -``` -def say_hello(name): - print ('Hello,', name) -``` - -**步骤三:修改编译脚本** - -打开`services\microPython\ports\quectel\core\Makefile`,分别在`$(BUILD)/_frozen_mpy.c`的依赖文件和执行命令中添加`$(BUILD)/hello.mpy \`,如下图所示: - -![](../media/helios-sdk/advanced/image_1f5cmta1hj4fo2515p41ovj1l3b9.png) - -> 注意`$(BUILD)/hello.mpy \`最后的反斜杠`\`。 -在Makefile中,将一条命令拆分为多行时,需要在最后加上反斜杠`\`,并且`\`后不能有任何字符,包括空格。 - -**步骤四:编译quecPython固件** - -因为`services\microPython\ports\quectel\core`下的脚本文件是以二进制的形式被编译到固件包,作为内建库存在的,因此需要执行编译命令,编译新的固件包。 - -编译命令参考《Helios SDK指南(2)--进阶》第2节。 - -**步骤五:烧录固件并验证功能** - -将固件烧录至模块并重启后,打开QPYcom。 -在交互串口输入以下两条命令: -``` -import hello -hello.say_hello('quecPython') -``` - -执行结果如下图所示: - -![](../media/helios-sdk/advanced/image_1f5cub1eb1590gi816j41t21fglm.png) - -由图可见,我们已经拥有了自己编写的`hello`模块,`hello.say_hello(name)`方法也成功执行。 - -## 3. 基于C语言编写py模块 - -有些需求可能无法通过使用Python语言编写py模块来实现,比如对执行速度有要求的一些算法,亦或是自己扩展的一些外设驱动等。 - -本节我们来演示如何基于C语言编写py模块。 - -### 3.1 模块接口层级分类 - -![](../media/helios-sdk/advanced/QuecPython.png) - -从上图可以看出,quecPython模块接口层级可以分为三大类:`module`、`type`和`function`。 - -三者之间的层级关系也显而易见: - -- quecPython下可以有多个module。 -- 每个module下可以有多个type或function。 -- 每个type下可以有多个function。 - -以`machine`模块为例,`machine.Pin`和`machine.UART`都属于type类型。 -type类下就是具体的函数了,比如`Pin.value()`用来设定GPIO的高低电平。 - -我们可以在QPYcom的交互窗口中,依次输入以下命令,查看各自的类型: -``` -import machine -type(machine) -type(machine.Pin) -type(machine.Pin.value) -``` - -输出结果如下图所示,验证了上面的论述。 - -![](../media/helios-sdk/advanced/image_1f5d7l5okngavt2fbrbvndoc1q.png) - -有了以上的层级分类的概念后,套用microPython提供的各类接口定义的模板即可进行C语言的py模块编写。 - -### 3.2 添加module - -**步骤一:新建modtest.c文件** - -进入`services\microPython\ports\quectel\core\source`目录,新建`modtest.c`文件。 - -> `services\microPython\ports\quectel\core\source`目录存放了所有quectel添加的C语言实现的py模块。 - -``` -#include -#include - -#include "obj.h" -#include "runtime.h" - -/* 定义的modtest全局字典,之后我们添加type和function就要添加在这里 */ -STATIC const mp_rom_map_elem_t modtest_globals_table[] = { - {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_modtest)}, //这个对应python层面的__name__ 属性 -}; - -/* 把modtest_globals_table注册到 mp_module_modtest_globals里面去 */ -STATIC MP_DEFINE_CONST_DICT(mp_module_modtest_globals, modtest_globals_table); - -/* 定义一个module类型 */ -const mp_obj_module_t mp_module_modtest = { - .base = {&mp_type_module}, - .globals = (mp_obj_dict_t *)&mp_module_modtest_globals, -}; -``` - -以上代码可以看出,`modtest.c`主要由三个部分组成,此三部分是添加一个module的必选项: - -- `mp_rom_map_elem_t modtest_globals_table[ ]` -- `MP_DEFINE_CONST_DICT(mp_module_modtest_globals, modtest_globals_table)` -- `mp_obj_module_t mp_module_modtest` - -**步骤二:将modtest模块注册到quecPython中** - -打开`services\microPython\ports\quectel\core\source\mpconfigport.h`。 - -找到`MICROPY_PORT_BUILTIN_MODULES`定义的地方,在它的前面对`mp_module_modtest`做外部引用声明。 - -参考`MICROPY_PORT_BUILTIN_MODULES`定义的格式,注册`modtest`模块。 - -完成后的代码如下: -``` -extern const struct _mp_obj_module_t mp_module_wifiscan; - -extern const struct _mp_obj_module_t mp_module_modtest; //对`mp_module_modtest`做外部引用声明 - -#define MICROPY_PORT_BUILTIN_MODULES \ - { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_usocket), (mp_obj_t)&mp_module_usocket }, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_misc), (mp_obj_t)&mp_module_misc}, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_osTimer), (mp_obj_t)&mp_ostimer_type}, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_example), (mp_obj_t)&example_module }, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_dial), (mp_obj_t)&mp_module_dial}, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_modem), (mp_obj_t)&mp_module_modem}, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module }, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_utils), (mp_obj_t)&mp_module_utils},\ - { MP_OBJ_NEW_QSTR(MP_QSTR_hmacSha1), (mp_obj_t)&mp_module_hmacSha1}, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_sms), (mp_obj_t)&mp_module_sms}, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_sim), (mp_obj_t)&mp_module_sim}, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_pm), (mp_obj_t)&mp_module_pm}, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_net), (mp_obj_t)&mp_module_net}, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_cellLocator), (mp_obj_t)&mp_module_celllocator}, \ - { MP_OBJ_NEW_QSTR(MP_QSTR_wifiScan), (mp_obj_t)&mp_module_wifiscan},\ - { MP_OBJ_NEW_QSTR(MP_QSTR_modtest), (mp_obj_t)&mp_module_modtest }, /* 注册`modtest`模块 */ \ - MICROPY_PORT_BUILTIN_MODULES_FOTA \ - MICROPY_PORT_BUILTIN_MODULES_LVGL -``` - -上述代码带注释的行,即为我们新增部分。 - -> -- C语言宏定义`MICROPY_PORT_BUILTIN_MODULES`,由于定义内容篇幅较长,使用反斜杠`\`进行换行,`\`后不能有任何字符,包括空格,该点和前面所述的Makefile的换行方法是一致的;如需注释,注释内容使用`/* */`括起来,并且放在`\`前。 -- `MICROPY_PORT_BUILTIN_MODULES`宏定义的最后两行的格式和前面不同,这是使用宏定义的方式,方便对相关功能进行裁剪。感兴趣的小伙伴可以深入研究下。 - -**步骤三:将`modtest.c`文件添加到编译脚本** - -打开`services\microPython\ports\quectel\core\Makefile`文件,参照以下代码的第7行,添加`modtest.c`文件: -``` -SRC_QSTR += source/moduos.c \ - source/quecpython.c \ - source/utf8togbk.c \ - ... \ - source/modlvgl.c \ - source/modwifiscan.c \ - source/modtest.c # 添加modtest.c文件 - -ifeq ($(strip $(PLAT)),ASR) -SRC_QSTR += source/modfota.c -endif -``` - -> 如果Makefile的当前行存在换行符`\`,并且下一行不是空行,在当前行的`\`的前**禁止**添加注释。否则,make系统会将以下跟换行符`\`连接的各行当成注释。 -上述代码,由于注释在换行的最后一行,因此可以添加注释。 - -打开`services\microPython\microPython.mk`文件,为`$(NAME)_SRCS`变量增加值`ports/quectel/core/source/modtest.c`,即下面代码的第7行。 - -``` -$(NAME)_SRCS = \ - extmod/modubinascii.c \ - extmod/moducryptolib.c \ - extmod/moductypes.c \ - ... \ - ports/quectel/core/source/modwifiscan.c \ - ports/quectel/core/source/modtest.c \ - ports/quectel/core/build/_frozen_mpy.c \ - ... -``` - -**步骤四:编译固件** - -参考《Helios SDK指南(2)--进阶》第2节,编译出新的quecPython固件。 - -**步骤五:烧录固件并验证功能** - -参考《Quectel_QFlash_用户指导》,烧录固件。 - -烧录完成后,重启模块,打开QPYcom,依次输入以下命令: -``` -import modtest -type(modtest) -dir(modtest) -``` - -QPYcom上会得到如图所示的输出: - -![](../media/helios-sdk/advanced/image_1f5dk2j3n10oeokp17hp1t0bk5e9.png) - -由图可见,`modtest`模块已经成功添加,但是没有添加任何type和function。 - -### 3.3 给module添加function - -函数分为无参数和有参数两类,接下来会为`modtest`分别添加一个无参数、一个有参数的函数。 -在Python中对应两个方法,即`modtest.func_noargs()`和`modtest.func_withargs(argv)` - -#### 3.3.1 无参数的function - -此处我们增加一个C函数实现的方法`mp_obj_t modtest_func_noargs(void)`。 -功能:在命令交互口输入`modtest.func_noargs()`,输出字符串`here we go`。 - -**步骤一:修改代码** - -打开`services\microPython\ports\quectel\core\source\modtest.c`文件,对代码做修改。 - -完整的代码如下: -``` -#include -#include -#include - -#include "obj.h" -#include "runtime.h" - -#include "mphal.h" - -/* modtest module下不带参数的函数定义 */ -STATIC mp_obj_t modtest_func_noargs(void) -{ - mp_hal_stdout_tx_strn("here we go\n", strlen("here we go\n")); - return mp_const_none; //python函数中不需要返回数据,就返回mp_const_none -} - -/* 注册function对象modtest_obj_func_noargs */ -STATIC const MP_DEFINE_CONST_FUN_OBJ_0(modtest_obj_func_noargs, modtest_func_noargs); - -/* 定义的modtest全局字典,之后我们添加type和function就要添加在这里 */ -STATIC const mp_rom_map_elem_t modtest_globals_table[] = { - {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_modtest)}, //这个对应python层面的__name__ 属性 - {MP_OBJ_NEW_QSTR(MP_QSTR_func_noargs), MP_ROM_PTR(&modtest_obj_func_noargs)}, //新增的modtest_obj_func_noargs对象注册到modtest中 -}; - -/* 把modtest_globals_table注册到 mp_module_modtest_globals里面去 */ -STATIC MP_DEFINE_CONST_DICT(mp_module_modtest_globals, modtest_globals_table); - -/* 定义一个module类型 */ -const mp_obj_module_t mp_module_modtest = { - .base = {&mp_type_module}, - .globals = (mp_obj_dict_t *)&mp_module_modtest_globals, -}; -``` - -下图的差异比较,可以看出该段代码和本章3.2节代码的差异(右侧为当前代码,红色是差异部分): - -- 新增了Python模块`modtest`的方法`modtest.func_noargs()`对应的C语言功能函数`modtest_func_noargs`。凡是Python对应的C语言功能函数,一律返回`mp_obj_t`类型。该示例中,`modtest.func_noargs()`方法无返回值,故对应的C函数中需返回`mp_const_none`,以此告知Python该方法无返回值。 -- 使用宏`MP_DEFINE_CONST_FUN_OBJ_0`将增加一个function对象`modtest_obj_func_noargs`,即第18行代码。 -- 将新增的`modtest_obj_func_noargs`对象注册到modtest中,即第23行代码。 - -![](../media/helios-sdk/advanced/image_1f5fj61re1q2q1r4jns4ouek6bm.png) - -**步骤二:验证功能** - -步骤一完成后,可直接编译和烧录,无需再修改脚本文件。 - -烧录完成后,重启模块,打开QPYcom,输入以下命令: -``` -import modtest -dir(modtest) -modtest.func_noargs() -``` - -执行结果如下图所示: - -![](../media/helios-sdk/advanced/image_1f5fk7lno1mjsfofrkepit122v1j.png) - -由图可见,`dir(modtest)`的执行结果,已经列出了`func_noargs`方法。 -执行`modtest.func_noargs()`,输出了预期的`here we go`字符串。 - -#### 3.3.2 有参数的function - -此处我们增加一个C函数实现的方法`mp_obj_t modtest_func_withargs(mp_obj_t argv)`。 -功能:在命令交互口输入`modtest.func_withargs('Bingo')`,输出字符串`Bingo, here we go`。 - -**步骤一:修改代码** - -打开`services\microPython\ports\quectel\core\source\modtest.c`文件,对代码做修改。 - -完整的代码如下: -``` -#include -#include -#include - -#include "obj.h" -#include "runtime.h" - -#include "mphal.h" - -/* modtest module下不带参数的函数定义 */ -STATIC mp_obj_t modtest_func_noargs(void) -{ - mp_hal_stdout_tx_strn("here we go\n", strlen("here we go\n")); - return mp_const_none; //python函数中不需要返回数据,就返回mp_const_none -} - -/* 注册function对象modtest_obj_func_noargs */ -STATIC const MP_DEFINE_CONST_FUN_OBJ_0(modtest_obj_func_noargs, modtest_func_noargs); - -/* modtest module下带参数的函数定义 */ -STATIC mp_obj_t modtest_func_withargs(mp_obj_t str) -{ - char reply_str[64] = {0}; - - /* - * 此处调用`mp_obj_str_get_str(str)`从参数`str`中提取出用户从Python层输入的字符串参数 - * 如果用户期望Python传递进来的参数是整数,则调用mp_obj_get_int(data)来提取出来整数 - */ - snprintf(reply_str, sizeof(reply_str), "%s, here we go\n", mp_obj_str_get_str(str)); - mp_hal_stdout_tx_strn(reply_str, strlen(reply_str)); - return mp_const_none; //同样没有返回值 -} - -/* - * 这里使用的宏定义和前面的名称不一样,后缀是1,不是0 - * 后缀是几,表示该方法携带几个参数 - */ -STATIC const MP_DEFINE_CONST_FUN_OBJ_1(modtest_obj_func_withargs, modtest_func_withargs); - -/* 定义的modtest全局字典,之后我们添加type和function就要添加在这里 */ -STATIC const mp_rom_map_elem_t modtest_globals_table[] = { - {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_modtest)}, //这个对应python层面的__name__ 属性 - {MP_OBJ_NEW_QSTR(MP_QSTR_func_noargs), MP_ROM_PTR(&modtest_obj_func_noargs)}, //新增的modtest_obj_func_noargs对象注册到modtest中 - {MP_OBJ_NEW_QSTR(MP_QSTR_func_withargs), MP_ROM_PTR(&modtest_obj_func_withargs)}, //新增的modtest_obj_func_withargs对象注册到modtest中 -}; - -/* 把modtest_globals_table注册到 mp_module_modtest_globals里面去 */ -STATIC MP_DEFINE_CONST_DICT(mp_module_modtest_globals, modtest_globals_table); - -/* 定义一个module类型 */ -const mp_obj_module_t mp_module_modtest = { - .base = {&mp_type_module}, - .globals = (mp_obj_dict_t *)&mp_module_modtest_globals, -}; -``` - -下图的差异比较,可以看出该段代码和本章3.3.1节代码的差异(右侧为当前代码,红色是差异部分): - -- 新增了Python模块`modtest`的方法`modtest.func_withargs(str)`对应的C语言功能函数`modtest_func_withargs`。该示例中,使用`mp_obj_str_get_str(str)`获取用户从Python层输入的字符串参数。 -- 使用宏`MP_DEFINE_CONST_FUN_OBJ_1`将增加一个function对象`modtest_obj_func_withargs`,即第38行代码。 -- 将新增的`modtest_obj_func_noargs`对象注册到modtest中,即第44行代码。 - -![](../media/helios-sdk/advanced/image_1f5fmta2oakmps0115mb8r1q0s20.png) - -**步骤二:验证功能** - -步骤一完成后,可直接编译和烧录,无需再修改脚本文件。 - -烧录完成后,重启模块,打开QPYcom,输入以下命令: -``` -import modtest -dir(modtest) -modtest.func_withargs('Bingo') # with argv: 'Bingo' -``` - -执行结果如下图所示: - -![](../media/helios-sdk/advanced/image_1f5fnjjmp1rqlr3d19ldk4onpb2d.png) - -由图可见,`dir(modtest)`的执行结果,已经列出了`func_withargs`方法。 -执行`modtest.func_withargs('Bingo')`,输出了预期的`Bingo, here we go`字符串。 - -### 3.4 给module添加type - -本节我们为`modtest`添加一个名为`math`的type。 - -**步骤一:新建modtest_math.c文件** - -进入`services\microPython\ports\quectel\core\source`目录,新建`modtest_math.c`文件。 - -``` -#include -#include - -#include "obj.h" -#include "runtime.h" - -/* 定义type的本地字典 */ -STATIC const mp_rom_map_elem_t math_locals_dict_table[] = { -}; - -/* 把math_locals_dict_table注册到math_locals_dict里面去 */ -STATIC MP_DEFINE_CONST_DICT(math_locals_dict, math_locals_dict_table); - -/* 定义mp_obj_type_t类型的结构体;注意这里和定义module使用的类型是不一样的 */ -const mp_obj_type_t modtest_math_type = { - .base={ &mp_type_type }, - .name = MP_QSTR_math, //type类的name属性是放在这里定义,而不是放在DICT中 - .locals_dict = (mp_obj_dict_t*)&math_locals_dict, //注册math_locals_dict -}; -``` - -以上代码可以看出,`modtest_math.c`主要由三个部分组成,此三部分是添加一个type的必选项: - -- `mp_rom_map_elem_t math_locals_dict_table[ ]` -- `MP_DEFINE_CONST_DICT(math_locals_dict, math_locals_dict_table)` -- `mp_obj_type_t modtest_math_type` - -和添加module的代码比较发现: - -- 二者都是由三部分组成。 -- 定义字典和注册字典的方式完全相同,区别只是变量名不同。 -- 定义module用的是结构体`mp_obj_module_t`,定义type用的是结构体`mp_obj_type_t`。 - -**步骤二:将`modtest_math.c`文件添加到编译脚本** - -该步和本章3.2节步骤三完全相同。 - -打开`services\microPython\ports\quectel\core\Makefile`文件,参照以下代码的第7、8两行,添加`modtest_math.c`文件: -``` -SRC_QSTR += source/moduos.c \ - source/quecpython.c \ - source/utf8togbk.c \ - ... \ - source/modlvgl.c \ - source/modwifiscan.c \ - source/modtest.c \ - source/modtest_math.c # 添加modtest_math.c文件 - -ifeq ($(strip $(PLAT)),ASR) -SRC_QSTR += source/modfota.c -endif -``` - -> 注意:在该脚本中增加`source/modtest_math.c`时,需要把前一行`source/modtest.c`后的注释删除掉,加上反斜杠`\`。 - -打开`services\microPython\microPython.mk`文件,为`$(NAME)_SRCS`变量增加值`ports/quectel/core/source/modtest.c`,即下面代码的第8行。 - -``` -$(NAME)_SRCS = \ - extmod/modubinascii.c \ - extmod/moducryptolib.c \ - extmod/moductypes.c \ - ... \ - ports/quectel/core/source/modwifiscan.c \ - ports/quectel/core/source/modtest.c \ - ports/quectel/core/source/modtest_math.c \ - ports/quectel/core/build/_frozen_mpy.c \ - ... -``` - -**步骤三:为modtest注册modtest_math_type** - -打开`services\microPython\ports\quectel\core\source\modtest.c`文件。 - -在`mp_rom_map_elem_t modtest_globals_table[ ]`前加入`modtest_math_type`的引用声明。 - -在`mp_rom_map_elem_t modtest_globals_table[ ]`内注册`modtest_math_type`。 - -修改后的代码如下: -``` -/* 定义的modtest全局字典,之后我们添加type和function就要添加在这里 */ -extern const mp_obj_type_t modtest_math_type; -STATIC const mp_rom_map_elem_t modtest_globals_table[] = { - {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_modtest)}, //这个对应python层面的__name__ 属性 - {MP_OBJ_NEW_QSTR(MP_QSTR_func_noargs), MP_ROM_PTR(&modtest_obj_func_noargs)}, //新增的modtest_obj_func_noargs对象注册到modtest中 - {MP_OBJ_NEW_QSTR(MP_QSTR_func_withargs), MP_ROM_PTR(&modtest_obj_func_withargs)}, //新增的modtest_obj_func_withargs对象注册到modtest中 - {MP_OBJ_NEW_QSTR(MP_QSTR_math), MP_ROM_PTR(&modtest_math_type)}, //注册modtest_math_type -}; -``` - -和修改之前相比,仅增加了上述代码的第2行和第7行。 - -**步骤四:验证功能** - -步骤三完成后,可直接编译和烧录。 - -烧录完成后,重启模块,打开QPYcom,输入以下命令: -``` -import modtest -dir(modtest) -type(modtest.math) -``` - -执行结果如下图所示: - -![](../media/helios-sdk/advanced/image_1f5fre64011297f61o9op5n13kn2q.png) - -由图可见,我们成功添加了一个名为`math`的type。 - -### 3.5 给type添加function - -本节演示如何给type添加function。 -和给module添加的function一样,也分为无参数和有参数两种。 - -#### 3.5.1 无参数的function - -此处我们增加一个C函数实现的方法`mp_obj_t math_nothing(void)`。 -功能:在命令交互口输入`modtest.math.nothing()`,输出字符串`do nothing in this function`。 - -**步骤一:修改代码** - -打开`services\microPython\ports\quectel\core\source\modtest_math.c`文件,对代码做修改。 - -完整的代码如下: -``` -#include -#include -#include - -#include "obj.h" -#include "runtime.h" - -#include "mphal.h" - -/* modtest_math_type下不带参数的函数定义 */ -STATIC mp_obj_t math_nothing(void) -{ - mp_hal_stdout_tx_strn("do nothing in this function\n", strlen("do nothing in this function\n")); - return mp_const_none; //python函数中不需要返回数据,就返回mp_const_none -} - -/* 注册function对象math_nothing_obj */ -STATIC MP_DEFINE_CONST_FUN_OBJ_0(math_nothing_obj, math_nothing); - -/* 定义type的本地字典 */ -STATIC const mp_rom_map_elem_t math_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_nothing), MP_ROM_PTR(&math_nothing_obj) }, //新增的math_nothing_obj对象注册到modtest_math_type中 -}; - -/* 把math_locals_dict_table注册到math_locals_dict里面去 */ -STATIC MP_DEFINE_CONST_DICT(math_locals_dict, math_locals_dict_table); - -/* 定义mp_obj_type_t类型的结构体;注意这里和定义module使用的类型是不一样的 */ -const mp_obj_type_t modtest_math_type = { - .base={ &mp_type_type }, - .name = MP_QSTR_math, //type类的name属性是放在这里定义,而不是放在DICT中 - .locals_dict = (mp_obj_dict_t*)&math_locals_dict, //注册math_locals_dict -}; -``` - -下图的差异比较,可以看出该段代码和修改之前的差异(右侧为当前代码,红色是差异部分): - -![](../media/helios-sdk/advanced/image_1f5ft863lnr51cqccqjdrcpk73k.png) - -可以看到,type中添加无参数function的方法和在module中是一样的,不做过多解释。 - -**步骤二:验证功能** - -步骤一完成后,可直接编译和烧录,无需再修改脚本文件。 - -烧录完成后,重启模块,打开QPYcom,输入以下命令: -``` -import modtest -dir(modtest.math) -modtest.math.nothing() -``` - -执行结果如下图所示: - -![](../media/helios-sdk/advanced/image_1f5fth4g315sn15il1q7i81cnv41.png) - -由图可见,执行`modtest.math.nothing()`,输出了预期的`do nothing in this function`字符串。 - -#### 3.5.2 有参数的function - -type中添加一个带参数的function,和在module中大不相同。 - -type类型在Python中是类,而要操作带参数的函数就需要实例化一个对象出来:比如`mymath=modtest.math()`,那么对应到C语言必须要有对应的分配空间、创建对象的函数和表示对象的结构体。 - -此处,我们实现的功能为:通过`modtest.math()`在Python层创建一个对象`mymath`,执行其加法运算的方法`mymath.add(10, 20)`,期望输出结果`30`。 - -**步骤一:定义`math_obj_t` 结构体** - -在`services\microPython\ports\quectel\core\source\modtest_math.c`中添加`math_obj_t` 结构体,用来表示math对象的结构: -``` -/* 定义math_obj_t结构体 */ -typedef struct _math_obj_t -{ - mp_obj_base_t base; //定义的对象结构体要包含该成员 - uint16_t value1; //下面的成员,根据需要自己添加 - uint16_t value2; -}math_obj_t; -``` - -**步骤二:为`modtest_math_type`添加`.make_new`属性,及对应的函数** - -代码如下: -``` -/* make new 创建一个实例对象的函数,对应Python中的mymath = modtest.math(),创建mymath对象 */ -const mp_obj_type_t modtest_math_type; -STATIC mp_obj_t modtest_math_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) -{ - mp_arg_check_num(n_args, n_kw, 0, 0, true); //检查参数个数 - math_obj_t *self = m_new_obj(math_obj_t); //创建对象,分配空间 - self->base.type = &modtest_math_type; //定义对象的类型 - return MP_OBJ_FROM_PTR(self); //返回对象的指针 -} - -/* 定义mp_obj_type_t类型的结构体;注意这里和定义module使用的类型是不一样的 */ -const mp_obj_type_t modtest_math_type = { - .base={ &mp_type_type }, - .name = MP_QSTR_math, //type类的name属性是放在这里定义,而不是放在DICT中 - .make_new=modtest_math_make_new, //添加的make new属性 - .locals_dict = (mp_obj_dict_t*)&math_locals_dict, //注册math_locals_dict -}; -``` - -**步骤三:为`modtest_math_type`添加`math_add`方法** - -代码如下: -``` -/* 定义math_add函数 */ -mp_obj_t math_add(mp_obj_t self_in, mp_obj_t val1, mp_obj_t val2) { - math_obj_t *self=MP_OBJ_TO_PTR(self_in); //从第一个参数里面取出对象的指针 - self->value1 = mp_obj_get_int(val1); //从第二个参数里面取出加法运算的第一个参数 - self->value2 = mp_obj_get_int(val2); //从第三个参数里面取出加法运算的第二个参数 - return mp_obj_new_int(self->value1+self->value2); //返回计算的结果 -} - -/* 注意:此处使用的是OBJ_3,因为math_add()函数有三个参数 */ -STATIC MP_DEFINE_CONST_FUN_OBJ_3(math_add_obj, math_add); - -/* 定义type的本地字典 */ -STATIC const mp_rom_map_elem_t math_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_nothing), MP_ROM_PTR(&math_nothing_obj) }, //新增的math_nothing_obj对象注册到modtest_math_type中 - { MP_ROM_QSTR(MP_QSTR_add), MP_ROM_PTR(&math_add_obj) }, //新增的math_add_obj对象注册到modtest_math_type中 -}; -``` - -以上三步完成后,我们看下本节代码和修改之前的差异。 -下图的红色部分,即为我们新增的所有代码。 - -![](../media/helios-sdk/advanced/image_1f5hsgiefmj0abp6ris3h464r.png) - -**步骤四:验证功能** - -前三步完成后,可直接编译和烧录,无需再修改脚本文件。 - -烧录完成后,重启模块,打开QPYcom,输入以下命令: -``` -import modtest -mymath = modtest.math() # 创建mymath对象 -mymath.add(10, 20) # 执行加法运算 -``` - -执行结果如下图所示: - -![](../media/helios-sdk/advanced/image_1f5ht30cv14281nh6hjj1t47leh58.png) - -上图可见,`mymath.add()`方法成功执行。 - -## 4. 结束语 - -到此为止,《Helios SDK指南》告一段落,相信大家已经能够顺利踏入quecPython的大门了。 -期望大家能够基于quecPython实现强大的功能。 diff --git a/docs/Getting_started/zh/helios-sdk/junior.md b/docs/Getting_started/zh/helios-sdk/junior.md deleted file mode 100644 index cf7bef76f372b945e71875c04a8a888584db6952..0000000000000000000000000000000000000000 --- a/docs/Getting_started/zh/helios-sdk/junior.md +++ /dev/null @@ -1,169 +0,0 @@ -# Helios SDK开发指南(2)__进阶 - -## 1. menuconfig - -menuconfig,顾名思义,具有menu和config的双重含义,即基于菜单式的配置。 - -### 1.1 menuconfig操作说明 - -#### **1.1.1 界面说明** - -在命令行中键入`helios menuconfig`,即可启动配置菜单,如图所示: - -![](../media/helios-sdk/junior/image_1f55eiuk4uub44d4ojq5nl2f9.png) - -图中可见,menuconfig主要由4部分组成,即**标题**、**使用说明**、**菜单列表**和**操作按键**。 - -- **标题:**图中最上方深蓝色背景上的一排浅蓝色字样`config.mk - Helios SDK Configuration`。 -- **使用说明:**图中灰色背景上最上面三排字样,指导用户如何操作menuconfig。 -- **菜单列表:**图中灰色背景上置于下凹框内的两排字样,即`System Configuration --->`和`quecPython Configuration --->`。 -- **操作按键:**图中最下方一排,由尖括号括起来的字样,即`