commit 74d0876c5978f5f0b37430334dc03ed92af030b4 Author: Rafal Zajac Date: Sun Nov 12 22:49:10 2017 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..13bbe5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4a52fcf --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +# Bootstrap before call to project(). +include("$ENV{ESPROOT}/esp-cmake/ESP8266.bootstrap.cmake") +cmake_minimum_required(VERSION 3.5) + +project(esp_drv C) +set(CMAKE_C_STANDARD 99) + +# The directory containing global user_congih.h header file. +set(ESP_USER_CONFIG_DIR "${CMAKE_CURRENT_LIST_DIR}/examples/include") +set(ESP_USER_CONFIG "${ESP_USER_CONFIG_DIR}/user_config.h") +set(ESP_CMAKE_FIND_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake") +set(ESP_LIB_BIN_DIR "${CMAKE_CURRENT_LIST_DIR}/bin") + +# Magic. +include("${ESP_CMAKE_DIR}/ESP8266.cmake") + +# Main application. +# Must define esp_main executable target. +add_subdirectory(src) +add_subdirectory(examples) + +set(CPACK_GENERATOR "TGZ") +include(CPack) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..895657b --- /dev/null +++ b/LICENSE @@ -0,0 +1,174 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f8f5af --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +## ESP8266 drivers. + +The collection of device drivers for ESP8266. + +- [DS18B20](src/esp_ds18b20) OneWire temperature sensor. +- [DHT22 (AM2302)](src/esp_dht22) temperature and humidity sensor. +- [SHT21 (Si7020)](src/esp_sht21) temperature and humidity sensor. + +## Build environment. + +This library is part of my build system for ESP8266 based on CMake. +To compile / flash examples you will have to have the ESP development +environment setup as described at https://github.com/rzajac/esp-dev-env. + +## Development environment installation. + +There are two ways to install device drivers in the development environment: + +``` +$ wget -O - https://raw.githubusercontent.com/rzajac/esp-drv/master/install.sh | bash +``` + +or if you already cloned this repository you can do: + +``` +$ cd build +$ cmake .. +$ make +$ make install +``` + +## Examples. + +- [DHT22 get temperature and humidity](examples/dht22_temp_hum) +- [DS18B20 get temperature](examples/ds18b20_temp) +- [Search for DS18B20](examples/ds18b20_search) + +# Dependencies. + +This library depends on: + +- https://github.com/rzajac/esp-prot + +to install dependency run: + +``` +$ wget -O - https://raw.githubusercontent.com/rzajac/esp-prot/master/install.sh | bash +``` + +## License. + +[Apache License Version 2.0](LICENSE) unless stated otherwise. diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/doc/dht22-pinout.jpg b/doc/dht22-pinout.jpg new file mode 100644 index 0000000..c9de3e0 Binary files /dev/null and b/doc/dht22-pinout.jpg differ diff --git a/doc/ds18b20.jpg b/doc/ds18b20.jpg new file mode 100644 index 0000000..c8e240b Binary files /dev/null and b/doc/ds18b20.jpg differ diff --git a/doc/sht21.jpg b/doc/sht21.jpg new file mode 100644 index 0000000..38dd307 Binary files /dev/null and b/doc/sht21.jpg differ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..6bb3e6b --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +add_subdirectory(ds18b20_search) +add_subdirectory(ds18b20_temp) +add_subdirectory(dht22_temp_hum) +add_subdirectory(sht21_example) diff --git a/examples/dht22_temp_hum/CMakeLists.txt b/examples/dht22_temp_hum/CMakeLists.txt new file mode 100644 index 0000000..83789d0 --- /dev/null +++ b/examples/dht22_temp_hum/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +find_package(esp_sdo REQUIRED) +find_package(esp_util REQUIRED) + +add_executable(dht22_temp_hum main.c ${ESP_USER_CONFIG}) + +target_include_directories(dht22_temp_hum PUBLIC + ${ESP_USER_CONFIG_DIR} + ${esp_sdo_INCLUDE_DIRS} + ${esp_util_INCLUDE_DIRS}) + +target_link_libraries(dht22_temp_hum + ${esp_sdo_LIBRARIES} + ${esp_util_LIBRARIES} + esp_dht22) + +esp_gen_exec_targets(dht22_temp_hum) diff --git a/examples/dht22_temp_hum/README.md b/examples/dht22_temp_hum/README.md new file mode 100644 index 0000000..4d7b1c5 --- /dev/null +++ b/examples/dht22_temp_hum/README.md @@ -0,0 +1,12 @@ +## DHT22 example. + +Demonstrates how to get temperature and humidity from DHT22 sensor. + +## Flashing + +``` +$ cd build +$ cmake .. +$ make dht22_temp_hum_flash +$ miniterm.py /dev/ttyUSB0 74880 +``` diff --git a/examples/dht22_temp_hum/main.c b/examples/dht22_temp_hum/main.c new file mode 100644 index 0000000..b43aec7 --- /dev/null +++ b/examples/dht22_temp_hum/main.c @@ -0,0 +1,117 @@ +/* + * Copyright 2017 Rafal Zajac . + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include + +os_timer_t timer; + +/** + * Rise base to power of. + * + * From: http://bbs.espressif.com/viewtopic.php?t=246 + * + * @param base The number. + * @param exp The exponent. + * + * @return Product. + */ +static int ICACHE_FLASH_ATTR +power(int base, int exp) +{ + int result = 1; + while (exp) { + result *= base; + exp--; + } + + return result; +} + +/** + * Get string representation of float. + * + * From: http://bbs.espressif.com/viewtopic.php?t=246 + * + * Warning: limited to 15 chars & non-reentrant. + * e.g., don't use more than once per os_printf call. + * + * @param num The float to convert to string. + * @param decimals The number of decimal places. + * + * @return The float string representation. + */ +static char *ICACHE_FLASH_ATTR +ftoa(float num, uint8_t decimals) +{ + static char *buf[16]; + + int whole = (int) num; + int decimal = (int) ((num - whole) * power(10, decimals)); + if (decimal < 0) { + // get rid of sign on decimal portion + decimal -= 2 * decimal; + } + + char *pattern[10]; // setup printf pattern for decimal portion + os_sprintf((char *) pattern, "%%d.%%0%dd", decimals); + os_sprintf((char *) buf, (const char *) pattern, whole, decimal); + + return (char *) buf; +} + +void ICACHE_FLASH_ATTR +sys_init_done(void* arg) +{ + esp_dht22_err err; + esp_dht22_dev *dev = esp_dht22_new_dev(GPIO2); + + // Get temperature. + err = esp_dht22_get(dev); + if (err != ESP_DHT22_OK) { + os_free(dev); + os_printf("DHT22 error code: %d\n", err); + return; + } + + os_printf("Temp: %s\n", ftoa(dev->temp, 2)); + os_printf("Hum: %s\n", ftoa(dev->hum, 2)); + os_printf("--------------------\n"); + os_free(dev); +} + +void ICACHE_FLASH_ATTR +user_init() +{ + // No need for wifi for this example. + wifi_station_disconnect(); + wifi_set_opmode_current(NULL_MODE); + + //system_init_done_cb(sys_init_done); + stdout_init(BIT_RATE_74880); + + // Initialize DHT22 on GPIO 2. + esp_dht22_init(GPIO2); + + // Wait before running main code. + os_printf("Initialized.\n"); + os_timer_disarm(&timer); + os_timer_setfn(&timer, sys_init_done, NULL); + os_timer_arm(&timer, 1500, true); +} diff --git a/examples/ds18b20_search/CMakeLists.txt b/examples/ds18b20_search/CMakeLists.txt new file mode 100644 index 0000000..433b27f --- /dev/null +++ b/examples/ds18b20_search/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +find_package(esp_sdo REQUIRED) +find_package(esp_ow REQUIRED) +find_package(esp_eb REQUIRED) +find_package(esp_util REQUIRED) + +add_executable(ds18b20_search main.c ${ESP_USER_CONFIG}) + +target_include_directories(ds18b20_search PUBLIC + ${ESP_USER_CONFIG_DIR} + ${esp_sdo_INCLUDE_DIRS} + ${esp_ow_INCLUDE_DIRS} + ${esp_eb_INCLUDE_DIRS} + ${esp_util_INCLUDE_DIRS}) + +target_link_libraries(ds18b20_search + ${esp_sdo_LIBRARIES} + ${esp_ow_LIBRARIES} + ${esp_eb_LIBRARIES} + ${esp_util_LIBRARIES} + esp_ds18b20) + +esp_gen_exec_targets(ds18b20_search) diff --git a/examples/ds18b20_search/README.md b/examples/ds18b20_search/README.md new file mode 100644 index 0000000..f44edce --- /dev/null +++ b/examples/ds18b20_search/README.md @@ -0,0 +1,12 @@ +## Find DS18b20 devices. + +Demonstrates how to search OneWire bus for DS18b20. + +## Flashing + +``` +$ cd build +$ cmake .. +$ make ds18b20_search_flash +$ miniterm.py /dev/ttyUSB0 74880 +``` diff --git a/examples/ds18b20_search/main.c b/examples/ds18b20_search/main.c new file mode 100644 index 0000000..5d9b410 --- /dev/null +++ b/examples/ds18b20_search/main.c @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Rafal Zajac . + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include + +// List of found devices on the OneWire bus. +static esp_ow_device *root = NULL; + +static void ICACHE_FLASH_ATTR +sys_init_done() +{ + esp_ow_err err; + + if (esp_ds18b20_init(GPIO2) == false) { + os_printf("Error initializing DS18B20.\n"); + return; + } + + err = esp_ds18b20_search(GPIO2, false, &root); + if (err != ESP_OW_OK) { + os_printf("Search error: %d\n", err); + return; + } + + // Log found devices to serial. + esp_ow_dump_found(root); +} + +void ICACHE_FLASH_ATTR +user_init() +{ + // No need for wifi for this examples. + wifi_station_disconnect(); + wifi_set_opmode_current(NULL_MODE); + + stdout_init(BIT_RATE_74880); + system_init_done_cb(sys_init_done); +} diff --git a/examples/ds18b20_temp/CMakeLists.txt b/examples/ds18b20_temp/CMakeLists.txt new file mode 100644 index 0000000..51a7e15 --- /dev/null +++ b/examples/ds18b20_temp/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +find_package(esp_sdo REQUIRED) +find_package(esp_ow REQUIRED) +find_package(esp_eb REQUIRED) +find_package(esp_util REQUIRED) + +add_executable(ds18b20_temp main.c ${ESP_USER_CONFIG}) + +target_include_directories(ds18b20_temp PUBLIC + ${ESP_USER_CONFIG_DIR} + ${esp_sdo_INCLUDE_DIRS} + ${esp_ow_INCLUDE_DIRS} + ${esp_eb_INCLUDE_DIRS} + ${esp_util_INCLUDE_DIRS}) + +target_link_libraries(ds18b20_temp + ${esp_sdo_LIBRARIES} + ${esp_ow_LIBRARIES} + ${esp_eb_LIBRARIES} + ${esp_util_LIBRARIES} + esp_ds18b20) + +esp_gen_exec_targets(ds18b20_temp) diff --git a/examples/ds18b20_temp/README.md b/examples/ds18b20_temp/README.md new file mode 100644 index 0000000..591a2cb --- /dev/null +++ b/examples/ds18b20_temp/README.md @@ -0,0 +1,12 @@ +## Get temperature measured by DS18B20. + +Demonstrates how to search OneWire bus for DS18b20. + +## Flashing + +``` +$ cd build +$ cmake .. +$ make ds18b20_temp_flash +$ miniterm.py /dev/ttyUSB0 74880 +``` diff --git a/examples/ds18b20_temp/main.c b/examples/ds18b20_temp/main.c new file mode 100644 index 0000000..ab0e721 --- /dev/null +++ b/examples/ds18b20_temp/main.c @@ -0,0 +1,81 @@ +/* + * Copyright 2017 Rafal Zajac . + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + + +#include +#include +#include +#include +#include +#include +#include + +// List of found devices on the OneWire bus. +static esp_ow_device *root = NULL; + + +// Handle temperature conversion callbacks. +static void ICACHE_FLASH_ATTR +temperature(const char *event, void *arg) +{ + esp_ow_device *dev = arg; + esp_ds18b20_st *st = dev->custom; + + if (st->last_temp == ESP_DS18B20_TEMP_ERR) { + os_printf("Temperature read error.\n"); + } else { + os_printf("Temperature: %s\n", esp_util_ftoa(st->last_temp, 4)); + } +} + +static void ICACHE_FLASH_ATTR +sys_init_done() +{ + esp_ow_err err; + + if (esp_ds18b20_init(GPIO2) == false) { + os_printf("Error initializing DS18B20.\n"); + return; + } + + err = esp_ds18b20_search(GPIO2, false, &root); + if (err != ESP_OW_OK) { + os_printf("Search error: %d\n", err); + return; + } + + if (root == NULL) { + os_printf("No devices found.\n"); + } + + // Callbacks will be called when conversion is finished. + esp_eb_attach(ESP_DS18B20_EV_TEMP_READY, temperature); + esp_eb_attach(ESP_DS18B20_EV_TEMP_ERROR, temperature); + + // Get temperature from the first DS18B20 sensor. + esp_ds18b20_convert(root); +} + +void ICACHE_FLASH_ATTR +user_init() +{ + // No need for wifi for this examples. + wifi_station_disconnect(); + wifi_set_opmode_current(NULL_MODE); + + stdout_init(BIT_RATE_74880); + system_init_done_cb(sys_init_done); +} diff --git a/examples/include/user_config.h b/examples/include/user_config.h new file mode 100644 index 0000000..780488c --- /dev/null +++ b/examples/include/user_config.h @@ -0,0 +1 @@ +// This is only here to make the API headers happy. diff --git a/examples/sht21_example/CMakeLists.txt b/examples/sht21_example/CMakeLists.txt new file mode 100644 index 0000000..d7d86bf --- /dev/null +++ b/examples/sht21_example/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +find_package(esp_sdo REQUIRED) +find_package(esp_i2c REQUIRED) +find_package(esp_util REQUIRED) + +add_executable(sht21_example main.c ${ESP_USER_CONFIG}) + +target_include_directories(sht21_example PUBLIC + ${ESP_USER_CONFIG_DIR} + ${esp_sdo_INCLUDE_DIRS} + ${esp_i2c_INCLUDE_DIRS} + ${esp_util_INCLUDE_DIRS}) + +target_link_libraries(sht21_example + ${esp_sdo_LIBRARIES} + ${esp_i2c_LIBRARIES} + ${esp_util_LIBRARIES} + esp_sht21) + +esp_gen_exec_targets(sht21_example) diff --git a/examples/sht21_example/README.md b/examples/sht21_example/README.md new file mode 100644 index 0000000..7644cca --- /dev/null +++ b/examples/sht21_example/README.md @@ -0,0 +1,15 @@ +## SHT21 example. + +Demonstrates how to: +- get serial number, +- get firmware version, +- get humidity and temperature. + +## Flashing + +``` +$ cd build +$ cmake .. +$ make sht21_example_flash +$ miniterm.py /dev/ttyUSB0 74880 +``` diff --git a/examples/sht21_example/main.c b/examples/sht21_example/main.c new file mode 100644 index 0000000..1b6d538 --- /dev/null +++ b/examples/sht21_example/main.c @@ -0,0 +1,131 @@ +/* + * Copyright 2017 Rafal Zajac . + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#define SCL GPIO0 +#define SDA GPIO2 + +os_timer_t timer; + +/** + * Rise base to power of. + * + * From: http://bbs.espressif.com/viewtopic.php?t=246 + * + * @param base The number. + * @param exp The exponent. + * + * @return Product. + */ +static int ICACHE_FLASH_ATTR +power(int base, int exp) +{ + int result = 1; + while (exp) { + result *= base; + exp--; + } + + return result; +} + +/** + * Get string representation of float. + * + * From: http://bbs.espressif.com/viewtopic.php?t=246 + * + * Warning: limited to 15 chars & non-reentrant. + * e.g., don't use more than once per os_printf call. + * + * @param num The float to convert to string. + * @param decimals The number of decimal places. + * + * @return The float string representation. + */ +static char *ICACHE_FLASH_ATTR +ftoa(float num, uint8_t decimals) +{ + static char *buf[16]; + + int whole = (int) num; + int decimal = (int) ((num - whole) * power(10, decimals)); + if (decimal < 0) { + // get rid of sign on decimal portion + decimal -= 2 * decimal; + } + + char *pattern[10]; // setup printf pattern for decimal portion + os_sprintf((char *) pattern, "%%d.%%0%dd", decimals); + os_sprintf((char *) buf, (const char *) pattern, whole, decimal); + + return (char *) buf; +} + +void ICACHE_FLASH_ATTR +run_sht21() +{ + uint8_t rev; + float value; + uint8_t sn[8]; + esp_i2c_err err; + + err = esp_sht21_init(SCL, SDA); + if (err != ESP_I2C_OK) { + os_printf("SHT21 init error.\n"); + return; + } + + err = esp_sht21_get_sn(sn); + if (err != ESP_I2C_OK) os_printf("SHT21 sn error %d.\n", err); + + os_printf("SHT21 SN: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", + sn[0], sn[1], sn[2], sn[3], + sn[4], sn[5], sn[6], sn[7]); + + err = esp_sht21_get_rev(&rev); + if (err != ESP_I2C_OK) os_printf("SHT21 sn error %d.\n", err); + + os_printf("Firmware rev: 0x%02X\n", rev); + + err = esp_sht21_get_rh(&value); + if (err != ESP_I2C_OK) os_printf("Get RH error: %d\n", err); + + os_printf("Humidity: %s%%\n", ftoa(value, 2)); + + err = esp_sht21_get_temp_last(&value); + if (err != ESP_I2C_OK) os_printf("Get TEMP error: %d\n", err); + + os_printf("Temperature: %s deg. C\n", ftoa(value, 2)); +} + +void ICACHE_FLASH_ATTR +user_init() +{ + // We don't need WiFi for this example. + wifi_station_disconnect(); + wifi_set_opmode(NULL_MODE); + + stdout_init(BIT_RATE_74880); + os_printf("Starting...\n"); + + os_timer_disarm(&timer); + os_timer_setfn(&timer, (os_timer_func_t *) run_sht21, NULL); + os_timer_arm(&timer, 1500, false); +} diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..b7cf6ad --- /dev/null +++ b/install.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# ESP8266 library installation script. +# +# Installer expects that: +# - The ESPROOT environment variable set. +# - The esp-open-sdk is installed and compiled at ESPROOT/esp-open-sdk. + +# The full GitHub URL to the library. +LIB_FULL_NAME="rzajac/esp-drv" + +# No modifications below this comment unless you know what you're doing. + +TMP_DIR=`mktemp -d` +LIB_REPO="https://github.com/${LIB_FULL_NAME}" +LIB_NAME=${LIB_FULL_NAME##*/} +CMAKE=`which cmake` + +# Remove temporary directory. +function rm_tmp() { + echo "Removing ${TMP_DIR}" + rm -rf ${TMP_DIR} +} + +# Check / set ESPROOT. +if [ "${ESPROOT}" == "" ]; then ESPROOT=$HOME/esproot; fi +if ! [ -d "${ESPROOT}" ]; then mkdir -p ${ESPROOT}; fi +echo "Using ${ESPROOT} as ESPROOT" + +echo "Cloning ${LIB_REPO} to temporary directory ${TMP_DIR}." +git clone ${LIB_REPO} ${TMP_DIR} +if [ $? != 0 ]; then + echo "Error: Cloning ${LIB_REPO} failed!" + rm_tmp + exit 1 +fi + +echo "Installing library ${LIB_NAME} to ${ESPROOT}" +mkdir ${TMP_DIR}/auto-build +(cd ${TMP_DIR}/auto-build && ${CMAKE} .. && make install) +if [ $? != 0 ]; then + echo "Error: Installing library ${LIB_NAME} failed!" + rm_tmp + exit 1 +fi + +rm_tmp + +exit 0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..aae6ab3 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +add_subdirectory(esp_ds18b20) +add_subdirectory(esp_dht22) +add_subdirectory(esp_sht21) diff --git a/src/esp_dht22/CMakeLists.txt b/src/esp_dht22/CMakeLists.txt new file mode 100644 index 0000000..bf16851 --- /dev/null +++ b/src/esp_dht22/CMakeLists.txt @@ -0,0 +1,32 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +project(esp_dht22 C) + +find_package(esp_gpio REQUIRED) + +add_library(esp_dht22 STATIC + esp_dht22.c + include/esp_dht22.h) + +target_include_directories(esp_dht22 PUBLIC + $ + $ + ${esp_gpio_INCLUDE_DIRS} + ${ESP_USER_CONFIG_DIR}) + +esp_gen_lib(esp_dht22 + ${ESP_CMAKE_FIND_DIR} + ${esp_gpio_LIBRARIES}) diff --git a/src/esp_dht22/Findesp_dht22.cmake b/src/esp_dht22/Findesp_dht22.cmake new file mode 100644 index 0000000..e601124 --- /dev/null +++ b/src/esp_dht22/Findesp_dht22.cmake @@ -0,0 +1,46 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# Try to find esp_dht22 +# +# Once done this will define: +# +# esp_dht22_FOUND - System found the library. +# esp_dht22_INCLUDE_DIR - The library include directory. +# esp_dht22_INCLUDE_DIRS - If library has dependencies this will be set +# to _INCLUDE_DIR [, ...]. +# esp_dht22_LIBRARY - The path to the library. +# esp_dht22_LIBRARIES - The dependencies to link to use the library. +# It will have a form of _LIBRARY [dep1_name_LIBRARIES, ...]. +# + + +find_path(esp_dht22_INCLUDE_DIR esp_dht22.h) +find_library(esp_dht22_LIBRARY NAMES esp_dht22) + +find_package(esp_gpio REQUIRED) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(esp_dht22 + DEFAULT_MSG + esp_dht22_LIBRARY + esp_dht22_INCLUDE_DIR) + +set(esp_dht22_INCLUDE_DIRS + ${esp_dht22_INCLUDE_DIR} + ${esp_gpio_INCLUDE_DIRS}) + +set(esp_dht22_LIBRARIES + ${esp_dht22_LIBRARY} + ${esp_gpio_LIBRARIES}) diff --git a/src/esp_dht22/README.md b/src/esp_dht22/README.md new file mode 100644 index 0000000..ca2d183 --- /dev/null +++ b/src/esp_dht22/README.md @@ -0,0 +1,13 @@ +## DHT22 (AM2302) driver for ESP8266. + +ESP8266 driver for very popular DHT22 (AM2302) temperature and humidity sensor. + +![DHT22](../../doc/dht22-pinout.jpg) + +Before you can get the temperature and humidity from DHT22 you have to call +`esp_dht22_init`. You need to call it only once unless you change the GPIO +pin setup somewhere else in your code. + + +See driver documentation in [esp_dht22.h](include/esp_dht22.h) header file +for more details. diff --git a/src/esp_dht22/esp_dht22.c b/src/esp_dht22/esp_dht22.c new file mode 100644 index 0000000..352ebb3 --- /dev/null +++ b/src/esp_dht22/esp_dht22.c @@ -0,0 +1,178 @@ +/* + * Copyright 2017 Rafal Zajac . + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include + +#define BUS_LOW(gpio_num) (GPIO_OUT_EN_S = (0x1 << (gpio_num))) +#define BUS_HIGH(gpio_num) (GPIO_OUT_EN_C = (0x1 << (gpio_num))) +#define BUS_RELEASE(gpio_num) (GPIO_OUT_EN_C = (0x1 << (gpio_num))) +#define BUS_READ(gpio_num) ((GPIO_IN & (0x1 << (gpio_num))) != 0) + +/** + * Measure number of 10us slots the state was kept. + * + * The maximum wait time for GPIO to change state is 500us. + * + * @param gpio_num The GPIO number. + * @param exp_state The state. + * + * @return The number time slots. + */ +static uint8_t ICACHE_FLASH_ATTR +state_lenght(uint8_t gpio_num, bool exp_state) +{ + uint8_t slot = 0; + + while (BUS_READ(gpio_num) == exp_state) { + if (slot++ > 50) break; + os_delay_us(8); + } + + return slot; +} + +/** + * Calculate temperature. + * + * @param data The pointer to 5 bytes read from the bus. + * + * @return Temperature in Celsius. + */ +static float calc_temp(const uint8_t *data) +{ + // Remove negative temp indicator bit. + float temp = data[2] & 0x7F; + temp *= 0x100; + temp += data[3]; + temp /= 10; + if ((data[2] & 0x80) > 0) temp *= -1; + + return temp; +} + +void ICACHE_FLASH_ATTR +esp_dht22_init(uint8_t gpio_num) +{ + esp_gpio_setup(gpio_num, GPIO_MODE_INPUT_PULLUP); +} + +esp_dht22_dev *ICACHE_FLASH_ATTR +esp_dht22_new_dev(uint8_t gpio_num) +{ + esp_dht22_dev *dev = os_zalloc(sizeof(esp_dht22_dev)); + if (dev == NULL) return NULL; + + dev->gpio_num = gpio_num; + + return dev; +} + +esp_dht22_err ICACHE_FLASH_ATTR +esp_dht22_get(esp_dht22_dev *device) +{ + uint16_t cnt = 0; + // The humidity, temperature and parity data. + uint8_t *data; + // Current byte index in data (0-4). + int8_t data_byte_idx = 0; + // Current mask for data byte. We start with MSB. + uint8_t data_byte_mask = 0x80; + // The bit from the bus. + bool bit; + // Previous bit. + bool prev_bit = false; + // Number of high states. + uint8_t high_cnt = 0; + + if (device == NULL) return ESP_DHT22_ERR_DEV_NULL; + + data = os_zalloc(5); + if (data == NULL) return ESP_DHT22_ERR_MEM; + + // Emmit start signal. + BUS_LOW(device->gpio_num); + os_delay_us(820); + + // End start signal. + BUS_RELEASE(device->gpio_num); + os_delay_us(25); + + // Entering time critical code. + ETS_GPIO_INTR_DISABLE(); + + // Device responds with 80us low followed by 80us high. + cnt = state_lenght(device->gpio_num, 0); + if (cnt < 8 || cnt > 10) { + ETS_GPIO_INTR_ENABLE(); + return ESP_DHT22_ERR_BAD_RESP_SIGNAL; + } + + cnt = state_lenght(device->gpio_num, 1); + if (cnt < 8 || cnt > 10) { + ETS_GPIO_INTR_ENABLE(); + return ESP_DHT22_ERR_BAD_RESP_SIGNAL; + } + + // Sample data bus for 40 bits. + // + // Device transmits + // - 0 as 50us low followed by 25us high (total 75us). + // - 1 as 50us low followed by 70us high (total 120us). + // + // Data is transmitted MSB first. + // + // This means 40 bits transfer takes between 3000us and 4800us. + // The code below samples the bus no more then 4800us (10us * 480). + // During that time we count falling edges and count number of + // high states seen since last falling edge. The number of seen + // high states tells us if this was o or 1. + do { + bit = BUS_READ(device->gpio_num); + if (bit) high_cnt++; + if (bit == false && prev_bit == true) { + if (high_cnt >= 6) data[data_byte_idx] |= data_byte_mask; + high_cnt = 0; + data_byte_mask >>= 1; + if (data_byte_mask == 0) { + data_byte_idx++; + data_byte_mask = 0x80; + } + } + prev_bit = bit; + cnt++; + os_delay_us(10); + } while (data_byte_idx < 5 && cnt <= 480); + + ETS_GPIO_INTR_ENABLE(); + + device->last_measure = system_get_time(); + + if (((uint8_t) (data[0] + data[1] + data[2] + data[3])) != data[4]) { + return ESP_DHT22_ERR_PARITY; + } + + device->temp = calc_temp(data); + device->hum = data[0] * 0x100; + device->hum += data[1]; + device->hum /= 10; + + //os_free(data); + return ESP_DHT22_OK; +} + diff --git a/src/esp_dht22/include/esp_dht22.h b/src/esp_dht22/include/esp_dht22.h new file mode 100644 index 0000000..83cd057 --- /dev/null +++ b/src/esp_dht22/include/esp_dht22.h @@ -0,0 +1,75 @@ +/* + * Copyright 2017 Rafal Zajac . + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#ifndef ESP_DHT22_H +#define ESP_DHT22_H + +#include + +// Structure representing DHT22 device. +typedef struct { + float hum; // Humidity. + float temp; // Temperature in Celsius. + uint8_t gpio_num; // The GPIO this device is connected to. + uint32_t last_measure; // Last measure time. Uses system_get_time(). +} esp_dht22_dev; + +// Error codes. +typedef enum { + ESP_DHT22_OK, + ESP_DHT22_ERR_MEM, + ESP_DHT22_ERR_DEV_NULL, + ESP_DHT22_ERR_BAD_RESP_SIGNAL, + ESP_DHT22_ERR_PARITY, +} esp_dht22_err; + +// Espressif SDK missing includes. +void ets_isr_mask(unsigned intr); +void ets_isr_unmask(unsigned intr); + +/** + * Initialize DHT22. + * + * @param gpio_num The GPIO number DHT22 is connected to. + */ +void ICACHE_FLASH_ATTR +esp_dht22_init(uint8_t gpio_num); + +/** + * Get DHT22 structure. + * + * It's up to a caller to release the memory at some point. + * + * @param gpio_num The GPIO number connected to the data bus. + * + * @return Device. + */ +esp_dht22_dev *ICACHE_FLASH_ATTR +esp_dht22_new_dev(uint8_t gpio_num); + +/** + * Get temperature and humidity. + * + * NOTE: You must keep calls to this function at least 2s apart. + * + * @param device The device structure to set values on. + * + * @return Error code. + */ +esp_dht22_err ICACHE_FLASH_ATTR +esp_dht22_get(esp_dht22_dev *device); + +#endif //ESP_DHT22_H diff --git a/src/esp_ds18b20/CMakeLists.txt b/src/esp_ds18b20/CMakeLists.txt new file mode 100644 index 0000000..f4bfcc0 --- /dev/null +++ b/src/esp_ds18b20/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +project(esp_ds18b20 C) + +find_package(esp_ow REQUIRED) +find_package(esp_eb REQUIRED) +find_package(esp_tim REQUIRED) + +add_library(esp_ds18b20 STATIC + esp_ds18b20.c + include/esp_ds18b20.h) + +target_include_directories(esp_ds18b20 PUBLIC + $ + $ + ${esp_ow_INCLUDE_DIRS} + ${esp_eb_INCLUDE_DIRS} + ${esp_tim_INCLUDE_DIRS} + ${ESP_USER_CONFIG_DIR}) + +esp_gen_lib(esp_ds18b20 + ${ESP_CMAKE_FIND_DIR} + ${esp_ow_LIBRARIES} + ${esp_eb_LIBRARIES} + ${esp_tim_LIBRARIES}) diff --git a/src/esp_ds18b20/Findesp_ds18b20.cmake b/src/esp_ds18b20/Findesp_ds18b20.cmake new file mode 100644 index 0000000..bcfeb80 --- /dev/null +++ b/src/esp_ds18b20/Findesp_ds18b20.cmake @@ -0,0 +1,52 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# Try to find esp_ds18b20 +# +# Once done this will define: +# +# esp_ds18b20_FOUND - System found the library. +# esp_ds18b20_INCLUDE_DIR - The library include directory. +# esp_ds18b20_INCLUDE_DIRS - If library has dependencies this will be set +# to _INCLUDE_DIR [, ...]. +# esp_ds18b20_LIBRARY - The path to the library. +# esp_ds18b20_LIBRARIES - The dependencies to link to use the library. +# It will have a form of _LIBRARY [dep1_name_LIBRARIES, ...]. +# + + +find_path(esp_ds18b20_INCLUDE_DIR esp_ds18b20.h) +find_library(esp_ds18b20_LIBRARY NAMES esp_ds18b20) + +find_package(esp_ow REQUIRED) +find_package(esp_eb REQUIRED) +find_package(esp_tim REQUIRED) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(esp_ds18b20 + DEFAULT_MSG + esp_ds18b20_LIBRARY + esp_ds18b20_INCLUDE_DIR) + +set(esp_ds18b20_INCLUDE_DIRS + ${esp_ds18b20_INCLUDE_DIR} + ${esp_ow_INCLUDE_DIRS} + ${esp_eb_INCLUDE_DIRS} + ${esp_tim_INCLUDE_DIRS}) + +set(esp_ds18b20_LIBRARIES + ${esp_ds18b20_LIBRARY} + ${esp_ow_LIBRARIES} + ${esp_eb_LIBRARIES} + ${esp_tim_LIBRARIES}) diff --git a/src/esp_ds18b20/README.md b/src/esp_ds18b20/README.md new file mode 100644 index 0000000..81f1332 --- /dev/null +++ b/src/esp_ds18b20/README.md @@ -0,0 +1,45 @@ +## DS18B20 driver for ESP8266. + +Full featured DS18B20 digital thermometer driver. + +![DS18B20](../../doc/ds18b20.jpg) + +Before you can get the temperature from DS18B20 you have to call +`esp_ds18b20_init`. You need to call it only once unless you change the GPIO +pin setup somewhere else in your code. + +To find device(s) on the OneWire bus you can use `esp_ds18b20_search` which +will give you linked list of `esp_ow_device` devices. + +It's up to the user to release memory allocated for the linked list with +library provided helper function `esp_ds18b20_free_list` when it's not needed +anymore. + +Most of the operations with the DS18B20 library is based on passing pointers +to the `esp_ow_device` structure which has pointer to `esp_ds18b20_st` +status structure. The `esp_ds18b20_st` keeps track of last scratch pad read +the last temperature conversion. + +``` +esp_ow_device *device; +esp_ds18b20_st *st = device->custom; +``` + +If you already know the ROM address of your device you can use +`esp_ds18b20_new_dev`: + +``` +uint8_t rom[8] = {0x28, 0x1D, 0x39, 0x31, 0x2, 0x0, 0x0, 0xF0} +esp_ow_dev *device = esp_ds18b20_new_dev(rom); + +``` + +With DS18B20 temperature measurement takes between 94 and 750ms depending +on resolution. You don't want to block the CPU for that long waiting for +example using some kind of delay. That's why library is using event bus +(esp_eb) to emmit events when the temperature conversion is ready to read. +Check [example program](../../examples/ds18b20_temp) to see how it should be +done. + +See driver documentation in [esp_ds18b20.h](include/esp_ds18b20.h) header file +for more details. diff --git a/src/esp_ds18b20/esp_ds18b20.c b/src/esp_ds18b20/esp_ds18b20.c new file mode 100644 index 0000000..8746511 --- /dev/null +++ b/src/esp_ds18b20/esp_ds18b20.c @@ -0,0 +1,305 @@ +/* + * Copyright 2017 Rafal Zajac . + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + + +#include +#include +#include +#include + + +/** + * Decode temperature. + * + * @param sp The address for first scrachpad byte. + * + * @return The temperature. + */ +float ICACHE_FLASH_ATTR +decode_temp(const uint8_t *sp) +{ + bool minus; + uint8_t integer = 0; + float decimal = 0; + + // Get only bits responsible for temperature and sign. + int16_t temp = sp[0] | (sp[1] << 8); + minus = (temp & 0x8000) > 0; + + // Detect negative number. + if (minus) { + temp = (int16_t) (~temp + 1); // 2's complement. + } + + // Remove fraction. + integer = (uint8_t) (temp >> 4); + // take only 3 LSB form MSB. + integer |= ((temp >> 8) & 0x07) << 4; + + // Calculate fraction accounting for resolution. + switch ((sp[4] & 0x60) >> 5) { + case ESP_DS18B20_RES_12: + decimal = (temp & 0x0F) * ((float) ESP_DS18B20_STEP_12); + break; + + case ESP_DS18B20_RES_11: + decimal = ((temp >> 1) & 0x07) * ((float) ESP_DS18B20_STEP_11); + break; + + case ESP_DS18B20_RES_10: + decimal = ((temp >> 2) & 0x03) * ((float) ESP_DS18B20_STEP_10); + break; + + case ESP_DS18B20_RES_9: + decimal = ((temp >> 3) & 0x01) * ((float) ESP_DS18B20_STEP_9); + break; + + default: + decimal = 0; + } + + decimal = integer + decimal; + if (minus) decimal = 0 - decimal; + + return decimal; +} + +esp_ow_err ICACHE_FLASH_ATTR +esp_d18b20_read_sp(esp_ow_device *device) +{ + uint8_t idx; + uint8_t crc = 0; + + esp_ds18b20_st *st = device->custom; + + if (esp_ow_reset(device->gpio_num) == false) { + st->retries = -1; + return ESP_OW_ERR_NO_DEV; + } + + esp_ow_match_dev(device); + esp_ow_write(device->gpio_num, ESP_DS18B20_CMD_READ_SP); + esp_ow_read_bytes(device->gpio_num, st->sp, 9); + + for (idx = 0; idx < 9; idx++) { + crc = esp_ow_crc8(crc, st->sp[idx]); + } + + if (crc != 0) { + memset(st->sp, 0, 9); + return ESP_OW_ERR_BAD_CRC; + } + + return ESP_OW_OK; +} + +esp_ow_err ICACHE_FLASH_ATTR +esp_ds18b20_write_sp(esp_ow_device *device) +{ + uint8_t *start; + esp_ds18b20_st *st = device->custom; + + if (esp_ow_reset(device->gpio_num) == false) { + return ESP_OW_ERR_NO_DEV; + } + + esp_ow_match_dev(device); + esp_ow_write(device->gpio_num, ESP_DS18B20_CMD_WRITE_SP); + + // We transmit only 3 bytes (Th, Tl, cfg). + start = &st->sp[2]; + + esp_ow_write_bytes(device->gpio_num, start, 3); + + return ESP_OW_OK; +} + +esp_ow_err ICACHE_FLASH_ATTR +read_temp(esp_ow_device *device) +{ + esp_ds18b20_st *st = device->custom; + esp_ow_err err = esp_d18b20_read_sp(device); + + st->retries = -1; + if (err != ESP_OW_OK) return err; + + st->last_temp = decode_temp(st->sp); + + return ESP_OW_OK; +} + +static void ICACHE_FLASH_ATTR +start_conversion(void *arg) +{ + esp_tim_timer *timer = arg; + + bool done = false; + esp_ow_device *dev = timer->payload; + esp_ds18b20_st *st = dev->custom; + uint32_t sample_count = 200; + + st->retries++; + + do { + if (esp_ow_read_bit(dev->gpio_num)) { + done = true; + break; + } + os_delay_us(5); + sample_count--; + } while (sample_count > 0); + + if (done == true) { + if (read_temp(dev) != ESP_OW_OK) { + esp_eb_trigger(ESP_DS18B20_EV_TEMP_ERROR, dev); + } else { + esp_eb_trigger(ESP_DS18B20_EV_TEMP_READY, dev); + } + } else { + // The worst case from datasheet is 750ms when + // 12 bits of resolution is set. + // Each call to this function takes about 200*5us (1ms). + // If temperature is not available we call ourselves again + // after 10ms. That gives 11ms per call. 68 calls * 11ms = 748ms. + // This condition basically makes sure we are not + // calling ourselves forever. + if (st->retries > 68) { + st->retries = -1; + esp_eb_trigger(ESP_DS18B20_EV_TEMP_ERROR, dev); + } else { + // Try again. + esp_tim_continue(timer); + } + } +} + +bool ICACHE_FLASH_ATTR +esp_ds18b20_init(uint8_t gpio_num) +{ + esp_ow_init(gpio_num); + + return true; +} + +esp_ow_err ICACHE_FLASH_ATTR +esp_ds18b20_search(uint8_t gpio_num, bool in_alert, esp_ow_device **list) +{ + esp_ow_device *curr; + + esp_ow_cmd cmd = in_alert ? ESP_OW_CMD_SEARCH_ROM_ALERT : ESP_OW_CMD_SEARCH_ROM; + esp_ow_err err = esp_ow_search_family(gpio_num, cmd, ESP_DS18B20_FAMILY_CODE, list); + if (err != ESP_OW_OK) return err; + + curr = *list; + while (curr) { + curr->custom = os_zalloc(sizeof(esp_ds18b20_st)); + ((esp_ds18b20_st *) curr->custom)->last_temp = ESP_DS18B20_TEMP_ERR; + ((esp_ds18b20_st *) curr->custom)->retries = -1; + curr = curr->next; + } + + return err; +} + +esp_ow_device *ICACHE_FLASH_ATTR +esp_ds18b20_new_dev(uint8_t *rom) +{ + esp_ow_device *device = os_zalloc(sizeof(esp_ow_device)); + esp_ds18b20_st *st = os_zalloc(sizeof(esp_ds18b20_st)); + + os_memcpy(device->rom, rom, 8); + st->last_temp = ESP_DS18B20_TEMP_ERR; + st->retries = -1; + device->custom = st; + + return device; +} + + +esp_ow_device *ICACHE_FLASH_ATTR +esp_ds18b20_get(uint8_t gpio_num) +{ + return esp_ow_read_rom_dev(gpio_num); +} + +esp_ow_err ICACHE_FLASH_ATTR +esp_ds18b20_get_alarm(esp_ow_device *dev, int8_t *low, int8_t *high) +{ + esp_ds18b20_st *st = dev->custom; + esp_ow_err err = esp_d18b20_read_sp(dev); + if (err != ESP_OW_OK) return err; + + *low = st->sp[3]; + *high = st->sp[2]; + + return ESP_OW_OK; +} + +esp_ow_err ICACHE_FLASH_ATTR +esp_ds18b20_set_alarm(esp_ow_device *dev, int8_t low, int8_t high) +{ + esp_ds18b20_st *st = dev->custom; + esp_ow_err err = esp_d18b20_read_sp(dev); + if (err != ESP_OW_OK) return err; + + st->sp[3] = low; + st->sp[2] = high; + + return esp_ds18b20_write_sp(dev); +} + +void ICACHE_FLASH_ATTR +esp_ds18b20_free_list(esp_ow_device *list) +{ + esp_ow_free_device_list(list, true); +} + +esp_ds18b20_err ICACHE_FLASH_ATTR +esp_ds18b20_convert(esp_ow_device *device) +{ + esp_ds18b20_st *st = device->custom; + + // We are already waiting for the conversion. + if (st->retries >= 0) return ESP_DS18B20_ERR_CONV_IN_PROG; + + if (esp_ow_reset(device->gpio_num) == false) return ESP_DS18B20_NO_DEV; + + // Send conversion command. + esp_ow_match_dev(device); + esp_ow_write(device->gpio_num, ESP_DS18B20_CMD_CONVERT); + + if (esp_tim_start(start_conversion, device)) { + st->retries = 0; + } + + return ESP_DS18B20_OK; +} + +bool ICACHE_FLASH_ATTR +esp_ds18b20_has_parasite(uint8_t gpio_num) +{ + bool has_parasite; + + if (esp_ow_reset(gpio_num) == false) { + return false; // No devices. + } + + esp_ow_write(gpio_num, ESP_OW_CMD_SKIP_ROM); + esp_ow_write(gpio_num, ESP_DS18B20_CMD_READ_PWR); + has_parasite = !esp_ow_read_bit(gpio_num); + + return has_parasite; +} diff --git a/src/esp_ds18b20/include/esp_ds18b20.h b/src/esp_ds18b20/include/esp_ds18b20.h new file mode 100644 index 0000000..f5318e7 --- /dev/null +++ b/src/esp_ds18b20/include/esp_ds18b20.h @@ -0,0 +1,187 @@ +/* + * Copyright 2017 Rafal Zajac . + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#ifndef ESP_DS18B20_H +#define ESP_DS18B20_H + +#include +#include + +// The DS18B20 family code from datasheet. +#define ESP_DS18B20_FAMILY_CODE 0x28 + +// The absolute zero temperature is returned as error. +#define ESP_DS18B20_TEMP_ERR (-273) + +// Temperature conversion ready. +#define ESP_DS18B20_EV_TEMP_READY "ds18b20tReady" +// Temperature conversion error. +#define ESP_DS18B20_EV_TEMP_ERROR "ds18b20tError" + +// Temperature resolutions. +#define ESP_DS18B20_RES_9 0x0 +#define ESP_DS18B20_RES_10 0x1 +#define ESP_DS18B20_RES_11 0x2 +#define ESP_DS18B20_RES_12 0x3 + +// Temperature steps. +#define ESP_DS18B20_STEP_9 0.5 +#define ESP_DS18B20_STEP_10 0.25 +#define ESP_DS18B20_STEP_11 0.125 +#define ESP_DS18B20_STEP_12 0.0625 + +// OneWire commands. +typedef enum { + ESP_DS18B20_CMD_READ_PWR = 0xB4, + ESP_DS18B20_CMD_CONVERT = 0x44, + ESP_DS18B20_CMD_READ_SP = 0xBE, + ESP_DS18B20_CMD_WRITE_SP = 0x4E, +} esp_ds18b20_cmd; + +typedef enum { + ESP_DS18B20_OK, + ESP_DS18B20_NO_DEV, + ESP_DS18B20_ERR_CONV_IN_PROG, // Conversion in progress. +} esp_ds18b20_err; + +// DS18B20 status. +typedef struct { + uint8_t sp[9]; + int8_t retries; // Is greater then zero when conversion in progress. + float last_temp; // Last successful temperature read. +} esp_ds18b20_st; + + +/** + * Initialize OneWire bus where DS18B20 is. + * + * To use many OneWire buses you have to initialize all of them. + * + * @param gpio_num The GPIO where OneWire bus is connected. + * + * @return Returns true on success, false otherwise. + */ +bool ICACHE_FLASH_ATTR +esp_ds18b20_init(uint8_t gpio_num); + +/** + * Find devices on OneWire bus. + * + * @param in_alert Find only devices in alert mode. + * @param list The list of found devices or NULL. + * + * @return OneWire error code. + */ +esp_ow_err ICACHE_FLASH_ATTR +esp_ds18b20_search(uint8_t gpio_num, bool in_alert, esp_ow_device **list); + +/** + * Construct DS18B20 device. + * + * It's up to a caller to release the memory at some point. + * + * @param rom The pointer to 8 byte ROM address + * + * @return The device. + */ +esp_ow_device *ICACHE_FLASH_ATTR +esp_ds18b20_new_dev(uint8_t *rom); + +/** + * Get the only device on the bus. + * + * Can be used only when there is only one device on the OneWire bus! + * + * @param gpio_num The GPIO where OneWire bus is connected. + * + * @return The OneWire device. On error ROM address has all zeros. + */ +esp_ow_device *ICACHE_FLASH_ATTR +esp_ds18b20_get(uint8_t gpio_num); + +/** + * Get DS18B20 alarm thresholds. + * + * @param dev The device to get alarm thresholds for. + * @param low The low threshold in Celsius. + * @param high The high threshold in Celsius. + * + * @return OneWire error code. + */ +esp_ow_err ICACHE_FLASH_ATTR +esp_ds18b20_get_alarm(esp_ow_device *dev, int8_t *low, int8_t *high); + +/** + * Set DS18B20 alarm thresholds. + * + * @param dev The device to get alarm thresholds for. + * @param low The low threshold in Celsius. + * @param high The high threshold in Celsius. + * + * @return OneWire error code. + */ +esp_ow_err ICACHE_FLASH_ATTR +esp_ds18b20_set_alarm(esp_ow_device *dev, int8_t low, int8_t high); + +/** + * Read scrachpad. + * + * @param device The device to read scratchpad for. + * + * @return OneWire error code. + */ +esp_ow_err ICACHE_FLASH_ATTR +esp_d18b20_read_sp(esp_ow_device *device); + +/** + * Write scratchpad to the device. + * + * @param device The device to write scratchpad to. + * + * @return OneWire error code. + */ +esp_ow_err ICACHE_FLASH_ATTR +esp_ds18b20_write_sp(esp_ow_device *device); + +/** + * Free memory allocated by devices found on OneWire bus. + * + * @param list + */ +void ICACHE_FLASH_ATTR +esp_ds18b20_free_list(esp_ow_device *list); + +/** + * Start temperature conversion. + * + * @param device The device to start conversion on. + * + * @return Error code. + */ +esp_ds18b20_err ICACHE_FLASH_ATTR +esp_ds18b20_convert(esp_ow_device *device); + +/** + * Check if OneWire bus has device with parasite power supply. + * + * @param gpio_num The GPIO where OneWire bus is connected. + * + * @return Returns true if parasite present, false otherwise. + */ +bool ICACHE_FLASH_ATTR +esp_ds18b20_has_parasite(uint8_t gpio_num); + +#endif //ESP_DS18B20_H diff --git a/src/esp_sht21/CMakeLists.txt b/src/esp_sht21/CMakeLists.txt new file mode 100644 index 0000000..8985324 --- /dev/null +++ b/src/esp_sht21/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +project(esp_sht21 C) + +find_package(esp_i2c REQUIRED) + +add_library(esp_sht21 STATIC + esp_sht21.c + include/esp_sht21.h) + +target_include_directories(esp_sht21 PUBLIC + $ + $ + ${esp_i2c_INCLUDE_DIRS} + ${ESP_USER_CONFIG_DIR}) + +esp_gen_lib(esp_sht21 + ${ESP_CMAKE_FIND_DIR} + ${esp_i2c_LIBRARIES} + ${esp_tim_LIBRARIES}) diff --git a/src/esp_sht21/Findesp_sht21.cmake b/src/esp_sht21/Findesp_sht21.cmake new file mode 100644 index 0000000..988d30a --- /dev/null +++ b/src/esp_sht21/Findesp_sht21.cmake @@ -0,0 +1,46 @@ +# Copyright 2017 Rafal Zajac . +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# Try to find esp_sht21 +# +# Once done this will define: +# +# esp_sht21_FOUND - System found the library. +# esp_sht21_INCLUDE_DIR - The library include directory. +# esp_sht21_INCLUDE_DIRS - If library has dependencies this will be set +# to _INCLUDE_DIR [, ...]. +# esp_sht21_LIBRARY - The path to the library. +# esp_sht21_LIBRARIES - The dependencies to link to use the library. +# It will have a form of _LIBRARY [dep1_name_LIBRARIES, ...]. +# + + +find_path(esp_sht21_INCLUDE_DIR esp_sht21.h) +find_library(esp_sht21_LIBRARY NAMES esp_sht21) + +find_package(esp_i2c REQUIRED) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(esp_sht21 + DEFAULT_MSG + esp_sht21_LIBRARY + esp_sht21_INCLUDE_DIR) + +set(esp_sht21_INCLUDE_DIRS + ${esp_sht21_INCLUDE_DIR} + ${esp_i2c_INCLUDE_DIRS}) + +set(esp_sht21_LIBRARIES + ${esp_sht21_LIBRARY} + ${esp_i2c_LIBRARIES}) diff --git a/src/esp_sht21/README.md b/src/esp_sht21/README.md new file mode 100644 index 0000000..e782677 --- /dev/null +++ b/src/esp_sht21/README.md @@ -0,0 +1,18 @@ +## SHT21 driver for ESP8266. + +SHT21 a.k.a Si7021 driver for ESP8266. Features: + +![DS18B20](../../doc/sht21.jpg) + +- Humidity and temperature measurements. +- Get/set humidity and temperature measurement resolution. +- Get SHT21 serial number. +- Get SHT21 firmware revision. +- Turn on on-board heater to drive off condensation. + +Before you can start communicating with SHT21 you have to call +`esp_sht21_init`. You need to call it only once unless you change the GPIO +pins setup somewhere else in your code. + +See driver documentation in [esp_sht21.h](include/esp_sht21.h) header file +for more details. diff --git a/src/esp_sht21/esp_sht21.c b/src/esp_sht21/esp_sht21.c new file mode 100644 index 0000000..783b87d --- /dev/null +++ b/src/esp_sht21/esp_sht21.c @@ -0,0 +1,324 @@ +/* + * Copyright 2017 Rafal Zajac . + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#include +#include + +static uint8_t ICACHE_FLASH_ATTR +calc_crc(uint8_t init, const uint8_t *data, uint8_t len) +{ + uint8_t idx; + uint8_t bit; + + // Calculates 8 bit checksum with polynomial 0x131 (10011001). + for (idx = 0; idx < len; idx++) { + init ^= (data[idx]); + for (bit = 0; bit < 8; bit++) { + if (init & 0x80) init = (uint8_t) ((init << 1) ^ 0x131); + else init = (init << 1); + } + } + + return init; +} + +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_init(uint8_t gpio_scl, uint8_t gpio_sda) +{ + return esp_i2c_init(gpio_scl, gpio_sda); +} + +static float ICACHE_FLASH_ATTR +calc_rh(const uint8_t *data) +{ + float hu; + hu = (float) (125.0 / 65536.0); + hu = hu * (((data[0] << 8) | data[1]) & ~0x3); + + return hu - 6; +} + +static float ICACHE_FLASH_ATTR +calc_temp(const uint8_t *data) +{ + float temp; + temp = (float) (175.72 / 65536.0); + temp = temp * (((data[0] << 8) | data[1]) & ~0x3); + + return (float) (temp - 46.85); +} + +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_get_rh(float *humidity) +{ + esp_i2c_err err; + uint8_t data[3]; + + *humidity = ESP_SHT21_BAD_RH; + + err = esp_i2c_start_read(ESP_SHT21_ADDRESS, ESP_SHT21_RH_HM); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_read_bytes(data, 3); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_stop(); + + if (calc_crc(0x0, data, 2) == data[2]) { + *humidity = calc_rh(data); + } else if (err == ESP_I2C_OK) { + err = ESP_I2C_ERR_DATA_CORRUPTED; + } + + return err; +} + +/** + * Get temperature. + * + * @param temp The temperature. + * @param cmd The temperature command. + * + * @return The I2C error code. + */ +static esp_i2c_err ICACHE_FLASH_ATTR +get_temp(float *temp, uint8_t cmd) +{ + esp_i2c_err err; + uint8_t data[3]; + + // When getting temperature from previous humidity measurement + // the CRC checksum is not available. + uint8_t data_len = (uint8_t) (cmd == ESP_SHT21_TEMP_HM ? 3 : 2); + + *temp = ESP_SHT21_BAD_TEMP; + + err = esp_i2c_start_read(ESP_SHT21_ADDRESS, cmd); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_read_bytes(data, data_len); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_stop(); + + if (data_len == 2 || calc_crc(0x0, data, 2) == data[2]) { + *temp = calc_temp(data); + } else if (err == ESP_I2C_OK) { + err = ESP_I2C_ERR_DATA_CORRUPTED; + } + + return err; +} + +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_get_temp(float *temp) +{ + return get_temp(temp, ESP_SHT21_TEMP_HM); +} + +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_get_temp_last(float *temp) +{ + return get_temp(temp, ESP_SHT21_TEMP_LAST); +} + +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_get_sn(uint8_t *sn) +{ + uint8_t idx; + esp_i2c_err err; + uint8_t sn_idx = 0; + uint8_t crc; + uint8_t cmd1[2] = {0xFA, 0x0F}; + uint8_t cmd2[2] = {0xFC, 0xC9}; + uint8_t data[14]; + + err = esp_i2c_start_read_write(ESP_I2C_ADDR_WRITE(ESP_SHT21_ADDRESS), true); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_write_bytes(cmd1, 2); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_start_read_write(ESP_I2C_ADDR_READ(ESP_SHT21_ADDRESS), true); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_read_bytes(data, 8); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_stop(); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_start_read_write(ESP_I2C_ADDR_WRITE(ESP_SHT21_ADDRESS), true); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_write_bytes(cmd2, 2); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_start_read_write(ESP_I2C_ADDR_READ(ESP_SHT21_ADDRESS), true); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_read_bytes((data + 8), 6); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_stop(); + + // Validate data. + crc = 0x0; + for (idx = 0; idx < 8; idx += 2) { + crc = calc_crc(crc, &data[idx], 1); + if (data[idx + 1] != crc) { + return ESP_I2C_ERR_DATA_CORRUPTED; + } + sn[sn_idx++] = data[idx]; + } + + crc = 0x0; + for (idx = 8; idx < 14; idx += 3) { + crc = calc_crc(crc, &data[idx], 2); + if (data[idx + 2] != crc) { + return ESP_I2C_ERR_DATA_CORRUPTED; + } + sn[sn_idx++] = data[idx]; + sn[sn_idx++] = data[idx + 1]; + } + + return err; +} + +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_get_rev(uint8_t *rev) +{ + esp_i2c_err err; + uint8_t cmd[2] = {0x84, 0xB8}; + + err = esp_i2c_start_read_write(ESP_I2C_ADDR_WRITE(ESP_SHT21_ADDRESS), true); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_write_bytes(cmd, 2); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_start_read_write(ESP_I2C_ADDR_READ(ESP_SHT21_ADDRESS), true); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_read_bytes(rev, 1); + if (err != ESP_I2C_OK) return err; + + return esp_i2c_stop(); +} + +static esp_i2c_err ICACHE_FLASH_ATTR +register_get(uint8_t address, uint8_t reg_adr, uint8_t *reg) +{ + esp_i2c_err err; + + // Read the register. + err = esp_i2c_start_read(address, reg_adr); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_read_bytes(reg, 1); + if (err != ESP_I2C_OK) return err; + + return esp_i2c_stop(); +} + +static esp_i2c_err ICACHE_FLASH_ATTR +register_set(uint8_t address, uint8_t reg_adr, uint8_t value) +{ + esp_i2c_err err; + + err = esp_i2c_start_write(address, reg_adr); + if (err != ESP_I2C_OK) return err; + + err = esp_i2c_write_bytes(&value, 1); + if (err != ESP_I2C_OK) return err; + + return esp_i2c_stop(); +} + +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_res_get(uint8_t *res) +{ + esp_i2c_err err; + uint8_t reg1 = 0; + + err = register_get(ESP_SHT21_ADDRESS, ESP_SHT21_UR1_READ, ®1); + if (err != ESP_I2C_OK) return err; + + *res = (uint8_t) (((reg1 >> 6) & 0x2) | (reg1 & 0x1)); + + return ESP_I2C_OK; +} + +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_res_set(uint8_t res) +{ + esp_i2c_err err; + uint8_t reg1 = 0; + + err = register_get(ESP_SHT21_ADDRESS, ESP_SHT21_UR1_READ, ®1); + if (err != ESP_I2C_OK) return err; + + // Clear bits 7 and 0 + reg1 = (uint8_t) (reg1 & 0x7E); + // Logical or after some bit manipulation. + reg1 |= ((((res & 0x3) != 0) << 7) | ((res & 0x1) != 0)); + + return register_set(ESP_SHT21_ADDRESS, ESP_SHT21_UR1_WRITE, reg1); +} + +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_heater_get(bool *on_off, uint8_t *level) +{ + esp_i2c_err err; + uint8_t reg1 = 0; + uint8_t hcr = 0; + + err = register_get(ESP_SHT21_ADDRESS, ESP_SHT21_UR1_READ, ®1); + if (err != ESP_I2C_OK) return err; + err = register_get(ESP_SHT21_ADDRESS, ESP_SHT21_HCR_READ, &hcr); + if (err != ESP_I2C_OK) return err; + + *on_off = (reg1 & 0x4) != 0; + *level = (uint8_t) (hcr & 0xF); + + return ESP_I2C_OK; +} + +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_heater_set(bool on_off, uint8_t level) +{ + esp_i2c_err err; + uint8_t reg1 = 0; + uint8_t hcr = 0; + + // Read both registers. + err = register_get(ESP_SHT21_ADDRESS, ESP_SHT21_UR1_READ, ®1); + if (err != ESP_I2C_OK) return err; + err = register_get(ESP_SHT21_ADDRESS, ESP_SHT21_HCR_READ, &hcr); + if (err != ESP_I2C_OK) return err; + + // Write new values. + reg1 = (uint8_t) (reg1 & 0xFB); + reg1 = reg1 | (on_off << 2); + err = register_set(ESP_SHT21_ADDRESS, ESP_SHT21_UR1_WRITE, reg1); + if (err != ESP_I2C_OK) return err; + + hcr = (uint8_t) ((hcr & 0xF0) | (level & 0x0F)); + err = register_set(ESP_SHT21_ADDRESS, ESP_SHT21_HCR_WRITE, 0x0); + if (err != ESP_I2C_OK) return err; + + return register_set(ESP_SHT21_ADDRESS, ESP_SHT21_HCR_WRITE, hcr); +} diff --git a/src/esp_sht21/include/esp_sht21.h b/src/esp_sht21/include/esp_sht21.h new file mode 100644 index 0000000..d842323 --- /dev/null +++ b/src/esp_sht21/include/esp_sht21.h @@ -0,0 +1,168 @@ +/* + * Copyright 2017 Rafal Zajac . + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + + +#ifndef ESP_SHT21_H +#define ESP_SHT21_H + +#include +#include + +#define ESP_SHT21_ADDRESS 0x40 +// Measure relative humidity. Hold Master Mode. +#define ESP_SHT21_RH_HM 0xE5 +// Measure relative humidity. No Hold Master Mode. +#define ESP_SHT21_RH_NHM 0xF5 +// Measure temperature. Hold Master Mode. +#define ESP_SHT21_TEMP_HM 0xE3 +// Measure temperature. No Hold Master Mode. +#define ESP_SHT21_TEMP_NHM 0xF3 +// Read temperature from previous RH measurement. +#define ESP_SHT21_TEMP_LAST 0xE0 +// Read User Register 1. +#define ESP_SHT21_UR1_READ 0xE7 +// Write User Register 1. +#define ESP_SHT21_UR1_WRITE 0xE6 +// Read Heater Control Register +#define ESP_SHT21_HCR_READ 0x11 +// Write Heater Control Register +#define ESP_SHT21_HCR_WRITE 0x51 + +// Invalid humidity. +#define ESP_SHT21_BAD_RH ((float)-1.00) +// Invalid temperature. +#define ESP_SHT21_BAD_TEMP ((float)-273) + +// SHT21 humidity and temperature resolutions. +// RH: 12bit TEMP: 14bit +#define ESP_SHT21_RES3 0x0 +// RH: 8bit TEMP: 12bit +#define ESP_SHT21_RES2 0x1 +// RH: 10bit TEMP: 13bit +#define ESP_SHT21_RES1 0x2 +// RH: 11bit TEMP: 11bit +#define ESP_SHT21_RES0 0x3 + +/** + * Initialize SHT21. + * + * @param gpio_scl The GPIO pin used for clock. + * @param gpio_sda The GPIO pin used for data. + * + * @return The I2C error code. + */ +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_init(uint8_t gpio_scl, uint8_t gpio_sda); + +/** + * Measure humidity. + * + * It takes approximately around 20ms to return the value. + * + * @return The I2C error code. + */ +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_get_rh(float *humidity); + +/** + * Measure temperature. + * + * This function slow. Faster method is to use esp_sht21_get_temp_last + * function which will return temperature value from previous + * humidity measurement. + * + * @param temp The measured temperature. + * + * @return The I2C error code. + */ +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_get_temp(float *temp); + +/** + * Get temperature from previous humidity measurement. + * + * This function is faster then esp_sht21_get_temp. + * + * @param temp The measured temperature. + * + * @return The I2C error code. + */ +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_get_temp_last(float *temp); + +/** + * Get 64 bit unique SHT21 serial number. + * + * @param sn The pointer to 8 byte array. + * + * @return The I2C error code. + */ +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_get_sn(uint8_t *sn); + +/** + * Get firmware revision. + * + * @param rev The revision to set. + * + * @return The I2C error code. + */ +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_get_rev(uint8_t *rev); + +/** + * Get humidity and temperature measurement resolution. + * + * @param res The measurement resolution. One of the ESP_SHT21_RES* defines. + * + * @return The I2C error code. + */ +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_res_get(uint8_t *res); + +/** + * Set SHT21 humidity and temperature measurement resolution. + * + * @param res The resolution. One of the ESP_SHT21_RES* defines. + * + * @return The I2C error code. + */ +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_res_set(uint8_t res); + +/** + * Get on-board heater status. + * + * @param on_off The on/off status. + * @param level The heater level value 0-15. + * + * @return The I2C error code. + */ +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_heater_get(bool *on_off, uint8_t *level); + +/** + * Set on-board heater status. + * + * @param on_off The on/off status. + * @param level The heater level value 0-15. + * + * @return The I2C error code. + */ +esp_i2c_err ICACHE_FLASH_ATTR +esp_sht21_heater_set(bool on_off, uint8_t level); + +#endif //ESP_SHT21_H