So you're a Noob? Post your questions here until you graduate! Don't be shy.

User avatar
By SandiaSlice
#66446 Hi, this is a first post - apologize if it is too long or if I'm ignoring some etiquette.

I am trying to post weather station data from an Arduino to a webserver running in a Linux VM running on a home server. I am using a Sparkfun ESP8266 wifi module https://www.sparkfun.com/products/13678 . I am using ESP8266wifi.h library found here: https://github.com/ekstrand/ESP8266wifi .

I actually have this working, but it is not to my satisfaction because the way it works is not very efficient. My Arduino Mega loops and continuously is reading weather station sensors and "conditioning" the data. Then every 30 seconds (or however long I decide to set the posting interval) the arduino loop code calls a subroutine to use the ESP8266 to post the data to my server via a TCP connection.

My issues are that :
If I try to post all of the data at once, i get a "server busy" error and the post fails.
However, if I break up the POST requests into 4 separate messages less than 250 bytes or so they are all successful.

Also, the function that I call to do the posting takes like 20-30 seconds to do all of the POST requests. This seems way too long, it closes the TCP connection and has to reconnect to my server between each of the 4 POSTs. This subroutine freezes up my arduino loop code so that the arduino can't do anything else while it is waiting for the server data submisions to "go through."

Am I doing it all wrong? Should I load up some code on the ESP8266 module to accept data from the arduino and somehow have the module itself work to post to the server while releasing the arduino to go back to the looping code? I also have a couple of Adafruit HUZZAH ESP8266 breakout boards, but they come loaded with something called NodeMCU Lua interpreter, which seems harder to work with (or am I wrong?).

following is my code on the Arduino Mega:
Code: Select all/*
BMP180 measures absolute pressure.
This is the actual ambient pressure seen by the device, which will vary with both altitude and weather.
You will also need to connect the I2C pins (SCL and SDA) to your Arduino.
For the Mega2560: SDA=20   SCL=21
look at the reference for your board if it is different.
My BMP180 has a tiny little on-board 5v->3.3v regulator, so I just use 5v, gnd, and the SCL and SDA I2C pins 1-4 on the mini board

DHT(11,22, or 21) sensors are used to sense temperature, & humidity.
Connect pin 1 (on the left) of the sensor to +5V
NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1 to 3.3V instead of 5V!
Connect pin 2 of the sensor to whatever your DHTPIN is (digital pin 2 (defined below) in my case)
Connect pin 4 (on the right) of the sensor to GROUND
Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor (if you have an AM2302, the resistor is already soldered in
to the package).
*/
#include "DHT.h"
#include <SFE_BMP180.h>
#include <Wire.h>
#include <ESP8266wifi.h>

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 11 on the Arduino
#define ONE_WIRE_BUS 11
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature DS18B20(&oneWire);
// array to hold DS18B20 (Dallas Temp Sensor) device address
DeviceAddress DS18B20_addr =  { 0x28, 0xFF, 0xA8, 0x76, 0xB5, 0x16, 0x03, 0xC9 };
// DS18B20 temp sensor
float DS18B20_TempF;
float DS18B20_tempC;
// variable for temp average
float Avg_of_TEMP_sensors;

#define DHTPIN 12     // what digital pin we're connected to for DHT11 or DHT22 sensor
// Uncomment whatever type DHT sensor you're using!
//#define DHTTYPE DHT11   // DHT 11
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

// create dht as DHT object
DHT dht(DHTPIN, DHTTYPE);
// Initialize DHT sensor.
float DHT_Humidity, DHT_TempF, DHT_TempC, hif, hic;

String ssid ="MySSID";
String password="MyW!F1P@sswd";
String server = "MyWeatherURL.net"; // I have a domain registered to point to my server's ip address
String uri[4] = {"/CFS_Weather_post0.php", "/CFS_Weather_post1.php", "/CFS_Weather_post2.php","/CFS_Weather_post3.php"};
String Str_sensorData[12];

