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)


