Go Down

Topic: LED matrix display - MD_Parola, MD_MAX72xx and MD_MAXPanel (Read 554091 times) previous topic - next topic

Jmeredith

#1530
Jun 13, 2019, 01:31 am Last Edit: Jun 13, 2019, 01:32 am by Jmeredith
I want the message to continue scrolling, but go get a new API call every hour.

so the message would be updated each hour with the latest forecast information.

I can get it to just print but it wont scroll at all

the newMessage buffer has the correct value shown in the serial window...

I feel like I'm so close!

marco_c

Yes you are close. You need to really think about the structure of loop() as it cannot block using delay(). Lots of examples on how to do that.

newMessage may have the message, but how does the logic know to replace the old with the new? Again, think about what the code does.
Arduino Libraries https://github.com/MajicDesigns?tab=Repositories
Parola for Arduino https://github.com/MajicDesigns/Parola
Arduino++ blog https://arduinoplusplus.wordpress.com

Jmeredith

Im trying to use the Parola_Scrolling sketch example as a basis.  I have the CurMessage defined before setup() and I set the newMessageAvailable flag = false so its ready to accept a new message.

Code: [Select]
// Global message buffers shared by Serial and Scrolling functions
#define  BUF_SIZE  250
char curMessage[BUF_SIZE] = { "Getting WX Data . . ." };
char newMessage[BUF_SIZE] = { "" };
bool newMessageAvailable = false;


then in setup() I fire off the P.begin() and set the PdisplayText up

Code: [Select]
Serial.begin(115200);
  //Turn LED Matrix On
  P.begin();
  //make LED Matrix say Getting WX Data... (curMessage)
  P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);


Im getting confused as to how to separate out the GET call every hour but continue to have the diplay active with the latest message.


marco_c

Quote
I have the CurMessage defined before setup() and I set the newMessageAvailable flag = false so its ready to accept a new message.
Which also means that when you create the new message you need to set the boolean flag for it to be picked up.

Quote
m getting confused as to how to separate out the GET call every hour but continue to have the diplay active with the latest message.
These may help
https://forum.arduino.cc/index.php?topic=223286.0
http://www.gammon.com.au/statemachine
https://forum.arduino.cc/index.php?topic=604505.0

And this may have more info you might find useful
https://forum.arduino.cc/index.php?topic=384198.0
Arduino Libraries https://github.com/MajicDesigns?tab=Repositories
Parola for Arduino https://github.com/MajicDesigns/Parola
Arduino++ blog https://arduinoplusplus.wordpress.com

Jmeredith

Well I got the GetForcast() function set up and working... but the millis timer thing has my brain hurting... I've been at the for 13 hours... I'm tapping out for the night


heres where I am at so far...  feel more lost than earlier though :)

Code: [Select]
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
#include <ArduinoJson.h>
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 12
#define CLK_PIN   D5
#define DATA_PIN  D7
#define CS_PIN    D8


// HARDWARE SPI
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);

uint8_t scrollSpeed = 25;    // default frame delay value
textEffect_t scrollEffect = PA_SCROLL_LEFT;
textPosition_t scrollAlign = PA_LEFT;
uint16_t scrollPause = 0; // in milliseconds

// Global message buffers shared by Serial and Scrolling functions
#define  BUF_SIZE  200
char curMessage[BUF_SIZE] = { "this is the curMessage" };
//char newMessage[BUF_SIZE] = { "This should be the lastest weather forecast" };
//bool newMessageAvailable = true;

byte onBoardLedState = LOW;
unsigned long currentMillis = 0;    // stores the value of millis() in each iteration of loop()
unsigned long previousOnBoardLedMillis = 0;   // will store last time the LED was updated
const int blinkDuration = 500; // number of millisecs that Builtin_LED is on

// Current fingerprint for https://api.weather.gov
const uint8_t fingerprint[20] = {0x1C, 0xE6, 0x10, 0xE0, 0x6D, 0x39, 0x26, 0x74, 0xEE, 0x44, 0x3A, 0x46, 0x9B, 0x44, 0x99, 0x77, 0xAC, 0xA3, 0xD4, 0x72};

ESP8266WiFiMulti WiFiMulti;

void setup()
{
  Serial.begin(115200);
  Serial.println("[WiFi] Set Mode -> Station");
  WiFi.mode(WIFI_STA);
  delay(2000);
  Serial.println("[WiFi] Mode = Station");
  delay(2000);
  WiFiMulti.addAP("--------", "--------------");
  Serial.println("[WiFi] Send authentication data...");
  if (WiFiMulti.run() == WL_CONNECTED) {
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
   
}
  P.begin();
  P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);
  pinMode(BUILTIN_LED, OUTPUT);
  digitalWrite(BUILTIN_LED, HIGH); //LED OFF = HIGH for WEMOS D1 MINI
}

