orgua / OneWireHub

OneWire slave device emulator
GNU General Public License v3.0
343 stars 86 forks source link

Supported and tested Hardware: Attiny84 - Global variables use 574 bytes (112%) of dynamic memory #124

Closed rtek1000 closed 1 year ago

rtek1000 commented 1 year ago

Supported and tested Hardware

embedded real life test
    setup: run test-example, use ds9490-master, arduino 1.8.3, Windows 10 and the board-library named in the brackets

Hi,

I tried to compile using this core ATTinyCore for Attiny84 and the example DS18B20 as thermometer:

Sketch uses 6170 bytes (75%) of program storage space. Maximum is 8192 bytes. data section exceeds available space in boardGlobal variables use 574 bytes (112%) of dynamic memory, leaving -62 bytes for local variables. Maximum is 512 bytes.

Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint. Error compiling for board ATtiny24/44/84(a) (No bootloader).

Any tips to shorten this code?

I would like to read the TMP117 sensor and send the data as if it were a DS18B20.

orgua commented 1 year ago

Well, you see the example DS18B20_thermometer.ino and it's overhead:

after removing this I get (for normal arduino, but tiny should be close):

Sketch uses 4576 bytes (14%) of program storage space. Maximum is 30720 bytes.
Global variables use 135 bytes (6%) of dynamic memory, leaving 1913 bytes for local variables. Maximum is 2048 bytes.

So there should be plenty of space left for your tmp117

rtek1000 commented 1 year ago

Hi @orgua ,

Assuming that scratchpad is private, how can I edit these registers in Setup() or Loop()?

I tried using the constructor, but it didn't accept it.

class DS18B20 : public OneWireItem
{
private:

    uint8_t scratchpad[9];

