Need help in firmware code

Hi,

Can anyone please help me in the development of firmware code? I need urgent help.

Brief Description: I’ve created a home automation project. Following is my code-part1 (working well). It consists- control appliances, temperature etc. Works when Internet is available.

and code-part2 (working well). It consists web-server, mDNS. It controls electric appliances without Internet (locally i.e. In WiFi range).

I just want that when Internet goes down then code-part2 start executing immediately so we can control appliances locally. Both code is working well indivisually but I’m not able to merge into one.

In simple terms, when Internet is available, code-part1 should execute & when Internet goes down then code-part2 should start automatically and if in background, Internet is available again then it should start running code-part1.

How can I code these scenario? I tried to implement but no success.

Note: If my question is not understandable then let me know. I also know, there are so many similar post/que. like this but I’m not getting exact method. Pls co-operate with me.

Thanks!

@ScruffR I expect help from you :smiley:

Do you expect or hope for help :confused:

1 Like

Yeah…Hope for help :smile:

OK then.

Just some extra questions:

  • why would you not just have the non-internet code execute just the same while connected?
  • Since you said code 2 is meant for the non-internet scenario, why would you still try to Particle.connect()?
  • Why would you not read your sensors in scenario 2?
  • What exactly do you mean with: “I tried to implement but no success”? What did you try and what didn’t work?

BTW, instead of int ctrl1, ctrl2, ... try using an array const int ctrl[6] = { A2, A3, A4, A5, A6, A7 }; - and the same for status#, s#

1 Like

Thank you for your reply.

Answers as per your questions:

  • Sorry, I didn’t get your first question.
  • It is my mistake. Actually, that code section was commented.
  • I’ll read the sensor value in scenario2. Presently, I didn’t code even now.
  • I tried to implement that I can control the appliances and read the sensors if Internet is available like image below. When Internet goes down, still I could control the appliances & read sensors by using IP adrdress that show in my dashboard. Technically, code1 and code2(webserver) works simultaneously. So, I can use without Internet too but I’m not able to combine in this way that both code functionality work simultaneously. Code2 works isolated very well.

You can see the below dashboard pics worked under code1:

Am I clear now?

Thank you for point me towards an array. I’ll definitely use now.

Sorry for my written English proficiency.

Have a try with this one

SYSTEM_MODE(SEMI_AUTOMATIC)
SYSTEM_THREAD(ENABLED)

#include <math.h>
#include "PietteTech_DHT/PietteTech_DHT.h"
#include "MDNS/MDNS.h" 
#include "WebServer/WebServer.h"

// system defines
#define DHTTYPE  DHT11              // Sensor type DHT11/21/22/AM2301/AM2302
#define DHTPIN   11         	    // Analog pin A1 for communications
//#define DHT_SAMPLE_INTERVAL   30000  // Sample every 30 seconds
#define WEBDUINO_FAVICON_DATA ""
#define WEBDUINO_FAIL_MESSAGE ""
#define POST_NAME_LENGTH    32
#define POST_VALUE_LENGTH   32

MDNS mdns;
WebServer webserver("", 80);

