Chapter 213: Dallas 1-Wire Sensor Networks

Chapter Objectives

By the end of this chapter, you will be able to:

  • Implement the 1-Wire Search ROM algorithm to discover all devices on the bus.
  • Differentiate between various 1-Wire devices using their unique Family Code.
  • Write ESP-IDF code to manage and communicate with multiple sensors concurrently.
  • Read from and write to 1-Wire EEPROM memory devices like the DS2431.
  • Design a robust application that handles a mixed network of sensors and memory chips.
  • Understand and mitigate the physical limitations of larger 1-Wire networks.

Introduction

In the previous chapter, we successfully communicated with a single DS18B20 temperature sensor. We treated the 1-Wire bus as a simple point-to-point connection. The true power of the 1-Wire protocol, however, lies in its networking capability—the ability to connect numerous devices to a single bus line. This opens up applications from monitoring temperature gradients across a large space to tagging and identifying objects with unique electronic labels.

This chapter elevates our understanding from a single slave to a multi-device network. We will dissect the ingenious Search ROM algorithm, the procedure a master uses to discover the unique 64-bit address of every device present. We will then learn to act on this information, polling multiple temperature sensors and even storing data on a 1-Wire EEPROM. By the end, you will be able to build sophisticated sensor and data-logging networks using just one GPIO pin on your ESP32.

Theory

1. The Challenge of a Multi-Drop Bus

When the master sends a reset pulse, all slaves respond with a presence pulse. This is great for detecting if anyone is listening. But what happens next? If the master tries to read a ROM code, all slaves would try to transmit their unique codes at the same time, resulting in a garbled mess on the wire.

To solve this, the 1-Wire protocol defines a special command, SEARCH ROM (0xF0), which initiates a clever discovery process that functions like a binary search.

2. The Search ROM Algorithm

The Search ROM algorithm allows the master to identify one slave device at a time, even with dozens on the bus. It works by essentially navigating a binary tree where each node represents a bit in the 64-bit ROM address.

Here is a simplified overview of the process for finding one device:

  1. Start Search: The master issues a SEARCH ROM command.
  2. Bit-by-Bit Discovery: For each of the 64 bits in the ROM address (from LSB to MSB):a. All slaves still participating in the search send the current bit of their ROM code.b. Simultaneously, they send the inverse of that bit.c. The master performs two consecutive “read bit” operations to see what the slaves sent.
  3. Interpreting the Response: The master analyzes the two bits it read:
    • 11: No devices responded. This indicates an error or the end of the search.
    • 01: All responding slaves have a 0 at this bit position. The master knows this bit of the address is 0.
    • 10: All responding slaves have a 1 at this bit position. The master knows this bit of the address is 1.
    • 00: A discrepancy. Some slaves sent 0 and others sent 1. The bus is pulled low for both reads. This is the key to the search.
  4. Resolving Discrepancies: When a discrepancy (00) occurs, the master must choose a path. It selects one value (e.g., 0), sends that value back out onto the bus, and records the bit position of this “fork in the road.”
  5. Pruning the Tree: After the master sends its chosen bit, only the slaves whose ROM codes match that bit at that position will continue to participate in the rest of the 64-bit search. The others go silent and wait for the next reset pulse.
  6. Completion: After 64 bits, the master has successfully identified the full ROM code of one device.

To find the next device, the master starts a new search, but it follows the same path as before until it reaches the last discrepancy point it recorded. This time, it chooses the other path (e.g., 1) and proceeds from there. This process is repeated until no more devices are found.