    void updateCRC(void);

https://github.com/orgua/OneWireHub/blob/main/src/DS18B20.h

DS18B20::DS18B20(uint8_t ID1, uint8_t ID2, uint8_t ID3, uint8_t ID4, uint8_t ID5, uint8_t ID6, uint8_t ID7) : OneWireItem(ID1, ID2, ID3, ID4, ID5, ID6, ID7)
{
    scratchpad[0] = 0xA0; // TLSB --> 10 degC as std
    scratchpad[1] = 0x00; // TMSB
    scratchpad[2] = 0x4B; // THRE --> Trigger register TH
    scratchpad[3] = 0x46; // TLRE --> TLow
    scratchpad[4] = 0x7F; // Conf
    // = 0 R1 R0 1 1 1 1 1 --> R=0 9bit .... R=3 12bit
    scratchpad[5] = 0xFF; // 0xFF
    scratchpad[6] = 0x00; // Reset
    scratchpad[7] = 0x10; // 0x10
    updateCRC(); // update scratchpad[8]

    ds18s20_mode = (ID1 == 0x10); // different tempRegister

    // disable bus-features:
    fast_search_rom = false;
}

https://github.com/orgua/OneWireHub/blob/main/src/DS18B20.cpp

rtek1000 commented 1 year ago

I believe I made a mistake, the TMP117 datasheet mentions SCRATCHPAD in the description of EEPROM1 and EEPROM3, but the DS18B20 datasheet says 64-Bit Lasered ROM Code.

What I'm trying to change are the 48-BIT SERIAL NUMBER, contained in the 64-Bit Lasered ROM Code.

rtek1000 commented 1 year ago

ROM Commands After the bus master has detected a presence pulse, it can issue a ROM command. These commands operate on the unique 64-bit ROM codes of each slave device and allow the master to single out a specific device if many are present on the 1-Wire bus. These commands also allow the master to determine how many and what types of devices are present on the bus or if any device has experienced an alarm condition. There are five ROM commands, and each command is 8 bits long. The master device must issue an appropriate ROM command before issuing a DS18B20 function command.

Search Rom [F0h] When a system is initially powered up, the master must identify the ROM codes of all slave devices on the bus, which allows the master to determine the number of slaves and their device types. The master learns the ROM codes through a process of elimination that requires the master to perform a Search ROM cycle (i.e., Search ROM command followed by data exchange) as many times as necessary to identify all of the slave devices. If there is only one slave on the bus, the simpler Read ROM [33h] command can be used in place of the Search ROM process. For a detailed explanation of the Search ROM procedure, refer to Application Note 937: Book of iButton® Standards. After every Search ROM cycle, the bus master must return to Step 1 (Initialization) in the transaction sequence.

Read Rom [33h] This command can only be used when there is one slave on the bus. It allows the bus master to read the slave’s 64-bit ROM code without using the Search ROM proce- dure. If this command is used when there is more than one slave present on the bus, a data collision will occur when all the slaves attempt to respond at the same time.

Match Rom [55H] The match ROM command followed by a 64-bit ROM code sequence allows the bus master to address a specific slave device on a multidrop or single-drop bus. Only the slave that exactly matches the 64-bit ROM code sequence will respond to the function command issued by the master; all other slaves on the bus will wait for a reset pulse.

rtek1000 commented 1 year ago

C.3. Search ROM Command Even if the master does not know the serial numbers of the devices connected to the 1–Wire bus, it is possible to address one single device at a time. This is done by using the command Search ROM, code F0H. This com- mand acts like Read ROM combined with Match ROM. All iButtons will sequentially send the true and the false value of the actual ROM bit during the two Read time slots following the Search ROM command. If all devices have a 0 in this bit position, the reading will be 01; if the bit position contains a 1, the result will be 10. If both, a 1 and a 0 occur in this bit position, reading will result in two 0 bits, indicating a conflict. The master now has to send the bit value 1 or 0 to select the devices that will remain in the process of selection. All deselected devices will be idle until they receive a Reset Pulse. After the first stage of selection, 63 reading/selecting cycles will fol- low, until finally the master has learned one device’s ROM code and simultaneously has addressed it. Each stage of selection consists of two Read time slots and one Write time slot. The complete process of learning and simultaneous addressing is about three times the length of the Match ROM command, but it allows selec- tion of all the connected devices sequentially without knowing the ROM values beforehand. In an application where the iButtons are fixed in position on the 1–Wire bus, it is most efficient for the master to evaluate all ROM contents with the Search ROM command and then to use the Match ROM command to address spe- cific devices. If the application requires constant identifi- cation and communication with new devices as they come and go, it will be necessary to use the Search ROM command to identify and address each new part.

https://pdfserv.maximintegrated.com/en/an/AN937.pdf

orgua commented 1 year ago

Just for reference:

What I'm trying to change are the 48-BIT SERIAL NUMBER, contained in the 64-Bit Lasered ROM Code.

so you don't want the scratchpad but ID1:7 - one of these is the family code the others add up to 48 bit.

rtek1000 commented 1 year ago

As guided in https://github.com/orgua/OneWireHub/pull/125,

Alternative Var 1 didn't work (another user said it worked, but it doesn't work if CRC is required).

  ds18b20.ID[1] = 0x01;

  // Setup OneWire
  hub.attach(ds18b20);

DS18B20 emulator:

/*
      Example-Code that emulates a DS18B20

      Tested with:
      - https://github.com/PaulStoffregen/OneWire --> DS18x20-Example, atmega328@16MHz as Slave
      - DS9490R-Master, atmega328@16MHz and teensy3.2@96MHz as Slave
*/

#include "OneWireHub.h"
#include "DS18B20.h"  // Digital Thermometer, 12bit

constexpr uint8_t pin_led       { 13 };
constexpr uint8_t pin_onewire   { 8 };

auto hub    = OneWireHub(pin_onewire);

auto ds18b20 = DS18B20(DS18B20::family_code, 0x00, 0x00, 0xB2, 0x18, 0xDA, 0x00); // DS18B20: 9-12bit, -55 -  +85 degC

bool blinking(void);