const String humidity_Field = "humidity";
const String longAvg_atm_press_Field = "p_12h_avg";
const String shortAvg_atm_press_Field = "p_1h_avg";
const String atm_press_Field = "p_mb";
const String hundredths_rain_Field = "rain_hth_in";
const String hr_rain_accum_Field = "rain_1h";
const String last24_rain_accum_Field = "rain_24h";
const String solar_radiation_Field = "solar";
const String temp_bmp180_Field = "t_bmp180";
const String temp_dht22_Field = "t_dht22";
const String temp_ds18b20_Field = "t_ds18b20";
const String temp_Field = "avg_temperature";
const String wind_speed_Field = "cur_wspd";
const String wind_direction_Field = "cur_wdir";
const String two_min_avg_wind_speed_Field = "wspd_2m_avg";
const String two_min_avg_wind_direction_Field = "wdir_2m_avg";
const String min_gust_speed_Field = "gust_wspd_1m";
const String min_gust_direction_Field = "gust_wdir_1m";
const String ten_min_gust_wind_speed_Field = "gust_wspd_10m";
const String ten_min_gust_wind_direction_Field = "gust_wdir_10m";
const String ten_min_avg_wind_speed_Field = "wspd_10m_avg";
const String ten_min_avg_wind_direction_Field = "wdir_10m_avg";

const unsigned long UPDATE_RATE = 60000; // every 1 min
unsigned long lastUpdate = 0; // Keep track of last update time

// create bmp as BMP180 object
SFE_BMP180 bmp;
#define ALTITUDE 1886.0 // Altitude of home in ABQ
double BMP180_TempC, BMP180_Pressure,BMP180_TempF, p0, a;
 
// setup wind-sensor variables
// for the wind-vane I am using a continuous turn potentiometer powered with 5v from the Arduino
float vane_degrees = 180.0;
int vane_Pin = 0;

// for the anenometer
// we do not need to define a digital Pin for the anenometer because I'm using the
// pin-attached interrupt on Arduino Digital Pin 2 (your arduino may use different pins so refer to
// the documentation on pin-attached interrupts if you are not using a Mega2560 compatible board
// my ISR (Interrupt Service Routine) modifies the following variable so we must declare it as "volatile"
volatile byte anenometer_half_revs;
byte anenometer_half_revs_old;
unsigned int anenometer_rpm;
unsigned long anenometer_time_mark;

// calulated data
float AvWSpd_2m = 0;
float AvWDir_2m = 0;
float GustSpd_1m = 0;
float GustDir_1m = 0;
float wind_speed = 10;
float wind_direction = 0;
float GustSpd_10m = 0;
float GustDir_10m = 0;
float AvWSpd_10m = 0;
float AvWDir_10m = 0;
float avg_atm_press_1hr = 0;
float rain_accum_1hr = 0;
float avg_atm_press_12hr = 0;
float rain_accum_24hr = 0;


// setup rain gauge variables
// we do not need to define a digital Pin for the rain gauge because I'm using the
// pin-attached interrupt on Arduino Digital Pin 3 (your arduino may use different pins so refer to
// the documentation on pin-attached interrupts if you are not using a Duemillenove compatible board
// I am using a "teeter-totter" rain gauge that I calibrated outside this program. Each time the bucket
// lever tilts from one side to the other, it is recorded by an ISR in the following "volatile" variable
volatile byte rain_gauge_tilts;
byte rain_gauge_tilts_old;
unsigned int hundredths_rain;
unsigned long rain_gauge_time_mark;

// Solar radiation sensor
int irradiance;
float solar_radiation;



//**ESP8266wifi(Stream serialIn, Stream serialOut, byte resetPin, Stream debugSerial)**
ESP8266wifi Esp8266(Serial1, Serial1, 7);
boolean ESP_connected = false;

