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:
- Start Search: The master issues a
SEARCH ROM
command. - 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.
- 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 a0
at this bit position. The master knows this bit of the address is0
.10
: All responding slaves have a1
at this bit position. The master knows this bit of the address is1
.00
: A discrepancy. Some slaves sent0
and others sent1
. The bus is pulled low for both reads. This is the key to the search.
- 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.” - 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.
- 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:
- Discover: Run the Search ROM algorithm to find all device ROM codes. Store these in an array.
- Identify: Loop through the array of discovered ROM codes.
- 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. - 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
- Hardware:
- An ESP32 development board.
- Two DS18B20 temperature sensors.
- One DS2431 EEPROM.
- A 4.7 kΩ resistor.
- A breadboard and jumper wires.
- 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.
- Connect ESP32
GND
to theGND
pin of all three devices. - Connect ESP32
3.3V
to theVDD
pin of all three devices. - Connect ESP32 GPIO 18 to the
DQ
pin of all three devices. - Place the single 4.7 kΩ pull-up resistor between the
DQ
line (GPIO 18) and3.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.
/* 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, ¤t_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
- Copy the static functions
onewire_rmt_init
,onewire_reset
,onewire_write_byte
, and the RMT encoder definitions from the Chapter 212 example into this file. - Build the project (
idf.py build
). - Flash the firmware (
idf.py -p [YOUR_PORT] flash
). - 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
- Component-Based Design: Refactor the entire code into a reusable
onewire
component. Createonewire.h
andonewire.c
files. The header should expose high-level functions likeonewire_search_devices()
,ds18b20_get_temperature(rom_code)
, andds2431_write_memory(rom_code, address, data)
. Yourapp_main
should become much simpler. - Asynchronous Conversions: Modify the main loop for efficiency. First, loop through your discovered devices and issue a
MATCH ROM
andCONVERT T
command to each DS18B20 without waiting. After commanding them all, perform a singlevTaskDelay(pdMS_TO_TICKS(800))
. Finally, loop through them again, usingMATCH ROM
to read the already-converted result from each one’s scratchpad. - 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.
- 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
- Maxim Integrated Application Note 187: 1-Wire Search Algorithm: https://www.analog.com/media/en/technical-documentation/app-notes/1wire-search-algorithm.pdf
- DS2431 1-Kb Protected 1-Wire EEPROM with SHA-1 Engine Datasheet: Required reading for understanding the specific memory commands, addressing, and timing for this EEPROM chip. https://www.analog.com/media/en/technical-documentation/data-sheets/DS2431.pdf