void setup()
{
  Serial.begin(9600);
  Serial.println("OneWire-Hub DS18B20 Temperature-Sensor");
  Serial.flush();

  pinMode(pin_led, OUTPUT);

  ds18b20.ID[1] = 0x01; // <------------------- New serial number

  // Setup OneWire
  hub.attach(ds18b20);

  Serial.print("Test - set Temperatures to 21 degC: ");
  const int8_t temperature = 21;
  ds18b20.setTemperature(temperature);
  Serial.println(ds18b20.getTemperature());

  Serial.println("config done");
}

void loop()
{
  // following function must be called periodically
  hub.poll();
  // this part is just for debugging (USE_SERIAL_DEBUG in OneWire.h must be enabled for output)
  if (hub.hasError()) hub.printError();

  // Blink triggers the state-change
  if (blinking())
  {
    // Set temp
    static float temperature = 20.0;
    temperature += 0.1;
    if (temperature > 30.0) temperature = 20.0;
    ds18b20.setTemperature(temperature);
    Serial.println(temperature);
  }
}

bool blinking(void)
{
  constexpr  uint32_t interval    = 1000;          // interval at which to blink (milliseconds)
  static uint32_t nextMillis  = millis();     // will store next time LED will updated

  if (millis() > nextMillis)
  {
    nextMillis += interval;             // save the next time you blinked the LED
    static uint8_t ledState = LOW;      // ledState used to set the LED
    if (ledState == LOW)    ledState = HIGH;
    else                    ledState = LOW;
    digitalWrite(pin_led, ledState);
    return 1;
  }
  return 0;
}

Tester.ino (modified):

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
#define TEMPERATURE_PRECISION 9 // Lower resolution

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

int numberOfDevices; // Number of temperature devices found

DeviceAddress tempDeviceAddress; // We'll use this variable to store a found device address

void setup(void)
{
  pinMode(13, OUTPUT);

  digitalWrite(13, LOW);
  // start serial port
  Serial.begin(9600);
  Serial.println("Dallas Temperature IC Control Library Demo");
}

void initSensors(void) {
  // Start up the library
  sensors.begin();

  // Grab a count of devices on the wire
  numberOfDevices = sensors.getDeviceCount();

  // locate devices on the bus
  Serial.print("Locating devices...");

  Serial.print("Found ");
  Serial.print(numberOfDevices, DEC);
  Serial.println(" devices.");

  // report parasite power requirements
  Serial.print("Parasite power is: ");
  if (sensors.isParasitePowerMode()) Serial.println("ON");
  else Serial.println("OFF");

  // Loop through each device, print out address
  for (int i = 0; i < numberOfDevices; i++)
  {
    // Search the wire for address
    if (sensors.getAddress(tempDeviceAddress, i))
    {
      Serial.print("Found device ");
      Serial.print(i, DEC);
      Serial.print(" with address: ");
      printAddress(tempDeviceAddress);
      Serial.println();

      Serial.print("Setting resolution to ");
      Serial.println(TEMPERATURE_PRECISION, DEC);

      // set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions)
      sensors.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION);

      Serial.print("Resolution actually set to: ");
      Serial.print(sensors.getResolution(tempDeviceAddress), DEC);
      Serial.println();
    } else {
      Serial.print("Found ghost device at ");
      Serial.print(i, DEC);
      Serial.print(" but could not detect address. Check power and cabling");
    }
  }
}

// function to print the temperature for a device
void printTemperature(DeviceAddress deviceAddress)
{
  // method 1 - slower
  //Serial.print("Temp C: ");
  //Serial.print(sensors.getTempC(deviceAddress));
  //Serial.print(" Temp F: ");
  //Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit

  // method 2 - faster
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == DEVICE_DISCONNECTED_C)
  {
    Serial.println("Error: Could not read temperature data");
    return;
  }
  Serial.print("Temp C: ");
  Serial.print(tempC);
  Serial.print(" Temp F: ");
  Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
}

