Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maximum number of OneWireNg_CurrentPlatform objects? #71

Closed
Ing-Dom opened this issue Jan 25, 2025 · 18 comments · Fixed by #72
Closed

Maximum number of OneWireNg_CurrentPlatform objects? #71

Ing-Dom opened this issue Jan 25, 2025 · 18 comments · Fixed by #72
Labels
bug Something isn't working

Comments

@Ing-Dom
Copy link

Ing-Dom commented Jan 25, 2025

Hi!

Thanks for that great lib.
I recently ran into a problem.. I think when I create more then 2 OneWireNg_CurrentPlatform objects
new (&m_ow) OneWireNg_CurrentPlatform(pin0, false);
my programm chrashes. 2 are fine.

Is this a limitation?

due to the wiring of my project (star topology) I would ne up to 16..

Running on RP2040 with arduino-pico core (within a PlattformIO project..)
for reference
https://github.com/OpenKNX/OFM-THPSensorModule/blob/v1/src/HWSensorchannel_DS18B20.cpp

@pstolarz
Copy link
Owner

pstolarz commented Jan 26, 2025

Hi @Ing-Dom, yes for RP2040 there is a limit for number of 1-wire masters with PIO driver (the default driver for the platform). Please take a look here for details: arduino/ArduinoCore-mbed#316 (comment)

@Ing-Dom
Copy link
Author

Ing-Dom commented Jan 26, 2025

ok, thank you for your reply.
Im now a little bit puzzled, because I thought - based on the documentation which I also understodd wrong - that also on RP2040, Bitbang is the default.
But - shame on me - I didn't do check the code..

# define CONFIG_RP2040_PIO_DRIVER 1

you stated

Using OneWireNg's OneWireNg_PicoRP2040PIO (1-wire PIO driver), it's possible to create up to 4 different 1-wire drivers working on 4 different GPIO pins - 2 of them on PIO-0 and 2 on PIO-1 (provided no other PIO program is already uploaded). PIO number for use by the driver is passed by 3rd argument of OneWireNg_PicoRP2040PIO constructor (by default PIO-0 is used).

Do I understand correctly:
When I need more than 2 drivers, I need to create the OneWireNg_PicoRP2040PIO myself, because I need to pass PIO-1 to number 3+4, right?
Is that the reason it crashes?

Anyway, I need to poll 16 DS18B20, connected to 16 different GPIO.
But I don't need to do this in parallel - so I could re-use one or 2 drivers to do that - but on a different pin.
right? Any advice how to do that?

@pstolarz
Copy link
Owner

Yes, you need to create 4 OneWireNg_PicoRP2040PIO objects 2 of them with 0 as 3rd arg for the class constructor and 2 with 1. See

OneWireNg_PicoRP2040PIO(unsigned pin, bool pullUp, int pioNum = 0)

What I don't understand it's why you need 16 masters to handle 16 sensors, instead having them all connected to single wire controlled by a single master.

@Ing-Dom
Copy link
Author

Ing-Dom commented Jan 26, 2025

because my hardware main purpose was to be able to connect up to 8 SHT3x (or other I2C Sensors) which only can be used once on one Bus.
Later there was the idea to also allow DS18B20 - and having only one sensor per GPIO make you free from assigning Sensor IDs..
thats the hardware:
https://github.com/OpenKNX/OpenKNX/wiki/SEN-UP1-8xTH

Is it possible to re-assign a different pin to a specific driver in programm flow?

@pstolarz
Copy link
Owner

pstolarz commented Jan 26, 2025

Yes you may destroy the driver na calling it's destructor and recreating it by calling new in-place constructor with different GPIO.

EDIT: I just realized that there is no destructor implementation for the PIO driver, so it won't help you. I need to investigate the PIO API if it supports freeing the loaded PIO program to properly implement the destructor.

@Ing-Dom
Copy link
Author

Ing-Dom commented Jan 26, 2025