void loop()
{

  //getforecast();
 currentMillis = millis();
 updateOnBoardLedState();
 // Serial.println(currentMillis);

  //P.displayAnimate();
  if (P.displayAnimate())
  {
    //if (newMessageAvailable)
    //{
    //  strcpy(curMessage, newMessage);
    //  newMessageAvailable = false;
    //}
    P.displayReset();
  }
}
void getforecast()
{   
    currentMillis = 0;
    std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
    client->setFingerprint(fingerprint);
    HTTPClient https;
    Serial.print("[HTTPS] begin...\n");
    if (https.begin(*client, "https://forecast.weather.gov/MapClick.php?lat=29.584&lon=-98.553&FcstType=json")) {
      Serial.print("[HTTPS] GET...\n");
      int httpCode = https.GET();
      if (httpCode > 0) {
        Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
    if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
      //pass GET results to doc
      //Serial.println(https.getString());
      String payload = https.getString();
      const size_t capacity = 2*JSON_ARRAY_SIZE(0) + 8*JSON_ARRAY_SIZE(13) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(7) + JSON_OBJECT_SIZE(11) + JSON_OBJECT_SIZE(12) + JSON_OBJECT_SIZE(20);
      DynamicJsonDocument doc(capacity+4000); //add in extra bytes for string duplication
      DeserializationError error = deserializeJson(doc, payload);
      if (error)
      {
        Serial.print(F("deserializeJson() failed: "));
        Serial.println(error.c_str());
        return;
      }
      Serial.println("\n\nResult of buffer print");
      char CurrentPeriodBuffer[800];  //used in ealier test
      strcpy(CurrentPeriodBuffer, doc["time"]["startPeriodName"][0].as<char*>());
      strcat(CurrentPeriodBuffer, ", ");
      strcpy(CurrentPeriodBuffer + strlen(CurrentPeriodBuffer), doc["data"]["text"][0].as<char*>());
      strcat(CurrentPeriodBuffer, " ");
      strcpy(CurrentPeriodBuffer + strlen(CurrentPeriodBuffer), doc["time"]["startPeriodName"][1].as<char*>());
      strcat(CurrentPeriodBuffer, ", ");
      strcpy(CurrentPeriodBuffer + strlen(CurrentPeriodBuffer), doc["data"]["text"][1].as<char*>());
      strcat(CurrentPeriodBuffer, "As of: ");
      strcpy(CurrentPeriodBuffer + strlen(CurrentPeriodBuffer), doc["creationDateLocal"].as<char*>());
      Serial.println(CurrentPeriodBuffer);
    }
     } else {
        Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
     }
    }
}
/=========
void updateOnBoardLedState() {

 if (onBoardLedState == LOW) {
         // if the Led is off, we must wait for the interval to expire before turning it on
   if (currentMillis - previousOnBoardLedMillis >= onBoardLedInterval) {
         // time is up, so change the state to HIGH
      onBoardLedState = HIGH;
         // and save the time when we made the change
      previousOnBoardLedMillis += onBoardLedInterval;
         // NOTE: The previous line could alternatively be
         //              previousOnBoardLedMillis = currentMillis
         //        which is the style used in the BlinkWithoutDelay example sketch
         //        Adding on the interval is a better way to ensure that succesive periods are identical

   }
 }
 else {  // i.e. if onBoardLedState is HIGH
 
         // if the Led is on, we must wait for the duration to expire before turning it off
   if (currentMillis - previousOnBoardLedMillis >= blinkDuration) {
         // time is up, so change the state to LOW
      onBoardLedState = LOW;
         // and save the time when we made the change
      previousOnBoardLedMillis += blinkDuration;
   }
 }
}
   

 



marco_c

OK, I put you out of your misery. See attached untested but you should get the idea.
Arduino Libraries https://github.com/MajicDesigns?tab=Repositories
Parola for Arduino https://github.com/MajicDesigns/Parola
Arduino++ blog https://arduinoplusplus.wordpress.com

Jmeredith

OMG! thank you :)  It took a little tweaking but I got it going.  Had to call the getforecast() function in setup to preload the curmessage with the "now" forecast... seems to be working.

thanks for the nudges!

-J

Jmeredith

well I thought it was all wrapped up but it doesn.t seem to be refreshing the scroll with each get.  so I'll need to play around with it a little more.   so close!

-J

Jmeredith

after getting a few minutes to check into it.. it was just a "-" instead of a "=" :) . think its all good now!  next phase is to make a slick container for it and mount it!

thanks again!

-J

Arduino_RuudvdMeer

