We started assembling and flashing the board following Intel instructions at https://software.intel.com/en-us/get-started-edison-windows
As the idea is to get feeds from the rotary grower and be able to change settings such as drum rotation and grow light time on/off periods we have to run MQTT broker and client.
At this stage, the broker will be only accessible on the local network but later I will set up a DNS server so we can access it from everywhere.
Building and Running Mosquitto* MQTT on the Intel Edison board
There are some tips on how to install it on the link below; however, the current version is mosquitto-1.4.12.tar.gz
To have the sketch running from boot and initialize the mosquitto broker I used the script below
#File: /etc/init.d# vi automateSketch.sh exec /sketch/./sketch.elf foo bar & exec mosquitto -d
Make the script executable by changing the permissions with chmod
root@edison:/etc/init.d# chmod +x /etc/init.d/automateSketch.sh
root@edison:/etc/init.d# chmod +x automateSketch.sh
To have the script executed every time Linux boots
root@edison:/etc/init.d# update-rc.d automateSketch.sh defaults
Below are the library files I used to have the MQTT running. I got those files from https://software.intel.com/en-us/blogs/2015/04/06/using-edison-securely-connect-iot-sensor-to-the-internet-with-mqtt
// File: MQTTClient.cpp
#include "MQTTClient.h"
#include <fcntl.h>
/*======================================================================
Constructor/Destructor
========================================================================*/
MQTTClient::MQTTClient()
{
}
MQTTClient::~MQTTClient()
{
close();
}
void MQTTClient::close()
{
if (spipe) {
fclose(spipe);
}
}
/*========================================================================
Initialization. Store variables to be used for subscribe/publish calls
==========================================================================*/
void MQTTClient::begin(char *broker, int port, security_mode smode,
char* cafile, char *user, char *psk)
{
strcpy(mqtt_broker, broker);
serverPort = port;
mode = smode;
if (mode == SSL) {
strcpy(certificate_file, cafile);
}
else if (mode == PSK) {
strcpy(psk_identity, user);
strcpy(psk_password, psk);
}
Serial.println("MQTTClient initialized");
Serial.print("Broker: "); Serial.println(mqtt_broker);
Serial.print("Port: "); Serial.println(serverPort);
Serial.print("Mode: "); Serial.println(mode);
}
/*=======================================================================
Subscribe to a topic, (*callback) is a function to be called when client
receive a message
=========================================================================*/
boolean MQTTClient::subscribe(char* topic,
void (*callback)(char* topic, char* message))
{
char cmdString[256];
if (mqtt_broker == NULL) {
return false;
}
if (topic == NULL) {
return false;
}
callback_function = callback;
switch(mode) {
case OPEN:
sprintf(cmdString,
"mosquitto_sub -h %s -p %d -t %s -v",
mqtt_broker, serverPort, topic);
break;
case SSL:
sprintf(cmdString,
"mosquitto_sub -h %s -p %d -t %s -v --cafile %s",
mqtt_broker, serverPort, topic, certificate_file);
break;
case PSK:
sprintf(cmdString,
"mosquitto_sub -h %s -p %d -t %s -v --psk-identity %s --psk %s",
mqtt_broker, serverPort, topic, psk_identity, psk_password);
break;
default:
break;
}
if ((spipe = (FILE*)popen(cmdString, "r")) != NULL) {
// we need to set the pipe read mode to non-blocking
int fd = fileno(spipe);
int flags = fcntl(fd, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
strcpy(topicString, topic);
return true;
}
else {
return false;
}
}
/*====================================================================
Check if there is data in the pipe,
if true, parse topic and message and execute callback function
return if pipe is empty
======================================================================*/
boolean MQTTClient::loop()
{
if (fgets(dataBuffer, sizeof(dataBuffer), spipe)) {
parseDataBuffer();
callback_function(topic, message);
}
}
/*====================================================================
Publish a message on the given topic
======================================================================*/
boolean MQTTClient::publish(char *topic, char *message)
{
FILE* ppipe;
char cmdString[256];
boolean retval = false;
if (this->mqtt_broker == NULL) {
return false;
}
if (topic == NULL) {
return false;
}
switch (this->mode) {
case OPEN:
sprintf(cmdString,
"mosquitto_pub -h %s -p %d -t %s -m \"%s\" %s",
mqtt_broker, serverPort, topic, message, retain_flag?"-r":"");
break;
case SSL:
sprintf(cmdString,
"mosquitto_pub -h %s -p %d --cafile %s -t %s -m \"%s\" %s",
mqtt_broker, serverPort, certificate_file,
topic, message, retain_flag?"-r":"");
break;
case PSK:
sprintf(cmdString,
"mosquitto_pub -h %s -p %d --psk-identity %s --psk %s -t %s -m \"%s\" %s",
mqtt_broker, serverPort, psk_identity, psk_password,
topic, message, retain_flag?"-r":"");
break;
}
if (!(ppipe = (FILE *)popen(cmdString, "w"))) {
retval = false;
}
if (fputs(cmdString, ppipe) != EOF) {
retval = true;
}
else {
retval = false;
}
fclose(ppipe);
return retval;
}
/*======================================================================
Parse data in the data buffer to topic and message buffer
delimiter is the first space
if there is only one recognizable string, it is assumed a message
string and topic is set to NULL
========================================================================*/
void MQTTClient::parseDataBuffer()
{
topic = dataBuffer;
message = dataBuffer;
while((*message) != 0) {
if ((*message) == 0x20) {
// replace the first space with the NULL character
(*message) = 0;
message++;
break;
}
else {
message++;
}
}
if (strlen(message) == 0) {
topic = NULL;
message = dataBuffer;
}
}
// File: MQTTClient.h
/*
  Minimalist MQTT Client using mosquitto_sub and mosquitto_pub
  Assume Mosquitto MQTT server already installed and mosquitto_pub/sub 
  are in search path
*/
#ifndef __MQTTClient_H__
#define __MQTTClient_H__
#include <Arduino.h>
#include <stdio.h>
enum security_mode {OPEN = 0, SSL = 1, PSK = 2};
class MQTTClient {
  public:
    MQTTClient();
    ~MQTTClient();
    void    begin(char * broker, int port, security_mode mode, 
                  char* certificate_file, char *psk_identity, char *psk);
    boolean publish(char *topic, char *message);
    boolean subscribe(char* topic, void (*callback)(char *topic, char* message));
    boolean loop();
    boolean available();
    void    close();
  private:
    void           parseDataBuffer();
    FILE*          spipe;
    char           mqtt_broker[32];
    security_mode  mode;
    char           topicString[64];
    char           certificate_file[64];
    char           psk_identity[32];
    char           psk_password[32];
    int            serverPort;
    char           *topic;
    char           *message;
    boolean         retain_flag;
    void           (*callback_function)(char* topic, char* message);
    char           dataBuffer[256];
};
#endif
Main Sketch
This sketch is basically running only the basic functions but I will keep updating as I implement more sensors and features.
//Rotary Growing System
// MQTT
#include <stdio.h>
#include <Arduino.h>
#include "MQTTClient.h"
#define SECURE_MODE     0
// 0 = No security
// 1 = SSL security
// 2 = TLS-PSK security
MQTTClient     mqttClient;
unsigned long  mqttPubInterval_1 = 1000;  // interval for publishing critical values
unsigned long  mqttPubInterval_2 = 5000;  // interval for publishing non-critical values
unsigned long  previousMqttMillis = 0;        
char           fmtString[256];            // utility string
char           topicString[64];           // topic for publishing
char           msgString[14];             // message
bool           autoManual = 1;            // mode initially set to automatic
unsigned long  currentMillis;
//Stepper Motor
int             drumRPH = 5;                // default drum rotation set at 5 Rotation per Hour
const int       stepperPin = 3;             // stepper pulse pin
const int       stepperENA = 4;             // stepper enable pin
int             stepperInterval;            // interval at which to pulse (milliseconds) calculated  dividing 2400 / required drum RPH
unsigned long   previousStepperMillis = 0;  // for stepper motor pulse
bool            remoteDrum = 0;             // value received from MQTT pub to set drum ON or OFF remotely, default OFF "0"
//Grow Light
const int   growLight = 5;                // growLight pin
int         set_on = 8;                   // default on time
int         set_off = 20;                 // default off time
char        date[100];                    // date display format
bool        remoteGrowLight = HIGH;       // value received from MQTT pub to set light ON or OFF remotely, default OFF "HIGH"
//Water Pump
const int     waterPump = 6;                  // water pump relay pin
unsigned long pumpInterval = 1800000;         // interval pump will switch on (every 30 min)
unsigned long pumpOnTime = 10000;             // default time pump stays on every cycle (10 seconds)
unsigned long previousPumpMillis = 0;         // for water pump timer
bool          remoteWaterPump = HIGH;         // value received from MQTT pub to set water pump ON or OFF remotely, default OFF "HIGH"
//Temperature Sensor
const int       pinTempSen = A0;            // pin of temperature sensor
float           temp;
//Light Sensor
const int       pinLightSen = A3;             // pin of light sensor
int             lightSensor = 0;
int             lux;
//LCD DISPLAY
#include <rgb_lcd.h>
#include <Wire.h>
rgb_lcd lcd;
int           screen = 0;
int           screenMax = 5;
bool          screenChanged = true;              // initially we have a new screen,  by definition
unsigned long previousLCDMillis = 0;             // for LCD screen update
unsigned long lcdInterval = 4000;                // LCD change interval
char          timenow[50];
// defines of the screens to show
#define TEMPERATURE 0
#define LUX         1
#define RPH         2
#define TIME        3
#define HUMIDITY    4
#define PH          5
void setup()
{
  pinMode(growLight, OUTPUT);         // light pin
  digitalWrite (growLight, HIGH);     // relay switches off at HIGH            
  pinMode(waterPump, OUTPUT);         // waterPump pin
  digitalWrite (waterPump, HIGH);     // relay switches off at HIGH
  pinMode(stepperENA, OUTPUT);        // stepper Enable pin
  digitalWrite (stepperENA, LOW);     // stepper Enable OFF
  pinMode(stepperPin, OUTPUT);        // stepper motor pulse pin  
  
  Serial.begin(9600);                 //initialize serial communications at 9600 bps
  Serial.println ("\n Initialising Rotary Growing System");
  
  lcd.begin(16, 2);                   // set up the LCD's number of columns and rows
  lcd.clear();
  lcd.setCursor(0, 0);                // set the cursor to column 0, line 0
  lcd.print("Initialising...");
  delay(2000);
  lcd.clear();
  lcd.setCursor(1, 0);               // set the cursor to column 1, line 0
  lcd.print("Rotary Growing");
  lcd.setCursor(5, 1);               // set the cursor to column 5, line 1
  lcd.print("System");
  delay(3000);
  // initializing MQTTClient
  #if ( SECURE_MODE == 0 )
    Serial.println("No security");
    mqttClient.begin("localhost", 1883, OPEN, NULL, NULL, NULL);
  #elif ( SECURE_MODE == 1 )
    Serial.println("SSL security");
    mqttClient.begin("localhost", 1994, SSL, "/home/mosquitto/certs/ca.crt", NULL, NULL);
  #elif ( SECURE_MODE == 2 )
   Serial.println("TLS-PSK security");
   mqttClient.begin("localhost", 1995, PSK, NULL, "user", "deadbeef");
  #endif
  // subscribe to all topics published under edison
  mqttClient.subscribe("edison/#", mqttCallback);
  mqttClient.publish("edison/sysMsg", "Rotary Growing System Booted");
  mqttClient.publish("edison/autoManual", "A");      // publish default Mode to sincronise remote values
  sprintf(msgString, "%d", drumRPH);                 // read default drumRPH value
  mqttClient.publish("edison/drumRPH", msgString);   // publish default drumRPH to sincronise remote values
  sprintf(msgString, "%d", set_on);                  // read default set_on value
  mqttClient.publish("edison/set_on", msgString);    // publish default light set_on time to sincronise remote values
  sprintf(msgString, "%d", set_off);                 // read default set_off value
  mqttClient.publish("edison/set_on", msgString);    // publish default light set_off time to sincronise remote values
}
// MQTT Callback function
void mqttCallback(char* topic, char* message)
{
  sprintf(fmtString, "mqttCallback(), topic: %s, message: %s", topic, message);
  Serial.print(fmtString);
  // Setting Drum RPH
  if (strcmp(topic, "edison/drumRPH") == 0) {     //check the topic then execute command as appropriate
   drumRPH = atoi(message);
  }
  //Seting grow light time ON
  if (strcmp(topic, "edison/set_on") == 0) {     //check the topic then execute command as appropriate
    set_on = atoi(message);
  }
    //Seting grow light time OFF
  if (strcmp(topic, "edison/set_off") == 0) {     //check the topic then execute command as appropriate
    set_off = atoi(message);
  }
  //Set Auto or Manual Mode
  if (strcmp(topic, "edison/autoManual") == 0) {  //check the topic then execute command as appropriate
    if (message[0] == 'A') {                      //set mode to 1 if Automatic
      autoManual = 1;
    }
    else {
      autoManual = 0;                                   //set mode to 0 if Manual
    }
  }
  // Switching Drum ON / OFF
  if (strcmp(topic, "edison/remoteDrum") == 0) {
    // then execute command as appropriate
    if (strncmp(message, "ON", 2) == 0) {
      remoteDrum = 1;
    }
    else {
      remoteDrum = 0;
    }
  }
  
    
  //Grow Light remote control
  if (strcmp(topic, "edison/remoteGrowLight") == 0) {
    // then execute command as appropriate
    if (strncmp(message, "ON", 2) == 0) {
      remoteGrowLight = LOW;
    }
    else {
      remoteGrowLight = HIGH;
    }
  }
  //Water Pump remote control
  if (strcmp(topic, "edison/remoteWaterPump") == 0) {
      // then execute command as appropriate
    if (strncmp(message, "ON", 2) == 0) {
      remoteWaterPump = LOW;
    }
    else {
      remoteWaterPump = HIGH;
    }
  }
  
}
void loop()
{
  mqttClient.loop();            // check for any new message from mqtt_sub
  currentMillis = millis();
  stepperMotor();  
  lightTimer();
  waterPumpTimer();
  grooveTempSen();
  grooveLightSen();
  grooveLCD();
  edisonMQTT();
}
//========================================================================================================================================
void stepperMotor()
{
  // check to see if it's time to pulse; that is, if the
  // difference between the current time and last pulse time
  // is bigger than the interval at which you want to pulse
  
  stepperInterval = 2400/drumRPH;
  bool pulseState;                 // pulseState used to set the stepperPin
  
  if (autoManual == 1 || (autoManual == 0 && remoteDrum == 1)){
        digitalWrite(stepperENA, LOW);
    if (currentMillis - previousStepperMillis >= stepperInterval) {
      previousStepperMillis = currentMillis;       // save the last Pulse time
  
      // if the Pulse is off turn it on and vice-versa:
      if (pulseState == LOW) {
        pulseState = HIGH;
      } else {
        pulseState = LOW;
      }
      // set the stepperPin with the pulseState of the variable:
      digitalWrite(stepperPin, pulseState);
    }
  }
  else {
    pulseState = LOW;
    digitalWrite(stepperENA, HIGH);
  }
}
//========================================================================================================================================
void lightTimer() {
  time_t t = time(NULL);
  struct tm tm = *localtime(&t);
  sprintf(date, "%02d/%02d/%04d %02d:%02d:%02d", tm.tm_mday, tm.tm_mon + 1, tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec);
  sprintf(timenow, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
  Serial.println(timenow);
  //if(set_on == set_off){
  //   digitalWrite(growLight, HIGH);            //HIGH sets light relay off
  //   Serial.println("Same On and OFF hours. Set at least 1h difference");
  //}
  //
  //if(set_on >23){
  //   digitalWrite(growLight, HIGH);            //HIGH sets light relay off
  //   Serial.println("Invalid hour value, choose from 0 until 23");
  //}
  //
  //if(set_off >23){
  //   digitalWrite(growLight, HIGH);           //HIGH sets light relay off
  //   Serial.println("Invalid hour value, choose from 0 until 23");
  //}
  if (autoManual == 1) {
    if (tm.tm_hour >= set_on && tm.tm_hour < set_off)    //Start timer
    {
      digitalWrite(growLight, LOW);       //LOW sets light relay on
    }
    else {
      digitalWrite(growLight, HIGH);      //HIGH sets light relay off
    }
  }
  else {
    Serial.println("Manual Mode Selected");
    digitalWrite(growLight, remoteGrowLight);
  }
}
//========================================================================================================================================
void waterPumpTimer() {
  // check to see if it's time to switch the water pump; if the
  // difference between the current time and last time the pump was on
  // is bigger than the interval set on pumpInterval
  // than switch it on for the time set on pumpOnTime
  
  if (autoManual == 1) {
    if (currentMillis - previousPumpMillis >= pumpInterval) {
      previousPumpMillis = currentMillis;       // save the last pump interval on time
      unsigned long previousPumpOnMillis;
      if (currentMillis - previousPumpOnMillis >= pumpOnTime) {
      previousPumpOnMillis = currentMillis;       // save the last pump time on time
      digitalWrite(waterPump, LOW);               // turn water pump ON
      }
      else {
      digitalWrite(waterPump, HIGH);              // turn water pump OFF
      }
    }
    else {
      digitalWrite(waterPump, HIGH);              // turn water pump OFF
    }
  }
  else {
    digitalWrite(waterPump, remoteWaterPump);
  }
}
//========================================================================================================================================
void grooveTempSen()                                    // grove temperature sensor
{
  int B = 3975;                                        // B value of the thermistor
  float resistance;
  int val = analogRead(pinTempSen);                    // get analog value
  resistance = (float)(1023 - val) * 10000 / val;      // get resistance
  temp = 1 / (log(resistance / 10000) / B + 1 / 298.15) - 273.15;  // calc temperature
  //    Serial.print("Temp: ");
  //    Serial.print(temp);
  //    Serial.println(" ºC");
}
//========================================================================================================================================
void grooveLightSen()                                  // grove light sensor
{
  lightSensor = analogRead(pinLightSen);
  lux = lightSensor;
  //    Serial.print("Light: ");
  //    Serial.print(lux);
  //    Serial.println(" LUX");
}
//========================================================================================================================================
void grooveLCD() {                                    // groove LCD RGB display
  unsigned long currentLCDMillis = millis();
  char number[4];
  { if (currentLCDMillis - previousLCDMillis > lcdInterval)             // save the last time you changed the display
    {
      previousLCDMillis = currentLCDMillis;
      screen++;
      if (screen > screenMax) screen = 0;  // all screens done? => start over
      screenChanged = true;
    }
    // debug Serial.println(screen);
    // DISPLAY CURRENT SCREEN
    if (screenChanged)   // only update the screen if the screen is changed.
    {
      screenChanged = false; // reset for next iteration
      switch (screen)
      {
        case TEMPERATURE:
          //print temperature
          lcd.clear();
          lcd.print("Temperature");
          lcd.setCursor(0, 1);
          sprintf(number, "%02.01f", temp);
          lcd.setCursor(3, 1);
          lcd.print(number);
          lcd.print(" C");
          break;
        case LUX:
          //print light
          lcd.clear();
          lcd.print("Light");
          lcd.setCursor(0, 1);
          sprintf(number, "%d", lux);
          lcd.setCursor(3, 1);
          lcd.print(number);
          lcd.print(" LUX");
          break;
        case RPH:
          //print RPM       // drum rotation per hour
          lcd.clear();
          lcd.print("Drum Speed");
          lcd.setCursor(0, 1);
          sprintf(number, "%d", (drumRPH));
          lcd.setCursor(3, 1);
          lcd.print(number);
          lcd.print(" RPH");
          break;
        case TIME:
          //print TIME
          lcd.clear();
          lcd.print(timenow);
          lcd.setCursor(0, 1);
          lcd.print("GROW LIGHT: ");
          lcd.setCursor(13, 1);
          if (digitalRead (growLight) == LOW) {
            lcd.print("ON");
          }
          else {
            lcd.print("OFF");
          }
          break;
        case HUMIDITY:
          //print humidity
          lcd.clear();
          lcd.print("Humidity");
          lcd.setCursor(0, 1);
          //sprintf(number,"%d",humid);
          lcd.setCursor(3, 1);
          lcd.print(38);
          lcd.print(" %");
          break;
        case PH:
          //print PH
          lcd.clear();
          lcd.print("PH");
          lcd.setCursor(0, 1);
          //sprintf(number,"%02.01f",ph);
          lcd.setCursor(3, 1);
          lcd.print(6.5);
        default:
          // cannot happen -> showError() ?
          break;
      }
    }
  }
}
//========================================================================================================================================
void edisonMQTT() {
  
  //critical information publishes immediately
  if (currentMillis - previousMqttMillis >= mqttPubInterval_1) {            
    previousMqttMillis = currentMillis;       // save the last Pulse time
   
    // publish drum Stopped
    if (stepperENA == HIGH){
    mqttClient.publish("edison/sysMsg", "DRUM STOPPED");
    }
    
    // publish light issue
    if ((digitalRead (growLight) == LOW) && (lux < 500)) {
    mqttClient.publish("edison/sysMsg", "FAULTY LIGHT");
    }
    // publish Auto/Manual mode change
    bool modeState;                      // indicates toggle on mode state
    if (modeState == LOW &&  autoManual == 1) {
        mqttClient.publish("edison/sysMsg", "MODE SET TO: AUTO");
        modeState = HIGH;
    }
    if (modeState == HIGH &&  autoManual == 0) {
    mqttClient.publish("edison/sysMsg", "MODE SET TO: MANUAL");
    modeState = LOW;
    }
    // publish grow light switching on or off
    bool lightState;                      // indicates toggle on light state
    if (lightState == LOW &&  (digitalRead (growLight) == HIGH)) {
        mqttClient.publish("edison/growLight", "OFF");    
        lightState = HIGH;
    }
    if (lightState == HIGH &&  (digitalRead (growLight) == LOW)) {
        mqttClient.publish("edison/growLight", "ON");
        lightState = HIGH;
    }
    
    // publish grow light switching on or off
    bool waterPumpState;                      // indicates toggle on water pump state
    if (waterPumpState == LOW &&  (digitalRead (waterPump) == HIGH)) {
        mqttClient.publish("edison/waterPump", "OFF");    
        waterPumpState = HIGH;
    }
    if (waterPumpState == HIGH &&  (digitalRead (waterPump) == LOW)) {
        mqttClient.publish("edison/waterPump", "ON");
        waterPumpState = HIGH;
    }
    
   }
  
  
  //non-critical information publishes every 10 minutes
  if (currentMillis - previousMqttMillis >= mqttPubInterval_2) {
    previousMqttMillis = currentMillis;       // save the last Pulse time
    
    // publish temperature
    sprintf(msgString, "%02.01f", temp);
    mqttClient.publish("edison/temperature", msgString);
    // publish light sensor reading
    sprintf(msgString, "%d", lux);
    mqttClient.publish("edison/lightSensor", msgString);
//    // publish hunidity sensor reading
//    sprintf(msgString, "%d", humid);
//    mqttClient.publish("edison/humidity", msgString);
//
//    // publish PH
//    sprintf(msgString, "%02.01f", ph);
//    mqttClient.publish("edison/ph", msgString);
    
  }
  
}
//END
On my mobile, I installed MQTT Dashboard so we can get the feeds and publish values such as Light Time Set On/Off and Drum RPH (Rotations per hour)
			    