void setup() {
  // begin Serial communications with a computer if connected to Arduino
  Serial.begin(115200);
  Serial.println("REBOOT");

  // begin Serial communications with ESP8266 module we are using as WiFi radio in client mode
  Serial1.begin(115200);
  delay(1000);
  Serial.println("Initializing radio"); delay(50);
  //call subroutine that just resets the radio
  esp_Reset();

  Serial.println("Accessing network"); delay(50);
  // call subroutine that connects ESP 8266 to your router' WiFi using your provided access credentials
  for (int x = 0; x <= 10; x++) {
    ESP_connected = esp_connectWifi();
    if (ESP_connected) {Serial.println("Initial WiFi connection successful!"); delay(50); break;}
    else {delay(500);}
  }
  // if esp8266 wifi connection fails after 10 quick attempts, wait 1 minute and reinitiate setup procedures 
  if (!ESP_connected) {Serial.println("Initial WiFi connection failed!"); delay(60000); setup();}
   
  Serial.println();
  Serial.print("provided altitude: ");
  Serial.print(ALTITUDE,0);
  Serial.print(" meters, ");
  Serial.print(ALTITUDE*3.28084,0);
  Serial.println(" feet");

  DS18B20.begin();
  Serial.print("Found ");
  Serial.print(DS18B20.getDeviceCount(), DEC);
  Serial.println(" devices.");
  DS18B20.setResolution(DS18B20_addr, 12);
  Serial.print("Device 0 Resolution: ");
  Serial.print(DS18B20.getResolution(DS18B20_addr), DEC);
  Serial.println();

 
  attachInterrupt(digitalPinToInterrupt(2), OH090U_detect, FALLING); //Initialize the intterrupt pin (Arduino digital pin 2)
  anenometer_half_revs = 0;
  anenometer_rpm = 0;
  anenometer_time_mark = 0;
 
  // Initialize the sensor (it is important to get calibration values stored on the device).

  if (bmp.begin())
    Serial.println("BMP180 init success");
  else
  {
    // Oops, something went wrong, this is usually a connection problem,
    // see the comments at the top of this sketch for the proper connections.

    Serial.println("BMP180 init fail\n\n");
    while(1); // Pause forever.
  }

  Serial.println("DHTxx init startup");
  dht.begin();

  readSensors(); // Get updated values from sensors
  Avg_of_TEMP_sensors = (DHT_TempF + DallasTemperature::toFahrenheit(DS18B20_tempC) + BMP180_TempF)/3.0;
  Serial.print("average of Temperatures: ");
  Serial.print(Avg_of_TEMP_sensors,1);
  Serial.println(" *F");
  Serial.println();

  Str_sensorData[0] = "avg_temperature=" + String(Avg_of_TEMP_sensors) + "&humidity=" + String(DHT_Humidity);
  Serial.println("Str_sensorData: " + Str_sensorData[0]); delay(10);

  if (PostData(1))
    {Serial.println("SUCCESS!"); lastUpdate = millis();}
  else
    {Serial.println("Failed :("); lastUpdate = millis();}

} // end setup();



