/*
WiFiweather Rev 1.1

Fixes the wrap around error associated with either no wind being reported (and no <wind> XML heading) or an error in reading 
webinformation so that nothing is detected by FindIt routine.  Also several Serial.print commands used to debug the program were
commented out to save final codee space

*/

#include <Arduino.h>
#include <LiquidCrystal_I2C.h>

#include "WiFiEspAT.h"


// Emulate Serial1 on pins 6/7 if not present
#ifndef HAVE_HWSERIAL1
#include "SoftwareSerial.h"
#include "LiquidCrystal_I2C.h"

SoftwareSerial Serial1(6, 7); // RX, TX
#endif

// The address for the LCD needs to included below:
//LiquidCrystal_I2C lcd(0x3F, 20, 4);   //  address for green display
LiquidCrystal_I2C lcd(0x27, 20, 4);     // address for blue display

char disp_line0[21];    //display buffer and pointers
char disp_line1[21];
char disp_line2[21];
char disp_line3[21];


 char *ptr1_target;
 char *ptr_test;
 char term = '\0';   // add to terminate character arraies 
char *d;
int addrs;

void display_buffer_reset(){
   memset(disp_line0,' ',20);
   memset(disp_line1,' ',20);
   memset(disp_line2,' ',20);
   memset(disp_line3,' ',20);
}

void display_update(){
    lcd.setCursor(0,0);
    lcd.print(disp_line0);
    lcd.setCursor(0,1);
    lcd.print(disp_line1);
    lcd.setCursor(0,2);
    lcd.print(disp_line2);
    lcd.setCursor(0,3);
    lcd.print(disp_line3); 
 if (strlen(disp_line1) > 20) {

   Serial.print(F("Error occurred  "));  // jprint statements used to find temperature wrap around issue.
   Serial.println(strlen(disp_line1));
  // Serial.println(disp_line0);
  // Serial.println(disp_line1);
 }
    else{  // these print statements are used to determine if strings written to display is too long
   /*
    Serial.print(F("The string lengths are "));
    int length = strlen(disp_line0);
    Serial.print(length);
    length = strlen(disp_line1);
    Serial.print(length);
    length = strlen(disp_line2);
    Serial.print(length);
    length = strlen(disp_line3);
    Serial.println(length);
    Serial.println(disp_line0);
    */
}
}
// input Network SSID and password information

const char ssid[] = "xxxxx";            // your network SSID (name)
const char pass[] = "yyyyy";        // your network password

int status = WL_IDLE_STATUS;     // the Wifi radio's status
const char server_qsl[] = "www.hamqsl.com";
const char server_ow[] = "api.openweathermap.org";
const char* timetest = "jsonplaceholder.typicode.com";
#define router "192.168.0.1";

int min;
int hr;
int trig_weather;
int trig_solar;

int temp_int=0;
float temperature;
const char degree = char(223);  //character for degree sign
float pressure_inches;

// limits for electron flux and proton flux
int eflux_limit = 1000;
int pflux_limit = 100;

// Initialize the Ethernet client object
WiFiClient client;

void printWifiStatus()
{
 /*   This print section outputs information on the WIFI connection
  // print the SSID of the network you're attached to
  Serial.print(F("SSID: "));
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address
  IPAddress ip = WiFi.localIP();
  Serial.print(F("IP Address: "));
  Serial.println(ip);

  // print the received signal strength
  long rssi = WiFi.RSSI();
  Serial.print(F("Signal strength (RSSI):"));
  Serial.print(rssi);
  Serial.println(F(" dBm"));
 */ 
}

// Software reset--this is called if the Internet connection hangs up in a way that forces a reset
// Credit for this to Gabriellalevine in instructable circuits

void(*resetFunc)(void) = 0;

//*****************************findIt**********************************************

/* findIt routine for parsing data from an Internet stream.

target -- search key
lgt -- length of return bytes if search code =1 choosen
skip -- number of characters skipped after key matched before
        characters are returned.  Used to save memory
search_code -
 0: return character sequence, exit
 1: return float and exit
 2: return int and exit
return type -
 0: character buffer
 1: float number
 2: integer number
*data_float - return address of returned float data
*integer_float - return address of returned float data  

Routine exits as soon as match as been determined and desired
data returned.  Routine can be called multiple times to 
find sequential data.
*/

