Wednesday, March 30, 2016

YellowJacket Arduino with WiShield Wifi (Part 15)

How to Sense the Status of Arduino/Wifi Pins — 
Question: How to check whether the YellowJacket Arduino SPI-CS pin D10 is triggered from normally HIGH to LOW, and how often?
To answer this question, add this code to your sketch Global section
/****** INT1 pin D3 capture CS pin change  **************/
// wire a jumper from CS inputPin D10 to sensePin D3 to catch hardware interrupt
     int watchPin = 10;  // the pin that is being watched
     boolean sensor_status = false;
     int sensePin = 3;   // digital pin 3 int1; int0 pin D2 is used by YJ MRF24 
     int ledPin = 4;   //the digital pin for output to LED display 

void sensorOn() { 
     if (sensor_status != true) { 
          sensor_status = true; 
          Serial.println(F("  >> Sensor Status: Activated >> ")); 
          ledOn(); 
     } 
} 

void sensorOff() { 
     if (sensor_status != false) { 
          sensor_status = false; 
          Serial.println(F("  >> Sensor Status: Cleared >> ")); 
          ledOff(); 
     } 
} 

void ledOn() { 
     digitalWrite(ledPin, HIGH); 
     //Serial.println(F("  >> LED Status: On >> ")); 
} 

void ledOff() { 
     digitalWrite(ledPin, LOW); 
     //Serial.println(F("  >> LED Status: Off >> ")); 
} 
/****** end INT1 pin D3 capture pin change  **************/
Add this code to the sketch setup()
/****** INT1 pin D3 capture pin change  **************/

// Attach interrupt INT1 to pin 3 held normally high through 10K resistor to 3V3
// or use "pinMode(sensePin, INPUT_PULLUP);" for internal pullup resistor
//    Trigger signal (example: D10 is normally HIGH) grounds pin D3 to set sensorOn

attachInterrupt(digitalPinToInterrupt(sensePin), sensorOn, FALLING);

// sensePin senses watchPin immediate state change HIGH to LOW
pinMode(ledPin, OUTPUT);      // sets the digital ledPin as output
pinMode(watchPin, INPUT);     // sets the digital watchPin as input of sense state

sensorOff();
ledOff();

/****** end INT1 pin D3 capture pin change  **************/
Trigger the sensor_status in sketch loop()
/****** INT1 pin D3 capture pin change  **************/

// clear the sensor_status to check for new sensorOn
// Clearing the sensor_status is required here
//    since in the example CS pin D10 was set during the Wifi INIT
//    and we want to check if and when the CS pin changes state

sensorOff();

/****** end INT1 pin D3 capture pin change  **************/
Example Sketch CSpin10 — wire pin D3 (INT1 interrupt) to pin D10 (MRF24 wifi Active). Run the sketch. Mac Wireshark app can be used to watch the network traffic between 10.0.1.56 Arduino and 10.0.1.11 Mac.
/***************************************************
 * CSpin10
 *
 * March 20, 2016
 * This sketch watches CS pin D10 for sensor_status on/off activity
 *
 *    reads a sensor on the Sensor Shield
 *    processes the data
 *    creates a php request
 *    sends the php request to Apache
 *    the php request is submitted to mysql
 *    data element is written to mysql table
 *
 *    The sketch can format the data as int, float, or char array
 *    and submit the request as a char array.
 *
 *    The dtostrf function for converting a float to a char array
 *    will preface the array with a blank for the - sign.
 *    Using an F0.2 format will dispose of leading blanks which
 *    cause errors loadint the data into mysql.
 *
 * Wireshark shows 10.0.1.56 HTTP request:
 *  GET /write_data?value=75.59 HTTP/1.0\r\n
 *    Request Method:  GET
 *    Request URI:  /write_data.php?value=75.79
 *    Request Version:  HTTP/1.0
 *  Host:  Mac_localhost\r\n
 *  \r\n
 *
 * 10.0.1.11 HTTP response:
 *  HTTP/1.1 200 OK\r\n
 *    Request Version:  HTTP/1.1
 *    Status Code:  200
 *    Response Phrase:  OK
 *  Date: Thu, 10 Mar 2016 12:38:14 GMT\r\n
 *  Server: Apache/2.4.10 (Unix) PHP/5.5.20\r\n
 *  X-Powered-By: PHP/5.5.20\r\n
 *  Content-Length: 26\r\n
 *  Connection: close\r\n
 *  Content-Type: text/html\r\n
 *  \r\n
 *  [HTTP response 1/1]
 *  [Time since request: 0.002082000 seconds]
 *  [Request in frame: 247]
 *  Line-based text data: text/html
 *
 *
 *
 ****************************************************/

#include <WiServerIO.h>
//#include "SPI.h"  // Arduino SPI.h not WiShield spi_WS.h

/************************* WiFi Access Point *********************************/

#define WLAN_SSID       "Network"
#define WLAN_PASS       "Network_Passcode"

/*---------------------------------------------------------------------------
 *
 * WiServer set up
 *
 */

#define WIRELESS_MODE_INFRA  1
#define WIRELESS_MODE_ADHOC 2

// Wireless configuration parameters ----------------------------------------
unsigned char local_ip[] = {10, 0, 1, 56}; // IP address of WiShield
unsigned char gateway_ip[] = {10, 0, 1, 1}; // router or gateway IP address
unsigned char subnet_mask[] = {255, 255, 255, 0}; // subnet mask for the local network
const char ssid[] PROGMEM = {"Network"};   // max 32 bytes
char* myhost_ip = "10.0.1.56";

unsigned char security_type = 3;  // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2
// WPA/WPA2 passphrase
const char security_passphrase[] PROGMEM = {"Network_Passcode"};  // max 64 characters

// WEP 128-bit keys
// sample HEX keys
const uint8_t wep_keys[] PROGMEM = {  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, // Key 0
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 1
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 2
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // Key 3
                                   };

// setup the wireless mode
// infrastructure - connect to AP
// adhoc - connect to another WiFi device
unsigned char wireless_mode = WIRELESS_MODE_INFRA;

unsigned char ssid_len;
unsigned char security_passphrase_len;

//---------------------------------------------------------------------------

/****************************** Feeds ***************************************/
/* ---------------------------------------------------------------------------
*
*  A simple data logger for the Arduino analog pins
*
*/

//#define aref_voltage 3.3// tie 3.3V to ARef
#define aref_voltage 5.0  // default internal 5V to ARef

int photocellPin = 1;     // the cell and 10K pulldown are connected to a0
int photocellReading;     // the analog reading from the analog resistor divider

int temperaturePin = 0;   //the analog pin the TMP36's Vout (sense) pin is connected to
//the resolution is 10 mV / degree centigrade with a
//500 mV offset to allow for negative temperatures
int temperatureReading;   // the analog reading from the sensor

/****** INT1 pin D3 capture CS pin change  **************/
// wire jumper from CS inputPin D10 to sensePin D3 to catch hardware interrupt
int watchPin = 10;  // the pin that is being watched

boolean sensor_status = false;
int sensePin = 3;   // digital pin 3 int1; int0 pin D2 is used by YJ MRF24
int ledPin = 4;   //the digital pin for output to LED display

void sensorOn() {
  if (sensor_status != true) {
    sensor_status = true;
    Serial.println(F("  >> Sensor Status: Activated >> "));
    ledOn();
  }
}

void sensorOff() {
  if (sensor_status != false) {
    sensor_status = false;
    Serial.println(F("  >> Sensor Status: Cleared >> "));
    ledOff();
  }
}

void ledOn() {
  digitalWrite(ledPin, HIGH);
  //Serial.println(F("  >> LED Status: On >> "));
}

void ledOff() {
  digitalWrite(ledPin, LOW);
  //Serial.println(F("  >> LED Status: Off >> "));
}
/****** end INT1 pin D3 capture pin change  **************/


char inURL[22] = "/write_data.php?value";
char requestURL[80];
//char data[20];            // char[] array for the converted int or float

float tempF;
int tF;

/*---------------------------------------------------------------------------*/

/*************************** Sketch Code ************************************/

// Function that prints data returned from the server
void printData(char* data, int len) {
  // Note that the data is not null-terminated, may be broken up into smaller packets,
  // and includes the HTTP header.
  while (len-- > 0) {
    Serial.print(*(data++));
  }
}

// Function that assembles URL to send to the server
void myURL() {
  /* ----------------------- data logger ----------------------------------------*/
  // Read data

  photocellReading = analogRead(photocellPin);
  temperatureReading = analogRead(temperaturePin);
  // converting that reading to voltage, which is based off the reference voltage
  float voltage = temperatureReading * aref_voltage / 1024;
  // calculate temperature
  float temperatureC = (voltage - 0.5) * 100 ;  //converting from 10 mv per degree wit 500 mV offset
  //                                            //to degrees ((voltage - 500mV) times 100)
  //Serial.print(temperatureC); Serial.println(" degrees C");

  // convert C to Fahrenheit
  float temperatureF = (temperatureC * 9 / 5) + 32;

  tempF = temperatureF; // save alue in global tempF

  double dF = temperatureF;

  Serial.println("... ");
  // Serial.println(""); Serial.print("Light reading = "); Serial.println(photocellReading);
  // the raw analog reading

  // DEBUG the calculated temperatureF to the Serial Monitor
  Serial.print(temperatureF); Serial.println(F(" degrees F"));

  // conversion of int or float to char[] may not be needed, since "WiServer.print (tempF);"
  // displays the same number (83.49) as "dtostrf(temperatureF, 6, 2, data);"
  // which converts float to char[] array (data), f6.2 format but has leading space for - sign
  // do not know how WiServer.print formats to f6.2 by default.

  char data[20];
  //sprintf will convert int to char, but not float to char which requires dtostrf
  dtostrf(dF, 0, 2, data);        // converts float to char[] array (data)
  // f0.2 format forces left justify which deletes any leading spaces which cause problems in mysql
  Serial.print(data); Serial.println(F(" char degrees F"));

  tF = (int)temperatureF;
  Serial.print(tF); Serial.println(F(" int degrees F"));

  char data1[20];
  sprintf(data1, "%i", tF); // converts int to char[] array (data)
  Serial.print(data1); Serial.println(F(" data[] int degrees F"));

  /* -------------------End Data Logger ----------------------------------------*/

  // Send an int value to mysql works reliably
  //sprintf(requestURL, "%s=%i", inURL, tF); // appends int number; this works

  // Send an int value as a char[] to mysql works reliably
  //sprintf(requestURL, "%s=%s", inURL, data1); // appends char number.

  // Send a double value as a char[] to mysql works reliably with f0.1 format
  sprintf(requestURL, "%s=%s", inURL, data); // appends dtostrf char double number.

  Serial.print(" requestURL= "); Serial.println(requestURL);
}