// All pages
P(Page_start) = "<!DOCTYPE html><html><head><title>SharpNode</title><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><meta charset=\"UTF-8\">";
P(Page_css) = "<style type=\"text/css\">html,body{font-family:sans-serif;}fieldset{margin-left:auto;margin-right:auto;max-width:480px;border-radius:8px;}legend{font-weight:bold;color:#444;}input.cmn-toggle-yes-no+label{padding:2px;width:60px;height:30px;white-space:wrap;overflow:hidden;text-overflow:ellipsis;text-align:center;font-size:12px;text-transform:uppercase}input.cmn-toggle-yes-no+label:before,input.cmn-toggle-yes-no+label:after{display:block;position:absolute;top:0;left:0;bottom:0;right:0;color:#fff;font-family:Roboto Slab,serif;text-align:center;line-height:30px;}input.cmn-toggle-yes-no+label:before{color:#fff;background-color:#444;content:attr(data-name);-moz-border-radius:5px 5px 5px 5px;-webkit-border-radius:5px 5px 5px 5px; border-radius:5px 5px 5px 5px;-webkit-transition:-webkit-transform 0.5s;-moz-transition:-moz-transform 0.5s;-o-transition:-o-transform 0.5s;transition:transform 0.5s;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden;-o-backface-visibility:hidden;backface-visibility:hidden;}input.cmn-toggle-yes-no+label:after{background-color:#00a65a;content:attr(data-name);-moz-border-radius:5px 5px 5px 5px;-webkit-border-radius:5px 5px 5px 5px;border-radius:5px 5px 5px 5px;-webkit-transition:-webkit-transform 0.5s;-moz-transition:-moz-transform 0.5s;-o-transition: -o-transform 0.5s; transition: transform 0.5s;-webkit-transform: rotateY(180deg);-moz-transform: rotateY(180deg);-ms-transform: rotateY(180deg); -o-transform: rotateY(180deg);transform: rotateY(180deg);-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;-ms-backface-visibility:hidden; -o-backface-visibility:hidden; backface-visibility: hidden;}input.cmn-toggle-yes-no:checked + label:before{ -webkit-transform: rotateY(180deg);-moz-transform: rotateY(180deg); -ms-transform: rotateY(180deg);-o-transform: rotateY(180deg); transform:rotateY(180deg);}input.cmn-toggle-yes-no:checked+label:after{-webkit-transform: rotateY(0);-moz-transform:rotateY(0);-ms-transform:rotateY(0);-o-transform:rotateY(0);transform:rotateY(0);}.cmn-toggle{position:absolute;margin-left:-9999px;visibility:hidden;}.cmn-toggle+label{display:block;position:relative;cursor:pointer;outline:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.switch{padding:4px 2px 0px 50px;float:right;}#status{text-align:center}#status.success{font-color:#00a65a;}.app-box {display: block;height:44px;width:230px;float:left;border:1px solid #ccc}.app-box-icon { display:block; margin:2px;float:left; height:40px; width:100px;font-size: 18px; text-align: center; line-height: 40px; background: rgba(0, 0, 0, 0.2);}.app-box-content{padding:2px;}.temphum { text-align:center;}@media screen and (max-width:480px) {.app-box{display:blck;width:100%;float:none}.switch{float:right;}}</style>";
P(Page_end) = "</body></html>";