%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans'}}}%%
graph TD
    subgraph "Search ROM Process for One Device"
    A("Start Search") --> B{"Issue SEARCH ROM<br>(0xF0)"};
    B --> C{For each of 64 bits...};
    
    subgraph "Bit and i"
        C --> D(Master performs<br>two 'read bit' ops);
        D --> E{"Read [Bit, Comp-Bit]"};
    end

    E --> F{"<font size=5><b>?</b></font><br>Response"};
    F -- "<b>10</b><br>(All Slaves have <b>1</b>)" --> G(Master records '1');
    F -- "<b>01</b><br>(All Slaves have <b>0</b>)" --> H(Master records '0');
    F -- "<b>00</b><br>(Discrepancy)" --> I{Conflict!<br>Choose a path};
    F -- "<b>11</b><br>(No response)" --> J(End of Search);

    I --> K(Master sends chosen bit<br>e.g., '0');
    K --> L(Record discrepancy location);
    L --> M(Only matching slaves continue);
    
    G --> N{Next bit};
    H --> N;
    M --> N;

    N -- "Bits < 64" --> C;
    N -- "All 64 bits done" --> O(Full ROM Code Found);
    end

    O --> P(Start new search);
    P --> Q{"Follow path to last<br>discrepancy"};
    Q --> R{"Choose alternate path<br>(e.g. 1)"};
    R --> C;

    %% Styling
    classDef startNode fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6;
    classDef processNode fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF;
    classDef decisionNode fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E;
    classDef endNode fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46;
    classDef checkNode fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B;

    class A,P startNode;
    class B,C,D,G,H,K,L,M,N,Q,R processNode;
    class E,F,I decisionNode;
    class J,O endNode;
    class C,sub_Bit_i checkNode

3. Device Identification with Family Codes

Once a 64-bit ROM code is discovered, its first byte is the Family Code. This code tells the master what kind of device it is.

Family Code Device Description
0x28 DS18B20 Digital Temperature Sensor
0x10 DS18S20 Older Digital Temperature Sensor
0x2D DS2431 1K-bit (128-byte) 1-Wire EEPROM
0x01 DS1990A iButton Unique Serial Number
0x22 DS1822 Econo Digital Temperature Sensor
0x33 DS2433 4K-bit (512-byte) 1-Wire EEPROM

By checking this first byte, our ESP32 application can know which function commands (like CONVERT T or WRITE SCRATCHPAD) are valid for that specific device.

4. Managing the Network

A robust network application follows a clear pattern:

  1. Discover: Run the Search ROM algorithm to find all device ROM codes. Store these in an array.
  2. Identify: Loop through the array of discovered ROM codes.
  3. Address and Command: For each device, use the MATCH ROM (0x55) command followed by its 64-bit ROM code. This ensures that only that specific device will listen to the subsequent function command.
  4. Execute: Send the appropriate function command based on the device’s family code.

Practical Examples

This example builds on Chapter 212. We will create a network with two DS18B20 sensors and one DS2431 EEPROM. The application will discover all three, read the temperature from the sensors, and write a counter value to the EEPROM.

Prerequisites

  1. Hardware:
    • An ESP32 development board.
    • Two DS18B20 temperature sensors.
    • One DS2431 EEPROM.
    • A 4.7 kΩ resistor.
    • A breadboard and jumper wires.
  2. Software:
    • VS Code with the ESP-IDF v5.x extension.
    • The code from Chapter 212 as a starting point.

Step 1: Wiring the Multi-device Network

The wiring is a simple extension of the previous chapter. All devices are connected in parallel.

  1. Connect ESP32 GND to the GND pin of all three devices.
  2. Connect ESP32 3.3V to the VDD pin of all three devices.
  3. Connect ESP32 GPIO 18 to the DQ pin of all three devices.
  4. Place the single 4.7 kΩ pull-up resistor between the DQ line (GPIO 18) and 3.3V.

Step 2: Writing the Code

We will significantly refactor the previous code to support device discovery and multiple device types.