// loop()
void loop()  {
  if (anenometer_half_revs_old < anenometer_half_revs) {Serial.println("detect");}
  anenometer_half_revs_old = anenometer_half_revs;

  if (anenometer_half_revs >= 6) {
     anenometer_rpm = 30*1000/(millis() - anenometer_time_mark)*anenometer_half_revs;
     anenometer_time_mark = millis();
     anenometer_half_revs = 0;
     Serial.println("Anenometer RPM: ");
     Serial.println(anenometer_rpm,DEC); 
  }
  readSensors(); // Get updated values from sensors

 
  // In the meanwhile, we'll print data to the serial monitor,
  // just to let the world know our Arduino is still operational:
  Serial.print(millis()); // Timestamp
  Serial.print("Wind Direction= ");
  Serial.print(vane_degrees);
  Serial.println();
  Serial.print("DS18B20 Temp F: ");
  Serial.println(DallasTemperature::toFahrenheit(DS18B20_tempC)); // Converts DS18B20_tempC to Fahrenheit
  Serial.println();
  Serial.print("Humidity: ");
  Serial.print(DHT_Humidity);
  Serial.print(" %\t");
  Serial.print(DHT_TempF);
  Serial.print(" *F\t");
  Serial.print(hif);
  Serial.println(" *F");
  Serial.println();
 
  Avg_of_TEMP_sensors = (DHT_TempF + DallasTemperature::toFahrenheit(DS18B20_tempC) + BMP180_TempF)/3.0;
  Serial.print("average of Temperatures: ");
  Serial.print(Avg_of_TEMP_sensors,1);
  Serial.println(" *F");
  Serial.println();

  Str_sensorData[0]=
    temp_Field + "=" + String(Avg_of_TEMP_sensors) + "&" + humidity_Field + "=" + String(DHT_Humidity) + "&" +
    atm_press_Field +  "=" + String(p0)  + "&" +   solar_radiation_Field + "=" + String(solar_radiation) + "&" +
    wind_speed_Field +  "=" + String(wind_speed) + "&" + wind_direction_Field + "=" + String(vane_degrees);
 
  Str_sensorData[1]=
    temp_bmp180_Field +  "=" + String(BMP180_TempF) + "&" + temp_dht22_Field +  "=" + String(DHT_TempF) + "&" +
    temp_ds18b20_Field + "=" + String(DS18B20_TempF)  + "&" +   longAvg_atm_press_Field +  "=" + String(avg_atm_press_12hr) + "&" +
    shortAvg_atm_press_Field +  "=" + String(avg_atm_press_1hr) + "&" + hundredths_rain_Field +  "=" + String(hundredths_rain);
   
  Str_sensorData[2]=
    hr_rain_accum_Field + "=" + String(rain_accum_1hr) + "&" + last24_rain_accum_Field + "=" + String(rain_accum_24hr)  + "&" +   
    two_min_avg_wind_speed_Field + "=" + String(AvWSpd_2m) + "&" + two_min_avg_wind_direction_Field +  "=" + String(AvWDir_2m) + "&" +
    min_gust_speed_Field + "=" + String(GustSpd_1m) + "&" + min_gust_direction_Field +  "=" + String(GustDir_1m);
 
  Str_sensorData[3]=
    ten_min_gust_wind_speed_Field +  "=" + String(GustSpd_10m) + "&" + ten_min_gust_wind_direction_Field + "=" + String(GustDir_10m)  + "&" +
    ten_min_avg_wind_speed_Field +  "=" + String(AvWSpd_10m) + "&" + ten_min_avg_wind_direction_Field + "=" + String(AvWDir_10m);
       
  Serial.println("Str_sensorData0: " + Str_sensorData[0]); Serial.println(); delay(10);
  Serial.println("Str_sensorData1: " + Str_sensorData[1]); Serial.println(); delay(10); 
  Serial.println("Str_sensorData2: " + Str_sensorData[2]); Serial.println(); delay(10);
  Serial.println("Str_sensorData3: " + Str_sensorData[3]); Serial.println(); delay(10); 
 
  // If current time is UPDATE_RATE milliseconds greater than
  // the last update, send new data.
  if (millis() > (lastUpdate + UPDATE_RATE))  {
    Serial.print("Sending update...");
    if (PostData(1)) {
      Serial.println("SUCCESS!"); lastUpdate = millis();
    }
    else
      Serial.println("Failed :(");
  }

} // end loop

//reset the esp8266 module
void esp_Reset() {
  Esp8266.begin();
  Serial1.println("AT+CIPMUX=1");
}

