Looking for some help to control spark via web

Hey @bko,

I think that would work as well. The original sketch at the beginning of this thread does exactly what I need. I guess the option I’m looking for that I cannot seem to figure out is how to make a web page that would allow me to change the program times and duration so I don’t have to go into the programmer every time I want to make a change.

OK–I see and that is also on my todo list.

Hey @bko, just thought I’d see how your alarm clock tutorial was coming along. I’ll have all the parts I need tomorrow to assemble my project. It runs fine with the current code, just looking for more flexibility.

Hi @james211

Sorry I got a little busy. I will try to whip something up this week.

No problem @bko, I wasn’t trying to put pressure on you, more of a friendly reminder.

All my best…

2 Likes

On a side note, if I wanted to have a surface mount an LED to show the status of the spark core on the enclosure, just as the core itself has on it, how would I go about that? I noticed that the little blue LED is connected to pin D7, but I couldn’t find out if there is a way to push that status to another pin.

Any chance you've been able to write something up already? Your library has been running successfully ever since you released it. Would be neat if you could show us a way to set the alarms externally. (No pressure though).
Thanks in advance for all your efforts, you're a great help for the whole community. Much appreciated!

I am working on it now!

2 Likes

OK guys I have something working, but it is not quite where I wanted it to be. I wanted to have the current values automatically refresh from the core, but it is just not stable and I don’t know what is wrong. So for now, you have to hit a Refresh button to get the current time and duration from the core. I will look at publishing the current settings instead of using variables, I guess. Also I decided that the seconds field could just always be zero, but feel free to change that.

I made one other important change to the firmware. In the previous version there was no way to turn off a channel. Now if you set the duration to zero, that returns it to the “not set” state and it turns the relay off. It will show as 00:00:00*0 in the display.

Here is the updated firmware–I had to rearrange the code since I think the preprocessor for the webIDE changed with the recent update.

// This #include statement was automatically added by the Spark IDE.
#include "SparkTime/SparkTime.h"

UDP UDPClient;
SparkTime rtc;

int relayPin[4] = {D0,D1,D2,D3};

//By relays number
bool    isSet[4] = {false,false,false,false};
uint8_t startHours[4]   = {0,0,0,0};
uint8_t startMinutes[4] = {0,0,0,0};
uint8_t startSeconds[4] = {0,0,0,0};
unsigned int duration[4] = {0,0,0,0};

unsigned long stopTime[4] = {0,0,0,0}; 

#define NCHARS 32
char relayStr[4][NCHARS];

// Parse the format: 08:56:05*6000 or 18:59:00*10
//  hh:mm:ss*duration
int parseTimeDuration(String command, int relay) {
    char copyStr[33];
    command.toCharArray(copyStr,33);
    char *p = strtok(copyStr, ":");
    
    startHours[relay]   = (uint8_t)atoi(p);
    p = strtok(NULL,":");
    startMinutes[relay] = (uint8_t)atoi(p);
    p = strtok(NULL,":");
    startSeconds[relay] = (uint8_t)atoi(p);
    p += 3;
    duration[relay]     = atoi(p);
    if (duration[relay]!=0) {
        isSet[relay] = true;
    } else {  // duration zero means clear all
        isSet[relay] = false;
        startHours[relay] = 0;
        startMinutes[relay] = 0;
        startSeconds[relay] = 0;
        digitalWrite(relayPin[relay],LOW);  // turn off
    }
    sprintf(relayStr[relay], "%02d:%02d:%02d*%d",startHours[relay],startMinutes[relay],startSeconds[relay],duration[relay]);
    return 1;
}

int setRelay1(String command) {
    return parseTimeDuration(command, 0);
}
int setRelay2(String command) {
    return parseTimeDuration(command, 1);
}
int setRelay3(String command) {
    return parseTimeDuration(command, 2);
}
int setRelay4(String command) {
    return parseTimeDuration(command, 3);
}

void setup()
{
   for(int relay=0;relay<4;relay++) {
       pinMode(relayPin[relay], OUTPUT);
       digitalWrite(relayPin[relay], LOW);
       strcpy(relayStr[relay], "Not set");
   }
   
   Spark.function("setRelay1", setRelay1);
   Spark.function("setRelay2", setRelay2);
   Spark.function("setRelay3", setRelay3);
   Spark.function("setRelay4", setRelay4);
   
   Spark.variable("getRelay1", relayStr[0], STRING);
   Spark.variable("getRelay2", relayStr[1], STRING);
   Spark.variable("getRelay3", relayStr[2], STRING);
   Spark.variable("getRelay4", relayStr[3], STRING);

rtc.begin(&UDPClient, "pool.ntp.org");
rtc.setTimeZone(-5); // gmt offset
rtc.setUseDST(true);

}

void loop() {

unsigned long currentTime = rtc.now();

for(int relay=0;relay<4;relay++) {
    if (TRUE==isSet[relay]) {
        if (rtc.hour(currentTime)==startHours[relay] &&
            rtc.minute(currentTime)==startMinutes[relay] &&
            rtc.second(currentTime)==startSeconds[relay]) {
                digitalWrite(relayPin[relay],HIGH);
                stopTime[relay] = currentTime + duration[relay];
            } // start time
    } // is set
    
    if (currentTime >= stopTime[relay]) {
        digitalWrite(relayPin[relay],LOW);
    }
    
}

delay(100);

}

Here’s the HTML file–don’t forget to add your device-id and access-token in the file before you save it! Like all these web page demos, you need to keep your access token private, so don’t go putting this on the Internet. If you need a server type solution, check out the simple PHP proxy thread.

<!DOCTYPE HTML>
<html>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript" charset="utf-8">  </script>
<body>
  <P>Relay 1: Current setting <span id="relay1">unknown</span>
    <button id="getR1" onclick="getRelay(1)">Refresh</button>
    &nbsp;&nbsp;&nbsp;&nbsp;New setting
    <select id="relay1hours">
    </select> :
    <select id="relay1minutes">
    </select>  
    Duration
    <input type="text" id="relay1duration" value="10">  seconds
    <button id="setR1" onclick="setRelay(1)">Set</button>

  <P>Relay 2: Current setting <span id="relay2">unknown</span>
    <button id="getR2" onclick="getRelay(2)">Refresh</button>
    &nbsp;&nbsp;&nbsp;&nbsp;New setting
    <select id="relay2hours">
    </select> :
    <select id="relay2minutes">
    </select>  
    Duration
    <input type="text" id="relay2duration" value="10">  seconds
    <button id="setR2" onclick="setRelay(2)">Set</button>

  <P>Relay 3: Current setting <span id="relay3">unknown</span>
    <button id="getR3" onclick="getRelay(3)">Refresh</button>
    &nbsp;&nbsp;&nbsp;&nbsp;New setting
    <select id="relay3hours">
    </select> :
    <select id="relay3minutes">
    </select>  
    Duration
    <input type="text" id="relay3duration" value="10">  seconds
    <button id="setR3" onclick="setRelay(3)">Set</button>

  <P>Relay 4: Current setting <span id="relay4">unknown</span>
    <button id="getR4" onclick="getRelay(4)">Refresh</button>
    &nbsp;&nbsp;&nbsp;&nbsp;New setting
    <select id="relay4hours">
    </select> :
    <select id="relay4minutes">
    </select>  
    Duration
    <input type="text" id="relay4duration" value="10">  seconds
    <button id="setR4" onclick="setRelay(4)">Set</button>


    <script type="text/javascript">

    for(i=0;i<24;i++) {
		      for(relay=1;relay<5;relay++) {
						   var rh = document.getElementById("relay"+relay.toString()+"hours");
						   var opt = document.createElement("OPTION");
						   opt.setAttribute("value", i.toString() );
						   var nd  = document.createTextNode(i.toString());
						   opt.appendChild(nd);
						   rh.appendChild(opt);
						   }
		      }
		
    for(i=0;i<60;i++) {
		      for(relay=1;relay<5;relay++) {
						   var rm = document.getElementById("relay"+relay.toString()+"minutes");
						   var opt = document.createElement("OPTION");
						   opt.setAttribute("value", i.toString() );
						   var nd  = document.createTextNode(i.toString());
						   opt.appendChild(nd);
						   rm.appendChild(opt);
						   }
		      }

      var deviceID    = "<<device id here>>";
      var accessToken = "<<access token here>>";
      var setFunc = "setRelay";
      var getFunc = "getRelay";

      function getRelay(relay) {
	     requestURL = "https://api.spark.io/v1/devices/" + deviceID + "/" + getFunc + relay.toString() + "/?access_token=" + accessToken;
             $.getJSON(requestURL, function(json) {
                 document.getElementById("relay"+relay.toString() ).innerHTML = json.result;
                 });
      }

      function setRelay(relay) {
        var newValue = document.getElementById("relay"+relay+"hours").value   + ":" +
                       document.getElementById("relay"+relay+"minutes").value + ":" +
                                                                         "00" + "*" +
                       document.getElementById("relay"+relay+"duration").value;
	var requestURL = "https://api.spark.io/v1/devices/" +deviceID + "/" + setFunc + relay + "/";
        $.post( requestURL, { params: newValue, access_token: accessToken });
      }

    </script>
</body>
</html>
2 Likes

@bko, thank you for starters! Second, when I compile the code, I get this error:

In file included from ../inc/spark_wiring.h:30:0,

                   from ../inc/application.h:29,
                   from /SparkTime.h:4,
                   from /SparkTime.cpp:31:
  ../../core-common-lib/SPARK_Firmware_Driver/inc/config.h:12:2: warning: #warning "Defaulting to Release Build" [-Wcpp]
   #warning  "Defaulting to Release Build"
    ^
  In file included from ../inc/spark_wiring.h:37:0,
                   from ../inc/application.h:29,
                   from /SparkTime.h:4,
                   from /SparkTime.cpp:31:
  ../inc/spark_wiring_ipaddress.h: In member function 'IPAddress::operator uint32_t()':
  ../inc/spark_wiring_ipaddress.h:53:52: warning: dereferencing 

type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

    operator uint32_t() { return *((uint32_t*)_address); };
                                                      ^
  ../inc/spark_wiring_ipaddress.h: In member function 'bool IPAddress::operator==(const IPAddress&)':
  ../inc/spark_wiring_ipaddress.h:54:72: warning: dereferencing 

type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

    bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); };
                                                                          ^
  ../inc/spark_wiring_ipaddress.h:54:105: warning: dereferencing 

type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

    bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); };
                                                                                                           ^
  In file included from ../inc/spark_wiring.h:30:0,
                   from ../inc/application.h:29,
                   from /time.cpp:2:
  ../../core-common-lib/SPARK_Firmware_Driver/inc/config.h:12:2: warning: #warning "Defaulting to Release Build" [-Wcpp]
   #warning  "Defaulting to Release Build"
    ^
  /time.cpp:2:33: fatal error: SparkTime/SparkTime.h: No such file or directory
   #include "application.h"
                                   ^
  compilation terminated.
  make: *** [/time.o] Error 1

Hi @james211

I think you need to add my SparkTime library your project.

I’m pretty sure I have it there…I just pasted the new firmware into the one I was previously using. Here is a screen shot. I’ll try it again just in case.

I figured it out…for some reason the #include statement in the beginning was changing to:

#include "SparkTime/SparkTime.h"

@bko, you are a brilliant man. Thank you for doing this.

2 Likes

Out of curiosity, is the spark core capable of running a web server? In my case I run OS X server at home so its not an issue. But for someone who doesn’t have a web server at home, it would be cool if you could run this whole program as an all inclusive server package that is only accessible on the LAN network.

Yes, you can run a simple web server on the Spark core and there are quite a few examples here in the forum.

Any one specific example I should look at? Is the html code written the same as what you put together or is a bit different?

Start here:

So I managed to get the Tiny Webserver running with the firmware you wrote. Where can I find info about how to structure the html properly for the tiny webserver to serve it? If its too much work I’m not going to worry about it. Hopefully the SD option will be available soon and it will make using standard html a bit easier.

This is what I did, is this correct?

// This #include statement was automatically added by the Spark IDE.
#include "SparkTime.h"

UDP UDPClient;
SparkTime rtc;
TCPClient webClient;
TCPServer webServer = TCPServer(80);
char myIpAddress[24];

int relayPin[4] = {D0,D1,D2,D3};

//By relays number
bool    isSet[4] = {false,false,false,false};
uint8_t startHours[4]   = {0,0,0,0};
uint8_t startMinutes[4] = {0,0,0,0};
uint8_t startSeconds[4] = {0,0,0,0};
unsigned int duration[4] = {0,0,0,0};

unsigned long stopTime[4] = {0,0,0,0}; 

#define NCHARS 32
char relayStr[4][NCHARS];

// Parse the format: 08:56:05*6000 or 18:59:00*10
//  hh:mm:ss*duration
int parseTimeDuration(String command, int relay) {
    char copyStr[33];
    command.toCharArray(copyStr,33);
    char *p = strtok(copyStr, ":");

    startHours[relay]   = (uint8_t)atoi(p);
    p = strtok(NULL,":");
    startMinutes[relay] = (uint8_t)atoi(p);
    p = strtok(NULL,":");
    startSeconds[relay] = (uint8_t)atoi(p);
    p += 3;
    duration[relay]     = atoi(p);
    if (duration[relay]!=0) {
        isSet[relay] = true;
    } else {  // duration zero means clear all
        isSet[relay] = false;
        startHours[relay] = 0;
        startMinutes[relay] = 0;
        startSeconds[relay] = 0;
        digitalWrite(relayPin[relay],LOW);  // turn off
    }
    sprintf(relayStr[relay], "%02d:%02d:%02d*%d",startHours[relay],startMinutes[relay],startSeconds[relay],duration[relay]);
    return 1;
}

