Git Product home page Git Product logo

Comments (8)

dok-net avatar dok-net commented on August 25, 2024 1

@peterk So this is ESP32 (esp_sleep_enable_timer_wakeup), right? Which means, you are on a hardware serial port? In my experience, the SDS011 wakes up upon level changes on it's RX, as high is the stop bit / idle level with RS232, sleep probably causes it to go to low. You could investigate if/how to keep the output pulled up during sleep. What happens if you remove the final delay(1000), perhaps even setting the TX to the SDS011 to LOW immediately after sending the stop message inside the library, will it still accept the command and respond?
The only sure thing that comes to my mind is shutting off the 5V operating power to the SDS011 with some external logic, this would also make sense in terms of conserving (battery) charge during sleep, from my previous tests, with an SDS011 attached even 5000mAh batteries don't last all that long.
Please keep reporting here for any solution you might find.

from esp_sds011.

dok-net avatar dok-net commented on August 25, 2024 1

Thanks for using this lib, and maybe the CoopTasks as well, which as you see I am actively working on and trying to "finish" real soon. Given that you are using external logic now, you could save a little cash and try the ESP8266. It doesn't have internal RTC and the wakeup logic is less advanced, but you can trigger a wakeup via timeout or button just fine with a piece of wire and maybe a resistor, too.
Heh heh, of course this requires another one of my libs, EspSoftwareSerial, but that's part of the Arduino-ESP8266 BSP packages anyway (hint: maybe Serial.swap() USB debug and SW serial to log via SW and run the SDS011 on the HW UART).

from esp_sds011.

peterk avatar peterk commented on August 25, 2024

Thank you for your reply. Yes this is the hardware serial (on an Adafruit Huzzah ESP32). It seems like the SDS011 is pretty good in sleep mode but switching off power completely may be the best option I guess. Maybe a darlington transistor could work?

from esp_sds011.

dok-net avatar dok-net commented on August 25, 2024

@peterk Power consumption of an idle SDS011 aside, I've tested your issue and can't confirm it - the SDS011 stops in ESP32 deep sleep and comes perfectly well alive after timeout.

To combine this with a shameless pitch for (my) cooperative-multitasking on Arduino CoopTask library, here is a working sketch that measures the particulate matter count every 10min. If a button is attached (on ESP32, to GPIO27), the measurement can be started anytime by pressing it. The ESP32 is put into deep sleep while no measurement is taking place. Results are logged on the USB serial output.

/*
    Name:       measure.ino
    Created:	2019-04-06 12:16:00
    Author:     Dirk O. Kaar <[email protected]>
*/

#ifndef ESP32
#include <SoftwareSerial.h>
#endif
#include <Sds011.h>
#include <CoopTask.h>
#include <CoopMutex.h>
#include <CoopSemaphore.h>

#define SDS_PIN_RX D7
#define SDS_PIN_TX D8

#ifdef ESP32
HardwareSerial& serialSDS(Serial2);
Sds011Async< HardwareSerial > sds011(serialSDS);
#else
SoftwareSerial serialSDS;
Sds011Async< SoftwareSerial > sds011(serialSDS);
#endif

#if defined(ESP8266)
constexpr auto LEDON = LOW;
constexpr auto LEDOFF = HIGH;
#else
constexpr auto LEDON = HIGH;
constexpr auto LEDOFF = LOW;
#endif

#if defined(ESP32)
#define BUTTON1 GPIO_NUM_27
#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI)
#define BUTTON1 D3
#else
#define BUTTON1 0
#endif

// This example stops the sensor for 600s, then runs it for 30s, then repeats.
// At tablesizes 19 and below, the tables get filled during duty cycle
// and measurement completes.
// At tablesizes 20 and above, the tables do not get completely filled
// and the rampup / 4 timeout trips, completing measurement at whatever
// number of measurements were recorded in the tables.
constexpr int pm_tablesize = 20;
int pm25_table[pm_tablesize];
int pm10_table[pm_tablesize];

bool is_SDS_running = true;

CoopMutex measureMutex;

CoopSemaphore measureInitializedSema(0);
CoopSemaphore measureStartSema(0);

void start_SDS() {
    CoopMutexLock lock(measureMutex);
    Serial.println("Start wakeup SDS011");

    if (sds011.set_sleep(false)) { is_SDS_running = true; }

    Serial.println("End wakeup SDS011");
}

void stop_SDS() {
    CoopMutexLock lock(measureMutex);
    Serial.println("Start sleep SDS011");

    if (sds011.set_sleep(true)) { is_SDS_running = false; }

    Serial.println("End sleep SDS011");
}