//connect to your wifi network
boolean esp_connectWifi() {
  if(Esp8266.isConnectedToAP()){Serial.println("WiFi ALREADY Connected!"); }
 
  else if(Esp8266.connectToAP(ssid,password)) {
    Serial.println("WiFi Connected!"); delay(50);
  }
  else {
    Serial.println("Cannot connect to wifi! #$@!"); delay(50);
    return false;
  }
  return true;
}


// this subroutine will return 0 if failed or 1 if successful
// this function is called with variable "post_type"
// valid values are: 1, 5, & 30 and represent minute intervals as not all
// data posts will include all data types 
int PostData(int post_type) {

  boolean posted = false;
 
  for (int i = 0; i < 4; i++) {
    if (Esp8266.connectToServer("cfs-weather.kicks-ass.net","80")) Serial.println("CONNECTED TO SERVER");
    String postRQ =
      "POST " + uri[0] + " HTTP/1.0\r\n" +
      "Host: " + server + "\r\n" +
      "Accept: *" + "/" + "*\r\n" +
      "Content-Length: " + Str_sensorData[i].length() + "\r\n" +
      "Content-Type: application/x-www-form-urlencoded\r\n" +
      "\r\n" + Str_sensorData[i]; 
    Serial.println("***BEGIN Constructed postRQ***");
    Serial.print(postRQ);
    Serial.println();
    Serial.println("***END Constructed postRQ***");
    if (Esp8266.send(SERVER, postRQ) == true){Serial.println("post #" + String(i) + " succesful!"); posted = true;}
                                        else {posted = false;} 
    Esp8266.disconnectFromServer(); delay(50);
  }
 
  if (posted==true){return 1;}
  else {Serial.println("Server Post Failed"); return 0;}
     // close the connection
    //Esp8266.disconnectFromServer();
   

}



