// Author: Xiaohai Li (xhli@citytech.cuny.edu)
// Robotics Research Lab, NYC College of Technology/CUNY
// License: GPL V2.0
// 05/10/2018

// This Code is for Photon to send data, receive and parse JSON-formatted commands from IBM Cloud.
// The JSON commands are generated by a Node-RED app on the cloud.

// This #include statement was automatically added by the Particle IDE.
#include <MQTT.h>

// This #include statement was automatically added by the Particle IDE.
#include <ArduinoJson.h>   //Library for parsing JSON object

#define HOST_PORT  1883
#define MQTT_QoS   0

char *MQTT_HOST = "your_organization_here.messaging.internetofthings.ibmcloud.com"; 
char *MQTT_CLIENT = "d:your_organization_here:your_Device_Type_here:your_DeviceID_here"; 
char *MQTT_USERNAME = "use-token-auth";
char *MQTT_PASSWORD = "your_device_token_here";
char *EVENT_TOPIC = "iot-2/evt/testevent/fmt/json";
char *COMMAND_TOPIC = "iot-2/cmd/testcommand/fmt/json";

MQTT client( MQTT_HOST, HOST_PORT, callback );

char payload[80];    //set payload size for published data

StaticJsonBuffer<200>  CommdJSONBuffer;   // set buffer size for received commands

int dummydata = 0;
int LedIndicator_Publish = D7;
int LedIndicator_CommLow = D6;
int LedIndicator_CommMid = D5;
int LedIndicator_CommHigh = D4;

void BlinkLed(int LedPin, int BlinkTimes, int BlinkPeriod);

void setup() {
   
  pinMode(LedIndicator_Publish, OUTPUT);
  pinMode(LedIndicator_CommLow, OUTPUT);
  pinMode(LedIndicator_CommMid, OUTPUT);
  pinMode(LedIndicator_CommHigh, OUTPUT);
 
  BlinkLed(LedIndicator_CommLow, 1, 300);
  BlinkLed(LedIndicator_CommMid, 1, 300);
  BlinkLed(LedIndicator_CommHigh, 1, 300);

  RGB.control(true);

  Serial.begin( 9600 );
  Serial.println( "Connecting Photon to IBM Watson IoT Platform ...... " );

  while( !Serial.available() ) {
    Particle.process();
  }  

  client.connect( MQTT_CLIENT, MQTT_USERNAME, MQTT_PASSWORD  );

  if( client.isConnected() ) {
    Serial.println( "Now connected!" );
    Serial.println();
    Serial.println( "Start to publish data to the Cloud ......");
    Serial.println( "and receive commands from the Cloud ......");

    client.subscribe( COMMAND_TOPIC );    //Subcribe commands
  }
  else { Serial.println( "MQTT connection failed!" ); }
}

void loop() {
  dummydata ++;
  if ( dummydata > 100) dummydata = 0;
 
  sprintf(payload, "{ \"dataproperty\": \"%d\" }", dummydata);   
  client.publish( EVENT_TOPIC, const_cast<char*> (payload) ); 
 
  BlinkLed(LedIndicator_Publish, 1, 200);

  client.loop();

  Serial.println();
  Serial.print( "Data being sent to Cloud: " );
  Serial.println( dummydata );
  delay( 7000 );
}

void callback( char* topic, byte* payload, unsigned int length ) {
  RGB.color(255,10,255);
  delay(300); RGB.color(0,0,0);
  BlinkLed(LedIndicator_Publish, 3, 300);

  char p[length + 1];   
  memcpy( p, payload, length );
  p[length] = NULL;   
  String message( p );
 
  Serial.print( "Received JSON command from Cloud: " );
  Serial.println(p);
 
  JsonObject& CommdObj =  CommdJSONBuffer.parseObject(p);   //Parse received command
  //const char* SpeedCommd = CommdObj["fanspeed"];   // manually set the datatype
  //int TimeDuration = CommdObj["duration"];
  auto Command1Value = CommdObj.get<char *>("Command1");
  auto Command2Value = CommdObj.get<int>("Command2");
 
  Serial.println("Parsed command:");
  Serial.print("Command1: ");
  Serial.print(Command1Value);
  Serial.print(";  Command2:  ");
  Serial.println(Command2Value);
 
  if (!strcmp(Command1Value, "turnlow"))      //(!strcmp(p, "turnlow"))
  {
       // RGB.color(255,0,0);
      BlinkLed(LedIndicator_CommLow, 1, 300);
  }
 
  else if (!strcmp(Command1Value, "turnmid"))    //(!strcmp(p, "turnmid")) 
  {
      // RGB.color(0, 255, 0);
      BlinkLed(LedIndicator_CommMid, 1, 300);
  }
  else if (!strcmp(Command1Value, "turnhigh"))    //(!strcmp(p, "turnhigh") )         
  {
      // RGB.color(0, 0, 255);
      BlinkLed(LedIndicator_CommHigh, 1, 300);
  }
  else {
      // {RGB.color(255,0,255); delay(1000); RGB.color(0,0,0); }
      BlinkLed(LedIndicator_CommLow, 1, 100);
      BlinkLed(LedIndicator_CommMid, 1, 100);
      BlinkLed(LedIndicator_CommHigh, 1, 100);
  }
}

void BlinkLed(int LedPin, int BlinkTimes, int BlinkPeriod)
{
    for (int k=0; k<BlinkTimes; k++){
        digitalWrite(LedPin, HIGH);
        delay(BlinkPeriod);
        digitalWrite(LedPin, LOW);
        delay(BlinkPeriod);
    }
}