Posted on
Tue Sep 02, 2014 5:11 pm
kw123 offline
User avatar
Posts: 6340
Joined: May 12, 2013
Location: Dallas, TX

this thing looks really promising, and is very small ~ 1/8 of an arduino: ~1"x0.5"

8 analog i/o's
8 digital i/o's
with build in wifi.
nice development interface.
takes 30 seconds to make the led blink. Things after that need a bit more tinkering ..

you can add an arduino shield to make it pin compatible
(the programming language looks VERY similar to an arduino., but not the same.)

for $39 + USB power supply +box+cables.. you could connect:
temp sensor, open close sensor, 16 channel alarm system,...

+ some relays you could do a sprinkler (self sufficient, running its own scheduled program... and only overwrite if you want to change it.)
still needs some programming, plugin etc .. some more weeks..

spark2.JPG (113.33 KiB) Viewed 1738 times

Posted on
Mon Sep 15, 2014 9:56 pm
kw123 offline
User avatar
Posts: 6340
Joined: May 12, 2013
Location: Dallas, TX


Is anyone interested in a plugin to
- configure input / output channels from indigo as devices / states
- read / write data from to the device

if nobody is interested, I will set it up very simple for myself ... writing a plugin fully configurable is a bit of work..



Posted on
Wed Sep 17, 2014 2:49 pm
berkinet offline
User avatar
Posts: 3210
Joined: Nov 18, 2008
Location: Berkeley, CA, USA & Mougins, France


Karl, they look very interesting... But, I will wait until I am back in the states in November to get a couple to play with. After that, if they seem useful, and, if you have some scripts working, maybe we can work together on a plugin.

Posted on
Wed Sep 17, 2014 6:01 pm
kw123 offline
User avatar
Posts: 6340
Joined: May 12, 2013
Location: Dallas, TX


I would like to design a general SPARK program that can be downloaded onto the spark, where only IPnumber etc and spark ID etc has to be configured by the user.
Then an indigo plugin can send commands to reconfigure the pins to be input or output pins
After that the plugin can write to pins, set voltage level or read pins as input.

Major challenge: can we dynamically configure / reconfigure the pins not in the initialization section, but within the main loop of the spark

If you order one, I would go with the one that has the option to add a wifi antenna(*). The build-in antenna is a little bit weak = range is not to good.
(*) need to be ordered separately.

My first project would be to "press buttons" on a ceiling fan remote handheld unit with the spark. The Insteon ceiling fan unit is not 100% compatible with all ceiling fans.


Posted on
Thu Oct 02, 2014 9:22 pm
kw123 offline
User avatar
Posts: 6340
Joined: May 12, 2013
Location: Dallas, TX


the attached code configures the such that you can defined the functions of the pins as input/output .. via e.g. curl commands e.g. set pin A1 to input mode and then read value of pin A1 every second.

Next step will be to setup the plugin so that you can do it from within indigo.

This will enable to have a device with 16 input/output channels for $40. (need to still add the input/output devices like relays, themometers ...)
One application could be a sprinkler system with 15 valves and a sensor for rain

This is still very much experimental, but if someone likes to try it and give feedback...


The attached code for a spark chip works stable for > 1 day without interruption... (after 3 weeks of fiddling). BUT NO GUARANTEE

copy the attached code into your spark module and load it. then use as described in the code
Code: Select all
// set / read /write to/from pins using local network only, not through the cloud
// can set modus of pins to INPUT,OUTPUT, INPUT_PULLDOWN, INPUT_PULLUP
// call:
// (1)  Mode function
// structure: {Mode=A/D-Pin#,Function&)
// curl -d{M=D1,I&} --max-time 3 192.x.y.x    sets mode of D1 to INPUT  ;returns: {status=OK}
// curl -d{M=D1,O&} --max-time 3 192.x.y.x    sets mode of D1 to OUTPUT  ;returns: {status=OK}
// curl -d{M=A2,I&} --max-time 3 192.x.y.x    sets mode of A2 to INPUT  ;returns: {status=OK}
// curl -d{M=A2,O&} --max-time 3 192.x.y.x    sets mode of A2 to OUTPUT  ;returns: {status=OK}
// curl -d{M=A2,D&} --max-time 3 192.x.y.x    sets mode of A2 to INPUT_PULLDOWN ;returns: {status=OK}
// curl -d{M=A2,U&} --max-time 3 192.x.y.x    sets mode of A2 to INPUT_PULLUP ;returns: {status=OK}

// structure: {M=Reboot&)
// curl -d{M=R&} --max-time 3 192.x.y.x   ;returns: nothing, just reboots
// structure: {M=Log0/1/3/4/5&)
// curl -d{M=L1&   or 2/3/4/5&} --max-time 3 192.x.y.x  ;returns {status=OK} set Logging on --> write to serial details depending on logging level
// curl -d{M=L0&} --max-time 3 192.x.y.x  ;returns {status=OK} set Logging off