char * findIt (char *target, int lgt, int skip = 0, 
int search_code = 0, int return_type = 0, float *data_float=0,
int *data_int=0){
 
  // up to 10 characters can be returned
  // static character definition so can be accessed outside of function.

  static char c[11] ="         " ;


  int status_code = 0;
  char *ptr_c;
  char *ptr_target;
  char web_Data;
  ptr_target = &target[0];
  bool flag = false;
  int test_counter = 0;
 
  char quot = '"';
  char xmp_left = '>';
  char xmp_right = '<';
  static char nothing_found[4] = {' ','-',' ','\0'};  //returned when search item isn't found in search mode 1 or 2

  int lgth_target = strlen (target);
  ptr_c = &c[0];         // Characters captured in c[]
  memset(c,' ',11);  // clear out character buffer
  int x = 0;   //counter of unknown

  /*  Status code used in switch commands:
 0 no match
 1 match of some key characters
 2 key match complete
 3 # characters to skip, search mode = 0
 4 use "" to isolate data; search mode = 1
 5 use <> to isolate data; search mode = 2
 6 decide which format to return data
 
 */
    // start reading data and searching 

  while ((status_code !=6) && client.available() !=0){
  web_Data = client.read();
  //Serial.print(web_Data);  // use to print out data being read from website
    switch(status_code){

      case 0:
      if (*ptr_target != web_Data){  // no match 
        test_counter ++;   //increment test counter
        break;
      }
      else{
        status_code = 1;
        ptr_target++;
        x++;
        break;
      }

      case 1:{
     
      x++;

      if(*ptr_target == web_Data &&  (x<lgth_target)){
         // x++;
          ptr_target++;
          break;
      }
      if (*ptr_target != web_Data && (x < lgth_target)){
          // incomplete match--reset for new search
          status_code = 0; 
          x=0;            //reset counter and target         
          ptr_target = &target[0];
          break;
      }

      if (x == lgth_target) {    
           
          if  (skip !=0){         
            
              status_code = 2;
              x=0;
              break;
          }
           if (search_code == 0){    //load number of specified characters
            status_code = 3;
            break;
          }
          if (search_code == 1){    //load characters between **'s
             test_counter = 0;
             status_code = 4;
             break;
          }
          if (search_code == 2){    // load characters between "<>"
             x=0;
             status_code = 5;
             break;
          }  
        }  
            
        }
          
       case 2:  {          // skip characters
       
        if (x < skip) {
         ptr_target++;    
         x++;
         break;
       }
          x=0;      //skip complete or no skip requested

          if (search_code == 0){    //load number of specified characters
            status_code = 3;
            break;
          }
          if (search_code == 1){    //load characters between **'s
             status_code = 4;
             break;
          }
          if (search_code == 2){    // load characters between "<>"
             status_code = 5;
             break;
          }  
        
       } 
       
      case 3:{

      if (x< lgt ){
       *ptr_c = web_Data;   // add the data to the string
        x++;      
        ptr_c++; 
        break;
      }
      else {
        *ptr_c = term;  //terminate character array
        status_code = 6;
        break;
      }
    }
    case 4: {
      if (web_Data == quot && flag==false){
        flag = true;
        break;
        }

      if (flag == true && web_Data != quot){
        *ptr_c = web_Data;
        ptr_c++;
        break;
        }
      if (flag == true && web_Data == quot){
        flag = false;
        *ptr_c = term;
        status_code = 6;  //exit routine
        break;
      }  
    }

    case 5:{
      if (web_Data != xmp_left && flag == false){   // '>' detected, start collecting
        break;
        }

      if (web_Data == xmp_left && flag == false){
        flag = true;
        break;
        }

      if (web_Data != xmp_right && flag == true){
        *ptr_c = web_Data;
        ptr_c++;
        break;

      }
      if (flag == true && web_Data == xmp_right){
        status_code = 6;  //exit routine
        break;
      }  
    }    
     
  }  
}     

      if (client.available() == 0){
        Serial.println(F("ran through webstrean without target match"));  // prints message when search passes through string without match
        return nothing_found;   // got to this point without finding the target word--return character " - "

      }
      if (return_type == 0){   //case 6 return character pointer
        
        return c;


        
      } 
      if (return_type == 1 ){      //return float
        double temp = atof(c);

        float temp1 =  temp;
        *data_float = temp1;

        return c;
       
      } 

      *data_int = atoi(c);
      
      return c;
}
     
  


//*******************Solar conditions reader **********