void loop(void)
{
  initSensors();

  digitalWrite(13, digitalRead(2));

  delay(500);

  // call sensors.requestTemperatures() to issue a global temperature
  // request to all devices on the bus
  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures

  // Loop through each device, print out temperature data
  for (int i = 0; i < numberOfDevices; i++)
  {
    // Search the wire for address
    if (sensors.getAddress(tempDeviceAddress, i))
    {
      // Output the device ID
      Serial.print("Temperature for device: ");
      Serial.println(i, DEC);

      // It responds almost immediately. Let's print out the data
      printTemperature(tempDeviceAddress); // Use a simple function to print out the data
    }
    //else ghost device! Check your power requirements and cabling

  }

  Serial.println("DONE\r\n\r\n");
}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}
rtek1000 commented 1 year ago

If updating the CRC this works:

  //Changing ROM serial number
  ds18b20.ID[1] = 0x12; // ID1...ID6 (48-BIT SERIAL NUMBER)

  // Update CRC after changing ROM serial number
  ds18b20.ID[7] = ds18b20.crc8(ds18b20.ID, 7);

  // Setup OneWire
  hub.attach(ds18b20);
orgua commented 1 year ago

Well, I can't help you with that one. First I don't know what you are trying to accomplish. Second, Var 1 is working for me and you keep the error-description to yourself. But you are right, some masters require a valid crc in ID7.

Your example-code shows that you can just set ID1 during construction (that could also be done in loop()):

DS18B20(ID1, 0x00, 0x00, 0xB2, 0x18, 0xDA, 0x00);

rtek1000 commented 1 year ago

