Petrescu Cristian
Published © GPL3+

Temperature Controller with Blynk and Linkit Duo

My latest project. A temperature controller with Blynk and Linkit Duo.

AdvancedFull instructions provided6 hours1,696
Temperature Controller with Blynk and Linkit Duo

Things used in this project

Hardware components

relay board
×1

Software apps and online services

PUTTY
WinSCP
Visual Studio Code
Arduino IDE
Arduino IDE

Story

Read more

Schematics

Circuit diagram

Take care, R1, R4, R7, R10 must be rated at 1W.

Code

Arduino code for Linkit Duo

Arduino
This code will create a simple serial protocol for linking MCU with the Node Js server running on Linkit Smart Duo. Just upload the code into your microcontroller
#include "Tasker.h"
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS A5
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

/*
  Valid commands list
 * is the character used in readStringUntil
  --------------------------------------------------
  1.Get status -> 0:0:0*
  2.PWM  ->1:pin:value*
  3.Digital out ->2:pin:value*
  4.Set explicitly digital out ->3:pin:value*
*/


Tasker tasker;
char endChar = '*'; // the enchar for a valid command
char splitter = ':';// data delimiter
int nrOfCom = 4;// number of commnds. Not impemented


float temp1 =-128.00;
int inputs[4] = {11, 10, 9, 8};
//int outputs[4] = {7, 6, 5, 4};
int outputs[4] = {A1, A2, A3, A4};
//int in_status[4] = {0, 0, 0, 0};
int out_status[4] = {0, 0, 0, 0};

///////////////////////////////////////////////////
/*
This function will return the status of the 
digital inputs
*/

String readDigitalIn() {
  String s = "";
  for (int i = 0; i < 4; i++) {
    if (digitalRead(inputs[i])) {
      s = s + "0" + ":";
    }
    else {
      s = s + "1" + ":";
    }
  }
  return s.substring(0, s.length() - 1);
}
/////////////////////////////////////////////////
/*
This function will return the status of the 
digital outputs
*/

String readDigitalOut() {
  String s = "";
  for (int i = 0; i < 4; i++) {
    if (out_status[i]) {
      s = s + "1" + ":";
    }
    else {
      s = s + "0" + ":";
    }
  }
  return s.substring(0, s.length() - 1);
}
////////////////////////////////////////////////
/*
This function will set the digital outputs
*/
void setDigitalOut(int pin, int value, boolean mode) {
  if (mode) {
    if (out_status[pin]) {
      out_status[pin] = 0;
      digitalWrite(outputs[pin], LOW);
    }
    else {
      out_status[pin] = 1;
      digitalWrite(outputs[pin], HIGH);
    }
  }
  if (mode == false) {
    digitalWrite(outputs[pin], value);
    out_status[pin] = value;
  }
}
////////////////////////////////////////////////
/*
Here the temperature is read.
*/
void readSensor(int) {
  temp1 = sensors.getTempCByIndex(0);
  Serial.println(temp1);
}
///////////////////////////////////////////////
/*
Here the conversion of temperature is started.
*/
void startConversion(int) {
  sensors.requestTemperatures();
  tasker.setTimeout(readSensor, 750);
}
/////////////////////////////////////////////
/*
This is the core of all things. MCU is checking if there
is somethig received on SerialPort. It will read a string
until it will find the '*' char. Then the string is splitted
and analyzed. The valid commnds are presented at the beginning 
of the listing.
If there is nothing on Serial it will send the current status.
*/
void sendData(int) {
  int index[2];
  if (Serial1.available() > 0) {
    String c = Serial1.readStringUntil(endChar);
    Serial.println(c);
    int j = 0;
    for (int i = 0; i < c.length(); i++) {
      if (c.charAt(i) == splitter) {
        index[j] = i;
        j++;
      }
    }
    String cmd = c.substring(0, index[0]); //command
    String pin = c.substring(index[0] + 1, index[1]); //pin
    String value = c.substring(index[1] + 1); //value

    if (cmd.toInt() == 2) { //digitalOut command
      setDigitalOut(pin.toInt(), value.toInt(),true);
    }
    if (cmd.toInt() == 3) { //digitalOut command
      setDigitalOut(pin.toInt(), value.toInt(),false);
    }
  }  
  String ts = String(temp1);
  Serial1.println(ts + splitter + readDigitalIn() + splitter + readDigitalOut());
}
void report(int){
  String ts = String(temp1);
  Serial1.println(ts + splitter + readDigitalIn() + splitter + readDigitalOut());
  }

void setup() {
  for (int i = 0; i < 4; i++) {
    pinMode(inputs[i], INPUT);
    pinMode(outputs[i], OUTPUT);
    digitalWrite(outputs[i], HIGH);
  }

  sensors.begin();

  Serial.begin(115200);  // open serial connection to USB Serial
  //port(connected to your computer)
  Serial1.begin(57600);  // open internal serial connection to
  //MT7688

  sensors.setWaitForConversion(false);// the converssion is called separatley
  delay(1000);
  tasker.setInterval(startConversion, 5000);//every 5 sec. a conversion is started
  //after a dealy of 750ms it will read the sensor and update the current temperature
  tasker.setInterval(sendData, 250);// every 250ms SerialPort is checked for a new command
  //if there is a new command it will be executed. If not the current status will be sent.
  //tasker.setInterval(report,3000);
  tasker.run();
}