int setRelay1(String command) {
    return parseTimeDuration(command, 0);
}
int setRelay2(String command) {
    return parseTimeDuration(command, 1);
}
int setRelay3(String command) {
    return parseTimeDuration(command, 2);
}
int setRelay4(String command) {
    return parseTimeDuration(command, 3);
}

void setup()
{
    Spark.variable("ipAddress", myIpAddress, STRING);
    IPAddress myIp = Network.localIP();
    sprintf(myIpAddress, "%d.%d.%d.%d", myIp[0], myIp[1], myIp[2], myIp[3]);

    webServer.begin();
    
   for(int relay=0;relay<4;relay++) {
       pinMode(relayPin[relay], OUTPUT);
       digitalWrite(relayPin[relay], LOW);
       strcpy(relayStr[relay], "Not set");
   }

   Spark.function("setRelay1", setRelay1);
   Spark.function("setRelay2", setRelay2);
   Spark.function("setRelay3", setRelay3);
   Spark.function("setRelay4", setRelay4);

   Spark.variable("getRelay1", relayStr[0], STRING);
   Spark.variable("getRelay2", relayStr[1], STRING);
   Spark.variable("getRelay3", relayStr[2], STRING);
   Spark.variable("getRelay4", relayStr[3], STRING);

rtc.begin(&UDPClient, "pool.ntp.org");
rtc.setTimeZone(-5); // gmt offset
rtc.setUseDST(true);

}



void loop() {

unsigned long currentTime = rtc.now();

for(int relay=0;relay<4;relay++) {
    if (TRUE==isSet[relay]) {
        if (rtc.hour(currentTime)==startHours[relay] &&
            rtc.minute(currentTime)==startMinutes[relay] &&
            rtc.second(currentTime)==startSeconds[relay]) {
                digitalWrite(relayPin[relay],HIGH);
                stopTime[relay] = currentTime + duration[relay];
            } // start time
    } // is set

    if (currentTime >= stopTime[relay]) {
        digitalWrite(relayPin[relay],LOW);
    }
    
    if (webClient.connected() && webClient.available()) {
        serveWebpage();
    }
    else {
        webClient = webServer.available();
    }
}
}

void serveWebpage() {
    //TODO: read in the request to see what page they want:
    //TODO: retrieve larger content from flash?

    webClient.println("<html>I'm serving a webpage!</html>\n\n");
    webClient.flush();
    webClient.stop();
    delay(100);
}

Hi Brian (or any other kind people of the internet that are reading this),

Alas, either I’m not understanding how to use the SPARTIME library, or maybe I’ve implemented your example above (in post 29) wrong?

…but in any case I can’t seem to make your example work (and yes, I included the SPARKTIME library in my app).

Question 1:
What values could/should I put into the web interface side (hours, minutes, duration) to simply turn D0 on?..and just leave D0 on (HIGH) for awhile (let’s say for at least 10 minutes). Note that I put your html out on a web site/page that I have.

Question 2:
Could I perhaps use jflasher utility (http://jflasher.github.io/spark-helper/) to do a simple post to set the hrs, min, duration (after I flash your code to my core of course)?..assume the function would be “setRelay1”, but not sure of the string/values that would follow?

In any event, I’ve tried every combo of values in the web “New setting” (hrs, min) and “Duration” text boxes I can think of and nothing happens when click Set,
…but I can see the GET’s and POSTs in the Chrome console communicating with the core,
…and the Refresh button does retrieve values that were set,
So I’m fairly certain the app seems to be working?

I really like your post here regarding the relay board using your SPARKTIME library as I too am interested in creating a sprinkler controller…but again can’t seem to make it work (the red light on the relay board never comes on…no click of the relay. etc.)

Note that I can make my relay board work with Tinker,
…and also can make the relay board work by flashing a simple ino function to my relay board that simply sets D0 high or low and calling the function with a copy of the jflasher utility (http://jflasher.github.io/spark-helper/ which I shamelessly copied/downloaded and then FTP’d out on my on website) so I know that my web site is working…at this point I’m controlling a low-voltage light via the relay board with my phone (from my web site).

any advice suggestions how to make your example work, or how other examples to simply incorporate SPARKTIME library would be mucho appreciated

thanks in advance
Forrest