Unacceptable Uno performance. My code? (weather station)

Hello all
I have built a weather station all of which functions… in its component parts :confused:
Uno WiFi R3 Developer Addition
DHT22
DS18B20
BMP280
AMS_5600 (windvane)
Hall sensor (2 actually but I have never got them both to work at the same time !) (Anemometer)

My final ‘battle’ is to communicate the data generated to the net. I gave up on serial comms between my mega and a nodemcu board (another story), and bought the Uno WiFi board. As its the developer addition the wifi lib is UnoWiFiDevEd.h.
The Uno has less memory than the mega so Ive had to strip my code down which I have done to the best of my abilities.

This is the compiler build report

Sketch uses 22188 bytes (68%) of program storage space. Maximum is 32256 bytes.
Global variables use 1683 bytes (82%) of dynamic memory, leaving 365 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.

This is high and may be part of my problem.
My problem is this:
The sketch seems to run ok except that while the code to actually send the data is included in the sketch,

CiaoData data = Ciao.write(CONNECTOR, SERVER_ADDR, uri);

the hall sensors counting the Anemometer revolutions just stops counting!!! The revolution counter variable is duly incremented in the ISR but when that variable is used to calculate wind speed a little later it is always 0.
Also, the call to Ciao.write doesnt return for over 7.5 seconds! That cant be correct ?

I think the long and short is that I need to be more creative with the memory I have.
Im afraid Ive reached the limits of my knowledge in this area and Id be Very grateful if anyone can suggest how I might make this thing run more reliably.

My code is below.
Thanks for reading thus far !

#include "AMS_5600.h"               //Windvane
#include <DHT.h>;                   //DHT humidity and temperature sensor 
#include "bmp280.h"                 //Barometric pressure sensor BMP280
#include "Wire.h"                   //BMP280
#include <OneWire.h>                //Mast head temperature sensor DS18B20 on pin 7
#include <UnoWiFiDevEd.h>
#include <SimpleTimer.h>

// wifi stuff
#define CONNECTOR     "rest" 
#define SERVER_ADDR   "184.106.153.149"
#define APIKEY_THINGSPEAK  "232DHIAPRH9HJSXF" 
// end wifi stuff

#define P0 1013.25                  //Pressure Ref for BMP280 module
#define ARM_RADIUS  105             //mm
#define KNOTS_PER_KM  0.539957      //Conversion factor
#define SAMPLE_TIME_FRONT  6000     //ms
#define SAMPLE_TIME_BACK  7000      //ms
#define READ_DS18B20_TIMER 5000     //ms = 6 sec
#define UPDATE_WIND_DIR_TIMER 2000  //ms
#define READ_BMP_TIMER  7000        //ms
#define KMPH 0x1                    //KMPH
#define DHTTYPE DHT22               //DHT22  (AM2302), AM2321  DHT humidity and temperature sensor 
#define READ_DHT22_TIMER  3000      //ms
#define FRONT_HALL_DEBOUNSE_TIME 40 //ms 
#define TEMPERATURE_PRECISION 9     //Lower resolution

#define DHTPIN 4                    //DHT data pin
#define DS18B20_ONE_WIRE_BUS 7      // Mast probe DS18B20 pin
AMS_5600    ams5600;
DHT         dht(DHTPIN, DHTTYPE);
SimpleTimer timer; 

volatile byte half_revolutions_front =0, half_revolutions_back =0;
int windCupCircumference;
unsigned int rpm_front, rpm_back, DHT22_humidity;
float WindSpeed_kmph, DS18B20_temp_C, DHT22_temp, DHT22_temperature, WindBearing;
double BMP_pressure, BMP_temp;
long last_front_interupt;

void setup(){
  windCupCircumference = 2*(3.142 * ARM_RADIUS);
  Serial.begin(9600);
  Serial.println(F("enter setup"));
     Ciao.begin(); 
  attachInterrupt(0, magnet_detect_front, CHANGE );

//timed actions setup
   timer.setInterval(SAMPLE_TIME_FRONT, frontHallTimeout);
   timer.setInterval(READ_BMP_TIMER, ReadBMP);
   timer.setInterval(READ_DHT22_TIMER, ReadDHT22); 
   timer.setInterval(READ_DS18B20_TIMER, ReadDS18B20);
   timer.setInterval(UPDATE_WIND_DIR_TIMER, UpdateWindDirection);
   last_front_interupt = millis(); //Used for de-bounce of Hall sensor
   dht.begin();
   Wire.begin();
   Serial.println(F("exit setup")); 
   myDelay(5000);
} //setup