%%{init: {'theme': 'base', 'themeVariables': { 'fontFamily': 'Open Sans'}}}%%
sequenceDiagram
    participant MainLoop as Main Loop
    participant OneWireBus as 1-Wire Bus
    participant Devices as Discovered Devices
    
    MainLoop->>+OneWireBus: Run Search ROM Algorithm
    activate OneWireBus
    Note over OneWireBus: Discover all connected<br>devices and their ROM codes.
    OneWireBus-->>-MainLoop: Return array of ROM codes
    MainLoop->>Devices: Store ROM codes
    
    loop Every 5 seconds
        MainLoop->>Devices: For each device...
        activate Devices
        Devices->>MainLoop: Get next ROM code
        
        MainLoop->>+OneWireBus: MATCH ROM + ROM Code
        Note over OneWireBus: Selects one specific device
        OneWireBus-->>-MainLoop: Device is listening
        
        MainLoop->>MainLoop: Check Family Code
        
        alt DS18B20 (Temp Sensor)
            MainLoop->>+OneWireBus: CONVERT T
            OneWireBus-->>-MainLoop: (Conversion starts)
            MainLoop->>MainLoop: Wait (e.g., 800ms)
            MainLoop->>+OneWireBus: MATCH ROM + READ SCRATCHPAD
            OneWireBus-->>-MainLoop: Return temperature data
        else DS2431 (EEPROM)
            MainLoop->>+OneWireBus: WRITE SCRATCHPAD + data
            OneWireBus-->>-MainLoop: (Data in buffer)
            MainLoop->>+OneWireBus: COPY SCRATCHPAD
            Note over OneWireBus: Commits data to memory
            OneWireBus-->>-MainLoop: (Write complete)
        else Other Device
            MainLoop->>MainLoop: Log "Unknown Device"
        end
        deactivate Devices
    end

Tip: The onewire_search function is complex. Pay close attention to the logic for handling discrepancies and backtracking to find all devices.

C
/* main/onewire_network_example.c */

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "driver/gpio.h"

static const char *TAG = "ONEWIRE_NET";

// Use the same RMT and timing defines from Chapter 212
#define ONEWIRE_GPIO         18
#define RMT_RESOLUTION_HZ    1000000 // 1MHz resolution, 1 tick = 1us
#define ONEWIRE_RESET_PULSE_DURATION_US      480
#define ONEWIRE_PRESENCE_PULSE_MIN_US        60
#define ONEWIRE_PRESENCE_PULSE_MAX_US        240
#define ONEWIRE_WRITE_0_PULSE_DURATION_US    60
#define ONEWIRE_WRITE_1_PULSE_DURATION_US    6
#define ONEWIRE_READ_PULSE_DURATION_US       6
#define ONEWIRE_TIMESLOT_DURATION_US         70

// ROM & Function Commands
#define DS18B20_FAMILY_CODE         0x28
#define DS2431_FAMILY_CODE          0x2D
#define ONEWIRE_CMD_SEARCH_ROM      0xF0
#define ONEWIRE_CMD_MATCH_ROM       0x55
#define DS18B20_CMD_CONVERT_T       0x44
#define DS18B20_CMD_READ_SCRATCHPAD 0xBE
#define DS2431_CMD_WRITE_SCRATCHPAD 0x0F
#define DS2431_CMD_READ_SCRATCHPAD  0xAA
#define DS2431_CMD_COPY_SCRATCHPAD  0x55

// Maximum number of devices to search for
#define MAX_ONEWIRE_DEVICES         10

typedef uint8_t onewire_rom_t[8];

// --- RMT Driver handles from Chapter 212 ---
static rmt_channel_handle_t tx_channel = NULL;
static rmt_channel_handle_t rx_channel = NULL;
static rmt_encoder_handle_t bytes_encoder = NULL;
static rmt_encoder_handle_t copy_encoder = NULL;

// --- Forward declarations for driver functions from Chapter 212 ---
static void onewire_rmt_init(void);
bool onewire_reset(void);
void onewire_write_byte(uint8_t data);
uint8_t onewire_read_byte(void);

/**
 * @brief Reads a single bit from the 1-Wire bus
 * @return The bit value read (0 or 1)
 */
static int onewire_read_bit(void) {
    // Structure for a read slot
    rmt_symbol_word_t read_slot[] = {
        {{.duration0 = ONEWIRE_READ_PULSE_DURATION_US, .level0 = 1, .duration1 = 5, .level1 = 0}},
        {{.duration0 = 55, .level0 = 0, .duration1 = 0, .level1 = 0}}, // Remainder of the timeslot
    };
    
    rmt_transmit_config_t tx_config = {.loop_count = 0};
    ESP_ERROR_CHECK(rmt_transmit(tx_channel, copy_encoder, &read_slot[0], sizeof(read_slot[0]), &tx_config));
    
    rmt_rx_done_event_data_t rx_data;
    rmt_receive_config_t rx_config = {.signal_range_min_ns = 1000, .signal_range_max_ns = ONEWIRE_TIMESLOT_DURATION_US * 1000};
    ESP_ERROR_CHECK(rmt_receive(rx_channel, NULL, 0, &rx_config));
    
    ESP_ERROR_CHECK(rmt_tx_wait_all_done(tx_channel, -1));
    
    if (xQueueReceive(rx_channel->rx_queue, &rx_data, pdMS_TO_TICKS(20)) == pdPASS) {
        // If the duration of the low pulse from the slave is very short, it's a 1.
        // It means the slave released the line quickly.
        if (rx_data.received_symbols[0].duration0 < 15) {
            return 1;
        }
    }
    return 0; // Otherwise, it's a 0 (slave held it low or timeout)
}


/**
 * @brief Writes a single bit to the 1-Wire bus
 */
static void onewire_write_bit(int bit) {
    rmt_symbol_word_t write_slot;
    if (bit) { // Write 1
        write_slot.duration0 = ONEWIRE_WRITE_1_PULSE_DURATION_US;
        write_slot.level0 = 1;
        write_slot.duration1 = ONEWIRE_TIMESLOT_DURATION_US - ONEWIRE_WRITE_1_PULSE_DURATION_US;
        write_slot.level1 = 0;
    } else { // Write 0
        write_slot.duration0 = ONEWIRE_WRITE_0_PULSE_DURATION_US;
        write_slot.level0 = 1;
        write_slot.duration1 = ONEWIRE_TIMESLOT_DURATION_US - ONEWIRE_WRITE_0_PULSE_DURATION_US;
        write_slot.level1 = 0;
    }
    rmt_transmit_config_t tx_config = {.loop_count = 0};
    ESP_ERROR_CHECK(rmt_transmit(tx_channel, copy_encoder, &write_slot, sizeof(write_slot), &tx_config));
    ESP_ERROR_CHECK(rmt_tx_wait_all_done(tx_channel, -1));
}


// --- CRC8 implementation (from DS18B20 datasheet) ---
static uint8_t onewire_crc8(const uint8_t *addr, uint8_t len) {
    uint8_t crc = 0;
    while (len--) {
        uint8_t inbyte = *addr++;
        for (int i = 8; i; i--) {
            uint8_t mix = (crc ^ inbyte) & 0x01;
            crc >>= 1;
            if (mix) {
                crc ^= 0x8C;
            }
            inbyte >>= 1;
        }
    }
    return crc;
}

// Variables for the search algorithm
static int last_discrepancy;
static bool last_device_found;
static onewire_rom_t current_rom;

/**
 * @brief Resets the search state to start from the beginning
 */
void onewire_search_reset(void) {
    last_discrepancy = 0;
    last_device_found = false;
    memset(current_rom, 0, sizeof(onewire_rom_t));
}

/**
 * @brief Performs one iteration of the 1-Wire search algorithm
 * @param[out] rom_code Pointer to store the found ROM code
 * @return true if a device was found, false if the search is complete
 */