int sds011Loop()
{
    delay(500);
    Sds011::Report_mode report_mode;
    if (!sds011.get_data_reporting_mode(report_mode)) {
        Serial.println("Sds011::get_data_reporting_mode() failed");
    }
    if (Sds011::REPORT_ACTIVE != report_mode) {
        Serial.println("Turning on Sds011::REPORT_ACTIVE reporting mode");
        if (!sds011.set_data_reporting_mode(Sds011::REPORT_ACTIVE)) {
            Serial.println("Sds011::set_data_reporting_mode(Sds011::REPORT_ACTIVE) failed");
        }
    }
    measureInitializedSema.post();
    for (;;)
    {
        {
            CoopMutexLock lock(measureMutex);
            sds011.perform_work();
        }
        yield();
    }
    return 0;
}

int measureLoop()
{
    measureInitializedSema.wait();
    for (;;)
    {
        // Per manufacturer specification, place the sensor in standby to prolong service life.
        // At an user-determined interval (here 30s down plus 30s duty = 1m), run the sensor for 30s.
        // Quick response time is given as 10s by the manufacturer, thus omit the measurements
        // obtained during the first 10s of each run.

        constexpr uint32_t down_s = 600;

        if (!measureStartSema.try_wait())
        {
            stop_SDS();
            Serial.print("stopped SDS011 (is running = ");
            Serial.print(is_SDS_running);
            Serial.println(")");

            measureStartSema.wait(down_s * 1000);
        }

        constexpr uint32_t duty_s = 30;

        start_SDS();
        Serial.print("started SDS011 (is running = ");
        Serial.print(is_SDS_running);
        Serial.println(")");

        sds011.on_query_data_auto_completed([](int n) {
            Serial.println("Begin Handling SDS011 query data");
            int pm25;
            int pm10;
            Serial.print("n = "); Serial.println(n);
            if (sds011.filter_data(n, pm25_table, pm10_table, pm25, pm10) &&
                !isnan(pm10) && !isnan(pm25)) {
                Serial.print("PM10: ");
                Serial.print(float(pm10) / 10);
                Serial.println("ug/qm");
                Serial.print("PM2.5: ");
                Serial.print(float(pm25) / 10);
                Serial.println("ug/qm");
            }
            Serial.println("End Handling SDS011 query data");
            });

        if (!sds011.query_data_auto_async(pm_tablesize, pm25_table, pm10_table)) {
            Serial.println("measurement capture start failed");
        }
        CoopTaskBase::delay(duty_s * 1000);
        measureStartSema.setval(0);
    }
    return 0;
}

#if defined(ESP8266) || defined(ESP32)
class Button {
protected:
    CoopSemaphore& measureStartSema;

public:
    Button(uint8_t reqPin, CoopSemaphore& _measureStartSema) : PIN(reqPin), measureStartSema(_measureStartSema) {
        pinMode(PIN, INPUT_PULLUP);
        attachInterruptArg(PIN, Button::buttonIsr_static, this, FALLING);
    };
    ~Button() {
        detachInterrupt(PIN);
    }

    void IRAM_ATTR buttonIsr() {
        measureStartSema.post();
    }

    static void IRAM_ATTR buttonIsr_static(void* const self) {
        reinterpret_cast<Button*>(self)->buttonIsr();
    }

private:
    const uint8_t PIN;
};

Button button(BUTTON1, measureStartSema);
#endif

#ifdef ESP32
bool awakened_from_sleep() {
    esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
    switch (wakeup_reason)
    {
    case 1: Serial.println("Wakeup caused by external signal using RTC_IO"); break;
    case 2: Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
    case 3: Serial.println("Wakeup caused by timer"); break;
    case 4: Serial.println("Wakeup caused by touchpad"); break;
    case 5: Serial.println("Wakeup caused by ULP program"); break;
    default: Serial.println("Wakeup was not caused by deep sleep");
        return false;
        break;
    }
    return true;
}

TaskHandle_t yieldGuardHandle;
#endif