From the Netherlands, i am glad with the MAX7219 drivers made by Marco Colli.
I am a (retired) hard, software developer and teacher, now for the Arduino.
See https://www.facebook.com/arduino.leiderdorp.9

I have a lot of contacts with Chinese manufacturers, by AliExpress.

I have asked to make another display with the MAX7219 chip but with the small display, the 788AS. And they have make theme for me, but also for the market. It is smaller than the normal version of 32 mm. it is 20x20mm nice and small.

I have instruct to make it compatible to the FC16 display, named in your library. It is made in a 2 and 3 displays version. You can make every combination of display counts with it. (Is was not possible to make a single display version, because of the size)

The are now available see this sites:

https://nl.aliexpress.com/store/product/OPEN-SMART-0-8-0-8-inch-8-16-Cascadable-Red-LED-Dot-Matrix-Display-Module/1199788_33007160158.html?spm=a2g1y.12024536.productList_1552059.pic_3

and

https://nl.aliexpress.com/store/product/OPEN-SMART-0-8-inch-0-8-8-24-Cascadable-Red-LED-Dot-Matrix-Display-Module/1199788_33010367698.html?spm=a2g1y.12024536.productList_1552059.pic_2

Nice to use. very compact and small compatible to the greater FC16 types.


Greetings Ruud vd Meer. :)

I have asked to make another display with the MAX7219 chip but with the small display, the 788AS. And they have make theme for me, but also for the market. It is smaller than the normal version of 32 mm. it is 20x20mm nice and small.
Good to know that the 788AS is compatible with one of the supported standards.
I was not able to make a 788BS matrix work without custom config.
I'm trying to make a 3x2 (24x16 pixels) display board but the number of wires is so big that it is quite difficult to execute.
I hope that more display modules start to appear, I'd love to have smaller matrices and the ones sold by adafruit are very expensive.

--
You never learn anything by doing it right.

marco_c

Thanks for letting us know, Ruud. Looks like an interesting product.
Arduino Libraries https://github.com/MajicDesigns?tab=Repositories
Parola for Arduino https://github.com/MajicDesigns/Parola
Arduino++ blog https://arduinoplusplus.wordpress.com

Gabao

Could I use MD_MAX72xx and the MD_Parola library to emulate the eyes and mouth of a robot through 4 devices connected in series? (my microcontroller is full). I want the first two to be running the animations of the eyes and the last two the animation of the mouth. Could you give me a hint how to do it?

marco_c

Start from the RobotEyes example. The eyes can have a start matrix specified in the constructor, so you can tell it which matrix is the first one for the eyes (i guess 0 if it is the first 2). That takes care of the easy half of the problem.

Animating a mouth is no different from doing the eyes and it will always be symmetrical about the midline (like the evil eyes) across the two modules or different (eg, one mouth corner up, the other mouth corner down). In either case you can treat the mouth like 'eyes' but you will need to define the animations and the bit patterns for the characters the same way that they eyes are done in the .cpp file. The data is the key, as the animations code will not need to change for the mouth. This blog post explains most of it https://arduinoplusplus.wordpress.com/2017/10/29/robot-eye-expressions-using-led-matrix-display/

You can define the additional mouth data in the Eyes object or you can copy and rename to create a specific mouth object with just mouth data. Either will work. You then define another object in the sketch created from this class (matrices starting at 2) and animate it to do the mouth parts.

Hopefully this is enough of a clue on what to do.
Arduino Libraries https://github.com/MajicDesigns?tab=Repositories
Parola for Arduino https://github.com/MajicDesigns/Parola
Arduino++ blog https://arduinoplusplus.wordpress.com

Ysayyed

Hi everyone,

I stuck in a project where I am not able to build fonts more than 255. However i need to build the fonts more than that. I altered the MD_MAX72XX library like this to get more strength. But still there is nothing displaying on the max7219 module when i address the 256th character. I am using the parola example of displaying scrolling effect on double height displays.

Here is the alteration i made:

#if USE_LOCAL_FONT
  // Font properties info structure
   typedef struct
   {
     uint16_t version;     // (v1) font definition version number (for compliance)
     uint8_t height;      // (v1) font height in pixels
     uint8_t widthMax;    // (v1) font maximum width in pixels (widest character)
     uint16_t firstASCII;  // (v1) the first ASCII character in the font table
     uint16_t lastASCII;   // (v1) the last ASCII character in the font table
     uint16_t dataOffset; // (v1) offset from the start of table to first character definition
   
I changed the uint8_t version to unit16_t version and uint8_t first ASCII and last ASCII to uint16_t first ASCII and last ASCII.

Any clues how can I do it? Am I wrong anywhere??

Go Up