// (2)  read / write function
// structure: {Read/Write=A/d-Pin#,Value&)
// curl -d{R=A2&} --max-time 3 192.x.y.x      read A2 value ;returns: {R=A2=123&status=OK}  if 123 is the value read
// curl -d{W=A2,200&} --max-time 3 192.x.y.x  writes 200 to pin A2 ;returns: {status=OK}
// curl -d{W=D2,1&} --max-time 3 192.x.y.x  writes 1 to pin D2 ;returns: {status=OK}

// error checking
// checks for pinNumber <0 >7, Mode not I O D U, value <0  and >1/255 (Digital/Analog)
// checks if '=' in 2. position
// checks if mode function=I,O,D,R,L
// checks if function =M,R,W
//  commands can be combined, up to 255 bytes long, after that its ignored, eg:
// curl -d{M=D1,I&W=D2,1&W=A2,200&} --max-time 3 192.x.y.x   ;returns: {status=OK}

//  !!  last characters  in subitted data must be "&}"  !!! if you omit the last & the last comamnd is ignored!!!

// pressing mode button puts spark into Spark.connect() for 30 seconds, but suspends server traffic for that time.
//    use this mode to update the code..

// the current values / modes of the pins will be stored in the eprom so that after a reboot they are present and will be set to the old values.

// Karl Wachs
// V 0.4.2
// Oct 2 2014

// next additions:
//  add average read (read same pin x times, y msec apart to average out wobbles), idea to do 4 reads for 20 msecs then average
//  add moment switch set pin ON for x seconds, then off , requires to store command and execute the off later, needs new structure

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

TCPServer server = TCPServer(80);
TCPClient client;

// storage
char inBuf[256];                // input buffer
char outBuf[256];               // this will be send back
char errMsg[12];                // stores error message
int numberOfChar =0;            // to protect inBuf < 255

bool comandFound=false;         // true if comamnd found

bool serverStarted=false;       // true once server.connect()
int sparkUP=99000;              //  millisecs since last spark.connect() disconnect spark site immediately

int Logging=1;

// ping vars
int npings =1;                  // number of pings send
int npongs =0;                  // number of pongs gotten back from pings
int noPing =0;                  // how often in a row no pings returned
IPAddress xxx(192,168,1,6);     // this is the router ip number to be pinged
IPAddress IPN( xxx );           // the ip number as used in
bool enbableRSSI=true;          // once we got RSSI = 2 back do not use anymore
int rssiBad=0;                  // bad signal, reboot after x tries

// millisec vars
unsigned long lastLoop=0;       // millesecs since last loop checks (ping ...)
unsigned long loopWait=1000;    // wait to check pings etc, changed dynamically depending on ping results.
unsigned long now =0;           // used to store last millis()

unsigned long loopCounter =0;   // simply count loops

// helper vars
char cmd[20];                   // here we stre each command sequentially
char c =' ';                    // to store a single char
int value=0;                    // integer value to be written to pings, read from pins
char valueText[6];              // text value of value
bool isDigital =false;          // is it D or A pin

int retCode=0;                  // indicate errors !=0 error

extern char* itoa(int a, char* buffer, unsigned char radix);
                                                            // 0-7 digital, 10-17 = analog, 
byte modeOfPin[18] ={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};  // 0=input,1=output,2=input_pullDown,3 input_PULLUP
int  valueOfPin[18]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};  // output value