uint8_t request_ip[] = {10, 0, 1, 11}; // IP Address for requested server Mac
char* myhost = "Mac_localhost";

// A request that gets a file from server at request_ip
GETrequest getMyRequest(request_ip, 80, myhost, requestURL);

void setup() {
  Serial.begin(115200);
  delay(10);

  /****** INT1 pin D3 capture pin change  **************/
  // Attach interrupt INT1 to pin 3 held normally high through 10K resistor to 3V3
  // or use "pinMode(sensePin, INPUT_PULLUP);" for internal pullup resistor
  //    Trigger signal (example: D10 is normally HIGH) grounds pin D3 to set sensorOn
  attachInterrupt(digitalPinToInterrupt(sensePin), sensorOn, FALLING);
  pinMode(sensePin, INPUT_PULLUP);
  // sensePin senses watchPin immediate state change HIGH to LOW
  pinMode(ledPin, OUTPUT);      // sets the digital ledPin as output
  pinMode(watchPin, INPUT);      // sets the digital watchPin as input of sense state
  sensorOff();
  ledOff();
  /****** end INT1 pin D3 capture pin change  **************/

  // Connect to WiFi access point.
  Serial.println();
  Serial.print(F("Init WiServer Client to External IO Server: "));
  Serial.println(myhost);

  // Initialize WiServer and have it use the sendMyPage function to serve pages
  // WiServer.init(sendMyPage);
  WiServer.init(NULL);

  // Ask WiServer to generate log messages (optional)
  WiServer.enableVerboseMode(true);

  // Have the processData function called when data is returned by the server
  getMyRequest.setReturnFunc(printData);

  Serial.println (">>> Send data on 30 sec intervals... ");
  Serial.println (" ");
}

uint32_t x = 0;
// Time (in millis) when the data should be retrieved
unsigned long tempTime = millis();
unsigned long updateTime = tempTime;
unsigned long waitTime = 1000UL * 30UL ; // 30 second cycle

void loop() {

  tempTime = millis();

  /****** INT1 pin D3 capture pin change  **************/
  // clear the sensor_status to check for new sensorOn
  // Clearing the sensor_status is required here
  //    since in the example CS pin D10 was set during the Wifi INIT
  //    and we want to check if and when the CS pin changes state
  sensorOff();
  /****** end INT1 pin D3 capture pin change  **************/


  // Check if it's time to get an update
  if (tempTime >= updateTime) {

    myURL();

    Serial.println (" "); // space to display

    getMyRequest.submit();
    Serial.println (" "); // space to display
    /* DEBUG
       Serial.println (" ");
       Serial.print (" >> If 1: millis() = ");
       Serial.print ( tempTime );
       Serial.print ("  updateTime = ");
       Serial.print ( updateTime );
       Serial.print ("  waitTime = ");
       Serial.println ( waitTime );
    End DEBUG */
    // Get another update 30 seconds from now


    updateTime += waitTime;
    /* DEBUG
        Serial.print (" Revised updateTime = ");
        Serial.println ( updateTime );
    End DEBUG */
  }

  // Run WiServer
  WiServer.server_task();

  delay(100);
}
Sketch CSpin10 Serial Monitor output: The sensor_status of CS pin D10 toggles frequently when needing to transmit data,  controlled by the MRF24 Wifi board.
>> Sensor Status: Cleared >>
>> Sensor Status: Activated >>
>> Sensor Status: Cleared >>
RX 214 bytes from Mac_localhost
HTTP/1.1 200 OK
Date: Tue, 01 Mar 2016 19:04:44 GMT
Server: Apache/2.4.10 (Unix) PHP/5.5.20
X-Powered-By: PHP/5.5.20
Content-Length: 27
Connection: close
Content-Type: text/html
Entered data successfully
>> Sensor Status: Activated >>
>> Sensor Status: Cleared >>
Ended connection with Mac_localhost
>> Sensor Status: Activated >>
>> Sensor Status: Cleared >>
>> Sensor Status: Activated >>
>> Sensor Status: Cleared >>
(Mar 30, 2016)

Monday, March 28, 2016

YellowJacket Arduino with WiShield Wifi (Part 14)

More About Handling JSON Data and Using POSTrequest —
In Part 13, the YJ56 Server sketch responded to “http://10.0.1.56/json” by returning a JSON-formatted data packet, such as, “{“tSensor”:”2016-03-26 14:55:29″,”tempF”:”79.1″,”light”:”281″,”button”:”1″,”led”:”1″}”. The JSON data packet was returned as part of the html stream but enclosed in non-standard <json> and </json> tags. These tags are ignored by an Arduino sketch and WiShield wifi software and can be used as delimiters to parse out the JSON data packet on the Client. The parsed JSON data can then be repackaged and sent to an Apache/php server that can send the data to mySQL for writing to a data table. The ArduinoJson parsing routines (Ref: https://github.com/bblanchon/ArduinoJson/wiki) are well done and saved a lot of time in developing the sketch code used by the YJ57 client.
The YJ56 Sensor Data Server as described, had a time clock, temperature sensor, photocell light sensor, a Pin 3 INT1 On/Off switch sensor, and an led On/Off display. The http code that selects which action to take was based on Ref: http://www.dfrobot.com/forum/viewtopic.php?f=2&t=61. The YJ56 Server can receive remote http commands to set or clear the On/Off switch sensor and the led On/Off display, or they could be set by the YJ56 server hardware or software. Also, both the switch sensor and led can be turned on or off with http commands from a web browser. Those settings are then transmitted within the JSON data to the data base. Some action could then be taken (i.e., alert, turn something on/off) depending on the user’s requirements. The intent of this part of the code is to demonstrate simple methods of remotely checking on sensor settings, such as, has a door been opened, or to turn On/Off a light or valve. The code does not have any security, but it would work in a local, secure wifi situation.
Another issue with using JSON parsing on the Arduino-328P is available dynamic memory which is limited to 2,048 bytes. One goal was to write the JSON data to an SD Card file, but the SD Card library could not be loaded with the ArduinoJson library and compiled within the 2,048 byte dynamic memory limit. Options might be to use an Arduino with more dynamic memory, add a third Arduino that handled the SD Card storage function, or to add other out-board SRAM or FRAM memory chips that might store the data without requiring the large memory requirements of a file system. For the time being, the data were shunted off to Apache/php/mySQL which relieved the dynamic memory issues and provided a method for sending the data to a data base server that has extensive capabilities. This approach also opens the possibilities for using multiple Arduino Data Servers transmitting data to a centralized data base server.
WiShield – Where is the http code?
In several Arduino wifi libraries, the html code for Get and Post requests is added as part of the sketch code. In WiShield, the html code statements are defined in strings.h. Only Get Content-Type:/form is defined in WiShield. For JSON Post Requests, the strings.h file was modified to add a /json Content-Type, as follows:
// Content type form data
const char PROGMEM contentTypeForm[] = {"Content-Type: application/x-www-form-urlencoded"};

