0
votes

I'm trying to display the readings from a DHT11 onto an LED Matrix. I can get the basic display to work, the issue is when I also put the time on the display. I started with the Morphing Clock as a base for the time then used the Adafruit Sensor code to read the DHT11. The issue seems to be with"

timerAlarmWrite(timer, 2000, true);

Which is setup to call:

void IRAM_ATTR display_updater(){
  // Increment the counter and set the time of ISR
  portENTER_CRITICAL_ISR(&timerMux);
  display.display(10);
  portEXIT_CRITICAL_ISR(&timerMux);
}

If I slow the timer down I can get readings from the DHT11 but the morphing time display doesn't update enough to look fluid. I'm new to coding for these devices so I'm not sure where I should be looking to move these things out of each others way. Here is the full app if the timer is set to something above 25000 you will get temp results most of the time, but the less are dimmer and the colons flash (they shouldn't).

#define double_buffer
#include <PxMatrix.h>
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include "Digit.h"

#include <Adafruit_Sensor.h>
#include <DHT.h>

const char* ssid     = "Gallifrey";
const char* password = "ThisIsAGoodPlaceToPutAPassword!";

// ESP32 Pins for LED MATRIX
#define P_LAT 22
#define P_A 19
#define P_B 23
#define P_C 18
#define P_D 5
#define P_E 15  // NOT USED for 1/16 scan
#define P_OE 2
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

PxMATRIX display(64,32,P_LAT, P_OE,P_A,P_B,P_C,P_D,P_E);

void IRAM_ATTR display_updater(){
  // Increment the counter and set the time of ISR
  portENTER_CRITICAL_ISR(&timerMux);
  display.display(10);
  portEXIT_CRITICAL_ISR(&timerMux);
}

// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

// Variables to save date and time
String formattedDate;
String dayStamp;
String timeStamp;

unsigned long prevEpoch;
byte prevhh;
byte prevmm;
byte prevss;


//====== Digits =======
Digit digit0(&display, 0, 63 - 1 - 9*1, 17, display.color565(0, 250, 0));
Digit digit1(&display, 0, 63 - 1 - 9*2, 17, display.color565(0, 250, 0));
Digit digit2(&display, 0, 63 - 4 - 9*3, 17, display.color565(0, 250, 0));
Digit digit3(&display, 0, 63 - 4 - 9*4, 17, display.color565(0, 250, 0));
Digit digit4(&display, 0, 63 - 7 - 9*5, 17, display.color565(0, 250, 0));
Digit digit5(&display, 0, 63 - 7 - 9*6, 17, display.color565(0, 250, 0));


#define DHTPIN 27
#define DHTTYPE DHT11

//DHT_Unified dht(DHTPIN, DHTTYPE);
DHT dht(DHTPIN, DHTTYPE);
//DHT dht;

const uint32_t delayMS = 6000;
uint32_t lastRead;

void setup() {

  display.begin(16); // 1/16 scan
  display.setFastUpdate(true);

   // Initialize Serial Monitor
  Serial.begin(115200);

  pinMode(DHTPIN, INPUT_PULLUP);
  dht.begin();
//  // Set delay between sensor readings based on sensor details.
  lastRead = 0;

  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &display_updater, true);
  timerAlarmWrite(timer, 1500, true); /// The Problem is Here!!!???!!!!?
  timerAlarmEnable(timer);

  display.fillScreen(display.color565(0, 0, 0));
  digit1.DrawColon(display.color565(100, 175, 0));
  digit3.DrawColon(display.color565(100, 175, 0));

  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Initialize a NTPClient to get time
  timeClient.begin();
  timeClient.setTimeOffset(-28800);

}

void loop() {

  while(!timeClient.update()) {
    timeClient.forceUpdate();
  }

  formattedDate = timeClient.getFormattedDate();

  // Extract date
  int splitT = formattedDate.indexOf("T");
  dayStamp = formattedDate.substring(0, splitT);

  // Extract time
  timeStamp = formattedDate.substring(splitT+1, formattedDate.length()-1);

  displayLocalTemp();
  updateTimeDisplay();

}

String readDHTTemperature() {
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature(true);
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //float t = dht.readTemperature(true);
  // Check if any reads failed and exit early (to try again).
  if (isnan(t)) {    
    Serial.println("Failed to read from DHT sensor!");
    return "--";
  }
  else {
    Serial.println(t);
    return String(t);
  }
}

String readDHTHumidity() {
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  if (isnan(h)) {
    Serial.println("Failed to read from DHT sensor!");
    return "--";
  }
  else {
    Serial.println(h);
    return String(h);
  }
}