void ReadDS18B20(){
OneWire ds(DS18B20_ONE_WIRE_BUS);
 byte data[12];
 byte addr[] = {0x28, 0xED, 0xA1, 0x3, 0x0, 0x0, 0x80, 0x87};
 ds.reset();
 ds.select(addr);
 ds.write(0x44,1);
 
 myDelay(750);

 byte present = ds.reset();
 ds.select(addr);  
 ds.write(0xBE);
 
 for (int i = 0; i < 9; i++) { 
  data[i] = ds.read(); }
 
 ds.reset_search();
 
 byte MSB = data[1];
 byte LSB = data[0];
 float tempRead = ((MSB << 8) | LSB); 
 DS18B20_temp_C = tempRead / 16;
}// ReadDS18B20()

void ReadDHT22(){
  DHT22_humidity = dht.readHumidity();

  DHT22_temperature = dht.readTemperature();
  
}//ReadDHT22()

void ReadBMP(){
   BMP280 bmp;
   bmp.begin();    
   bmp.setOversampling(4);  
   char result = bmp.startMeasurment();
   if(result!=0){
      myDelay(result);
      bmp.getTemperatureAndPressure(BMP_temp,BMP_pressure);
   }// if result
}//ReadBMP()   
   

void UpdateWindDirection() {
   WindBearing = (convertScaledAngleToDegrees(ams5600.getScaledAngle()));
}

void magnet_detect_front(){//called whenever hall sensor 'front' detects a CHANGE
 if ( millis() - last_front_interupt > FRONT_HALL_DEBOUNSE_TIME) { 
    half_revolutions_front++;
    Serial.print("detected ");Serial.println(half_revolutions_front);
    last_front_interupt = millis();
 }
}/

void frontHallTimeout(){
    CalculateWindSpeed((half_revolutions_front / 2), SAMPLE_TIME_FRONT);
    Serial.print("frontHallTimeout revs= ");Serial.println(half_revolutions_front);
    BuildDataStr();
    half_revolutions_front = 0;
    
 }

void PostThingSpeak(){
   double start,finish=0;
//   Ciao.begin();
   String uri = "/update?api_key=";
   uri += APIKEY_THINGSPEAK;
   uri += "&field1=";
   uri += DS18B20_temp_C;
   uri += "&field2=";
   uri += WindSpeed_kmph;
   uri += "&field3=";
   uri += WindBearing;
   uri += "&field4=";
   uri += BMP_temp;
   uri += "&field5=";
   uri += BMP_pressure;
   uri += "&field6=";
   uri += DHT22_temperature;
   finish = millis();

   start = millis();
   CiaoData data = Ciao.write(CONNECTOR, SERVER_ADDR, uri);
   finish = millis();  
   Serial.print(F("caio write takes: ")); Serial.println(finish-start);

}

void BuildDataStr(){
  int i=0;
  String dataStr;
  dataStr = "<";
  dataStr += DS18B20_temp_C;
  dataStr += ",";
  dataStr += WindSpeed_kmph;
  dataStr += ",";
  dataStr += WindBearing; 
  dataStr += ",";
  dataStr += BMP_temp;
  dataStr += ",";
  dataStr += BMP_pressure;
  dataStr += ",";
  dataStr += DHT22_temperature;
  dataStr += DHT22_humidity;
  dataStr += ">";
  Serial.println(dataStr);
  PostThingSpeak();
}//BuildDataStr()

void myDelay(int x){
  for(int i=0; i<=x; i++){
    delayMicroseconds(1000);
  }//for
}

float convertScaledAngleToDegrees(word newAngle){
  word startPos = ams5600.getStartPosition();
  word endPos = ams5600.getEndPosition();
  word maxAngle = ams5600.getMaxAngle();
  float multipler = 0;

  if(maxAngle >0)
  {
    if(startPos == 0)
      multipler = (maxAngle*0.0878)/4096;
    else  //startPos is set to something
      multipler = ((maxAngle*0.0878)-(startPos * 0.0878))/4096;
  }
  else
  {
    if((startPos == 0) && (endPos == 0))
      multipler = 0.0878;
    else if ((startPos > 0 ) && (endPos == 0))
      multipler = ((360 * 0.0878) - (startPos * 0.0878)) / 4096; 
    else if ((startPos == 0 ) && (endPos > 0))
      multipler = (endPos*0.0878) / 4096;
    else if ((startPos > 0 ) && (endPos > 0))
      multipler = ((endPos*0.0878)-(startPos * 0.0878))/ 4096;
  }
  return (newAngle * multipler);
}

