Spark.publish crashing core?

What I want to do is as following:

I want to control the onboard LED via the tinker app, which I got working. Besides that, I want to publish the values of R, G and B in my browser (for future projects).

I took the standard tinker app and added a littlebit of code. It worked fine until I added the spark.publish line. It responds really slow or not at all and nothing is getting published. The code:

int tinkerAnalogWrite(String command)
{
	//convert ascii to integer
	int pinNumber = command.charAt(1) - '0';
	//Sanity check to see if the pin numbers are within limits
	if (pinNumber< 0 || pinNumber >7) return -1;

	String value = command.substring(3);

	if(command.startsWith("D"))
	{
		pinMode(pinNumber, OUTPUT);
		analogWrite(pinNumber, value.toInt());
		return 1;
	}
	else if(command.startsWith("A"))
	{
	    if (pinNumber == 0)
	    {
	        r = value.toInt();
	    }
	    
	    if (pinNumber == 1)
	    {
	        g = value.toInt();
	    }
	    
	    if (pinNumber == 4)
	    {
	        b = value.toInt();
	    }	    
	    
	    RGB.color(r,g,b);
	    
	    unsigned long now = millis();
        //Every 1 seconds publish uptime
        if (now-lastTime>2000UL) {
        lastTime = now;
	    
	    sprintf(publishString,"%u:%u:%u",r,g,b);
        Spark.publish("Uptime",publishString);
        }

	    
		pinMode(pinNumber+10, OUTPUT);
		analogWrite(pinNumber+10, value.toInt());
		return 1;
		
	}
	else return -2;
}

The HTML file is this:


<br><br>
<button onclick="start()">Connect</button>

<script type="text/javascript">
function start() {

    document.getElementById("uptime").innerHTML = "Waiting for data...";
    var deviceID = "<<hex device id here>>";
    var accessToken = "<<hex access token here>>";
    var eventSource = new EventSource("https://api.spark.io/v1/devices/" + deviceID + "/events/?access_token=" + accessToken);

    eventSource.addEventListener('open', function(e) {
        console.log("Opened!"); },false);

    eventSource.addEventListener('error', function(e) {
        console.log("Errored!"); },false);

    eventSource.addEventListener('Uptime', function(e) {
        var parsedData = JSON.parse(e.data);
        var tempSpan = document.getElementById("uptime");
        var tsSpan   = document.getElementById("tstamp");
        tempSpan.innerHTML = "Core: " + parsedData.coreid + " uptime: " + parsedData.data + " (h:m:s)";
        tempSpan.style.fontSize = "28px";
        tsSpan.innerHTML = "At timestamp " + parsedData.published_at;
        tsSpan.style.fontSize = "9px";
    }, false);
}
</script>

Hi @TheHawk1337

I don’t see anything obvious in the code that your posted, but I do have a question:

How us publishString defined in you code? Is it the way I did with char publishString[40];

There are some potential issues there with char versus char *.

Also, how are r,g,b defined? As unsigned?

We’ll figure it out!

Hi,

I defined them as following:

int r = 0;
int g = 0;
int b = 0;
char publishString[40];

Hi @TheHawk1337

The declaration for publishString is great, but the types don’t match for r, g, b in the sprintf. Maybe you could try:

    sprintf(publishString,"%d:%d:%d",r,g,b);

I just tried this code, and it seems to fail for me too.

char publishString[40];
sprintf(publishString,"Pin: %d - State: %d - Delay: %d",pinNum,pinSet,pinDelay);
Spark.publish("setPin", publishString);

Seems to have some problems getting past the Spark.publish()

Hi @Mikey

Depending on how large the values are for your three integer variables, this could overflow 40 characters in publishString, so you might want to increase that from 40 to say 64 characters. You always need one more character at the end for the null terminator. I am assuming that pinNum and pinSet and pinDelay are type int.

Let me play around with it and see if I can figure anything out.

They are only pin numbers (defined as int), like 0-7 and state is 0 or 1, delay can go up higher though.

Also didn’t help increasing it to 64.

I just tried this simpler example and it worked great for me:

// publishme.ino -- Spark Publishing Example 
unsigned long lastTime = 0UL;
char publishString[64];

int sec = 10000;
int min =  5000; 
int hours = 1000000;

void setup() {
}

void loop() {
    unsigned long now = millis();
    //Every 15 seconds publish uptime
    if (now-lastTime>15000UL) {
        lastTime = now;

        sprintf(publishString,"Pin: %d - State: %d - Delay: %d",hours++,min++,sec++);
        Spark.publish("Uptime",publishString);
    }
}

Can you try this simple example and see if that works? Here’s a screen shot from Chrome.

Can the problem be when doing a publish inside a function?

I am using Spark.publish to give me debug info from something in a Spark.function