// ref: http://stackoverflow.com/questions/4794975/problem-creating-a-http-post-message-with-the-wiserver-library
// add Content-Type /json
const char PROGMEM contentTypeJson[] = {"Content-Type: application/json; charset=utf-8"};
To choose which Content-Type to use in a sketch, a variable, “fType”, was added to WiShield. WiServer.h and .cpp were renamed to WiServerIO.h and .cpp to clarify that changes had been made to those files. Also, all references to WiServer.h had to be changed in the library files and sketches to use WiServerIO.h. In WiServerIO.cpp, a new contentTypeJson was added:
extern const char PROGMEM contentTypeForm[];
extern const char PROGMEM contentTypeJson[];
And below the extern definitions, the fType variable and setContentType function were added:
// use fType to select contentType
int fType = 0;
void setContentType(int value){ fType = value; }
Also in WiServerIO.cpp, code was added within “if (isPost){” to respond to the setting of “fType”:
if (isPost) {
  // Since a post has a body after the blank header line, it has to include
  // an accurate content length so that the server knows when it has received
  // all of the body data.
  char* lengthFieldPos; // Cursor position where the content length place holder starts
  char* contentStart; // Start of the body
  char* contentEnd; // End of the body

    // use 0=text or 1=json data format; added for JSON POST
    if (fType > 0) {
        // JSON data
        WiServer.println_P(contentTypeJson);
    }else{
        // TEXT form data
        WiServer.println_P(contentTypeForm);
}
In WiServerIO.h, add the function definition:
void setContentType(int value); // choose 0=text or 1=json data
Finally, within the Arduino sketch “void setup(){“, the fType can be chosen as:
// choose text or json format data; sets value in WiServerIO.h, .cpp
setContentType(1);     // 0=Text, 1=JSON
setContentType(0) is the default set in WiServerIO.cpp, so sketches using standard html Get requests do not need the setContentType(0) statement added. The code modifications above, can be also extended for transmitting other data types, such as, image files. Rather than using integers to choose the Type, SetContentType might better be defined as a char array that would allow the user to define the desired content type in the sketch, such as,
setContentType("Content-Type: application/json; charset=utf-8");
Ref: http://forum.arduino.cc/index.php?topic=49407.0 shows sketch examples of many data types which might be considered.
  • Content-Type: text/html
  • Content-Type: text/css
  • Content-Type: image/png
  • Content-Type: image/jpeg
  • Content-Type: image/gif
  • Content-Type: video/mpeg
  • Content-Type: application/pdf
  • Content-Type: application/x-javascript
  • Content-Type: application/xml
  • Content-Type: text
POSTrequest Example Sketch Code
Following are portions of sketch code that can be used to create a POST request which might be desired for more secure data transmission. See previous WiServer example sketches for mySensor(), printData(), and other sketch code to fill out this example. Notice the three sendMyRequest functions that are defined in WiShield Library request.cpp.
/*************************** createBody function *********************************/

// POSTrequest setBodyFunc(bodyFunction body);
void createBody()
{
  // char json[] = "{\"val1\":\"12.34\",\"val2\":\"56\"}"; // example JSON
  //    Serial.print(F(" json body >")); Serial.print(json); Serial.println(F("< "))
  // WiServer.print(json);

  // place sensor data into JSON format
  WiServer.print("{\"val1\":\"");
  WiServer.print(vj1);
  WiServer.print("\",\"val2\":\"");
  WiServer.print(vj2);
  WiServer.println("\"}");

}
/*************************** end createBody function *********************************/

/*************************** POSTrequest function *********************************/
//
uint8_t request_ip[] = {10, 0, 1, 50}; // Wifi IP Address for requested server Mac
char* myhost = "mac.localhost";
char inCMD[23] = "/write_json_2data.php?"; // php file to process the JSON POSTrequest

// A request that POSTs to a php server at request_ip
// Create an instance of POSTrequest with the JSON text in the body
POSTrequest sendMyRequest(request_ip, 80, myhost, inCMD, createBody);

/*************************** setup function *********************************/
//
void setup() {

  // If you want to set the aref to something other than 5v
  //analogReference(EXTERNAL);  // enable EXTERNAL if using 3.3 AREF
  analogReference(DEFAULT);     // DEFAULT for no external AREF

  Serial.begin(115200);
  delay(10);

  // Connect to WiFi access point.
  Serial.println();
  Serial.print(F("  > Init WiClient to External IO Server: "));
  Serial.println(myhost);

  WiServer.init(NULL);  // use for POSTrequests

  // Ask WiServer to generate log messages (optional)
  WiServer.enableVerboseMode(true);
  
  // choose text or json format data; sets value in WiServerIO.h, .cpp
  setContentType(1);     // 0=Text, 1=JSON
  
  // Have the printData function called when data is returned by the server
  sendMyRequest.setReturnFunc(printData);
  sendMyRequest.setBodyFunc(createBody);

  Serial.println (F(" >> Send data on 30 sec intervals... "));
  Serial.println (F(">>>"));
}
/*************************** end setup function *********************************/

// Time (in millis) when the data should be retrieved
unsigned long updateTime = 0; // tempTime;
unsigned long waitTime = 1000UL * 30UL ; // 30 second cycle

/*************************** loop function *********************************/
//
void loop() {

  // Check if it's time to get an update
  if (millis() >= updateTime)
  {
    // Obtain sensor data as char arrays
    mySensor();

    Serial.println("Start POST >");
    sendMyRequest.submit();
    Serial.println("< End POST");
    // Get another update waitTime from now
    updateTime += waitTime;
  }

  // Run WiServer task
  WiServer.server_task();

  delay(100);
}
/*************************** end loop function *********************************/

And a php file that would process the returned POSTrequest for 2 sensor values must read the JSON input stream and use json_decode to extract the data elements:
<?php

 // File "write_json_2data.php"
 // written as part of Arduino sketch "JsonSend2Values"
 // to insert two values into MySql table using POSTrequest
 // Prepare variables for database connection

 $dbhost = 'localhost:3306';
 $dbuser = "arduino";  // enter database username
 $dbpass = "arduinotest";  // enter database password
 $conn = mysql_connect($dbhost, $dbuser, $dbpass);

 if(! $conn ) {
   die('Could not connect: ' . mysql_error());
 }
 
 mysql_select_db(‘Arduino’);
 
 // For val1,val2 submitted as JSON POSTrequest
        // read the json input stream as 'php://input' from client
 $jsondata = file_get_contents('php://input');
 // echo $jsondata;

        //convert json object to php associative array
        $data = json_decode($jsondata, true);
 // echo $data;
 
 // get the data value details
 $v1 = $data['val1'];
 $v2 = $data['val2'];

 // Prepare the SQL statement
 $sql = "INSERT INTO Arduino.sensor2 (val1,val2) VALUES ('$v1', '$v2')"; 
 //echo $sql;
 
 // Execute SQL statement
 $retval = mysql_query( $sql, $conn );

 if(! $retval ) {
   die('Could not enter data: ' . mysql_error());
 }

 echo "\nEntered 2 data values successfully into Table sensor2\n";

 mysql_close($conn);

?>

(Mar 28, 2016)

Saturday, March 26, 2016

YellowJacket Arduino with WiShield Wifi (Part 13)

JSON-formatted Sensor Data Moved Over Wifi to mySQL Database — 
In Part 11 of this series, temperature and photocell data were sent as JSON-formatted data from a YellowJacket Arduino data server (YJ56) to a YellowJacket Arduino client (YJ57) using the local wifi network.
In Part 12, sensor data from one YellowJacket was sent via wifi to a local network Mac-based Apache/php server, and php submitted the data to a mySQL data base table for storage.
In this Part 13, examples from Parts 11 and 12 will be merged. The JSON-formatted data from the data server (YJ56) will be transmitted to the YJ57 client where the JSON data will be parsed and sent over the local wifi network to the Mac-based Apache/php server, and php will submit the data to a mySQL data base table.
Hardware Setup
  • YellowJacket YJ56 Arduino with MRF24WB0MA Wifi backpack, Sensor Shield, and SD Card Shield with Real-time clock.
  • YellowJacket YJ57 Arduino with MRF24WB0MA Wifi backpack.
  • Mac running Apache, php, and mySQL.
  • Local wifi network.
Brief Detour: One Fix for blanks in dtostrf output to php
In Part 12, the Arduino code “Send2Values_04.ino” used dtostrf(temperature, 0, 2, data1) to output a non-padded char array “data1” with 2 digits after the decimal point. dtostrf appears to use the necessary number of digits before the decimal point to hold the integer portion of temperature when width=0 is used in the function. Using any width greater than the total width that is required (e.g. 8,2 or -8,2 when only 5,2 is required), either left or right justified, caused failure of data to load into the mySQL data table. By using 0,2, there were no padding spaces either before or after the number stored into char array data1.
Another method to fix the space-padded char array problem is that by contributor “unwind” who shared a deblank function in Ref: http://stackoverflow.com/questions/13084236/function-to-remove-spaces-from-string-char-array-in-c
char * deblank(char *str)
{
  char *out = str, *put = str;

  for(; *str != '\0'; ++str)
  {
    if(*str != ' ')
      *put++ = *str;
  }
  *put = '\0';

  return out;
}
which is implemented as:
double df;
df = temperatureF;
char data1[10];
  dtostrf(dF, 8, 2, data1); // converts float to char[] array as: data1=>>>79.31\0, where ">" = space padding and data1 is \0 terminated
    // Remove blanks from data1 after dtostrf
    deblank(data1);

result: data1=79.31\0, with no leading or trailing blanks,
passes data1 to php with no errors loading the data into mySQL.
Just add the deblank function before  “void setup ()” in the Arduino sketch and “deblank(your_char_array)” to remove any blanks in “your_char_array”. The \0 termination is added by deblank at the end of the number.
YJ56 Sensor Shield WiServer
The SimpleJsonServer_5Sensors_V3 WiServer sketch (on YJ56) collects sensor data and submits it as a response to a GetRequest from a client, e.g., Safari browser or WiClient.
Sensors on Sensor Shield include: time (RTC on SD Card Shield), tempF (pin A0), light (pin A1), button (pin D3 INT1), and led (pin 5).
Pin D3 INT1 would automatically turn on when the board was powered. To fix this, pin D3 was initally held high through 10K resistor to 3V3 (for safety since MRF24 is 3V3), and when the push button was pushed, the pin was grounded through the push button, turning on the interrupt. INT1 was set to trigger on FALLING. Arduino has internal pullup resistors that can be turned on so that external resistors are not required.
The YJ56 Arduino does not have a real-time clock, so time was set using the RTC chip on the SD Card Shield which was backpacked to the YJ56 and Sensor Shield.
/* SimpleJsonServer_5Sensors_V3
 * A simple sketch that uses WiServer to serve a web page
 * containing JSON formatted data
 *
 * 1) GETrequest from Client triggers this server
 * 2) Sensors are read
 * 3) Sensor data formatted into JSON string
 * 4) HTTP response including JSON string is sent over Wifi
 *
 * Sensor Data:
 *  tSensor "YYYY-MM-DD hh:mm:ss"
 *  tempF VARCHAR XXX.YY
 *  light VARCHAR XXX
 *  button int 0 off, 1 on (button sensor, set on with button push)
 *  led1, int 0 off, 1 on  (led state off or on)
 *
 *  10.0.1.56/json        GETrequest for JSON formatted data
 *  10.0.1.56/sensorOn    GETrequest to Set Sensor TRUE
 *  10.0.1.56/sensorOff   GETrequest to clear Sensor FALSE
 *  10.0.1.56/ledOn       GETrequest to Set LED HIGH
 *  10.0.1.56/ledOff      GETrequest to clear LED LOW
 *
 *  button sensor sets interrupt INT1 on pin D3. Pin D3 is held high through 10K resistor to 3V3.
 *  Pin D3 sensor_status is set false on initalization of sketch. Int pins can also use an
 *  internal interrupt "pinMode(buttonPin, INPUT_PULLUP);"
 *  On button push, pin D3 is grounded, which sets INT1 sensor_status to true.
 *
 *  RTC I2C pins A4 and A5 need to be connected between YJ and SD Card Shield.
 */

#include <WiServerIO.h>

// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include <Wire.h>
#include <RTClib.h>
RTC_DS1307 rtc;
DateTime now;

#define WIRELESS_MODE_INFRA 1
#define WIRELESS_MODE_ADHOC 2

// Wireless configuration parameters ----------------------------------------
unsigned char local_ip[] = {10, 0, 1, 56}; // IP address of WiShield
unsigned char gateway_ip[] = {10, 0, 1, 1}; // router or gateway IP address
unsigned char subnet_mask[] = {255, 255, 255, 0}; // subnet mask for the local network
const char ssid[] PROGMEM = {"Network"};   // max 32 bytes

const char* myhost = "WiServer";
char* myhost_ip = "10.0.1.56";

unsigned char security_type = 3;  // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2

// WPA/WPA2 passphrase
const char security_passphrase[] PROGMEM = {"Network_Passcode"};  // max 64 characters

// WEP 128-bit keys
// sample HEX keys
const uint8_t wep_keys[] PROGMEM = {  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, // Key 0
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 1
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 2
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // Key 3
                                   };

// setup the wireless mode
// infrastructure - connect to AP
// adhoc - connect to another WiFi device
unsigned char wireless_mode = WIRELESS_MODE_INFRA;
//unsigned char wireless_mode = WIRELESS_MODE_ADHOC;

unsigned char ssid_len;
unsigned char security_passphrase_len;
//---------------------------------------------------------------------------


/****************************** Data Logger **********************************
*
*  Assign the data sensors to the Arduino analog pins
*
*/

//#define aref_voltage 3.3// tie 3.3V to ARef
#define aref_voltage 5.0  // default internal 5V to ARef

boolean sensor_status = false;

int photocellPin = 1;     // the cell and 10K pulldown are connected to a1
int photocellReading;     // the analog reading from the analog resistor divider

int temperaturePin = 0;   //the analog pin connected to the TMP36's Vout (sense) pin
int temperatureReading;   // the analog reading from the sensor

int buttonPin = 3;   // digital pin 3 int1; int0 pin D2 is used by YJ MRF24
// D2 MUST have internal or external 10K pullup resistor to either 5V0 or 3V3
int buttonReading;   // the digital reading from the sensor

int ledPin = 5;   //the digital pin for output to LED
int ledReading;   // the digital reading from the sensor

char inCMD[24] = "/write_json_TTLSL1.php?"; // php file to process the json POSTrequest
char tStamp[20];  // time stamp as char array
char timeStamp[20];  // time stamp as char array
char tF[10];      // sensor 1 data as char array
char light[10];   // sensor 2 data as char array
char button[5];   // sensor 3 data as char array
char led[5];      // sensor 4 data as char array

float tempF;      // float sensor 1 data
// int tF;        // int sensor 1 data if desired

/*************************** Sketch functions ************************************/

/*************************** mySensor ************************************/

// loop Function that assembles data to send to the server
void mySensor() {

  // Read sensors data

  char tS[20] = "0000-00-00 00:00:00"; // Arduino has no clock, need an RTC on Sensor board
  photocellReading = analogRead(photocellPin);
  temperatureReading = analogRead(temperaturePin);
  //buttonReading = digitalRead(buttonPin);
  buttonReading = sensor_status;
  ledReading = digitalRead(ledPin);

  // converting that reading to voltage, which is based off the reference voltage
  float voltage = temperatureReading * aref_voltage / 1024;
  // calculate temperature
  float temperatureC = (voltage - 0.5) * 100 ;  //converting from 10 mv per degree wit 500 mV offset
  //                                            //to degrees ((voltage - 500mV) times 100)
  //Serial.print(temperatureC); Serial.println(" degrees C");

  // convert C to Fahrenheit
  tempF = (temperatureC * 9 / 5) + 32;
  /*
    // Print the sensor values to the Serial Monitor
    Serial.println(F(">>> Read sensors "));
    Serial.print(tS); Serial.println(F(" TIMESTAMP"));
    Serial.print(tempF); Serial.println(F(" degrees F"));
    Serial.print(photocellReading); Serial.println(F(" Photocell"));
    Serial.print(buttonReading); Serial.println(F(" Button"));
    Serial.print(sensor_status); Serial.println(F(" sensor_status"));
    Serial.print(ledReading); Serial.println(F(" LED1"));
  */

  // convert int to char uses sprintf
  // convert float to char uses dtostrf
  //   note: <dtostrf(tempF, 6, 2, data);> will add a leading blank for the - sign
  //         which fails to load the data into MySQL table. Use <dtostrf(tempF, 0, 2, data);>
  //         which will left justify the data xyz.dd, and will load into MySQL

  dtostrf(tempF, 0, 1, tF);        // converts float to char[] array (data)
  //Serial.print(data1); Serial.println(F(" data1 char dtostrf degrees F"));

  // tF = (int)tempF; // if int value is desired rather than float
  // sprintf(v1, "%i", tF); // converts int to char[] array (data)
  //Serial.print(tF); Serial.println(F(" int degrees F"));

  sprintf(light, "%i", photocellReading); // converts int to char[] array (data)
  //Serial.print(v2); Serial.println(F(" data char int degrees Photocell"));

  sprintf(button, "%i", buttonReading); // converts int to char[] array (data)
  sprintf(led, "%i", ledReading); // converts int to char[] array (data)
  sprintf(tStamp, "%s", tS); // converts char[] array to char[] array (data)
  sprintf(timeStamp, "%s", tS); // default 0-0-0 0:0:0 if rtc is not available
  timestampNow();

}

void sensorOn() {
  sensor_status = true;
  buttonReading = sensor_status;
  //Serial.println(F("  >> Sensor Status: Activated >> "));

  // for testing, set the LED status to sensor_status
  //ledOn();

}

void sensorOff() {
  sensor_status = false;
  buttonReading = sensor_status;
  //Serial.println(F("  >> Sensor Status: Cleared >> "));
}

void ledOn() {
  digitalWrite(ledPin, HIGH);
  //Serial.println(F("  >> LED Status: On >> "));
}

void ledOff() {
  digitalWrite(ledPin, LOW);
  //Serial.println(F("  >> LED Status: Off >> "));
}

// Convert DateTime to char array
void timestampNow() {
  //DateTime now = rtc.now();
  //desired format: 0000-00-00 00:00:00
  int y = (now.year());
  int m = (now.month());
  int d = (now.day());
  int h = (now.hour());
  int n = (now.minute());
  int s = (now.second());
  sprintf(timeStamp, "%04d%s%02d%s%02d%s%02d%s%02d%s%02d", y, "-", m, "-", d, " ", h, ":", n, ":", s );
}

// print DateTime to Serial Monitor
void printNow() {
  //DateTime now = rtc.now();
  //char tS[20] = "0000-00-00 00:00:00";
  Serial.print(now.year(), DEC);
  Serial.print('-');
  Serial.print(now.month(), DEC);
  Serial.print('-');
  Serial.print(now.day(), DEC);
  Serial.print(' ');
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.println();
}

/*************************** sendMyPage function *********************************/

// This is our page serving function that generates web pages
boolean sendMyPage(char* URL) {

  // Check if the requested URL matches "/"
  if (strcmp(URL, "/") == 0) {
    WiServer.print("");

    WiServer.print(F(" >> GETrequest server_ip/json.......return JSON formatted data "));
    WiServer.print(F(" >> GETrequest server_ip/sensorOn...set sensor ON "));
    WiServer.print(F(" >> GETrequest server_ip/sensorOff..set sensor OFF "));
    WiServer.print(F(" >> GETrequest server_ip/ledOn......set led ON "));
    WiServer.print(F(" >> GETrequest server_ip/ledOff.....set led OFF "));

    WiServer.print("");
    // URL was recognized
    return true;
  }


  // Check if the requested URL matches "/json"
  if (strcmp(URL, "/json") == 0) {

   // Use WiServer's print and println functions to write out the page content
    WiServer.println("<html>");
    // for Arduino to Arduino over wifi, use <json> and </json> tags as delimiters for parsing
    // print ... on one contiguous line
    WiServer.print("<json>");

    // test example json data array
    //char json[] = "{\"val1\":\"12.34\",\"val2\":\"56\"}"; // example JSON
    //WiServer.print(json);

    // obtain the sensor values
    mySensor();

    Serial.println("");
    Serial.print("timeStamp: ");
    Serial.println(timeStamp);

    // place sensor data into JSON format
    WiServer.print("{\"tSensor\":\"");
    WiServer.print(timeStamp);
    WiServer.print("\",\"tempF\":\"");
    WiServer.print(tF);
    WiServer.print("\",\"light\":\"");
    WiServer.print(light);
    WiServer.print("\",\"button\":\"");
    WiServer.print(button);
    WiServer.print("\",\"led\":\"");
    WiServer.print(led);
    WiServer.print("\"}");
    WiServer.println("</json>");

    WiServer.println("</html>");

    // URL was recognized
    return true;
  }

  // Check if the requested URL matches "/sensorOn"
  if (strcmp(URL, "/sensorOn") == 0) {
    sensorOn(); // set sensor status; normally set by button push
    WiServer.print("<html>");
    WiServer.print(F(" >> GETrequest server_ip/sensorOn: Sensor set ON >> "));
    WiServer.print("</html>");
    // URL was recognized
    return true;
  }


  // Check if the requested URL matches "/sensorOff"
  if (strcmp(URL, "/sensorOff") == 0) {
    sensorOff(); // probably not good to clear status until some action is taken
    WiServer.print("("<html>");
    WiServer.print(F(" >> GETrequest server_ip/sensorOff: Sensor set OFF >> "));
    WiServer.print("</html>");
    // URL was recognized
    return true;
  }

  // Check if the requested URL matches "/ledOn"
  if (strcmp(URL, "/ledOn") == 0) {
    ledOn(); // set LED On by URL request
    WiServer.print("<html>");
    WiServer.print(F(" >> GETrequest server_ip/ledOn: LED set ON >> "));
    WiServer.print("</html>");
    // URL was recognized
    return true;
  }

  // Check if the requested URL matches "/ledOff"
  if (strcmp(URL, "/ledOff") == 0) {
    ledOff(); // probably not good to clear status until some action is taken
    WiServer.print("<html>");
    WiServer.print(F(" >> GETrequest server_ip/ledOff: LED set OFF >> "));
    WiServer.print("</html>");
    // URL was recognized
    return true;
  }

  WiServer.print("<html>");
  WiServer.print(F(" >> URL not found >> "));
  WiServer.print("</html>");

  // URL not found
  return false;
}

/*************************** setup function *********************************/

void setup() {

  // If you want to set the aref to something other than 5v
  //analogReference(EXTERNAL);  // enable EXTERNAL if using 3.3 AREF, ie, SD Card Shield and UNO
  analogReference(DEFAULT);     // DEFAULT for no external AREF, ie, SD Card Shield and YellowJacket

  // Enable Serial output
  Serial.begin(115200);

  Wire.begin();
  rtc.begin();
  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  // Attach interrupt INT1 to pin 3 held normally high through 10K resistor to 3V3. Button grounds pin 3.
  pinMode(buttonPin, INPUT_PULLUP);      // sets the interupt pin 3 as input with the internal pullup
  attachInterrupt(digitalPinToInterrupt(buttonPin), sensorOn, FALLING); // buttonPin senses immediate state
  pinMode(temperaturePin, INPUT);      // sets the analog pin 0 as input
  pinMode(photocellPin, INPUT);      // sets the analog pin 1 as input
  pinMode(ledPin, OUTPUT);      // sets the digital pin 5 as output

  delay(100); // wait for power to stabilize
  buttonReading = 0;
  sensorOff();
  ledOff();

  // /*
  // Print the timestamp
  now = rtc.now();
  timestampNow();
  Serial.println("");
  Serial.print("timeStamp: ");
  Serial.println(timeStamp);

  Serial.println("");
  Serial.println(">> SimpleJsonServer_5Sensors_V3 sketch ");
  Serial.print(">> WiServer.server_task initialized on ");
  Serial.print(myhost);
  Serial.print(", ip: ");
  Serial.println(myhost_ip);
  // */

  // Initialize WiServer and have it use the sendMyPage function to serve pages
  WiServer.init(sendMyPage);

  // Verbose=true >> ask WiServer to generate log messages to Serial Monitor
  WiServer.enableVerboseMode(true);

  Serial.println("");
  Serial.println(F(">> Listening for connections..."));

}

/*************************** loop function *********************************/

void loop() {

  now = rtc.now();

  // Run WiServer
  WiServer.server_task();

  delay(10);
}

/*************************** end loop function *********************************/
The YJ56 server “SimpleJsonServer_5Sensors_V3” sketch (above), transmits a JSON formatted data packet to the client
(e.g. {“tSensor”:”2016-03-26 14:55:29″,”tempF”:”79.1″,”light”:”281″,”button”:”1″,”led”:”1″} )
each time it receives a GetRequest “http://10.0.1.56/json”, either through wifi or ethernet.
On the Mac, mySQL requires a table to log that 5-sensor data. A new Arduino data base table, “TTLSL1” was added with 7 fields: 1) id, INT 11, unsigned, primary key, auto_increment; 2) time, TIMESTAMP, default CURRENT_TIMESTAMP (mySQL timestamp); 3) tSensor, VARCHAR 20 (YJ56 timestamp of sensor data); 4) tempF, VARCHAR 10; 5) light, VARCHAR 10; 6) button, INT 1; and, 7) led, INT 1. The field names match those in the JSON data packet.
Also on the Mac, a php file is required to send the JSON data to mySQL.
<?php

 // File "write_json_TTLSL1.php"
 // written as part of Arduino sketch "SimpleJsonWiClient_5Sensors_V3"
 // to insert five values into MySql table using POSTrequest
 // Prepare variables for database connection

 $dbhost = 'localhost:3306';
 $dbuser = "arduino";  // enter database username, I used "arduino" in step 2.2
 $dbpass = "arduinotest";  // enter database password, I used "arduinotest" in step 2.2
 $conn = mysql_connect($dbhost, $dbuser, $dbpass);

 if(! $conn ) {
   die('Could not connect: ' . mysql_error());
 }
 
 mysql_select_db(‘Arduino’);
 
 // For data submitted as JSON POSTrequest
        //read the json input stream as 'php://input' from client
 $jsondata = file_get_contents('php://input');
 // echo $jsondata;

        //convert json object to php associative array
        $data = json_decode($jsondata, true);
 // echo $data;
 
 // get the data value details
 $v1 = $data['tSensor'];
 $v2 = $data['tempF'];
 $v3 = $data['light'];
 $v4 = $data['button'];
 $v5 = $data['led'];

 // Prepare the SQL statement
 $sql = "INSERT INTO Arduino.TTLSL1 (tSensor,tempF,light,button,led) VALUES ('$v1', '$v2', '$v3', '$v4', '$v5')"; 
 //echo $sql;
 
 // Execute SQL statement
 $retval = mysql_query( $sql, $conn );

 if(! $retval ) {
   die('Could not enter data: ' . mysql_error());
 }

 echo "\nEntered 5 data values successfully into Table TTLSL1\n";

 mysql_close($conn);