Yes, in loop() it is possible to change the serial number, as long as you remove and add it, it is not necessary to add a new one (As instructed in Var 2 https://github.com/orgua/OneWireHub/discussions/119)


    hub.detach(ds18b20);

    ds18b20.ID[1] = new_serial_number++;

    ds18b20.ID[7] = ds18b20.crc8(ds18b20.ID, 7);

    // Setup OneWire
    hub.attach(ds18b20);

Full code:

/*
      Example-Code that emulates a DS18B20

      Tested with:
      - https://github.com/PaulStoffregen/OneWire --> DS18x20-Example, atmega328@16MHz as Slave
      - DS9490R-Master, atmega328@16MHz and teensy3.2@96MHz as Slave
*/

#include "OneWireHub.h"
#include "DS18B20.h"  // Digital Thermometer, 12bit

constexpr uint8_t pin_led       { 13 };
constexpr uint8_t pin_onewire   { 8 };

auto hub    = OneWireHub(pin_onewire);

auto ds18b20 = DS18B20(DS18B20::family_code, 0x00, 0x00, 0xB2, 0x18, 0xDA, 0x00); // DS18B20: 9-12bit, -55 -  +85 degC

bool blinking(void);

uint8_t new_serial_number = 0;

void setup()
{
  Serial.begin(9600);
  Serial.println("OneWire-Hub DS18B20 Temperature-Sensor");
  Serial.flush();

  pinMode(pin_led, OUTPUT);

  ds18b20.ID[1] = 0x12;

  ds18b20.ID[7] = ds18b20.crc8(ds18b20.ID, 7);

  // Setup OneWire
  hub.attach(ds18b20);

  Serial.print("Test - set Temperatures to 21 degC: ");
  const int8_t temperature = 21;
  ds18b20.setTemperature(temperature);
  Serial.println(ds18b20.getTemperature());

  Serial.println("config done");
}

void loop()
{
  // following function must be called periodically
  hub.poll();
  // this part is just for debugging (USE_SERIAL_DEBUG in OneWire.h must be enabled for output)
  if (hub.hasError()) hub.printError();

  // Blink triggers the state-change
  if (blinking())
  {
    hub.detach(ds18b20);

    ds18b20.ID[1] = new_serial_number++;

    ds18b20.ID[7] = ds18b20.crc8(ds18b20.ID, 7);

    // Setup OneWire
    hub.attach(ds18b20);

    // Set temp
    static float temperature = 20.0;
    temperature += 0.1;
    if (temperature > 30.0) temperature = 20.0;
    ds18b20.setTemperature(temperature);
    Serial.println(temperature);
  }
}

bool blinking(void)
{
  constexpr  uint32_t interval    = 1000;          // interval at which to blink (milliseconds)
  static uint32_t nextMillis  = millis();     // will store next time LED will updated

  if (millis() > nextMillis)
  {
    nextMillis += interval;             // save the next time you blinked the LED
    static uint8_t ledState = LOW;      // ledState used to set the LED
    if (ledState == LOW)    ledState = HIGH;
    else                    ledState = LOW;
    digitalWrite(pin_led, ledState);
    return 1;
  }
  return 0;
}
rtek1000 commented 1 year ago

Var 1 is working for me and you keep the error-description to yourself

There is not much to comment, the master simply does not find the device:

Serial Monitor output from Tester.ino (previously mentioned code):

Wrong CRC:

08:29:06.164 -> Locating devices...Found 0 devices. 08:29:06.197 -> Parasite power is: OFF 08:29:06.661 -> Requesting temperatures...DONE

Correct CRC:

08:30:15.419 -> Locating devices...Found 1 devices. 08:30:15.452 -> Parasite power is: OFF 08:30:15.452 -> Found device 0 with address: 280700B218DA00DD 08:30:15.518 -> Setting resolution to 9 08:30:15.551 -> Resolution actually set to: 9 08:30:16.015 -> Requesting temperatures...Temperature for device: 0 08:30:16.082 -> Temp C: 20.87 Temp F: 69.57 08:30:16.115 -> DONE

This library uses CRC, it's one of the best I've seen for DS18B20: https://github.com/milesburton/Arduino-Temperature-Control-Library

This library supports the following devices : DS18B20 DS18S20 DS1822 DS1820 MAX31820 MAX31850

rtek1000 commented 1 year ago

Observation, the tests were done with Arduino UNO/Nano with 16MHz 5V, I still need to test if it works with Attiny85, which is the objective.

Apparently Attiny85 has to run at 8MHz for 3.3V (which is the voltage the Datalogger supplies [3.5V ~ 4.2V from 18650 battery]).

orgua commented 1 year ago

things I consider when you just mention "Var 1 didn't work" without any context (error-description, used hardware, software, setup):

Alright then, good luck with https://github.com/milesburton/Arduino-Temperature-Control-Library

rtek1000 commented 1 year ago

things I consider when you just mention "Var 1 didn't work" without any context (error-description, used hardware, software, setup):

* there is actually an issue with this lib

* your compiler complained

* the ID stayed the same, despite the attempt to change it

* your house burned down

* your ow-master complains

Alright then, good luck with https://github.com/milesburton/Arduino-Temperature-Control-Library

About the Arduino-Temperature-Control-Library library, I didn't want to say that it's better than yours, just that it's the best I found to read DS18B20, if your library also reads DS18B20 I haven't tested that part.

I didn't understand some items on your list, can you explain more please?

orgua commented 1 year ago

explanation: I can't look into your head or read between the lines - so I can't help you, sorry. This issue was strange from the beginning as you completely ignored the issue-presets, including the provided help-sections and requests for context. If you don't take the time, why should I?

It became clear that there is no issue with this lib. So please try to find support in some forum, stackoverflow or chatgpt.

Good luck with your project :)

rtek1000 commented 1 year ago

explanation: I can't look into your head or read between the lines - so I can't help you, sorry. This issue was strange from the beginning as you completely ignored the issue-presets, including the provided help-sections and requests for context. If you don't take the time, why should I?

It became clear that there is no issue with this lib. So please try to find support in some forum, stackoverflow or chatgpt.

Good luck with your project :)

Horrible behavior. Avoid treating people like that.

orgua commented 1 year ago

Where did I step on your toe? Would you like to elaborate?

I put many hours into development and documentation and offer everything free of charge on the internet. Github issues are mainly for bugs in the repo & feature-request and less for end-user support and programming-lessons -> Thats what forums are for.

The issue started with a specific programming-problem without enough context, especially the source-code that was troubling you. I tried to guess and help you since, but you present problem after problem, all programming-related and this thread is now far into offtopic-land.

For 100 $/h I'm happy to write your code or teach you c++.