Hi @Mikey

That certainly has the potential to be a problem since the Spark.function returns an integer value, you could be running into a recursion that the Spark engineers did not design for. The Spark.function could be blocking the cloud channel until it is done and returns a value.

There is a fairly easy way out: create a global variable of type bool called publishNow. Then in your Spark.function() set the publishNow global variable to true. In your loop() function, if the publishNow variable is true, call Spark.publish() and set the publishNow back to false. You will need to a global publishString as well to send the value.

I will see if I can write up a simple example later today.

That would start to get a bit annoying, I was planning to have multiple publish for different things, having to do it all global and setting booleans to make it trigger and so easily gets messy.

It is only because Spark.Function might be blocking the cloud connect. In the general case of “I got some event and I want to tell the world about it” you don’t need this. Only when the event you got is a Spark.function() which the cloud is sending to you. As to the boolean triggers, you could also an int type for the publishing trigger and a switch/case statement and group all your publish commands, if you wanted to.

Maybe @zachary or @Dave can comment on using Spark.publish() inside a Spark.function() . I am not sure that was a use-case they anticipated.

I think it is more like Spark.function() having a connecting, Spark.publish() then uses that, and closes the connection, not allowing Spark.function to return what it should. Everything triggered by the Spark.funcion() is executed, it just does not return anything.

I tried using %d, same effect (unresponsive core)

I am currently experimenting with the spark.function() thing u suggested, will update later.

EDIT:

So I did the following;

In the code where I previously called the function Spark.publish, I put the boolean publish = true after which I added an if statement to the loop. The complete code:

/**
 ******************************************************************************
 * @file    application.cpp
 * @authors  Satish Nair, Zachary Crockett and Mohit Bhoite
 * @version V1.0.0
 * @date    05-November-2013
 * @brief   Tinker application
 ******************************************************************************
  Copyright (c) 2013 Spark Labs, Inc.  All rights reserved.

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation, either
  version 3 of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this program; if not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/  
#include "application.h"

/* Function prototypes -------------------------------------------------------*/
int tinkerDigitalRead(String pin);
int tinkerDigitalWrite(String command);
int tinkerAnalogRead(String pin);
int tinkerAnalogWrite(String command);
    int r = 0;
    int g = 0;
    int b = 0;
    char publishString[40];
    unsigned long lastTime = 0UL;
    bool publish = false;

/* This function is called once at start up ----------------------------------*/
void setup()
{
	//Setup the Tinker application here

	//Register all the Tinker functions
	Spark.function("digitalread", tinkerDigitalRead);
	Spark.function("digitalwrite", tinkerDigitalWrite);

	Spark.function("analogread", tinkerAnalogRead);
	Spark.function("analogwrite", tinkerAnalogWrite);
    RGB.control(true);
}

/* This function loops forever --------------------------------------------*/
void loop()
{
	if (publish == true)
    {
    	sprintf(publishString,"%d:%d:%d",r,g,b);
        Spark.publish("Uptime",publishString);
        publish = false;
    }
}

/*******************************************************************************
 * Function Name  : tinkerDigitalRead
 * Description    : Reads the digital value of a given pin
 * Input          : Pin 
 * Output         : None.
 * Return         : Value of the pin (0 or 1) in INT type
                    Returns a negative number on failure
 *******************************************************************************/
int tinkerDigitalRead(String pin)
{
	//convert ascii to integer
	int pinNumber = pin.charAt(1) - '0';
	//Sanity check to see if the pin numbers are within limits
	if (pinNumber< 0 || pinNumber >7) return -1;

	if(pin.startsWith("D"))
	{
		pinMode(pinNumber, INPUT_PULLDOWN);
		return digitalRead(pinNumber);
	}
	else if (pin.startsWith("A"))
	{
		pinMode(pinNumber+10, INPUT_PULLDOWN);
		return digitalRead(pinNumber+10);
	}
	return -2;
}

/*******************************************************************************
 * Function Name  : tinkerDigitalWrite
 * Description    : Sets the specified pin HIGH or LOW
 * Input          : Pin and value
 * Output         : None.
 * Return         : 1 on success and a negative number on failure
 *******************************************************************************/
int tinkerDigitalWrite(String command)
{
	bool value = 0;
	//convert ascii to integer
	int pinNumber = command.charAt(1) - '0';
	//Sanity check to see if the pin numbers are within limits
	if (pinNumber< 0 || pinNumber >7) return -1;

	if(command.substring(3,7) == "HIGH") value = 1;
	else if(command.substring(3,6) == "LOW") value = 0;
	else return -2;

	if(command.startsWith("D"))
	{
		pinMode(pinNumber, OUTPUT);
		digitalWrite(pinNumber, value);
		
		return 1;
	}
	else if(command.startsWith("A"))
	{
		pinMode(pinNumber+10, OUTPUT);
		digitalWrite(pinNumber+10, value);
		return 1;
	}
	else return -3;
}

