I2C communication between DS28E18 and Arduino

Hi everyone,
I have an I2C connection between the DS28E18 as Master and the Arduino slave.

It is attempting to send 0x00 to the Arduino slave and receive 2 Bytes of data, in this case 0x41 and 0x42. However, these 2 Bytes are not stored in the placeholder and the initial values, 0xFA and 0xFB, are read.

I checked the I2C signal with an oscilloscope and found that only 0x90 and 0x00 were signaled. Normally, 0x91 should be output after this.

The Arduino slave sketch sends 2 bytes of data immediately upon receiving 0x00.

I suspect my sketch either mishandles the DS28E18's handling of I2C commands, or it is not properly delayed.

What do you guys think?

DS28E18 Master:

#include <OneWire.h>
#include "OneWireOpenDrain.h"

OneWireOpenDrain ds28e18(12);     //D12 pin, 330-ohm pullup required

byte data[8];

void setup() {
  Serial.begin(115200);
  Serial.println("init....");
  delay(500);
  ds28e18.reset();

  /* Clear POR bit */
  Serial.println("Send Device Status Command.(7A)");
  ds28e18.reset();
  ds28e18.write(0xCC);            //SKIP_ROM
  ds28e18.write(0x66);            //COMMAND_START
  ds28e18.write(0x01);            //Length 1
  ds28e18.write(0x7A);            //Command 7A(Device Status)
  for (int i = 0; i < 2; i++) {
    data[i] = ds28e18.read();     //Rx:CRC-16(8bit*2)
  }
  ds28e18.write(0xAA);            //Release Byte
  delay(1);                       //delay <t_OP> or more
  Serial.print(" Result; ");
  for (int i = 0; i < 8; i++) {
    data[i] = ds28e18.read();     //Rx: Dummy Byte, Length Byte, Result Byte, Device Status Byte, Version Byte, MANID[0], MANID[1], CRC-16(8bit*2)
    Serial.print(data[i], HEX);
    Serial.print(",");
  }
  ds28e18.reset();
  Serial.print("\n");

  /* Disable internal pullup resistor */
  Serial.println("Write GPIO Configuration.(83)");
  ds28e18.reset();
  ds28e18.write(0xCC);            //SKIP_ROM
  ds28e18.write(0x66);            //COMMAND_START
  ds28e18.write(0x05);            //Length 5
  ds28e18.write(0x83);            //Command 83(Write GPIO Configuration)
  ds28e18.write(0x0B);            //CFG_REG_TARGET 0B
  ds28e18.write(0x03);            //CFG_REG_MOD
  ds28e18.write(0x00);            //GPIO_CTRL_HI External pullup resistor, open drain
  ds28e18.write(0x00);            //GPIO_CTRL_LO GPIO Output low
  for (int i = 0; i < 2; i++) {
    data[i] = ds28e18.read();     //Rx:CRC-16(8bit*2)
  }
  ds28e18.write(0xAA);            //Release Byte
  delay(1);                       //delay <t_OP> or more
  Serial.print(" Result; ");
  for (int i = 0; i < 5; i++) {
    data[i] = ds28e18.read();     //Rx: Dummy Byte, Length Byte, Result Byte, CRC-16(8bit*2)
    Serial.print(data[i], HEX);
    Serial.print(",");
  }
  ds28e18.reset();
  Serial.print("\n");

  /* Write I2C sequence to SRAM in DS28E18 */
  Serial.println("Store I2C sequence in SRAM.(11)");
  ds28e18.reset();
  ds28e18.write(0xCC);            //SKIP_ROM
  ds28e18.write(0x66);            //COMMAND_START
  ds28e18.write(0x16);            //Length 22
  ds28e18.write(0x11);            //01 Command 11(Write Sequencer Command Transfer)
  ds28e18.write(0x00);            //02 ADDR_LO
  ds28e18.write(0x00);            //03 ADDR_HI
  ds28e18.write(0xCC);            //04 0x0000 01 SENS_VDD On Command
  ds28e18.write(0xDD);            //05 0x0001 02 Delay Command
  ds28e18.write(0x00);            //06 0x0002 03 1ms
  ds28e18.write(0x02);            //07 0x0003 04 I2C Start Command
  ds28e18.write(0xE3);            //08 0x0004 05 I2C Write Data Command
  ds28e18.write(0x02);            //09 0x0005 06 I2C Write Length
  ds28e18.write(0x90);            //10 0x0006 07 I2C Address 0x90(Write)
  ds28e18.write(0x00);            //11 0x0007 08 Send "0x00"
  ds28e18.write(0x03);            //12 0x0008 09 I2C Stop Command
  ds28e18.write(0x02);            //13 0x0009 10 I2C Start Command
  ds28e18.write(0xE3);            //14 0x000A 11 I2C Write Data Command
  ds28e18.write(0x01);            //15 0x000B 12 I2C Write Length
  ds28e18.write(0x91);            //16 0x000C 13 I2C Address 0x91(Read)
  ds28e18.write(0xD4);            //17 0x000D 14 I2C Read Data Command
  ds28e18.write(0x02);            //18 0x000E 15 Read Length
  ds28e18.write(0xFA);            //19 0x000F 16 Read Data Placeholder - Byte 1
  ds28e18.write(0xFB);            //20 0x0010 17 Read Data Placeholder - Byte 2
  ds28e18.write(0x03);            //21 0x0011 18 I2C Stop Command
  ds28e18.write(0xBB);            //22 0x0012 19 SENS_VDD Off Command
  for (int i = 0; i < 2; i++) {
    data[i] = ds28e18.read();     //Rx:CRC-16(8bit*2)
  }
  ds28e18.write(0xAA);            //Release Byte
  delay(1);                       //delay <t_OP> or more
  Serial.print(" Result; ");
  for (int i = 0; i < 5; i++) {
    data[i] = ds28e18.read();     //Rx: Dummy Byte, Length Byte, Result Byte, CRC-16(8bit*2)
    Serial.print(data[i], HEX);
    Serial.print(",");
  }
  ds28e18.reset();
  Serial.print("\n");
}

void loop() {
  
  /* Run a sequence written to SRAM */
  Serial.println("Run stored sequence.(33)");
  ds28e18.reset();
  ds28e18.write(0xCC);            //SKIP_ROM
  ds28e18.write(0x66);            //COMMAND_START
  ds28e18.write(0x04);            //Length 4
  ds28e18.write(0x33);            //Command 33(Run Sequencer)
  ds28e18.write(0x00);            //ADDR_LO
  ds28e18.write(0x26);            //SLEN_LO:ADDR_HI Length 19
  ds28e18.write(0x00);            //SLEN_HI
  for (int i = 0; i < 2; i++) {
    data[i] = ds28e18.read();     //Rx:CRC-16(8bit*2)
  }
  ds28e18.write(0xAA);            //Release Byte
  delay(300);                     //delay <t_OP> or more
  Serial.print(" Result; ");
  for (int i = 0; i < 8; i++) {
    data[i] = ds28e18.read();     //Rx: Dummy Byte,Length Byte,Result Byte,CRC-16(8bit*2)
    Serial.print(data[i], HEX);
    Serial.print(",");
  }
  ds28e18.reset();
  Serial.print("\n");

  /* Transmits data received in SRAM to the host */
  Serial.println("Read I2C results.(22)");
  ds28e18.reset();
  ds28e18.write(0xCC);            //SKIP_ROM
  ds28e18.write(0x66);            //COMMAND_START
  ds28e18.write(0x03);            //Length 3
  ds28e18.write(0x22);            //Command 22(Read Sequencer)
  ds28e18.write(0x0F);            //ADDR_LO Placeholder Address
  ds28e18.write(0x04);            //SLEN:ADDR_HI (2byte)
  for (int i = 0; i < 2; i++) {
    data[i] = ds28e18.read();     //Rx:CRC-16(8bit*2)
  }
  ds28e18.write(0xAA);            //Release Byte
  delay(1);                       //delay <t_OP> or more
  Serial.print(" Result; ");
  for (int i = 0; i < 7; i++) {
    data[i] = ds28e18.read();     //Rx: Dummy Byte,Length Byte,Result Byte,Data Byte,CRC-16(8bit*2)
    Serial.print(data[i], HEX);   
    Serial.print(",");
  }
  ds28e18.reset();
  Serial.print("\n");

  /* Output received data to serial monitor */
  Serial.print("Received data: 0x");
  Serial.print(data[3], HEX);
  Serial.print(", 0x");
  Serial.print(data[4], HEX);
  Serial.print("\n");
}

Arduino Slave:

#include <Wire.h>

#define SLAVE_ADDRESS 0x48 // 7-bit Slave Address

void receiveData(int byteCount) {
  while (Wire.available()) {
    byte receivedData = Wire.read(); // Read the received data from the master

    // Process the received data and send appropriate responses
    switch (receivedData) {
      case 0x00:
        Wire.write(0x41); // Send "0x41"
        Wire.write(0x42); // Send "0x42"
        break;
      case 0x01:
        Wire.write(0x51); // Send "0x51"
        Wire.write(0x52); // Send "0x52"
        break;
      default:
        // Handle any other cases if needed
        break;
    }
  }
}

void setup() {
  Wire.begin(SLAVE_ADDRESS);  // Initialize I2C communication with the provided slave address
  Wire.onReceive(receiveData); // Register the receive event handler function
}

void loop() {
  // Additional code for the main loop, if needed
}

Please clarify your project. Are you run an Arduino code on DS28E18 device?
I think you have a two arduino, one connected to DS28E18

If your i2c slave has address 0x48

why do you trnamit data from master to address 0x90 and 0x91 ?

Please describe your project in more detail/ What is the point of using ds28e18 as a bridge to i2c devices, why do you not use i2c bus directly?

I agree with b707, you project needs clarification.

Why do you to want to use the 1-Wire bus to extend the length of a I2C bus ? To say that it is overly complex is a understatement. It might not work.

I can write a few of the many problems that I see, but what I write below is useless in my opinion. Please use something normal, like a RS-485 bus. The RS-485 needs three wires, because it also needs a GND wire.


Which Arduino boards do you use and which core do you run on them ?
The chip is a 3.3V chip, you can not connect that to a 5V Arduino board. Can you show a schematic ? (a drawing on a piece of paper is fine).

Do you know if your Arduino board works in I2C Slave mode ?

Can you tell which library the "OneWireOpenDrain" is ? Please give a link to it.

Your "receiveData()" function is the "receive handler". It runs in a interrupt when data is received over the I2C bus. You may not use Wire.write() in the receive handler.

The actual wiring is a bit more complicated. I am trying in vain to test the utility of 1-Wire communication.

Arduino Pro Mini(3.3V) ---1W--- DS28E18(3.3V) ---I2C--- ADUM1251ARZ(3.3V/5V Isolator) ---I2C--- ATmega328PB(MiniCore,5V)

If the slave address of the Arduino is defined as 0x48,(0100 1000), I am assuming that I need to specify 0x90(1001 0000) for writing and 0x91(1001 0001) for reading from DS28E18.

Original code:
https://akizukidenshi.com/download/ds/akizuki/DS28E18_ADT7410-210716a.zip

I am assuming that as well, but you need an example on Github or a Application Note.
You also need a Logic Analzyer. A USB Logic Analyzer with the software on the PC is the best option in my opinion. The software can decode the 1-Wire and I2C signals for you.
The LHT00SU1 with sigrok/PulseView was only 20 or 25 dollars in the past, but now 30 or 35 dollars. There is code to turn a 5 dollar Raspberry Pi Pico into a Logic Analzyer, but I have not tried that.
However, you have many more problems.

Can you tell what it is for ? What is your project ? Please give us a broader view.

So this:

from the datasheet:

Have you tried Analog Devices for support?

https://www.analog.com/en/products/ds28e18.html#product-discussions

So have you got it working with simple wiring first?

Not yet. I designed the board for this test. It doesn't get any simpler than this, except for the isolation between the 1-Wire device and the Arduino's I2C communication.

I recognize that there are multiple problems with this sketch.

As Koepel pointed out, It seems that Wire.onReceive() should not include Wire.write(). Arduino has a similar function, Wire.onRequest(), but this should not be available in this device configuration... No, maybe I just don't know how to use it.

I2C Slave Peripheral is definitely a road less travelled on Arduino (in fact, on microcontrollers in general) - so would probably be best to get that working with just a normal, wired connection before adding the complexities of 1-WireTM, isolation, etc...

It seems that Wire.onRequest() on the slave side must be used in combination with Wire.requestFrom() on the master side. I am not familiar with the Arduino backend, so I don't know how to request data from the DS28E18.

Information on DS28E18 is scant. As far as I know, it is about the following page:
The Basics of Using the DS28E18

This is an example of communicating with a strictly I2C device, and the situation may be different than when I build my own slave.

