Trey German
Published

IoT Smoker

Internet connected electric smoker. Make your BBQ heavenly by cooking it from the cloud!

AdvancedFull instructions provided3,353
IoT Smoker

Things used in this project

Story

Read more

Schematics

Solid State Relay BoosterPack

Solid State Relay BoosterPack that is implemented discretely using TRIACs and Opto-Isolators.

Code

IoT Smoker Energia Code

C/C++
This is the embedded code that runs on the MSP430 LaunchPad.
#include "Energia.h"

#include <WiFi.h>
#include <ADS1118.h>
#include <PubSubClient.h>
#include <SPI.h>
#include <PID_v1.h>
#include <String.h>


void setup();
void loop();

#define RelayPin 37

WiFiClient wclient;

byte server[] = { 198, 41, 30, 241 }; //  Public MQTT Brokers: http://mqtt.org/wiki/doku.php/public_brokers
byte ip[]     = { 172, 16, 0, 100 };

char sensorRead[4];

#define       WIFI_SSID         "AndroidAP"
#define       WIFI_PWD          "Energia!"

PubSubClient client(server, 1883, callback, wclient);

ADS1118 ADS;

char smokerState = 0;
double smokerTemp = 0.0;
double meatTemp = 0.0;
double meatSetpoint = 0.0;

//Define Variables we'll be connecting to
double Setpoint, Output;

//Specify the links and initial tuning parameters
PID myPID(&smokerTemp, &Output, &Setpoint, 850, 0.5, 0.1, DIRECT);

int WindowSize = 5000;
unsigned long windowStartTime;

void callback(char* inTopic, byte* payload, unsigned int length){
  // In order to republish this payload, a copy must be made
  // as the orignal payload buffer will be overwritten whilst
  // constructing the PUBLISH packet.
  
  Serial.println(inTopic);
  Serial.println((char*)payload);
  
  if(strcmp(inTopic, "LpSmokerStateOut")==0){
    Serial.println("State Change Recieved");
    if(payload[0] == '1'){
      smokerState = 1;
      Serial.println("Smoker On");
    }else if(payload[0] == '0'){
      smokerState = 0;
      Serial.println("Smoker Off");
    }
//    Serial.println(payload);
    
  }else if(strcmp(inTopic, "LpSmokerSetOut")==0){
    Serial.println("Smoker Temp Recieved");
    payload[3] = 0;
    Setpoint = double(atoi((char*)payload));
    Serial.println(Setpoint);
//    Serial.println(payload);
    
  }else if(strcmp(inTopic, "LpMeatSetOut")==0){
    Serial.println("Meat Temp Recieved");
    payload[3] = 0;
    meatSetpoint = double(atoi((char*)payload));
    Serial.println(meatSetpoint);
//    Serial.println(payload);
    
  }
//  // Allocate the correct amount of memory for the payload copy
//  byte* p = (byte*)malloc(length);
//  // Copy the payload to the new buffer
//  memcpy(p,payload,length);
//  client.publish("outTopic", p, length);
//  // Free the memory
//  free(p);
}



void setup() {
  
  Setpoint = 225;
  smokerState = 0;
  
  //IO setup
  pinMode(40, OUTPUT);
  pinMode(39, OUTPUT);
  pinMode(38, OUTPUT);
  pinMode(37, OUTPUT);
  digitalWrite(40, HIGH);
  digitalWrite(39, HIGH); 
  digitalWrite(38, HIGH);
  digitalWrite(37, HIGH); 
  
  //Start up the Serial port for debugging
  Serial.begin(115200);
  //Configure the thermocouple interface
  ADS.begin();
  //Turn off that annoying buzzer
  noTone(2);
    
  //Start up WiFi, but don't wait for a connection  
  Serial.println("Start WiFi");
  WiFi.begin(WIFI_SSID);//, WIFI_PWD);
  
  while (WiFi.localIP() == INADDR_NONE) {
    // print dots while we wait for an ip addresss
    Serial.print(".");
    delay(300);
  }

  Serial.println("\nIP Address obtained");


  windowStartTime = millis();

  //initialize the variables we're linked to
  Setpoint = 225;
  
  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);
  
  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  
    client.connect("LaunchPadClientTrey"); 
    Serial.print(".");
    delay(300);
    

    client.subscribe("LpSmokerStateOut");
    client.subscribe("LpSmokerSetOut");
    client.subscribe("LpMeatSetOut");
  
}


