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

https://software.intel.com/en-us/blogs/2015/02/20/building-and-running-mosquitto-mqtt-on-intel-edison

 

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)

 

Screenshot_20170604-170955.pngScreenshot_20170604-170917.pngScreenshot_20170604-211219.png