├── .gitignore
├── CMakeLists.txt
├── Note.md
├── README.md
├── components
└── infrared_tools
│ ├── CMakeLists.txt
│ ├── component.mk
│ ├── include
│ ├── ir_timings.h
│ └── ir_tools.h
│ └── src
│ ├── ir_builder_rmt_nec.c
│ ├── ir_builder_rmt_rc5.c
│ ├── ir_parser_rmt_nec.c
│ └── ir_parser_rmt_rc5.c
├── docs
├── changed_src
│ └── app_wifi.c
├── images
│ ├── 0.png
│ ├── 1.png
│ ├── 10.PNG
│ ├── 11.PNG
│ ├── 12.PNG
│ ├── 13.PNG
│ ├── 14.PNG
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ ├── 5.png
│ ├── 6.png
│ ├── 7.png
│ ├── 8.PNG
│ ├── 9.PNG
│ └── H0.png
├── pdf
│ ├── DW07D.pdf
│ ├── ESP32_AC_Controller.pdf
│ ├── HAP-Specification-Non-Commercial-Version.pdf
│ ├── Stand-Alone Linear Li-Ion Li-Polymer Charge Management Controller Data Sheet.pdf
│ └── tps63000.pdf
├── word
│ └── R05d电控功能说明书.doc
└── 原理图PCB
│ ├── ESP32_AC_Controller.PcbDoc
│ ├── ESP32_AC_Controller.PrjPcb
│ └── ESP32_AC_Controller.SchDoc
├── main
├── CMakeLists.txt
├── Kconfig.projbuild
├── air_conditioner.c
├── air_conditioner.h
├── app_main.c
└── component.mk
├── partitions_hap.csv
└── sdkconfig.defaults
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | .vscode/
3 | sdkconfig
4 | sdkconfig.old
5 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 |
2 | # (Automatically converted from project Makefile by convert_to_cmake.py.)
3 |
4 | # The following lines of boilerplate have to be in your project's CMakeLists
5 | # in this exact order for cmake to work correctly
6 | cmake_minimum_required(VERSION 3.5)
7 |
8 | if(DEFINED ENV{HOMEKIT_PATH})
9 | set(HOMEKIT_PATH $ENV{HOMEKIT_PATH})
10 | else()
11 | set(HOMEKIT_PATH ${CMAKE_CURRENT_LIST_DIR}/../..)
12 | endif(DEFINED ENV{HOMEKIT_PATH})
13 |
14 | set(EXTRA_COMPONENT_DIRS ${HOMEKIT_PATH}/components ${HOMEKIT_PATH}/components/homekit ${HOMEKIT_PATH}/examples/common )
15 |
16 | include($ENV{IDF_PATH}/tools/cmake/project.cmake)
17 | project(AirConditioner_dormitory)
18 |
--------------------------------------------------------------------------------
/Note.md:
--------------------------------------------------------------------------------
1 | # ESP32 HomeKit AirConditioner项目笔记
2 |
3 | 我是小白,肯定有一些欠妥的地方,欢迎批评指正!
4 |
5 | ## 硬件部分
6 |
7 | [原理图和PCB文件](/docs/AD_files)可以在/docs/原理图PCB中找到。
8 |
9 | ### 锂电池充电放电
10 |
11 | ##### 锂电池充电MCP73833
12 |
13 | 首先USB5V进来,通过MCP73833锂电池充电芯片给锂电池充电,这个芯片是参考[peng-zhihui的Gluon-Power](https://github.com/peng-zhihui/Project-Quantum/tree/master/1.Hardware/Gluon-Power)才用的,因为之前没没有设计过锂电池相关的电路。
14 |
15 | 可以驱动三个LED显示当前充电状态,R6那个1KΩ的电阻确定充电电流为1A,达到4.2V LED会显示充满并停止充电。
16 |
17 | ##### USB 锂电池供电选择
18 |
19 | 这个电路是群里大佬分享的,因为担心在插着USB充电的情况下,锂电池边充电边放电会有问题,所以才有这个电路。功能就是在有USB5V接入的时候,DCDC芯片的输入是USB5V提供的,而断开锂电池的输入;在没有USB5V接入时,DCDC芯片的输入是锂电池提供。这样就可以做到充电的时候只是单纯的给锂电池充电,同时5V输入负责给ESP32供电。[原理传送门](https://mp.weixin.qq.com/s/PFX6RQysr8UhUfQNe2Paqg)。
20 |
21 | ##### 锂电池保护电路DW07
22 |
23 | 网上随便找的一个芯片DW07,按照数据手册的电路图链接的。具体细节没有研究。
24 |
25 | ##### DCDC芯片TPS6300
26 |
27 | 在TI官网找的一个满足功能,价钱合适的一款buck-boost芯片TPS6300。自动切换升压降压模式,这一款是可以根据分压电阻改变输出电压的,主要还是可变电压的这一款便宜点。最大电流可以达到1A(如果我没理解错的话),因为用了7个红外二极管,每个大概50mA,一共就350mA了,再加个ESP32的电流,所以找的这个能输出1A的芯片。
28 |
29 | ### ESP32模块
30 |
31 | 这个没什么好说的,我怕天线那部分搞不好,所以直接买的模组。只把下载口,使能,启动模式接口引了出来。但是在送出去打样之后我发现个问题,我在设计PCB的时候,当时还纳闷为什么模组天线下面低层敷铜的话敷不上去,模组不是在顶层吗?后来想到应该是防止电磁屏蔽之类的,但是我PCB除了天线之外的部分底层都敷铜了,这样应该会影响到信号吧。
32 |
33 | ### 红外二极管驱动
34 |
35 | 这里选择用N沟道的MOSFET来驱动,为什么不用P沟道,我是这样想的:因为ESP32的rmt模块输出的信号是38kHz调制后的,比如要发送引导码(高4.4ms再低4.4ms),相当于要让红外二极管前4.4ms是有38kHz的脉冲,后面4.4ms是没有导通的,也就是MOSFET前4.4ms以38kHz以占空比33%导通,后面4.4ms关闭,用N沟道的话很好理解,4.4ms的高电平加上4.4ms的低电平经过调制接到MOSFET的G极就好了,但是如果用P沟道,P沟道的G极的电平是要前4.4ms以占空比66%为高电平(其中的33%的低电平P-MOS是导通的),后4.4ms为高电平,调制前的引导码怎么设计才能输出这样的波形呢?我能想到的是,IO口一直输出高电平,但是前4.4ms调制,后4.4ms不调制,这样没有N沟道用着简单。
36 |
37 | ## 软件部分
38 |
39 | 就按照程序的启动顺序来写。
40 |
41 | ### HomeKit
42 |
43 | ##### 核心概念
44 |
45 | 首先要知道,在HomeKit中,定义了一个配件accessory,一个配件可以有多个服务service,每个服务又可以有多个特征characteristic。比如一个有照明功能的风扇,这是一个配件,但是他又三个服务:必备的的信息服务the Required Accessory Information service、风扇服务a Fan service、电灯服务a Light Bulb service;在风扇服务中,又有多个特征,必备的特征:状态Active,可选则特征:名称Name,转向Rotation Direction,转速 Rotation Speed等等。这些以及被定义的服务和特征都可以在[HAP-Specification-Non-Commercial-Version.pdf](/docs/pdf/HAP-Specification-Non-Commercial-Version.pdf)找到。特征又包括读写权限,这个就是造成某些信息在改变后不能即使更新,而是必须把App切到后台再切回来才能更新的原因。
46 |
47 | 在这个项目中,只有一个空调服务,查看代码可以直到,这个所谓的空调,实际上是用的HomeKit中定义的恒温器来实现的。恒温器和空调确实不是一个东西,当时我也看了一些以及做好的空调伴侣中家庭App的显示,也都是以恒温器的界面来显示的。为什么HomeKit不专门定义一个空调服务呢?就是因为是恒温器,才造成了一堆问题。
48 |
49 | 恒温器目前定义的特征有:
50 | - Active(开关状态)- 读写
51 | - Current Temperature(当前环境温度)- 只读
52 | - Current Heater Cooler State(当前运行模式)- 只读
53 | - Target Heater Cooler State(设定的模式)- 读写
54 | - Name(名称)- 只读
55 | - Rotation Speed(转速)- 读写
56 | - Cooling Threshold Temperature(开始制冷门限)- 读写
57 | - Heating Threshold Temperature(开始制热门限)- 读写
58 |
59 | > 这里的读写,是以家庭App为主语来说的,是家庭App的读写权限。但是在ESP32上,这些值不论读写,都是可以被改变的。
60 |
61 | 在家庭App的配件的详情页中,三种模式(自动、制热、制冷)对应的是Target Heater Cooler State,制冷制热模式对应的温度是Cooling Threshold Temperature或者Heating Threshold Temperature,转速对应Rotation Speed,开关对应Active。
62 |
63 |
64 |
65 | ##### 温度范围问题:
66 |
67 | 发现没有,这些特征都是可写的。同时这也是为什么在制热模式下,最高温度只能开到25℃的原因,因为这个25℃是在Heating Threshold Temperature特征中被HoneKit限制死的,实际上设定的是制热门限,就是说,温度到了XX度以下开始制热的温度,所以HomeKit最高才给了25℃,同样的Cooling Threshold Temperature最高能到35℃,意思是说超过35℃之后开始制冷的温度。什么叫TM的惊喜!这就叫TM的惊喜!
68 |
69 | ##### 只读特征刷新问题:
70 |
71 | 而在刚刚打开家庭App的界面上,“我的家”三个字下面的圆环里的温度对应着Current Temperature,圆环下面的“关闭”的位置对应着Current Heater Cooler State,因为关闭的时候没有模式一说,所以显示的是关闭,这个问题不大。主要是:在详情页中改变了温度和模式之后,再回到刚开始的页面(前提是不切到后台再进来),会发现圆环里的温度还是原来的温度26℃,模式显示的是“闲置”,这个26和闲置,是刚刚创建恒温器服务时赋的初值。所以说刚刚修改的模式和温度并没有在这里体现,因为他们根本不是一个特征啊!**这些特征是只读的,目前只发现在打开或者从后台切出家庭App时这些特征会被重新读取,没有找到主动读取的方法。**
72 |
73 |
74 |
75 |
76 | ### ESP HomeKit SDK
77 |
78 | ##### Register Service Write/Read callback
79 |
80 | 程序在创建完上面配件必要的一些服务和特征时候,就会添加写回调函数`hap_serv_set_write_cb(service, air_conditioner_write)`和读回调函数`hap_serv_set_read_cb(service, air_conditioner_read)`了。我目前的理解只是知道在家庭App中写入特征的值时会调用`air_conditioner_write`在读取时会调用`air_conditioner_read`同时这两个函数的返回值,如果返回失败,那么家庭App中就会显示配件“未响应”
81 |
82 | - `air_conditioner_write`:它一次处理的是多个特征的值,比如从关机状态切换到制冷状态,会发送Target Heater Cooler State和Active这两个特征的值(因为温度默认初始26℃,所以没有发送)所以函数的参数是一个`hap_write_data_t`的数组,for循环每次只对其中一个特征进行匹配和处理,记录下设定的值用来发送红外指令和`air_conditioner_read`中的更新。然后把这些新的值通过`hap_char_update_val`写入到HAP Core中完成更新。
83 |
84 | > 这里一开始有一个小bug,就是在改变模式的时候,不知道为什么,家庭App会向配件发送两次相同的数据,就是会调用`air_conditioner_write`两次(如果你用Siri会发送三次,第一次是原来的模式,后面两次是修改后的模式),在监视器输出中也能看到打印的信息有两次是重复的。不过不处理的话,ESP32会连续向空调发送重复的红外指令,所以后面添加了一个1.5S的等待新指令的程序来解决这个。
85 | >
86 | > 还有一个值得注意的是:我猜测,HAP Core中有这些特征真正的值,`air_conditioner_write`参数中的都只是拷贝罢了,因为最后是要调用`hap_char_update_val`才是真正的写入。
87 |
88 | - `air_conditioner_read`:这个不同于`air_conditioner_write`会一次输入多个特征的值,而是一个特征对应一次调用。为了改变上面的只读特征刷新的问题,在上面的`air_conditioner_write`处理对应特征的时候记录了设定的温度和模式,这样最起码能在下次打开App时刷新,而不是圆圈里一直显示着26℃和“闲置”,比如,在改变制冷模式下的温度时,设定的实际上是Cooling Threshold Temperature,处理这个特征的时候保存了当前设定的温度,再后面重新切回到家庭App时,`air_conditioner_read`会被调用,此时就根据之前记录的设定的温度和设定的模式更新Current Temperature为设定的温度,Current Heater Cooler State为设定的模式,以及Cooling Threshold Temperature和Heating Threshold Temperature为去掉小数后的值。然后调用`hap_char_update_val`写入到HAP Core中完成更新。其他可以读取的特征在这个函数里都要进行处理,目的是要返回`HAP_SUCCESS`不让配件显示为未响应的情况。
89 |
90 | > `hap_char_update_val`是把特征的值写入到HAP Core中,所以说**每次打开家庭App中读取的状态或者值是从HAP Core中读取的**。
91 | >
92 | > 一开始是没有`hap_serv_set_read_cb(service, air_conditioner_read)`这行代码登记回调函数的,这种情况下只会去读取`air_conditioner_write`中被更新的值。
93 |
94 | **所以说目前最大的问题就没有找到让家庭App主动去读HAP Core中特征的值的办法,这样就不需要折腾了。**
95 |
96 | ### RMT (Remote Control) module driver
97 |
98 | ##### 基本介绍
99 |
100 | 这个是ESP-IDF自带的一个发送和接收红外遥控信号的库,可以设定载波的频率和占空比,然后自动帮你把要发送的码元进行调制。这个项目代码是基于NEC例程的代码改过来的。有两个名词需要知道,具体的[官方文档](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/peripherals/rmt.html)有详细介绍。
101 | - items:一个长度为32bit的结构体,bit0-bit14是电平1持续的周期(单位ticks),bit15是这个电平1的高低;bit16-bit30是电平2持续的周期,bit31是电平2的高低。对应的结构体类型是`rmt_item32_t`,如果items中的周期为0,则会停止发送。
102 | - ticks:是时钟分频后的方波周期,项目里是80M的80分频,也就是1us。
103 |
104 | 在发送摩斯电码的例程中,启动发射的函数`rmt_write_items(ac_tx_channel, items, length, true)`就是按照rmt_item32_t数组中的信息来发送的。
105 |
106 | 要搞清楚三个结构体类型(源码也贴在下面了):
107 |
108 | - `ir_builder_config_t` :ir_builder的配置
109 |
110 | typedef struct {
111 | uint32_t buffer_size; /*!< Size of the internal buffer used by IR builder */
112 | ir_dev_t dev_hdl; /*!< IR device handle */
113 | uint32_t flags; /*!< Flags for IR builder, different flags will enable different features */
114 | } ir_builder_config_t;
115 |
116 |
117 | - `nec_builder_t` :NEC编码的一些引导码等码的高低电平时间,自己添加了分割码,以及rmt_item32_t数据存放的空间的首地址
118 |
119 | typedef struct {
120 | ir_builder_t parent;
121 | uint32_t buffer_size;
122 | uint32_t cursor; // 记录当前读取到的items的下标
123 | uint32_t flags;
124 | uint32_t leading_code_high_ticks;
125 | uint32_t leading_code_low_ticks;
126 | uint32_t repeat_code_high_ticks;
127 | uint32_t repeat_code_low_ticks;
128 | uint32_t payload_logic0_high_ticks;
129 | uint32_t payload_logic0_low_ticks;
130 | uint32_t payload_logic1_high_ticks;
131 | uint32_t payload_logic1_low_ticks;
132 | uint32_t ending_code_high_ticks;
133 | uint32_t ending_code_low_ticks;
134 | uint32_t divide_code_high_ticks; // 添加分割码
135 | uint32_t divide_code_low_ticks;
136 | bool inverse;
137 | rmt_item32_t buffer[0];
138 | } nec_builder_t;
139 |
140 | - `ir_builder_t` :在`nec_builder_t`中使用的一个变量类型,主要是存放一些回调函数的地址,也添加了分割码
141 |
142 | struct ir_builder_s {
143 | uint32_t repeat_period_ms;
144 | esp_err_t (*make_head)(ir_builder_t *builder);
145 | esp_err_t (*make_logic0)(ir_builder_t *builder);
146 | esp_err_t (*make_logic1)(ir_builder_t *builder);
147 | esp_err_t (*make_end)(ir_builder_t *builder);
148 | esp_err_t (*build_frame)(ir_builder_t *builder, const AC_R05D_PAYLOAD_CODE send_code);
149 | esp_err_t (*build_repeat_frame)(ir_builder_t *builder);
150 | esp_err_t (*build_divide_frame)(ir_builder_t *builder);
151 | esp_err_t (*get_result)(ir_builder_t *builder, void *result, uint32_t *length);
152 | esp_err_t (*del)(ir_builder_t *builder);
153 | };
154 |
155 | ##### 大致流程
156 |
157 | ###### 配置
158 |
159 | 先根据基本的配置信息进行配置,`ir_builder_config_t`类型的变量`ir_builder_config`设置的保存items的空间大小为128个items(`nec_builder_t`中的`buffer`就是指向的这额外128*32bits的空间的首地址)。然后调用`ir_builder_rmt_new_nec`返回给一个`ir_builder_t`类型的`ir_builder`变量,这个函数的作用就是开辟了`ir_builder_t`类型大小+额外128个`rmt_item32_t`大小的空间,把首地址给一个`nec_builder_t`类型的`nec_builder`变量,然后根据一些R05D编码时长的宏定义赋值到`nec_builder`变量的成员中保存,以及构造逻辑0逻辑1和引导码,分割码的这些函数指针赋值给`nec_builder`的成员`parnet`的函数指针成员。最后返回`&nec_builder->parent`赋给`ir_builder`。
160 |
161 | > 这个时候返回的`ir_builder`变量的成员里主要是一些函数指针,具体的码的时间长度是在他的“父结构体”中的,这也是为什么像`nec_builder_make_head`这些函数中使用了`__containerof(builder, nec_builder_t, parent)`来获取“父结构体”的地址。(这只是我的简单理解)
162 |
163 | ###### 建立帧结构
164 |
165 | 基本的准备完成了之后,再根据之前再HomeKit的写回调函数中保存的一些模式温度的信息,就到了发送红外指令的阶段了,先调用之前`ir_builder_rmt_new_nec`返回的`ir_builder`变量传进来的函数地址指向的函数体`nec_build_frame`,顾名思义,就是根据之前的信息建立NEC码(实际上是R05D码)的items,往之前开辟的128个items里放入合适的周期数和电平等级。
166 |
167 | 先用`__containerof(builder, nec_builder_t, parent)`获得他的“父结构体”的首地址给`nec_builder`变量,因为这个`nec_builder`中存放着一些要发送的码的时长等信息,同时把cursor清零,表示我们从第一个itmes开始填数据。先建立要发送的引导码,就是4.4ms的高电平,再4.4ms的低电平。就是往第一个itmes空间放入电平1的周期为4400个ticks(每个ticks是1us,一共就是4.4ms),电平1的高低为高电平,因为`nec_builder->inverse`的值为`false`,电平2的周期为4400个ticks,电平2的高低为低电平,然后`cursor += 1`这样引导码就建立好了。
168 |
169 | > 只能说根据实际来看,很多文档中说的引导码应该是先低电平再高电平,但是**发的时候确是按相反的来发**的,后面的逻辑0逻辑1,分割码,结束码也是一样。就是说发引导码的时候红外二极管要先以4.4ms f = 38kHz D=33%导通,再4.4ms关闭,这里说的和硬件没关系,用P沟道红外二极管也是这样。
170 |
171 | 后面就是根据R05D的**AA' BB' CC' S AA' BB' CC'** 这样的格式建立起来的,[R05d电控功能说明书](/docs/word/R05d电控功能说明书.doc),A一般都是固定的0xB2即0b1011 0010,就for循环8次,每次判断是填逻辑0的编码还是填逻辑1的编码,**但是这里是从最高位开始发的要注意!**
172 |
173 | 咱一开始是不知道要发结束码的,后来怎么搞空调都没反应,走头无路了,就试着加上一个结束码0.54ms的高电平再30ms的低电平,低电平应该够长就行,加上后空调终于有响应了,添加结束码的时候,要把下一个items的周期填0,这样才能停止发送。然后`ir_builder->get_result`这个函数就是把刚刚那块itmes的首地址赋值给传入的指针和长度(长度也就是`cursor`最后的值),这里面的强制类型转换放在等号左边我想了很久。。。之后就是把刚刚填入的那些items的信息通过`rmt_write_items`发送出去就行了。
174 |
175 | ###### 发送
176 |
177 | 构造帧,发送帧这些都放在了一个`ac_send_r05d_code(AC_INFO ac_info)`函数中了,根据要发送的空调的信息把AA' BB' CC' S AA' BB' CC'这些赋值不同的代码,再构造帧后发送。原本这个`ac_send_r05d_code`函数在HomeKit的写回调函数的最后会被调用,但因为短时间内发送多条红外指令的问题,改成了把允许发送的`ac_send_r05d_code_flag`变量置为`true`,并且记录HomeKit写回调函数被调用时的tick计数给`ac_send_tick_count`变量,用来给后面的另一个线程判断1.5S内没有新的指令。
178 |
179 | ###### 发送等待
180 |
181 | 上面提到的在改变模式时,以及改变风速时,会造成短时间内发送多条红外指令,为了避免这样的问题,新开辟了一个线程,而且FreeRTOS以前没有接触过,就按照其他的一些例程照葫芦画瓢了。
182 |
183 | 这个线程就是每隔0.1S检查`ac_send_r05d_code_flag`是否为`true`,当为`true`后,检查`ac_send_r05d_code`和`nowTickCount`的差值,如果他们之间的差值大于1.5S的tick个数,那么就根据当前保存的模式发送红外指令,同时把`ac_send_r05d_code_flag`置为`false`否则我们就不发送红外指令。这样如果1.5S内有新的指令到来,`ac_send_r05d_code_flag`就会被更新,那么之前的指令有可能就会被覆盖而没有发送出去,直到1.5S没有新的指令到来,才根据当前的信息发送指令。
184 |
185 | 这样就发完了一个基本功能的红外指令,其他的重复码,以及空调的高级功能,emmmmmm随缘吧(~ ̄▽ ̄)~
186 |
187 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 接入HomeKit的ESP32空调控制器
2 | ## 更新日志
3 |
4 | - **2021.9.25**:根据power_save例程添加了light-sleep模式及DFS,系统自动决定CPU频率和是否进入light-sleep模式。
5 |
6 | **因为手里只有一个量程为20A的电流表(小量程用不了),测得结果肯定不准确,之前的代码在不用家庭App操控时的电流大约为30\~40mA,现在大约为10\~20mA,如果与家庭App通信时电流大约为40~70mA**
7 |
8 | 目前只添加了WiFi使用HARDCODED时的代码;以及`rmt_config_t`变量中的成员`flags`设置为`RMT_CHANNEL_FLAGS_AWARE_DFS`和`clk_div`为`1`以支持DFS;在发生红外指令时,调用电源管理锁`esp_pm_lock_handle_t`防止自动进入light-sleep模式。
9 |
10 | 1. 需要在menuconfig中启动`Support for power management`和`Tickless idle support`选项,以及进行`WiFi listen interval`、`power save mode`和`Maximum/Minimum CPU frequency`配置
11 |
12 | 2. 需要在`esp-homekit-sdk/examples/common/app_wifi/app_wifi.c`中275行
13 |
14 | ```c
15 | #ifdef CONFIG_APP_WIFI_USE_HARDCODED
16 | #define APP_WIFI_SSID CONFIG_APP_WIFI_SSID
17 | #define APP_WIFI_PASS CONFIG_APP_WIFI_PASSWORD
18 | ```
19 |
20 | 下面添加如下宏定义:
21 |
22 | ```c
23 | #define DEFAULT_LISTEN_INTERVAL CONFIG_APP_WIFI_LISTEN_INTERVAL
24 |
25 | #if CONFIG_APP_POWER_SAVE_MIN_MODEM
26 | #define DEFAULT_PS_MODE WIFI_PS_MIN_MODEM
27 | #elif CONFIG_APP_POWER_SAVE_MAX_MODEM
28 | #define DEFAULT_PS_MODE WIFI_PS_MAX_MODEM
29 | #elif CONFIG_APP_POWER_SAVE_NONE
30 | #define DEFAULT_PS_MODE WIFI_PS_NONE
31 | #else
32 | #define DEFAULT_PS_MODE WIFI_PS_NONE
33 | #endif /*CONFIG_POWER_SAVE_MODEM*/
34 | ```
35 |
36 | 3. `app_wifi_start()`函数中`password`下面添加一行
37 |
38 | `.listen_interval = DEFAULT_LISTEN_INTERVAL,`
39 |
40 | 4. `ESP_ERROR_CHECK(esp_wifi_start() );`下面添加
41 |
42 | ```c
43 | ESP_LOGI(TAG, "esp_wifi_set_ps().");
44 | esp_wifi_set_ps(DEFAULT_PS_MODE);
45 | ```
46 |
47 | > 相关宏定义会在main/Kconfig.projbuild文件中自动生成。
48 | >
49 | > 附上修改后的[app_wifi.c](/docs/changed_src/app_wifi.c)文件。
50 |
51 | ## 介绍
52 |
53 | - 因为宿舍空调遥控器有问题,而且最近没啥事情做,所以想到使用ESP32做一个空调伴侣,接入Apple的HomeKit实现iOS设备对空调的控制,目前只能对美的空调进行控制,只包括基本功能:**开关**、**模式**(自动、制冷、制热)、**温度**(制冷17-30℃,制热17-25℃)、**风速**(自动风、低、中、高风)。锂电池供电,可用MicroUSB接口充电,充满自动断开,基本的锂电池保护(硬件是设计了但我也没实际测试过O(∩_∩)O),锂电池保护,DW07D这里要注意一点,就是如果第一次装上电池,需要先充电一下才能正常供电,只要不把电池拆下来,就不必再这样做。
54 |
55 | - 目前问题或bug(具体原因[笔记](/Note.md)中会解释):
56 | - 家庭App界面,在设定空调一些参数后(比如模式),长按配件后出现的设定界面会更新状态,但是打开家庭的初始界面中不能立刻更新,必须把家庭App切到后台再切回来才能更新。
57 | - 家庭App中空调制热的设定范围,注意是显示的设定范围,是到0\~25℃,制冷温度的设定范围是10\~35℃,实际上如果超出了17\~30℃这个范围的话就还是17℃或者30℃。
58 |
59 | - 这个项目参考了乐鑫的homekitSDK和ESP-IDF中的红外NEC编码示例。以及美的的R05D编码的文档。相关链接放在下面:
60 |
61 | > [ESP HomeKit SDK](https://github.com/espressif/esp-homekit-sdk) 其中的/examples/emulator例程相当于是一个仿真,在这个例程里可以创建很多的homekit支持的配件,但是没有实际的效果,参考了里面air conditioner的一段程序。这个SDK都是没有MFI认证的Non-Commercial-Version,所以配对时都会提示此配件不可信任,具体关于homekit协议的参考[HAP-Specification-Non-Commercial-Version](/docs/pdf/HAP-Specification-Non-Commercial-Version.pdf)这个文档。
62 | >
63 | > [ESP-IDF NEC](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/rmt/ir_protocols) 乐鑫官方的SDK ESP-IDF中的关于NEC编码的例程。
64 | >
65 | > [R05D介绍](https://www.codenong.com/cs109263771/) 介绍了关于美的空调的红外编码R05D格式,以及一些红外遥控的代码,其中最重要的就是高位先发,低位后发,而且发的是和定义的码电平相反的,这个其他很多文档我都没看到说。以及[ir_Coolix.h](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h) 这个红外库的头文件中也可以部分参考(我能发现的就是模式那里这个ir_Coolix.h中多了一位)。
66 |
67 | - 这个项目,我很多东西也都是才接触,所以难免有不准确不合适或者错误的地方,看到的兄弟麻烦指出来。
68 | - [视频地址](https://www.bilibili.com/video/BV1eU4y137YL/)
69 |
70 | ## 硬件连接
71 |
72 | 只用了一个GPIO18,你可以选择IO直接驱动红外二极管,最好加个NPN三极管或者N沟道的MOSFET驱动。一开始测试的时候我是IO18接到红外二极管正极,通过一个220欧的电阻再到GND,我担心把IO搞坏掉,所以电阻阻值比较大,在220欧的情况下,红外发射距离大致只有5m。所以后面打样的PCB中设计的电路如下:
73 |
74 | [具体的AD文件](/docs/原理图PCB)
75 |
76 | > 如果图片不显示,请科学上网。或者到/docs/images/目录下查看。
77 |
78 | 
79 |
80 |
81 | ## 环境搭建
82 |
83 | - ESP-IDF
84 | - ESP HomeKit SDK
85 |
86 | ### ESP-IDF
87 |
88 | 要搞好关于ESP-IDF的安装,需要一个比较好的网络,我安装失败了很多次,几乎都是网络问题。具体请参考[官方教程](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/get-started/index.html)。我只是补充一下,如果用windows的[ESP-IDF 工具安装器](https://dl.espressif.com/dl/esp-idf/?idf=4.4)嫌麻烦建议直接下载离线版本,虽然大,但是解千愁,不会出现什么失败的问题。如果是想用[VS Code的IDE插件](https://github.com/espressif/vscode-esp-idf-extension)的话,也最好找个网好的地方安装,如果本地没有esp-idf,那么要是esp-idf下载失败就会从头开始下载,即使使用本地的esp-idf文件,但是还是需要下载其他东西的,而且这些东西,也挺容易下载失败的,我自己下的时候就算科学上网也是失败了很多次。我下载的时候是4.2版本的,后面更新到4.3也是可以的(4.3好像会弹一些警告),下面以VS Code的IDE插件为例。
89 |
90 | ### ESP HomeKit SDK
91 |
92 | 具体也请参考[官方教程](https://github.com/espressif/esp-homekit-sdk),前提是先安装好ESP-IDF。
93 |
94 | ## 项目编译、烧录
95 |
96 | ### 编译准备
97 |
98 | 1、先把这个项目文件夹放到/esp-homekit-sdk/examples目录下面.
99 |
100 | **切记**项目文件夹的路径不要太深,导致路径太长,这样会导致编译过程中出现问题。
101 |
102 | > 因为默认[CMakeList.txt](/CMakeLists.txt)的文件是读取上上级目录中的HomeKitSDK的路径,如下面所示。或者修改根目录下的CMakeLists.txt文件里面添加HomeKitSDK的组件也可以,[CMakeLists文件说明传送门](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-guides/build-system.html#cmakelists)。或者自己定义系统变量`HOMEKIT_PATH`
103 |
104 |
105 | if(DEFINED ENV{HOMEKIT_PATH})
106 | set(HOMEKIT_PATH $ENV{HOMEKIT_PATH})
107 | else()
108 | set(HOMEKIT_PATH ${CMAKE_CURRENT_LIST_DIR}/../..)
109 | endif(DEFINED ENV{HOMEKIT_PATH})
110 |
111 |
112 |
113 | 2、使用VS Code打开这个项目文件夹,先clean一下项目,按F1输入ESP Full Clean Project(如果有build文件夹就是把build文件夹删除)要不然里面的某些文件路径可能会导致编译会报错,除非项目路径和我编译时候的一模一样。
114 |
115 | 
116 |
117 | 3、F1打开输入ESP Device configuration 选择自己使用的设备。
118 |
119 | 
120 |
121 | 4、选择端口,同样的F1后输入ESP Select port to use选择使用的端口,要先插上板子才会显示。
122 |
123 | 
124 |
125 | 5、提前设置好wifi的名称和密码,点击左下角的插件中的齿轮图标,然后点击App Wi-Fi,选择使用 Use Hardcoded,填入wifi名称和密码,保存。
126 |
127 | 
128 | 
129 |
130 | > 这个如果使用的是ESP-IDF的CMD或者PowerShell的话(就是安装完ESP-IDF,开始菜单里面多的那俩),再进入到项目根目录下敲入`idf.py menuconfig`这个命令,也会出现相同的设置。
131 | > 这个设置完后,我的理解是把设置的信息都保存到了一个[sdkconfig](/sdkconfig)文件里面,**如果上面的wifi名称有中文,那么这个文件里面的`CONFIG_APP_WIFI_SSID`的值实际上是乱码的,需要在sdkconfig文件中找到`CONFIG_APP_WIFI_SSID`这个定义然后改正确**
132 | >
133 | > 或者在App WiFi中也可以不选择Use HardCode而选择Use Provisioning,这样不需要先准备好wifi名称和密码,而是在烧录完程序后,他会弹出个二维码,需要下载**ESP BLE Prov**这个App去扫描,用蓝牙连接后再设置wifi。
134 |
135 | 这里还可以改变到时候在家庭App中配对的代码,默认是11122333,也可以在sdkconfig文件中修改。
136 |
137 | 
138 |
139 | > 如果上面的过程中遇到什么问题了,先别忘了clean project,然后可以试一试ESP-IDF PowerShell来搞上面的这些,我在使用的时候,有时候就是VS Code插件里面的会报错,但是ESP-IDF PowerShell里就不会。
140 |
141 | ### 编译、下载、监视 一条龙服务
142 |
143 | 上面没问题的话,就可以点击左下角的火焰按钮了,或者依次点击左边三个也是一样的。
144 |
145 | > 下载的时候,先把GPIO0拉低(按住GPIO0的按钮SW1不放),然后让EN引脚拉低一下(按下EN的按钮SW2松开),看见控制台有提示连接上后就可以松开GPIO0的按钮SW1了。
146 |
147 | 
148 |
149 | 在监视的输出界面中,他会弹出个二维码,在iOS设备和ESP32在同一个wifi的情况下,使用家庭App扫描这个二维码就可以配对了,这样就不需要输出那个配对码11122333了
150 |
151 | 如果二维码扫描不了,但是又想扫的话,可以点击二维码下面的链接。
152 |
153 | 
154 |
155 | 或者也可以选择“我没有或无法扫描代码”,手动输入配对码添加配件。
156 |
157 |
158 |
159 | 一般没有问题的话,他会直接找到这个配件,就像下面这样,点击后输入11122333就可以配对了。
160 |
161 |
162 |
163 | 没有MFI认证就会有这个弹窗,点击仍然添加就是啦
164 |
165 |
166 |
167 | 配对完成后,监视控制台应该也可看到相应的输出,按“Ctrl+]”可以退出监视器。
168 |
169 | ## 使用
170 |
171 |
172 |
173 | 在家庭App刚刚添加的这个空调配件中,自动模式、制冷模式、制热模式、关闭和设置温度都可以使用,只要不是关闭其他模式都会打开空调。
174 |
175 | 温度虽然可以设定为小数xx.5℃,但是实际上设定的温度会去掉小数部分。关于设定范围,这个问题在开头提了。
176 |
177 | 下面的风扇速度对应着空调的风速,因为是一个滑块,滑倒最左边相当于关闭空调,0\~33%低风,33\~66%中风,66\~99%高风,100%也就是最右边为自动风。
178 |
179 | > 因为这个滑块在滑动的时候会不断的发送新的值给配件,所以在配件程序里设置了一个延时发送的程序,如果在接收到新的指令后的1.5S内,没有新的指令到来了,才发送红外指令,如果有新的指令,那么上一个指令会被覆盖,再等待1.5S,直到没有新的指令到来后才发送。
180 | >
181 | > 所以存在1.5S+的延时,有种反应速度慢的感觉。
182 |
183 | 有一个值得提醒的是:你在长按配件打开详情界面时,这时会发送一次红外指令出去,也就是你当时设置的状态。
184 |
185 | 至于延时关机开机的功能,没有加上去,第一因为懒,第二是因为在快捷指令App中的自动化可以完成这些功能。
186 |
187 | 具体的细节,硬件设计思路,程序思路可以参考本人的[笔记](/Note.md)。
188 |
189 | ## 遇到的bug
190 |
191 | - 在添加DFS和light-sleep调试的时候,遇到过下载程序后复位发生两次或者数次重启的情况,在断开USB接口重新插入后正常启动,后来这些bug莫名其妙消失了,也许是因为清空了FLASH。
192 |
193 | - 在IOS15时,添加配件完成后,对配件进行相关操作时,家庭App中无法切换至显示操作后的状态,比如一开始是关闭状态,要设置为制冷模式,家庭App中设置以后,配件实际上执行了相应的操作,但是在家庭App中还是立马切换至了关闭的状态。这种情况关闭家庭App后重新打开即可。
194 | - IOS15中,改变风速后,遇到过一次配件接收不到相应的指令的情况。也许是因为`power save mode`设置为`minimum modem`的原因。
195 |
--------------------------------------------------------------------------------
/components/infrared_tools/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | set(component_srcs "src/ir_builder_rmt_nec.c"
2 | )
3 |
4 | idf_component_register(SRCS "${component_srcs}"
5 | INCLUDE_DIRS "include"
6 | PRIV_INCLUDE_DIRS ""
7 | PRIV_REQUIRES "driver"
8 | REQUIRES "main")
9 |
10 |
--------------------------------------------------------------------------------
/components/infrared_tools/component.mk:
--------------------------------------------------------------------------------
1 | COMPONENT_ADD_INCLUDEDIRS := include
2 |
3 | COMPONENT_SRCDIRS := src
4 |
--------------------------------------------------------------------------------
/components/infrared_tools/include/ir_timings.h:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #ifdef __cplusplus
18 | extern "C" {
19 | #endif
20 |
21 | /**
22 | * @brief Timings for NEC protocol
23 | *
24 | */
25 | #define NEC_LEADING_CODE_HIGH_US (9000)
26 | #define NEC_LEADING_CODE_LOW_US (4500)
27 | #define NEC_PAYLOAD_ONE_HIGH_US (560)
28 | #define NEC_PAYLOAD_ONE_LOW_US (1690)
29 | #define NEC_PAYLOAD_ZERO_HIGH_US (560)
30 | #define NEC_PAYLOAD_ZERO_LOW_US (560)
31 | #define NEC_REPEAT_CODE_HIGH_US (9000)
32 | #define NEC_REPEAT_CODE_LOW_US (2250)
33 | #define NEC_ENDING_CODE_HIGH_US (560)
34 |
35 |
36 | #define R05D_LEADING_CODE_HIGH_US (4400)
37 | #define R05D_LEADING_CODE_LOW_US (4400)
38 | #define R05D_PAYLOAD_ONE_HIGH_US (540)
39 | #define R05D_PAYLOAD_ONE_LOW_US (1620)
40 | #define R05D_PAYLOAD_ZERO_HIGH_US (540)
41 | #define R05D_PAYLOAD_ZERO_LOW_US (540)
42 | #define R05D_REPEAT_CODE_HIGH_US (9000)
43 | #define R05D_REPEAT_CODE_LOW_US (2250)
44 | #define R05D_ENDING_CODE_HIGH_US (540)
45 | #define R05D_ENDING_CODE_LOW_US (30000)
46 | #define R05D_DIVIDE_CODE_HIGH_US (540)
47 | #define R05D_DIVIDE_CODE_LOW_US (5220)
48 |
49 | /**
50 | * @brief Timings for RC5 protocol
51 | *
52 | */
53 | #define RC5_PULSE_DURATION_US (889)
54 |
55 | #ifdef __cplusplus
56 | }
57 | #endif
58 |
--------------------------------------------------------------------------------
/components/infrared_tools/include/ir_tools.h:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #ifdef __cplusplus
18 | extern "C" {
19 | #endif
20 |
21 | #include "esp_err.h"
22 | #include "air_conditioner.h"
23 | #define IR_TOOLS_FLAGS_PROTO_EXT (1 << 0) /*!< Enable Extended IR protocol */
24 | #define IR_TOOLS_FLAGS_INVERSE (1 << 1) /*!< Inverse the IR signal, i.e. take high level as low, and vice versa */
25 |
26 | /**
27 | * @brief IR device type
28 | *
29 | */
30 | typedef void *ir_dev_t;
31 |
32 | /**
33 | * @brief IR builder type
34 | *
35 | */
36 | typedef struct ir_builder_s ir_builder_t;
37 |
38 | /**
39 | * @brief IR parser type
40 | *
41 | */
42 | typedef struct ir_parser_s ir_parser_t;
43 |
44 | /**
45 | * @brief Type definition of IR builder
46 | *
47 | */
48 | struct ir_builder_s {
49 | /**
50 | * @brief Period time of sending repeat code
51 | *
52 | */
53 | uint32_t repeat_period_ms;
54 |
55 | /**
56 | * @brief Build frame header
57 | *
58 | * @param[in] builder: Handle of IR builder
59 | *
60 | * @return
61 | * - ESP_OK: Build frame header successfully
62 | * - ESP_FAIL: Build frame header failed because some error occurred
63 | */
64 | esp_err_t (*make_head)(ir_builder_t *builder);
65 |
66 | /**
67 | * @brief Build logic bit zero
68 | *
69 | * @param[in] builder: Handle of IR builder
70 | *
71 | * @return
72 | * - ESP_OK: Build logic bit zero successfully
73 | * - ESP_FAIL: Build logic bit zero failed because some error occurred
74 | */
75 | esp_err_t (*make_logic0)(ir_builder_t *builder);
76 |
77 | /**
78 | * @brief Build logic bit one
79 | *
80 | * @param[in] builder: Handle of IR builder
81 | *
82 | * @return
83 | * ESP_OK: Build logic bit one successfully
84 | * ESP_FAIL: Build logic bit one failed because some error occurred
85 | */
86 | esp_err_t (*make_logic1)(ir_builder_t *builder);
87 |
88 | /**
89 | * @brief Build frame tail
90 | *
91 | * @param[in] builder: Handle of IR builder
92 | *
93 | * @return
94 | * - ESP_OK: Build frame tail successfully
95 | * - ESP_FAIL: Build frame tail failed because some error occurred
96 | */
97 | esp_err_t (*make_end)(ir_builder_t *builder);
98 |
99 | /**
100 | * @brief Build a complete frame
101 | *
102 | * @param[in] builder: Handle of IR builder
103 | *
104 | * @return
105 | * - ESP_OK: Build a complete frame successfully
106 | * - ESP_FAIL: Build a complete frame failed because some error occurred
107 | */
108 | esp_err_t (*build_frame)(ir_builder_t *builder, const AC_R05D_PAYLOAD_CODE send_code);
109 |
110 | /**
111 | * @brief Build a repeat frame
112 | *
113 | * @param[in] builder: Handle of IR builder
114 | *
115 | * @return
116 | * - ESP_OK: Build a repeat frame successfully
117 | * - ESP_FAIL: Build a repeat frame failed because some error occurred
118 | */
119 | esp_err_t (*build_repeat_frame)(ir_builder_t *builder);
120 |
121 |
122 | /**
123 | * @brief Build a divide frame
124 | *
125 | * @param[in] builder: Handle of IR builder
126 | *
127 | * @return
128 | * - ESP_OK: Build a repeat frame successfully
129 | * - ESP_FAIL: Build a repeat frame failed because some error occurred
130 | */
131 | esp_err_t (*build_divide_frame)(ir_builder_t *builder);
132 |
133 | /**
134 | * @brief Get the result frame after a series of building steps
135 | *
136 | * @param[in] builder: Handle of IR builder
137 | * @param[out] result: Result of frame building, which contains all of the raw data that could be send directly
138 | * @param[out] length: Length of result data
139 | *
140 | * @return
141 | * - ESP_OK: Get result data successfully
142 | * - ESP_ERR_INVALID_ARG: Get result data failed because of invalid arguments
143 | * - ESP_FAIL: Get result data failed because some other errors occurred
144 | */
145 | esp_err_t (*get_result)(ir_builder_t *builder, void *result, uint32_t *length);
146 |
147 | /**
148 | * @brief Free resources used by IR builder
149 | *
150 | * @param[in] builder: Handle of IR builder
151 | *
152 | * @return
153 | * - ESP_OK: Free resources successfully
154 | * - ESP_FAIL: Free resources failed because some error occurred
155 | */
156 | esp_err_t (*del)(ir_builder_t *builder);
157 | };
158 |
159 | /**
160 | * @brief Type definition of IR parser
161 | *
162 | */
163 | struct ir_parser_s {
164 | /**
165 | * @brief Input raw data to IR parser
166 | *
167 | * @param[in] parser: Handle of IR parser
168 | * @param[in] raw_data: Raw data which need decoding by IR parser
169 | * @param[in] length: Length of raw data
170 | *
171 | * @return
172 | * - ESP_OK: Input raw data successfully
173 | * - ESP_ERR_INVALID_ARG: Input raw data failed because of invalid argument
174 | * - ESP_FAIL: Input raw data failed because some other error occurred
175 | */
176 | esp_err_t (*input)(ir_parser_t *parser, void *raw_data, uint32_t length);
177 |
178 | /**
179 | * @brief Get the scan code after decoding of raw data
180 | *
181 | * @param[in] parser: Handle of IR parser
182 | * @param[out] address: Address of the scan code
183 | * @param[out] command: Command of the scan code
184 | * @param[out] repeat: Indicate if it's a repeat code
185 | *
186 | * @return
187 | * - ESP_OK: Get scan code successfully
188 | * - ESP_ERR_INVALID_ARG: Get scan code failed because of invalid arguments
189 | * - ESP_FAIL: Get scan code failed because some error occurred
190 | */
191 | esp_err_t (*get_scan_code)(ir_parser_t *parser, uint32_t *address, uint32_t *command, bool *repeat);
192 |
193 | /**
194 | * @brief Free resources used by IR parser
195 | *
196 | * @param[in] parser: Handle of IR parser
197 | *
198 | * @return
199 | * - ESP_OK: Free resource successfully
200 | * - ESP_FAIL: Free resources fail failed because some error occurred
201 | */
202 | esp_err_t (*del)(ir_parser_t *parser);
203 | };
204 |
205 | /**
206 | * @brief Configuration type of IR builder
207 | *
208 | */
209 | typedef struct {
210 | uint32_t buffer_size; /*!< Size of the internal buffer used by IR builder */
211 | ir_dev_t dev_hdl; /*!< IR device handle */
212 | uint32_t flags; /*!< Flags for IR builder, different flags will enable different features */
213 | } ir_builder_config_t;
214 |
215 | /**
216 | * @brief Configuration type of IR parser
217 | *
218 | */
219 | typedef struct {
220 | ir_dev_t dev_hdl; /*!< IR device handle */
221 | uint32_t flags; /*!< Flags for IR parser, different flags will enable different features */
222 | uint32_t margin_us; /*!< Timing parameter, indicating the tolerance to environment noise */
223 | } ir_parser_config_t;
224 |
225 | /**
226 | * @brief Default configuration for IR builder
227 | *
228 | */
229 | #define IR_BUILDER_DEFAULT_CONFIG(dev) \
230 | { \
231 | .buffer_size = 64, \
232 | .dev_hdl = dev, \
233 | .flags = 0, \
234 | }
235 |
236 | /**
237 | * @brief Default configuration for IR parser
238 | *
239 | */
240 | #define IR_PARSER_DEFAULT_CONFIG(dev) \
241 | { \
242 | .dev_hdl = dev, \
243 | .flags = 0, \
244 | .margin_us = 200, \
245 | }
246 |
247 | /**
248 | * @brief Creat a NEC protocol builder
249 | *
250 | * @param config: configuration of NEC builder
251 | * @return
252 | * Handle of NEC builder or NULL
253 | */
254 | ir_builder_t *ir_builder_rmt_new_nec(const ir_builder_config_t *config);
255 |
256 | /**
257 | * @brief Creat a RC5 protocol builder
258 | *
259 | * @param config: configuration of RC5 builder
260 | * @return
261 | * Handle of RC5 builder or NULL
262 | */
263 | ir_builder_t *ir_builder_rmt_new_rc5(const ir_builder_config_t *config);
264 |
265 | /**
266 | * @brief Creat a NEC protocol parser
267 | *
268 | * @param config: configuration of NEC parser
269 | * @return
270 | * Handle of NEC parser or NULL
271 | */
272 | ir_parser_t *ir_parser_rmt_new_nec(const ir_parser_config_t *config);
273 |
274 | /**
275 | * @brief Creat a RC5 protocol parser
276 | *
277 | * @param config: configuration of RC5 parser
278 | * @return
279 | * Handle of RC5 parser or NULL
280 | */
281 | ir_parser_t *ir_parser_rmt_new_rc5(const ir_parser_config_t *config);
282 | #ifdef __cplusplus
283 | }
284 | #endif
285 |
--------------------------------------------------------------------------------
/components/infrared_tools/src/ir_builder_rmt_nec.c:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.#include
14 | #include
15 | #include "esp_log.h"
16 | #include "ir_tools.h"
17 | #include "ir_timings.h"
18 | #include "driver/rmt.h"
19 |
20 |
21 |
22 | static const char *TAG = "nec_builder";
23 | #define NEC_CHECK(a, str, goto_tag, ret_value, ...) \
24 | do \
25 | { \
26 | if (!(a)) \
27 | { \
28 | ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
29 | ret = ret_value; \
30 | goto goto_tag; \
31 | } \
32 | } while (0)
33 |
34 |
35 |
36 | typedef struct {
37 | ir_builder_t parent;
38 | uint32_t buffer_size;
39 | uint32_t cursor; // 记录当前读取到的items的下标
40 | uint32_t flags;
41 | uint32_t leading_code_high_ticks;
42 | uint32_t leading_code_low_ticks;
43 | uint32_t repeat_code_high_ticks;
44 | uint32_t repeat_code_low_ticks;
45 | uint32_t payload_logic0_high_ticks;
46 | uint32_t payload_logic0_low_ticks;
47 | uint32_t payload_logic1_high_ticks;
48 | uint32_t payload_logic1_low_ticks;
49 | uint32_t ending_code_high_ticks;
50 | uint32_t ending_code_low_ticks;
51 | uint32_t divide_code_high_ticks; // 添加分割码
52 | uint32_t divide_code_low_ticks;
53 | bool inverse;
54 | rmt_item32_t buffer[0];
55 | } nec_builder_t;
56 |
57 | static esp_err_t nec_builder_make_head(ir_builder_t *builder)
58 | {
59 | nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent); // 获取结构体成员所在的结构体的地址
60 | // nec_builder->cursor = 0;
61 | nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
62 | nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->leading_code_high_ticks;
63 | nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
64 | nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->leading_code_low_ticks;
65 | nec_builder->cursor += 1;
66 | return ESP_OK;
67 | }
68 |
69 | static esp_err_t nec_builder_make_logic0(ir_builder_t *builder)
70 | {
71 | nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
72 | nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
73 | nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->payload_logic0_high_ticks;
74 | nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
75 | nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->payload_logic0_low_ticks;
76 | nec_builder->cursor += 1;
77 | return ESP_OK;
78 | }
79 |
80 | static esp_err_t nec_builder_make_logic1(ir_builder_t *builder)
81 | {
82 | nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
83 | nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
84 | nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->payload_logic1_high_ticks;
85 | nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
86 | nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->payload_logic1_low_ticks;
87 | nec_builder->cursor += 1;
88 | return ESP_OK;
89 | }
90 |
91 | static esp_err_t nec_builder_make_end(ir_builder_t *builder)
92 | {
93 | nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
94 | nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
95 | nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->ending_code_high_ticks;
96 | nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
97 | nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->ending_code_low_ticks;
98 | nec_builder->cursor += 1;
99 | nec_builder->buffer[nec_builder->cursor].val = 0; // 全部放0表示结束
100 | nec_builder->cursor += 1;
101 | return ESP_OK;
102 | }
103 |
104 | static esp_err_t nec_build_frame(ir_builder_t *builder, const AC_R05D_PAYLOAD_CODE send_code)
105 | {
106 | esp_err_t ret = ESP_OK;
107 | nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
108 |
109 | nec_builder->cursor = 0;
110 | builder->make_head(builder);
111 | // 从最高位开始发送(填入到32*(128-1)的buffer中 上面的head占用了1个嘛)
112 | // A
113 | for (int i = 0; i < 8; i++) {
114 | if (send_code.A & ((1 << 7) >> i)) {
115 | builder->make_logic1(builder);
116 | } else {
117 | builder->make_logic0(builder);
118 | }
119 | }
120 | // A'
121 | for (int i = 0; i < 8; i++) {
122 | if (send_code.A_ & ((1 << 7) >> i)) {
123 | builder->make_logic1(builder);
124 | } else {
125 | builder->make_logic0(builder);
126 | }
127 | }
128 | // B
129 | for (int i = 0; i < 8; i++) {
130 | if (send_code.B & ((1 << 7) >> i)) {
131 | builder->make_logic1(builder);
132 | } else {
133 | builder->make_logic0(builder);
134 | }
135 | }
136 | // B'
137 | for (int i = 0; i < 8; i++) {
138 | if (send_code.B_ & ((1 << 7) >> i)) {
139 | builder->make_logic1(builder);
140 | } else {
141 | builder->make_logic0(builder);
142 | }
143 | }
144 | // C
145 | for (int i = 0; i < 8; i++) {
146 | if (send_code.C & ((1 << 7) >> i)) {
147 | builder->make_logic1(builder);
148 | } else {
149 | builder->make_logic0(builder);
150 | }
151 | }
152 | // C'
153 | for (int i = 0; i < 8; i++) {
154 | if (send_code.C_ & ((1 << 7) >> i)) {
155 | builder->make_logic1(builder);
156 | } else {
157 | builder->make_logic0(builder);
158 | }
159 | }
160 | // 添加分隔码 S
161 | builder->build_divide_frame(builder);
162 | // 再发一次
163 | builder->make_head(builder);
164 | // A
165 | for (int i = 0; i < 8; i++) {
166 | if (send_code.A & ((1 << 7) >> i)) {
167 | builder->make_logic1(builder);
168 | } else {
169 | builder->make_logic0(builder);
170 | }
171 | }
172 | // A'
173 | for (int i = 0; i < 8; i++) {
174 | if (send_code.A_ & ((1 << 7) >> i)) {
175 | builder->make_logic1(builder);
176 | } else {
177 | builder->make_logic0(builder);
178 | }
179 | }
180 | // B
181 | for (int i = 0; i < 8; i++) {
182 | if (send_code.B & ((1 << 7) >> i)) {
183 | builder->make_logic1(builder);
184 | } else {
185 | builder->make_logic0(builder);
186 | }
187 | }
188 | // B'
189 | for (int i = 0; i < 8; i++) {
190 | if (send_code.B_ & ((1 << 7) >> i)) {
191 | builder->make_logic1(builder);
192 | } else {
193 | builder->make_logic0(builder);
194 | }
195 | }
196 | // C
197 | for (int i = 0; i < 8; i++) {
198 | if (send_code.C & ((1 << 7) >> i)) {
199 | builder->make_logic1(builder);
200 | } else {
201 | builder->make_logic0(builder);
202 | }
203 | }
204 | // C'
205 | for (int i = 0; i < 8; i++) {
206 | if (send_code.C_ & ((1 << 7) >> i)) {
207 | builder->make_logic1(builder);
208 | } else {
209 | builder->make_logic0(builder);
210 | }
211 | }
212 |
213 | builder->make_end(builder);
214 |
215 | return ret;
216 | }
217 |
218 | static esp_err_t nec_build_repeat_frame(ir_builder_t *builder)
219 | {
220 | nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
221 | nec_builder->cursor = 0;
222 | nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
223 | nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->repeat_code_high_ticks;
224 | nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
225 | nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->repeat_code_low_ticks;
226 | nec_builder->cursor += 1;
227 | nec_builder_make_end(builder);
228 | return ESP_OK;
229 | }
230 | // 添加分割码
231 | static esp_err_t nec_build_divide_frame(ir_builder_t *builder)
232 | {
233 | nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
234 | nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
235 | nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->divide_code_high_ticks;
236 | nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
237 | nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->divide_code_low_ticks;
238 | nec_builder->cursor += 1;
239 | return ESP_OK;
240 | }
241 |
242 | static esp_err_t nec_builder_get_result(ir_builder_t *builder, void *result, uint32_t *length)
243 | {
244 | esp_err_t ret = ESP_OK;
245 | nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
246 | NEC_CHECK(result && length, "result and length can't be null", err, ESP_ERR_INVALID_ARG);
247 | *(rmt_item32_t **)result = nec_builder->buffer; // 传进来的实际上是一个void的指针的指针,那肯定要先变成rmt_item32_t的指针的指针后,再改变*result的值(指向的地址)
248 | *length = nec_builder->cursor;
249 | // printf("DEBUG:%d\n", *length);
250 | return ESP_OK;
251 | err:
252 | return ret;
253 | }
254 |
255 | static esp_err_t nec_builder_del(ir_builder_t *builder)
256 | {
257 | nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
258 | free(nec_builder);
259 | return ESP_OK;
260 | }
261 |
262 | ir_builder_t *ir_builder_rmt_new_nec(const ir_builder_config_t *config)
263 | {
264 | ir_builder_t *ret = NULL;
265 | NEC_CHECK(config, "nec configuration can't be null", err, NULL);
266 | NEC_CHECK(config->buffer_size, "buffer size can't be zero", err, NULL);
267 |
268 | uint32_t builder_size = sizeof(nec_builder_t) + config->buffer_size * sizeof(rmt_item32_t);
269 | nec_builder_t *nec_builder = calloc(1, builder_size);
270 | NEC_CHECK(nec_builder, "request memory for nec_builder failed", err, NULL);
271 |
272 | nec_builder->buffer_size = config->buffer_size;
273 | nec_builder->flags = config->flags;
274 | if (config->flags & IR_TOOLS_FLAGS_INVERSE) {
275 | nec_builder->inverse = true;
276 | }
277 | uint32_t counter_clk_hz = 0;
278 | NEC_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
279 | "get rmt counter clock failed", err, NULL);
280 | float ratio = (float)counter_clk_hz / 1e6; // 一个ticks的时间us 默认时钟80M,80分频那么就是1M也就是1us
281 | nec_builder->leading_code_high_ticks = (uint32_t)(ratio * R05D_LEADING_CODE_HIGH_US); // 改变引导码长度
282 | nec_builder->leading_code_low_ticks = (uint32_t)(ratio * R05D_LEADING_CODE_LOW_US);
283 | nec_builder->repeat_code_high_ticks = (uint32_t)(ratio * R05D_REPEAT_CODE_HIGH_US);
284 | nec_builder->repeat_code_low_ticks = (uint32_t)(ratio * R05D_REPEAT_CODE_LOW_US);
285 | nec_builder->payload_logic0_high_ticks = (uint32_t)(ratio * R05D_PAYLOAD_ZERO_HIGH_US);
286 | nec_builder->payload_logic0_low_ticks = (uint32_t)(ratio * R05D_PAYLOAD_ZERO_LOW_US);
287 | nec_builder->payload_logic1_high_ticks = (uint32_t)(ratio * R05D_PAYLOAD_ONE_HIGH_US);
288 | nec_builder->payload_logic1_low_ticks = (uint32_t)(ratio * R05D_PAYLOAD_ONE_LOW_US);
289 | nec_builder->ending_code_high_ticks = (uint32_t)(ratio * R05D_ENDING_CODE_HIGH_US);
290 | nec_builder->ending_code_low_ticks = (uint32_t)(ratio * R05D_ENDING_CODE_LOW_US);
291 | nec_builder->divide_code_high_ticks = (uint32_t)(ratio * R05D_DIVIDE_CODE_HIGH_US); // 添加分隔码
292 | nec_builder->divide_code_low_ticks = (uint32_t)(ratio * R05D_DIVIDE_CODE_LOW_US);
293 | nec_builder->parent.make_head = nec_builder_make_head;
294 | nec_builder->parent.make_logic0 = nec_builder_make_logic0;
295 | nec_builder->parent.make_logic1 = nec_builder_make_logic1;
296 | nec_builder->parent.make_end = nec_builder_make_end;
297 | nec_builder->parent.build_frame = nec_build_frame;
298 | nec_builder->parent.build_repeat_frame = nec_build_repeat_frame;
299 | nec_builder->parent.build_divide_frame = nec_build_divide_frame; // 添加分割码
300 | nec_builder->parent.get_result = nec_builder_get_result;
301 | nec_builder->parent.del = nec_builder_del;
302 | nec_builder->parent.repeat_period_ms = 4000;
303 | return &nec_builder->parent;
304 | err:
305 | return ret;
306 | }
307 |
--------------------------------------------------------------------------------
/components/infrared_tools/src/ir_builder_rmt_rc5.c:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | #include
15 | #include
16 | #include "esp_log.h"
17 | #include "ir_tools.h"
18 | #include "ir_timings.h"
19 | #include "driver/rmt.h"
20 |
21 | static const char *TAG = "rc5_builder";
22 | #define RC5_CHECK(a, str, goto_tag, ret_value, ...) \
23 | do \
24 | { \
25 | if (!(a)) \
26 | { \
27 | ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
28 | ret = ret_value; \
29 | goto goto_tag; \
30 | } \
31 | } while (0)
32 |
33 | typedef struct {
34 | ir_builder_t parent;
35 | uint32_t buffer_size;
36 | uint32_t cursor;
37 | uint32_t pulse_duration_ticks;
38 | uint32_t flags;
39 | bool toggle;
40 | bool s2_bit;
41 | bool inverse;
42 | rmt_item32_t buffer[0];
43 | } rc5_builder_t;
44 |
45 | static esp_err_t rc5_builder_make_head(ir_builder_t *builder)
46 | {
47 | rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
48 | rc5_builder->cursor = 0;
49 | rc5_builder->toggle = !rc5_builder->toggle;
50 | // S1 default (not inverse) is 0
51 | rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->inverse;
52 | rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
53 | rc5_builder->buffer[rc5_builder->cursor].level1 = !rc5_builder->inverse;
54 | rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
55 | rc5_builder->cursor += 1;
56 | // S2 default (not inverse) is depend on whether use extended protocol
57 | rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->s2_bit ^ rc5_builder->inverse;
58 | rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
59 | rc5_builder->buffer[rc5_builder->cursor].level1 = !(rc5_builder->s2_bit ^ rc5_builder->inverse);
60 | rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
61 | rc5_builder->cursor += 1;
62 | // T
63 | rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->toggle;
64 | rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
65 | rc5_builder->buffer[rc5_builder->cursor].level1 = !rc5_builder->toggle;
66 | rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
67 | rc5_builder->cursor += 1;
68 | return ESP_OK;
69 | }
70 |
71 | static esp_err_t rc5_builder_make_logic0(ir_builder_t *builder)
72 | {
73 | rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
74 | rc5_builder->buffer[rc5_builder->cursor].level0 = !rc5_builder->inverse;
75 | rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
76 | rc5_builder->buffer[rc5_builder->cursor].level1 = rc5_builder->inverse;
77 | rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
78 | rc5_builder->cursor += 1;
79 | return ESP_OK;
80 | }
81 |
82 | static esp_err_t rc5_builder_make_logic1(ir_builder_t *builder)
83 | {
84 | rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
85 | rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->inverse;
86 | rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
87 | rc5_builder->buffer[rc5_builder->cursor].level1 = !rc5_builder->inverse;
88 | rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
89 | rc5_builder->cursor += 1;
90 | return ESP_OK;
91 | }
92 |
93 | static esp_err_t rc5_builder_make_end(ir_builder_t *builder)
94 | {
95 | rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
96 | rc5_builder->buffer[rc5_builder->cursor].val = 0;
97 | rc5_builder->cursor += 1;
98 | return ESP_OK;
99 | }
100 |
101 | static esp_err_t rc5_build_frame(ir_builder_t *builder, uint32_t address, uint32_t command)
102 | {
103 | rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
104 | if (rc5_builder->flags & IR_TOOLS_FLAGS_PROTO_EXT) {
105 | // RC5-extended protocol uses S2 bit as a 7th command bit (MSB of a command)
106 | if (command > 63) {
107 | rc5_builder->s2_bit = true;
108 | } else {
109 | rc5_builder->s2_bit = false;
110 | }
111 | }
112 | builder->make_head(builder);
113 | // MSB -> LSB
114 | for (int i = 4; i >= 0; i--) {
115 | if (address & (1 << i)) {
116 | builder->make_logic1(builder);
117 | } else {
118 | builder->make_logic0(builder);
119 | }
120 | }
121 | for (int i = 5; i >= 0; i--) {
122 | if (command & (1 << i)) {
123 | builder->make_logic1(builder);
124 | } else {
125 | builder->make_logic0(builder);
126 | }
127 | }
128 | builder->make_end(builder);
129 | return ESP_OK;
130 | }
131 |
132 | static esp_err_t rc5_build_repeat_frame(ir_builder_t *builder)
133 | {
134 | // repeat frame is just the latest build frame, so do nothing here
135 | return ESP_OK;
136 | }
137 |
138 | static esp_err_t rc5_builder_get_result(ir_builder_t *builder, void *result, uint32_t *length)
139 | {
140 | esp_err_t ret = ESP_OK;
141 | rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
142 | RC5_CHECK(result && length, "result and length can't be null", err, ESP_ERR_INVALID_ARG);
143 | *(rmt_item32_t **)result = rc5_builder->buffer;
144 | *length = rc5_builder->cursor;
145 | return ESP_OK;
146 | err:
147 | return ret;
148 | }
149 |
150 | static esp_err_t rc5_builder_del(ir_builder_t *builder)
151 | {
152 | rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
153 | free(rc5_builder);
154 | return ESP_OK;
155 | }
156 |
157 | ir_builder_t *ir_builder_rmt_new_rc5(const ir_builder_config_t *config)
158 | {
159 | ir_builder_t *ret = NULL;
160 | RC5_CHECK(config, "rc5 configuration can't be null", err, NULL);
161 | RC5_CHECK(config->buffer_size, "buffer size can't be zero", err, NULL);
162 |
163 | uint32_t builder_size = sizeof(rc5_builder_t) + config->buffer_size * sizeof(rmt_item32_t);
164 | rc5_builder_t *rc5_builder = calloc(1, builder_size);
165 | RC5_CHECK(rc5_builder, "request memory for rc5_builder failed", err, NULL);
166 |
167 | rc5_builder->buffer_size = config->buffer_size;
168 | rc5_builder->flags = config->flags;
169 | if (config->flags & IR_TOOLS_FLAGS_INVERSE) {
170 | rc5_builder->inverse = true;
171 | }
172 |
173 | uint32_t counter_clk_hz = 0;
174 | RC5_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
175 | "get rmt counter clock failed", err, NULL);
176 | float ratio = (float)counter_clk_hz / 1e6;
177 | rc5_builder->pulse_duration_ticks = (uint32_t)(ratio * RC5_PULSE_DURATION_US);
178 | rc5_builder->parent.make_head = rc5_builder_make_head;
179 | rc5_builder->parent.make_logic0 = rc5_builder_make_logic0;
180 | rc5_builder->parent.make_logic1 = rc5_builder_make_logic1;
181 | rc5_builder->parent.make_end = rc5_builder_make_end;
182 | rc5_builder->parent.build_frame = rc5_build_frame;
183 | rc5_builder->parent.build_repeat_frame = rc5_build_repeat_frame;
184 | rc5_builder->parent.get_result = rc5_builder_get_result;
185 | rc5_builder->parent.del = rc5_builder_del;
186 | rc5_builder->parent.repeat_period_ms = 114;
187 | return &rc5_builder->parent;
188 | err:
189 | return ret;
190 | }
191 |
--------------------------------------------------------------------------------
/components/infrared_tools/src/ir_parser_rmt_nec.c:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | #include
15 | #include
16 | #include "esp_log.h"
17 | #include "ir_tools.h"
18 | #include "ir_timings.h"
19 | #include "driver/rmt.h"
20 |
21 | static const char *TAG = "nec_parser";
22 | #define NEC_CHECK(a, str, goto_tag, ret_value, ...) \
23 | do \
24 | { \
25 | if (!(a)) \
26 | { \
27 | ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
28 | ret = ret_value; \
29 | goto goto_tag; \
30 | } \
31 | } while (0)
32 |
33 | #define NEC_DATA_FRAME_RMT_WORDS (34)
34 | #define NEC_REPEAT_FRAME_RMT_WORDS (2)
35 |
36 | typedef struct {
37 | ir_parser_t parent;
38 | uint32_t flags;
39 | uint32_t leading_code_high_ticks;
40 | uint32_t leading_code_low_ticks;
41 | uint32_t repeat_code_high_ticks;
42 | uint32_t repeat_code_low_ticks;
43 | uint32_t payload_logic0_high_ticks;
44 | uint32_t payload_logic0_low_ticks;
45 | uint32_t payload_logic1_high_ticks;
46 | uint32_t payload_logic1_low_ticks;
47 | uint32_t margin_ticks;
48 | rmt_item32_t *buffer;
49 | uint32_t cursor;
50 | uint32_t last_address;
51 | uint32_t last_command;
52 | bool repeat;
53 | bool inverse;
54 | } nec_parser_t;
55 |
56 | static inline bool nec_check_in_range(uint32_t raw_ticks, uint32_t target_ticks, uint32_t margin_ticks)
57 | {
58 | return (raw_ticks < (target_ticks + margin_ticks)) && (raw_ticks > (target_ticks - margin_ticks));
59 | }
60 |
61 | static bool nec_parse_head(nec_parser_t *nec_parser)
62 | {
63 | nec_parser->cursor = 0;
64 | rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
65 | bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
66 | nec_check_in_range(item.duration0, nec_parser->leading_code_high_ticks, nec_parser->margin_ticks) &&
67 | nec_check_in_range(item.duration1, nec_parser->leading_code_low_ticks, nec_parser->margin_ticks);
68 | nec_parser->cursor += 1;
69 | return ret;
70 | }
71 |
72 | static bool nec_parse_logic0(nec_parser_t *nec_parser)
73 | {
74 | rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
75 | bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
76 | nec_check_in_range(item.duration0, nec_parser->payload_logic0_high_ticks, nec_parser->margin_ticks) &&
77 | nec_check_in_range(item.duration1, nec_parser->payload_logic0_low_ticks, nec_parser->margin_ticks);
78 | return ret;
79 | }
80 |
81 | static bool nec_parse_logic1(nec_parser_t *nec_parser)
82 | {
83 | rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
84 | bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
85 | nec_check_in_range(item.duration0, nec_parser->payload_logic1_high_ticks, nec_parser->margin_ticks) &&
86 | nec_check_in_range(item.duration1, nec_parser->payload_logic1_low_ticks, nec_parser->margin_ticks);
87 | return ret;
88 | }
89 |
90 | static esp_err_t nec_parse_logic(ir_parser_t *parser, bool *logic)
91 | {
92 | esp_err_t ret = ESP_FAIL;
93 | bool logic_value = false;
94 | nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
95 | if (nec_parse_logic0(nec_parser)) {
96 | logic_value = false;
97 | ret = ESP_OK;
98 | } else if (nec_parse_logic1(nec_parser)) {
99 | logic_value = true;
100 | ret = ESP_OK;
101 | }
102 | if (ret == ESP_OK) {
103 | *logic = logic_value;
104 | }
105 | nec_parser->cursor += 1;
106 | return ret;
107 | }
108 |
109 | static bool nec_parse_repeat_frame(nec_parser_t *nec_parser)
110 | {
111 | nec_parser->cursor = 0;
112 | rmt_item32_t item = nec_parser->buffer[nec_parser->cursor];
113 | bool ret = (item.level0 == nec_parser->inverse) && (item.level1 != nec_parser->inverse) &&
114 | nec_check_in_range(item.duration0, nec_parser->repeat_code_high_ticks, nec_parser->margin_ticks) &&
115 | nec_check_in_range(item.duration1, nec_parser->repeat_code_low_ticks, nec_parser->margin_ticks);
116 | nec_parser->cursor += 1;
117 | return ret;
118 | }
119 |
120 | static esp_err_t nec_parser_input(ir_parser_t *parser, void *raw_data, uint32_t length)
121 | {
122 | esp_err_t ret = ESP_OK;
123 | nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
124 | NEC_CHECK(raw_data, "input data can't be null", err, ESP_ERR_INVALID_ARG);
125 | nec_parser->buffer = raw_data;
126 | // Data Frame costs 34 items and Repeat Frame costs 2 items
127 | if (length == NEC_DATA_FRAME_RMT_WORDS) {
128 | nec_parser->repeat = false;
129 | } else if (length == NEC_REPEAT_FRAME_RMT_WORDS) {
130 | nec_parser->repeat = true;
131 | } else {
132 | ret = ESP_FAIL;
133 | }
134 | return ret;
135 | err:
136 | return ret;
137 | }
138 |
139 | static esp_err_t nec_parser_get_scan_code(ir_parser_t *parser, uint32_t *address, uint32_t *command, bool *repeat)
140 | {
141 | esp_err_t ret = ESP_FAIL;
142 | uint32_t addr = 0;
143 | uint32_t cmd = 0;
144 | bool logic_value = false;
145 | nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
146 | NEC_CHECK(address && command && repeat, "address, command and repeat can't be null", out, ESP_ERR_INVALID_ARG);
147 | if (nec_parser->repeat) {
148 | if (nec_parse_repeat_frame(nec_parser)) {
149 | *address = nec_parser->last_address;
150 | *command = nec_parser->last_command;
151 | *repeat = true;
152 | ret = ESP_OK;
153 | }
154 | } else {
155 | if (nec_parse_head(nec_parser)) {
156 | for (int i = 0; i < 16; i++) {
157 | if (nec_parse_logic(parser, &logic_value) == ESP_OK) {
158 | addr |= (logic_value << i);
159 | }
160 | }
161 | for (int i = 0; i < 16; i++) {
162 | if (nec_parse_logic(parser, &logic_value) == ESP_OK) {
163 | cmd |= (logic_value << i);
164 | }
165 | }
166 | *address = addr;
167 | *command = cmd;
168 | *repeat = false;
169 | // keep it as potential repeat code
170 | nec_parser->last_address = addr;
171 | nec_parser->last_command = cmd;
172 | ret = ESP_OK;
173 | }
174 | }
175 | out:
176 | return ret;
177 | }
178 |
179 | static esp_err_t nec_parser_del(ir_parser_t *parser)
180 | {
181 | nec_parser_t *nec_parser = __containerof(parser, nec_parser_t, parent);
182 | free(nec_parser);
183 | return ESP_OK;
184 | }
185 |
186 | ir_parser_t *ir_parser_rmt_new_nec(const ir_parser_config_t *config)
187 | {
188 | ir_parser_t *ret = NULL;
189 | NEC_CHECK(config, "nec configuration can't be null", err, NULL);
190 |
191 | nec_parser_t *nec_parser = calloc(1, sizeof(nec_parser_t));
192 | NEC_CHECK(nec_parser, "request memory for nec_parser failed", err, NULL);
193 |
194 | nec_parser->flags = config->flags;
195 | if (config->flags & IR_TOOLS_FLAGS_INVERSE) {
196 | nec_parser->inverse = true;
197 | }
198 |
199 | uint32_t counter_clk_hz = 0;
200 | NEC_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
201 | "get rmt counter clock failed", err, NULL);
202 | float ratio = (float)counter_clk_hz / 1e6;
203 | nec_parser->leading_code_high_ticks = (uint32_t)(ratio * NEC_LEADING_CODE_HIGH_US);
204 | nec_parser->leading_code_low_ticks = (uint32_t)(ratio * NEC_LEADING_CODE_LOW_US);
205 | nec_parser->repeat_code_high_ticks = (uint32_t)(ratio * NEC_REPEAT_CODE_HIGH_US);
206 | nec_parser->repeat_code_low_ticks = (uint32_t)(ratio * NEC_REPEAT_CODE_LOW_US);
207 | nec_parser->payload_logic0_high_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ZERO_HIGH_US);
208 | nec_parser->payload_logic0_low_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ZERO_LOW_US);
209 | nec_parser->payload_logic1_high_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ONE_HIGH_US);
210 | nec_parser->payload_logic1_low_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ONE_LOW_US);
211 | nec_parser->margin_ticks = (uint32_t)(ratio * config->margin_us);
212 | nec_parser->parent.input = nec_parser_input;
213 | nec_parser->parent.get_scan_code = nec_parser_get_scan_code;
214 | nec_parser->parent.del = nec_parser_del;
215 | return &nec_parser->parent;
216 | err:
217 | return ret;
218 | }
219 |
--------------------------------------------------------------------------------
/components/infrared_tools/src/ir_parser_rmt_rc5.c:
--------------------------------------------------------------------------------
1 | // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 | #include
15 | #include
16 | #include "esp_log.h"
17 | #include "ir_tools.h"
18 | #include "ir_timings.h"
19 | #include "driver/rmt.h"
20 |
21 | static const char *TAG = "rc5_parser";
22 | #define RC5_CHECK(a, str, goto_tag, ret_value, ...) \
23 | do \
24 | { \
25 | if (!(a)) \
26 | { \
27 | ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
28 | ret = ret_value; \
29 | goto goto_tag; \
30 | } \
31 | } while (0)
32 |
33 | #define RC5_MAX_FRAME_RMT_WORDS (14) // S1+S2+T+ADDR(5)+CMD(6)
34 |
35 | typedef struct {
36 | ir_parser_t parent;
37 | uint32_t flags;
38 | uint32_t pulse_duration_ticks;
39 | uint32_t margin_ticks;
40 | rmt_item32_t *buffer;
41 | uint32_t buffer_len;
42 | uint32_t last_command;
43 | uint32_t last_address;
44 | bool last_t_bit;
45 | } rc5_parser_t;
46 |
47 | static inline bool rc5_check_in_range(uint32_t raw_ticks, uint32_t target_ticks, uint32_t margin_ticks)
48 | {
49 | return (raw_ticks < (target_ticks + margin_ticks)) && (raw_ticks > (target_ticks - margin_ticks));
50 | }
51 |
52 | static esp_err_t rc5_parser_input(ir_parser_t *parser, void *raw_data, uint32_t length)
53 | {
54 | esp_err_t ret = ESP_OK;
55 | rc5_parser_t *rc5_parser = __containerof(parser, rc5_parser_t, parent);
56 | rc5_parser->buffer = raw_data;
57 | rc5_parser->buffer_len = length;
58 | if (length > RC5_MAX_FRAME_RMT_WORDS) {
59 | ret = ESP_FAIL;
60 | }
61 | return ret;
62 | }
63 |
64 | static inline bool rc5_duration_one_unit(rc5_parser_t *rc5_parser, uint32_t duration)
65 | {
66 | return (duration < (rc5_parser->pulse_duration_ticks + rc5_parser->margin_ticks)) &&
67 | (duration > (rc5_parser->pulse_duration_ticks - rc5_parser->margin_ticks));
68 | }
69 |
70 | static inline bool rc5_duration_two_unit(rc5_parser_t *rc5_parser, uint32_t duration)
71 | {
72 | return (duration < (rc5_parser->pulse_duration_ticks * 2 + rc5_parser->margin_ticks)) &&
73 | (duration > (rc5_parser->pulse_duration_ticks * 2 - rc5_parser->margin_ticks));
74 | }
75 |
76 | static esp_err_t rc5_parser_get_scan_code(ir_parser_t *parser, uint32_t *address, uint32_t *command, bool *repeat)
77 | {
78 | esp_err_t ret = ESP_FAIL;
79 | uint32_t parse_result = 0; // 32 bit is enough to hold the parse result of one RC5 frame
80 | uint32_t addr = 0;
81 | uint32_t cmd = 0;
82 | bool s1 = true;
83 | bool s2 = true;
84 | bool t = false;
85 | bool exchange = false;
86 | rc5_parser_t *rc5_parser = __containerof(parser, rc5_parser_t, parent);
87 | RC5_CHECK(address && command && repeat, "address, command and repeat can't be null", out, ESP_ERR_INVALID_ARG);
88 | for (int i = 0; i < rc5_parser->buffer_len; i++) {
89 | if (rc5_duration_one_unit(rc5_parser, rc5_parser->buffer[i].duration0)) {
90 | parse_result <<= 1;
91 | parse_result |= exchange;
92 | if (rc5_duration_two_unit(rc5_parser, rc5_parser->buffer[i].duration1)) {
93 | exchange = !exchange;
94 | }
95 | } else if (rc5_duration_two_unit(rc5_parser, rc5_parser->buffer[i].duration0)) {
96 | parse_result <<= 1;
97 | parse_result |= rc5_parser->buffer[i].level0;
98 | parse_result <<= 1;
99 | parse_result |= !rc5_parser->buffer[i].level0;
100 | if (rc5_duration_one_unit(rc5_parser, rc5_parser->buffer[i].duration1)) {
101 | exchange = !exchange;
102 | }
103 | } else {
104 | goto out;
105 | }
106 | }
107 | if (!(rc5_parser->flags & IR_TOOLS_FLAGS_INVERSE)) {
108 | parse_result = ~parse_result;
109 | }
110 | s1 = ((parse_result & 0x2000) >> 13) & 0x01;
111 | s2 = ((parse_result & 0x1000) >> 12) & 0x01;
112 | t = ((parse_result & 0x800) >> 11) & 0x01;
113 | // Check S1, must be 1
114 | if (s1) {
115 | if (!(rc5_parser->flags & IR_TOOLS_FLAGS_PROTO_EXT) && !s2) {
116 | // Not standard RC5 protocol, but S2 is 0
117 | goto out;
118 | }
119 | addr = (parse_result & 0x7C0) >> 6;
120 | cmd = (parse_result & 0x3F);
121 | if (!s2) {
122 | cmd |= 1 << 6;
123 | }
124 | *repeat = (t == rc5_parser->last_t_bit && addr == rc5_parser->last_address && cmd == rc5_parser->last_command);
125 | *address = addr;
126 | *command = cmd;
127 | rc5_parser->last_address = addr;
128 | rc5_parser->last_command = cmd;
129 | rc5_parser->last_t_bit = t;
130 | ret = ESP_OK;
131 | }
132 | out:
133 | return ret;
134 | }
135 |
136 | static esp_err_t rc5_parser_del(ir_parser_t *parser)
137 | {
138 | rc5_parser_t *rc5_parser = __containerof(parser, rc5_parser_t, parent);
139 | free(rc5_parser);
140 | return ESP_OK;
141 | }
142 |
143 | ir_parser_t *ir_parser_rmt_new_rc5(const ir_parser_config_t *config)
144 | {
145 | ir_parser_t *ret = NULL;
146 | RC5_CHECK(config, "rc5 configuration can't be null", err, NULL);
147 |
148 | rc5_parser_t *rc5_parser = calloc(1, sizeof(rc5_parser_t));
149 | RC5_CHECK(rc5_parser, "request memory for rc5_parser failed", err, NULL);
150 |
151 | rc5_parser->flags = config->flags;
152 |
153 | uint32_t counter_clk_hz = 0;
154 | RC5_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
155 | "get rmt counter clock failed", err, NULL);
156 | float ratio = (float)counter_clk_hz / 1e6;
157 | rc5_parser->pulse_duration_ticks = (uint32_t)(ratio * RC5_PULSE_DURATION_US);
158 | rc5_parser->margin_ticks = (uint32_t)(ratio * config->margin_us);
159 | rc5_parser->parent.input = rc5_parser_input;
160 | rc5_parser->parent.get_scan_code = rc5_parser_get_scan_code;
161 | rc5_parser->parent.del = rc5_parser_del;
162 | return &rc5_parser->parent;
163 | err:
164 | return ret;
165 | }
166 |
--------------------------------------------------------------------------------
/docs/changed_src/app_wifi.c:
--------------------------------------------------------------------------------
1 | /*
2 | This example code is in the Public Domain (or CC0 licensed, at your option.)
3 |
4 | Unless required by applicable law or agreed to in writing, this
5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
6 | CONDITIONS OF ANY KIND, either express or implied.
7 | */
8 |
9 |
10 | /*
11 | * This supports 3 ways of connecting to the Wi-Fi Network
12 | * 1. Hard coded credentials
13 | * 2. Unified Provisioning
14 | * 3. Apple Wi-Fi Accessory Configuration (WAC. Available only on MFi variant of the SDK)
15 | *
16 | * Unified Provisioning has 2 options
17 | * 1. BLE Provisioning
18 | * 2. SoftAP Provisioning
19 | *
20 | * Unified and WAC Provisioning can co-exist.
21 | * If the SoftAP Unified Provisioning is being used, the same SoftAP interface
22 | * will be used for WAC. However, if BLE Unified Provisioning is used, the HAP
23 | * Helper functions for softAP start/stop will be used
24 | */
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0)
34 | // Features supported in 4.1+
35 | #define ESP_NETIF_SUPPORTED
36 | #endif
37 |
38 | #ifdef ESP_NETIF_SUPPORTED
39 | #include
40 | #else
41 | #include
42 | #endif
43 |
44 | #include
45 |
46 | #ifdef CONFIG_APP_WIFI_USE_BOTH_PROVISIONING
47 | #define USE_UNIFIED_PROVISIONING
48 | #define USE_WAC_PROVISIONING
49 | #endif
50 |
51 | #ifdef CONFIG_APP_WIFI_USE_UNIFIED_PROVISIONING
52 | #define USE_UNIFIED_PROVISIONING
53 | #endif
54 |
55 | #ifdef CONFIG_APP_WIFI_USE_WAC_PROVISIONING
56 | #define USE_WAC_PROVISIONING
57 | #endif
58 |
59 | #ifdef USE_UNIFIED_PROVISIONING
60 | #ifdef CONFIG_APP_WIFI_PROV_TRANSPORT_BLE
61 | #include
62 | #else /* CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP */
63 | #include
64 | #include
65 | #endif /* CONFIG_APP_WIFI_PROV_TRANSPORT_BLE */
66 | #include
67 | #endif /* USE_UNIFIED_PROVISIONING */
68 |
69 | #ifdef USE_WAC_PROVISIONING
70 | #include
71 | #endif /* USE_WAC_PROVISIONING */
72 |
73 | #include
74 | #include
75 | #include "app_wifi.h"
76 |
77 | static const char *TAG = "app_wifi";
78 | static const int WIFI_CONNECTED_EVENT = BIT0;
79 | static EventGroupHandle_t wifi_event_group;
80 |
81 |
82 | #ifdef USE_UNIFIED_PROVISIONING
83 | #define PROV_QR_VERSION "v1"
84 |
85 | #define PROV_TRANSPORT_SOFTAP "softap"
86 | #define PROV_TRANSPORT_BLE "ble"
87 | #define QRCODE_BASE_URL "https://espressif.github.io/esp-jumpstart/qrcode.html"
88 |
89 | #define CREDENTIALS_NAMESPACE "rmaker_creds"
90 | #define RANDOM_NVS_KEY "random"
91 |
92 | static void app_wifi_print_qr(const char *name, const char *pop, const char *transport)
93 | {
94 | if (!name || !pop || !transport) {
95 | ESP_LOGW(TAG, "Cannot generate QR code payload. Data missing.");
96 | return;
97 | }
98 | char payload[150];
99 | snprintf(payload, sizeof(payload), "{\"ver\":\"%s\",\"name\":\"%s\"" \
100 | ",\"pop\":\"%s\",\"transport\":\"%s\"}",
101 | PROV_QR_VERSION, name, pop, transport);
102 | #ifdef CONFIG_APP_WIFI_PROV_SHOW_QR
103 | ESP_LOGI(TAG, "-----QR Code for ESP Provisioning-----");
104 | ESP_LOGI(TAG, "Scan this QR code from the phone app for Provisioning.");
105 | qrcode_display(payload);
106 | #endif /* CONFIG_APP_WIFI_PROV_SHOW_QR */
107 | ESP_LOGI(TAG, "If QR code is not visible, copy paste the below URL in a browser.\n%s?data=%s", QRCODE_BASE_URL, payload);
108 | }
109 |
110 | static void get_device_service_name(char *service_name, size_t max)
111 | {
112 | uint8_t eth_mac[6];
113 | const char *ssid_prefix = "PROV_";
114 | esp_wifi_get_mac(WIFI_IF_STA, eth_mac);
115 | snprintf(service_name, max, "%s%02X%02X%02X",
116 | ssid_prefix, eth_mac[3], eth_mac[4], eth_mac[5]);
117 | }
118 |
119 | static esp_err_t get_device_pop(char *pop, size_t max)
120 | {
121 | if (!pop || !max) {
122 | return ESP_ERR_INVALID_ARG;
123 | }
124 |
125 | uint8_t eth_mac[6];
126 | esp_err_t err = esp_wifi_get_mac(WIFI_IF_STA, eth_mac);
127 | if (err == ESP_OK) {
128 | snprintf(pop, max, "%02x%02x%02x%02x", eth_mac[2], eth_mac[3], eth_mac[4], eth_mac[5]);
129 | return ESP_OK;
130 | } else {
131 | return err;
132 | }
133 | }
134 | #endif /* USE_UNIFIED_PROVISIONING */
135 |
136 | #ifdef USE_WAC_PROVISIONING
137 | #ifdef CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP
138 | static void app_wac_softap_start(char *ssid)
139 | {
140 | }
141 | #else
142 | static void app_wac_softap_start(char *ssid)
143 | {
144 | hap_wifi_softap_start(ssid);
145 | }
146 | #endif /* ! CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP */
147 | static void app_wac_softap_stop(void)
148 | {
149 | hap_wifi_softap_stop();
150 | }
151 | static void app_wac_sta_connect(wifi_config_t *wifi_cfg)
152 | {
153 | #ifdef USE_UNIFIED_PROVISIONING
154 | wifi_prov_mgr_configure_sta(wifi_cfg);
155 | #else
156 | hap_wifi_sta_connect(wifi_cfg);
157 | #endif
158 | }
159 | #endif /* USE_WAC_PROVISIONING */
160 |
161 | /* Event handler for catching system events */
162 | static void event_handler(void* arg, esp_event_base_t event_base,
163 | int event_id, void* event_data)
164 | {
165 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
166 | esp_wifi_connect();
167 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
168 | #ifdef ESP_NETIF_SUPPORTED
169 | esp_netif_create_ip6_linklocal((esp_netif_t *)arg);
170 | #else
171 | tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
172 | #endif
173 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
174 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
175 | ESP_LOGI(TAG, "Connected with IP Address:" IPSTR, IP2STR(&event->ip_info.ip));
176 | /* Signal main application to continue execution */
177 | xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_EVENT);
178 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) {
179 | ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data;
180 | ESP_LOGI(TAG, "Connected with IPv6 Address:" IPV6STR, IPV62STR(event->ip6_info.ip));
181 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
182 | ESP_LOGI(TAG, "Disconnected. Connecting to the AP again...");
183 | esp_wifi_connect();
184 | #ifdef USE_UNIFIED_PROVISIONING
185 | } else if (event_base == WIFI_PROV_EVENT) {
186 | switch (event_id) {
187 | case WIFI_PROV_START:
188 | ESP_LOGI(TAG, "Provisioning started");
189 | break;
190 | case WIFI_PROV_CRED_RECV: {
191 | wifi_sta_config_t *wifi_sta_cfg = (wifi_sta_config_t *)event_data;
192 | ESP_LOGI(TAG, "Received Wi-Fi credentials"
193 | "\n\tSSID : %s\n\tPassword : %s",
194 | (const char *) wifi_sta_cfg->ssid,
195 | (const char *) wifi_sta_cfg->password);
196 | break;
197 | }
198 | case WIFI_PROV_CRED_FAIL: {
199 | wifi_prov_sta_fail_reason_t *reason = (wifi_prov_sta_fail_reason_t *)event_data;
200 | ESP_LOGE(TAG, "Provisioning failed!\n\tReason : %s"
201 | "\n\tPlease reset to factory and retry provisioning",
202 | (*reason == WIFI_PROV_STA_AUTH_ERROR) ?
203 | "Wi-Fi station authentication failed" : "Wi-Fi access-point not found");
204 | break;
205 | }
206 | case WIFI_PROV_CRED_SUCCESS:
207 | ESP_LOGI(TAG, "Provisioning successful");
208 | break;
209 | case WIFI_PROV_END:
210 | #ifdef USE_WAC_PROVISIONING
211 | hap_wac_stop();
212 | #endif
213 | /* De-initialize manager once provisioning is finished */
214 | wifi_prov_mgr_deinit();
215 | break;
216 | default:
217 | break;
218 | }
219 | #endif /* USE_UNIFIED_PROVISIONING */
220 | #ifdef USE_WAC_PROVISIONING
221 | } else if (event_base == HAP_WAC_EVENT) {
222 | switch (event_id) {
223 | case HAP_WAC_EVENT_REQ_SOFTAP_START:
224 | app_wac_softap_start((char *)event_data);
225 | break;
226 | case HAP_WAC_EVENT_REQ_SOFTAP_STOP:
227 | app_wac_softap_stop();
228 | break;
229 | case HAP_WAC_EVENT_RECV_CRED:
230 | app_wac_sta_connect((wifi_config_t *)event_data);
231 | break;
232 | case HAP_WAC_EVENT_STOPPED:
233 | ESP_LOGI(TAG, "WAC Stopped");
234 | break;
235 | default:
236 | break;
237 | }
238 | #endif /* USE_WAC_PROVISIONING */
239 | }
240 | }
241 |
242 | void app_wifi_init(void)
243 | {
244 | /* Initialize TCP/IP */
245 | #ifdef ESP_NETIF_SUPPORTED
246 | esp_netif_init();
247 | #else
248 | tcpip_adapter_init();
249 | #endif
250 |
251 | /* Initialize the event loop */
252 | ESP_ERROR_CHECK(esp_event_loop_create_default());
253 | wifi_event_group = xEventGroupCreate();
254 |
255 | /* Initialize Wi-Fi including netif with default config */
256 | #ifdef ESP_NETIF_SUPPORTED
257 | esp_netif_t *wifi_netif = esp_netif_create_default_wifi_sta();
258 | #endif
259 |
260 | /* Register our event handler for Wi-Fi, IP and Provisioning related events */
261 | #ifdef ESP_NETIF_SUPPORTED
262 | ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, wifi_netif));
263 | #else
264 | ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
265 | #endif
266 | ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
267 | ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_GOT_IP6, &event_handler, NULL));
268 |
269 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
270 | ESP_ERROR_CHECK(esp_wifi_init(&cfg));
271 | }
272 |
273 | #ifdef CONFIG_APP_WIFI_USE_HARDCODED
274 | #define APP_WIFI_SSID CONFIG_APP_WIFI_SSID
275 | #define APP_WIFI_PASS CONFIG_APP_WIFI_PASSWORD
276 |
277 | #define DEFAULT_LISTEN_INTERVAL CONFIG_APP_WIFI_LISTEN_INTERVAL
278 |
279 | #if CONFIG_APP_POWER_SAVE_MIN_MODEM
280 | #define DEFAULT_PS_MODE WIFI_PS_MIN_MODEM
281 | #elif CONFIG_APP_POWER_SAVE_MAX_MODEM
282 | #define DEFAULT_PS_MODE WIFI_PS_MAX_MODEM
283 | #elif CONFIG_APP_POWER_SAVE_NONE
284 | #define DEFAULT_PS_MODE WIFI_PS_NONE
285 | #else
286 | #define DEFAULT_PS_MODE WIFI_PS_NONE
287 | #endif /*CONFIG_POWER_SAVE_MODEM*/
288 | esp_err_t app_wifi_start(TickType_t ticks_to_wait)
289 | {
290 | wifi_config_t wifi_config = {
291 | .sta = {
292 | .ssid = APP_WIFI_SSID,
293 | .password = APP_WIFI_PASS,
294 | #ifdef CONFIG_PM_ENABLE
295 | .listen_interval = DEFAULT_LISTEN_INTERVAL,
296 | #endif
297 | /* Setting a password implies station will connect to all security modes including WEP/WPA.
298 | * However these modes are deprecated and not advisable to be used. Incase your Access point
299 | * doesn't support WPA2, these mode can be enabled by commenting below line */
300 | .threshold.authmode = WIFI_AUTH_WPA2_PSK,
301 |
302 | .pmf_cfg = {
303 | .capable = true,
304 | .required = false
305 | },
306 | },
307 | };
308 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
309 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
310 | ESP_ERROR_CHECK(esp_wifi_start() );
311 | #ifdef CONFIG_PM_ENABLE
312 | ESP_LOGI(TAG, "esp_wifi_set_ps().");
313 | esp_wifi_set_ps(DEFAULT_PS_MODE);
314 | #endif
315 | /* Wait for Wi-Fi connection */
316 | xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_EVENT, false, true, ticks_to_wait);
317 | return ESP_OK;
318 | }
319 | #endif
320 |
321 | #ifdef CONFIG_APP_WIFI_USE_PROVISIONING
322 | static void wifi_init_sta()
323 | {
324 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
325 | ESP_ERROR_CHECK(esp_wifi_start());
326 | }
327 |
328 | esp_err_t app_wifi_start(TickType_t ticks_to_wait)
329 | {
330 | #ifdef USE_UNIFIED_PROVISIONING
331 | /* Configuration for the provisioning manager */
332 | wifi_prov_mgr_config_t config = {
333 | /* What is the Provisioning Scheme that we want ?
334 | * wifi_prov_scheme_softap or wifi_prov_scheme_ble */
335 | #ifdef CONFIG_APP_WIFI_PROV_TRANSPORT_BLE
336 | .scheme = wifi_prov_scheme_ble,
337 | #else /* CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP */
338 | .scheme = wifi_prov_scheme_softap,
339 | #endif /* CONFIG_APP_WIFI_PROV_TRANSPORT_BLE */
340 |
341 | /* Any default scheme specific event handler that you would
342 | * like to choose. Since our example application requires
343 | * neither BT nor BLE, we can choose to release the associated
344 | * memory once provisioning is complete, or not needed
345 | * (in case when device is already provisioned). Choosing
346 | * appropriate scheme specific event handler allows the manager
347 | * to take care of this automatically. This can be set to
348 | * WIFI_PROV_EVENT_HANDLER_NONE when using wifi_prov_scheme_softap*/
349 | #ifdef CONFIG_APP_WIFI_PROV_TRANSPORT_BLE
350 | .scheme_event_handler = WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
351 | #else /* CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP */
352 | .scheme_event_handler = WIFI_PROV_EVENT_HANDLER_NONE,
353 | #endif /* CONFIG_APP_WIFI_PROV_TRANSPORT_BLE */
354 | };
355 |
356 | /* Initialize provisioning manager with the
357 | * configuration parameters set above */
358 | ESP_ERROR_CHECK(wifi_prov_mgr_init(config));
359 | #endif /* USE_UNIFIED_PROVISIONING */
360 |
361 | /* Let's find out if the device is provisioned */
362 | bool provisioned = false;
363 | #ifdef USE_UNIFIED_PROVISIONING
364 | ESP_ERROR_CHECK(wifi_prov_mgr_is_provisioned(&provisioned));
365 | #else
366 | ESP_ERROR_CHECK(hap_wifi_is_provisioned(&provisioned));
367 | #endif
368 |
369 | /* If device is not yet provisioned start provisioning service */
370 | if (!provisioned) {
371 | ESP_LOGI(TAG, "Starting provisioning");
372 | #ifdef ESP_NETIF_SUPPORTED
373 | #if defined(CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP) || defined(USE_WAC_PROVISIONING)
374 | esp_netif_create_default_wifi_ap();
375 | #endif
376 | #endif /* ESP_NETIF_SUPPORTED */
377 | #ifdef USE_UNIFIED_PROVISIONING
378 | esp_event_handler_register(WIFI_PROV_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL);
379 | /* What is the Device Service Name that we want
380 | * This translates to :
381 | * - Wi-Fi SSID when scheme is wifi_prov_scheme_softap
382 | * - device name when scheme is wifi_prov_scheme_ble
383 | */
384 | char service_name[12];
385 | get_device_service_name(service_name, sizeof(service_name));
386 |
387 | /* What is the security level that we want (0 or 1):
388 | * - WIFI_PROV_SECURITY_0 is simply plain text communication.
389 | * - WIFI_PROV_SECURITY_1 is secure communication which consists of secure handshake
390 | * using X25519 key exchange and proof of possession (pop) and AES-CTR
391 | * for encryption/decryption of messages.
392 | */
393 | wifi_prov_security_t security = WIFI_PROV_SECURITY_1;
394 |
395 | /* Do we want a proof-of-possession (ignored if Security 0 is selected):
396 | * - this should be a string with length > 0
397 | * - NULL if not used
398 | */
399 | char pop[9];
400 | esp_err_t err = get_device_pop(pop, sizeof(pop));
401 | if (err != ESP_OK) {
402 | ESP_LOGE(TAG, "Error: %d. Failed to get PoP from NVS, Please perform Claiming.", err);
403 | return err;
404 | }
405 |
406 | /* What is the service key (Wi-Fi password)
407 | * NULL = Open network
408 | * This is ignored when scheme is wifi_prov_scheme_ble
409 | */
410 | const char *service_key = NULL;
411 |
412 | #ifdef CONFIG_APP_WIFI_PROV_TRANSPORT_BLE
413 | /* This step is only useful when scheme is wifi_prov_scheme_ble. This will
414 | * set a custom 128 bit UUID which will be included in the BLE advertisement
415 | * and will correspond to the primary GATT service that provides provisioning
416 | * endpoints as GATT characteristics. Each GATT characteristic will be
417 | * formed using the primary service UUID as base, with different auto assigned
418 | * 12th and 13th bytes (assume counting starts from 0th byte). The client side
419 | * applications must identify the endpoints by reading the User Characteristic
420 | * Description descriptor (0x2901) for each characteristic, which contains the
421 | * endpoint name of the characteristic */
422 | uint8_t custom_service_uuid[] = {
423 | /* This is a random uuid. This can be modified if you want to change the BLE uuid. */
424 | /* 12th and 13th bit will be replaced by internal bits. */
425 | 0xb4, 0xdf, 0x5a, 0x1c, 0x3f, 0x6b, 0xf4, 0xbf,
426 | 0xea, 0x4a, 0x82, 0x03, 0x04, 0x90, 0x1a, 0x02,
427 | };
428 | err = wifi_prov_scheme_ble_set_service_uuid(custom_service_uuid);
429 | if (err != ESP_OK) {
430 | ESP_LOGE(TAG, "wifi_prov_scheme_ble_set_service_uuid failed %d", err);
431 | return err;
432 | }
433 | #endif /* CONFIG_APP_WIFI_PROV_TRANSPORT_BLE */
434 | #ifdef CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP
435 | wifi_prov_scheme_softap_set_httpd_handle(hap_platform_httpd_get_handle());
436 | #endif /* CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP */
437 |
438 | /* Start provisioning service */
439 | ESP_ERROR_CHECK(wifi_prov_mgr_start_provisioning(security, pop, service_name, service_key));
440 | /* Print QR code for provisioning */
441 | #ifdef CONFIG_APP_WIFI_PROV_TRANSPORT_BLE
442 | app_wifi_print_qr(service_name, pop, PROV_TRANSPORT_BLE);
443 | #else /* CONFIG_APP_WIFI_PROV_TRANSPORT_SOFTAP */
444 | app_wifi_print_qr(service_name, pop, PROV_TRANSPORT_SOFTAP);
445 | #endif /* CONFIG_APP_WIFI_PROV_TRANSPORT_BLE */
446 | ESP_LOGI(TAG, "Provisioning Started. Name : %s, POP : %s", service_name, pop);
447 | #endif /* USE_UNIFIED_PROVISIONING */
448 | #ifdef USE_WAC_PROVISIONING
449 | esp_event_handler_register(HAP_WAC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL);
450 | hap_wac_start();
451 | #endif /* USE_WAC_PROVISIONING */
452 | } else {
453 | ESP_LOGI(TAG, "Already provisioned, starting Wi-Fi STA");
454 | #ifdef USE_UNIFIED_PROVISIONING
455 | /* We don't need the manager as device is already provisioned,
456 | * so let's release it's resources */
457 | wifi_prov_mgr_deinit();
458 | #endif /* USE_UNIFIED_PROVISIONING */
459 | /* Start Wi-Fi station */
460 | wifi_init_sta();
461 | }
462 | /* Wait for Wi-Fi connection */
463 | xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_EVENT, false, true, ticks_to_wait);
464 | return ESP_OK;
465 | }
466 | #endif /* CONFIG_APP_WIFI_USE_PROVISIONING */
467 |
--------------------------------------------------------------------------------
/docs/images/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/0.png
--------------------------------------------------------------------------------
/docs/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/1.png
--------------------------------------------------------------------------------
/docs/images/10.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/10.PNG
--------------------------------------------------------------------------------
/docs/images/11.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/11.PNG
--------------------------------------------------------------------------------
/docs/images/12.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/12.PNG
--------------------------------------------------------------------------------
/docs/images/13.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/13.PNG
--------------------------------------------------------------------------------
/docs/images/14.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/14.PNG
--------------------------------------------------------------------------------
/docs/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/2.png
--------------------------------------------------------------------------------
/docs/images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/3.png
--------------------------------------------------------------------------------
/docs/images/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/4.png
--------------------------------------------------------------------------------
/docs/images/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/5.png
--------------------------------------------------------------------------------
/docs/images/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/6.png
--------------------------------------------------------------------------------
/docs/images/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/7.png
--------------------------------------------------------------------------------
/docs/images/8.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/8.PNG
--------------------------------------------------------------------------------
/docs/images/9.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/9.PNG
--------------------------------------------------------------------------------
/docs/images/H0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/images/H0.png
--------------------------------------------------------------------------------
/docs/pdf/DW07D.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/pdf/DW07D.pdf
--------------------------------------------------------------------------------
/docs/pdf/ESP32_AC_Controller.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/pdf/ESP32_AC_Controller.pdf
--------------------------------------------------------------------------------
/docs/pdf/HAP-Specification-Non-Commercial-Version.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/pdf/HAP-Specification-Non-Commercial-Version.pdf
--------------------------------------------------------------------------------
/docs/pdf/Stand-Alone Linear Li-Ion Li-Polymer Charge Management Controller Data Sheet.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/pdf/Stand-Alone Linear Li-Ion Li-Polymer Charge Management Controller Data Sheet.pdf
--------------------------------------------------------------------------------
/docs/pdf/tps63000.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/pdf/tps63000.pdf
--------------------------------------------------------------------------------
/docs/word/R05d电控功能说明书.doc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/word/R05d电控功能说明书.doc
--------------------------------------------------------------------------------
/docs/原理图PCB/ESP32_AC_Controller.PcbDoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/原理图PCB/ESP32_AC_Controller.PcbDoc
--------------------------------------------------------------------------------
/docs/原理图PCB/ESP32_AC_Controller.PrjPcb:
--------------------------------------------------------------------------------
1 | [Design]
2 | Version=1.0
3 | HierarchyMode=0
4 | ChannelRoomNamingStyle=0
5 | ReleasesFolder=
6 | ChannelDesignatorFormatString=$Component_$RoomName
7 | ChannelRoomLevelSeperator=_
8 | OpenOutputs=1
9 | ArchiveProject=0
10 | TimestampOutput=0
11 | SeparateFolders=0
12 | TemplateLocationPath=
13 | PinSwapBy_Netlabel=1
14 | PinSwapBy_Pin=1
15 | AllowPortNetNames=0
16 | AllowSheetEntryNetNames=1
17 | AppendSheetNumberToLocalNets=0
18 | NetlistSinglePinNets=0
19 | DefaultConfiguration=Sources
20 | UserID=0xFFFFFFFF
21 | DefaultPcbProtel=1
22 | DefaultPcbPcad=0
23 | ReorderDocumentsOnCompile=1
24 | NameNetsHierarchically=0
25 | PowerPortNamesTakePriority=0
26 | AutoSheetNumbering=1
27 | AutoCrossReferences=0
28 | PushECOToAnnotationFile=1
29 | DItemRevisionGUID=
30 | ReportSuppressedErrorsInMessages=0
31 | FSMCodingStyle=eFMSDropDownList_OneProcess
32 | FSMEncodingStyle=eFMSDropDownList_OneHot
33 | IsProjectConflictPreventionWarningsEnabled=0
34 | OutputPath=
35 | LogFolderPath=
36 | ManagedProjectGUID=
37 | IncludeDesignInRelease=0
38 |
39 | [Preferences]
40 | PrefsVaultGUID=
41 | PrefsRevisionGUID=
42 |
43 | [Document1]
44 | DocumentPath=ESP32_AC_Controller.SchDoc
45 | AnnotationEnabled=1
46 | AnnotateStartValue=1
47 | AnnotationIndexControlEnabled=0
48 | AnnotateSuffix=
49 | AnnotateScope=All
50 | AnnotateOrder=0
51 | DoLibraryUpdate=1
52 | DoDatabaseUpdate=1
53 | ClassGenCCAutoEnabled=1
54 | ClassGenCCAutoRoomEnabled=1
55 | ClassGenNCAutoScope=None
56 | DItemRevisionGUID=
57 | GenerateClassCluster=0
58 | DocumentUniqueId=QPMOFIKF
59 |
60 | [Document2]
61 | DocumentPath=ESP32_AC_Controller.PcbDoc
62 | AnnotationEnabled=1
63 | AnnotateStartValue=1
64 | AnnotationIndexControlEnabled=0
65 | AnnotateSuffix=
66 | AnnotateScope=All
67 | AnnotateOrder=-1
68 | DoLibraryUpdate=1
69 | DoDatabaseUpdate=1
70 | ClassGenCCAutoEnabled=1
71 | ClassGenCCAutoRoomEnabled=1
72 | ClassGenNCAutoScope=None
73 | DItemRevisionGUID=
74 | GenerateClassCluster=0
75 | DocumentUniqueId=ICRNADTA
76 |
77 | [GeneratedDocument1]
78 | DocumentPath=Project Outputs for ESP32_AC_Controller\Design Rule Check - ESP32_AC_Controller.html
79 | DItemRevisionGUID=
80 |
81 | [Configuration1]
82 | Name=Sources
83 | ParameterCount=0
84 | ConstraintFileCount=0
85 | ReleaseItemId=
86 | Variant=[No Variations]
87 | OutputJobsCount=0
88 | ContentTypeGUID=CB6F2064-E317-11DF-B822-12313F0024A2
89 | ConfigurationType=Source
90 |
91 | [Generic_SmartPDF]
92 | AutoOpenFile=0
93 | AutoOpenOutJob=-1
94 |
95 | [Generic_SmartPDFSettings]
96 | ProjectMode=-1
97 | ZoomPrecision=50
98 | AddNetsInformation=-1
99 | AddNetPins=-1
100 | AddNetNetLabels=-1
101 | AddNetPorts=-1
102 | ShowComponentParameters=-1
103 | GlobalBookmarks=0
104 | ExportBOM=0
105 | TemplateFilename=
106 | TemplateStoreRelative=-1
107 | PCB_PrintColor=0
108 | SCH_PrintColor=0
109 | PrintQuality=-3
110 | SCH_ShowNoErc=-1
111 | SCH_ShowParameter=-1
112 | SCH_ShowProbes=-1
113 | SCH_ShowBlankets=-1
114 | SCH_NoERCSymbolsToShow="Thin Cross","Thick Cross","Small Cross",Checkbox,Triangle
115 | SCH_ShowNote=-1
116 | SCH_ShowNoteCollapsed=-1
117 | SCH_ExpandLogicalToPhysical=-1
118 | SCH_VariantName=[No Variations]
119 | SCH_ExpandComponentDesignators=-1
120 | SCH_ExpandNetlabels=0
121 | SCH_ExpandPorts=0
122 | SCH_ExpandSheetNumber=0
123 | SCH_ExpandDocumentNumber=0
124 | SCH_HasExpandLogicalToPhysicalSheets=-1
125 | SaveSettingsToOutJob=0
126 |
127 | [Generic_EDE]
128 | OutputDir=
129 |
130 | [OutputGroup1]
131 | Name=Netlist Outputs
132 | Description=
133 | TargetPrinter=Microsoft Print to PDF
134 | PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
135 | OutputType1=CadnetixNetlist
136 | OutputName1=Cadnetix Netlist
137 | OutputDocumentPath1=
138 | OutputVariantName1=
139 | OutputDefault1=0
140 | OutputType2=CalayNetlist
141 | OutputName2=Calay Netlist
142 | OutputDocumentPath2=
143 | OutputVariantName2=
144 | OutputDefault2=0
145 | OutputType3=EDIF
146 | OutputName3=EDIF for PCB
147 | OutputDocumentPath3=
148 | OutputVariantName3=
149 | OutputDefault3=0
150 | OutputType4=EESofNetlist
151 | OutputName4=EESof Netlist
152 | OutputDocumentPath4=
153 | OutputVariantName4=
154 | OutputDefault4=0
155 | OutputType5=IntergraphNetlist
156 | OutputName5=Intergraph Netlist
157 | OutputDocumentPath5=
158 | OutputVariantName5=
159 | OutputDefault5=0
160 | OutputType6=MentorBoardStationNetlist
161 | OutputName6=Mentor BoardStation Netlist
162 | OutputDocumentPath6=
163 | OutputVariantName6=
164 | OutputDefault6=0
165 | OutputType7=MultiWire
166 | OutputName7=MultiWire
167 | OutputDocumentPath7=
168 | OutputVariantName7=
169 | OutputDefault7=0
170 | OutputType8=OrCadPCB2Netlist
171 | OutputName8=Orcad/PCB2 Netlist
172 | OutputDocumentPath8=
173 | OutputVariantName8=
174 | OutputDefault8=0
175 | OutputType9=PADSNetlist
176 | OutputName9=PADS ASCII Netlist
177 | OutputDocumentPath9=
178 | OutputVariantName9=
179 | OutputDefault9=0
180 | OutputType10=Pcad
181 | OutputName10=Pcad for PCB
182 | OutputDocumentPath10=
183 | OutputVariantName10=
184 | OutputDefault10=0
185 | OutputType11=PCADNetlist
186 | OutputName11=PCAD Netlist
187 | OutputDocumentPath11=
188 | OutputVariantName11=
189 | OutputDefault11=0
190 | OutputType12=PCADnltNetlist
191 | OutputName12=PCADnlt Netlist
192 | OutputDocumentPath12=
193 | OutputVariantName12=
194 | OutputDefault12=0
195 | OutputType13=Protel2Netlist
196 | OutputName13=Protel2 Netlist
197 | OutputDocumentPath13=
198 | OutputVariantName13=
199 | OutputDefault13=0
200 | OutputType14=ProtelNetlist
201 | OutputName14=Protel
202 | OutputDocumentPath14=
203 | OutputVariantName14=
204 | OutputDefault14=0
205 | OutputType15=RacalNetlist
206 | OutputName15=Racal Netlist
207 | OutputDocumentPath15=
208 | OutputVariantName15=
209 | OutputDefault15=0
210 | OutputType16=RINFNetlist
211 | OutputName16=RINF Netlist
212 | OutputDocumentPath16=
213 | OutputVariantName16=
214 | OutputDefault16=0
215 | OutputType17=SciCardsNetlist
216 | OutputName17=SciCards Netlist
217 | OutputDocumentPath17=
218 | OutputVariantName17=
219 | OutputDefault17=0
220 | OutputType18=TangoNetlist
221 | OutputName18=Tango Netlist
222 | OutputDocumentPath18=
223 | OutputVariantName18=
224 | OutputDefault18=0
225 | OutputType19=TelesisNetlist
226 | OutputName19=Telesis Netlist
227 | OutputDocumentPath19=
228 | OutputVariantName19=
229 | OutputDefault19=0
230 | OutputType20=WireListNetlist
231 | OutputName20=WireList Netlist
232 | OutputDocumentPath20=
233 | OutputVariantName20=
234 | OutputDefault20=0
235 |
236 | [OutputGroup2]
237 | Name=Simulator Outputs
238 | Description=
239 | TargetPrinter=Microsoft Print to PDF
240 | PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
241 |
242 | [OutputGroup3]
243 | Name=Documentation Outputs
244 | Description=
245 | TargetPrinter=Virtual Printer
246 | PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
247 | OutputType1=Composite
248 | OutputName1=Composite Drawing
249 | OutputDocumentPath1=
250 | OutputVariantName1=
251 | OutputDefault1=0
252 | PageOptions1=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=2.91|XCorrection=1.00|YCorrection=1.00|PrintKind=0|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
253 | Configuration1_Name1=OutputConfigurationParameter1
254 | Configuration1_Item1=DesignatorDisplayMode=Physical|PrintArea=DesignExtent|PrintAreaLowerLeftCornerX=0|PrintAreaLowerLeftCornerY=0|PrintAreaUpperRightCornerX=0|PrintAreaUpperRightCornerY=0|Record=PcbPrintView|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
255 | Configuration1_Name2=OutputConfigurationParameter2
256 | Configuration1_Item2=IncludeBoardCutouts=False|IncludeBottomLayerComponents=True|IncludeMultiLayerComponents=True|IncludeTopLayerComponents=True|IncludeViewports=True|Index=0|Mirror=False|Name=Multilayer Composite Print|PadNumberFontSize=14|Record=PcbPrintOut|ShowHoles=False|ShowPadNets=False|ShowPadNumbers=False|SubstituteFonts=False|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
257 | Configuration1_Name3=OutputConfigurationParameter3
258 | Configuration1_Item3=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=TopOverlay|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
259 | Configuration1_Name4=OutputConfigurationParameter4
260 | Configuration1_Item4=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=TopLayer|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
261 | Configuration1_Name5=OutputConfigurationParameter5
262 | Configuration1_Item5=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=BottomLayer|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
263 | Configuration1_Name6=OutputConfigurationParameter6
264 | Configuration1_Item6=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=MultiLayer|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
265 | Configuration1_Name7=OutputConfigurationParameter7
266 | Configuration1_Item7=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=BottomOverlay|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
267 | Configuration1_Name8=OutputConfigurationParameter8
268 | Configuration1_Item8=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=KeepOutLayer|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
269 | Configuration1_Name9=OutputConfigurationParameter9
270 | Configuration1_Item9=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical1|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
271 | Configuration1_Name10=OutputConfigurationParameter10
272 | Configuration1_Item10=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical2|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
273 | Configuration1_Name11=OutputConfigurationParameter11
274 | Configuration1_Item11=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical4|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
275 | Configuration1_Name12=OutputConfigurationParameter12
276 | Configuration1_Item12=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical6|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
277 | Configuration1_Name13=OutputConfigurationParameter13
278 | Configuration1_Item13=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical8|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
279 | Configuration1_Name14=OutputConfigurationParameter14
280 | Configuration1_Item14=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical13|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
281 | Configuration1_Name15=OutputConfigurationParameter15
282 | Configuration1_Item15=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical15|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
283 | OutputType2=PCB 3D Print
284 | OutputName2=PCB 3D Print
285 | OutputDocumentPath2=
286 | OutputVariantName2=[No Variations]
287 | OutputDefault2=0
288 | PageOptions2=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
289 | OutputType3=PCB 3D Video
290 | OutputName3=PCB 3D Video
291 | OutputDocumentPath3=
292 | OutputVariantName3=[No Variations]
293 | OutputDefault3=0
294 | PageOptions3=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
295 | OutputType4=PCB Print
296 | OutputName4=PCB Prints
297 | OutputDocumentPath4=
298 | OutputVariantName4=
299 | OutputDefault4=0
300 | PageOptions4=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
301 | OutputType5=PCBDrawing
302 | OutputName5=Draftsman
303 | OutputDocumentPath5=
304 | OutputVariantName5=[No Variations]
305 | OutputDefault5=0
306 | PageOptions5=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
307 | OutputType6=PCBLIB Print
308 | OutputName6=PCBLIB Prints
309 | OutputDocumentPath6=
310 | OutputVariantName6=
311 | OutputDefault6=0
312 | PageOptions6=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
313 | OutputType7=PDF3D
314 | OutputName7=PDF3D
315 | OutputDocumentPath7=
316 | OutputVariantName7=[No Variations]
317 | OutputDefault7=0
318 | PageOptions7=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
319 | OutputType8=PDF3D MBA
320 | OutputName8=PDF3D MBA
321 | OutputDocumentPath8=
322 | OutputVariantName8=
323 | OutputDefault8=0
324 | PageOptions8=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
325 | OutputType9=Report Print
326 | OutputName9=Report Prints
327 | OutputDocumentPath9=
328 | OutputVariantName9=
329 | OutputDefault9=0
330 | PageOptions9=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
331 | OutputType10=Schematic Print
332 | OutputName10=Schematic Prints
333 | OutputDocumentPath10=
334 | OutputVariantName10=
335 | OutputDefault10=0
336 | PageOptions10=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
337 | OutputType11=SimView Print
338 | OutputName11=SimView Prints
339 | OutputDocumentPath11=
340 | OutputVariantName11=
341 | OutputDefault11=0
342 | PageOptions11=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
343 |
344 | [OutputGroup4]
345 | Name=Assembly Outputs
346 | Description=
347 | TargetPrinter=Microsoft Print to PDF
348 | PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
349 | OutputType1=Assembly
350 | OutputName1=Assembly Drawings
351 | OutputDocumentPath1=
352 | OutputVariantName1=[No Variations]
353 | OutputDefault1=0
354 | PageOptions1=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
355 | OutputType2=Pick Place
356 | OutputName2=Generates pick and place files
357 | OutputDocumentPath2=
358 | OutputVariantName2=[No Variations]
359 | OutputDefault2=0
360 | OutputType3=Test Points For Assembly
361 | OutputName3=Test Point Report
362 | OutputDocumentPath3=
363 | OutputVariantName3=[No Variations]
364 | OutputDefault3=0
365 |
366 | [OutputGroup5]
367 | Name=Fabrication Outputs
368 | Description=
369 | TargetPrinter=Microsoft Print to PDF
370 | PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
371 | OutputType1=Board Stack Report
372 | OutputName1=Report Board Stack
373 | OutputDocumentPath1=
374 | OutputVariantName1=
375 | OutputDefault1=0
376 | PageOptions1=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
377 | OutputType2=CompositeDrill
378 | OutputName2=Composite Drill Drawing
379 | OutputDocumentPath2=
380 | OutputVariantName2=
381 | OutputDefault2=0
382 | PageOptions2=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
383 | OutputType3=Drill
384 | OutputName3=Drill Drawing/Guides
385 | OutputDocumentPath3=
386 | OutputVariantName3=
387 | OutputDefault3=0
388 | PageOptions3=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
389 | OutputType4=Final
390 | OutputName4=Final Artwork Prints
391 | OutputDocumentPath4=
392 | OutputVariantName4=[No Variations]
393 | OutputDefault4=0
394 | PageOptions4=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
395 | OutputType5=Gerber
396 | OutputName5=Gerber Files
397 | OutputDocumentPath5=
398 | OutputVariantName5=[No Variations]
399 | OutputDefault5=0
400 | OutputType6=Gerber X2
401 | OutputName6=Gerber X2 Files
402 | OutputDocumentPath6=
403 | OutputVariantName6=
404 | OutputDefault6=0
405 | OutputType7=IPC2581
406 | OutputName7=IPC-2581 Files
407 | OutputDocumentPath7=
408 | OutputVariantName7=
409 | OutputDefault7=0
410 | OutputType8=Mask
411 | OutputName8=Solder/Paste Mask Prints
412 | OutputDocumentPath8=
413 | OutputVariantName8=
414 | OutputDefault8=0
415 | PageOptions8=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
416 | OutputType9=NC Drill
417 | OutputName9=NC Drill Files
418 | OutputDocumentPath9=
419 | OutputVariantName9=
420 | OutputDefault9=0
421 | OutputType10=ODB
422 | OutputName10=ODB++ Files
423 | OutputDocumentPath10=
424 | OutputVariantName10=[No Variations]
425 | OutputDefault10=0
426 | OutputType11=Plane
427 | OutputName11=Power-Plane Prints
428 | OutputDocumentPath11=
429 | OutputVariantName11=
430 | OutputDefault11=0
431 | PageOptions11=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
432 | OutputType12=Test Points
433 | OutputName12=Test Point Report
434 | OutputDocumentPath12=
435 | OutputVariantName12=
436 | OutputDefault12=0
437 |
438 | [OutputGroup6]
439 | Name=Report Outputs
440 | Description=
441 | TargetPrinter=Microsoft Print to PDF
442 | PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
443 | OutputType1=BOM_PartType
444 | OutputName1=Bill of Materials
445 | OutputDocumentPath1=
446 | OutputVariantName1=[No Variations]
447 | OutputDefault1=0
448 | PageOptions1=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
449 | Configuration1_Name1=ColumnNameFormat
450 | Configuration1_Item1=CaptionAsName
451 | Configuration1_Name2=ForceUpdateSettings
452 | Configuration1_Item2=False
453 | Configuration1_Name3=General
454 | Configuration1_Item3=OpenExported=False|AddToProject=False|ReportBOMViolationsInMessages=False|ForceFit=False|NotFitted=False|Database=False|DatabasePriority=False|IncludePcbData=False|IncludeVaultData=False|IncludeCloudData=False|IncludeDocumentData=True|IncludeAlternatives=False|ShowExportOptions=True|TemplateFilename=|TemplateVaultGuid=|TemplateItemGuid=|TemplateRevisionGuid=|BatchMode=5|FormWidth=1200|FormHeight=710|SupplierProdQty=1|SupplierAutoQty=False|SupplierUseCachedPricing=False|SupplierCurrency=USD|SolutionsPerItem=1|SuppliersPerSolution=1|ViewType=0|UseDirectApi=False|BomSetName=
455 | Configuration1_Name4=GroupOrder
456 | Configuration1_Item4=Comment=True|Footprint=True
457 | Configuration1_Name5=SortOrder
458 | Configuration1_Item5=Designator=Up|Comment=Up|Footprint=Up
459 | Configuration1_Name6=VisibleOrder
460 | Configuration1_Item6=Comment=120|Description=120|Designator=120|Footprint=120|LibRef=120|Quantity=120
461 | OutputType2=BOM_ReportCompare
462 | OutputName2=BOM Compare
463 | OutputDocumentPath2=
464 | OutputVariantName2=[No Variations]
465 | OutputDefault2=0
466 | PageOptions2=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
467 | OutputType3=ComponentCrossReference
468 | OutputName3=Component Cross Reference Report
469 | OutputDocumentPath3=
470 | OutputVariantName3=[No Variations]
471 | OutputDefault3=0
472 | OutputType4=Export Comments
473 | OutputName4=Export Comments
474 | OutputDocumentPath4=
475 | OutputVariantName4=[No Variations]
476 | OutputDefault4=0
477 | PageOptions4=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
478 | OutputType5=ReportHierarchy
479 | OutputName5=Report Project Hierarchy
480 | OutputDocumentPath5=
481 | OutputVariantName5=[No Variations]
482 | OutputDefault5=0
483 | OutputType6=Script
484 | OutputName6=Script Output
485 | OutputDocumentPath6=
486 | OutputVariantName6=[No Variations]
487 | OutputDefault6=0
488 | OutputType7=SimpleBOM
489 | OutputName7=Simple BOM
490 | OutputDocumentPath7=
491 | OutputVariantName7=[No Variations]
492 | OutputDefault7=0
493 | OutputType8=SinglePinNetReporter
494 | OutputName8=Report Single Pin Nets
495 | OutputDocumentPath8=
496 | OutputVariantName8=[No Variations]
497 | OutputDefault8=0
498 |
499 | [OutputGroup7]
500 | Name=Other Outputs
501 | Description=
502 | TargetPrinter=Microsoft Print to PDF
503 | PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
504 | OutputType1=Text Print
505 | OutputName1=Text Print
506 | OutputDocumentPath1=
507 | OutputVariantName1=
508 | OutputDefault1=0
509 | PageOptions1=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
510 | OutputType2=Text Print
511 | OutputName2=Text Print
512 | OutputDocumentPath2=
513 | OutputVariantName2=
514 | OutputDefault2=0
515 | PageOptions2=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
516 | OutputType3=Text Print
517 | OutputName3=Text Print
518 | OutputDocumentPath3=
519 | OutputVariantName3=
520 | OutputDefault3=0
521 | PageOptions3=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
522 | OutputType4=Text Print
523 | OutputName4=Text Print
524 | OutputDocumentPath4=
525 | OutputVariantName4=
526 | OutputDefault4=0
527 | PageOptions4=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
528 | OutputType5=Text Print
529 | OutputName5=Text Print
530 | OutputDocumentPath5=
531 | OutputVariantName5=
532 | OutputDefault5=0
533 | PageOptions5=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
534 | OutputType6=Text Print
535 | OutputName6=Text Print
536 | OutputDocumentPath6=
537 | OutputVariantName6=
538 | OutputDefault6=0
539 | PageOptions6=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
540 | OutputType7=Text Print
541 | OutputName7=Text Print
542 | OutputDocumentPath7=
543 | OutputVariantName7=
544 | OutputDefault7=0
545 | PageOptions7=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
546 | OutputType8=Text Print
547 | OutputName8=Text Print
548 | OutputDocumentPath8=
549 | OutputVariantName8=
550 | OutputDefault8=0
551 | PageOptions8=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
552 | OutputType9=Text Print
553 | OutputName9=Text Print
554 | OutputDocumentPath9=
555 | OutputVariantName9=
556 | OutputDefault9=0
557 | PageOptions9=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
558 | OutputType10=Text Print
559 | OutputName10=Text Print
560 | OutputDocumentPath10=
561 | OutputVariantName10=
562 | OutputDefault10=0
563 | PageOptions10=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
564 | OutputType11=Text Print
565 | OutputName11=Text Print
566 | OutputDocumentPath11=
567 | OutputVariantName11=
568 | OutputDefault11=0
569 | PageOptions11=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
570 | OutputType12=Text Print
571 | OutputName12=Text Print
572 | OutputDocumentPath12=
573 | OutputVariantName12=
574 | OutputDefault12=0
575 | PageOptions12=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
576 | OutputType13=Text Print
577 | OutputName13=Text Print
578 | OutputDocumentPath13=
579 | OutputVariantName13=
580 | OutputDefault13=0
581 | PageOptions13=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
582 | OutputType14=Text Print
583 | OutputName14=Text Print
584 | OutputDocumentPath14=
585 | OutputVariantName14=
586 | OutputDefault14=0
587 | PageOptions14=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
588 | OutputType15=Text Print
589 | OutputName15=Text Print
590 | OutputDocumentPath15=
591 | OutputVariantName15=
592 | OutputDefault15=0
593 | PageOptions15=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
594 | OutputType16=Text Print
595 | OutputName16=Text Print
596 | OutputDocumentPath16=
597 | OutputVariantName16=
598 | OutputDefault16=0
599 | PageOptions16=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
600 | OutputType17=Text Print
601 | OutputName17=Text Print
602 | OutputDocumentPath17=
603 | OutputVariantName17=
604 | OutputDefault17=0
605 | PageOptions17=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
606 |
607 | [OutputGroup8]
608 | Name=Validation Outputs
609 | Description=
610 | TargetPrinter=Microsoft Print to PDF
611 | PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
612 | OutputType1=BOM_Violations
613 | OutputName1=BOM Checks Report
614 | OutputDocumentPath1=
615 | OutputVariantName1=
616 | OutputDefault1=0
617 | OutputType2=Component states check
618 | OutputName2=Server's components states check
619 | OutputDocumentPath2=
620 | OutputVariantName2=
621 | OutputDefault2=0
622 | OutputType3=Configuration compliance
623 | OutputName3=Environment configuration compliance check
624 | OutputDocumentPath3=
625 | OutputVariantName3=
626 | OutputDefault3=0
627 | OutputType4=Design Rules Check
628 | OutputName4=Design Rules Check
629 | OutputDocumentPath4=
630 | OutputVariantName4=
631 | OutputDefault4=0
632 | PageOptions4=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
633 | OutputType5=Differences Report
634 | OutputName5=Differences Report
635 | OutputDocumentPath5=
636 | OutputVariantName5=
637 | OutputDefault5=0
638 | PageOptions5=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
639 | OutputType6=Electrical Rules Check
640 | OutputName6=Electrical Rules Check
641 | OutputDocumentPath6=
642 | OutputVariantName6=
643 | OutputDefault6=0
644 | PageOptions6=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
645 | OutputType7=Footprint Comparison Report
646 | OutputName7=Footprint Comparison Report
647 | OutputDocumentPath7=
648 | OutputVariantName7=
649 | OutputDefault7=0
650 |
651 | [OutputGroup9]
652 | Name=Export Outputs
653 | Description=
654 | TargetPrinter=Microsoft Print to PDF
655 | PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
656 | OutputType1=AutoCAD dwg/dxf PCB
657 | OutputName1=AutoCAD dwg/dxf File PCB
658 | OutputDocumentPath1=
659 | OutputVariantName1=
660 | OutputDefault1=0
661 | OutputType2=AutoCAD dwg/dxf Schematic
662 | OutputName2=AutoCAD dwg/dxf File Schematic
663 | OutputDocumentPath2=
664 | OutputVariantName2=
665 | OutputDefault2=0
666 | OutputType3=ExportIDF
667 | OutputName3=Export IDF
668 | OutputDocumentPath3=
669 | OutputVariantName3=
670 | OutputDefault3=0
671 | OutputType4=ExportPARASOLID
672 | OutputName4=Export PARASOLID
673 | OutputDocumentPath4=
674 | OutputVariantName4=[No Variations]
675 | OutputDefault4=0
676 | OutputType5=ExportSTEP
677 | OutputName5=Export STEP
678 | OutputDocumentPath5=
679 | OutputVariantName5=[No Variations]
680 | OutputDefault5=0
681 | OutputType6=ExportVRML
682 | OutputName6=Export VRML
683 | OutputDocumentPath6=
684 | OutputVariantName6=[No Variations]
685 | OutputDefault6=0
686 | OutputType7=MBAExportPARASOLID
687 | OutputName7=Export PARASOLID
688 | OutputDocumentPath7=
689 | OutputVariantName7=
690 | OutputDefault7=0
691 | OutputType8=MBAExportSTEP
692 | OutputName8=Export STEP
693 | OutputDocumentPath8=
694 | OutputVariantName8=
695 | OutputDefault8=0
696 | OutputType9=Save As/Export PCB
697 | OutputName9=Save As/Export PCB
698 | OutputDocumentPath9=
699 | OutputVariantName9=
700 | OutputDefault9=0
701 | OutputType10=Save As/Export Schematic
702 | OutputName10=Save As/Export Schematic
703 | OutputDocumentPath10=
704 | OutputVariantName10=
705 | OutputDefault10=0
706 | OutputType11=Specctra Design PCB
707 | OutputName11=Specctra Design PCB
708 | OutputDocumentPath11=
709 | OutputVariantName11=
710 | OutputDefault11=0
711 |
712 | [OutputGroup10]
713 | Name=PostProcess Outputs
714 | Description=
715 | TargetPrinter=Microsoft Print to PDF
716 | PrinterOptions=Record=PrinterOptions|Copies=1|Duplex=1|TrueTypeOptions=3|Collate=1|PrintJobKind=1|PrintWhat=1
717 | OutputType1=Copy Files
718 | OutputName1=Copy Files
719 | OutputDocumentPath1=
720 | OutputVariantName1=
721 | OutputDefault1=0
722 |
723 | [Modification Levels]
724 | Type1=1
725 | Type2=1
726 | Type3=1
727 | Type4=1
728 | Type5=1
729 | Type6=1
730 | Type7=1
731 | Type8=1
732 | Type9=1
733 | Type10=1
734 | Type11=1
735 | Type12=1
736 | Type13=1
737 | Type14=1
738 | Type15=1
739 | Type16=1
740 | Type17=1
741 | Type18=1
742 | Type19=1
743 | Type20=1
744 | Type21=1
745 | Type22=1
746 | Type23=1
747 | Type24=1
748 | Type25=1
749 | Type26=1
750 | Type27=1
751 | Type28=1
752 | Type29=1
753 | Type30=1
754 | Type31=1
755 | Type32=1
756 | Type33=1
757 | Type34=1
758 | Type35=1
759 | Type36=1
760 | Type37=1
761 | Type38=1
762 | Type39=1
763 | Type40=1
764 | Type41=1
765 | Type42=1
766 | Type43=1
767 | Type44=1
768 | Type45=1
769 | Type46=1
770 | Type47=1
771 | Type48=1
772 | Type49=1
773 | Type50=1
774 | Type51=1
775 | Type52=1
776 | Type53=1
777 | Type54=1
778 | Type55=1
779 | Type56=1
780 | Type57=1
781 | Type58=1
782 | Type59=1
783 | Type60=1
784 | Type61=1
785 | Type62=1
786 | Type63=1
787 | Type64=1
788 | Type65=1
789 | Type66=1
790 | Type67=1
791 | Type68=1
792 | Type69=1
793 | Type70=1
794 | Type71=1
795 | Type72=1
796 | Type73=1
797 | Type74=1
798 | Type75=1
799 | Type76=1
800 | Type77=1
801 | Type78=1
802 | Type79=1
803 | Type80=1
804 | Type81=1
805 | Type82=1
806 | Type83=1
807 | Type84=1
808 | Type85=1
809 | Type86=1
810 | Type87=1
811 | Type88=1
812 | Type89=1
813 | Type90=1
814 | Type91=1
815 | Type92=1
816 | Type93=1
817 | Type94=1
818 | Type95=1
819 | Type96=1
820 | Type97=1
821 | Type98=1
822 | Type99=1
823 | Type100=1
824 | Type101=1
825 | Type102=1
826 | Type103=1
827 | Type104=1
828 | Type105=1
829 | Type106=1
830 | Type107=1
831 | Type108=1
832 | Type109=1
833 | Type110=1
834 | Type111=1
835 | Type112=1
836 | Type113=1
837 | Type114=1
838 | Type115=1
839 | Type116=1
840 | Type117=1
841 | Type118=1
842 | Type119=1
843 |
844 | [Difference Levels]
845 | Type1=1
846 | Type2=1
847 | Type3=1
848 | Type4=1
849 | Type5=1
850 | Type6=1
851 | Type7=1
852 | Type8=1
853 | Type9=1
854 | Type10=1
855 | Type11=1
856 | Type12=1
857 | Type13=1
858 | Type14=1
859 | Type15=1
860 | Type16=1
861 | Type17=1
862 | Type18=1
863 | Type19=1
864 | Type20=1
865 | Type21=1
866 | Type22=1
867 | Type23=1
868 | Type24=1
869 | Type25=1
870 | Type26=1
871 | Type27=1
872 | Type28=1
873 | Type29=1
874 | Type30=1
875 | Type31=1
876 | Type32=1
877 | Type33=1
878 | Type34=1
879 | Type35=1
880 | Type36=1
881 | Type37=1
882 | Type38=1
883 | Type39=1
884 | Type40=1
885 | Type41=1
886 | Type42=1
887 | Type43=1
888 | Type44=1
889 | Type45=1
890 | Type46=1
891 | Type47=1
892 | Type48=1
893 | Type49=1
894 | Type50=1
895 | Type51=1
896 | Type52=1
897 | Type53=1
898 | Type54=1
899 | Type55=1
900 | Type56=1
901 | Type57=1
902 | Type58=1
903 | Type59=1
904 | Type60=1
905 | Type61=1
906 | Type62=1
907 | Type63=1
908 | Type64=1
909 | Type65=1
910 | Type66=1
911 | Type67=1
912 | Type68=1
913 |
914 | [Electrical Rules Check]
915 | Type1=1
916 | Type2=1
917 | Type3=2
918 | Type4=1
919 | Type5=2
920 | Type6=2
921 | Type7=0
922 | Type8=1
923 | Type9=1
924 | Type10=1
925 | Type11=2
926 | Type12=2
927 | Type13=2
928 | Type14=1
929 | Type15=1
930 | Type16=1
931 | Type17=1
932 | Type18=1
933 | Type19=1
934 | Type20=0
935 | Type21=0
936 | Type22=0
937 | Type23=0
938 | Type24=1
939 | Type25=2
940 | Type26=0
941 | Type27=2
942 | Type28=1
943 | Type29=1
944 | Type30=1
945 | Type31=1
946 | Type32=2
947 | Type33=0
948 | Type34=2
949 | Type35=1
950 | Type36=2
951 | Type37=1
952 | Type38=2
953 | Type39=2
954 | Type40=2
955 | Type41=0
956 | Type42=2
957 | Type43=1
958 | Type44=0
959 | Type45=0
960 | Type46=0
961 | Type47=0
962 | Type48=0
963 | Type49=0
964 | Type50=2
965 | Type51=0
966 | Type52=0
967 | Type53=1
968 | Type54=1
969 | Type55=1
970 | Type56=2
971 | Type57=1
972 | Type58=1
973 | Type59=2
974 | Type60=0
975 | Type61=0
976 | Type62=0
977 | Type63=0
978 | Type64=0
979 | Type65=2
980 | Type66=3
981 | Type67=2
982 | Type68=2
983 | Type69=0
984 | Type70=2
985 | Type71=2
986 | Type72=2
987 | Type73=2
988 | Type74=1
989 | Type75=2
990 | Type76=1
991 | Type77=1
992 | Type78=1
993 | Type79=1
994 | Type80=2
995 | Type81=3
996 | Type82=3
997 | Type83=3
998 | Type84=3
999 | Type85=3
1000 | Type86=2
1001 | Type87=2
1002 | Type88=2
1003 | Type89=1
1004 | Type90=1
1005 | Type91=3
1006 | Type92=3
1007 | Type93=2
1008 | Type94=2
1009 | Type95=2
1010 | Type96=2
1011 | Type97=2
1012 | Type98=0
1013 | Type99=1
1014 | Type100=2
1015 | Type101=0
1016 | Type102=2
1017 | Type103=2
1018 | Type104=1
1019 | Type105=2
1020 | Type106=2
1021 | Type107=2
1022 | Type108=2
1023 | Type109=1
1024 | Type110=1
1025 | Type111=1
1026 | Type112=1
1027 | Type113=1
1028 | Type114=2
1029 | Type115=2
1030 | Type116=2
1031 | Type117=3
1032 | Type118=3
1033 | Type119=3
1034 | MultiChannelAlternate=2
1035 | AlternateItemFail=3
1036 | Type122=2
1037 | Type123=1
1038 | Type124=1
1039 | Type125=1
1040 |
1041 | [ERC Connection Matrix]
1042 | L1=NNNNNNNNNNNWNNNWW
1043 | L2=NNWNNNNWWWNWNWNWN
1044 | L3=NWEENEEEENEWNEEWN
1045 | L4=NNENNNWEENNWNENWN
1046 | L5=NNNNNNNNNNNNNNNNN
1047 | L6=NNENNNNEENNWNENWN
1048 | L7=NNEWNNWEENNWNENWN
1049 | L8=NWEENEENEEENNEENN
1050 | L9=NWEENEEEENEWNEEWW
1051 | L10=NWNNNNNENNEWNNEWN
1052 | L11=NNENNNNEEENWNENWN
1053 | L12=WWWWNWWNWWWNWWWNN
1054 | L13=NNNNNNNNNNNWNNNWW
1055 | L14=NWEENEEEENEWNEEWW
1056 | L15=NNENNNNEEENWNENWW
1057 | L16=WWWWNWWNWWWNWWWNW
1058 | L17=WNNNNNNNWNNNWWWWN
1059 |
1060 | [Annotate]
1061 | SortOrder=3
1062 | SortLocation=0
1063 | ReplaceSubparts=0
1064 | MatchParameter1=Comment
1065 | MatchStrictly1=1
1066 | MatchParameter2=Library Reference
1067 | MatchStrictly2=1
1068 | PhysicalNamingFormat=$Component_$RoomName
1069 | GlobalIndexSortOrder=3
1070 | GlobalIndexSortLocation=0
1071 |
1072 | [PrjClassGen]
1073 | CompClassManualEnabled=0
1074 | CompClassManualRoomEnabled=0
1075 | NetClassAutoBusEnabled=1
1076 | NetClassAutoCompEnabled=0
1077 | NetClassAutoNamedHarnessEnabled=0
1078 | NetClassManualEnabled=1
1079 | NetClassSeparateForBusSections=0
1080 |
1081 | [LibraryUpdateOptions]
1082 | SelectedOnly=0
1083 | UpdateVariants=1
1084 | UpdateToLatestRevision=1
1085 | PartTypes=0
1086 | FullReplace=1
1087 | UpdateDesignatorLock=1
1088 | UpdatePartIDLock=1
1089 | PreserveParameterLocations=1
1090 | PreserveParameterVisibility=1
1091 | DoGraphics=1
1092 | DoParameters=1
1093 | DoModels=1
1094 | AddParameters=0
1095 | RemoveParameters=0
1096 | AddModels=1
1097 | RemoveModels=1
1098 | UpdateCurrentModels=1
1099 |
1100 | [DatabaseUpdateOptions]
1101 | SelectedOnly=0
1102 | UpdateVariants=1
1103 | UpdateToLatestRevision=1
1104 | PartTypes=0
1105 | ComponentLibIdentifierKind0=Library Name And Type
1106 | ComponentLibraryIdentifier0=Celestial Altium Library - altium_library.DbLib/Capacitors - Ceramic - 0603
1107 | ComponentDesignItemID0=C0603C105K4RACAUTO
1108 | ComponentSymbolReference0=Capacitor
1109 | ComponentUpdate0=1
1110 | ComponentIsDeviceSheet0=0
1111 | ComponentLibIdentifierKind1=Library Name And Type
1112 | ComponentLibraryIdentifier1=Celestial Altium Library - altium_library.DbLib/Capacitors - Ceramic - 0603
1113 | ComponentDesignItemID1=C0603T104K8RAC7867
1114 | ComponentSymbolReference1=Capacitor
1115 | ComponentUpdate1=1
1116 | ComponentIsDeviceSheet1=0
1117 | ComponentLibIdentifierKind2=Library Name And Type
1118 | ComponentLibraryIdentifier2=Celestial Altium Library - altium_library.DbLib/Capacitors - Ceramic - 0603
1119 | ComponentDesignItemID2=CC0603KRX7R6BB105
1120 | ComponentSymbolReference2=Capacitor
1121 | ComponentUpdate2=1
1122 | ComponentIsDeviceSheet2=0
1123 | ComponentLibIdentifierKind3=Library Name And Type
1124 | ComponentLibraryIdentifier3=Celestial Altium Library - altium_library.DbLib/Capacitors - Ceramic - 0603
1125 | ComponentDesignItemID3=GRM188R60J106ME47D
1126 | ComponentSymbolReference3=Capacitor
1127 | ComponentUpdate3=1
1128 | ComponentIsDeviceSheet3=0
1129 | ComponentLibIdentifierKind4=Library Name And Type
1130 | ComponentLibraryIdentifier4=Celestial Altium Library - altium_library.DbLib/Capacitors - Ceramic - 0603
1131 | ComponentDesignItemID4=GRM188R61A106MAALD
1132 | ComponentSymbolReference4=Capacitor
1133 | ComponentUpdate4=1
1134 | ComponentIsDeviceSheet4=0
1135 | ComponentLibIdentifierKind5=Library Name And Type
1136 | ComponentLibraryIdentifier5=Celestial Altium Library - altium_library.DbLib/Capacitors - Tantalum
1137 | ComponentDesignItemID5=CWR11MH105KB
1138 | ComponentSymbolReference5=Polarised Capacitor
1139 | ComponentUpdate5=1
1140 | ComponentIsDeviceSheet5=0
1141 | ComponentLibIdentifierKind6=Library Name And Type
1142 | ComponentLibraryIdentifier6=Celestial Altium Library - altium_library.DbLib/Diodes - Rectifiers - Single
1143 | ComponentDesignItemID6=B5818W-TP
1144 | ComponentSymbolReference6=Diode
1145 | ComponentUpdate6=1
1146 | ComponentIsDeviceSheet6=0
1147 | ComponentLibIdentifierKind7=Library Name And Type
1148 | ComponentLibraryIdentifier7=Celestial Altium Library - altium_library.DbLib/Resistors - Surface Mount - 0603
1149 | ComponentDesignItemID7=AC0603FR-07100RL
1150 | ComponentSymbolReference7=Resistor
1151 | ComponentUpdate7=1
1152 | ComponentIsDeviceSheet7=0
1153 | ComponentLibIdentifierKind8=Library Name And Type
1154 | ComponentLibraryIdentifier8=Celestial Altium Library - altium_library.DbLib/Resistors - Surface Mount - 0603
1155 | ComponentDesignItemID8=AC0603FR-071KL
1156 | ComponentSymbolReference8=Resistor
1157 | ComponentUpdate8=1
1158 | ComponentIsDeviceSheet8=0
1159 | ComponentLibIdentifierKind9=Library Name And Type
1160 | ComponentLibraryIdentifier9=Celestial Altium Library - altium_library.DbLib/Resistors - Surface Mount - 0603
1161 | ComponentDesignItemID9=CPF0603B220RE1
1162 | ComponentSymbolReference9=Resistor
1163 | ComponentUpdate9=1
1164 | ComponentIsDeviceSheet9=0
1165 | ComponentLibIdentifierKind10=Library Name And Type
1166 | ComponentLibraryIdentifier10=Celestial Altium Library - altium_library.DbLib/Resistors - Surface Mount - 0603
1167 | ComponentDesignItemID10=RNCS0603BKE10K0
1168 | ComponentSymbolReference10=Resistor
1169 | ComponentUpdate10=1
1170 | ComponentIsDeviceSheet10=0
1171 | ComponentLibIdentifierKind11=Library Name And Type
1172 | ComponentLibraryIdentifier11=Celestial Altium Library - altium_library.DbLib/Resistors - Surface Mount - 0603
1173 | ComponentDesignItemID11=SDR03EZPF62R0
1174 | ComponentSymbolReference11=Resistor
1175 | ComponentUpdate11=1
1176 | ComponentIsDeviceSheet11=0
1177 | ComponentLibIdentifierKind12=Library Name And Type
1178 | ComponentLibraryIdentifier12=Celestial Altium Library - altium_library.DbLib/Sensors - PTC Thermistors
1179 | ComponentDesignItemID12=B59641A0145A062
1180 | ComponentSymbolReference12=Thermistor PTC
1181 | ComponentUpdate12=1
1182 | ComponentIsDeviceSheet12=0
1183 | ComponentLibIdentifierKind13=Library Name And Type
1184 | ComponentLibraryIdentifier13=MyIntegrated_Library.IntLib
1185 | ComponentDesignItemID13=DW07D
1186 | ComponentSymbolReference13=DW07D
1187 | ComponentUpdate13=1
1188 | ComponentIsDeviceSheet13=0
1189 | ComponentLibIdentifierKind14=Library Name And Type
1190 | ComponentLibraryIdentifier14=vh_altium_library.intLib
1191 | ComponentDesignItemID14=HEADER-1X2
1192 | ComponentSymbolReference14=HEADER-1X2
1193 | ComponentUpdate14=1
1194 | ComponentIsDeviceSheet14=0
1195 | ComponentLibIdentifierKind15=Library Name And Type
1196 | ComponentLibraryIdentifier15=vh_altium_library.intLib
1197 | ComponentDesignItemID15=HEADER-1X6
1198 | ComponentSymbolReference15=HEADER-1X6
1199 | ComponentUpdate15=1
1200 | ComponentIsDeviceSheet15=0
1201 | ComponentLibIdentifierKind16=Library Name Only
1202 | ComponentLibraryIdentifier16=Altium Content Vault
1203 | ComponentDesignItemID16=CMP-0133-00083-1
1204 | ComponentSymbolReference16=PM-BM-MCP73833-IL11
1205 | ComponentUpdate16=1
1206 | ComponentIsDeviceSheet16=0
1207 | ComponentLibIdentifierKind17=Library Name Only
1208 | ComponentLibraryIdentifier17=Altium Content Vault
1209 | ComponentDesignItemID17=CMP-02424-000104-1
1210 | ComponentSymbolReference17=725321dda6339637d4d059423bc1c3c
1211 | ComponentUpdate17=1
1212 | ComponentIsDeviceSheet17=0
1213 | ComponentLibIdentifierKind18=Library Name Only
1214 | ComponentLibraryIdentifier18=Altium Content Vault
1215 | ComponentDesignItemID18=CMP-0345-00001-2
1216 | ComponentSymbolReference18=TI-TPS63000DRC10-3000X1650TP
1217 | ComponentUpdate18=1
1218 | ComponentIsDeviceSheet18=0
1219 | ComponentLibIdentifierKind19=Library Name Only
1220 | ComponentLibraryIdentifier19=Altium Content Vault
1221 | ComponentDesignItemID19=CMP-0434-00001-1
1222 | ComponentSymbolReference19=STM-NEMOS-G1S2D3-3
1223 | ComponentUpdate19=1
1224 | ComponentIsDeviceSheet19=0
1225 | ComponentLibIdentifierKind20=Library Name Only
1226 | ComponentLibraryIdentifier20=Altium Content Vault
1227 | ComponentDesignItemID20=CMP-1489-00006-1
1228 | ComponentSymbolReference20=WE-LED_K1A2-2
1229 | ComponentUpdate20=1
1230 | ComponentIsDeviceSheet20=0
1231 | ComponentLibIdentifierKind21=Library Name Only
1232 | ComponentLibraryIdentifier21=Altium Content Vault
1233 | ComponentDesignItemID21=CMP-1666-00027-1
1234 | ComponentSymbolReference21=LED-RED-AK-2
1235 | ComponentUpdate21=1
1236 | ComponentIsDeviceSheet21=0
1237 | ComponentLibIdentifierKind22=Library Name Only
1238 | ComponentLibraryIdentifier22=Altium Content Vault
1239 | ComponentDesignItemID22=CMP-194065-000008-1
1240 | ComponentSymbolReference22=ee866abf0b6c51247b7f5cdebf2f250
1241 | ComponentUpdate22=1
1242 | ComponentIsDeviceSheet22=0
1243 | ComponentLibIdentifierKind23=Library Name Only
1244 | ComponentLibraryIdentifier23=Altium Content Vault
1245 | ComponentDesignItemID23=CMP-2000-05827-1
1246 | ComponentSymbolReference23=USB-5-6SHLD
1247 | ComponentUpdate23=1
1248 | ComponentIsDeviceSheet23=0
1249 | ComponentLibIdentifierKind24=Library Name Only
1250 | ComponentLibraryIdentifier24=Altium Content Vault
1251 | ComponentDesignItemID24=CMP-2000-06247-1
1252 | ComponentSymbolReference24=PEMOS-GSD-3
1253 | ComponentUpdate24=1
1254 | ComponentIsDeviceSheet24=0
1255 | ComponentLibIdentifierKind25=Library Name Only
1256 | ComponentLibraryIdentifier25=Altium Content Vault
1257 | ComponentDesignItemID25=CMP-2000-06693-1
1258 | ComponentSymbolReference25=FUSE-2
1259 | ComponentUpdate25=1
1260 | ComponentIsDeviceSheet25=0
1261 | ComponentLibIdentifierKind26=Library Name Only
1262 | ComponentLibraryIdentifier26=Altium Content Vault
1263 | ComponentDesignItemID26=CMP-2100-03586-2
1264 | ComponentSymbolReference26=ae622034201233ca1abcedc05908f33
1265 | ComponentUpdate26=1
1266 | ComponentIsDeviceSheet26=0
1267 |
1268 | [Comparison Options]
1269 | ComparisonOptions0=Kind=Net|MinPercent=75|MinMatch=3|ShowMatch=0|UseName=-1|InclAllRules=0
1270 | ComparisonOptions1=Kind=Net Class|MinPercent=75|MinMatch=3|ShowMatch=0|UseName=-1|InclAllRules=0
1271 | ComparisonOptions2=Kind=Component Class|MinPercent=75|MinMatch=3|ShowMatch=0|UseName=-1|InclAllRules=0
1272 | ComparisonOptions3=Kind=Rule|MinPercent=75|MinMatch=3|ShowMatch=0|UseName=-1|InclAllRules=0
1273 | ComparisonOptions4=Kind=Differential Pair|MinPercent=50|MinMatch=1|ShowMatch=0|UseName=0|InclAllRules=0
1274 | ComparisonOptions5=Kind=Structure Class|MinPercent=75|MinMatch=3|ShowMatch=0|UseName=-1|InclAllRules=0
1275 |
1276 | [SmartPDF]
1277 | PageOptions=Record=PageOptions|CenterHorizontal=True|CenterVertical=True|PrintScale=1.00|XCorrection=1.00|YCorrection=1.00|PrintKind=1|BorderSize=5000000|LeftOffset=0|BottomOffset=0|Orientation=2|PaperLength=1000|PaperWidth=1000|Scale=100|PaperSource=7|PrintQuality=-3|MediaType=1|DitherType=10|PrintScaleMode=1|PaperKind=A4|PaperIndex=9
1278 | Configuration_Name1=OutputConfigurationParameter1
1279 | Configuration_Item1=DesignatorDisplayMode=Physical|PrintArea=DesignExtent|PrintAreaLowerLeftCornerX=0|PrintAreaLowerLeftCornerY=0|PrintAreaUpperRightCornerX=0|PrintAreaUpperRightCornerY=0|Record=PcbPrintView|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1280 | Configuration_Name2=OutputConfigurationParameter2
1281 | Configuration_Item2=IncludeBoardCutouts=False|IncludeBottomLayerComponents=True|IncludeMultiLayerComponents=True|IncludeTopLayerComponents=True|IncludeViewports=True|Index=0|Mirror=False|Name=Multilayer Composite Print|PadNumberFontSize=14|Record=PcbPrintOut|ShowHoles=False|ShowPadNets=False|ShowPadNumbers=False|SubstituteFonts=False|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1282 | Configuration_Name3=OutputConfigurationParameter3
1283 | Configuration_Item3=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=TopOverlay|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1284 | Configuration_Name4=OutputConfigurationParameter4
1285 | Configuration_Item4=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=TopLayer|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1286 | Configuration_Name5=OutputConfigurationParameter5
1287 | Configuration_Item5=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=BottomLayer|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1288 | Configuration_Name6=OutputConfigurationParameter6
1289 | Configuration_Item6=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=MultiLayer|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1290 | Configuration_Name7=OutputConfigurationParameter7
1291 | Configuration_Item7=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=BottomOverlay|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1292 | Configuration_Name8=OutputConfigurationParameter8
1293 | Configuration_Item8=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=KeepOutLayer|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1294 | Configuration_Name9=OutputConfigurationParameter9
1295 | Configuration_Item9=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical1|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1296 | Configuration_Name10=OutputConfigurationParameter10
1297 | Configuration_Item10=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical2|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1298 | Configuration_Name11=OutputConfigurationParameter11
1299 | Configuration_Item11=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical4|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1300 | Configuration_Name12=OutputConfigurationParameter12
1301 | Configuration_Item12=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical6|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1302 | Configuration_Name13=OutputConfigurationParameter13
1303 | Configuration_Item13=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical8|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1304 | Configuration_Name14=OutputConfigurationParameter14
1305 | Configuration_Item14=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical13|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1306 | Configuration_Name15=OutputConfigurationParameter15
1307 | Configuration_Item15=CArc=Full|CFill=Full|Comment=Full|Coordinate=Full|CPad=Full|CRegion=Full|CText=Full|CTrack=Full|CVia=Full|Designator=Full|Dimension=Full|DLayer1=TopLayer|DLayer2=BottomLayer|DrillType=Regular|FArc=Full|FFill=Full|FPad=Full|FRegion=Full|FText=Full|FTrack=Full|FVia=Full|Layer=Mechanical15|Polygon=Full|PrintOutIndex=0|Record=PcbPrintLayer|DocumentPath=E:\Altium\AD21_Projects\ESP32_AirConditionerController\ESP32_AC_Controller\ESP32_AC_Controller.PcbDoc
1308 |
1309 |
--------------------------------------------------------------------------------
/docs/原理图PCB/ESP32_AC_Controller.SchDoc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DaMiBear/ESP32_HomeKit_AirConditioner/20bba86675b2381d4f37f453ed810e0409db9605/docs/原理图PCB/ESP32_AC_Controller.SchDoc
--------------------------------------------------------------------------------
/main/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | idf_component_register(SRCS ./app_main.c ./air_conditioner.c
2 | INCLUDE_DIRS ".")
3 |
--------------------------------------------------------------------------------
/main/Kconfig.projbuild:
--------------------------------------------------------------------------------
1 | menu "Example Configuration"
2 |
3 | config EXAMPLE_USE_HARDCODED_SETUP_CODE
4 | bool "Use hard-coded setup code"
5 | default y
6 | help
7 | HomeKit does not recommend having the setup code programmed in the accessory as is.
8 | Instead, it expects setup info (salt-verifier pair) generated for the given setup code.
9 | Use this option only for testing purposes. For production, the setup info should be
10 | used.
11 |
12 | config EXAMPLE_SETUP_CODE
13 | string "HomeKit Setup Code"
14 | default "111-22-333"
15 | depends on EXAMPLE_USE_HARDCODED_SETUP_CODE
16 | help
17 | Setup code to be used for HomeKot pairing, if hard-coded setup code is enabled.
18 |
19 | config EXAMPLE_SETUP_ID
20 | string "HomeKit Setup Id"
21 | default "ES32"
22 | depends on EXAMPLE_USE_HARDCODED_SETUP_CODE
23 | help
24 | Setup id to be used for HomeKot pairing, if hard-coded setup code is enabled.
25 |
26 |
27 | config APP_WIFI_LISTEN_INTERVAL
28 | int "WiFi listen interval"
29 | default 3
30 | help
31 | Interval for station to listen to beacon from AP. The unit of listen interval is one beacon interval.
32 | For example, if beacon interval is 100 ms and listen interval is 3, the interval for station to listen
33 | to beacon is 300 ms.
34 |
35 | choice APP_POWER_SAVE_MODE
36 | prompt "power save mode"
37 | default APP_POWER_SAVE_MIN_MODEM
38 | help
39 | Power save mode for the esp32 to use. Modem sleep mode includes minimum and maximum power save modes.
40 | In minimum power save mode, station wakes up every DTIM to receive beacon. Broadcast data will not be
41 | lost because it is transmitted after DTIM. However, it can not save much more power if DTIM is short
42 | for DTIM is determined by AP.
43 | In maximum power save mode, station wakes up every listen interval to receive beacon. Broadcast data
44 | may be lost because station may be in sleep state at DTIM time. If listen interval is longer, more power
45 | is saved but broadcast data is more easy to lose.
46 |
47 | config APP_POWER_SAVE_NONE
48 | bool "none"
49 | config APP_POWER_SAVE_MIN_MODEM
50 | bool "minimum modem"
51 | config APP_POWER_SAVE_MAX_MODEM
52 | bool "maximum modem"
53 | endchoice
54 |
55 | choice APP_MAX_CPU_FREQ
56 | prompt "Maximum CPU frequency"
57 | default APP_MAX_CPU_FREQ_80
58 | depends on PM_ENABLE
59 | help
60 | Maximum CPU frequency to use for dynamic frequency scaling.
61 |
62 | config APP_MAX_CPU_FREQ_80
63 | bool "80 MHz"
64 | config APP_MAX_CPU_FREQ_160
65 | bool "160 MHz"
66 | config APP_MAX_CPU_FREQ_240
67 | bool "240 MHz"
68 | depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
69 | endchoice
70 |
71 | config APP_MAX_CPU_FREQ_MHZ
72 | int
73 | default 80 if APP_MAX_CPU_FREQ_80
74 | default 160 if APP_MAX_CPU_FREQ_160
75 | default 240 if APP_MAX_CPU_FREQ_240
76 |
77 |
78 | choice APP_MIN_CPU_FREQ
79 | prompt "Minimum CPU frequency"
80 | default APP_MIN_CPU_FREQ_10M
81 | depends on PM_ENABLE
82 | help
83 | Minimum CPU frequency to use for dynamic frequency scaling.
84 | Should be set to XTAL frequency or XTAL frequency divided by integer.
85 |
86 | config APP_MIN_CPU_FREQ_40M
87 | bool "40 MHz (use with 40MHz XTAL)"
88 | depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
89 | config APP_MIN_CPU_FREQ_20M
90 | bool "20 MHz (use with 40MHz XTAL)"
91 | depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
92 | config APP_MIN_CPU_FREQ_10M
93 | bool "10 MHz (use with 40MHz XTAL)"
94 | depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32C3 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
95 | config APP_MIN_CPU_FREQ_26M
96 | bool "26 MHz (use with 26MHz XTAL)"
97 | depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
98 | config APP_MIN_CPU_FREQ_13M
99 | bool "13 MHz (use with 26MHz XTAL)"
100 | depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
101 | endchoice
102 |
103 | config APP_MIN_CPU_FREQ_MHZ
104 | int
105 | default 40 if APP_MIN_CPU_FREQ_40M
106 | default 20 if APP_MIN_CPU_FREQ_20M
107 | default 10 if APP_MIN_CPU_FREQ_10M
108 | default 26 if APP_MIN_CPU_FREQ_26M
109 | default 13 if APP_MIN_CPU_FREQ_13M
110 | endmenu
111 |
--------------------------------------------------------------------------------
/main/air_conditioner.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ESPRESSIF MIT License
3 | *
4 | * Copyright (c) 2018
5 | *
6 | * Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
7 | * it is free of charge, to any person obtaining a copy of this software and associated
8 | * documentation files (the "Software"), to deal in the Software without restriction, including
9 | * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
11 | * to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all copies or
14 | * substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | *
23 | */
24 |
25 |
26 |
27 | #include
28 |
29 | #include "driver/ledc.h"
30 | #include "esp_log.h"
31 | #include "air_conditioner.h"
32 | #include
33 | #include
34 | #include "driver/rmt.h"
35 | #include "ir_tools.h"
36 |
37 |
38 | static const char *TAG = "Air Conditioner";
39 | static rmt_channel_t ac_tx_channel = RMT_CHANNEL_0;
40 |
41 | static rmt_item32_t *items = NULL;
42 | static uint32_t length = 0;
43 | static ir_builder_t *ir_builder = NULL;
44 |
45 | const uint8_t kCoolixTempMap[kCoolixTempRange] = {
46 | 0b0000, // 17C
47 | 0b0001, // 18c
48 | 0b0011, // 19C
49 | 0b0010, // 20C
50 | 0b0110, // 21C
51 | 0b0111, // 22C
52 | 0b0101, // 23C
53 | 0b0100, // 24C
54 | 0b1100, // 25C
55 | 0b1101, // 26C
56 | 0b1001, // 27C
57 | 0b1000, // 28C
58 | 0b1010, // 29C
59 | 0b1011 // 30C
60 | };
61 |
62 |
63 | hap_serv_t *hap_air_conditioner_create(void)
64 | {
65 | hap_serv_t *hs = hap_serv_heater_cooler_create(0, 26.0, 0, 0); // 实际上创建的是加热器制冷器
66 | hap_serv_add_char(hs, hap_char_heating_threshold_temperature_create(24.0)); // 可以理解为制热模式的初始温度,以及自动模式的初始下限
67 | hap_serv_add_char(hs, hap_char_cooling_threshold_temperature_create(26.0)); // 制冷模式的初始温度,以及自动模式的初始上限
68 | return hs;
69 | }
70 |
71 | /**
72 | * @brief 初始化rmt模块
73 | *
74 | */
75 | void air_conditioner_init(void)
76 | {
77 | /* 红外初始化 */
78 | rmt_config_t rmt_tx_config = RMT_DEFAULT_CONFIG_TX(RMT_TX_GPIO, ac_tx_channel);
79 | rmt_tx_config.tx_config.carrier_en = true;
80 | rmt_tx_config.flags = RMT_CHANNEL_FLAGS_AWARE_DFS;
81 | rmt_tx_config.clk_div = 1;
82 | rmt_config(&rmt_tx_config);
83 | rmt_driver_install(ac_tx_channel, 0, 0);
84 | ir_builder_config_t ir_builder_config = IR_BUILDER_DEFAULT_CONFIG((ir_dev_t)ac_tx_channel);
85 | ir_builder_config.buffer_size = 128;
86 | ir_builder_config.flags |= IR_TOOLS_FLAGS_PROTO_EXT; // Using extended IR protocols (both NEC and RC5 have extended version)
87 | ir_builder = ir_builder_rmt_new_nec(&ir_builder_config);
88 | }
89 |
90 | /**
91 | * @brief 根据风速、温度、模式等(todo)通过红外二极管发送r05d指令
92 | *
93 | * @param ac_info 空调设置信息
94 | *
95 | * @return void
96 | */
97 | void ac_send_r05d_code(AC_INFO ac_info)
98 | {
99 | ESP_LOGI(TAG, "Send - Fan Speed: %d, Temp: %d, Mode: %d, On: %s", ac_info.fan_speed, ac_info.temp, ac_info.mode, ac_info.on ? "on" : "off");
100 | AC_R05D_PAYLOAD_CODE send_code = {
101 | .A = 0,
102 | .B = 0,
103 | .C = 0
104 | };
105 | /* 默认识别码 */
106 | send_code.A = 0xB2;
107 | send_code.A_ = 0x4D;
108 | if (ac_info.on == true) // 开机前提下
109 | {
110 | /* 风速代码 B7 B6 B5 */
111 | switch (ac_info.fan_speed)
112 | {
113 | case AUTO_FAN_SPEED:
114 | send_code.B = (kCoolixFanAuto << 5) | 0b11111;
115 | break;
116 | case MIN_FAN_SPEED:
117 | send_code.B = (kCoolixFanMin << 5) | 0b11111;
118 | break;
119 | case MEDIUM_FAN_SPEED:
120 | send_code.B = (kCoolixFanMed << 5) | 0b11111;
121 | break;
122 | case MAX_FAN_SPEED:
123 | send_code.B = (kCoolixFanMax << 5) | 0b11111;
124 | break;
125 | case FIXED_FAN_SPEED:
126 | send_code.B = (kCoolixFanAuto0 << 5) | 0b11111;
127 | break;
128 | default:
129 | send_code.B = (kCoolixFanAuto << 5) | 0b11111;
130 | break;
131 | }
132 |
133 | /* 温度代码 */
134 | if (ac_info.temp > kCoolixTempMax) // 先限制范围
135 | ac_info.temp = kCoolixTempMax;
136 | else if (ac_info.temp < kCoolixTempMin)
137 | ac_info.temp = kCoolixTempMin;
138 |
139 | send_code.C = kCoolixTempMap[ac_info.temp%kCoolixTempMin] << 4;
140 |
141 | /* 模式代码 */
142 | switch (ac_info.mode)
143 | {
144 | case AUTO_MODE:
145 | send_code.C |= kCoolixAuto << 2;
146 | send_code.B = (kCoolixFanAuto0 << 5) | 0b11111; // 自动风模式下,风速必须为固定风
147 | break;
148 | case COOL_MODE:
149 | send_code.C |= kCoolixCool << 2;
150 | break;
151 | case HEAT_MODE:
152 | send_code.C |= kCoolixHeat << 2;
153 | break;
154 | default:
155 | send_code.C |= kCoolixAuto << 2;
156 | break;
157 | }
158 | }
159 | else
160 | { // 关机代码
161 | send_code.B = 0x7B;
162 | send_code.C = 0xE0;
163 | }
164 |
165 | send_code.B_ = ~send_code.B;
166 | send_code.C_ = ~send_code.C;
167 |
168 | ESP_ERROR_CHECK(ir_builder->build_frame(ir_builder, send_code));
169 | ESP_ERROR_CHECK(ir_builder->get_result(ir_builder, &items, &length)); // 改变items这个指针指向的地址,相当指向rmt_item32_t结构体数组首地址
170 | //To send data according to the waveform items.
171 | rmt_write_items(ac_tx_channel, items, length, true);
172 | ESP_LOGI(TAG, "Send R05D Code : %#X %#X %#X %#X %#X %#X", send_code.A, send_code.A_, send_code.B, send_code.B_, send_code.C, send_code.C_);
173 | }
174 |
175 |
176 | /**
177 | * @brief deinitialize the air conditioner
178 | *
179 | */
180 | void air_conditioner_deinit(void)
181 | {
182 | ir_builder->del(ir_builder);
183 | rmt_driver_uninstall(ac_tx_channel);
184 | }
185 |
186 |
187 |
188 |
--------------------------------------------------------------------------------
/main/air_conditioner.h:
--------------------------------------------------------------------------------
1 | #ifndef _AIR_CONDITIONER_H_
2 | #define _AIR_CONDITIONER_H_
3 |
4 |
5 | #include "hap.h"
6 |
7 | #define RMT_TX_GPIO GPIO_NUM_18
8 | /* 空调设定模式 */
9 | typedef enum {
10 | AUTO_MODE = 0,
11 | COOL_MODE,
12 | HEAT_MODE
13 | }AC_MODE;
14 |
15 | /* 空调设定的风速 */
16 | typedef enum {
17 | AUTO_FAN_SPEED = 0,
18 | MIN_FAN_SPEED,
19 | MEDIUM_FAN_SPEED,
20 | MAX_FAN_SPEED,
21 | FIXED_FAN_SPEED,
22 | OFF_FAN_SPEED
23 | }AC_FAN_SPEED;
24 |
25 | /* R05D码格式 */
26 | typedef struct {
27 | uint8_t A;
28 | uint8_t A_; // A的按位取反
29 | uint8_t B;
30 | uint8_t B_;
31 | uint8_t C;
32 | uint8_t C_;
33 | }AC_R05D_PAYLOAD_CODE;
34 |
35 | /* 空调指令信息 */
36 | typedef struct {
37 | bool on; // 记录空调开关状态
38 | AC_MODE mode; // 记录当前空调设定的模式
39 | uint8_t temp; // 记录当前空调设定的温度
40 | AC_FAN_SPEED fan_speed; // 记录当前空调设定的风速
41 | }AC_INFO;
42 | /* 下面部分参考 https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h */
43 | /* 风速代码 */
44 | #define kCoolixFanMin (0b100)
45 | #define kCoolixFanMed (0b010)
46 | #define kCoolixFanMax (0b001)
47 | #define kCoolixFanAuto (0b101)
48 | #define kCoolixFanAuto0 (0b000) // 固定风
49 | /* 模式代码 只选用了四种模式 */
50 | #define kCoolixCool (0b00)
51 | #define kCoolixDry (0b01)
52 | #define kCoolixAuto (0b10)
53 | #define kCoolixHeat (0b11)
54 |
55 | /* 温度代码 */
56 | #define kCoolixTempMin (17) // Celsius
57 | #define kCoolixTempMax (30) // Celsius
58 | #define kCoolixTempRange (kCoolixTempMax - kCoolixTempMin + 1)
59 |
60 |
61 | /**
62 | * @brief initialize the air conditioner
63 | *
64 | * @param none
65 | *
66 | * @return none
67 | */
68 | void air_conditioner_init(void);
69 |
70 |
71 |
72 | /**
73 | * @brief 创建空调服务
74 | *
75 | * @return hap_serv_t* HAP Service handle
76 | */
77 | hap_serv_t *hap_air_conditioner_create(void);
78 |
79 | /**
80 | * @brief 根据风速、温度、模式等(todo)通过红外二极管发送r05d指令
81 | *
82 | * @param ac_info 空调设置信息
83 | *
84 | * @return void
85 | */
86 | void ac_send_r05d_code(AC_INFO ac_info);
87 |
88 | /**
89 | * @brief 清除申请的内存空间以及失能红外配置
90 | *
91 | */
92 | void air_conditioner_deinit(void);
93 |
94 |
95 |
96 | #endif /* _AIR_CONDITIONER_H_ */
97 |
--------------------------------------------------------------------------------
/main/app_main.c:
--------------------------------------------------------------------------------
1 | /*
2 | * ESPRESSIF MIT License
3 | *
4 | * Copyright (c) 2018
5 | *
6 | * Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
7 | * it is free of charge, to any person obtaining a copy of this software and associated
8 | * documentation files (the "Software"), to deal in the Software without restriction, including
9 | * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 | * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
11 | * to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in all copies or
14 | * substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | *
23 | */
24 |
25 |
26 |
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include "esp_log.h"
32 |
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | #include
39 |
40 | #include
41 | #include
42 |
43 | #include "esp_pm.h"
44 | #include "air_conditioner.h"
45 |
46 | /* Comment out the below line to disable Firmware Upgrades */
47 | #define CONFIG_FIRMWARE_SERVICE
48 |
49 | static const char *TAG = "HAP Air Conditioner";
50 |
51 | #define AIRCONDITIONER_TASK_PRIORITY 1
52 | #define AIRCONDITIONER_TASK_STACKSIZE 4 * 1024
53 | #define AIRCONDITIONER_TASK_NAME "hap_airconditioner"
54 |
55 | /* Reset network credentials if button is pressed for more than 3 seconds and then released */
56 | #define RESET_NETWORK_BUTTON_TIMEOUT 3
57 |
58 | /* Reset to factory if button is pressed and held for more than 10 seconds */
59 | #define RESET_TO_FACTORY_BUTTON_TIMEOUT 10
60 |
61 | /* The button "Boot" will be used as the Reset button for the example */
62 | #define RESET_GPIO GPIO_NUM_0
63 |
64 | static void air_conditioner_send_task(void *arg);
65 |
66 | TaskHandle_t air_conditioner_send_task_handle = NULL;
67 |
68 | AC_INFO ac_current_info = {
69 | .on = false,
70 | .mode = AUTO_MODE,
71 | .temp = 26,
72 | .fan_speed = AUTO_FAN_SPEED
73 | };
74 |
75 | static TickType_t ac_send_tick_count = 0;
76 |
77 | bool ac_send_r05d_code_flag = false; // 是否可以发送红外信号
78 |
79 | esp_pm_lock_handle_t rmt_send_task_pm_lock = NULL; // 电源管理锁,用于防止在要使用rmt时自动进入light-sleep状态
80 |
81 | /**
82 | * @brief The network reset button callback handler.
83 | * Useful for testing the Wi-Fi re-configuration feature of WAC2
84 | */
85 | static void reset_network_handler(void* arg)
86 | {
87 | hap_reset_network();
88 | }
89 | /**
90 | * @brief The factory reset button callback handler.
91 | */
92 | static void reset_to_factory_handler(void* arg)
93 | {
94 | hap_reset_to_factory();
95 | }
96 |
97 | /**
98 | * The Reset button GPIO initialisation function.
99 | * Same button will be used for resetting Wi-Fi network as well as for reset to factory based on
100 | * the time for which the button is pressed.
101 | */
102 | static void reset_key_init(uint32_t key_gpio_pin)
103 | {
104 | button_handle_t handle = iot_button_create(key_gpio_pin, BUTTON_ACTIVE_LOW);
105 | iot_button_add_on_release_cb(handle, RESET_NETWORK_BUTTON_TIMEOUT, reset_network_handler, NULL);
106 | iot_button_add_on_press_cb(handle, RESET_TO_FACTORY_BUTTON_TIMEOUT, reset_to_factory_handler, NULL);
107 | }
108 |
109 | /* Mandatory identify routine for the accessory.
110 | * In a real accessory, something like LED blink should be implemented
111 | * got visual identification
112 | */
113 | static int air_conditioner_identify(hap_acc_t *ha)
114 | {
115 | ESP_LOGI(TAG, "Accessory identified");
116 | return HAP_SUCCESS;
117 | }
118 |
119 |
120 | static char val[260];
121 | static char * emulator_print_value(hap_char_t *hc, const hap_val_t *cval)
122 | {
123 | uint16_t perm = hap_char_get_perm(hc);
124 | if (perm & HAP_CHAR_PERM_PR) {
125 | hap_char_format_t format = hap_char_get_format(hc);
126 | switch (format) {
127 | case HAP_CHAR_FORMAT_BOOL : {
128 | snprintf(val, sizeof(val), "%s", cval->b ? "true":"false");
129 | break;
130 | }
131 | case HAP_CHAR_FORMAT_UINT8:
132 | case HAP_CHAR_FORMAT_UINT16:
133 | case HAP_CHAR_FORMAT_UINT32:
134 | case HAP_CHAR_FORMAT_INT:
135 | snprintf(val, sizeof(val), "%d", cval->i);
136 | break;
137 | case HAP_CHAR_FORMAT_FLOAT :
138 | snprintf(val, sizeof(val), "%f", cval->f);
139 | break;
140 | case HAP_CHAR_FORMAT_STRING :
141 | if (cval->s) {
142 | snprintf(val, sizeof(val), "%s", cval->s);
143 | } else {
144 | snprintf(val, sizeof(val), "null");
145 | }
146 | break;
147 | default :
148 | snprintf(val, sizeof(val), "unsupported");
149 | }
150 | } else {
151 | snprintf(val, sizeof(val), "null");
152 | }
153 | return val;
154 | }
155 |
156 | /* Callback for handling writes on the Air Conditioner Service
157 | */
158 | static int air_conditioner_write(hap_write_data_t write_data[], int count,
159 | void *serv_priv, void *write_priv)
160 | {
161 | int i, ret = HAP_SUCCESS;
162 | // static float last_fan_speed = 100;
163 | // static AC_FAN_SPEED last_fan_mode = AUTO_FAN_SPEED;
164 | // bool enable_send = false;
165 | hap_write_data_t *write;
166 | // printf("DEBUG!!!!!!!!!!! count: %d\n", count);
167 | for (i = 0; i < count; i++) {
168 | /* 每次只对一个特征hc进行处理 */
169 | write = &write_data[i];
170 | /* Setting a default error value */
171 | *(write->status) = HAP_STATUS_VAL_INVALID;
172 | // int iid = hap_char_get_iid(write->hc);
173 | // int aid = hap_acc_get_aid(hap_serv_get_parent(hap_char_get_parent(write->hc)));
174 | // printf("Write aid = %d, iid = %d, val = %s\n", aid, iid, emulator_print_value(write->hc, &(write->val)));
175 | // printf("hap_char_get_type_uuid(write->hc) : %s\n", hap_char_get_type_uuid(write->hc));
176 | /* 开关操作 */
177 | if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_ACTIVE)) { // ACTIVE实际上是8位的,但是目前HomitKit中只定义了0和1
178 | ESP_LOGI(TAG, "Received Write for ACTIVE: %s", write->val.u ? "On" : "Off");
179 | if (write->val.u == 0)
180 | ac_current_info.on = false;
181 | else
182 | ac_current_info.on = true;
183 |
184 | *(write->status) = HAP_STATUS_SUCCESS;
185 |
186 | }
187 | /* 模式操作 */
188 | else if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_TARGET_HEATER_COOLER_STATE))
189 | {
190 | if (write->val.u == 0)
191 | { // 自动模式
192 | ac_current_info.mode = AUTO_MODE;
193 | ESP_LOGI(TAG, "Received Write for TARGET_HEATER_COOLER_STATE: Heat or Cool");
194 | }
195 | else if (write->val.u == 1)
196 | { // 加热模式
197 | ac_current_info.mode = HEAT_MODE;
198 | ESP_LOGI(TAG, "Received Write for TARGET_HEATER_COOLER_STATE: Heat");
199 | }
200 | else if (write->val.u == 2)
201 | { // 制冷模式
202 | ac_current_info.mode = COOL_MODE;
203 | ESP_LOGI(TAG, "Received Write for TARGET_HEATER_COOLER_STATE: Cool");
204 | }
205 |
206 | *(write->status) = HAP_STATUS_SUCCESS;
207 | }
208 | /* 设定制冷温度操作 */
209 | else if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_COOLING_THRESHOLD_TEMPERATURE))
210 | { /* 根据空调实际温度范围进行限制 */
211 | if (write->val.f > 30 )
212 | {
213 | write->val.f = 30;
214 | }
215 | else if (write->val.f < 17)
216 | {
217 | write->val.f = 17;
218 | }
219 | ac_current_info.temp = (uint8_t)write->val.f;
220 | ESP_LOGI(TAG, "Received Write for COOLING_THRESHOLD_TEMPERATURE: %d", ac_current_info.temp);
221 | *(write->status) = HAP_STATUS_SUCCESS;
222 | }
223 | /* 设定制热温度操作 */
224 | else if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_HEATING_THRESHOLD_TEMPERATURE))
225 | { /* 根据空调实际温度范围进行限制 */
226 | if (write->val.f < 17)
227 | {
228 | write->val.f = 17;
229 | }
230 | /** 因为制热门限在制冷门限的下面
231 | * 所以在自动模式,修改上限或下限温度时
232 | * ac_current_info.temp先为制冷门限,再被重新赋值为制热门限
233 | * 空调实际设置的温度总是以制热门限来确定
234 | */
235 | ac_current_info.temp = (uint8_t)write->val.f;
236 | ESP_LOGI(TAG, "Received Write for HEATING_THRESHOLD_TEMPERATURE: %d", ac_current_info.temp);
237 | *(write->status) = HAP_STATUS_SUCCESS;
238 | }
239 | /* 风扇转速(风速)操作 */
240 | else if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_ROTATION_SPEED))
241 | {
242 | if (write->val.f == 100)
243 | { // 自动风
244 | ac_current_info.fan_speed = AUTO_FAN_SPEED;
245 | ESP_LOGI(TAG, "Received Write for ROTATION_SPEED: Auto");
246 | }
247 | else if (write->val.f > 0 && write->val.f <= 33)
248 | { // 低风
249 | ac_current_info.fan_speed = MIN_FAN_SPEED;
250 | ESP_LOGI(TAG, "Received Write for ROTATION_SPEED: Low");
251 | }
252 | else if (write->val.f > 33 && write->val.f <= 66)
253 | { // 中风
254 | ac_current_info.fan_speed = MEDIUM_FAN_SPEED;
255 | ESP_LOGI(TAG, "Received Write for ROTATION_SPEED: Medium");
256 | }
257 | else if (write->val.f > 66 && write->val.f < 100)
258 | { // 高风
259 | ac_current_info.fan_speed = MAX_FAN_SPEED;
260 | ESP_LOGI(TAG, "Received Write for ROTATION_SPEED: Max");
261 | }
262 | else if (write->val.f == 0)
263 | {
264 | ac_current_info.fan_speed = OFF_FAN_SPEED;
265 | ESP_LOGI(TAG, "Received Write for ROTATION_SPEED: 0/OFF");
266 | }
267 |
268 | // if (last_fan_mode != ac_current_info.fan_speed)
269 | // enable_send = true;
270 | // last_fan_mode = ac_current_info.fan_speed;
271 | // last_fan_speed = write->val.f; // 保存为上一个风速
272 | *(write->status) = HAP_STATUS_SUCCESS;
273 | }
274 | else {
275 | *(write->status) = HAP_STATUS_RES_ABSENT;
276 | }
277 |
278 | /* If the characteristic write was successful, update it in hap core
279 | */
280 | if (*(write->status) == HAP_STATUS_SUCCESS) {
281 | hap_char_update_val(write->hc, &(write->val));
282 | } else {
283 | /* Else, set the return value appropriately to report error */
284 | ESP_LOGE(TAG, "Received Write for Air Conditioner Failed!");
285 | ret = HAP_FAIL;
286 | }
287 | }
288 | /* 在保存设定的状态后,使能发送红外指令的信号,并且记录此时的tick计数 */
289 | ac_send_r05d_code_flag = true;
290 | ac_send_tick_count = xTaskGetTickCount(); // 更新当前要发红外时的tick计数
291 | if (air_conditioner_send_task_handle == NULL) // 如果没有被创建,则创建air_conditioner_send_task任务
292 | {
293 | xTaskCreate(air_conditioner_send_task, "air_conditioner_send_task", 2048, NULL, 1, &air_conditioner_send_task_handle);
294 | if (air_conditioner_send_task_handle == NULL)
295 | ESP_LOGE(TAG, "Create air_conditioner_send_task fail!");
296 | }
297 |
298 | return ret;
299 | }
300 |
301 | static int air_conditioner_read(hap_char_t *hc, hap_status_t *status_code, void *serv_priv, void *read_priv)
302 | {
303 | int ret = HAP_SUCCESS;
304 | int iid = hap_char_get_iid(hc);
305 | int aid = hap_acc_get_aid(hap_serv_get_parent(hap_char_get_parent(hc)));
306 | printf("Read aid = %d, iid = %d, val = %s\n", aid, iid, emulator_print_value(hc, hap_char_get_val(hc)));
307 | printf("hap_char_get_type_uuid(hc) : %s\n", hap_char_get_type_uuid(hc));
308 | hap_val_t new_val;
309 | /* 更新当前模式为设定模式 */
310 | if (!strcmp(hap_char_get_type_uuid(hc), HAP_CHAR_UUID_CURRENT_HEATER_COOLER_STATE))
311 | {
312 | if (ac_current_info.mode == HEAT_MODE)
313 | { // 加热模式
314 | new_val.u = 2;
315 | ESP_LOGI(TAG, "Received Read for CURRENT_HEATER_COOLER_STATE: Heat");
316 | }
317 | else if (ac_current_info.mode == COOL_MODE)
318 | { // 制冷模式
319 | new_val.u = 3;
320 | ESP_LOGI(TAG, "Received Read for CURRENT_HEATER_COOLER_STATE: Cool");
321 | }
322 | else if (ac_current_info.mode == AUTO_MODE)
323 | { // 自动模式
324 | new_val.u = 1; // 显示为Idle
325 | ESP_LOGI(TAG, "Received Read for CURRENT_HEATER_COOLER_STATE: Cool");
326 | }
327 | *status_code = HAP_STATUS_SUCCESS;
328 | }
329 | /* 更新当前温度为空调设定温度 */
330 | else if (!strcmp(hap_char_get_type_uuid(hc), HAP_CHAR_UUID_CURRENT_TEMPERATURE))
331 | {
332 | new_val.f = ac_current_info.temp;
333 | ESP_LOGI(TAG, "Received Read for CURRENT_TEMPERATURE: %d", ac_current_info.temp);
334 | *status_code = HAP_STATUS_SUCCESS;
335 | }
336 | /* 下面这些特征有读写权限,在write回调函数中可以被更新 */
337 | /* 开关状态 */
338 | else if (!strcmp(hap_char_get_type_uuid(hc), HAP_CHAR_UUID_ACTIVE))
339 | {
340 | *status_code = HAP_STATUS_SUCCESS;
341 | return HAP_SUCCESS; // 因为这些特征不需要被更新,为了防止后面的hap_char_update_val传入的为空,直接返回
342 | }
343 | /* 目标加热冷却状态 */
344 | else if (!strcmp(hap_char_get_type_uuid(hc), HAP_CHAR_UUID_TARGET_HEATER_COOLER_STATE))
345 | {
346 | *status_code = HAP_STATUS_SUCCESS;
347 | return HAP_SUCCESS;
348 | }
349 | /* 加热温度门限 */
350 | else if (!strcmp(hap_char_get_type_uuid(hc), HAP_CHAR_UUID_HEATING_THRESHOLD_TEMPERATURE))
351 | { // 修改为空调设定温度,而非门限
352 | new_val.f = ac_current_info.temp; // 更新为空调设定温度
353 | ESP_LOGI(TAG, "Received Read for HEATING_THRESHOLD_TEMPERATURE: %d", ac_current_info.temp);
354 | *status_code = HAP_STATUS_SUCCESS;
355 | }
356 | /* 冷却温度门限 */
357 | else if (!strcmp(hap_char_get_type_uuid(hc), HAP_CHAR_UUID_COOLING_THRESHOLD_TEMPERATURE))
358 | {
359 | new_val.f = ac_current_info.temp; // 更新为空调设定温度
360 | ESP_LOGI(TAG, "Received Read for COOLING_THRESHOLD_TEMPERATURE: %d", ac_current_info.temp);
361 | *status_code = HAP_STATUS_SUCCESS;
362 | }
363 | /* 名字 */
364 | else if (!strcmp(hap_char_get_type_uuid(hc), HAP_CHAR_UUID_NAME))
365 | {
366 | *status_code = HAP_STATUS_SUCCESS;
367 | return HAP_SUCCESS;
368 | }
369 | /* 风速 */
370 | else if (!strcmp(hap_char_get_type_uuid(hc), HAP_CHAR_UUID_ROTATION_SPEED))
371 | {
372 | *status_code = HAP_STATUS_SUCCESS;
373 | return HAP_SUCCESS;
374 | }
375 | else
376 | {
377 | *status_code = HAP_STATUS_RES_ABSENT;
378 | }
379 |
380 | if (*status_code == HAP_STATUS_SUCCESS)
381 | {
382 | hap_char_update_val(hc, &new_val);
383 | } else {
384 | ret = HAP_FAIL;
385 | }
386 |
387 | return ret;
388 | }
389 |
390 | /*The main thread for handling the Air Conditioner Accessory */
391 | static void air_conditioner_thread_entry(void *arg)
392 | {
393 | hap_acc_t *accessory;
394 | hap_serv_t *service;
395 |
396 | /* Initialize the HAP core */
397 | hap_init(HAP_TRANSPORT_WIFI);
398 |
399 | /* Initialise the mandatory parameters for Accessory which will be added as
400 | * the mandatory services internally
401 | */
402 | hap_acc_cfg_t cfg = {
403 | .name = "AirConditioner_dormitory",
404 | .manufacturer = "DM",
405 | .model = "Esp32_01",
406 | .serial_num = "0000001",
407 | .fw_rev = "0.1.1",
408 | .hw_rev = "1.0",
409 | .pv = "1.1.0",
410 | .identify_routine = air_conditioner_identify,
411 | .cid = HAP_CID_AIR_CONDITIONER,
412 | };
413 |
414 | /* Create accessory object */
415 | accessory = hap_acc_create(&cfg);
416 | if (!accessory) {
417 | ESP_LOGE(TAG, "Failed to create accessory");
418 | goto air_conditioner_err;
419 | }
420 |
421 | /* Add a dummy Product Data */
422 | uint8_t product_data[] = {'E','S','P','3','2','H','A','P'};
423 | hap_acc_add_product_data(accessory, product_data, sizeof(product_data));
424 |
425 | /* Create the Air Conditioner Service. Include the "name" since this is a user visible service */
426 | service = hap_air_conditioner_create();
427 | if (!service) {
428 | ESP_LOGE(TAG, "Failed to create air_conditioner Service");
429 | goto air_conditioner_err;
430 | }
431 |
432 | /* Add the optional characteristic to the Air Conditioner Service */
433 | int ret = hap_serv_add_char(service, hap_char_name_create("air_conditioner_DM"));
434 | ret |= hap_serv_add_char(service, hap_char_rotation_speed_create(100.0)); // 添加转速
435 | if (ret != HAP_SUCCESS) {
436 | ESP_LOGE(TAG, "Failed to add optional characteristics to AirConditioner");
437 | goto air_conditioner_err;
438 | }
439 | /* Set the write callback for the service */
440 | hap_serv_set_write_cb(service, air_conditioner_write);
441 |
442 | hap_serv_set_read_cb(service, air_conditioner_read);
443 | /* Add the Air Conditioner Service to the Accessory Object */
444 | hap_acc_add_serv(accessory, service);
445 |
446 | #ifdef CONFIG_FIRMWARE_SERVICE
447 | /* Required for server verification during OTA, PEM format as string */
448 | static char server_cert[] = {};
449 | hap_fw_upgrade_config_t ota_config = {
450 | .server_cert_pem = server_cert,
451 | };
452 | /* Create and add the Firmware Upgrade Service, if enabled.
453 | * Please refer the FW Upgrade documentation under components/homekit/extras/include/hap_fw_upgrade.h
454 | * and the top level README for more information.
455 | */
456 | service = hap_serv_fw_upgrade_create(&ota_config);
457 | if (!service) {
458 | ESP_LOGE(TAG, "Failed to create Firmware Upgrade Service");
459 | goto air_conditioner_err;
460 | }
461 | hap_acc_add_serv(accessory, service);
462 | #endif
463 |
464 | /* Add the Accessory to the HomeKit Database */
465 | hap_add_accessory(accessory);
466 |
467 | /* Initialize the air conditioner Bulb Hardware */
468 | air_conditioner_init();
469 |
470 | /* Register a common button for reset Wi-Fi network and reset to factory.
471 | */
472 | reset_key_init(RESET_GPIO);
473 |
474 | /* TODO: Do the actual hardware initialization here */
475 |
476 | /* For production accessories, the setup code shouldn't be programmed on to
477 | * the device. Instead, the setup info, derived from the setup code must
478 | * be used. Use the factory_nvs_gen utility to generate this data and then
479 | * flash it into the factory NVS partition.
480 | *
481 | * By default, the setup ID and setup info will be read from the factory_nvs
482 | * Flash partition and so, is not required to set here explicitly.
483 | *
484 | * However, for testing purpose, this can be overridden by using hap_set_setup_code()
485 | * and hap_set_setup_id() APIs, as has been done here.
486 | */
487 | #ifdef CONFIG_EXAMPLE_USE_HARDCODED_SETUP_CODE
488 | /* Unique Setup code of the format xxx-xx-xxx. Default: 111-22-333 */
489 | hap_set_setup_code(CONFIG_EXAMPLE_SETUP_CODE);
490 | /* Unique four character Setup Id. Default: ES32 */
491 | hap_set_setup_id(CONFIG_EXAMPLE_SETUP_ID);
492 | #ifdef CONFIG_APP_WIFI_USE_WAC_PROVISIONING
493 | app_hap_setup_payload(CONFIG_EXAMPLE_SETUP_CODE, CONFIG_EXAMPLE_SETUP_ID, true, cfg.cid);
494 | #else
495 | app_hap_setup_payload(CONFIG_EXAMPLE_SETUP_CODE, CONFIG_EXAMPLE_SETUP_ID, false, cfg.cid);
496 | #endif
497 | #endif
498 |
499 | /* Enable Hardware MFi authentication (applicable only for MFi variant of SDK) */
500 | hap_enable_mfi_auth(HAP_MFI_AUTH_HW);
501 |
502 | /**
503 | * Configure dynamic frequency scaling:
504 | * maximum and minimum frequencies are set in sdkconfig,
505 | * automatic light sleep is enabled if tickless idle support is enabled.
506 | *
507 | */
508 | #if CONFIG_PM_ENABLE
509 | #if CONFIG_IDF_TARGET_ESP32
510 | esp_pm_config_esp32_t pm_config = {
511 | .max_freq_mhz = CONFIG_APP_MAX_CPU_FREQ_MHZ,
512 | .min_freq_mhz = CONFIG_APP_MIN_CPU_FREQ_MHZ,
513 | #if CONFIG_FREERTOS_USE_TICKLESS_IDLE
514 | .light_sleep_enable = true
515 | #endif
516 | };
517 | ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
518 | #endif
519 | #endif
520 |
521 | ESP_ERROR_CHECK(esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "rmt_send_task", &rmt_send_task_pm_lock)); // 创建锁
522 | /* Initialize Wi-Fi */
523 | app_wifi_init();
524 |
525 | /* After all the initializations are done, start the HAP core */
526 | hap_start();
527 | /* Start Wi-Fi */
528 | app_wifi_start(portMAX_DELAY);
529 |
530 | /* The task ends here. The read/write callbacks will be invoked by the HAP Framework */
531 | vTaskDelete(NULL);
532 |
533 | air_conditioner_err:
534 | hap_acc_delete(accessory);
535 | vTaskDelete(NULL);
536 | }
537 |
538 | /** 在write回调函数更新红外指令后的一段时间内,如果没有新的更新,才发送,否则知道没有新的更新为止再发送
539 | * 因为在家庭App进行模式切换时,会被调用两次write回调函数,为了防止发重复指令
540 | * 也防止在改变风速这一特征时,造成发送频率太高
541 | */
542 | static void air_conditioner_send_task(void *arg)
543 | {
544 | TickType_t nowTickCount = 0;
545 |
546 | while (1)
547 | {
548 | if (ac_send_r05d_code_flag == true)
549 | {
550 | nowTickCount = xTaskGetTickCount();
551 | if ((TickType_t)(nowTickCount - ac_send_tick_count) > pdMS_TO_TICKS(1500))
552 | {
553 | esp_pm_lock_acquire(rmt_send_task_pm_lock); // 获取锁
554 | ac_send_r05d_code(ac_current_info);
555 | ac_send_r05d_code_flag = false;
556 | esp_pm_lock_release(rmt_send_task_pm_lock); // 释放锁
557 | air_conditioner_send_task_handle = NULL; // 赋值为NULL 也就是删除自己
558 | vTaskDelete(air_conditioner_send_task_handle); // 发送一次后删除任务
559 |
560 | }
561 | }
562 | vTaskDelay(pdMS_TO_TICKS(100));
563 | }
564 |
565 | }
566 | #ifdef CONFIG_PM_PROFILING
567 | /**
568 | * 用来打印相关pm的信息
569 | */
570 | static void pm_track_task(void *arg)
571 | {
572 | while (1)
573 | {
574 | esp_pm_dump_locks(stdout);
575 | vTaskDelay(pdMS_TO_TICKS(5000));
576 | }
577 |
578 |
579 | }
580 | #endif
581 | void app_main()
582 | {
583 | xTaskCreate(air_conditioner_thread_entry, AIRCONDITIONER_TASK_NAME, AIRCONDITIONER_TASK_STACKSIZE,
584 | NULL, AIRCONDITIONER_TASK_PRIORITY, NULL);
585 | #ifdef CONFIG_PM_PROFILING
586 | xTaskCreate(pm_track_task, "pm_track_task", 2048, NULL, 0, NULL);
587 | #endif
588 | }
589 |
--------------------------------------------------------------------------------
/main/component.mk:
--------------------------------------------------------------------------------
1 | #
2 | # "main" pseudo-component makefile.
3 | #
4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
5 |
--------------------------------------------------------------------------------
/partitions_hap.csv:
--------------------------------------------------------------------------------
1 | # Name, Type, SubType, Offset, Size, Flags
2 | # Note: Firmware partition offset needs to be 64K aligned, initial 36K (9 sectors) are reserved for bootloader and partition table
3 | sec_cert, 0x3F, ,0xd000, 0x3000, , # Never mark this as an encrypted partition
4 | nvs, data, nvs, 0x10000, 0x6000,
5 | otadata, data, ota, , 0x2000
6 | phy_init, data, phy, , 0x1000,
7 | ota_0, app, ota_0, 0x20000, 1600K,
8 | ota_1, app, ota_1, , 1600K,
9 | factory_nvs, data, nvs, 0x340000, 0x6000
10 | nvs_keys, data, nvs_keys,0x346000, 0x1000
11 |
--------------------------------------------------------------------------------
/sdkconfig.defaults:
--------------------------------------------------------------------------------
1 | CONFIG_PARTITION_TABLE_CUSTOM=y
2 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_hap.csv"
3 | CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000
4 | CONFIG_FREERTOS_UNICORE=y
5 | CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=n
6 | CONFIG_FREERTOS_ASSERT_FAIL_ABORT=n
7 | CONFIG_FREERTOS_ASSERT_DISABLE=y
8 | CONFIG_LWIP_MAX_SOCKETS=16
9 | CONFIG_LWIP_SO_REUSE=y
10 | CONFIG_LWIP_MAX_ACTIVE_TCP=12
11 | CONFIG_LWIP_MAX_LISTENING_TCP=12
12 | CONFIG_LWIP_UDP_RECVMBOX_SIZE=10
13 | CONFIG_MBEDTLS_HARDWARE_MPI=y
14 | CONFIG_MBEDTLS_HARDWARE_SHA=n
15 | CONFIG_ESP_TASK_WDT=n
16 | CONFIG_LWIP_AUTOIP=y
17 | CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
18 | CONFIG_LWIP_AUTOIP_RATE_LIMIT_INTERVAL=60
19 | CONFIG_MP_BLOB_SUPPORT=y
20 | CONFIG_ENABLE_UNIFIED_PROVISIONING=y
21 |
--------------------------------------------------------------------------------