Corrupted display [SOLVED]

Hi, I have a problem that I cant seem to get passed. I'm using an ESP8266-12F to extract temp, wind speed, wind direction, humidity, pressure and weather description from openweathermap.org . After extraction the data is transfered to global variables....so far so good.

All data is displayed and scrolls correctly on my display except the weather description infromation. The serial.prints show the weather description information is correct while the other information is displayed but gets corrupted when the weather desciption passes through line 134
snprintf(scrollText, 30, "%s", weatherOut);
or sprintf(scrollText, "%s", weatherOut); both have the same problem.

The weather description MAY display correctly first time through the loop but later is corrupted by being replaced with squares, at symbols , and other symbols.
Any suggestions? Thanks


//ESP8266 12F & OLED
//TX->IO1
//RX->IO3
//
//SDA - <I02
//SCL - <IO14
#include <Arduino.h>
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <U8g2lib.h>

#define SUN  0
#define SUN_CLOUD  1
#define CLOUD 2
#define RAIN 3
#define THUNDER 4

U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R0, D5, D4 , U8X8_PIN_NONE);

WiFiClient client;

//Globals
float dataVar[5] = {0.00, 0.00, 0.00, 000, 0000.00};
int WId; //weatherID
const char* weatherOut;
const char* terms[] = {"Temperature (C)", "Wind Speed (m/s)", "Wind Direction (deg)", "Humidity (%)", "Pressure (hPa)"};
const unsigned long postingInterval = 10L * 60L * 1000L;  // posting interval of 10 minutes  (10L *60L*1000L; 10 seconds delay for testing)
unsigned long lastConnectionTime = (millis() - postingInterval);
const char* ssid = "XXXXXXXXXXX";
const char* pass = "XXXXXXXXXXX";

void updateweather()
{
  if (client.connect("api.openweathermap.org", 80))
  {
    client.println("GET /data/2.5/weather?id=XXXXXXX&units=metric&APPID= XXXXXXXXXXXXXXXXXXXXXX  HTTP/1.1");
    //requires HTTP/1.1 to get correct status
    client.println("Host: api.openweathermap.org");
    client.println("Connection: close");
    client.println();
    // Check HTTP status
    char status[32] = {0};
    client.readBytesUntil('\r', status, sizeof(status));
    if (strcmp(status + 9, "200 OK") != 0)
      {
        Serial.print(F("Unexpected response: "));
        Serial.println(status);
        lastConnectionTime = millis();
        return;
      }
    // Skip HTTP headers
    char endOfHeaders[] = "\r\n\r\n";
    if (!client.find(endOfHeaders))
      {
        Serial.println(F("Invalid response"));
        lastConnectionTime = millis();
        return;
      }
  }
  StaticJsonDocument<1024> doc;
  // Parse JSON object
  DeserializationError error = deserializeJson(doc, client);
  if (error) 
  {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.c_str());
    lastConnectionTime = millis();
    return;
  }
  //Disconnect
  client.stop();

  JsonObject weather_0 = doc["weather"][0];
  int weatherId = weather_0 ["id"].as<int>();
  const char* weatherNow = weather_0["description"]; // "rain"
  JsonObject main = doc["main"];
  //transfer weatherNow to global WeatherOut

  weatherOut = weatherNow;
  WId = weatherId;

  dataVar[0] = main["temp"]; // 16.27
  dataVar[1] = doc["wind"]["speed"]; // 3.6
  dataVar[2] = doc["wind"]["deg"]; // 130
  dataVar[3] = main["humidity"]; // 21
  dataVar[4] = main["pressure"]; // 1028

  //Prints to monitor for checking
  Serial.println (dataVar[0]);
  Serial.println (dataVar[1]);
  Serial.println (dataVar[2]);
  Serial.println (dataVar[3]);
  Serial.println (dataVar[4]);
  Serial.println (weatherNow);
  Serial.println ("The end");
  Serial.println (weatherOut);
  Serial.println (WId);
  Serial.println ("The end1");
  return;
}