void readSensors()  {
  char status;

 
  // some of this code(and documentaion) was borrowed from example libraries and was modified for
  // use here only if necessary - attribution is given when necessary/requested
  /* Before taking a pressure reading you must take a temperature reading.
  This is done with startTemperature() and getTemperature().
  Start a temperature measurement:
  If request is successful, the number of ms to wait is returned.
  If request is unsuccessful, 0 is returned.
  The result is in degrees C.
   */
  status = bmp.startTemperature();
  if (status != 0)
  {
    delay(2*status); // Wait for the measurement to complete

   // Retrieve the completed temperature measurement:
   // Note that the measurement is stored in the variable BMP180_Temp.
   // Function returns 1 if successful, 0 if failure.
   status = bmp.getTemperature(BMP180_TempC);
    if (status != 0)
    {
      // Print out the measurement:
      Serial.print("BMP 180 temperature: ");
      Serial.print(BMP180_TempC,2);
      Serial.print(" deg C, ");
      BMP180_TempF = (9.0/5.0)*BMP180_TempC+32.0;
      Serial.print(BMP180_TempF,2);
      Serial.println(" deg F");
      /*
      Once you have a temperature reading, you can take a pressure reading.
      This is done with startPressure() and getPressure().
      The result is in millibar (mb) aka hectopascals (hPa).
      Start a pressure measurement:
      The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
      If request is successful, the number of ms to wait is returned.
      If request is unsuccessful, 0 is returned.
       */
      status = bmp.startPressure(3);
      if (status != 0)
      {
        delay(2*status); // Wait for the measurement to complete
       
        // Retrieve the completed pressure measurement:
        // Note that the measurement is stored in the variable BMP180_Pressure.
        // Note also that the function requires the previous temperature measurement (T).
        // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
        // Function returns 1 if successful, 0 if failure.
        status = bmp.getPressure(BMP180_Pressure,BMP180_TempC);
        if (status != 0)
        {

          // Print out the measurement:
          Serial.print("absolute pressure: ");
          Serial.print(BMP180_Pressure,2);
          Serial.print(" mb, ");
          Serial.print(BMP180_Pressure*0.0295333727,2);
          Serial.println(" inHg");

          // The pressure sensor returns abolute pressure, which varies with altitude.
          // To remove the effects of altitude, use the sealevel function and your current altitude.
          // This number is commonly used in weather reports.
          // Parameters: P = absolute pressure in mb, ALTITUDE = current altitude in m.
          // Result: p0 = sea-level compensated pressure in mb

          p0 = bmp.sealevel(BMP180_Pressure,ALTITUDE);
          Serial.print("relative (sea-level) pressure: ");
          Serial.print(p0,2);
          Serial.print(" mb, ");
          Serial.print(p0*0.0295333727,2);
          Serial.println(" inHg");
        }
      }
    }
  }
  delay(50);
 
  // DHTxx: Reading temperature or humidity  takes about 250 milliseconds!
  // Sensor readings will only be refreshed about every 2 seconds
  // We will use the available DHT sensor library to do this
  DHT_Humidity = dht.readHumidity();
  //  // Read temperature as Celsius (the default)
  //  float DHT_TempC = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  DHT_TempF = dht.readTemperature(true);
  // Check if any reads failed and exit early (to try again).
  if (isnan(DHT_Humidity) || isnan(DHT_TempC) || isnan(DHT_TempF)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }
  // Compute heat index in Fahrenheit (the default)
  hif = dht.computeHeatIndex(DHT_TempF, DHT_Humidity);
  //  // Compute heat index in Celsius (isFahreheit = false)
  //  float hic = dht.computeHeatIndex(DHT_TempC, DHT_Humidity, false);


 
  // reading from voltage divider circuit set up across the wind-vane's
  // continuously rotating potentiometer. The analogRead returns an int from 0 to 1023
  // that needs to be converted to degrees so each step on the analog pin reading is
  // 360/1024 degr or 0.35 degrees with a zero / 360 reading being due north
  // and the 270 reading from the East - thus I need to subtract my sensor value from 360.
  // the Arduino Reference says you can read this 10,000 times a second, so I'll read it 10 times
  // over (about) one-hundreth of a second and take the average
  int vane_val = 0;
  for (int i = 0; i < 10; i++) {
    vane_val = vane_val + analogRead(vane_Pin);
    delayMicroseconds(10);
  }
  vane_degrees = 360 - ((vane_val/1024.0/10.0)*360);

  // need to receive the solar radiation sensors and implement them for now - just report
  // some fraction of micros()
  solar_radiation = 1.0 * micros() / 4500000000.0;

  DS18B20.requestTemperatures(); // Send the command to get temperatures
  DS18B20_tempC = DS18B20.getTempC(DS18B20_addr);
  DS18B20_TempF = (9.0/5.0)*DS18B20_tempC+32.0;
 
} // End readSensors()



void OH090U_detect()//This ISR function is called whenever a magnet/interrupt is detected by the arduino
 {
   anenometer_half_revs++;
 }

void Status_by_LED_Delay(boolean fastLED, int sec_delay) {
  unsigned long delaytimer = (millis() + (sec_delay * 1000));
  int ledPin = 13;                 // LED connected to digital pin 13

  if (fastLED) {
    while (millis() < delaytimer) {
      digitalWrite(ledPin, HIGH);   // sets the LED on
      delay(250);
      digitalWrite(ledPin, LOW);   // sets the LED on
      delay(250);
    }
  }
  else {
    while (millis() < delaytimer) {
      digitalWrite(ledPin, HIGH);   // sets the LED on
      delay(500);
      digitalWrite(ledPin, LOW);   // sets the LED on
      delay(500);
    }
  }   
}
User avatar
By SandiaSlice
#66514
Barnabybear wrote:Hi, I now include and use ESP8266HTTPClient.h as I find to closes the connection much quicker than ESP8266wifi.h which basicaly just lets it timeout, this can take up to 10 seconds.



Thank you, I will try that.
Do you know any tricks to be able to send a larger POST request message?

SDC