void solar_conditions()
{
   // This is code associated with the connection to QSL solar data XML feed
 
    int solarflux;
    int aindex;
    int kindex;
    char * xray;
    int sunspots;
    int protonflux;
    int electonflux;
    float swind;
    int swind_int;
    float dummy;

    bool flag_alert;
    int alert_type;
    int y;  // used to define alert type: 0-Xray, 1-EFLUX, 2-PFlux

    char target1[] = "solarflux";
    char target2[] = "aindex";
    char target3[] = "kindex";
    char target4[] = "xray";
    char target5[] = "sunspots";
    char target6[] = "proton";
    char target7[] = "electon";
    char target8[] = "solarwind";

    char alert [3][6] = {{'X','-','R','a','y','\0'} , {'E','F','l','u','x','\0'}, {'P','F','l','u','x','\0'}};

   
   flag_alert = false;  //set to quiescent state 
          
  if (client.connect(server_qsl,80)) {
    //Serial.println(F("Connected to QSL server"));
    // Make a HTTP request
    client.println(F("GET /solarxml.php HTTP/1.1"));
    //client.println(F("GET /solar.php HTTP/1.1"));
    client.println(F("Host:www.hamqsl.com"));    
    client.println(F("User-Agent: arduino-Wifi"));
    client.println(F("Connection: close"));
    client.println();
    
    
  }
 else{
    Serial.println(F("unable to connect 3"));
  }
  delay(1000);  // wait for data to arrive from hamqsl.com
  
  findIt(target1, 0, 0, 2, 2,&dummy, &solarflux);

  findIt(target2,0,0,2,2,&dummy,&aindex);

  findIt(target3,0,0,2,2,&dummy,&kindex);

 xray= findIt(target4,0,0,2,0);
  
    if(( *xray != 'A') && (*xray != 'B')){
      flag_alert = true;
      y = 0;
      //Serial.println("x-ray true");
    }

  findIt(target5,0,0,2,2,&dummy,&sunspots);

  findIt(target6,0,0,2,2,&dummy,&protonflux);
    if (protonflux > pflux_limit){
      flag_alert = true;
      y = 2;
     // Serial.println("PFlux true");
    }

  findIt(target7,0,0,2,2,&dummy,&electonflux);
    if(electonflux> eflux_limit){  
      flag_alert = true;
      y = 1;
   //   Serial.println("Eflux true");
    }

  findIt(target8,0,0,2,1,&swind);
   // Serial.print(swind);
    swind_int = (int) swind;
    
    if (solarflux == 0){
      return; // if get zero readings, skip update
    }

    for (alert_type = 0; alert_type !=3; alert_type ++){
   // Serial.println(alert[alert_type]);}

    // if no alerts, use this output sprintf statements
    if (flag_alert == false) { 
    sprintf(disp_line2,"SN=%-3i  A=%-2i SWind= ",sunspots,aindex);
     
    sprintf(disp_line3,"SFI=%-3i K=%-1i %4ikm/s",solarflux,kindex,swind_int);
    }

    // if alerts use these output statements instead
    
    else{
     
    sprintf(disp_line2,"SN=%-3i  A=%-2i *ALERT*",sunspots,aindex);  
    sprintf(disp_line3,"SFI=%-3i K=%-1i  %6s ",solarflux,kindex,alert[y]);
    }
    display_update();

    return;

   /*
    Serial.println();    print of all solar weather information
    Serial.print ("Solar Flux =  ");
    Serial.println (solarflux);
    Serial.print ("A index =  ");
    Serial.println (aindex);
    Serial.print (" K index =  ");
    Serial.println (kindex);
    Serial.print ("Sunspot number is  ");
    Serial.println (sunspots);
  */
    
     
 }  
}

//**************** Open Weather reader***********