void execCMD(){                                             // this module executes the requested commands  called one by one
        retCode =0;
        int pinNumber = cmd[3]-48;                          // 0 = 48 ascii
        if (pinNumber >7){
            pinNumber =7;
        if (pinNumber <0){
            pinNumber =0;
        if (cmd[2]=='D'){                                       // digital ir analog pins
            pinNumber +=10;                                     // analog pins =10...17
        if (cmd[0]=='M'){                                       // function = Mode
            if (cmd[5]=='I'){                                   // set to input mode
                pinMode(pinNumber, INPUT);
                modeOfPin[pinNumber] =0;
            if (cmd[5]=='O'){                                   // output
                pinMode(pinNumber, OUTPUT);       
                modeOfPin[pinNumber] =1;
            if (cmd[5]=='D'){                                   // INPUT_PULLDOWN
                pinMode(pinNumber, INPUT_PULLDOWN);
                modeOfPin[pinNumber] =2;
            if (cmd[5]=='U'){                                   // INPUT_PULLUP
                pinMode(pinNumber, INPUT_PULLUP);
                modeOfPin[pinNumber] =3;

            if (cmd[2]=='L'){                                   // Logging
                if (pinNumber != 10){
                    Logging =pinNumber-10;                      //  logging level  stored as analog pinNumber (-10 --> 0....9)
                    Serial.print("set logging to level: ");
                    Logging =0;
                    Serial.println("set logging off");
            if (cmd[2]=='R'){                                   // reboot requested
                if (Logging>0){Serial.print("===reboot requested===");}
                NVIC_SystemReset();                             // this resets the system

        if (cmd[0]=='W'){                                       // funtion = write
            value = atoi(cmd+5);                                // make an integer out of the value
            if (value <0){
                value =0;
            valueOfPin[pinNumber]= value;
            if (isDigital)  {
                if (value >1){
                    value =1;
                    digitalWrite(pinNumber, value);             // write to pin after error checking
            else {
                if (value >255){
                    value =255;
                    analogWrite(pinNumber, value);              // write to pin after error checking
        if (cmd[0]=='R'){                                       // read from pin
            if (isDigital)  {
                value =digitalRead(pinNumber);
            else            {
                value =analogRead(pinNumber);
            itoa(value,valueText,10);                           // convert integer to text
            strcat(outBuf,cmd);                                 // compose the return string with the value read eg R=A1,4301
            strcat(outBuf,"&");                                 // dont forget the & at the end
        if (!comandFound){                                      // no valid function found
            retCode =-6;

void parseALL(){                                                // this module parses all commands  and then if ok calls execCMD for each CMD

        for (int i=0;i<12;i++){errMsg[i]='\0';}
        for (int i=1;i<256;i++){outBuf[i]='\0';}
        outBuf[0]='{';                                          // return string is { ....}
        retCode =0;
        char *start=inBuf;
        char *amp = strchr(inBuf,'&');   
        while (amp!=NULL ){
            if (retCode !=0){ break;}                           // on error stop next commands
            for (int i=0;i<20;i++){cmd[i]='\0';}

            if (Logging>2){ Serial.print("cmd:"); Serial.println(cmd); }

            if (cmd[1] == '='){ execCMD(); }                    // here we execute the command
            else{ strcpy(errMsg,"no = sign"); retCode =-10; }   // bad command string
            start=amp+1;                                        // get pointers for next command ... do not forget to end with& otherwise last cmd is ignored
            amp = strchr(amp+1,'&');

        if (strlen(errMsg)==0){strcat(outBuf,"status=OK}");}    // all fine
        else{                                                   // compose eror msg
                                                                // send results / status back
        server.print("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: ");  // header
        server.println(strlen(outBuf));                                                   // number of bytes send back
        server.print("\r\n");                                                             // header has to end with empty line
        server.println(outBuf);                                                           // here the msg back


void setup(){
                                                                // Make sure your Serial Terminal app is closed before powering your Core
        Logging =;

        if (Logging>0){
            Serial.println("Version 0.4.2");
            Serial.println("restoring modes and value of pins Mode =0/1/2/3 in/out/inp_pullDOWN/ipn/pullUP values =0.1 0..255 ");
        for (int i=0; i<18;i++){
            if (i==8 || i==9 ){continue;}
            valueOfPin[i]   =;                    // write value is 0...255 not 0..4095
            modeOfPin[i]    =;                       // is byte 0/1/2
            if (modeOfPin[i] >3){                                   // set to default if mode > 3 = output and 0
                modeOfPin[i]    = 0;
                valueOfPin[i]   = 0;                                 
            if (i< 8 && valueOfPin[i] >1){ valueOfPin[i] =1;}       // set max number for digital pins  (0/1)
            if (modeOfPin[i] ==0) {pinMode(i, INPUT);}
            if (modeOfPin[i] ==1) {
                pinMode(i, OUTPUT);
                if (i<8){ digitalWrite(i, valueOfPin[i]);}
                if (i>9){ analogWrite( i, valueOfPin[i]);}
            if (modeOfPin[i] ==2) {pinMode(i, INPUT_PULLDOWN);}
            if (modeOfPin[i] ==3) {pinMode(i, INPUT_PULLUP);}

            if (Logging>0){
                Serial.print("pin:");       Serial.print(i);
                Serial.print(" mode:");     Serial.print(modeOfPin[i]);
                Serial.print("  value:");   Serial.println(valueOfPin[i]);

void loop(){
        now = millis();
        if (now < lastLoop){                                            // reset after 46 days
        if (Logging>0){Serial.println(" resetting msec counter ");}
            lastLoop =now;
        if (now-lastLoop  > 30000){                                     // 30 seconds no action = not entered into this loop .. no pings no input, no action  something really wrong ..  reboot
            if (Logging>0){Serial.println(" restarting system due to  loop stuck ");}
                NVIC_SystemReset();                                     // this resets the system

        if  ( now-lastLoop > loopWait){                                 // check network ... conditions
            npongs =,npings);                         

            if (Logging>0){
                Serial.print("=============== loop milli-seconds:");Serial.print(now);  // just write out the msecs
                Serial.print("... counter:"); Serial.println(loopCounter);
                Serial.print("=============== pinging: ");Serial.print(IPN);            // here starts the ping test
                Serial.print(" ... pongs: ");Serial.println(npongs);                    // got any pongs to the pings?
            if (npongs >0){ 
                noPing =0;                                              // yes , reset bad counter
                loopWait=20000;                                         // wait at least 20 seconds before next test
                rssiBad=0;                                              // reset also RSSI
                npings=2;                                               // just do 2 pings next time
                noPing++;                                               // no pongs, increment bad counter
                loopWait =4000;                                         // failed ping takes ~ 2 seconds of the wait so instead of 2 seconds use 4 seconds
                npings=4;                                               // do 4 pings next time
            if (noPing > 10){                                           // reboot after 10 bad ping responses in a row
            if (Logging>0){Serial.println(" restarting system due to no pongs on pings ");}
                NVIC_SystemReset(); // this resets the system

            // RSSI test
            if (npongs ==0 && enbableRSSI ){                            // try RSSI if noping
                int wSignal = WiFi.RSSI();
                if (Logging>0){Serial.print("=============== RSSI: ");Serial.println(wSignal);}
                if (wSignal <0 ){                                       // RSII=1: error, =2 timeout, no response
                    if (wSignal < -100 ){                               // good signal should be > -100
                        if (rssiBad >5){
                            if (Logging>0){Serial.println(" restarting system due to bad WiFi signal ");}
                            NVIC_SystemReset();                         // this resets the system   
                        rssiBad=0;                                      // RSSI was good, reset bad counter
                    enbableRSSI=false;                                  // once bad dont use it  again

        if (!serverStarted){                                            // start server if not started and if not cloud not connected
            if (!Spark.connected()){
                if (Logging>0){Serial.println("=============== server.begin");}

        now = millis();
        if (Spark.connected()){                                         // stop cloud connection if connected for > 20 seconds
            if (now- sparkUP> 30000 ){
                if (Logging>0){Serial.println("=============== Spark.disconnect");}

        if(BUTTON_GetDebouncedTime(BUTTON1) >= 100) {                   // if mode button pressed enable cloud connection
           if (Logging){ Serial.println("=============== Spark.connect");}
            sparkUP= now;                                               // mark time when we started could connection

        if (!Spark.connected() && serverStarted){                       // in normal operation mode
            bool begin=false;                                           // have not received "{" yetr

            if (client.connected()) {                                   // any data?
                for (int i=0;i<256;i++){inBuf[i]='\0';}                 // clear input buffer
                while (client.available()) {                            // character available, readthem
                    c =;
                    if (Logging>4){Serial.print(c);}
                    if (numberOfChar >254) numberOfChar =254;           // no more than 255 input characters
                    if (c == '{') { begin=true;}                        // found '{' now the data comes. before this was the header
                    if (begin==true && c!='{'){                         // skip '{'
                        if (c !='}' ) {
                            inBuf[numberOfChar] = c;                    // move next char into buffer
                            numberOfChar+=1;                            // inc char counetr
                            inBuf[numberOfChar]='\0';                   // add 0 to end
                        else{                                           // done with input
                            if (Logging>1){
                            if (Logging>4){Serial.print(inBuf);}
                            parseALL();                                 // anlyze data and send back requested status and values
                            delay(30);                                  // give it some time, we are done with analyzing and responding
                //            client.stop();  /// !!!! dont do this ... this makes it unstable..
                lastLoop = millis();                                    //  reset ping counter as we just had a communication
                if (Logging>1){Serial.println(outBuf);}
                if (Logging>3){Serial.println("pin values stored into eprom:");}
                for (int i=0;i<18;i++){                                 // save curent pin mode and values in EEprom
                    if ( i==8 || i==9) {continue;}                      // skip the non existing numbers
                    EEPROM.write(i, modeOfPin[i]);                      // byte value  0/1/2/3
                    EEPROM.write(i+20, valueOfPin[i]&255);              // take lower part of integer value
                    if (Logging>3){
                        Serial.print("pin:");       Serial.print(i);
                        Serial.print(", mode:");    Serial.print(modeOfPin[i]);
                        Serial.print(", value:");   Serial.println(valueOfPin[i]);
                delay(100);                                             // after the answer is done, give back some time, dont expect another querry the next second anyway...
            else {
            // if no client is yet connected, check for a new connection
                client = server.available();                            // check if new request and connect

Page 1 of 1

Who is online

Users browsing this forum: No registered users and 1 guest