?>
The YJ57 Client sketch, “SimpleJsonWiClient_5Sensors_V3”, sends a GetRequest to YJ56 server, obtains the HTML – JSON data packet, parses the JSON data fields, and transmits them to php on a Mac for storage in a mySQL data table.
/*  SimpleJsonWiClient_5Sensors_V3
 *
 *  March 17, 2016
 *
 * A simple sketch that uses MRF24 WiClient to read web page from MRF24 WiServer
 * The WiServer web page content includes a JSON formatted data string between
 * <json> json data </json> tags
 * Parse out the json data as a char array and send that to ArduinoJson to parse into
 * individual field names and values.
 *
 * Write the JSON data to Mac15 MySQL Arduino DB, table TTLSL1.
 * Sensors: time, wtempF (pin A0), light (pin A1), button (pin D3 INT1), and led (pin 5).
 *
 * Ref: ArduinoJson Library
 *
 * Compiled with WiShield_V3 library
 *
 * Uses apps-conf.h "#define APP_WISERVER"
 *
 *  Connecting Two YellowJackets for Data Transfer
 *
 *  Hardware:
 *    YJ Arduino board (YJ56) at ip=10.0.1.56
 *    YJ Arduino board (YJ57) at ip=10.0.1.57
 *    Local network gateway ip 10.0.1.1
 *
 *  Example: WiClient YJ57 requests web page from WiServer YJ56.
 *    a) Load sketch SimpleJsonServer_5Sensors_V3 into YJ56. Sketch uses
 *      “WiServer.init(sendMyPage);” to transmit web page with JSON content by Wifi.
 *    b) Load sketch SimpleJsonWiClient_5Sensors_V3 into YJ57. Sketch uses
 *      GetRequest to Host: “10.0.1.56”, URL=“/json“
 *    c) Result: WiServer web page is requested every 30 seconds by WiClient
 *    d) Returned request stream is parsed for <json> . . . </json> string
 *    e) <json> string is parsed by ArduinoJSON to extract individual field_name,field_data
 *    f) json data are transmitted to Mac php to MySQL table.
 *
 *  This sketch
 *
 *  loop function calls GETrequest every 30 seconds
 *    GETrequest sent to WiServer returns JSON data string within HTTP "data" array
 *
 *    extractJSON function extracts the JSON formatted string from the HTTP "data" array
 *
 *    extractJSON function calls jsonParser()
 *      jsonParser() function parses the field titles and data into individual variables
 *      (reads 5 sensor values as JSON formatted data within HTML stream from WiServer)
 *
 *    jsonParser() function calls  doJSON() function
 *      doJSON() function Serial.prints the parsed data elements
 *
 *    doJSON() function calls write_MySQL() funtion
 *
 *    write_MySQL() function assembles a POSTrequest to create a php request
 *      setContentType(1); is used for JSON POSTs
 *      POSTrequest sendMyRequest(request_ip, 80, myhost, inCMD, createBody);
 *      createBody calls createBody function
 *      createbody function uses WiServer.print to create the body for the POST
 *      setContentType(0); is reset for TEXT GETs
 *
 *    sends the php request to Apache
 *    the php request is submitted to mysql
 *    data elements are written to mysql table
 *
 *    jsonArray = {"tSensor":"2016-03-26 14:55:29","tempF":"79.1","light":"281","button":"1","led":"1"}
 *
 *  // A WiClient or Browser request that gets web page from server
 *  // or controls sensors on server YJ56
 *  // >> GETrequest getWebPage(server_ip, 80, myhost_ip, "/"); // return Commands Help webpage
 *  // >> GETrequest server_ip/json.......return JSON formatted data in HTTP stream
 *  // >> GETrequest server_ip/sensorOn...set sensor ON
 *  // >> GETrequest server_ip/sensorOff..set sensor OFF
 *  // >> GETrequest server_ip/ledOn......set led ON
 *  // >> GETrequest server_ip/ledOff.....set led OFF
 *
 */