void setup()
{
    Serial.begin(115200);

#ifdef ESP32
    serialSDS.begin(9600, SERIAL_8N1, SDS_PIN_RX, SDS_PIN_TX);
    delay(100);
#else
    serialSDS.begin(9600, SDS_PIN_RX, SDS_PIN_TX, SWSERIAL_8N1, false, 192);
#endif

    Serial.println("SDS011 start/stop and reporting sample");

    auto sds011Task = createCoopTask(F("SDS011"), sds011Loop);
    if (!sds011Task) Serial.printf("creating %s task failed\n", sds011Task->name().c_str());
    if (!*sds011Task) Serial.printf("CoopTask %s out of stack\n", sds011Task->name().c_str());

    auto measureTask = createCoopTask(F("Measure"), measureLoop);
    if (!measureTask) Serial.printf("creating %s task failed\n", measureTask->name().c_str());
    if (!*measureTask) Serial.printf("CoopTask %s out of stack\n", measureTask->name().c_str());

#ifndef ESP32
    auto aliveTask = createCoopTask(F("Alive"), []() {
        pinMode(LED_BUILTIN, OUTPUT);
        for (;;)
        {
            digitalWrite(LED_BUILTIN, LEDOFF);
            CoopTaskBase::delay(is_SDS_running ? 1000 : 10000);
            digitalWrite(LED_BUILTIN, LEDON);
            CoopTaskBase::delay(1000);
        }
        return 0;
        }
    , 0x280);
    if (!aliveTask) Serial.printf("creating %s task failed\n", aliveTask->name().c_str());
    if (!*aliveTask) Serial.printf("CoopTask %s out of stack\n", aliveTask->name().c_str());
#endif

#ifdef ESP32
    if (awakened_from_sleep()) measureStartSema.post();
#endif
}

void taskReaper(const CoopTaskBase* const task)
{
    Serial.print(task->name()); Serial.print(" returns = "); Serial.println(static_cast<const CoopTask<>*>(task)->exitCode());
}

bool onDelay(uint32_t ms)
{
    if (ms > 30000)
    {
        Serial.printf("ESP32 entering deep sleep for %ius\n", ms * 1000);
        esp_sleep_enable_timer_wakeup(ms * 1000);
        esp_sleep_enable_ext0_wakeup(BUTTON1, LOW);
	gpio_deep_sleep_hold_en();
        esp_deep_sleep_start();
    }
    return true;
}

void loop()
{
    runCoopTasks(taskReaper, onDelay);
}

from esp_sds011.

peterk avatar peterk commented on August 25, 2024

Thank you! Maybe my sensor has some mode set that wakes it up.

Could not build your code (got latest Coop source from git) but it complains about this:

core/core.a(esp32-hal-misc.c.o): In function `yield':
/Users/xxx/Library/Arduino15/packages/esp32/hardware/esp32/1.0.4/cores/esp32/esp32-hal-misc.c:48: multiple definition of `yield'
libraries/CoopTask/CoopTaskBase.cpp.o:/Users/xxx/Documents/Arduino/libraries/CoopTask/src/CoopTaskBase.cpp:49: first defined here
libraries/CoopTask/CoopTaskBase.cpp.o:(.literal.yield+0x0): undefined reference to `__yield'
libraries/CoopTask/CoopTaskBase.cpp.o: In function `CoopTaskBase::yield(CoopTaskBase*)':
/Users/xxx/Documents/Arduino/libraries/CoopTask/src/CoopTaskBase.h:209: undefined reference to `__yield'

from esp_sds011.

dok-net avatar dok-net commented on August 25, 2024

@peterk You need Arduino-ESP32 from Github, HEAD of master branch, for CoopTask. I could have mentioned that here, but it's in the CoopTask README :-)

from esp_sds011.

peterk avatar peterk commented on August 25, 2024

Thank you again! I think I found the problem. I didn't read the ESP32 docs enough. There is no guarantee that GPIO state is maintained when the ESP32 is in deep sleep (it can fluctuate).

I decided to try to shut down power completely to the SDS011 as per your suggestion. I use a darlington transistor (TIP120). It seems to work fine except that the GPIO state is not maintained in deep sleep. I found out that this is by design and in the ESP32 docs I found that the combination of gpio_hold_en() and gpio_deep_sleep_hold_en() needs to be called for pin state to be maintained. Then the hold needs to be cleared after wakeup with gpio_hold_dis().

This means I now control the power to the SDS011 and my real time clock through a darlington transistor.

Thank you for your kind help! Next up is adding a small web server that I wake with a button. It is all part of a portable PM sensor that I will place in Stockholm. Now I also know more about the CoopTask library which seems very interesting.

from esp_sds011.

dok-net avatar dok-net commented on August 25, 2024

@peterk If you're interested, I've have just released CoopTask 2.6.0. I am updating the above sample source code, a lot of the boilerplate code is now gone.
Oh, and about adding a webserver to your project, the CoopTask example blinkbuttonandweb proves that the ESP webserver runs nicely from inside a CoopTask.

from esp_sds011.

Related Issues (5)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.