// Form-specific 
P(Form_css) = "<style type=\"text/css\">p{clear:both;width:100%;line-height:1.5em;}label{width:49%;text-align:right;display:inline-block;line-height:1.5em;float:left;margin-left:-1em;}select,input[type=\"text\"]{width:49%;text-align:left;float:right;}#c{text-align:left;float:left;margin-left:2em;}</style>"; 
P(Form_settings) = "<fieldset><legend>SharpNode Local</legend><div id=\"status\" class=\"success\">Want to control online? goto <b><a href=\"http://sharpnode.com/dashboard\">Dashboard</a></b></div><hr/><div class=\"app-box\"><span class=\"app-box-icon\">Switch-1</span><div class=\"app-box-content\"><div class=\"switch\"><input id=\"switch-one\" class=\"cmn-toggle cmn-toggle-yes-no\" type=\"checkbox\" onclick=\"bindSwiches('switch-one')\"><label for=\"switch-one\" data-name=\"Switch\"></label></div></div></div><div class=\"app-box\"><span class=\"app-box-icon\">Switch-2</span><div class=\"app-box-content\"><div class=\"switch\"><input id=\"switch-two\" class=\"cmn-toggle cmn-toggle-yes-no\" type=\"checkbox\" onclick=\"bindSwiches('switch-two')\"><label for=\"switch-two\" data-name=\"Switch\"></label></div></div></div><div class=\"app-box\"><span class=\"app-box-icon\">Switch-3</span><div class=\"app-box-content\"><div class=\"switch\"><input id=\"switch-three\" class=\"cmn-toggle cmn-toggle-yes-no\" type=\"checkbox\" onclick=\"bindSwiches('switch-three')\"><label for=\"switch-three\" data-name=\"Switch\"></label></div></div></div><div class=\"app-box\"><span class=\"app-box-icon\">Switch-4</span><div class=\"app-box-content\"><div class=\"switch\"><input id=\"switch-four\" class=\"cmn-toggle cmn-toggle-yes-no\" type=\"checkbox\" onclick=\"bindSwiches('switch-four')\"><label for=\"switch-four\" data-name=\"Switch\"></label></div></div></div><div class=\"app-box\"><span class=\"app-box-icon\">Switch-5</span><div class=\"app-box-content\"><div class=\"switch\"><input id=\"switch-five\" class=\"cmn-toggle cmn-toggle-yes-no\" type=\"checkbox\" onclick=\"bindSwiches('switch-five')\"><label for=\"switch-five\" data-name=\"Switch\"></label></div></div></div><div class=\"app-box\"><span class=\"app-box-icon\">Switch-6</span><div class=\"app-box-content\"><div class=\"switch\"><input id=\"switch-six\" class=\"cmn-toggle cmn-toggle-yes-no\" type=\"checkbox\" onclick=\"bindSwiches('switch-six')\"><label for=\"switch-six\" data-name=\"Switch\"></label></div></div></div><div style=\"clear:both\"></div><hr/><div class=\"temphum\"><b>Temp:</b> <span id=\"temp\"></span>&deg;C <b>Humidity:</b><span id=\"humd\"></span>%<br/><input id=\"btn-refresh\" type=\"button\" value=\"Refresh\" onclick=\"window.location.reload();\"></div></fieldset>";
P(Form_javascript1) = "<script type=\"text/javascript\">";
P(Form_javascript2) = "function setCookie(e){var t=new Date;t.setTime(t.getTime()+31536e6);var s='expires='+t.toGMTString();document.cookie='status='+e+'; '+s}function getCookie(e){for(var t=e+'=',s=document.cookie.split(';'),c=0;c<s.length;c++){for(var i=s[c];' '==i.charAt(0);)i=i.substring(1);if(0==i.indexOf(t))return i.substring(t.length,i.length)}return''}function switchBack(){var e=getCookie('status');e.length>0&&(e=JSON.parse(e)),switches.forEach(function(t){document.getElementById(t).checked=e[t],console.log(t+':'+e[t])}),document.getElementById('temp').innerHTML=temp,document.getElementById('humd').innerHTML=humd}function bindSwiches(e){var t,s,c;if(c=e,console.log(c),1==document.getElementById(c).checked)switch(c){case switches[0]:t='a',s='1';break;case switches[1]:t='b',s='1';break;case switches[2]:t='c',s='1';break;case switches[3]:t='d',s='1';break;case switches[4]:t='e',s='1';break;case switches[5]:t='f',s='1'}else switch(c){case switches[0]:t='a',s='0';break;case switches[1]:t='b',s='0';break;case switches[2]:t='c',s='0';break;case switches[3]:t='d',s='0';break;case switches[4]:t='e',s='0';break;case switches[5]:t='f',s='0'}var i={};switches.forEach(function(e){1==document.getElementById(e).checked?i[e]=!0:i[e]=!1}),i=JSON.stringify(i),setCookie(i);var n='<form id=\"dynForm\" method=\"POST\"><input type=\"hidden\" name=\"'+t+'\" value=\"'+s+'\"></form>';document.body.innerHTML+=n,document.getElementById('dynForm').submit()}window.onload=function(e){switchBack()};var switches=['switch-one','switch-two','switch-three','switch-four','switch-five','switch-six'];";
P(Form_javascript3) = "</script></head><body>";

// Fail page 
P(Fail_message) = "<p>Failed to send switch command</p>";

void web_fail(WebServer &server, WebServer::ConnectionType type, char *, bool); 
void web_index(WebServer &server, WebServer::ConnectionType type, char *, bool); 