#include <WiServerIO.h>
#include "SPI.h" // Arduino SPI
#include <ArduinoJson.h>


/****************************** Configure Wifi **********************************/

#define WIRELESS_MODE_INFRA 1
#define WIRELESS_MODE_ADHOC 2

unsigned char local_ip[] = {10, 0, 1, 57}; // IP address of WiShield
unsigned char gateway_ip[] = {10, 0, 1, 1}; // router or gateway IP address
unsigned char subnet_mask[] = {255, 255, 255, 0}; // subnet mask for the local network
const char ssid[] PROGMEM = {"Network"};   // max 32 bytes

unsigned char security_type = 3;  // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2

// WPA/WPA2 passphrase
const char security_passphrase[] PROGMEM = {"Network_Passcode"};  // max 64 characters

// WEP 128-bit keys
// sample HEX keys
const uint8_t wep_keys[] PROGMEM = {  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, // Key 0
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 1
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 2
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // Key 3
                                   };

// setup the wireless mode
// WIRELESS_MODE_INFRA - connect to AP
// WIRELESS_MODE_ADHOC - connect to another WiFi device
unsigned char wireless_mode = WIRELESS_MODE_INFRA;
unsigned char ssid_len;
unsigned char security_passphrase_len;

/****************************** End Configure Wifi **********************************/

/****************************** Define global variables **********************************/
//#define aref_voltage 3.3// we tie 3.3V to ARef and measure it with a multimeter!
#define aref_voltage 5.0  // default internal 5V to ARef
float tempF;
//int tF;

/****************************** Setup JSON **********************************/
//
// define the jsonArray that will hold the json data string for data transfer
// example: {"tSensor":"2016-03-26 14:55:29","tempF":"79.1","light":"281","button":"1","led":"1"}
// ArduinoJson will be used to parse this array to extract the data elements

const int js = 100;   // set the jsonArray max dimension size (adjust as needed for data string)
char jsonArray[js];   // adjust the size of this character array to hold incoming JSON data string
char jsonAcopy[js];   // copy of jsonArray[] sinse jsonArray gets clobbered
int jsonLength = 0;   // length of json string from extractJson function

// Required: define the json data from the server. This could be a struct.
const char id1[] = "tSensor";  // field names used in MySQL table
char cv1[20]; // holds the returned WiServer DateTime, which may be 0 if server has no clock.
const char id2[] = "tempF";
double v2;  // holds the returned val2
const char id3[] = "light";
int v3;
const char id4[] = "button";
int v4;
const char id5[] = "led";
int v5;

// create char arrays to hold the json int or float data elements as char
// char cv1[20];  // sensor 1 data as char array defined above
char cv2[10];  // sensor 2 data as char array
char cv3[5];  // sensor 3 data as char array
char cv4[5];  // sensor 4 data as char array
char cv5[5];  // sensor 5 data as char array

// The jsonArray is used for POSTing the data to php

/*************************** GETrequest function ****************************
 *  The GETrequest function is run every 30 seconds by call from loop
 */
uint8 server_ip[] = {10, 0, 1, 56}; // IP Address for WiServer YJ56
// Server for GETrequest for JSON formatted data
const char* myServer = "WiServer";
char* myhost_ip = "10.0.1.56";

// A request that gets web page from server
// >> GETrequest getWebPage(server_ip, 80, myhost_ip, "/"); // return Commands Help webpage
// >> GETrequest server_ip/json.......return JSON formatted data in HTTP stream
// >> GETrequest server_ip/sensorOn...set sensor ON
// >> GETrequest server_ip/sensorOff..set sensor OFF
// >> GETrequest server_ip/ledOn......set led ON
// >> GETrequest server_ip/ledOff.....set led OFF

GETrequest getWebPage(server_ip, 80, myhost_ip, "/json"); // request JSON formatted data

/*************************** end GETrequest setup ****************************/

/*************************** POSTrequest setup ****************************
 *  This POST defines the request for sending JSON data to MySQL database
 */
uint8_t request_ip[] = {10, 0, 1, 11}; // IP Address for requested server Mac
char* myhost = "Mac_localhost";
char inCMD[24] = "/write_json_TTLSL1.php?"; // php file to process the json POSTrequest

POSTrequest sendMyRequest(request_ip, 80, myhost, inCMD, createBody);

/*************************** end POSTrequest setup ****************************/
/*************************** POSTrequest createBody function ******************/

// POSTrequest setBodyFunc(bodyFunction body);
void createBody()
{
  WiServer.print (jsonAcopy); // just print the json formatted string
  
  //
  /* below is code to build the JSON string from the individual parsed values
  // place sensor data into JSON format using individual variable names and values
  WiServer.print("{\"tSensor\":\"");
  WiServer.print(cv1);
  WiServer.print("\",\"tempF\":\"");
  WiServer.print(cv2);
  WiServer.print("\",\"light\":\"");
  WiServer.print(cv3);
  WiServer.print("\",\"button\":\"");
  WiServer.print(cv4);
  WiServer.print("\",\"led\":\"");
  WiServer.print(cv5);
  WiServer.println("\"}");
  //
  */

}
/*************************** end createBody function *********************************/

/***************************** End Global variables **********************************/

/*************************** sketch setup() ****************************/
void setup() {

  // Enable Serial output
  Serial.begin(115200);
  // If you want to set the aref to something other than 5v
  // analogReference(EXTERNAL); // for 3.3V ARef
  analogReference(DEFAULT);

  /*************************** Setup Wifi  ****************************/

  Serial.println (" ");
  Serial.println (F("=== Initializing WiClient to display WiServer web page on Serial Monitor ==="));

  // Initialize WiServer (we'll pass NULL for the page serving function since we don't need to serve web pages)
  WiServer.init(NULL);

  // Ask WiServer to generate log messages (optional)
  WiServer.enableVerboseMode(true);

  // Have the printData function called when data is returned by the server
  getWebPage.setReturnFunc(printJsonData);
  // Have the createBody function called to POST data to MySQL
  sendMyRequest.setReturnFunc(printData);
  sendMyRequest.setBodyFunc(createBody);

  Serial.println (" ");
  Serial.println (F("=== WiClient is running... ==="));
  Serial.println (F(">>> Request response on 30 second intervals... "));
  Serial.println (" ");

}

/*************************** end sketch setup() ****************************/

/*************************** sketch loop() ****************************/

// Time (in millis) when the data should be retrieved
unsigned long updateTime = 0;
unsigned long waitTime = 1000UL * 60UL; // * UL unsigned long number format used to avoid overflow

void loop() {

  // Check if it's time to get an update
  if (millis() >= updateTime) {
    getWebPage.submit();
    // Get another update 30 seconds from now
    updateTime += waitTime;
  }

  // Run WiServer
  WiServer.server_task();

  delay(10);
}

/*************************** end sketch loop() ****************************/

/*************************** function routines ************************/

// Must extract JSON string from HTTP stream
//
/********************************* extractJSON ************************/
//
// Function that extracts JSON data from incoming data stream from the server

void extractJSON(char* data, int len) {

  // Note that the data stream is not null-terminated,
  // may be broken up into smaller packets, and
  // includes the HTTP header.
  // This code expects data stream to be short and in one array.
  // This could be a bad assumption, since the json array might be split

  /******* Parse out JSON section from data Stream ***************/

  // Use care with data array. It is the returned stream from the server.

  // Server needs to wrap the JSON data array within  . .  tags
  //   These tags are used to parse out the json data array from the incoming http data stream
  const char str1[] = "<json>";
  const char str2[] = "</json>";
  char* ptr1;
  char* ptr2;
  char* ptr3;
  int jsLength;

  //Serial.println(len);
  // make sure there is content in data array
  if (len > 0)  // if length of data array is > 0, then proceed
  {
    ptr1 = strstr (data, str1); // get index location of  string "<" within data array
    if (ptr1 != NULL)  // if substring is found, continue
    {
      ptr2 = strstr(ptr1, "{");       // move the pointer to the first brace
      //Serial.println(ptr2);         // print from brace to end of data array

      if (ptr2 != NULL)               // leading brace found, so continue
      {
        ptr3 = strstr(data, str2);    // move ptr3 to "<" in 
        if (ptr3 != NULL)             // "<" found, so now extract the json string { // calc chars between index2 and index3 jsLength = ptr3 - ptr2; Serial.println(""); Serial.print(F(" > JSON Length = ")); Serial.println(jsLength);
          if (jsLength < js) { // make sure // length is OK, continue memcpy(jsonArray, ptr2, jsLength); // copy json string to jsonArray memcpy(jsonAcopy, ptr2, jsLength); // copy jsonArray[jsLength] = '\0'; // for insurance, add NULL to end of jsonArray jsonAcopy[jsLength] = '\0'; // for insurance, add NULL to end of jsonAcopy //size_t sizeof_jA = strlen(jsonArray); // gives array char count jsonLength = jsLength; // copy string length to global value // Serial.print(F("jsonArray=")); Serial.println(jsonArray); // will print: {"val... xx"} // if extract is successful, report out //Serial.println (""); Serial.println (F(" > JSON Array was Extracted from GETrequest stream"));
            Serial.println ("");

            // Parse jsonArray with ArduinoJson
            //
            jsonParser();

          } else {
            //Serial.println ("");
            Serial.println (F(" *** Error extracting JSON Array from GETrequest stream"));
            Serial.println (F("  ** json length > jsonArray dimension"));
            Serial.println ("");
          }
          // while (ptr2 != NULL) {}; // hang here to test above code
        } // end if (ptr3 != NULL)
      } // end if (ptr2 != NULL)
    } else { // end if (ptr1 != NULL)
      Serial.println ("");
      Serial.println (F(" *** Error JSON Array Not Found in GETrequest stream"));
      Serial.println ("");
    }
  } // end if (len > 0)
} // end function
/***************************** End extractJSON function**********************/

/*************************** jsonParser function ****************************/
// ref: ArduinoJson Library
//
// jsonParser function
//
void jsonParser() {

  StaticJsonBuffer jsonBuffer; // limit size so as to not exceed available memory
  JsonObject& root = jsonBuffer.parseObject(jsonArray);
  // Serial.println(" *** root.printTo(Serial)");
  // root.printTo(Serial);
  // Serial.println(" *** end root.printTo(Serial)");
  // Test if parsing succeeds.
  if (!root.success()) {
    Serial.println(" *** ArduinoJson Parse failed");
    return;
  }
  // Fetch values.

  // place json data into char arrays

  // json variables defined as globals for use elsewhere in sketch
  const char* s1 = root[id1]; // tSensor datetime requires cast from char* to char[]
  sprintf(cv1, "%s", s1);  // tSensor datetime
  const char* s2 = root[id2]; // cast from char* to char[]
  sprintf(cv2, "%s", s2);  // tempF
  const char* s3 = root[id3]; // cast from char* to char[]
  sprintf(cv3, "%s", s3);  // light
  const char* s4 = root[id4]; // cast from char* to char[]
  sprintf(cv4, "%s", s4);  // button
  const char* s5 = root[id5]; // cast from char* to char[]
  sprintf(cv5, "%s", s5);  // led

  // place the json data into char, int, or float as desired

  // sprintf(cv1, "%s", s1);  // tSensor datetime already exists
  v2 = root[id2]; // tempF xx.xx
  v3 = root[id3]; // light int
  v4 = root[id4]; // button 0,1
  v5 = root[id5]; // led 0,1

  // Now use the json array data as desired
  doJSON();

}
//
/*************************** End jsonParser function ****************************/

/****************************** doJSON function ************************/
//
// Function that uses the JSON data

void doJSON() {

  // do something with the JSON data

  Serial.println (F(" > JSON Array was parsed for values"));

  // Print json int, float, or char values.
  Serial.print(F(" tSensor: ")); Serial.println(cv1);
  Serial.print(F("   tempF: ")); Serial.println(v2, 2);
  Serial.print(F("   light: ")); Serial.println(v3);
  Serial.print(F("  button: ")); Serial.println(v4);
  Serial.print(F("     led: ")); Serial.println(v5);

  /*
    // Print json as char values.
    Serial.print(F(" tSensor: ")); Serial.println(cv1);
    Serial.print(F("   tempF: ")); Serial.println(cv2);
    Serial.print(F("   light: ")); Serial.println(cv3);
    Serial.print(F("  button: ")); Serial.println(cv4);
    Serial.print(F("     led: ")); Serial.println(cv5);
  */

  write_MySQL(); // write the json data through Wifi to MySQL table via php

}

/****************************** end doJSON function ************************/


/****************************** write_MySQL function ************************/