I just wanted to answer you, that that would have been my first guess also, but there is no destructor :)
But you discovered yourself in the meantime.

I'll check the bitbang drivers in the meantime and thanks for your help ! Really appreciate it.

@pstolarz
Copy link
Owner

I don't recommend the BB driver for RP2040, the PIO one is much better. I'm gonna do some fixes for the PIO driver soon and give you for tests.

@pstolarz pstolarz added the enhancement New feature or request label Jan 26, 2025
@pstolarz
Copy link
Owner

Take a look at branch pio_driver_deinit, where I've added destructor for OneWireNg_PicoRP2040PIO class. From now, you should be able to create/destroy the PIO drivers on the fly to handle as many 1-wire buses as you need. Let me know if it works for you.

I've also updated the README to contain information how to create many PIO drivers controlling different buses. I hope that will give some insight for more advanced library users.

@Ing-Dom
Copy link
Author

Ing-Dom commented Jan 26, 2025

ohhhh :) that was fast, thanks, Ill give it a try immidiatelly

@Ing-Dom
Copy link
Author

Ing-Dom commented Jan 26, 2025

        Placeholder<OneWireNg_PicoRP2040PIO> m_ow;
        Placeholder<OneWireNg_PicoRP2040PIO> m_ow2;
  
 new (&m_ow) OneWireNg_PicoRP2040PIO(pin0, false);
    new (&m_ow2) OneWireNg_PicoRP2040PIO(pin1, false);

    DSTherm drv(m_ow);
    drv.writeScratchpadAll(0, 0, DSTherm::RES_12_BIT);

    DSTherm drv2(m_ow2);
    drv2.writeScratchpadAll(0, 0, DSTherm::RES_12_BIT);

    delete (&m_ow); // <- that crashes. Is that correct? Im not so familiar what you are doing here with the template class..

ok - this way it does not anymore crash. I continue testing sensor readings..

void HWSensorchannel_DS18B20::Setup(uint8_t pin0, uint8_t pin1, uint8_t channel_number)
{
    HWSensorchannel::Setup(pin0, pin1, channel_number);

    OneWireNg_PicoRP2040PIO _ow0(pin0, false);
    OneWireNg_PicoRP2040PIO _ow1(pin1, false);

    DSTherm drv0(_ow0);
    drv0.writeScratchpadAll(0, 0, DSTherm::RES_12_BIT);

    DSTherm drv1(_ow1);
    drv1.writeScratchpadAll(0, 0, DSTherm::RES_12_BIT);

    drv0.~DSTherm();
    drv1.~DSTherm();

    _ow0.~OneWireNg_PicoRP2040PIO();
    _ow1.~OneWireNg_PicoRP2040PIO();
}

@pstolarz
Copy link
Owner

pstolarz commented Jan 26, 2025

Not exactly, if you are using in-place driver creation:

Placeholder<OneWireNg_PicoRP2040PIO> ow;

new (&ow) OneWireNg_PicoRP2040PIO(pin0, false);

DSTherm drv(ow);
// ...

// virtually calls destructor in-place w/o actual memory deallocation
(*ow).~OneWireNg();

for classic allocation/deallocation case:

OneWireNg *ow = new OneWireNg_PicoRP2040PIO(pin0, false);

DSTherm drv(*ow);
// ...

// delete the object
delete ow;

EDIT: If you create OneWireNg_PicoRP2040PIO objects locally (inside some method for example), you don't need to call destructors explicitly - they will be called automatically by the compiler.

@Ing-Dom
Copy link
Author

Ing-Dom commented Jan 26, 2025

hmm there seems to be some problem.
I poll one sensor after the other - this way:

{
m_ow0 = new OneWireNg_PicoRP2040PIO(m_pin0, false);
DSTherm drv0(*m_ow0);
drv0.convertTempAll(0, false);
}

delay..

