├── LICENSE ├── OneWireESP32.cpp ├── OneWireESP32.h ├── README.md ├── examples └── ds18b20 │ └── ds18b20.ino └── library.properties /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 htmltiger https://github.com/junkfix/esp32-ds18b20 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OneWireESP32.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/junkfix/esp32-ds18b20 3 | */ 4 | 5 | #include 6 | #include "OneWireESP32.h" 7 | 8 | #define OWR_OK 0 9 | #define OWR_CRC 1 10 | #define OWR_BAD_DATA 2 11 | #define OWR_TIMEOUT 3 12 | #define OWR_DRIVER 4 13 | 14 | #define OW_RESET_PULSE 500 15 | #define OW_RESET_WAIT 200 16 | #define OW_RESET_PRESENCE_WAIT_MIN 15 17 | #define OW_RESET_PRESENCE_MIN 60 18 | #define OW_SLOT_BIT_SAMPLE_TIME 15 19 | #define OW_SLOT_START 2 20 | #define OW_SLOT_BIT 60 21 | #define OW_SLOT_RECOVERY 5 22 | #define OW_TIMEOUT 50 23 | 24 | 25 | static rmt_symbol_word_t ow_bit0 = { 26 | .duration0 = OW_SLOT_START + OW_SLOT_BIT, 27 | .level0 = 0, 28 | .duration1 = OW_SLOT_RECOVERY, 29 | .level1 = 1 30 | }; 31 | 32 | static rmt_symbol_word_t ow_bit1 = { 33 | .duration0 = OW_SLOT_START, 34 | .level0 = 0, 35 | .duration1 = OW_SLOT_BIT + OW_SLOT_RECOVERY, 36 | .level1 = 1 37 | }; 38 | 39 | const rmt_transmit_config_t owtxconf = { 40 | .loop_count = 0, 41 | .flags = { 42 | .eot_level = 1 43 | } 44 | }; 45 | const rmt_receive_config_t owrxconf = { 46 | .signal_range_min_ns = 1000, 47 | .signal_range_max_ns = (OW_RESET_PULSE + OW_RESET_WAIT) * 1000, 48 | }; 49 | 50 | 51 | OneWire32::OneWire32(uint8_t pin){ 52 | owpin = static_cast(pin); 53 | 54 | rmt_bytes_encoder_config_t bnc = { 55 | .bit0 = ow_bit0, 56 | .bit1 = ow_bit1, 57 | .flags = { 58 | .msb_first = 0 59 | } 60 | }; 61 | 62 | if(rmt_new_bytes_encoder(&bnc, &(owbenc)) != ESP_OK) { 63 | return; 64 | } 65 | 66 | rmt_copy_encoder_config_t cnc = {}; 67 | 68 | if(rmt_new_copy_encoder(&cnc, &(owcenc)) != ESP_OK) { 69 | return; 70 | } 71 | 72 | const rmt_rx_channel_config_t rxconf = { 73 | .gpio_num = owpin, 74 | .clk_src = RMT_CLK_SRC_DEFAULT, 75 | .resolution_hz = 1000000, 76 | .mem_block_symbols = MAX_BLOCKS 77 | }; 78 | 79 | if(rmt_new_rx_channel(&rxconf, &(owrx)) != ESP_OK) { 80 | return; 81 | } 82 | 83 | const rmt_tx_channel_config_t txconf = { 84 | .gpio_num = owpin, 85 | .clk_src = RMT_CLK_SRC_DEFAULT, 86 | .resolution_hz = 1000000, 87 | .mem_block_symbols = MAX_BLOCKS, 88 | .trans_queue_depth = 4, 89 | .flags = { 90 | .io_loop_back = 1, 91 | .io_od_mode = 1 92 | } 93 | }; 94 | 95 | if(rmt_new_tx_channel(&txconf, &owtx) != ESP_OK) { 96 | return; 97 | } 98 | 99 | owqueue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t)); 100 | if(owqueue == NULL) { 101 | return; 102 | } 103 | 104 | rmt_rx_event_callbacks_t rx_callbacks = { 105 | .on_recv_done = owrxdone 106 | }; 107 | 108 | if(rmt_rx_register_event_callbacks(owrx, &rx_callbacks, owqueue) != ESP_OK) { 109 | return; 110 | } 111 | 112 | if(rmt_enable(owrx) != ESP_OK) { 113 | return; 114 | } 115 | 116 | if(rmt_enable(owtx) != ESP_OK) { 117 | return; 118 | } 119 | 120 | static rmt_symbol_word_t release_symbol = { 121 | .duration0 = 1, 122 | .level0 = 1, 123 | .duration1 = 0, 124 | .level1 = 1, 125 | }; 126 | rmt_transmit(owtx, owcenc, &release_symbol, sizeof(rmt_symbol_word_t), &owtxconf); 127 | 128 | drv = 1; 129 | 130 | } 131 | 132 | 133 | OneWire32::~OneWire32(){ 134 | if(owbenc) { 135 | rmt_del_encoder(owbenc); 136 | } 137 | if(owcenc) { 138 | rmt_del_encoder(owcenc); 139 | } 140 | if(owrx) { 141 | rmt_disable(owrx); 142 | rmt_del_channel(owrx); 143 | } 144 | if(owtx) { 145 | rmt_disable(owtx); 146 | rmt_del_channel(owtx); 147 | } 148 | if(owqueue) { 149 | vQueueDelete(owqueue); 150 | } 151 | drv = 0; 152 | } 153 | 154 | 155 | bool owrxdone(rmt_channel_handle_t ch, const rmt_rx_done_event_data_t *edata, void *udata) { 156 | BaseType_t h = pdFALSE; 157 | xQueueSendFromISR((QueueHandle_t)udata, edata, &h); 158 | return (h == pdTRUE); 159 | } 160 | 161 | 162 | bool OneWire32::reset(){ 163 | 164 | rmt_symbol_word_t symbol_reset = { 165 | .duration0 = OW_RESET_PULSE, 166 | .level0 = 0, 167 | .duration1 = OW_RESET_WAIT, 168 | .level1 = 1 169 | }; 170 | 171 | rmt_rx_done_event_data_t evt; 172 | rmt_receive(owrx, owbuf, sizeof(owbuf), &owrxconf); 173 | rmt_transmit(owtx, owcenc, &symbol_reset, sizeof(rmt_symbol_word_t), &owtxconf); 174 | bool found = false; 175 | if(xQueueReceive(owqueue, &evt, pdMS_TO_TICKS(OW_TIMEOUT)) == pdTRUE) { 176 | size_t symbol_num = evt.num_symbols; 177 | rmt_symbol_word_t *symbols = evt.received_symbols; 178 | 179 | if (symbol_num > 1) { 180 | if (symbols[0].level1 == 1) { 181 | if (symbols[0].duration1 > OW_RESET_PRESENCE_WAIT_MIN && symbols[1].duration0 > OW_RESET_PRESENCE_MIN) { 182 | found = true; 183 | } 184 | } else { 185 | if (symbols[0].duration0 > OW_RESET_PRESENCE_WAIT_MIN && symbols[1].duration1 > OW_RESET_PRESENCE_MIN) { 186 | found = true; 187 | } 188 | } 189 | } 190 | if(rmt_tx_wait_all_done(owtx, OW_TIMEOUT) != ESP_OK) { 191 | found = false; 192 | } 193 | 194 | } 195 | return found; 196 | 197 | } 198 | 199 | 200 | bool OneWire32::read(uint8_t &data, uint8_t len){ 201 | 202 | rmt_rx_done_event_data_t evt; 203 | rmt_receive(owrx, owbuf, sizeof(owbuf), &owrxconf); 204 | 205 | if(!write((len > 1)? 0xff : 1, len) || xQueueReceive(owqueue, &evt, pdMS_TO_TICKS(OW_TIMEOUT)) != pdTRUE) { 206 | return false; 207 | } 208 | 209 | size_t symbol_num = evt.num_symbols; 210 | rmt_symbol_word_t *symbol = evt.received_symbols; 211 | data = 0; 212 | for (uint8_t i = 0; i < symbol_num && i < 8; i++) { 213 | if(!(symbol[i].duration0 > OW_SLOT_BIT_SAMPLE_TIME)){ 214 | data |= 1 << i; 215 | } 216 | } 217 | 218 | if(len != 8){ data = data & 0x01; } 219 | return true; 220 | } 221 | 222 | 223 | bool OneWire32::write(const uint8_t data, uint8_t len){ 224 | 225 | if(len < 8){ 226 | const rmt_symbol_word_t *sb; 227 | for(uint8_t i = 0; i < len; i++){ 228 | sb = &ow_bit0; 229 | if((data & (1 << i)) != 0) { 230 | sb = &ow_bit1; 231 | } 232 | if(rmt_transmit(owtx, owcenc, sb, sizeof(rmt_symbol_word_t), &owtxconf) != ESP_OK){ 233 | return false; 234 | } 235 | } 236 | }else{ 237 | if(rmt_transmit(owtx, owbenc, &data, 1, &owtxconf) != ESP_OK){ 238 | return false; 239 | } 240 | } 241 | 242 | return (rmt_tx_wait_all_done(owtx, OW_TIMEOUT) == ESP_OK); 243 | } 244 | 245 | 246 | void OneWire32::request(){ 247 | if(drv && reset()){ 248 | write(0xCC); 249 | write(0x44); 250 | } 251 | } 252 | 253 | const uint8_t crc_table[] = {0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53}; 254 | 255 | uint8_t OneWire32::getTemp(uint64_t &addr, float &temp){ 256 | uint8_t error = OWR_OK; 257 | if(!drv){return OWR_DRIVER;} 258 | if(reset()){ //connected 259 | write(0x55); 260 | uint8_t *a = (uint8_t *)&addr; 261 | for(uint8_t i = 0; i < 8; i++){ 262 | write(a[i]); 263 | } 264 | write(0xBE); // Read 265 | uint8_t data[9]; uint16_t zero = 0; uint8_t crc = 0; 266 | for(uint8_t j = 0; j < 9; j++){ 267 | if(!read(data[j],8)){data[j] = 0;} 268 | zero += data[j]; 269 | if(j < 8 ){crc = crc_table[crc ^ data[j]];} 270 | } 271 | if(zero != 0x8f7 && zero){ 272 | if(data[8] == crc ){ //CRC OK 273 | int16_t t = (data[1] << 8) | data[0]; 274 | temp = ((float)t / 16.0); 275 | }else{ 276 | error = OWR_CRC; 277 | } 278 | }else{ 279 | error = OWR_BAD_DATA; 280 | } 281 | }else{ 282 | error = OWR_TIMEOUT; 283 | } 284 | return error; 285 | } 286 | 287 | 288 | uint8_t OneWire32::search(uint64_t *addresses, uint8_t total) { 289 | int8_t last_src; 290 | int8_t last_dev = -1; 291 | uint8_t found = 0; 292 | uint8_t loop = 1; 293 | if(!drv){return found;} 294 | uint64_t addr = 0; 295 | while(loop && found < total){ 296 | loop = 0; 297 | last_src = last_dev; 298 | if(!reset()){ 299 | found = 0; 300 | break; 301 | } 302 | write(0xF0,8); 303 | for(uint8_t i = 0; i < 64; i += 1){ 304 | uint8_t bitA, bitB; uint64_t m = 1ULL << i; 305 | if(!read(bitA, 1) || !read(bitB, 1) || (bitA && bitB)){ 306 | addr = found = loop = 0; 307 | break; 308 | }else if(!bitA && !bitB){ 309 | if(i == last_src){ 310 | write(1, 1); addr |= m; 311 | }else{ 312 | if((addr & m) == 0 || i > last_src){ 313 | write(0, 1); loop = 1; addr &= ~m; 314 | last_dev = i; 315 | }else{ 316 | write(1, 1); 317 | } 318 | } 319 | }else{ 320 | if(bitA){ 321 | write(1, 1); addr |= m; 322 | }else{ 323 | write(0, 1); addr &= ~m; 324 | } 325 | } 326 | } 327 | if(addr){ 328 | addresses[found] = addr; 329 | found++; 330 | } 331 | } 332 | return found; 333 | } 334 | -------------------------------------------------------------------------------- /OneWireESP32.h: -------------------------------------------------------------------------------- 1 | /* 2 | https://github.com/junkfix/esp32-ds18b20 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include "freertos/FreeRTOS.h" 9 | #include "freertos/task.h" 10 | #include "freertos/queue.h" 11 | #include "driver/rmt_tx.h" 12 | #include "driver/rmt_rx.h" 13 | #include "sdkconfig.h" 14 | 15 | #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 16 | #define MAX_BLOCKS 64 17 | #else 18 | #define MAX_BLOCKS 48 19 | #endif 20 | 21 | IRAM_ATTR bool owrxdone(rmt_channel_handle_t ch, const rmt_rx_done_event_data_t *edata, void *udata); 22 | 23 | class OneWire32 { 24 | private: 25 | gpio_num_t owpin; 26 | rmt_channel_handle_t owtx; 27 | rmt_channel_handle_t owrx; 28 | rmt_encoder_handle_t owcenc; 29 | rmt_encoder_handle_t owbenc; 30 | rmt_symbol_word_t owbuf[MAX_BLOCKS]; 31 | QueueHandle_t owqueue; 32 | uint8_t drv = 0; 33 | public: 34 | OneWire32(uint8_t pin); 35 | ~OneWire32(); 36 | bool reset(); 37 | void request(); 38 | uint8_t getTemp(uint64_t &addr, float &temp); 39 | uint8_t search(uint64_t *addresses, uint8_t total); 40 | bool read(uint8_t &data, uint8_t len = 8); 41 | bool write(const uint8_t data, uint8_t len = 8); 42 | }; 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple 1-Wire Arduino library for the ESP32 2 | Arduino Library for ESP32 DS18B20 Maxim Integrated "1-Wire" protocol. 3 | 4 | Using the ESP32's RMT peripheral, results in very accurate read/write timeslots and more reliable operation. 5 | 6 | [Version 1.0.7](https://github.com/junkfix/esp32-ds18b20/releases/tag/1.0.7) is for Arduino 2.x based on ESP-IDF 4.x 7 | 8 | Version 2.0.x is For Arduino 3.0.0 based on ESP-IDF 5.x 9 | 10 | The example is provided and tested using Arduino 3.0.0 based on ESP-IDF 5.1.4 11 | 12 | --- 13 | 14 | Buy Me A Coffee 15 | -------------------------------------------------------------------------------- /examples/ds18b20/ds18b20.ino: -------------------------------------------------------------------------------- 1 | // https://github.com/junkfix/esp32-ds18b20 2 | 3 | #include "OneWireESP32.h" 4 | const uint8_t MaxDevs = 2; 5 | 6 | float currTemp[MaxDevs]; 7 | 8 | void tempTask(void *pvParameters){ 9 | OneWire32 ds(13); //gpio pin 10 | 11 | uint64_t addr[MaxDevs]; 12 | 13 | //uint64_t addr[] = { 14 | // 0x183c01f09506f428, 15 | // 0xf33c01e07683de28, 16 | //}; 17 | 18 | //to find addresses 19 | uint8_t devices = ds.search(addr, MaxDevs); 20 | for (uint8_t i = 0; i < devices; i += 1) { 21 | Serial.printf("%d: 0x%llx,\n", i, addr[i]); 22 | //char buf[20]; snprintf( buf, 20, "0x%llx,", addr[i] ); Serial.println(buf); 23 | } 24 | //end 25 | 26 | for(;;){ 27 | ds.request(); 28 | vTaskDelay(750 / portTICK_PERIOD_MS); 29 | for(byte i = 0; i < MaxDevs; i++){ 30 | uint8_t err = ds.getTemp(addr[i], currTemp[i]); 31 | if(err){ 32 | const char *errt[] = {"", "CRC", "BAD","DC","DRV"}; 33 | Serial.print(i); Serial.print(": "); Serial.println(errt[err]); 34 | }else{ 35 | Serial.print(i); Serial.print(": "); Serial.println(currTemp[i]); 36 | } 37 | } 38 | vTaskDelay(3000 / portTICK_PERIOD_MS); 39 | } 40 | } // tempTask 41 | 42 | void setup() { 43 | delay(1000); 44 | Serial.begin(115200); 45 | xTaskCreatePinnedToCore(tempTask, "tempTask", 2048, NULL, 1, NULL, 0); 46 | } 47 | 48 | 49 | void loop() {} 50 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=esp32-ds18b20 2 | version=2.0.2 3 | author=junkfix 4 | maintainer=junkfix 5 | sentence=Minimal, non-blocking, DS18B20 sensor library for ESP32 using RMT pheripheral, supports multiple sensors, lightweight, no dependencies, will need Arduino esp32 3.x based on IDF 5.X 6 | paragraph= 7 | category=Sensors 8 | url=https://github.com/junkfix/esp32-ds18b20 9 | architectures=esp32 10 | --------------------------------------------------------------------------------