├── .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 | 13 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 | 14 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 | ![H0](/docs/images/H0.png) 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 | ![0](/docs/images/0.png) 116 | 117 | 3、F1打开输入ESP Device configuration 选择自己使用的设备。 118 | 119 | ![1](/docs/images/1.png) 120 | 121 | 4、选择端口,同样的F1后输入ESP Select port to use选择使用的端口,要先插上板子才会显示。 122 | 123 | ![2](/docs/images/2.png) 124 | 125 | 5、提前设置好wifi的名称和密码,点击左下角的插件中的齿轮图标,然后点击App Wi-Fi,选择使用 Use Hardcoded,填入wifi名称和密码,保存。 126 | 127 | ![3](/docs/images/3.png) 128 | ![4](/docs/images/4.png) 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 | ![5](/docs/images/5.png) 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 | ![6](/docs/images/6.png) 148 | 149 | 在监视的输出界面中,他会弹出个二维码,在iOS设备和ESP32在同一个wifi的情况下,使用家庭App扫描这个二维码就可以配对了,这样就不需要输出那个配对码11122333了 150 | 151 | 如果二维码扫描不了,但是又想扫的话,可以点击二维码下面的链接。 152 | 153 | ![7](/docs/images/7.png) 154 | 155 | 或者也可以选择“我没有或无法扫描代码”,手动输入配对码添加配件。 156 | 157 | 8 158 | 159 | 一般没有问题的话,他会直接找到这个配件,就像下面这样,点击后输入11122333就可以配对了。 160 | 161 | 9 162 | 163 | 没有MFI认证就会有这个弹窗,点击仍然添加就是啦 164 | 165 | 10 166 | 167 | 配对完成后,监视控制台应该也可看到相应的输出,按“Ctrl+]”可以退出监视器。 168 | 169 | ## 使用 170 | 171 | 12 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 | --------------------------------------------------------------------------------