/*******************************************************************************
 * Function Name  : tinkerAnalogRead
 * Description    : Reads the analog value of a pin
 * Input          : Pin 
 * Output         : None.
 * Return         : Returns the analog value in INT type (0 to 4095)
                    Returns a negative number on failure
 *******************************************************************************/
int tinkerAnalogRead(String pin)
{
	//convert ascii to integer
	int pinNumber = pin.charAt(1) - '0';
	//Sanity check to see if the pin numbers are within limits
	if (pinNumber< 0 || pinNumber >7) return -1;

	if(pin.startsWith("D"))
	{
		pinMode(pinNumber, INPUT);
		return analogRead(pinNumber);
	}
	else if (pin.startsWith("A"))
	{
		pinMode(pinNumber+10, INPUT);
		return analogRead(pinNumber+10);
	}
	return -2;
}

/*******************************************************************************
 * Function Name  : tinkerAnalogWrite
 * Description    : Writes an analog value (PWM) to the specified pin
 * Input          : Pin and Value (0 to 255)
 * Output         : None.
 * Return         : 1 on success and a negative number on failure
 *******************************************************************************/
int tinkerAnalogWrite(String command)
{
	//convert ascii to integer
	int pinNumber = command.charAt(1) - '0';
	//Sanity check to see if the pin numbers are within limits
	if (pinNumber< 0 || pinNumber >7) return -1;

	String value = command.substring(3);

	if(command.startsWith("D"))
	{
		pinMode(pinNumber, OUTPUT);
		analogWrite(pinNumber, value.toInt());
		return 1;
	}
	else if(command.startsWith("A"))
	{
	    if (pinNumber == 0)
	    {
	        r = value.toInt();
	    }
	    
	    if (pinNumber == 1)
	    {
	        g = value.toInt();
	    }
	    
	    if (pinNumber == 4)
	    {
	        b = value.toInt();
	    }	    
	    
	    RGB.color(r,g,b);
	    
	    unsigned long now = millis();
        //Every 1 seconds publish uptime
        if (now-lastTime>2000UL) {
        lastTime = now;
	    publish = true;
        }

	    
		pinMode(pinNumber+10, OUTPUT);
		analogWrite(pinNumber+10, value.toInt());
		return 1;
		
	}
	else return -2;
}

sorry it looks so messy, cannot find a proper way to put it in a neat box… Goes automatically appearently

After doing this I flashed the core again and well, there is good news and bad news:

The good news is that it responds now, I can change the colours by changing the analog outputs A0, A1 and A4.
The bad news is that it does not publish anything at all, the HTML page just keeps waiting for data…

Hmm, I would think this would work, but it’s possible there could be a bug there, I’ll try it out and report back when I get a chance.

Hi @TheHawk1337,
since it’s somehow what you are doing - have you seen this ExtTinkerApp + Firmware?
I have also played with Spark.publish() and did do the publishing in the loop whenever the string to publish was not an empty string.
This works fine.
If you wish, I can send you my publishing enabled ExtTinkerApp + Firmware :wink:

I would love to :smile:

I am planning on using the spark core for a school project (bedside monitor measuring heartrate, blood pressure and temperature). This is just some basic testing and proof of concept.

Appreciate it :smile:

You might want to check out the JSON publishing tutorial I just put up. It’s an expansion of the previous simple one showing you how to use JSON structures to publish multiple parameters. You could easily have an event “Health” that published a JSON with parameters “heartrate”, “bloodpressure”, and “temperature”.

1 Like

I saw your post, currently looking into it :smile:

Much appreciated :smile:

EDIT:

IT IS WORKING! After turning the core off and on and reloading the HTML page it works! I can now read the PWM values for the onboard LED via my browser :smiley:

1 Like

I forgot to mention that my ExtTinkerApp only works on Android - I hope this is no problem for you.

I’ve just uploaded my publish enabled ExtTinkerFirmware which works together with the Android app and I’ve also altered @bko 's test html page a bit and placed it for download here .

The published message for standard TinkerApp commands will be the received command (e.g. D7,HIGH) and for a RGB-LED command CL,123456789 (AA, RR, GG, BB) where 123456789 will be the decimal ARGB value and AA,RR,GG,BB the equivalent hex values (Alpha -> 0 = RGB.control(false) otherwise RGB.control(true) ).

At the moment I’m trying to produce an APK of ExtTinkerApp, and as soon I got that I could send you a link for that too - otherwise you could download the source from my gitHub

Edit: Got the APK exported - if you just want to test the app without the need to build it yourself, just send me an PM, I’ll send you a link to download a debug version.

1 Like