void loop() {
  // there is no need for loop :)

}

Node Js server for "talking" Blynk

JavaScript
This code will link the MCU with Blynk server
const Blynk=require('blynk-library');
const SerialPort =require('serialport');
const port = new SerialPort('/dev/ttyS0', {
  baudRate: 57600,
  autoOpen:false,
  parser: SerialPort.parsers.readline('\n')
});
/*
Every command will be pushed inside buffer array
*/
var buffer=[];
var oldRes='null';
var firstTime=true;

port.open(function(err){
    if(err){
        console.log('Error on opening port...');
    }
    console.log('Comport found !');
});

port.on('open',function(){
    console.log('Conection estabilished!');
});

/*Every 250ms Arduino will send the current status.
If there is something inside the buffer array it will removed
be send back to Arduino. The values received from Arduino will be
sent to the Blynk server only if they are different than the previous
ones.
*/
port.on('data',function(data){
    //console.log(data);
    //console.log(oldRes);
    if(buffer.length>0){
      port.write(buffer.shift());
      //console.log(buffer);
    }
    if(oldRes != data){
      oldRes=data;
      var res=data.substring(0, data.length - 1).split(':');
      temp.crtTemp=parseFloat(res[0]);
      alarm.state=res.slice(1,5);
      alarm.update();
      buttons.state=res.slice(5);
      buttons.update();
      temp.update();
    }  
});


var blynk = new Blynk.Blynk('ace7db88efbd4433b3412ff02c9abc49');
/*Here is the temeprature object. It contains all the values and 
functions that regard temperature*/
var temp={
  crtTemp:0,//current temperature
  triggerH:true,//this variable is used to raise only once a max temp. alarm
  triggerL:true,//this variable is used to raise only once a min temp. alarm
  update:function(){//here we update the temperature status
    blynk.virtualWrite(0,this.crtTemp);//send to Blynk the current temp.
    ///High temperature alarm
    if(this.crtTemp>termo.setpointHigh && this.triggerH){
      var d = new Date();
      terminal.write(d.getDate()+"/"+d.getMonth()+"@"+d.getHours()+":"+d.getMinutes()+'--- Temperature Alarm: '+temp.crtTemp+" C \n");
      blynk.notify('High Temperature Alarm. Current temperature: '+temp.crtTemp +" C");
      this.triggerH=false;
    }
    if(this.crtTemp<termo.setpointHigh){
      this.triggerH=true;
    }
    ///Low temperature alarm
    if(this.crtTemp<termo.setpointLow && this.triggerL){
      var d = new Date();
      terminal.write(d.getDate()+"/"+d.getMonth()+"@"+d.getHours()+":"+d.getMinutes()+'--- Temperature Alarm: '+temp.crtTemp+" C \n");
      blynk.notify('Low Temperature Alarm. Current temperature: '+temp.crtTemp +" C");
      this.triggerL=false;
    }
    if(this.crtTemp>termo.setpointLow){
      this.triggerL=true;
    }
    ///Set min-max registerd temperatures.
    if(firstTime && this.crtTemp!=0 && this.crtTemp!=-128){
      firstTime=false;
      this.minTemp=this.crtTemp;
      this.maxTemp=this.crtTemp;
      blynk.virtualWrite(1,this.minTemp);
      blynk.virtualWrite(2,this.maxTemp);
    }
    else{
      if(this.crtTemp<this.minTemp && this.crtTemp!=-128){
        this.minTemp=this.crtTemp;
        blynk.virtualWrite(1,this.minTemp);
      }
      if(this.crtTemp>this.maxTemp && this.crtTemp!=-128){
        this.maxTemp=this.crtTemp;
        blynk.virtualWrite(2,this.maxTemp);
      }
    }
  },
  minTemp:0,
  maxTemp:0,
}
/*This is the alarm object. It's linked with digitalIn pins */
var alarm={
  trigger:[true,true,true,true],
  state:[0,0,0,0],
  names:['Backdoor','Main Enterance','Backyard Window','Garage Door'],
  vPins:[20,21,22,23],
  update:function(){
    for(i=0;i<this.vPins.length;i++){
      if(this.state[i]==0){
        blynk.setProperty(this.vPins[i],"color","#129b59");
        blynk.virtualWrite(this.vPins[i],'OK');
        if(this.trigger[i]==false){
          this.trigger[i]=true;
        }
      }
      else{
        blynk.setProperty(this.vPins[i],"color","#c6170d");
        blynk.virtualWrite(this.vPins[i],'ALARM!');
        if(this.trigger[i]){
          this.trigger[i]=false;
          blynk.notify('Alarm on ' + this.names[i]);
          var d = new Date();
          terminal.write(d.getDate()+"/"+d.getMonth()+"@"+d.getHours()+":"+d.getMinutes()+'--- Alarm on ' + this.names[i]+'\n');
        }
      }
    }
  }
}