// Temperature and Humidity (Assign appropriate function to set actual value) 
int temp = 32;
int humd = 56; 

// Lib instantiate
void dht_wrapper();
PietteTech_DHT DHT(DHTPIN, DHTTYPE, dht_wrapper);
void dht_wrapper() { DHT.isrCallback(); }

// name the pins
const int pinCount = 6;
const int ctrlPin[pinCount] = { A2, A3, A4, A5, A6, A7 };
const int statPin[pinCount] = { D2, D3, D4, D5, D6, D7 };

//status variables
//int s[pinCount] = { 0, 0, 0, 0, 0, 0 };
int status;

//PIR variables
const int inputPin = A0;
int motionCounter = 0;
bool available;

void setup() 
{
  WiFi.on();
  WiFi.connect();
  waitUntil(WiFi.ready);  
    
  bool mdns_success = mdns.setHostname("sharpnode");

  if(mdns_success) 
  {
    mdns.addService("tcp", "http", 80, "SharpNode Local");
    mdns.begin();
  }

  webserver.setDefaultCommand(&web_index);
  webserver.setFailureCommand(&web_fail);
  webserver.addCommand("index.html", &web_index); 
  webserver.begin();

  //Register our Particle function here
  Particle.function("led", ledControl);
  Particle.function("sharpnode", sharpnode);
  // PArticle variables
  Particle.variable("status", status);

  for (int i=0; i < pinCount; i++) 
  {
    pinMode(ctrlPin[i], OUTPUT);
    pinMode(statPin[i], INPUT_PULLDOWN);
    //digitalWrite(ctrlPin[i], EEPROM.read(pinCount-i-1)); // why does this have to be the other way round??
    digitalWrite(ctrlPin[i], EEPROM.read(i));
  }
  pinMode(RX, OUTPUT);
  digitalWrite(RX, EEPROM.read(6));

  Particle.connect();   
  waitFor(Particle.connected, 30000); // wait up to 30sec for cloud
}

void loop()
{
  static uint32_t msLastConnect;
  static int prevPIR = LOW;
  int currPIR = digitalRead(inputPin);
  int web_len = 64;
  char web_buff[web_len];

  if (!Particle.connected() && millis() - msLastConnect > 60000) // only try connect once per minute 
  {
    msLastConnect = millis();
    Particle.connect();
  }

  mdns.processQueries();
  webserver.processConnection(web_buff, &web_len);
 
  if (currPIR != prevPIR) // only react on state change
  { 
    prevPIR = currPIR;
    if(currPIR)
    {
      motionCounter++;    // increment motion counter
    
      available = (motionCounter <= 2);

      if(Particle.connected()) 
      {
        if (available)
        {
          Particle.publish("motion_nodetect","Motion not detected!"); //publish to conf_avail webhook
        }      
        else
        {
          Particle.publish("motion_detect","Motion Detected!"); //publish to conf_inuse webhook
          motionCounter = 2; // reset motion counter
        }
        delay(1000); // to ensure publish rate limit
      }
    }
  }
}

//Sharpnode fn consists temp, humidity and IP

int sharpnode(String string){
  // Check if we need to start the next sample
  if (string == "start") 
  {
    status = 0;
    for(int i = 0; i < pinCount; i++)
    {
      //s[i] = 1+digitalRead(statPin[i]); // LOW = 1 (1+0)  HIGH = 2 (1+1)
      // why 1 & 2 instead of 0 & 1? %06d would also show leading zeroes
      status += digitalRead(statPin[i]) * pow(10, i); 
    }
    //status = (s6*100000)+(s5*10000)+(s4*1000)+(s3*100)+(s2*10)+(s1*1); //RMD tells relay1 status & LMD tells relay6 status
      
    DHT.acquire();
    uint32_t ms = millis();
    while(DHT.acquiring() && millis() - ms < 100) Particle.process();	// has sample completed?
    if (!DHT.acquiring())
    {
      char buf[128];
      snprintf(buf, sizeof(buf), "{ \"status\":\"%06d\",\"temperature\":\"%.2f\",\"humidity\":\"%.2f\", \"ip\":\"%s\"}", status, DHT.getCelsius(), DHT.getHumidity(), WiFi.localIP().toString().c_str());
      Particle.publish("sharpnode1", buf, PRIVATE); 
      return 1; // signal success
    }
    else
    {
      return 0; // signal fail
    }
  }   
  return -1; // signal wrong command
  // always return useful information - makes debugging easier ;-)
}

// This function gets called whenever there is a matching API request
// the command string format is l<led number>,<state>
// for example: l1,HIGH or l1,LOW
//              l2,HIGH or l2,LOW
int ledControl(String command)
{
   int state = 0;
   int pinNumber = (command.charAt(1) - '0') - 1;
   //Sanity check to see if the pin numbers are within limits
   if (pinNumber < 0 || pinNumber > 7) return -1;

   // find out the state of the led
   if(command.substring(3,7) == "HIGH") state = 1;
   else if(command.substring(3,6) == "LOW") state = 0;
   else return -1;
 
   return eepromWrite(pinNumber,state);
}

int eepromWrite(int pinNumber,int state)
{
  switch(pinNumber) //pinNumber will impact on pin
  {
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
      EEPROM.write(pinNumber,state);
      //digitalWrite(ctrlPin[pinCount-pinNumber-1],state);  // why wrong way round?
      digitalWrite(ctrlPin[pinNumber],state);
      break;
        
    case 6:
      EEPROM.write(6,state);
      digitalWrite(RX,state); //for PIR ON/OFF
      break;

    //case 7:
    //  EEPROM.write(10,state);
    //  digitalWrite(pinNumber,state);
    //  break;

    default:
      return -1; // signal fail (wrong pinNumber)
  }
  return pinNumber; // signal success
}

// index.html 
void web_index(WebServer &server, WebServer::ConnectionType type, char *, bool) 
{   
  // variable type and size define
  URLPARAM_RESULT rc;
  char name[POST_NAME_LENGTH];
  char value[POST_VALUE_LENGTH];

  server.httpSuccess();
  server.printP(Page_start); 
  server.printP(Page_css);
  server.printP(Form_css);
  server.printP(Form_javascript1); 
  server.printP("var temp=\""+String(temp)+"\";"); 
  server.printP("var humd=\""+String(humd)+"\";"); 
  server.printP(Form_javascript2); 
  server.printP(Form_javascript3); 
  server.printP(Form_settings);
  server.printP(Page_end); 

  // read first parameter and thier value (Loop removed to work fast)
  server.readPOSTparam(name, POST_NAME_LENGTH, value, POST_VALUE_LENGTH); 
  int pinNr = name[0] - 'a'; // take first character as index 
    
  eepromWrite(pinNr, atoi(value)); 
}

// Bad requests
void web_fail(WebServer &server, WebServer::ConnectionType type, char *, bool) 
{
  server.httpFail();

  server.printP(Page_start);
  server.printP(Fail_message);
  server.printP(Page_end);
}

I’ve taken the liberty to flip the order in which the data is stored in EEPROM, since I don’t see much point in having the order inverted between pin numbers and byte position of the respective data.
I’m also not sure, why you’re using motionCounter = 2 and motionCouter <= 2 - the way I understood the code, there is not much difference to a mere boolean since I’d only ever expect to see 2 or 3 as the possible values for motionCounter (apart from the two very first motion events) - there never is a reset to 0 and you only increment from 2 to 3 and the reset to 2.

Hi @ScruffR,

Thank you so much. I try the code and revert you back soon.

I used motionCounter = 2 and motionCounter <= 2 because when my PIR sensors gets power, it gives two HIGH value during initialization. So, I want to ignore these values. After that if PIR detects any motion then it’ll be correct value.

For that I’d rather use a timeout in setup()

Hi @ScruffR,

Code is working smoothly according to what I want.

You always help me. Thanks a lot :relaxed:

Yes, I’ll also use timeout for that.

1 Like