void CalculateWindSpeed(byte revolutions,unsigned int Sample_time){
  float mmph;
  int tipDistance = 0; 
  if (revolutions > 0){
    tipDistance = revolutions * windCupCircumference;
    mmph = ((3600000 / Sample_time)*tipDistance);
    WindSpeed_kmph = mmph / 1000000;
     }// if tipDistance > 0, ie wind is blowing!
  else
  {
    WindSpeed_kmph = 0;
  }// else
} //CalculateWindSpeed()

void loop(){timer.run();}//loop
OneWire ds(DS18B20_ONE_WIRE_BUS);

This should not be inside the function. Same thing applies to the BMP180 code.

But the biggest problem is that you are using the String class on an UNO and you only have 385 bytes of ram left. You have to use C-style null-terminated strings.

Pete

Run, don't walk from that abomination: UnoWiFi-Duhveloper-Edition-Lib. Even if you pass in C strings, it will create Strings for you. Several copies. :P

And the error codes? They're Strings too:

    #define ID_ERROR      String(-1)

OMG. What were they thinking?

I gave up on serial comms between my mega and a nodemcu board (another story).

Out of the frying pan and into the fire, I'd say. I'm sorry that their poor decisions have led you down this path. There are many ways to crack this nut -- using that library on an UNO is not one of them.

The library could be re-written to for the UNO (i.e., avoiding String). I could do it, but they'd have to pay me more than they paid the interns... the interns that hacked this out during summer break.

Chaps Thanks for your replies. el_supremo, I agree, not the right place for the declaration, I was trying to improve memory footprint! -dev, I get the feeling your not a fan of the new wifi lib! Would you agree that the bizarre happenings aboard my Uno are a result of 'not enough memory' as promised by the compiler? In which case, do you think I can further reduce the memory demand in software, or I could spend some more cash on aArduino-Industrial-101-ATmega32U4 ? Much more memory!

There is something to be said for a PC full of RAM chips !!!!

Regards Philip

Would you agree that the bizarre happenings aboard my Uno are a result of 'not enough memory' as promised by the compiler?

Yes.

But that message is about how much the UNO sketch starts with. Even if you reduce the starting RAM usage, the Strings used by that library will rapidly fragment the heap, causing weird failures at random times.

do you think I can further reduce the memory demand in software

Not by fiddling with your sketch. The library must be overhauled. Many of the modules are ok, but the top-most interface that your sketch uses is the real culprit. I've seen the same thing in the Arduino Bridge software. Urk.

I honestly have no idea how that library could ever pass a real usage test.

There is something to be said for a PC full of RAM chips !!!!

That library was written as if the 101 was full of RAM chips. :-/

I could spend some more cash...

You could, but...

Thousands upon thousands of projects have sent data wirelessly to a base station. There many examples of weather stations. The problem is not the limited RAM. The problem is the poorly-designed library that is wasteful of the RAM you DO have.

I see several ways to proceed:

1) Re-work the library to avoid String.

2) Re-visit the nodemcu + Mega

3) Pick one of the many documented projects (here's one)

Personally, I would pick option 1, but that's because I'm comfortable with C strings and communications. (The library is really a communications library, because it format messages to send to the ESP89266 and then parses the responses.) If it's something you want to try, there is lots of help here for you.

I have a feeling that you may have given up on option 2 and tried "throwing hardware" at the problem. Sometimes that works. Could you link to some of the threads that didn't help you get this going?

Option 3 is very low risk if you follow the steps exactly. That can be a good way to get your feet wet. Then take that project and modify it to your needs, one piece at a time.

Cheers, /dev

-dev Thanks for your most helpful reply. So Ill abandon the lib and consign the uno to 'light duties'. Think I'll revisit nodemcu, with reference to the example in the link you provided.

So near...... !

Thanks again Phil