void weather(){
//  Code associated with connecting to Open Service Weather Service

      const String location = "palo%20alto,usa";     // change for desired location
      const String apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";   // insert API key from Open Weather
      int pressure;
      
      char pres1[4];
      float wind;
      char target1[] = "value";  //followed by 14 skips
      char target2[] = "pres";  //followed by 11 skips
      char target3[] = "speed";  //followed by 7 skips
      char target4[] = "code";   //followed by 1 skip
      
      float dummy;   
      float temp;
      int wind_int;
      char * wind_dir;

    if (client.connect(server_ow, 80)) {
    //Serial.println(F("Connected to weather server"));
    // Make a HTTP request
    client.print("GET /data/2.5/weather?");
    client.print("id=5380748");
    client.print("&appid="+apiKey);
    client.print("&mode=xml");
    client.println("&units=imperial");
    client.println("Host:api.openweathermap.org HTTP/1.1");
    client.println("connection: close");
    client.println();
  }
  
  else{
    Serial.println(F("unable to connect 2"));
    resetFunc(); //force a program reset
  }
    delay(1000);  // wait to be sure data ready
    
    findIt(target1,0,0,1,1,&temp);

    findIt(target2,0,0,1,2,&dummy,&pressure);

    findIt(target3,0,0,1,1,&wind);
    wind_int = (int)wind;

   wind_dir = findIt(target4,0,0,1);
    
   temp_int = (int)temp;
  // Serial.println(wind_dir);
  
   pressure_inches = pressure*.02953;  //following rounds to a tenth
   pressure_inches = 10*pressure_inches;
   pressure_inches = round (pressure_inches);
   pressure_inches = pressure_inches/10;
   dtostrf(pressure_inches,3,1,pres1);

  // Serial.print(F("wind direction= "));
  // Serial.println(wind_dir);
  
  

   if (pressure_inches!=0){  // update display only if got a good internet reading
   //sprintf(disp_line0,"%-2s/%-2s   %-5s  %3i%1cF",mm,dd,time,temp_int,degree);
   sprintf(disp_line1,"P=%3s   %2imph  %3s ",pres1, wind_int, wind_dir);
   display_update();
   
   }

}
  


//*************** time_reader*****************



int time_reader(){

   
    char * d;
    char target1[] = "Date:";
    char * t;
    char target2[] =  "20";  //Trigger on year 20xx. we are good
                        // for 80 years.
    char *ptr_date;
    char date[7];
    ptr_date = &date[0];
    int n=0; 
  //if (client.connect(server_nist,13)){
    if (client.connect(timetest,80))
  {
    //Serial.println("Connected to time server");
    client.println("HEAD / HTTP/1.1");
    client.print ("Host: ");
    client.println (timetest);
    client.println("User-Agent: arduino-Wifi");
    client.println("Connection: close");
    //client.print("Host:time.nist.gov");
    client.println();
    client.flush();
  }
  else{
    Serial.println(F("unable to connect 1"));
     //this is a bad place to be; go to reset and start over
    resetFunc();      
    void start_up();  // restart 
  }
  delay(1000); // wait for data to start filling display_buffer_reset

  d = findIt(target1,6,5);
  
   for (n=0; (n< 6); n++) {
    *(ptr_date + n) = *(d + n);}
   ptr_date[6] = term; 
  

  t = findIt(target2,5,2);

//get hour and 10 minute triggers
  hr = *(t+1) - '0';  //this operation converts characters to int numbers
  min = *(t+3) - '0';

sprintf(disp_line0, "%-5s  %-5s %3i%1cF ",date,t,temp_int,degree);
//Serial.print(date);
//Serial.println(t);

display_update();
return 0;
}



//  This is the WiFi start up routine.  It will be called initaially and if in heat of action, 
// the connection is lost during a web lookup.


void start_up(){
  display_buffer_reset();
   WiFi.init(&Serial1);
  //Serial.println("initalization complete");
  sprintf(disp_line0," Connecting to WiFi ");
  display_update();
  // check for the presence of the shield
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println(F("WiFi shield not present"));
    // don't continue
    while (true);
  }
  // attempt to connect to WiFi network
  while ( status != WL_CONNECTED) {
   // Serial.print(F("Attempting to connect to SSID: "));
   // Serial.println(ssid);
    // Connect to WPA/WPA2 network
    status = WiFi.begin(ssid, pass);
    
  }

  // you're connected now, so print out the data
  //Serial.println(F("You're connected to the network"));
  
  display_update();
  delay(1000);
  printWifiStatus();

  time_reader();      //get data for the first time and select triggers for next reading
  trig_weather = min;
  trig_solar = hr;
  //
  weather();
  //time_reader();
  solar_conditions();
    
  }



     


//**************************** Setup ***********************************

void setup()
{
  // initialize serial for debugging
  Serial.begin(115200);
  // initialize serial for ESP module
  Serial1.begin(9600);
  
  lcd.init();      //initialize the lcd
  lcd.backlight();

  start_up();  // initilize WiFi and get first data from sources
}
  
    
 
void loop(){

time_reader();

if (min != trig_weather){    //update weather every 10 minutes
  weather();
  trig_weather = min;
}

if (hr != trig_solar){       // update solar conditions every hour
  solar_conditions();
  trig_solar = hr;

  }
 delay(10000);     //read time again in 10 seconds over and over
}