/*This is the buttons object. It's linked with digitalOut pins */
var buttons={
  state:[0,0,0,0],
  vPins:[40,41,42,43],
  update:function(){
    for(i=0;i<this.vPins.length;i++){
      if(this.state[i]==0){
        blynk.setProperty(this.vPins[i],"color","#129b59");
        blynk.setProperty(this.vPins[i],"onLabel",'ON');
        blynk.virtualWrite(this.vPins[i],1);
      }
      else{
        blynk.setProperty(this.vPins[i],"color","#c6170d");
        blynk.setProperty(this.vPins[i],"offLabel",'OFF');
        blynk.virtualWrite(this.vPins[i],0);
      }
    }
  }
}

/*This is the temperature control object. */
var termo={
  pins:[50,51,52,53,54],//not implemented yet
  ///startup values
  setpointLow:25,
  hys:6,
  setpointHigh:0,
  updateSetpoint:function(){
    this.setpointHigh=this.setpointLow+this.hys;
    blynk.virtualWrite(52,this.setpointHigh);
  },
  mode:1,//1=heating, 2=cooling//
  control:2,// 2=manual, 1=automated
  ///send new variables to server
  updateTermo:function(){
    this.updateSetpoint();
    blynk.virtualWrite(50,this.setpointLow);
    blynk.virtualWrite(51,this.hys);
    blynk.virtualWrite(53,this.mode);
    blynk.virtualWrite(54,this.control);
  },
  ///Automated temperature control.
  autoControl:function(){
    if(this.control==1){ //automated control
      if(this.mode==2){ ////heating
        if(temp.crtTemp<=this.setpointLow){
          buffer.push('3:0:1*');//start heating
        }
        if(temp.crtTemp>=this.setpointHigh){
          buffer.push('3:0:0*');//stop heating
        }
      }
      else { //cooling
        if(temp.crtTemp>=this.setpointHigh){
          buffer.push('3:0:1*');//start cooling
        }
        if(temp.crtTemp<=this.setpointLow){
          buffer.push('3:0:0*');//stop cooling
        }
      } 
    }
  }
}


/*Here the graph is updated */

var updateGraph=setInterval(function(){blynk.virtualWrite(55,temp.crtTemp)},600000);

//////////////Virtual variables !!!!!!!
var terminal=new blynk.WidgetTerminal(60);

var tempControl = new blynk.VirtualPin(40);
var gardenPump= new blynk.VirtualPin(41);
var garageDoor= new blynk.VirtualPin(42);
var basementLight= new blynk.VirtualPin(43);

var sliderLow=new blynk.VirtualPin(50);
var sliderHys=new blynk.VirtualPin(51);
var setHigh=new blynk.VirtualPin(52);
var mode=new blynk.VirtualPin(53);
var control=new blynk.VirtualPin(54);
//////////////Commands from server !!!!!!!

//This button is locked if control is automated.
tempControl.on('write',function(param){
  if(termo.control==2){
    buffer.push("2:0:1*");
  }
  else{
    if(buttons.state[0]==1){
      blynk.setProperty(40,"color","#129b59");
      blynk.setProperty(40,"onLabel",'ON');
      blynk.virtualWrite(40,1);
    }
    else{
      blynk.setProperty(40,"color","#c6170d");
      blynk.setProperty(40,"offLabel",'OFF');
      blynk.virtualWrite(40,0);
    }
  } 
});

/*As you can see commands for Arduino are sent in buffer first */
gardenPump.on('write',function(param){
    buffer.push("2:1:1*");  
});
garageDoor.on('write',function(param){
    buffer.push("2:2:1*") 
});
basementLight.on('write',function(param){
   buffer.push("2:3:1*") 
});

sliderLow.on('write',function(param){
  termo.setpointLow=parseFloat(param[0]);
  termo.updateSetpoint();
})
sliderHys.on('write',function(param){
  termo.hys=parseFloat(param[0]);
  termo.updateSetpoint();
})
mode.on('write',function(param){
  termo.mode=parseInt(param[0]);
});
control.on('write',function(param){
  termo.control=parseInt(param[0]);
});

//////////////On connect
blynk.on('connect', function() { 
  console.log("Blynk ready.");
  termo.updateTermo();
  buttons.update();
  alarm.update();
  temp.update();
  blynk.virtualWrite(55,temp.crtTemp);
  terminal.write('Connected'+'\n');
});
blynk.on('disconnect', function() { 
  console.log("DISCONNECT"); 
});

var x=setInterval(function(){
  termo.autoControl();
  temp.update();
},2000);

Credits

Petrescu Cristian

Petrescu Cristian

3 projects • 2 followers

Comments