void sendMQTT(){
  
  //Make sure we're connected to WiFi
  if(WiFi.localIP() == INADDR_NONE) {
    return;
  }
  
  
  //Make sure we are connected to the MQTT broker
  if(!client.connected()){
    client.connect("LaunchPadClientTrey"); 
    Serial.print(".");
    delay(300);
    

    client.subscribe("LpSmokerStateOut");
    client.subscribe("LpSmokerSetOut");
    client.subscribe("LpMeatSetOut");
    return;
  }
  
  //Publish our data to the MQTT Broker
  // convert into to char array
  String str = (String)int(smokerState); 
  int str_len = str.length() + 1;  // Length (with one extra character for the null terminator)
  char char_array[str_len];  // Prepare the character array (the buffer) 
  str.toCharArray(char_array, str_len);  // Copy it over 
  
  // publish data to MQTT broker
  if (client.connected()) {
    client.publish("LpSmokerStateIn", char_array);
  }
  
  String str1 = (String)int(Setpoint); 
  int str_len1 = str1.length() + 1;  // Length (with one extra character for the null terminator)
  char char_array1[str_len1];  // Prepare the character array (the buffer) 
  str1.toCharArray(char_array1, str_len1);  // Copy it over 
  
  // publish data to MQTT broker
  if (client.connected()) {
    client.publish("LpSmokerSetIn", char_array1);
  }
  
  String str2 = (String)int(meatSetpoint); 
  int str2_len = str2.length() + 1;  // Length (with one extra character for the null terminator)
  char char_array2[str2_len];  // Prepare the character array (the buffer) 
  str2.toCharArray(char_array2, str2_len);  // Copy it over 
  
  // publish data to MQTT broker
  if (client.connected()) {
    client.publish("LpMeatSetIn", char_array2);
  }
  
  String str3 = (String)int(smokerTemp); 
  int str_len3 = str3.length() + 1;  // Length (with one extra character for the null terminator)
  char char_array3[str_len3];  // Prepare the character array (the buffer) 
  str3.toCharArray(char_array3, str_len3);  // Copy it over 
  
  // publish data to MQTT broker
  if (client.connected()) {
    client.publish("LpSmokerTempAct", char_array3);
  }
  
  String str4 = (String)int(meatTemp); 
  int str_len4 = str4.length() + 1;  // Length (with one extra character for the null terminator)
  char char_array4[str_len4];  // Prepare the character array (the buffer) 
  str4.toCharArray(char_array4, str_len4);  // Copy it over 
  
  // publish data to MQTT broker
  if (client.connected()) {
    client.publish("LpMeatTempAct", char_array4);
  }
  
}

void loop() {
  static int loadShare = 0;
  static long lastTime = millis();
  
  ADS.run();
  
  if((millis() - lastTime) > 15000){  
    //Turn off the heat temporarily
    digitalWrite(40, HIGH);
    digitalWrite(39, HIGH); 
    digitalWrite(38, HIGH);
    digitalWrite(37, HIGH);
    
    if(client.connected())
      client.loop();
    sendMQTT();
  }
    
  meatTemp = ADS.readFarenheit(1);
  smokerTemp = ADS.readFarenheit(0);
  
//  Serial.print("Smoker Temp: ");
//  Serial.print(smokerTemp);
//  Serial.println("F");
//
//
//  Serial.print("Meat Temp: ");
//  Serial.print(meatTemp);
//  Serial.println("F");

  if(smokerState)
  {
        myPID.Compute();
      
        /************************************************
         * turn the output pin on/off based on pid output
         ************************************************/
        unsigned long now = millis();
        if(now - windowStartTime>WindowSize)
        { //time to shift the Relay Window
          windowStartTime += WindowSize;
        }
        if(Output > now - windowStartTime){
          
          //Turn the heat on and share the load across the triacs
          digitalWrite(40, loadShare & 1);
          digitalWrite(39, loadShare & 2);   
          digitalWrite(38, loadShare & 4);
          digitalWrite(37, loadShare & 8);
          loadShare++;
          if(loadShare == 0x10)
            loadShare = 1;
          Serial.println("Heat On");
          
        }else{ 
          
          //Turn the heat off
          digitalWrite(40, HIGH);
          digitalWrite(39, HIGH); 
          digitalWrite(38, HIGH);
          digitalWrite(37, HIGH);
          Serial.println("Heat Off");
        }
        
  } else {
    
    //If the smoker isn't turned on make sure the triacs are turned off
    digitalWrite(40, HIGH);
    digitalWrite(39, HIGH); 
    digitalWrite(38, HIGH);
    digitalWrite(37, HIGH); 
  }
  

}

Node-RED Flow