void u8g2_prepare(void)
{
  //initialise display
  //u8g2.setFont(u8g2_font_6x10_tf);
  u8g2.setFont(u8g2_font_ncenB08_tr);
  // u8g2.setFont(u8g2_font_logisoso32_tf);
  u8g2.setFontRefHeightExtendedText();
  u8g2.setDrawColor(1);
  u8g2.setFontPosTop();
  u8g2.setFontDirection(0);
  u8g2.enableUTF8Print();
}

// Pause for a 10 minute
void wait() 
{
  Serial.println("Wait 10 minutes");
  lastConnectionTime = millis();
  while (millis() - lastConnectionTime < postingInterval)
    {
      u8g2.clearBuffer();         // clear the internal memory
      for (int i = 0; i < 6; i++)
        { 
          char scrollText[32];
          if (i < 5)
            {
              sprintf(scrollText, "%s : %4.1f ", terms[i], dataVar[i]);
            }
      else
        {
          snprintf(scrollText, 30, "%s", weatherOut); //not working.Works first time, sometimes
        }
      Serial.print ("scrollText = ");
      Serial.println(scrollText);
      Serial.print ("Weather Display = ");
      Serial.println(weatherOut);

      if (WId == 800)  //clear
        {
          draw(scrollText, SUN, dataVar[0]);
        }
      else
        {
        switch (WId / 100)
          {
            case 2:     //Thunderstorm
              draw(scrollText, THUNDER, dataVar[0]);
             break;

            case 3:     //Drizzle
            case 5:     //Rain
              draw(scrollText, RAIN, dataVar[0]);
            break;

            case 7:     //Sun with clouds
              draw(scrollText, SUN_CLOUD, dataVar[0]);
            break;
            case 8:     //clouds
              draw(scrollText, CLOUD, dataVar[0]);
            break;

            default:    //Sun with clouds
              draw(scrollText, SUN_CLOUD, dataVar[0]);
            break;
          }
        }
      }
    }
  Serial.println("At the end of ten minute loop");
}

void drawWeatherSymbol(u8g2_uint_t x, u8g2_uint_t y, uint8_t symbol)
{
  switch (symbol)
  {
    case SUN:
      u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2.drawGlyph(x, y, 69);
     break;
    case SUN_CLOUD:
      u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2.drawGlyph(x, y, 65);
     break;
    case CLOUD:
      u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2.drawGlyph(x, y, 64);
     break;
    case RAIN:
      u8g2.setFont(u8g2_font_open_iconic_weather_6x_t);
      u8g2.drawGlyph(x, y, 67);
     break;
    case THUNDER:
      u8g2.setFont(u8g2_font_open_iconic_embedded_6x_t);
      u8g2.drawGlyph(x, y, 67);
     break;
  }
}

void drawWeather(uint8_t symbol, int degree)
{
  //drawWeatherSymbol(0, 48, symbol);
  drawWeatherSymbol(0, 0, symbol);
  u8g2.setFont(u8g2_font_logisoso32_tf);
  //u8g2.setCursor(48 + 3, 42);
  u8g2.setCursor(48 + 5, 5);
  u8g2.print(degree);
  u8g2.print("°C");   // requires enableUTF8Print()
}
/*
  Draw a string with specified pixel offset.
  The offset can be negative.
  Limitation: The monochrome font with 8 pixel per glyph
*/
void drawScrollString(int16_t offset, const char *s)
{
  static char buf[36];  // should work for screen with up to 256 pixel width
  size_t len;
  size_t lencopy;
  size_t char_offset = 0;
  u8g2_uint_t dx = 0;
  size_t visible = 0;
  len = strlen(s);
  const char* weatherCopy;
  weatherCopy = weatherOut;
  Serial.print ("s = ");
  Serial.println (s);
  Serial.print ("weatherOut = ");
  Serial.println (weatherOut);
  Serial.print ("weatherCopy = ");
  Serial.println (weatherCopy);

  if ( offset < 0 )
    {
      char_offset = (-offset) / 8;
      dx = offset + char_offset * 8;
      if ( char_offset >= u8g2.getDisplayWidth() / 8 )
      return;
      visible = u8g2.getDisplayWidth() / 8 - char_offset + 1;
      strncpy(buf, s, visible);
      Serial.println ("buf = ");
      Serial.println(buf);
      buf[visible] = '\0';
      u8g2.setFont(u8g2_font_8x13_mf);
      u8g2.drawStr(char_offset * 8 - dx, 50, buf);
    }
  else
    {
      char_offset = offset / 8;
      if ( char_offset >= len )
      return; // nothing visible
      dx = offset - char_offset * 8;
      visible = len - char_offset;
      if ( visible > u8g2.getDisplayWidth() / 8 + 1 )
      visible = u8g2.getDisplayWidth() / 8 + 1;
      strncpy(buf, s + char_offset, visible);
      Serial.println ("buf = ");
      Serial.println(buf);
      buf[visible] = '\0';
      u8g2.setFont(u8g2_font_8x13_mf);
      u8g2.drawStr(-dx, 50, buf);
    }
  Serial.print ("s out = ");
  Serial.println (s);
  Serial.print ("weatherOut out = ");
  Serial.println (weatherOut);
  Serial.print ("weatherCopy out = ");
  Serial.println (weatherCopy);
  weatherOut = weatherCopy;
}