void displayLocalTemp() {

  uint32_t currentTime = millis();
  uint32_t waited = currentTime - lastRead;

  static String lastTemp;
  static String lastHumid;

  if (waited > delayMS) {
    lastRead = currentTime;

    String temp = readDHTTemperature();
    String humidity = readDHTHumidity();
    String preTemp = "T:";
    String preHumidity = "H:";
    String tempDisplay = preTemp + temp;
    String humidDisplay = preHumidity + humidity;

    Serial.print("temp: ");
    Serial.print(temp);
    Serial.print(" -- humidity: ");
    Serial.println(humidity);

    display.setTextColor(display.color565(0,0,0));
    display.setCursor(20,16);
    display.print(lastTemp);
    display.setCursor(20,25);
    display.print(lastHumid);

    display.setTextColor(display.color565(0,255,0));
    display.setCursor(20,16);
    display.print(tempDisplay);
    display.setCursor(20,25);
    display.print(humidDisplay);

    lastTemp = tempDisplay;
    lastHumid = humidDisplay;
  }

}

void updateTimeDisplay() {

  unsigned long epoch = timeClient.getEpochTime();

  if (epoch != prevEpoch) {
    int hh = timeClient.getHours();
    int mm = timeClient.getMinutes();
    int ss = timeClient.getSeconds();

    if (hh > 12) hh = hh % 12;

    if (prevEpoch == 0) { // If we didn't have a previous time. Just draw it without morphing.
      digit0.Draw(ss % 10);
      digit1.Draw(ss / 10);
      digit2.Draw(mm % 10);
      digit3.Draw(mm / 10);
      digit4.Draw(hh % 10);
      digit5.Draw(hh / 10);
    }
    else
    {
      // epoch changes every miliseconds, we only want to draw when digits actually change.
      if (ss!=prevss) { 
        int s0 = ss % 10;
        int s1 = ss / 10;
        if (s0!=digit0.Value()) digit0.Morph(s0);
        if (s1!=digit1.Value()) digit1.Morph(s1);
        //ntpClient.PrintTime();
        prevss = ss;
      }

      if (mm!=prevmm) {
        int m0 = mm % 10;
        int m1 = mm / 10;
        if (m0!=digit2.Value()) digit2.Morph(m0);
        if (m1!=digit3.Value()) digit3.Morph(m1);
        prevmm = mm;
      }

      if (hh!=prevhh) {
        int h0 = hh % 10;
        int h1 = hh / 10;
        if (h0!=digit4.Value()) digit4.Morph(h0);
        if (h1!=digit5.Value()) digit5.Morph(h1);
        prevhh = hh;
      }
    }
    prevEpoch = epoch;
  }

}
1

1 Answers

0
votes

You could try to assign tasks explicitly to a core.
When you start playing with ESP32 multi core code execution be aware of the following issues:

  • Both the setup and the main loop functions execute with a priority of 1.
  • Arduino main loop runs on core 1.
  • The execution is pinned, so it’s not expected that the core will change during execution of the program
  • On FreeRTOS (the underlying OS), tasks have an assigned priority which the scheduler uses to decide which task will run.
  • High priority tasks ready to run will have preference over lower priority tasks, which means that as long as a higher priority task can run, a lower priority task will not have the CPU.
  • CAUTION shared resources like Serial might be potential issues. Due to two core tasks accessing uncoordinated the same hardware may lead to deadlocks and crashes

For implementation purposes, you need to take in consideration that FreeRTOS priorities are assigned from 0 to N, where lower numbers correspond to lower priorities. So, the lowest priority is 0.
First of all, declare a global variable that will contain the number of the core where the FreeRTOS task to launch will be pinned

static int taskCore = 0; // The core the task should run on

now create the assignment of a task to the core in Setup()

xTaskCreatePinnedToCore(
                myCoreTask,   /* Function to implement the task */
                "myCoreTask", /* Name of the task */
                10000,      /* Stack size in words */
                NULL,       /* Task input parameter */
                0,          /* Priority of the task */
                NULL,       /* Task handle. */
                taskCore);  /* Core where the task should run */

Here is a test function which you call in loop()

void myCoreTask( void * pvParameters ){
   while(true){
      Serial.println("Task running on core ");
      Serial.print(xPortGetCoreID());
    // This is here to show that other tasks run 
     // NEVER use in production
      delay(1000); 
    }
}

Hope this gives you an idea how to tackle your problem, read more here RTOS and here ESP32-IDF