bool onewire_search(onewire_rom_t *rom_code) {
    int id_bit_number;
    int last_zero, rom_byte_number, search_result;
    int id_bit, cmp_id_bit;
    uint8_t rom_byte_mask;

    id_bit_number = 1;
    last_zero = 0;
    rom_byte_number = 0;
    rom_byte_mask = 1;

    if (last_device_found) {
        onewire_search_reset();
        return false;
    }

    if (!onewire_reset()) {
        onewire_search_reset();
        return false;
    }

    onewire_write_byte(ONEWIRE_CMD_SEARCH_ROM);

    do {
        id_bit = onewire_read_bit();
        cmp_id_bit = onewire_read_bit();

        if ((id_bit == 1) && (cmp_id_bit == 1)) {
            break; // No devices responded
        } else {
            if (id_bit != cmp_id_bit) {
                search_result = id_bit; // No conflict
            } else {
                // Conflict
                if (id_bit_number < last_discrepancy) {
                    search_result = ((current_rom[rom_byte_number] & rom_byte_mask) > 0);
                } else {
                    search_result = (id_bit_number == last_discrepancy);
                }
                if (search_result == 0) {
                    last_zero = id_bit_number;
                }
            }

            if (search_result == 1) {
                current_rom[rom_byte_number] |= rom_byte_mask;
            } else {
                current_rom[rom_byte_number] &= ~rom_byte_mask;
            }

            onewire_write_bit(search_result);
            id_bit_number++;
            rom_byte_mask <<= 1;
            if (rom_byte_mask == 0) {
                rom_byte_number++;
                rom_byte_mask = 1;
            }
        }
    } while (rom_byte_number < 8);

    if (id_bit_number > 64) {
        last_discrepancy = last_zero;
        if (last_discrepancy == 0) {
            last_device_found = true;
        }
        memcpy(rom_code, &current_rom, sizeof(onewire_rom_t));
        // Check CRC
        if(onewire_crc8((uint8_t *)rom_code, 7) == (*rom_code)[7]) {
            return true;
        }
    }
    
    // Search failed, reset for next time
    if (search_result == 0 || id_bit_number < 65) {
        onewire_search_reset();
    }
    
    return false;
}


void app_main(void) {
    onewire_rmt_init();

    onewire_rom_t discovered_roms[MAX_ONEWIRE_DEVICES];
    int device_count = 0;

    // --- 1. Discover all devices on the bus ---
    ESP_LOGI(TAG, "Starting 1-Wire device discovery...");
    onewire_search_reset();
    while (device_count < MAX_ONEWIRE_DEVICES && onewire_search(&discovered_roms[device_count])) {
        ESP_LOGI(TAG, "Found device %d with ROM code: %02X%02X%02X%02X%02X%02X%02X%02X",
                 device_count,
                 discovered_roms[device_count][7], discovered_roms[device_count][6],
                 discovered_roms[device_count][5], discovered_roms[device_count][4],
                 discovered_roms[device_count][3], discovered_roms[device_count][2],
                 discovered_roms[device_count][1], discovered_roms[device_count][0]);
        device_count++;
    }
    ESP_LOGI(TAG, "Discovery complete. Found %d devices.", device_count);

    uint8_t eeprom_write_counter = 0;

    while (1) {
        ESP_LOGI(TAG, "--- Starting network poll ---");
        // --- 2. Iterate through discovered devices and interact with them ---
        for (int i = 0; i < device_count; i++) {
            uint8_t family_code = discovered_roms[i][0];
            ESP_LOGI(TAG, "Polling device %d (Family Code: 0x%02X)", i, family_code);
            
            // --- 3. Address the specific device ---
            if (!onewire_reset()) continue;
            onewire_write_byte(ONEWIRE_CMD_MATCH_ROM);
            for(int j = 0; j < 8; j++) {
                onewire_write_byte(discovered_roms[i][j]);
            }

            // --- 4. Act based on family code ---
            switch (family_code) {
                case DS18B20_FAMILY_CODE: {
                    // Start conversion
                    onewire_write_byte(DS18B20_CMD_CONVERT_T);
                    vTaskDelay(pdMS_TO_TICKS(800)); // Wait for conversion

                    // Read scratchpad
                    onewire_reset();
                    onewire_write_byte(ONEWIRE_CMD_MATCH_ROM);
                     for(int j = 0; j < 8; j++) {
                        onewire_write_byte(discovered_roms[i][j]);
                    }
                    onewire_write_byte(DS18B20_CMD_READ_SCRATCHPAD);
                    uint8_t scratchpad[9];
                    for(int k=0; k<9; k++) scratchpad[k] = onewire_read_byte();
                    
                    if (onewire_crc8(scratchpad, 8) == scratchpad[8]) {
                         int16_t temp_raw = (scratchpad[1] << 8) | scratchpad[0];
                         float temp = temp_raw / 16.0;
                         ESP_LOGI(TAG, "  DS18B20 Temp: %.2f C", temp);
                    } else {
                         ESP_LOGE(TAG, "  DS18B20 CRC Error");
                    }
                    break;
                }

                case DS2431_FAMILY_CODE: {
                    // Write a counter to the first two bytes of the EEPROM
                    if (!onewire_reset()) continue;
                    onewire_write_byte(ONEWIRE_CMD_MATCH_ROM);
                    for(int j = 0; j < 8; j++) onewire_write_byte(discovered_roms[i][j]);
                    
                    onewire_write_byte(DS2431_CMD_WRITE_SCRATCHPAD);
                    onewire_write_byte(0x00); // Target address LSB
                    onewire_write_byte(0x00); // Target address MSB
                    onewire_write_byte(eeprom_write_counter); // Data to write

                    // Read back scratchpad to get confirmation bytes
                    onewire_reset();
                    onewire_write_byte(ONEWIRE_CMD_MATCH_ROM);
                    for(int j = 0; j < 8; j++) onewire_write_byte(discovered_roms[i][j]);
                    onewire_write_byte(DS2431_CMD_READ_SCRATCHPAD);
                    uint8_t talsb = onewire_read_byte();
                    uint8_t tamsb = onewire_read_byte();
                    uint8_t es = onewire_read_byte();
                    
                    // Copy scratchpad to EEPROM
                    onewire_reset();
                    onewire_write_byte(ONEWIRE_CMD_MATCH_ROM);
                    for(int j = 0; j < 8; j++) onewire_write_byte(discovered_roms[i][j]);
                    onewire_write_byte(DS2431_CMD_COPY_SCRATCHPAD);
                    onewire_write_byte(talsb);
                    onewire_write_byte(tamsb);
                    onewire_write_byte(es);

                    vTaskDelay(pdMS_TO_TICKS(15)); // Wait for copy
                    ESP_LOGI(TAG, "  DS2431: Wrote value %d to address 0x0000", eeprom_write_counter);
                    eeprom_write_counter++;
                    break;
                }
                
                default:
                    ESP_LOGW(TAG, "  Unknown device family: 0x%02X", family_code);
                    break;
            }
        }
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

// NOTE: The implementations for onewire_rmt_init, onewire_reset, onewire_write_byte, 
// and onewire_read_byte should be copied from the previous chapter's example code.
// They are omitted here for brevity but are required for the code to compile and run.

Step 3: Build, Flash, and Monitor

  1. Copy the static functions onewire_rmt_initonewire_resetonewire_write_byte, and the RMT encoder definitions from the Chapter 212 example into this file.
  2. Build the project (idf.py build).
  3. Flash the firmware (idf.py -p [YOUR_PORT] flash).
  4. Monitor the output (idf.py -p [YOUR_PORT] monitor).

The application will first discover and list the three devices. Then, every five seconds, it will loop through them, printing the temperature from the two sensors and incrementing a counter written to the EEPROM.

Variant Notes

  • ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2: Just as in the previous chapter, the RMT-based driver is fully portable across all ESP32 variants that include the RMT peripheral. The main performance bottleneck in a 1-Wire network is not the microcontroller but the protocol’s timing constraints and the physical properties of the bus itself (e.g., capacitance). Your choice of ESP32 variant should be based on other project needs (Wi-Fi 6, Bluetooth 5, etc.), not on 1-Wire performance.

Common Mistakes & Troubleshooting Tips

Mistake / Issue Symptom(s) Troubleshooting / Solution
Flawed Search Algorithm Search finds only one device, no devices, or gets stuck in a loop. Incorrect ROM codes are returned. Trace the search loop. Add ESP_LOGI calls to print id_bit, cmp_id_bit, and last_discrepancy. Meticulously compare logic with Maxim Integrated’s official documentation.
Using SKIP ROM (0xCC) Works with one sensor but fails with multiple. Bus voltage collapses, causing device resets, CRC errors, and failed commands. Never use SKIP ROM in a multi-device network for power-intensive commands. After discovery, always use MATCH ROM (0x55) to address each device individually.
Ignoring the Family Code Sending a CONVERT T command to an EEPROM, or a memory command to a sensor. The device ignores the command; the application gets no response or invalid data. After discovering a device, always check its family code (the first byte of the ROM). Use a switch statement to call functions specific to that device type.
Bus Loading & Signal Integrity Network is unreliable. Devices intermittently disappear or fail CRC checks. Search fails, especially after adding more devices or longer wires. Keep wires short. For >5 devices or >5m wire length, use twisted-pair cable. Try a stronger pull-up resistor (e.g., from 4.7 kΩ down to 2.2 kΩ). For large networks, use a dedicated 1-Wire driver IC.
Incorrect CRC Calculation Data read from a device (e.g., temperature) appears valid, but the CRC check fails. Ensure your onewire_crc8 function is correct. For a ROM code, CRC is calculated on the first 7 bytes. For a DS18B20 scratchpad, it’s calculated on the first 8 bytes. Double-check you are passing the correct length.

Exercises

  1. Component-Based Design: Refactor the entire code into a reusable onewire component. Create onewire.h and onewire.c files. The header should expose high-level functions like onewire_search_devices()ds18b20_get_temperature(rom_code), and ds2431_write_memory(rom_code, address, data). Your app_main should become much simpler.
  2. Asynchronous Conversions: Modify the main loop for efficiency. First, loop through your discovered devices and issue a MATCH ROM and CONVERT T command to each DS18B20 without waiting. After commanding them all, perform a single vTaskDelay(pdMS_TO_TICKS(800)). Finally, loop through them again, using MATCH ROM to read the already-converted result from each one’s scratchpad.
  3. Data Logging: Combine the functionality of the devices. Modify the main loop to read the temperature from the first DS18B20 sensor and then immediately write that temperature value into the DS2431 EEPROM.
  4. Dynamic Network Scanning: Create a separate FreeRTOS task that runs every 30 seconds and re-scans the 1-Wire bus. It should compare the new list of discovered devices with the old one and log a message if a device has been added or removed (hot-plugged). This is crucial for systems where sensors might be replaced.

Summary

  • The Search ROM (0xF0) algorithm is the key to discovering all unique 64-bit device addresses on a shared 1-Wire bus.
  • The algorithm works by navigating conflicts in the bit values reported by slaves to individually map out each device’s address.
  • The first byte of a ROM code, the Family Code, identifies the device type (e.g., temperature sensor, EEPROM).
  • In a multi-device network, MATCH ROM must be used to address and command devices individually to avoid conflicts and manage power.
  • Physical network size is limited by bus capacitance and power delivery, which can affect signal timing and reliability.

Further Reading

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top