void write_MySQL() {

  // Submit the POSTrequest to a php server at request_ip
  // Create an instance of POSTrequest with the JSON text in the body

  // choose text or json data; sets value in WiServerIO.h, .cpp
  setContentType(1);     // set JSON 0=Text, 1=JSON

  sendMyRequest.submit();

  Serial.println("");
  Serial.println(F(" ^^ POST JSON to MySQL submitted"));
  Serial.println("");

  // choose text or json data; sets value in WiServerIO.h, .cpp
  setContentType(0);     // reset TEXT 0=Text, 1=JSON

}
/****************************** end write_MySQL function ************************/




/****************************** printData ************************/
//
// Function that prints data from the server

void printJsonData(char* data, int len) {

  // Extract the JSON data as char array jsonArray from the server data stream
  // if the server data stream has content
  if (len > 0) {
    extractJSON(data, len); // extract JSON data from incoming data stream
  }

  // Print the data returned by the server
  // Note that the data is not null-terminated, may be broken up into smaller packets, and
  // includes the HTTP header.
  Serial.println ("");
  Serial.println (F("<< Print GET Json Data HTTP Stream from Server >>"));

  while (len-- > 0) {
    Serial.print(*(data++));
  }
  Serial.println (F("<< End Print GET Json Data HTTP Stream from Server >>")); // terminate messages with CR
  Serial.println ("");
}


void printData(char* data, int len) {

  // Print the data returned by the server
  // Note that the data is not null-terminated, may be broken up into smaller packets, and
  // includes the HTTP header.
  Serial.println ("");
  Serial.println (F("<< Print POSTrequest HTTP Stream from Apache Server >>"));

  while (len-- > 0) {
    Serial.print(*(data++));
  }
  Serial.println (F("<< End Print POSTrequest >>")); // terminate messages with CR
  Serial.println ("");
}

/********************************* End printData ************************/



Output from the YJ57 Client sketch displays on Serial Monitor as:
=== Initializing WiClient to display WiServer web page on Serial Monitor ===
 
=== WiClient is running... ===
>>> Request response on 30 second intervals... 
 
Connected to 10.0.1.56
TX 65 bytes
RX 0 bytes from 10.0.1.56
RX 136 bytes from 10.0.1.56

 > JSON Length = 85
 > JSON Array was Extracted from GETrequest stream

 > JSON Array was parsed for values
 tSensor: 2016-03-26 13:31:40
   tempF: 80.90
   light: 217
  button: 0
     led: 0

 ^^ POST JSON to MySQL submitted


<< Print GET Json Data HTTP Stream from Server >>
HTTP/1.0 200 OK

<html>
<json>{"tSensor":"2016-03-26 13:31:40","tempF":"80.9","light":"217","button":"0","led":"0"}</json>
<html>
<< End Print GET Json Data HTTP Stream from Server >>

Ended connection with 10.0.1.56

<< Print GET Json Data HTTP Stream from Server >>
<< End Print GET Json Data HTTP Stream from Server >>

Connected to Mac_localhost
TX 244 bytes
RX 0 bytes from Mac_localhost
RX 241 bytes from Mac_localhost

<< Print POSTrequest HTTP Stream from Apache Server >>
HTTP/1.1 200 OK
Date: Wed, 26 Mar 2016 17:34:21 GMT
Server: Apache/2.4.18 (Unix) PHP/5.5.34
X-Powered-By: PHP/5.5.34
Content-Length: 54
Connection: close
Content-Type: text/html


Entered 5 data values successfully into Table TTLSL1
<< End Print POSTrequest >>

Ended connection with Mac_localhost

<< Print POSTrequest HTTP Stream from Apache Server >>
<< End Print POSTrequest >>




(Mar 26, 2016)

Thursday, March 10, 2016

YellowJacket Arduino with WiShield Wifi (Part 12)