JSON
This is the Node-RED flow that runs in the cloud and produces the user interface. It also sends command to the board and receives the current temperature data.
[{"id":"e4924113.e3d978","type":"mqtt-broker","broker":"198.41.30.241","port":"1883","clientid":""},{"id":"c66445d5.585f48","type":"websocket-listener","path":"/ws/mobiui","wholemsg":"false"},{"id":"c8ba3187.3745d","type":"http in","name":"http request: control","url":"/control","method":"get","x":178,"y":90,"z":"6fb59101.904a7","wires":[["413cc48.fbec33c"]]},{"id":"413cc48.fbec33c","type":"template","name":"chart","field":"payload","template":"<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\" />\n  <meta HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE\" />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /> \n  <meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />\n  <title>Node-RED mobi ui</title>\n  <link rel=\"stylesheet\" href=\"//code.jquery.com/mobile/1.4.3/jquery.mobile-1.4.3.min.css\" />\n  <script src=\"//code.jquery.com/jquery-1.11.1.min.js\"></script>\n  <script src=\"//code.jquery.com/mobile/1.4.3/jquery.mobile-1.4.3.min.js\"></script>\n  <script src=\"//cdnjs.cloudflare.com/ajax/libs/highcharts/4.0.3/highcharts.js\"></script>\n  <script>//Node-Red mobi ui - LHG industrialinternet.co.uk\n//\tconsole.log(\"NR mobi UI 1.0: Controls, Chart\");\n\tvar  schedule = null;\n\tvar itemLookup = null;\n\tvar statusMsg = false;\n\tvar  daiableWidgets = false; \n\tvar connected = false;\n\tvar wsUri=\"wss://\"+window.location.hostname+\"/ws/mobiui\";\n\tvar ws=null;\n\tvar smokerTempAct=69;\n\tvar meatTempAct=69;\n\t\n\t        var options = {\n            chart: {\n                type: 'spline',\n                animation: Highcharts.svg, // don't animate in old IE\n                events: {\n                    load: function () {\n\n                        // set up the updating of the chart each second\n                       var series0 = this.series[0];\n                       setInterval(function () {\n                           var x = (new Date()).getTime(), // current time\n                               y = smokerTempAct;\n                           series0.addPoint([x, y], true, true);\n                       }, 15000);\n                        var series1 = this.series[1];\n                        setInterval(function () {\n                            var x = (new Date()).getTime(), // current time\n                                y = meatTempAct;\n                            series1.addPoint([x, y], true, true);\n                        }, 15000);\n                    }\n                }\n\n            },\n            title: {\n                text: 'Current Smoker Temperature'\n            },\n            xAxis: {\n                type: 'datetime',\n                tickPixelInterval: 1500\n            },\n            yAxis: {\n                title: {\n                    text: 'Temperature (F)'\n                },\n                plotLines: [{\n                    value: 0,\n                    width: 1,\n                    color: '#808080'\n                }]\n            },\n            tooltip: {\n                formatter: function () {\n                    return '<b>' + this.series.name + '</b><br/>' +\n                        Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' +\n                        Highcharts.numberFormat(this.y, 2);\n                }\n            },\n            legend: {\n                enabled: true\n            },\n            exporting: {\n                enabled: false\n            },\n            series: [{\n                name: 'Smoker Temperature',\n                data: (function () {\n                    // generate an array of random data\n                    var data = [],\n                        time = (new Date()).getTime(),\n                        i;\n\n                    for (i = -19; i <= 0; i += 1) {\n                        data.push({\n                            x: time + i * 1000,\n                            y: Math.random()\n                        });\n                    }\n                    return data;\n                }())\n            },{\n                name: 'Meat Temperature',\n                data: (function () {\n                    // generate an array of random data\n                    var data = [],\n                        time = (new Date()).getTime(),\n                        i;\n\n                    for (i = -19; i <= 0; i += 1) {\n                        data.push({\n                            x: time + i * 1000,\n                            y: Math.random()\n                        });\n                    }\n                    return data;\n                }())\n            }]\n        };\n\n\n\t\n\tfunction wsConn() {\n\t\tws = new WebSocket(wsUri);\n\t\tws.onmessage = function(m) {\n//\t\t\tconsole.log('< from-node-red:',m.data);\n\t\t\tif (typeof(m.data) === \"string\" && m. data !== null){\n\t\t\t\tvar msg =JSON.parse(m.data);\n\t\t\t\tvar ftc = msg.id.substring(0, 3);\n\t\t\t\t//console.log(\"id:\"+msg.id+\" fct:\"+ftc);\n\t\t\t\tif(ftc== \"init\") {init( msg.v);}\n\t\t\t\tif(ftc==\"tsw\"){setFlip(msg.id,msg.v);}\n\t\t\t\tif(ftc==\"sld\"){setSlider(msg.id,msg.v);}\n\t\t\t\tif(ftc==\"val\"){setValue(msg.id,msg.v);}\n\t\t\t\t//if(ftc==\"cha\"){showCharts( msg.v.values,msg.v.colors,msg.v.engs,msg.v.tags,msg.v.names,msg.v.nos,msg.v.title,.msg.v.yTitle)};\n\t\t\t\tif(ftc==\"cha\"){showCharts(msg.id,msg.v)};\n\t\t\t\tif(ftc==\"shd\"){setSchedule(msg.v);}\n\t\t\t\tif(ftc==\"sta\"){setStatus(msg.v.stMsg,msg.v.dur,msg.v.pri);}\n\t\t\t\tif(ftc==\"ack\"){clearReq();}\n\t\t\t}\n\t\t}\n\t\tws.onopen = function() { \n\t\t\tstatusMsg=false;\n\t\t\tif(daiableWidgets==true){enablePage();}\n\t\t\tsetStatus(\"Connected\",5,0); \n\t\t\tconnected = true;\n\t\t\tvar obj = {\"id\":\"init\",\"v\":1};\n\t\t\tgetRequest = JSON.stringify(obj); \t\n\t\t\tws.send(getRequest);\t\t\t// Request ui status from NR\n//\t\t\tconsole.log(\"sent init requeset\");\n\t\t\t\n\t\t$('#container').highcharts(options);\n\t\t\t\n\t\t\t\n\t\t}\n\t\tws.onclose   = function()  {\n//\t\t\tconsole.log('Node-RED connection closed: '+new Date().toUTCString()); \n\t\t\tconnected = false; \n\t\t\tws = null;\n\t\t\tsetStatus(\"No connection to server!\",0,1);\n\t\t\tif(daiableWidgets==false){disablePage();}\n\t\t\tsetTimeout(wsConn, 10000);\n\t\t}\t\n\t\tws.onerror  = function(){\n\t\t\t//console.log(\"connection error\");\n\t\t}\n\t}\n\t$( window ).load(function(){wsConn()});\n//\t wsConn(); \t\t\t\t\t// connect to Node-RED server\n\tfunction init(values){ \t\t// initialise UI controls\n\t\tui = JSON.parse(values);\n\t\t for (var item in ui) {\n\t\t\t//console.log(\"item: \"+item);\n\t\t\tvar  m =  ui[item];\n\t\t\tinitSetters(m);\n\t\t}\n\t}\n\tfunction initSetters(msg){  \t// update UI widgets on connect \n//\t\tconsole.log(\"init item id:\"+msg.id+\" value:\"+ msg.v);\n\t\tif(ftc==\"tsw\"){setFlip(msg.id,msg.v);}\n\t\tif(ftc==\"sld\"){setSlider(msg.id,msg.v);}\n\t\tif(ftc==\"val\"){setValue(msg.id,msg.v);}\n\t\tif(ftc==\"cha\"){showCharts(msg.id,msg.v)};\n\t\tif(ftc==\"shd\"){setSchedule(msg.v);}\n\t\tif(ftc==\"sta\"){setStatus(msg.v);}\n\t}\n\tfunction setFlip(_id,_v){ \t// update flip \n\t\tmyselect = $(\"#\"+_id);\n\t\t//console.log(\"flip id:\"+_id+\" value:\"+_v+\" tyepof:\"+ typeof _v +\" state:\"+.data('state')+\" req:\"+myselect.data('req'));\n\t\t//if(myselect.data('req')==1) return; // request on progress stops flip UI chatter \n\t\tif(myselect.data('state')!=_v){\n\t\t\tif(_v== true || _v=='true'){\n\t\t\t\tmyselect[0].selectedIndex=1; \n\t\t\t\tmyselect.data('state',1)\n\t\t\t} \n\t\t\telse { \n\t\t\t\tmyselect[0].selectedIndex=0;\n\t\t\t\tmyselect.data('state',0);\n\t\t\t}\n\t\t\tmyselect.flipswitch(\"refresh\");\n\t\t\t//myselect.stopImmediatePropagation();\n\t\t\t//console.log(\"jq:\"+myselect[0].selectedIndex+\" flip id:\"+_id+\" v:\"+value+\" data-state:\"+myselect.data('state'));\n\t\t}\n\t}\n\tfunction setSlider(_id,_v){\t// update slider\n\t\tmyselect = $(\"#\"+_id);\n\t\t myselect.val(_v);\n\t\t myselect.slider('refresh');\n\t}\n\tfunction setValue(_id,_v){\t// update value display\n\t\tmyselect = $(\"#\"+_id);\n\t\tmyselect.val(_v);\n\t}\n\tfunction showCharts(_id,_v){ // render chart\n\t\tif(_id == \"chart-1\"){\n\t\t\tsmokerTempAct = _v;\n\t\t}else if(_id == \"chart-2\"){\n\t\t\tmeatTempAct = _v;\n\t\t}\n\t\t\t\n                        \n\t}\n\tfunction setSchedule(_v){\t// update schedule \n\t\t//console.log(\"shed:\"+_v);\n\t\tschedule  = JSON.parse(_v);\n//\t\tconsole.log(\"shed:\"+schedule.items[0].id);\n\t}\n\tfunction setStatus(msg,dur,pri){\t // show msg on status bar\n\t\tif(statusMsg == true){return};\n\t\tstatusMsg= true;\n\t\tif(pri>0){\n\t\t\t//msg = \"<span class='alert'>\"+msg+\"</span>\";\n\t\t\t$(\"#statusView\").toggleClass(\"statusViewAlert\");\n\t\t} else {\n\t\t\t$(\"#statusView\").toggleClass(\"statusView\");\n\t\t}\n\t\t$(\"#statusView\").show();\n\t\t$(\"#statusView\").html(msg);\n\t\tdur = dur*1000;\n\t\tif(dur >0){\n\t\t\tsetTimeout(function(){$(\"#statusView\").hide(200);$(\"#statusView\").html(\"\"); statusMsg= false},dur)\n\t\t}\n\t}\t\n\tfunction disablePage(){\t\t\n\t\t$(\"[data-role=flipswitch]\").flipswitch( \"disable\" );\n\t\t//$(\"[data-role=range]\").disabled = true;\n\t\t$(\"[data-role=range]\").slider( \"option\", \"disabled\", true );\n\t\t$(\"[data-rel=popup]\").toggleClass(\"ui-disabled\");\n\t\tdaiableWidgets = true;\n\t}\n\tfunction enablePage(){\n\t\t$(\"[data-role=flipswitch]\").flipswitch( \"enable\" );\n\t\t$(\"[data-role=range]\").slider( \"option\", \"enabled\", true );\n\t\t$(\"[data-role=range]\").slider( \"enable\" );\n\t\t$(\"[data-rel=popup]\").toggleClass(\"ui-disabled\");\n\t\tdaiableWidgets = false;\n\t}\n\t$(function() { \t\t\t\t// UI event handlers           \n\t\t// Flip-switch change\n\t\t$(\"[data-role=flipswitch]\").bind( \"change\", function(event, ui) {\n\t\t\t//console.log(\"id:\"+this.id+\" val:\"+$(this).flipswitch().val()+\" state:\"+$(this).data('state')+\" req:\"+$(this).data('reqstate'));\n\t\t\tvar _value =  $(this).flipswitch().val();\n\t\t\tif($(this).data('state') != _value){\n\t\t\t\t$(this).data('state',_value);        \n\t\t\t\tvar obj = {\"id\":\"\"+this.id+\"\",\"v\":_value};\n\t\t\t\tsetActions = JSON.stringify(obj); \t\n\t\t\t\tws.send(setActions);\n\t\t\t}\n\t\t});\n\t\t// Slider end\n\t\t$(\".ui-slider\").bind( \"slidestop\", function(event, ui) {\n\t\t\tvar obj = {\"id\":\"\"+event.target.id+\"\",\"v\":event.target.value};\n\t\t\tsetActions = JSON.stringify(obj); \t\n\t\t\tws.send(setActions);\n\t\t});\n\t\t// Popup send\n\t\t$(\"[data-ui-type=pop-save]\").bind( \"click\", function(event, ui) {\n\t\t\tbid = this.id.split(\"_\");\n\t\t\twid =\"#\"+bid[0]+\"-pop\";\n\t\t\t$(wid ).popup( \"close\" );\n\t\t\ttid=\"#\"+bid[0];\n\t\t\tvar obj = {\"id\":\"\"+bid[0]+\"\",\"v\":$(tid ).val()};\n\t\t\tsetActions = JSON.stringify(obj); \t\n\t\t\tws.send(setActions);\n\t\t});\n\t\t// Grouped Radio buttons click\n\t\t$(\"[data-ui-type=shd-sel]\").bind( \"click\", function(event, ui) {\n\t\t\t$(\"[data-ui-type=shd-sel]\").prop( \"checked\", false ).checkboxradio( \"refresh\" );\n\t\t\t$(this ).prop( \"checked\", true ).checkboxradio( \"refresh\" );\n\t\t\tvar  item = this.id.split(\"-\");\n\t\t\tif( itemLookup == null){\n\t\t\t\t itemLookup =  item[1]-1;\n\t\t\t } else { // Copy item edits back obj\n\t\t\t\tschedule.items[itemLookup].tag\t\t =  $('#shd-tag').val();\n\t\t\t\tschedule.items[itemLookup].startTime =   $('#shd-st').val();\n\t\t\t\tschedule.items[itemLookup].startValue = $('#shd-st-v').val();\n\t\t\t\tschedule.items[itemLookup].endTime\t    = $('#shd-et').val();\n\t\t\t\tschedule.items[itemLookup].endValue\t    = $('#shd-et-v').val();\n\t\t\t\tfor (dow = 0; dow< 7; dow++) {\n\t\t\t\t\tvar  dowUI = '#shd-dow-'+dow;\n\t\t\t\t\tif( $(dowUI).prop( \"checked\")){\n\t\t\t\t\t\tschedule.items[itemLookup].dofWeek[dow] = 1 ;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tschedule.items[itemLookup].dofWeek[dow] = 0 ;\n\t\t\t\t\t}\n\t\t\t\t} \n\t\t\t\titemLookup =  item[1]-1;\n\t\t\t }\n//\t\t\tconsole.log(\"shed item\"+item[1]+\" lookup tag:\"+schedule.items[itemLookup].tag);\n\t\t\t$('#shd-tag').val(schedule.items[itemLookup].tag);\n\t\t\t$('#shd-st').val(schedule.items[itemLookup].startTime);\n\t\t\t$('#shd-st-v').val(schedule.items[itemLookup].startValue);\n\t\t\t$('#shd-et').val(schedule.items[itemLookup].endTime);\n\t\t\t$('#shd-et-v').val(schedule.items[itemLookup].endValue);\n\t\t\t//console.log(\"group radio - id:\"+ this.id+\" val:\"+$(this).val());\n\t\t\tfor (dow = 0; dow< 7; dow++) {\n\t\t\t\tvar  dowUI = '#shd-dow-'+dow;\n\t\t\t\tif (schedule.items[itemLookup].dofWeek[dow]==1){\n\t\t\t\t\t$(dowUI).prop( \"checked\", true ).checkboxradio( \"refresh\" )\n\t\t\t\t} else {\n\t\t\t\t\t$(dowUI).prop( \"checked\", false ).checkboxradio( \"refresh\" )\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\t// Schedule save\n\t\t$( \"#shd-save\" ).bind( \"click\", function(event, ui) {\n//\t\t\tconsole.log(\"shd-save\");\n\t\t\tif( itemLookup != null){\n\t\t\t\tschedule.items[itemLookup].tag\t\t =  $('#shd-tag').val();\n\t\t\t\tschedule.items[itemLookup].startTime =   $('#shd-st').val();\n\t\t\t\tschedule.items[itemLookup].startValue = $('#shd-st-v').val();\n\t\t\t\tschedule.items[itemLookup].endTime\t    = $('#shd-et').val();\n\t\t\t\tschedule.items[itemLookup].endValue\t    = $('#shd-et-v').val();\n\t\t\t\tfor (dow = 0; dow< 7; dow++) {\n\t\t\t\t\tvar  dowUI = '#shd-dow-'+dow;\n\t\t\t\t\tif( $(dowUI).prop( \"checked\")){\n\t\t\t\t\t\tschedule.items[itemLookup].dofWeek[dow] = 1 ;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tschedule.items[itemLookup].dofWeek[dow] = 0 ;\n\t\t\t\t\t}\n\t\t\t\t} \n\t\t\t\tvar obj = {\"id\":\"shd-save\",\"v\":schedule};\n\t\t\t\tgetRequest = JSON.stringify(obj); \t\n\t\t\t\tws.send(getRequest);\n\t\t\t}\n\t\t});\n\t\t// Utills\n\t\tvar showHide=0;\n\t\t$(window).keydown(function(event) {\n\t\t\tif(event.shiftKey && event.keyCode == 68 ) { \n\t\t\t\t//console.log(event.keyCode);\n\t\t\t\tif(showHide==0){$(\"#foo\").show('slow');showHide=1;}else{$(\"#foo\").hide();showHide=0;}\n\t\t\t\tevent.preventDefault(); \n\t\t\t}\n\t\t});  \n\t\t$( document ).on( \"vclick\", \"#b1\", function() {\n\t\t\tlocation.reload();\n//\t\t\tconsole.log(\"reload button\");\n\t\t});\t\n\t});\n      </script>\n  <style>\n\t\t@media only screen and (min-width: 521px){\n\t\t\t#header1 {width: 507px !important;  margin:auto auto !important; position: relative !important; border:1px solid #cccccc;}\n\t\t\t#c1 {width: 475px !important; min-height:500px !important; margin:auto auto !important; position: relative !important; border:1px solid #cccccc;}\n\t\t}\t\n\t\t.alert {font-weight: bold; color: #FF6C01;}\n\t\t.smallin .ui-input-text {width: 200px !important; color:red;}\n\t\tlegend.h1lb {padding:10px 0 5px 0;}\n\t\t.statusView {width: 100% !important;  margin:0px; position: relative !important; height:28px; padding:10px 0 0 0; background-color:#FAFAFA; font-weight:bold; -webkit-border-radius: 0.7em !important; border-radius: 0.7em; display:none; text-align:center; }\n\t\t.statusViewAlert {width: 100% !important;  margin:0px; position: relative !important; height:28px; padding:10px 0 0 0; background-color:#F8B584 !important; font-weight:bold; -webkit-border-radius: 0.7em !important; border-radius: 0.7em; display:none; text-align:center; }\n\t\t.ui-flipswitch {\"border-radius: 2em !important; -webkit-border-radius: 2em !important; -moz-border-radius: 2em !important;}\n\t\t/* Active button */\n\t\t.ui-page-theme-a .ui-btn.ui-btn-active,html .ui-bar-a .ui-btn.ui-btn-active,html .ui-body-a .ui-btn.ui-btn-active,html body .ui-group-theme-a .ui-btn.ui-btn-active,html head + body .ui-btn.ui-btn-a.ui-btn-active,\n\t\t/* Active checkbox icon */\n\t\t.ui-page-theme-a .ui-checkbox-on:after,html .ui-bar-a .ui-checkbox-on:after,html .ui-body-a .ui-checkbox-on:after,html body .ui-group-theme-a .ui-checkbox-on:after,.ui-btn.ui-checkbox-on.ui-btn-a:after,\n\t\t/* Active flipswitch background */.ui-page-theme-a .ui-flipswitch-active,html .ui-bar-a .ui-flipswitch-active,html .ui-body-a .ui-flipswitch-active,html body .ui-group-theme-a .ui-flipswitch-active,\n\t\thtml body .ui-flipswitch.ui-bar-a.ui-flipswitch-active,\n\t\t/* Active slider track */\n\t\t.ui-page-theme-a .ui-slider-track .ui-btn-active,html .ui-bar-a .ui-slider-track .ui-btn-active,html .ui-body-a .ui-slider-track .ui-btn-active,html body .ui-group-theme-a .ui-slider-track .ui-btn-active,html body div.ui-slider-track.ui-body-a .ui-btn-active {\n\t\t\tbackground-color: \t\t#2A2A2A !important;\n\t\t\tborder-color:\t \t\t#FF6C01 !important;\n\t\t\tcolor: \t\t\t\t\t#fff /*{a-active-color}*/;\n\t\t\ttext-shadow: 0 /*{a-active-shadow-x}*/ 1px /*{a-active-shadow-y}*/ 0 /*{a-active-shadow-radius}*/ #005599 /*{a-active-shadow-color}*/;\n\t\t}\n   </style>\n</head>\n<body bgcolor=\"#2A2A2A\">\n  <!-- Home -->\n  <div data-role=\"page\" id=\"controls-charts\">\n\t<div id=\"c1\" class=\"ui-content\">\n\t<div id=\"statusView\" style=\"\"></div>\t\n\t\t<div class=\"ui-field-contain\">\n\t\t\t<label for=\"tsw-1\">Smoker State:</label>\n\t\t\t<select id=\"tsw-1\" data-role=\"flipswitch\" data-state=\"0\" data-req=\"\">\n\t\t\t  <option value=\"0\">Off</option>\n\t\t\t  <option value=\"1\">On</option>\n\t\t\t</select>\n\t\t\t</div>\n\t\t\t<div class=\"ui-field-contain\">\n\t\t\t<form>\n    \t\t\t<label for=\"sld-1\">Smoker Setpoint (F):</label>\n    \t\t\t<input type=\"range\" name=\"sld-1\" id=\"sld-1\" min=\"100\" max=\"350\" value=\"225\">\n    \t\t</form>\n\t\t\t</div>\n\t\t\t<div class=\"ui-field-contain\">\n\t\t\t<form>\n   \t\t\t \t<label for=\"sld-2\">Meat Done Temperature (F):</label>\n \t\t\t   \t<input type=\"range\" name=\"sld-2\" id=\"sld-2\" min=\"100\" max=\"350\" value=\"190\">\n \t\t\t</form>\n\t\t</div>\n\t\t<div id=\"container\" style=\"width: 100%; margin-left:-15px;\"></div>\n\t\t</div>\n\t\t\t\t\n\t</div><!-- eof content -->\n  </body>\n</html>","x":451,"y":81,"z":"6fb59101.904a7","wires":[["5cc132d3.a33ecc"]]},{"id":"5cc132d3.a33ecc","type":"http response","name":"http response: Control Interface","x":737,"y":106,"z":"6fb59101.904a7","wires":[]},{"id":"b1b3bc12.4e4c4","type":"function","name":"ui dispatcher","func":"/* \tUI dispatcher v0.1a 23/06/2014\n\tRecives changes from UI and actions them */\nvar obj = JSON.parse(msg.payload);\ndelete msg.payload;\ndelete msg._session;\n\n// toggle switch tsw-1\t- output 1 \nif(obj.id==\"tsw-1\"){\n\tmsg.id = obj.id; \t\n\tmsg.state = obj.v;\t\n\treturn [msg, null, null, null];\n\n// toggle switch tsw-2\t- output 2 \n} else if(obj.id==\"tsw-2\"){\n\tmsg.id = obj.id; \t\t\n\tmsg.state = obj.v;\t\n\treturn [null, msg, null];\n\t\n// slider  sld-1 \t\t- output 3 \t\n} else if(obj.id==\"sld-1\"){\n\tmsg.id = obj.id; \n\tmsg.value = obj.v;\t\n\treturn [null, null, msg, null];\n\n// txt popup txt-1 \t\t- output 4 \t\n} else if(obj.id==\"sld-2\"){\n\tmsg.id = obj.id; \n\tmsg.value = obj.v;\t\n\treturn [null, null, null, msg];\n\n//  do nothing\n} else {\n\treturn [null, null, null, null];\n}","outputs":"4","x":398,"y":384,"z":"6fb59101.904a7","wires":[["e6cf752c.193088","531e2139.ace1e"],[],["5ae130f6.a51ed","531e2139.ace1e"],["7229fba2.8dd604","531e2139.ace1e"]]},{"id":"13af600d.ec50a","type":"comment","name":"Dispatch mobi UI commands - web sockets","info":"","x":244.9999942779541,"y":328.9999899864197,"z":"6fb59101.904a7","wires":[]},{"id":"2fdb1f6c.d024e","type":"websocket in","name":"web socket mobiUI - in","server":"c66445d5.585f48","x":185.9999942779541,"y":383.99998235702515,"z":"6fb59101.904a7","wires":[["b1b3bc12.4e4c4","4474dc21.bb8b24"]]},{"id":"e6cf752c.193088","type":"function","name":"Format state slider","func":"if(msg.id == \"tsw-1\"){\n\tmsg.payload = msg.state.toString();\n\treturn msg;\n}\nreturn null;\n","outputs":1,"x":598.888916015625,"y":326.4444274902344,"z":"6fb59101.904a7","wires":[["bfdce141.40232","fdb92c8e.0246d"]]},{"id":"5ae130f6.a51ed","type":"function","name":"Format Smoker Slider","func":"msg.payload = msg.value.toString();\nreturn msg;","outputs":1,"x":606.4444580078125,"y":389.22222900390625,"z":"6fb59101.904a7","wires":[["d630819b.29cf8","fdb92c8e.0246d"]]},{"id":"de0cac56.21f35","type":"websocket out","name":"web socket mobiUI - in","server":"c66445d5.585f48","x":625,"y":530,"z":"6fb59101.904a7","wires":[]},{"id":"da42a1b8.25bd6","type":"inject","name":"","topic":"","payload":"0","payloadType":"string","repeat":"","crontab":"","once":false,"x":189,"y":511,"z":"6fb59101.904a7","wires":[["5e418315.a1be7c"]]},{"id":"5e418315.a1be7c","type":"function","name":"","func":"msg.payload = {id:\"tsw-1\",v:parseInt(msg.payload)};\nreturn msg;","outputs":1,"x":360,"y":516,"z":"6fb59101.904a7","wires":[["de0cac56.21f35"]]},{"id":"832107a6.7cdef8","type":"inject","name":"","topic":"","payload":"1","payloadType":"string","repeat":"","crontab":"","once":false,"x":190,"y":545,"z":"6fb59101.904a7","wires":[["5e418315.a1be7c"]]},{"id":"e939184.f16c6e8","type":"function","name":"","func":"msg.payload = {id:\"sld-1\",v:msg.payload}\nreturn msg;","outputs":1,"x":356,"y":595,"z":"6fb59101.904a7","wires":[[]]},{"id":"6f43f374.90bc0c","type":"inject","name":"","topic":"","payload":"120","payloadType":"string","repeat":"","crontab":"","once":false,"x":187,"y":619,"z":"6fb59101.904a7","wires":[["e939184.f16c6e8"]]},{"id":"e7881b62.1877e8","type":"mqtt in","name":"","topic":"LpSmokerTempAct","broker":"e4924113.e3d978","x":192,"y":747,"z":"6fb59101.904a7","wires":[["47016916.b8fe98"]]},{"id":"47016916.b8fe98","type":"function","name":"","func":"msg.payload = {id:\"chart-1\",v:parseInt(msg.payload)}\nreturn msg;","outputs":1,"x":363,"y":748,"z":"6fb59101.904a7","wires":[["de0cac56.21f35"]]},{"id":"644d8c90.9bb274","type":"function","name":"","func":"msg.payload = {id:\"chart-2\",v:parseInt(msg.payload)}\nreturn msg;","outputs":1,"x":364,"y":790,"z":"6fb59101.904a7","wires":[["de0cac56.21f35"]]},{"id":"7059f7d9.8fa608","type":"mqtt in","name":"","topic":"LpMeatTempAct","broker":"e4924113.e3d978","x":184,"y":784,"z":"6fb59101.904a7","wires":[["644d8c90.9bb274"]]},{"id":"7229fba2.8dd604","type":"function","name":"Format Meat Slider","func":"msg.payload = msg.value.toString();\nreturn msg;","outputs":1,"x":600,"y":439,"z":"6fb59101.904a7","wires":[["adaed7a4.525128","fdb92c8e.0246d"]]},{"id":"bfdce141.40232","type":"mqtt out","name":"","topic":"LpSmokerStateOut","qos":"","retain":"","broker":"e4924113.e3d978","x":856,"y":326,"z":"6fb59101.904a7","wires":[]},{"id":"d630819b.29cf8","type":"mqtt out","name":"","topic":"LpSmokerSetOut","qos":"","retain":"","broker":"e4924113.e3d978","x":850,"y":389,"z":"6fb59101.904a7","wires":[]},{"id":"adaed7a4.525128","type":"mqtt out","name":"","topic":"LpMeatSetOut","qos":"","retain":"","broker":"e4924113.e3d978","x":842,"y":439,"z":"6fb59101.904a7","wires":[]},{"id":"4ca6fb60.b35904","type":"mqtt in","name":"","topic":"LpSmokerStateIn","broker":"e4924113.e3d978","x":180,"y":471,"z":"6fb59101.904a7","wires":[["597cc969.a68338","5e418315.a1be7c"]]},{"id":"45539404.baac6c","type":"mqtt in","name":"","topic":"LpSmokerSetIn","broker":"e4924113.e3d978","x":173,"y":580,"z":"6fb59101.904a7","wires":[["e939184.f16c6e8"]]},{"id":"5ed28371.a12d7c","type":"mqtt in","name":"","topic":"LpMeatSetIn","broker":"e4924113.e3d978","x":173,"y":655,"z":"6fb59101.904a7","wires":[["b996cebd.46693"]]},{"id":"3ee6ccd8.c11934","type":"inject","name":"","topic":"","payload":"150","payloadType":"string","repeat":"","crontab":"","once":false,"x":182,"y":691,"z":"6fb59101.904a7","wires":[["b996cebd.46693"]]},{"id":"b996cebd.46693","type":"function","name":"","func":"msg.payload = {id:\"sld-2\",v:msg.payload}\nreturn msg;","outputs":1,"x":360,"y":668,"z":"6fb59101.904a7","wires":[[]]},{"id":"b2b8212.f4d47e","type":"inject","name":"","topic":"","payload":"120","payloadType":"string","repeat":"","crontab":"","once":false,"x":177,"y":844,"z":"6fb59101.904a7","wires":[["47016916.b8fe98"]]},{"id":"9f5aea11.60a518","type":"inject","name":"","topic":"","payload":"130","payloadType":"string","repeat":"","crontab":"","once":false,"x":193,"y":891,"z":"6fb59101.904a7","wires":[["644d8c90.9bb274"]]},{"id":"4474dc21.bb8b24","type":"debug","name":"","active":false,"console":"false","complete":"false","x":389,"y":277,"z":"6fb59101.904a7","wires":[]},{"id":"531e2139.ace1e","type":"debug","name":"","active":false,"console":"false","complete":"false","x":580,"y":268,"z":"6fb59101.904a7","wires":[]},{"id":"fdb92c8e.0246d","type":"debug","name":"","active":false,"console":"false","complete":"false","x":903,"y":220,"z":"6fb59101.904a7","wires":[]},{"id":"b25b38fc.4da4c8","type":"inject","name":"","topic":"","payload":"1","payloadType":"string","repeat":"","crontab":"","once":false,"x":660,"y":202,"z":"6fb59101.904a7","wires":[["bfdce141.40232","fdb92c8e.0246d"]]},{"id":"597cc969.a68338","type":"debug","name":"","active":true,"console":"false","complete":"false","x":382,"y":471,"z":"6fb59101.904a7","wires":[]}]

Credits

Trey German

Trey German

10 projects • 33 followers
Freelance Engineer, hacker, sky junkie, programmer, designer. All projects are my own.

Comments