{
DSTherm drv0(*m_ow0);

static Placeholder<DSTherm::Scratchpad> scrpd;
OneWireNg::ErrorCode ec = drv0.readScratchpadSingle(scrpd);
if (ec == OneWireNg::EC_SUCCESS)
{
	long temp = scrpd->getTemp2();
	SetTemperature((float)temp / 16);
}
else if (ec == OneWireNg::EC_CRC_ERROR)
{
	logDebugP("CRC-Error 0");
}
else
{
	logDebugP("Onewire Error 0x%x",ec);
}

drv0.~DSTherm();
m_ow0->~OneWireNg_PicoRP2040PIO();
}

the first two sensor - regardless which pin - work.
all other sensors / pins return a crc error.
when i reach the first two again, they work, all others don't.

@pstolarz
Copy link
Owner

Thanks, I will look at this later today...

@pstolarz
Copy link
Owner

I've just tested the fix on my setup (RP2040, PicoSDK lib) and works as expected. Code as follows:

static void printScratchpad(const DSTherm::Scratchpad& scrpd)
{
    const uint8_t *scrpd_raw = scrpd.getRaw();

    printf("  Scratchpad:");
    for (size_t i = 0; i < DSTherm::Scratchpad::LENGTH; i++)
        printf("%c%02x", (!i ? ' ' : ':'), scrpd_raw[i]);

    printf("; Th:%d; Tl:%d; Resolution:%d",
        scrpd.getTh(), scrpd.getTl(),
        9 + (int)(scrpd.getResolution() - DSTherm::RES_9_BIT));

    long temp = scrpd.getTemp2();
    printf("; Temp:");
    if (temp < 0) {
        temp = -temp;
        printf("-");
    }
    printf("%d.%04d C\n", (int)temp / 16, (10000 * ((int)temp % 16)) / 16);
}

int main()
{
    stdio_init_all();

    Placeholder<DSTherm::Scratchpad> scrpd;

    for(int i = 0;; i++) {
        int pin = (i % 16);
        printf("[PIN %d]\n", pin);

        auto ow = OneWireNg_CurrentPlatform(pin, false);
        DSTherm drv(ow);

        drv.convertTempAll(DSTherm::MAX_CONV_TIME, false);

        for (const auto& id: ow) {
            OneWireNg::ErrorCode ec = drv.readScratchpad(id, scrpd);
            if (ec == OneWireNg::EC_SUCCESS) {
                printScratchpad(scrpd);
            } else {
                printf(" ERROR: %d\n", ec);
            }
        }
    }

    return 0;
}  

so, I'm probing GPIOs 0-15 in the main loop against connected sensors printing readouts on the console. Try this code on your setup.

@Ing-Dom
Copy link
Author

Ing-Dom commented Jan 27, 2025

I had to modify you code a little bit to run within my arduino-pico environment.

#include <stdio.h>
#include "pico/stdio.h"

#include "OneWireNg_CurrentPlatform.h"
#include "drivers/DSTherm.h"
#include "utils/Placeholder.h"
#include "platform/Platform_Delay.h"
#include "DallasTemperature.h"

static void printScratchpad(const DSTherm::Scratchpad& scrpd)
{
    const uint8_t *scrpd_raw = scrpd.getRaw();

    Serial.printf("  Scratchpad:");
    for (size_t i = 0; i < DSTherm::Scratchpad::LENGTH; i++)
        Serial.printf("%c%02x", (!i ? ' ' : ':'), scrpd_raw[i]);

    Serial.printf("; Th:%d; Tl:%d; Resolution:%d",
        scrpd.getTh(), scrpd.getTl(),
        9 + (int)(scrpd.getResolution() - DSTherm::RES_9_BIT));

    long temp = scrpd.getTemp2();
    Serial.printf("; Temp:");
    if (temp < 0) {
        temp = -temp;
        Serial.printf("-");
    }
    Serial.printf("%d.%04d C\n", (int)temp / 16, (10000 * ((int)temp % 16)) / 16);
}