void draw(const char *s, uint8_t symbol, int degree)
{
  int16_t offset = -(int16_t)u8g2.getDisplayWidth();
  int16_t len = strlen(s);
  for (;;)
  {
    u8g2.firstPage();
    u8g2.clearBuffer();
    do 
      {
        drawWeather(symbol, degree);
        drawScrollString(offset, s);
      } 
    while ( u8g2.nextPage() );
    delay(20);
    offset += 4;
    if ( offset > len * 8 + 1 )
    break;
  }
}

void setup() 
{
  u8g2.setI2CAddress(120);
  Wire.begin(D4, D5);     //sda,scl.
  Serial.begin(115200);
  u8g2.begin();
  u8g2_prepare();
  u8g2.clear();
  Serial.println("Connecting to Network");
  WiFi.begin(ssid, pass);
  int counter = 0;
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(200);
    if (++counter > 100)
    ESP.restart();
    Serial.print(".");
  }
  Serial.println("\nWiFi connected");
}

void loop() 
{
  {
    updateweather();// gets weather and update values
    wait();         // connection delay and display update.
  }
}

From the symptoms it sounds very much like you are writing outside the bounds of an array somewhere in the code

I didn't dig into it deeply enough to be sure, but I suspect that you're pointing weatherOut to a string in a JSON doc that's gone out of scope when you try to put it on the display.

That’s what I thought but I can’t find it.
G

That’s why I transferred the variables to global variables.....unless I’ve gone about that wrong.
G

Try making weatherOut an array of char of sufficient size and copy the data into it when the JSON object still exists.

I thought that was what I was doing here.


  JsonObject weather_0 = doc["weather"][0];
  int weatherId = weather_0 ["id"].as<int>();
  const char* weatherNow = weather_0["description"]; // "rain"
  JsonObject main = doc["main"];
  //transfer weatherNow to global WeatherOut

  weatherOut = weatherNow;<<<<<<<<<<<<<<copied to global
  WId = weatherId;

  dataVar[0] = main["temp"]; // 16.27
  dataVar[1] = doc["wind"]["speed"]; // 3.6
  dataVar[2] = doc["wind"]["deg"]; // 130
  dataVar[3] = main["humidity"]; // 21
  dataVar[4] = main["pressure"]; // 1028

There is a difference in making a copy of a pointer and making a copy of the string pointed to.

Nope. You are pointing a global variable at some locally defined storage. If you want to make the data available elsewhere then you could use strcmp. Make sure weatherout is an array with enough space though.

Face meets palm.
G

I’ll try in the morrow but should that be strcpy ?
G

Yes. Not sure what my fingers were thinking :wink:

Actually strlcpy is safer, like the snprintf you were using. BUT check the return value to see if the buffer was big enough see this example sketch strlcpy_strlcat.ino
for snprintf see C static code analysis

Thanks for your help. Strlcpy (or strcpy) provided the solution.
Every day is a learning day.
Cheers
G

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.