1-Wire Slave

milimilo: Hello Youen

Thank you very much for your lib.

My project is this one, and of course doesn't work: I have an home automation zwave device that can read DS18B20 1Wire sensors. I would like to use my Arduino UNO to send some value through this zwave module.

May you give me a simple example of code that sends a value when pressing a simple button?

I'm blocked trying to give your library a value to send (using your demo .ino ...).

Sorry for my completely noob question (i am really new to Arduino and totally off the computing universe) and thank you very much for your help!

If I understand correctly, the zwave thing does not matter here, things would be exactly the same if you wanted to talk to a classical 1-wire master?

So what you want to do is emulate a DS18B20 sensor. For that, you need to understand the specific protocol of this device, you can look at this document for example : http://www.adafruit.com/datasheets/DS18B20.pdf, check the section "DS18B20 FUNCTION COMMANDS"

For a very basic operation, you'll need to understand the temperature conversion command, and how to send the response (which happens after the "read scratchpad" command, two bytes of the scratchpad are used to transmit the temperature). However, if your zwave device, which acts as a master, always sends configuration, or performs other operations, you might need to implement that part of the protocol as well.

Once you understand the protocol, implementing it with my library should be straightforward : as in the sample, you initialize the library with the device ROM (which may or may not be an official ROM number, but must have the correct family code), and register the event callback. This callback is a function that you write yourself, and will be called each time something happens (a byte is received, an error occurs, etc.). Then you'll need to react to the various commands that can be sent to a DS18B20 ("begin temperature conversion" and "read scratchpad" being the most important).

Communicating with your development PC using the serial port of the Arduino should help you in the process of understanding what happens in your program (so that you can see what command you have received, etc.)

Hi, if you show how to emulate the DS18B20, it will be great!

I'm trying to implement DS18B20 with OneWireArduinoSlave library. First problem - I need to respond to the master reset. How to do it? From documentation: "When the DS18B20 detects this rising edge, it waits 15µs to 60µs and then transmits a presence pulse by pulling the 1-Wire bus low for 60µs to 240µs."

I do so, but it not works:

void owReceive(OneWireSlave::ReceiveEvent evt, byte data) {
switch (evt) {
   // Detect Reset on bus
   case OneWireSlave::RE_Reset:
       Serial.println("Reset");
       _delay_us(60);
       pinMode(2, OUTPUT);
       digitalWrite(2, HIGH);
       _delay_us(200);
       digitalWrite(2, LOW);
   case OneWireSlave::RE_Byte:
     // Read Scratchpad [BEh] command
     if (data == 0xBE) { 
      Serial.println("Scratchpad command");
     }
OneWire.write(&acknowledge, 1, NULL);

break;

default:
}

Arduino slave receives Reset, so in console I see "Reset".

I've seen your message on github, and I think you've already solved this particular issue, but just in case I'll answer the question anyway: the library handles the reset procedure, so you don't have to implement that part of the DS18B20 protocol.

The library listens for the reset signal, then sends the presence, and responds to the search ROM procedure as well. This is entirely automatic, and performed as a background task. In fact, you can completely ignore the RE_Reset event, you probably don't need it in your case.

It will then receive bytes (automatic as well), and call owReceive for each byte received. Also, you don't need to read or set the pin mode directly, and that would probably interfere with what the library is doing.

Finally, as I said on github but it might be usefull for other readers here as well, you should avoid any long operation in owReceive, such as waiting, performing serial communication, etc. The default serial speed is 9600bps, which means to send "Reset" it will take 4167 micro seconds. At this point, you've probably already missed everything you hoped to receive.

What you can do, however, is store what you receive in a global variable, and when you detect that variable has changed in your main loop, write to the serial port from the main loop. This way, you don't block other background tasks. Don't forget to mark the variable as volatile, since it's going to be modified from an interrupt handler (owReceive is always called from an interrupt handler).

@Youen Big thanks for the library!
I created a DS18B20 Emulator based on OneWireArduinoSlave library, it’s really very easy!

#include "Arduino.h"
#include "LowLevel.h"
#include "OneWireSlave.h"

// Defines to access to Low and High bytes
#define Lo(x)   ((x) & 0xFF) 
#define Hi(x)   (((x)>>8) & 0xFF) 

// This is the pin that will be used for one-wire data (depending on your arduino model, you are limited to a few choices, because some pins don't have complete interrupt support)
// On Arduino Uno, you can use pin 2 or pin 3
Pin oneWireData(2);

// This is the ROM the arduino will respond to, make sure it doesn't conflict with another device, 0x28 - DS18B20 identificator
const byte owROM[] = {0x28, 0xF2, 0x27, 0xD6, 0x04, 0x00, 0x00, 0x3D};

// 0 - Low Byte Temperature 
// 1 - High Byte Temperature
// 2-7 - reserved
// 8 - CRC8
byte SCRATCHPAD[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x00};

// This function will be called each time the OneWire library has an event to notify (reset, error, byte received)
void owReceive(OneWireSlave::ReceiveEvent evt, byte data);

void setup() {
	// Setup the OneWire library
	OneWire.setReceiveCallback(&owReceive);
	OneWire.begin(owROM, oneWireData.getPinNumber());
}

void loop() {
	delay(100);  
  int temperature = 25;
  
  // Add temperature to the SCRATCHPAD
  if (temperature > 99)  temperature = 99;
  if (temperature < -55) temperature = -55;
  if (temperature >= 0) {
    SCRATCHPAD[0] = Lo(temperature*16);
    SCRATCHPAD[1] = Hi(temperature*16);
  } 
  else {
    SCRATCHPAD[0] = Lo(temperature*16);
    SCRATCHPAD[1] = Hi(temperature*16) | 0xF8;
  }
  // Add CRC8 to the SCRATCHPAD
  SCRATCHPAD[8] = OneWire.crc8(SCRATCHPAD,8);
  
	cli();//disable interrupts

	sei();//enable interrupts
}

void owReceive(OneWireSlave::ReceiveEvent evt, byte data) {
	switch (evt) {
    case OneWireSlave::RE_Reset:
      break;
    case OneWireSlave::RE_Byte:
      // Read Scratchpad [BEh] command
      if (data == 0xBE) {
        // Send SCRATCHPAD 9 byte with temperature and CRC
        for (byte i = 8, j = 1; j<=9; i--, j++) {
          OneWire.write(&SCRATCHPAD[i], j, NULL);
          //Serial.print(5);
        }
      }
      break;
    case OneWireSlave::RE_Error:
		  break;
	  default:
		;
	}
}

DS18B20Emulator.zip (10.1 KB)

I don’t know why, but with some devices previous code didn’t work. But with ROM from real DS18B20 all works fine, so in this code I use ROM from real device and send brightness at request.

#include "Arduino.h"
#include "LowLevel.h"
#include "OneWireSlave.h"

// Defines to access to Low and High bytes
#define Lo(x)   ((x) & 0xFF) 
#define Hi(x)   (((x)>>8) & 0xFF) 

// This is the pin that will be used for one-wire data (depending on your arduino model, you are limited to a few choices, because some pins don't have complete interrupt support)
// On Arduino Uno, you can use pin 2 or pin 3
Pin oneWireData(2);
#define brightnessPin 0

// This is the ROM the arduino will respond to, make sure it doesn't conflict with another device, 0x28 - DS18B20 identificator
const byte owROM[] = {0x28, 0xF2, 0x27, 0xD6, 0x05, 0x00, 0x00, 0x96};


// 0 - Low Byte Temperature 
// 1 - High Byte Temperature
// 2-7 - reserved
// 8 - CRC8
byte SCRATCHPAD[] = {0x00, 0x00, 0x4B, 0x46, 0x7F, 0xFF, 0x05, 0x10, 0x00};

int brightness = 0;

// This function will be called each time the OneWire library has an event to notify (reset, error, byte received)
void owReceive(OneWireSlave::ReceiveEvent evt, byte data);

void setup() {
	// Setup the OneWire library
	OneWire.setReceiveCallback(&owReceive);
	OneWire.begin(owROM, oneWireData.getPinNumber());
  Serial.begin(9600);
}

void loop() {
  Serial.print("brightness=");
  Serial.println(brightness);
  delay(5000);
  
	cli();//disable interrupts

	sei();//enable interrupts
}

void owReceive(OneWireSlave::ReceiveEvent evt, byte data) {
	switch (evt) {
    case OneWireSlave::RE_Reset:
      break;
    case OneWireSlave::RE_Byte:
      // Start measuring brightness
      if (data == 0x44) {
        brightness = analogRead(brightnessPin);     
        // Add brightness to the SCRATCHPAD
        SCRATCHPAD[0] = Lo(brightness*16);
        SCRATCHPAD[1] = Hi(brightness*16);
        // Add CRC8 to the SCRATCHPAD
        SCRATCHPAD[8] = OneWire.crc8(SCRATCHPAD,8);  
      }
    
      // Read Scratchpad [BEh] command
      if (data == 0xBE) {
        // Send SCRATCHPAD 9 byte with brightness and CRC
        for (byte i = 8, j = 1; j<=9; i--, j++) {
          OneWire.write(&SCRATCHPAD[i], j, NULL);
        }
      }        
      break;
    case OneWireSlave::RE_Error:
		  break;
	  default:
		;
	}
}

DS18B20Emulator_brightness.zip (9.62 KB)

@Youen Thank you for your answer.

Hi, I am trying to port the one wire libraries from Arduino to ATTiny85. My sketch, which just returns addresses of all slaves on the line, works on Arduino Uno Master - Uno Slave combination. But when I program the same sketch with .cpp and .h files into the ATtiny85 slave at 8MHz internal clock or 1MHz internal clock, I don’t get the addresses as intended on my serial monitor. What might be the problem? I am using only ds.init(rom) and ds.waitForRequest(false) functions in my sketch.

Hi, i read this thread highly interested, because i want to make my Water-Counter one-Wire-accessable with an Arduino Nano and the DS9490R. I tried both Sources, from Post #102 and #105, but the device does not appear in the List of my OW-Server (Linux Ubuntu).

Then i put a Serial.println("RESET"); in the RESET-Part of the

case OneWireSlave::RE_Reset:

This produces 8 RESET-Entries in my Serial Console (one for every native DS18B20 that i'm using on my BUS).

So the Uno recognizes the Signal from the Controller. I also put another Serial.print in the RE_Byte-Part, but after Reading Post #103, i wondered if this could be my Problem.

But the original Program without any changes also does not work.

I could remove my Arduino Uno out of my Productive environment for testing, but i don't really belive it would solve my problem.

Can you help? Any suggestions? Thanks

Stephan

[quote author=Coding Badly link=msg=481145 date=1309853409] There have been a few brief discussions of a 1-Wire slave library. I'd like to revisit the idea.

  1. Is it possible for an AVR processor to run from "parasitic power"?

[/quote]

Hi, has anybody found a way to parasitically power the ATtiny85 slaves? I got it working for 1-Wire using Markus' library, but parasitic power requires the use of two pins - Tx and Rx.

Any suggestions would be very helpful.

Thanks

Hi

I have not been succesful in getting this (the code included in the post - #58) to work. I am using Arduino IDE 1.6.7 and programming the ATtiny85 (8Mhz) with Arduino Nano.

The code compile but no response from the 1-wire. Code in later in this topic will not compile.

Any suggestions are appreciated. Erik

I am trying to get the source in post #59 to work on an ATtiny85, running 8Mhz. It compiles and upload (using Arduino 1.6.7), but I cannot see the device (on OWFS). ATtiny lib is version 1.0.1 by David A Melis (http://highlowtech.org/?p=1695);

What am I doing wrong?

Hello, I implemented the code posted in #104 and #105 on two arduino nano, one master and second one slave. The command sent by master (i.e. 0xBE, 0x44) is correctly received by slave. But the communication in the other direction fails, the data received by master do no comply with those sent by the slave.

Another problem occures during device search. The arduino slave can't be found if alone on the bus. But with an original DS18B20 on the bus, all devices were recognized (incl. arduino slave).

I replaced the Nano slave by a mega 2560 without any improvement.

Any idea what's going wrong?

regards, CB

Hi, I'm trying using the 1-Wire Slave Library and I'm really far to make it working.

I've a master that it's able to read a DS18B20 without any problem. When I replace it with the Arduino Slave, it does not work (Reset signal is recognized but master does not receive the address).

I replaced the library with this one and it works like a charm, so the wiring is ok.

I suspect the reason why the 1-Wire Slave Library does not work is related timing. In the implementation i noted:

    const unsigned long ResetMinDuration = 480;
    const unsigned long ResetMaxDuration = 900;
    const unsigned long PresenceWaitDuration = 30;
    const unsigned long PresenceDuration = 300;
    const unsigned long ReadBitSamplingTime = 25;
    const unsigned long SendBitDuration = 35;

that are used by

    void OneWireSlave::setTimerEvent_(short delayMicroSeconds, void(*handler)())

In my case, I'm using and Arduino Pro Mini 3.3V that is running 8MHz. I suspect that the above settings are related to 16MHz arduino.

Could you confirm that the implementation is frequency dependent? If it's the case does anyone have the setting for 8MHz?

Thanks hjfabius

There is indeed a risk that any change on the arduino hardware results in timing issues. Most of the code should be independent of the microcontroller frequency (it counts microseconds, not ticks), but even then, if instructions are executed slower maybe you’ll need to adjust by waiting a little less here and there, if at all possible.

There is also a part that is clearly dependent on frequency: the timer configuration. It is currently configured for 16MHz, and halving that frequency will double all timer durations. The timer is used when an event must happen after a predetermined time, rather than on an input pin state change (i.e. reset detection and bit reading).

The relevant line is probably this one:

[color=navy]tccr1bEnable_ = (1 << WGM12) | (1 << CS11) | (1 << CS10);[/color] [color=limegreen]// turn on CTC mode with 64 prescaler[/color]

which is used to configure the timer in function setTimerEvent_

You’ll need to check the documentation of your microcontroller to find how to set the prescaler to 32 instead of 64. I should probably replace this code by a portable timer library if I find one…

To debug this kind of “fast” communications (with timings of a few microseconds), you might need a digital oscilloscope (I would probably not have managed to get it to work on my Uno without one), since you can’t debug by logging to the serial port (too slow). I’ve bought mine for about $50 (second hand), it connects to the computer through USB to visualize data.

For those interested with using my library with an ATTiny, you might find what you want in ntruchsess fork. Don't know the status (probably work in progress), but apparently he is working on it.