void setup()
{

    Placeholder<DSTherm::Scratchpad> scrpd;
    uint8_t mypins[4] = {22,23,24,25};

    Serial.begin();
    while(!Serial)
        delay(1);
    Serial.println("Start reading DS18B20..");
    delay(1000);
    Serial.println("Start reading DS18B20..");
    delay(1000);
    Serial.println("Start reading DS18B20..");
    delay(1000);

    for(int i = 0;; i++) {
        int pin = mypins[i % 4];
        Serial.printf("[PIN %d]\n", pin);

        auto ow = OneWireNg_CurrentPlatform(pin, false);
        DSTherm drv(ow);

        drv.convertTempAll(DSTherm::MAX_CONV_TIME, false);

        for (const auto& id: ow) {
            OneWireNg::ErrorCode ec = drv.readScratchpad(id, scrpd);
            if (ec == OneWireNg::EC_SUCCESS) {
                printScratchpad(scrpd);
            } else {
                Serial.printf(" ERROR: %d\n", ec);
            }
        }
    }
}

void loop()
{}

it prints;

[PIN 22]
Scratchpad: 77:01:00:00:7f:ff:7f:10:8b; Th:0; Tl:0; Resolution:12; Temp:23.4375 C
[PIN 23]
Scratchpad: 77:01:00:00:7f:ff:7f:10:8b; Th:0; Tl:0; Resolution:12; Temp:23.4375 C
[PIN 24]
Scratchpad: 74:01:00:00:7f:ff:7f:10:4e; Th:0; Tl:0; Resolution:12; Temp:23.2500 C
[PIN 25]
Scratchpad: 70:01:00:00:7f:ff:7f:10:5b; Th:0; Tl:0; Resolution:12; Temp:23.0000 C
[PIN 22]
Scratchpad: 77:01:00:00:7f:ff:7f:10:8b; Th:0; Tl:0; Resolution:12; Temp:23.4375 C
[PIN 23]
Scratchpad: 77:01:00:00:7f:ff:7f:10:8b; Th:0; Tl:0; Resolution:12; Temp:23.4375 C
[PIN 24]
Scratchpad: 74:01:00:00:7f:ff:7f:10:4e; Th:0; Tl:0; Resolution:12; Temp:23.2500 C
[PIN 25]
Scratchpad: 70:01:00:00:7f:ff:7f:10:5b; Th:0; Tl:0; Resolution:12; Temp:23.0000 C
[PIN 22]
Scratchpad: 77:01:00:00:7f:ff:7f:10:8b; Th:0; Tl:0; Resolution:12; Temp:23.4375 C
[PIN 23]
Scratchpad: 77:01:00:00:7f:ff:7f:10:8b; Th:0; Tl:0; Resolution:12; Temp:23.4375 C
[PIN 24]
Scratchpad: 74:01:00:00:7f:ff:7f:10:4e; Th:0; Tl:0; Resolution:12; Temp:23.2500 C
[PIN 25]
Scratchpad: 70:01:00:00:7f:ff:7f:10:5b; Th:0; Tl:0; Resolution:12; Temp:23.0000 C

so, it seems to work if you do it the right way. I'll try again to integrate into my setup..

@tyeth
Copy link

tyeth commented Jan 27, 2025

Wow, what timing, we have a bug related to destroying and reinitialising the pio driver (user adds/removes DS18b20 pin device and we initialise/delete the bus - works 1st and 2nd time and crashes 3rd), I was going to suggest implementing the destructor, but you did yesterday!

Looks like I missed turning off the outputs (instead just unclaiming them). I'll retest with your PR tomorrow morning.

@pstolarz
Copy link
Owner

@tyeth Let me know about results. I'm going to merge #72 later today, do you need a new OneWireNg release with the change?

@pstolarz pstolarz added bug Something isn't working and removed enhancement New feature or request labels Jan 28, 2025
@tyeth
Copy link

tyeth commented Jan 28, 2025

@pstolarz looks good, thank you! Retested this PR. A release shouldn't be necessary as we git clone this repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants