Feodor Hilitski
Published © CC BY

Remote Control and Sensing Solutions

We are developing the remote control and sensing platform to connect scientific laboratory equipment to the cloud.

AdvancedWork in progress2,958
Remote Control and Sensing Solutions

Things used in this project

Story

Read more

Schematics

Sample sensors set-up

Sample sensors set-up

Example of the I2C sensor connection to the Arduino UNO stacked with the WiFi shield

Code

Code example for sending temperature readings to the cloud

Arduino
The web platform should be configured to accept POST requests and record posted values of temperature and analog inputs (for a wide variety of analog sensors).
/*************************************************** 
  This is a Arduino Remote Temperature Logger with the ARDUINO Wi-fi Shiled 
  
         This Web-based logger sends A0-A3 voltage readings
         A4 and A5 are used in the I2C interface communication
         with the temperature and humidity sensor (ex. TMP102).
         Instead of sending voltages, A4 now is a floating number 
         containing the temperature (T) and A5 is a floating number
         with % relative humidity (%RH).
         Sensor TMP 102 connections (Breakout board to from SparkFun):
           -VCC = 3.3V
           -GND = GND
           -SDA = A4 (use inline 330 ohm resistor if your board is 5V)
           -SCL = A5 (use inline 330 ohm resistor if your board is 5V)
           
        Reading analog inputs A0-A3 usually results in noisy measurement. In            order to improve the precision (but not accuracy, which depends on the          sensor itself), several measurements are averaged for each analog input
 
        Server status LED on digital pin 5 indicates connection to the HTTP             server has been sucessfully established.

***************************************************

#include <WiFi.h>
#include <SPI.h>
#include <EEPROM.h>
#include <Wire.h>
#include <SparkFunHTU21D.h>

/*Network Settings*/
char ssid[] = "!!Yor network SSID"; // cannot be longer than 32 characters!
char pass[] = "********"; //boolean secure = false; for the unsecured networks
boolean secure = true;

/* Forming Request to Server*/
/* Configure this for your back-end API */
char WEBSITE[]="backend-api.yourserver.com";
char WEBPAGE[]="/data-script.php";

/* For access to the DB on www.fhilitski.com,
Each Arduino device has set credentials:
1. device ID
2. device S/N
3. password 
They are stored in the EEPROM memory
*/

char deviceID[2]; 
char deviceSN[9];
char password[9];
char request1[55]; //the first part of the request (device and password)
char request2[53]; //the second part of the request (output values);

/* Define variable to monitor status of the server connection */
boolean connectionStatus = false;
//initialize LED status for indication of successful link to server
int ledStatus = 0;
int serverLED = 5; 
//LED on this pin will serve as a status indicator for establishing server connection;

unsigned int responseTime = 30000; 
//(ms) timeout for server response
/*
Amount of time to wait (in milliseconds) for the data to be
received before closing the connection.  If you know the server
you're accessing is quick to respond, you can reduce this value.
*/
long updateTime = 60000;
//(ms) time between successive voltage updates sent to the cloud (1 min by default)
byte averagesNumber = 10; 
//number of readings to average per channel for the analogRead function



//WiFi client object
WiFiClient client;
int wifi_status = WL_IDLE_STATUS;

//ip address
uint32_t ip;

/**************************************************************************/

//for the I2C bus, define which sensor is connected. At this point, only two below are supported
boolean wire_HTU21D = true;
boolean wire_TMP102 = false;
// TMP 102 address for the I2C sensor
int tmp102Address = 0x48;

int voltage; 
//temp variable for a voltage, voltage is stored as direct output of analogRead
float temperature;
//temperature variable for reading from the TMP102

//for the I2C bus, define which sensor is connected. At this point, only two below are supported
boolean wire_HTU21D = true;
boolean wire_TMP102 = false;

boolean ref5v = false; //set to true if reverence voltage is 5V, false for 1.1V
//char voltage_string[4] = {'0','0','0','0'}; //temp string for conversion dtostrf
//char voltage_string[6] = {'0','.','0','0','0'}; //temp string for conversion dtostrf

void setup(void)
{
  Serial.begin(9600);
  Serial.println("Welcome to Remote control Web example");
  Serial.print("Data update time (ms): "); Serial.println(updateTime);
  /*Read EEPROM for S/N, password and device ID */
  deviceID[0] = char(EEPROM.read(1)); 
  deviceID[1] = '\0';
  //device ID is a first bit in the EEPROM
  //Serial.print("deviceID = " ); Serial.println(deviceID);
  
  for (i = 2; i<=9; i++)
  {
    deviceSN[i-1] = char(EEPROM.read(i));
  }
  deviceSN[8] = '\0';
  //Serial.print("S/N = "); Serial.println(deviceSN);
  //Serialnumber (S/N) is 8 bytes
  
  //make a database name;
  strcat(dbName,"db_");
  strcat(dbName,deviceID);
  strcat(dbName,deviceSN); 
  //Serial.print("Database: "); Serial.println(dbName);
    
  for (i = 10; i<=17; i++)
  {
    password[i-10] = char(EEPROM.read(i));
  }
  password[8] = '\0';
  //Serial.print("password = "); Serial.println(password);
  //Password (S/N) is 8 bytes
  
  //Create GET request #1!
  strcat(request1, WEBPAGE);
  strcat(request1, "?pwd=");
  strcat(request1, password);
  strcat(request1, "&device=");
  strcat(request1, deviceID);
  strcat(request1, "&num=");
  strcat(request1, deviceSN);
  //Serial.print("Request 1 string = "); Serial.println(request1);
  //temporary check of the request length.
  //Serial.print("Size of request 1 string = ");Serial.println(sizeof(request1));
    
  //initialize all outputs
  pinMode(serverLED, OUTPUT);
  //pin 4 controls SD card. At appears that SD card reader uses analog inputs
  //explicitly disable SD by setting digital pin 4 to LOW
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
  
  /*set-up reference for the analogRead function*/
  if (ref5v){
    analogReference(DEFAULT);
  }
  else
  {  
    analogReference(INTERNAL);//internal 1.1V reference
  }
  
  /* initialize I2C communication */
  Serial.print("Starting I2C: ");
  if (wire_HTU21D) 
    {
        myHumidity.begin();
        Serial.println("HTU21D");
    }
   if (wire_TMP102)
      {
        Wire.begin();
        Serial.println("TMP102");
    }
  
  //initialize server LED to off
  statusIndicator(serverLED, 0);
  
  //Serial.print("Starting I2C: ");
  Wire.begin();
  Serial.println("TMP102");
  
  /* Initialize the module */
  Serial.println("Initializing WiFi...");
  
  // check for the presence of the shield:
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not found!");
    // don't continue:
    while (true){};
  }
  
  String fv = WiFi.firmwareVersion();
  Serial.print("WiFi Sheild Firmware: "); Serial.println(fv);
  if ( fv != "1.1.0" ){
    Serial.println("Please upgrade the firmware!");
  }
  
  //Print WiFi MAC address:
  printMacAddress();
  //Optional SSID scan
  Serial.println("Scanning available networks...");
  listNetworks();
  
  while ( wifi_status != WL_CONNECTED){
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid); 
    if (secure)
    {
      // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
      wifi_status = WiFi.begin(ssid, pass);
    }
    else
    {
      wifi_status = WiFi.begin(ssid);
    }
    // wait 1 second for connection:
    delay(1000);
  } 
  Serial.println("Connected to Wifi Network!");
  printWifiStatus();
  Serial.println("Set-up complete. Entering logger mode...");
}

void loop(void){
 
  //temporary variable to convert analogRead value to string
 char tmp[4] = {'t','m','p'};
 /* leds is a string to store the 8 character long server response
    It is utilized after the request is sent to server
    There is an option to receive a response and use it to control the device
 */
 char leds[9] = {'n','o','t',' ','r','e','a','d'}; 
 
 
 //make sure request2 variable is empty (memory has been pre-allocated in the beginning)
 //this one will store the voltages as part of the request
 for (int i = 0; i < sizeof(request2); i++){
   request2[i] = '\0';
 }
 
 //preform one analog read. I noticed that the first analog after reset/power up read is always erroneous 
 //and results in the spike in temperature time-series. Performing single analogRead prior to the actual data-taking helps to get rid of this issue.
 analogRead(0);
 delay(10);
 
 //read voltages A0-A3, temperature and humidity (A4 + A5 I2C-bus HTU21SD sensor)
 for (byte i = 0; i < 6; i++){
   char output_buffer[2] = "0";
   char voltage_string[5] = {'0','0','0','0','\0'};
   if (i<4) {
     //initialize voltage
     voltage = 0;
     //get voltage
     for (byte j = 1; j <= averagesNumber; j++){
        voltage = voltage + analogRead(i);
       //voltage = getVoltage(i, ref5v);    
     }
     voltage = voltage / averagesNumber;
     //output for debug purposes
     Serial.print("Voltage on pin A");
     Serial.print(i);
     Serial.print(" = ");
     Serial.println(voltage);
     
     //add voltage and analog pin to the request
     strcat(request2, "&a");
     strcat(request2, itoa(i,output_buffer,10));
     strcat(request2, "=");
     strcat(request2, itoa(voltage,voltage_string,10)); 
   }
  if (i == 4)
   { 
     //reading the temperature
     //998 is the default reading which means temperature is not read properly
     //HTU21D returns 998 in case of error
     //the back-end software should know how to treat 998 reading properly, so id does not show up on plots
     temperature = 998;
     if (wire_HTU21D) 
     {
        temperature = myHumidity.readTemperature();
     }
     if (wire_TMP102)
     {
        temperature = getTemperature_TMP102(tmp102Address);
     }

     Serial.print("Temperature: ");
     Serial.print(temperature, 1);
     Serial.println("C");
     //add temperature to the request
     strcat(request2, "&a");
     strcat(request2, itoa(i,output_buffer,10));
     strcat(request2, "=");
     strcat(request2, dtostrf(temperature,4,2,voltage_string));  
   }  
   if (i == 5)
   { 
     //reading the humidity
     temperature = 998;
     if (wire_HTU21D) 
     {
        temperature = myHumidity.readHumidity();
     }
     Serial.print("Humidity: ");
     Serial.print(temperature, 1);
     Serial.println("%");
     //add temperature to the request
     strcat(request2, "&a");
     strcat(request2, itoa(i,output_buffer,10));
     strcat(request2, "=");
     strcat(request2, dtostrf(temperature,4,2,voltage_string));  
   }
 }
 
////Code for help with debugging request merge
//Serial.print("Request 2 string = "); Serial.println(request2);
////temporary check of the request length.
//Serial.print("Size of request 2 string = ");Serial.println(sizeof(request2));  
 /*
  Now all readings are acquired and it's time ti connect to the Web server
  and send the readings to the cloud
 */
  Serial.println("---------------------------");
  Serial.print("Connecting to: "); Serial.println(WEBSITE);
  connectionStatus = client.connect(WEBSITE, 80);
  delay(1000);
  Serial.println(connectionStatus);

  while (client.connected()){ 
    //update server led
    statusIndicator(serverLED, 1);
    //Serial.print("Connected to... "); Serial.println(ssid);
    //Serial.println("Sending request...");   
    //delay(100);    
    //Serial.println(request1);
    //Serial.println(request2);
    client.print("POST ");
    client.print(request1);
    client.print(request2);
    client.println(" HTTP/1.1");
    client.print("Host: "); client.println(WEBSITE);
    //client.println("User-Agent: ArduinoWiFi/1.1");
    client.println("Connection: close");
    //client.println("Content-Length: 8");
    client.println();
    /* The response has been sent. We now wait for server response. 
       Back-end API may communicate something back to the device
       For instance, acknowledgement of the receipt of the data
       Or a control sequence to change some of the parameters
    */
    unsigned long lastRead = millis(); //time of the last response reading
    boolean gotResponse = false;   //flag indicating the receipt of the response from server
    
    while ((millis() - lastRead < responseTime) && (!gotResponse)) {
      //wait for the response... waiting time is given by the responseTime
  
      Serial.print("Trying to read: ");
      Serial.print( millis() - lastRead); Serial.println(" ms");
      int ledCounter = 0;
      boolean readStatus = false;
     
      while (client.available()) {
          char c = client.read();
          Serial.print(c); 
          if (readStatus){
            addchar8(leds,c);
            
            ledCounter++;
          }   
          addchar3(tmp,c);
          tmp[3] = '\0';
         
          if ((strcmp(tmp,"LED") == 0) && (!readStatus)) {
               readStatus = true;
               //Serial.println("READING!!!");
          }
          if (ledCounter == 8){
              readStatus = 0;
              leds[8] = '\0';
              // STOP reading response
            }
          lastRead = millis();
          gotResponse = true; 
          
          //Serial.print("Current led array: "); Serial.println(leds);
          //Serial.print("Current tmp array: "); Serial.println(tmp);
       }  
    }   
    Serial.println();
    Serial.print("Got response: "); Serial.println(gotResponse);
    Serial.print("Got LEDS: ");  Serial.println(leds);
    //Serial.print("RAM Available: "); Serial.println(getFreeRam(), DEC);
    
    if (!gotResponse) {
      //if response was not received during the time alloted,
      //increase waiting time interval      
      //responseTime += 100;
      
      //disconnect and attempt to reconnect
      responseTime += 0;
      Serial.print("Did not receive response... Adjust waiting time (ms) to: ");
      Serial.println(responseTime);
      //client.stop();
      //statusIndicator(serverLED,client.connected()); 
    }
    else{
      ledStatus = parseResponse(leds, ledStatus, false);
      sendToShift(datapin, clockpin, latchpin, ledStatus, false);
    }
    
    /* Now, wait appropriate amount of time sepcified by the updateTime 
        This can be accomplished by delay(updateTime);
        but more interactive way of doing this is below...
    */    
    Serial.print("Waiting "); Serial.print(updateTime);
    Serial.print(" ms for next update.");
    unsigned long lastUpdateSent = millis(); //time of the last update
    unsigned long waitTime = millis()-lastUpdateSent; //time to wait until the new update
    unsigned long intermediateInterval = waitTime;  //intermediate time interval to indicate progress in waiting
    while (waitTime < updateTime) {
      //this will print a dot passing of each intermediate interval
      //thus indicating the program is still running
      if ((waitTime - intermediateInterval)>10000)
      {
        Serial.print(".");
        intermediateInterval = waitTime;
      }
      waitTime = millis() - lastUpdateSent;
    }
    Serial.println();
    
    connectionStatus = client.connected();
    Serial.print("Still connected? Answer: "); Serial.println(connectionStatus);
    if (!connectionStatus) {
      Serial.println();
      Serial.println("Disconnected from the host...");
      statusIndicator(serverLED,connectionStatus); 
    }

  }
  Serial.println("Out of the loop..");
  client.stop();
  //WiFi.disconnect();
}

/*
  TMP102 temperature measurement with the Wire library (I2C bus)
*/
float getTemperature_TMP102(int addr){
  Wire.requestFrom(addr,2); 
  byte MSB = Wire.read();
  //delay(1);
  byte LSB = Wire.read();
  //delay(1);
  //it's a 12bit int, using two's compliment for negative
  int TemperatureSum = ((MSB << 8) | LSB) >> 4; 
  float celsius = TemperatureSum*0.0625;
  return celsius;
}


/*adds char c to the end of the string*/
void addchar3 (char initial[3], char c){
  initial[0]=initial[1];
  initial[1] = initial[2];
  initial[2] = c;  
}

/*adds char c to the end of the string*/
char* addchar8 (char initial[8], char c){
  initial[0] = initial[1];
  initial[1] = initial[2];
  initial[2] = initial[3];
  initial[3] = initial[4];
  initial[4] = initial[5];
  initial[5] = initial[6];
  initial[6] = initial[7];
  initial[7] = c; 
  return initial;
}

/* 
  Updates connection status indicator LED on pin "pin"
*/
byte statusIndicator(int pin, boolean connection)
{
  if (connection) {
    digitalWrite(pin, HIGH);
    return 1;
  }
  else {
    digitalWrite(pin, LOW);
    return 0;
  }
}

/*
  Processes html server response
*/
byte parseResponse(char ledString[8] , byte currentLEDstatus, boolean debugMode){
  
//  Serial.print("Received string: "); Serial.println(ledString);
    
  byte i;
  byte value;
  byte LEDstatus = currentLEDstatus;
 
  for(i=0; i<=7; i++)
    {

      char c = ledString[i];
      
      value = atoi(&c);      
      if (debugMode){
        Serial.print(c);
        Serial.print(" becomes ");
        Serial.println(value);
      }
      bitWrite(LEDstatus, i, value);
    }
  if (debugMode){
//    Serial.print("New LED status: "); Serial.println(LEDstatus, BIN); //for debug purposes
  }
  currentLEDstatus = LEDstatus;   


  return currentLEDstatus;
}

/**************************************************************************/
/*!
    @brief  Begins an SSID scan and prints out all the visible networks
*/
/**************************************************************************/
void listNetworks() {
  // scan for nearby networks:
  Serial.println("** Scan Networks **");
  int numSsid = WiFi.scanNetworks();
  if (numSsid == -1)
  {
    Serial.println("Couldn't get a wifi connection! Reset to retry...");
    while (true);
  }

  // print the list of networks seen:
  Serial.print("Number of available networks: ");
  Serial.println(numSsid);

  // print the network number and name for each network found:
  for (int thisNet = 0; thisNet < numSsid; thisNet++) {
    Serial.print(thisNet);
    Serial.print(") ");
    Serial.print(WiFi.SSID(thisNet));
    Serial.print("\tSignal: ");
    Serial.print(WiFi.RSSI(thisNet));
    Serial.print(" dBm");
    Serial.print("\tEncryption: ");
    printEncryptionType(WiFi.encryptionType(thisNet));
  }
}

void printEncryptionType(int thisType) {
  // read the encryption type and print out the name:
  switch (thisType) {
    case ENC_TYPE_WEP:
      Serial.println("WEP");
      break;
    case ENC_TYPE_TKIP:
      Serial.println("WPA");
      break;
    case ENC_TYPE_CCMP:
      Serial.println("WPA2");
      break;
    case ENC_TYPE_NONE:
      Serial.println("None");
      break;
    case ENC_TYPE_AUTO:
      Serial.println("Auto");
      break;
  }
}

/**************************************************************************/
/*!
    @brief  Tries to read the 6-byte MAC address of the WiFi
*/
/**************************************************************************/
void printMacAddress() {
  // the MAC address of your Wifi shield
  byte mac[6];

  // print your MAC address:
  WiFi.macAddress(mac);
  Serial.print("MAC: ");
  Serial.print(mac[5], HEX);
  Serial.print(":");
  Serial.print(mac[4], HEX);
  Serial.print(":");
  Serial.print(mac[3], HEX);
  Serial.print(":");
  Serial.print(mac[2], HEX);
  Serial.print(":");
  Serial.print(mac[1], HEX);
  Serial.print(":");
  Serial.println(mac[0], HEX);
}

/**************************************************************************/
/*!
    @brief  Prints SSID, local IP and signal strength (RSSI) of the connected network
*/
/**************************************************************************/
void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

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

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

Credits

Feodor Hilitski

Feodor Hilitski

2 projects • 3 followers
Physics PhD Student at Brandeis University Studying biophysics and building connected devices for research labs

Comments