Plenty on the manufacturer's product page:

It shouldn't be.

The whole point of standard interfaces like this is that it's independent of the slave peripheral implementation - the master controller neither knows nor cares whether it's a dedicated part or a microcontroller.

Again, the key to this type of thing is to take one small step at a time - don't pile up a whole load of unknowns at once!

It seems there is also a DS28E17 - which is I2C only:

awneil, Thanks for your advice.

It seems I was trying to create a complex system without understanding the whole thing.

I think I will buy the I2C-connected sensor for which you gave a concrete example and make sure it works first.

1 Like

There was also a problem with the sketch on the Arduino slave side, so I fixed this for now.

Arduino Slave:

#include <Wire.h>

#define SLAVE_ADDRESS 0x48 // 7-bit Slave Address

byte receivedData;
bool receiveFlag;

void receiveData(int byteCount) {
  while (Wire.available()) {
    receivedData = Wire.read();
  }
  receiveFlag = true;
}

void setup() {
  Wire.begin(SLAVE_ADDRESS);
  Wire.onReceive(receiveData);
}

void loop() {
  if (receiveFlag == true) {
    switch (receivedData) {
      case 0x00:
        Wire.write(0x41); // Send "0x41"
        Wire.write(0x42); // Send "0x42"
        break;
      case 0x01:
        Wire.write(0x51); // Send "0x51"
        Wire.write(0x52); // Send "0x52"
        break;
      default:
        // Handle any other cases if needed
        break;
    }
    receiveFlag = false;
  }
}

When I see this:

void receiveData(int byteCount) {
  while (Wire.available()) {
    receivedData = Wire.read();
  }
  receiveFlag = true;
}

then your protocol for the data is: Receive the data from the I2C bus and throw away all the bytes except the last byte. Why would you do that ?

If you expect one byte, then read one byte:

void receiveData(int byteCount) {
  receivedData = Wire.read();
  receiveFlag = true;
}

I prefer to have an extra check for the number of bytes that is expected:

void receiveEvent(int howMany) 
{
  if (howMany == 1)         // expecting one byte
  {
    receivedData = Wire.read();
    receiveFlag = true;
  }
}

When a variable is used both in a interrupt and in the loop(), then add the word "volatile" to it. In most cases it won't make a difference, but those variables should be "volatile".

volatile byte receivedData;
volatile bool receiveFlag;

Did you know that a "if" condition is true or false and that a "bool" variable is also true or false ?
So you can do this:

bool fast = false;
bool running = true;
if (!fast and running)  // not fast and running
{
  Serial.println("Running slow");
}

In your code:

if (receiveFlag) {
  ...

Do you think that this will help to make your project work ? I have serious doubts. I still don't see why your project would make any sense.

1 Like

Arduino Slave:

#include <Wire.h>

#define SLAVE_ADDRESS 0x48 // 7-bit Slave Address

volatile byte receivedData;
volatile bool receiveFlag;

void receiveData(int byteCount) {
  if (byteCount == 1) {
    receivedData = Wire.read();
    receiveFlag = true;
  }
}

void setup() {
  Wire.begin(SLAVE_ADDRESS);
  Wire.onReceive(receiveData);
}

void loop() {
  if (receiveFlag) {
    switch (receivedData) {
      case 0x00:
        Wire.write(0x41); // Send "0x41"
        Wire.write(0x42); // Send "0x42"
        break;
      case 0x01:
        Wire.write(0x51); // Send "0x51"
        Wire.write(0x52); // Send "0x52"
        break;
      default:
        // Handle any other cases if needed
        break;
    }
    receiveFlag = false;
  }
}

The I2C is not that kind of "bus". You can not put data at one end and assume that it appears at the other end.

To write data, you need: Wire.beginTransmission - Wire.write - Wire.endTransmission.
Because the I2C bus uses packages of data.

If a Arduino receives data as a Slave, and then it also writes data on the I2C bus, so you have two Masters on the I2C bus. That will not work.

Where do you want to send those 0x41,0x42 and 0x51,0x52 to ? Can the DS28E18 be set as a Slave ? Is that somewhere in the datasheet ? If you want the DS28E18 to request data, then you have to add the onRequest handler.

Do you know how far you are from a working project ? You have added the DS28E18 to solve a problem, but I don't know what that problem is. I think that you are trying to fix the wrong problem.