Processing Data Obtained Over Wifi from an Arduino Data  Server — 
In Part 11 of this series, temperature and photocell data were sent as JSON formatted data from a YellowJacket Arduino data server (YJ56) to a YellowJacket Arduino client (YJ57) using the local wifi network.
In this Part 12, sensor data will be sent via wifi to a local Apache/php server, and php will write the data to a local mySQL data base table.
There are three software packages that had to be installed and implemented on the Mac used for software development:
  • Apache (Ref: http://httpd.apache.org/download.cgi)
  • php (Ref: http://php.net/manual/en/install.php)
  • mySQL (Ref: http://dev.mysql.com/doc/refman/5.7/en/installing.html)
A fourth software, Sequel Pro (http://www.sequelpro.com), was installed to take advantage of its user-friendly GUI and ease of use. Each of these packages was installed individually rather than using MAMP or other bundled group of packages. MAMP was easily installed and made operational, but it installed in the user space rather than a system-wide install. After testing, MAMP was determined to conflict with Mac native installs of Apache, php, and MySQL. MAMP was then removed.
Each of these installs is fairly involved, decisions must be made about how to install them as either a system-wide or user install, file permissions must be set to work within the host computer OS security requirements, and network sharing must all be configured. This all can take minutes to hours to get working. There are several mySQL management packages that are a personal choice: Sequel Pro, MySqlWorkbench, or phpMyAdmin. Each takes some time to use proficiently, since data bases and data tables must be designed, and SQL queries are required to enter and manipulate data added to tables. Just move forward with the installs and soon they will all be working.
The installation and familiarity with Apache server, php, mySQL, and any of the mySQL management packages is outside the scope of this series. However, they will be required for the rest of this series, and must be installed on the user’s software development computer or network.
Other references (many on the web) that may help:
  • https://discussions.apple.com/docs/DOC-3083
  • http://osxdaily.com/2012/09/10/enable-php-apache-mac-os-x/
  • http://www.sequelpro.com/docs/Install_MySQL_on_your_Mac
  • http://jason.pureconcepts.net/2015/10/install-apache-php-mysql-mac-os-x-el-capitan/
  • Google for help with any problems or issues
For this series on YellowJacket Arduino Wifi, the following versions of software were used:
  • Terminal command >> Mac: me$ httpd -v
  • Server version: Apache/2.4.18 (Unix)
  • Safari browser command >> http://localhost/phpinfo.php
  • PHP Version 5.5.34
  • mySQL Version: 5.7.11
  • Sequel Pro Version: 1.1.2
  • Arduino IDE 1.6.5
  • Mac OSX 10.11 El Capitan and 10.10 Yosemite
Some notes:
On Mac, localhost php files are stored in /Library/WebServer/Documents.
The localhost php default port is 80.
To test that the web page server is operational: Safari> localhost <cr> should return: It works! The web page server works independently of mySQL being on or off.
Mac /Library/WebServer/Documents is the location for Safari> “http://localhost/phpinfo.php” which gives the system info on Apache, php, and mysql.
The file Test.php was saved into the /Library/WebServer/Documents directory.
<?php
     echo "Hello from PHP on Mac";
?>
Safari: localhost/Test.php displays the “Hello from PHP on Mac”.
php uses port 80, mysql uses port 3306 on Mac.
Alternatively, local webpages are run from the “Sites” location (e.g., file:///Users/me/Sites/…). Sites was probably chosen as a standard location for html web pages since the /Library/ and /usr/ locations are system file folders with system security.
Sequel Pro: To connect to MySQL on Mac: Sequel Pro: “Favorites: Mac_MySQL:Arduino”.
Connect as “Socket”, Name: localhost, Username: root, Password: <Mac mysql password>, Database: Arduino, Socket: optional.
The login fields can be saved under “Name: Arduino” as “Sequel Pro:File:Add to Favorites” which will save the login info for a specific mysql database.
Submitting data from php to MySQL
Ref: http://www.icreateproject.info/uncategorized/arduino-save-data-to-database/
This Ref explains how to send data from Arduino through the local network to a php file which then submits the data to mysql for adding to a mysql data base table.
As in Ref, the mysql data base “Arduino” was created on Mac mySQL using Sequel Pro. The “Arduino” data base has table “sensor” to hold the data submitted by php. The “sensor” table has three fields: 1) id (automatic sequential integer), 2) time (automatic timestamp entered by mySQL), and 3) value (user added).
The php file “/Library/WebServer/Documents/write_data.php” contains the code to accept the data from the Arduino over the network and submit it to mySQL.
<?php
 // ref: http://www.icreateproject.info/uncategorized/
  arduino-save-data-to-database/
 // Prepare variables for database connection
 $dbhost = 'localhost:3306';
 $dbuser = "arduino";  // enter database username "arduino"
 $dbpass = "arduinotest";  // enter database password "arduinotest" 
 $conn = mysql_connect($dbhost, $dbuser, $dbpass);

 if(! $conn ) {
   die('Could not connect: ' . mysql_error());
 }

 // Prepare the SQL statement
 $sql = "INSERT INTO Arduino.sensor (value) VALUES (".$_GET["value"].")";    

 // Execute SQL statement
 mysql_select_db(‘Arduino’);
 $retval = mysql_query( $sql, $conn );

 if(! $retval ) {
   die('Could not enter data: ' . mysql_error());
 }

 echo "Entered data successfully\n";

 mysql_close($conn);

?>
http://localhost/write_data.php?value=92 writes the value 92 into the mysql “Arduino:sensor” table with the return statement “Entered data successfully”.
Write Temperature and Light Data to mySQL
In MySQL, add a new table “sensor2” with 4 fields:
  • id (int 11, unsigned, key: Primary, extra: auto_increment)
  • time (TIMESTAMP, default: CURRENT_TIMESTAMP)
  • temperature (VARCHAR 10)
  • light (VARCHAR 10)
Add a new php file, write_2data.php to your php documents directory:
<?php

 // File "write_2data.php"
 // written as part of Arduino sketch "Send2Values_04"
 // to insert two values into MySql table
 // Prepare variables for database connection

 $dbhost = 'localhost:3306';
 $dbuser = "arduino";  // enter database username
 $dbpass = "arduinotest";  // enter database password
 $conn = mysql_connect($dbhost, $dbuser, $dbpass);

 if(! $conn ) {
   die('Could not connect: ' . mysql_error());
 }

 // Prepare the SQL statement
 $sql = "INSERT INTO Arduino.sensor2 (temperature,light) VALUES (".$_GET["temperature"]. ",".$_GET["light"]. ")"; 
 //echo $sql;
 
 // Execute SQL statement
 mysql_select_db(‘Arduino’);
 $retval = mysql_query( $sql, $conn );

 if(! $retval ) {
   die('Could not enter data: ' . mysql_error());
 }

 echo "\nEntered 2 data values successfully into Table sensor2\n";

 mysql_close($conn);

?>

The Arduino Send2Values_04.ino sketch collects data from temperature and photocell sensors and submits those through php file write_2data.php which inserts a data record with two values (temperature (f-6.2 tempF), light(int photocell)) into MySql database “Arduino” and table “sensor2”.
/***************************************************
 * Send2Values_04
 *
 * March 10, 2016
 * This sketch
 *    reads two sensors
 *    processes the data
 *    creates a php request
 *    sends the php request to Apache
 *    the php request is submitted to mysql
 *    data elements are written to mysql table
 *
 *    The sketch can format the data as int, float, or char array
 *    and submit the request as a char array.
 *
 *    The dtostrf function for converting a float to a char array
 *    will preface the array with a blank for the - sign.
 *    Using an F0.2 format will dispose of leading blanks which
 *    cause errors loadint the data into mysql.
 *
 * The Atmel Reference Manual addresses the issue
 * of left or right justification by using the (-) Width. 
 * That does not fix the problem of padding with leading
 * or trailing blanks in the output to php.
 * 
 ****************************************************/

#include 
#include "SPI.h"

/************************* WiFi Access Point *********************************/

#define WLAN_SSID       "Network"
#define WLAN_PASS       "Network_Passcode"

/*---------------------------------------------------------------------------
 *
 * WiServer set up
 *
 */

#define WIRELESS_MODE_INFRA  1
#define WIRELESS_MODE_ADHOC 2

// Wireless configuration parameters ----------------------------------------
unsigned char local_ip[] = {10, 0, 1, 56}; // IP address of WiShield
unsigned char gateway_ip[] = {10, 0, 1, 1}; // router or gateway IP address
unsigned char subnet_mask[] = {255, 255, 255, 0}; // subnet mask for the local network
const char ssid[] PROGMEM = {"Network"};   // max 32 bytes
char* myhost_ip = "10.0.1.56";

unsigned char security_type = 3;  // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2
// WPA/WPA2 passphrase
const char security_passphrase[] PROGMEM = {"Network_Passcode"};  // max 64 characters

// WEP 128-bit keys
// sample HEX keys
const uint8_t wep_keys[] PROGMEM = {  0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, // Key 0
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 1
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 2
                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // Key 3
                                   };

// setup the wireless mode
// infrastructure - connect to AP
// adhoc - connect to another WiFi device
unsigned char wireless_mode = WIRELESS_MODE_INFRA;

unsigned char ssid_len;
unsigned char security_passphrase_len;

//---------------------------------------------------------------------------

/****************************** Feeds ***************************************/
/* ---------------------------------------------------------------------------
*
*  A simple data logger for the Arduino analog pins
*
*/

//#define aref_voltage 3.3// we tie 3.3V to ARef and measure it with a multimeter!
#define aref_voltage 5.0  // default internal 5V to ARef

int photocellPin = 1;     // the cell and 10K pulldown are connected to a1
int photocellReading;     // the analog reading from the analog resistor divider

int temperaturePin = 0;   //the analog pin the TMP36's Vout (sense) pin is connected to
//the resolution is 10 mV / degree centigrade with a
//500 mV offset to allow for negative temperatures
int temperatureReading;   // the analog reading from the sensor

char inCMD[18] = "/write_2data.php?";
char requestCMD[80];

float tempF;
int tF;

/*---------------------------------------------------------------------------*/

/*************************** Sketch Code ************************************/

// Function that prints data returned from the server
void printData(char* data, int len) {
  // Note that the data is not null-terminated, may be broken up into smaller packets,
  // and includes the HTTP header.
  while (len-- > 0) {
    Serial.print(*(data++));
  }
}

// Function that assembles CMD to send to the server
void myCMD() {
  /* ----------------------- data logger ----------------------------------------*/
  // Read data

  photocellReading = analogRead(photocellPin);
  temperatureReading = analogRead(temperaturePin);
  // converting that reading to voltage, which is based off the reference voltage
  float voltage = temperatureReading * aref_voltage / 1024;
  // calculate temperature
  float temperatureC = (voltage - 0.5) * 100 ;  //converting from 10 mv per degree wit 500 mV offset
  //                                            //to degrees ((voltage - 500mV) times 100)
  //Serial.print(temperatureC); Serial.println(" degrees C");

  // convert C to Fahrenheit
  float temperatureF = (temperatureC * 9 / 5) + 32;

  tempF = temperatureF; // save alue in global tempF

  double dF = temperatureF;

  Serial.println("... ");
  // Serial.println(""); Serial.print("Light reading = "); Serial.println(photocellReading);
  // the raw analog reading

  // DEBUG the calculated temperatureF to the Serial Monitor
  Serial.print(temperatureF); Serial.println(F(" degrees F"));

  // conversion of int or float to char[] may not be needed, since "WiServer.print (tempF);"
  // displays the same number (83.49) as "dtostrf(temperatureF, 6, 2, data);"
  // which converts float to char[] array (data), f6.2 format but has leading space for - sign
  // do not know how WiServer.print formats to f6.2 by default.

  char data1[10];
  //sprintf will convert int to char, but not float to char which requires dtostrf

  dtostrf(dF, 0, 2, data1);        // converts float to char[] array (data)
  // f0.2 format forces left justify which deletes any leading spaces which cause problems in mysql
  Serial.print(data1); Serial.println(F(" data1 char dtostrf degrees F"));

  //tF = (int)temperatureF;
  //Serial.print(tF); Serial.println(F(" int degrees F"));

  char data2[10];
  sprintf(data2, "%i", photocellReading); // converts int to char[] array (data)
  Serial.print(data2); Serial.println(F(" data char int degrees Photocell"));

  /* -------------------End Data Logger ----------------------------------------*/

  // Send an int value to mysql works reliably
  //sprintf(requestCMD, "%s=%i", inCMD, tF); // appends int number; this works

  // Send an int value as a char[] to mysql works reliably
  //sprintf(requestCMD, "%s=%s", inCMD, data1); // appends char number.

  // Send a double value as a char[] to mysql works reliably with f0.1 format
  //sprintf(requestCMD, "%s=%s", inCMD, data); // appends dtostrf char double number.

  char tc[40];
  sprintf(tc, "%stemperature=%s", inCMD, data1); // appends dtostrf char double number.
  sprintf(requestCMD, "%s&light=%s", tc, data2); // appends dtostrf char double number.

  Serial.print(" requestCMD="); Serial.println(requestCMD);
}

uint8_t request_ip[] = {10, 0, 1, 11}; // IP Address for requested server Mac
char* myhost = "Mac_localhost";

// A request that gets a file from server at request_ip
GETrequest getMyRequest(request_ip, 80, myhost, requestCMD);

void setup() {
  Serial.begin(115200);
  delay(10);

  // Connect to WiFi access point.
  Serial.println();
  Serial.print(F("Init WiServer Client to External php-MySQL Server: "));
  Serial.println(myhost);

  // Initialize WiServer and have it use the sendMyPage function to serve pages
  // WiServer.init(sendMyPage);
  WiServer.init(NULL);

  // Ask WiServer to generate log messages (optional)
  WiServer.enableVerboseMode(true);

  // Have the processData function called when data is returned by the server
  getMyRequest.setReturnFunc(printData);

  Serial.println (">>> Send data on 30 sec intervals... ");
  Serial.println (" ");
}

// Time (in millis) when the data should be retrieved
unsigned long tempTime = millis();
unsigned long updateTime = tempTime;
unsigned long waitTime = 1000UL * 30UL; // * 30UL; seconds as long numbers

void loop() {

  tempTime = millis();

  // Check if it's time to get an update
  if (tempTime >= updateTime) {

    myCMD();

    Serial.println (" "); // space to display
    getMyRequest.submit();
    Serial.println (" "); // space to display
    // Get another update one hour from now
    updateTime += waitTime;
  }

  // Run WiServer
  WiServer.server_task();

  delay(100);
}


The Arduino Serial Monitor displayed:
Init WiServer Client to External php-MySQL Server: Mac_localhost
>>> Send data on 30 sec intervals... 
 
80.87 degrees F
80.87 data1 char dtostrf degrees F
327 data char int degrees Photocell
 requestCMD=/write_2data.php?temperature=80.87&light=327
 
 
Connected to Mac_localhost
TX 102 bytes
RX 0 bytes from Mac_localhost
RX 242 bytes from Mac_localhost
HTTP/1.1 200 OK
Date: Wed, 25 May 2016 16:25:54 GMT
Server: Apache/2.4.18 (Unix) PHP/5.5.34
X-Powered-By: PHP/5.5.34
Content-Length: 55
Connection: close
Content-Type: text/html


Entered 2 data values successfully into Table sensor2
Ended connection with Mac_localhost
... 
79.99 degrees F
79.99 data1 char dtostrf degrees F
306 data char int degrees Photocell
 requestCMD=/write_2data.php?temperature=79.99&light=306
 
 
Connected to Mac_localhost
TX 102 bytes
RX 0 bytes from Mac_localhost
RX 242 bytes from Mac_localhost
HTTP/1.1 200 OK
Date: Wed, 25 May 2016 16:26:24 GMT
Server: Apache/2.4.18 (Unix) PHP/5.5.34
X-Powered-By: PHP/5.5.34
Content-Length: 55
Connection: close
Content-Type: text/html


Entered 2 data values successfully into Table sensor2
Ended connection with Mac_localhost
... 

And the mySQL Arduino::sensor2 data table displayed:
id      time                    temperature     light
4 2016-05-25 12:26:54 79.99         332
5 2016-05-25 12:27:24 80.87         310
This exampled demonstrated:
  • data collection by Arduino
  • converting the int or float data to char array values
  • packaging the char array data as name/value pairs similar to JSON formatting
  • transmitting the data package via wifi to a Mac-based Apache/php server
  • php writing the data package into a mySQL data base table “sensor2”
The next example will expand on this work to:
  • collect the data on YJ56 as a data server
  • convert the int or float data to char array values
  • transmit the data package via wifi to YJ57 as JSON formatted data
  • YJ57 will transmit the data via wifi to a Mac-based Apache/php server
  • php write the data package into a mySQL data base table
(Mar 10, 2016)