Code working on Photon doesn't work for Core

core
photon
Tags: #<Tag:0x00007fe2212b1c80> #<Tag:0x00007fe2212b1848>

#1

I have some code compiled on the web IDE which works nicely when flashed on a Photon but doesn’t work on an old Core, in both cases I’m compiling for firmware version 1.1.0.

// This #include statement was automatically added by the Particle IDE.
#include <elapsedMillis.h>

// This #include statement was automatically added by the Particle IDE.
#include <HttpClient.h>

int count;
int interval;
String id;
String channel;
elapsedMillis elapsed;

void setup() {
    Particle.function("update", update);
    Particle.function("setId", setId);
    Particle.function("setChannel", setChannel);
    Particle.function("setInterval", setInterval);
    Particle.variable("count", count);
    Particle.variable("channel", channel);
    Particle.variable("interval", interval);
    Particle.variable("id", id);
    id = read(20);
    channel = read(0);
    EEPROM.get(70, interval);
    if (interval < 10000) {
        interval = 10000;
    }
    update("start");
}

void loop() {
    if (elapsed > interval) {
        update("auto");
    }
}

HttpClient http;
http_request_t request;
http_response_t response;
http_header_t headers[] = {
    { "Accept" , "*/*"},
    { NULL, NULL } // NOTE: Always terminate headers will NULL
};

int update(String extra) {
    request.hostname = "myhost.example.com";
    request.port = 80;
    request.path = "/counter/" + channel + "/" + id;
    http.get(request, response, headers);
    if (response.status != 200) {
        return -1;
    } else {
        elapsed = 0;
        count = atoi(response.body);
        Particle.publish("counter", extra + " " + channel + ":" + count);
        return count;
    }
}

int setInterval(String val) {
    interval = atoi(val) * 1000;
    EEPROM.put(70, interval);
    return 1;
}

int setId(String val) {
    id = val;
    write(20, val, 50);
    return 1;
}

int setChannel(String val) {
    if (val == "youtube" || val == "twitter") {
        channel = val;
        write(0, val, 20);
        return 1;
    }
    return -1;
}

const int STRING_BUF_SIZE = 64;
void write(int addr, String data, const int max) {
    
	char stringBuf[STRING_BUF_SIZE];

	data.getBytes((unsigned char *)stringBuf, max);
	EEPROM.put(addr, stringBuf);
}

String read(int addr) {
	char stringBuf[STRING_BUF_SIZE];
	
    EEPROM.get(addr, stringBuf);
	stringBuf[sizeof(stringBuf) - 1] = 0; // make sure it's null terminated
	String str(stringBuf);
	return str;
}

On the core, from the web console:

  • the OS version doesn’t show up
  • any attempt to invoke the setChannel function fails (returns -1)
  • any function call seems unable to write to EEPROM
  • no event gets published

#2

Are you sure the code sticks on the Core?
Do you get a new application hash event in console when you flash the new code and the Core starts the first time?
You can try flashing via USB particle flash --usb firmware.bin -v.
To download the binary click the image icon next to the project name in Web IDE.
Before flashing check particle binary inspect firmware.bin to make sure the binary is actually meant for the Core.

You are also always writing 64 bytes to EEPROM even if that would overwrite following data.
EEPROM.put() uses the size of the object you pass in to determin the length you want to write, not its contents. Even if the string is only 20 bytes long, the array is 64.
You may rather want to use this

 void write(int addr, String data, const int max) {  
	char stringBuf[max];

	data.getBytes((unsigned char *)stringBuf, max);
	stringBuf[max-1] = '\0'; // may not be needed, but the docs for getBytes() aren't clear on that
	EEPROM.put(addr, stringBuf);
}

(this is also safer since your original code would even accept max to be greater than STRING_BUF_SIZE which would be fatal)

You should add a scope (e.g. PRIVATE) there.

BTW, when your setChannel() call returns -1 that would indicate the incoming val string doesn’t match your options.
How about Serial.println(val) to see why?

You should add some more of these Serial.print() statements in order to get a feeling where your code goes wrong. That would usually be the first step of debugging your code yourself.


#3

Do you get a new application hash event in console when you flash the new code and the Core starts the first time?

In console I see 3 events:

  • spark/flash/status - started
  • spark/flash/status - success
  • spark/device/last_reset - user

BTW, when your setChannel() call returns -1 that would indicate the incoming val string doesn’t match your options.
How about Serial.println(val) to see why?

Did it and what I see on the terminal is some garbage followed by the function name and the provided value… Please note I added the Serial.println statement as first in the function, so nothing has messed with the value yet. I also double checked the terminal connection was using the right settings (baudrate and everything).

}┘ÍbÃQ~Ú[j3¾sj░◆•⎽)⎺V²§šNŒIº[w≤u¶b¿¸µ={µJ·¤
Äπ5œÿÐ5ouA⎻±f
setChannelGtwitter

Before flashing check particle binary inspect firmware.bin to make sure the binary is actually meant for the Core.

Output is

firmware.bin
 CRC is ok (c6769ed6)
 Compiled for core
 This is a monolithic firmware number 0 at version 1102

You can try flashing via USB particle flash --usb firmware.bin -v .

Done, but i was pretty sure this didn’t change much anyway, here follows the output:

dfu-util 0.9

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2016 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to http://sourceforge.net/p/dfu-util/tickets/

Opening DFU capable USB device...
ID 1d50:607f
Run-time device DFU version 011a
Claiming USB DFU Interface...
Setting Alternate Setting #0 ...
Determining device status: state = dfuERROR, status = 10
dfuERROR, clearing status
Determining device status: state = dfuIDLE, status = 0
dfuIDLE, continuing
DFU mode device DFU version 011a
Device returned transfer size 1024
DfuSe interface name: "Internal Flash  "
Downloading to address = 0x08005000, size = 109324
Download        [=========================] 100%       109324 bytes
Download done.
File downloaded successfully
Transitioning to dfuMANIFEST state
Invalid DFU suffix signature
A valid DFU suffix will be required in a future dfu-util release!!!

Flash success!

You are also always writing 64 bytes to EEPROM even if that would overwrite following data.
EEPROM.put() uses the size of the object you pass in to determine the length you want to write, not its contents. Even if the string is only 20 bytes long, the array is 64.

I applied the suggested change, which required an additional cast to (unsigned char *) when performing the put to avoid a compilation error:

no matching function for call to 'EEPROMClass::put(int&, char [max])'

As expected, this didn’t change much as the issue seems to be with the incoming value.

You should add some more of these Serial.print() statements in order to get a feeling where your code goes wrong. That would usually be the first step of debugging your code yourself .

You are totally right and it is my general practice, I was surprised the same code was running nicely on the Photon though…

Is it possible I had to do something special to upgrade the system part of the firmware on my Core other than flashing a new sketch? I remember it was a different, more complex process back in the days… If that’s not the case then my conclusion is something is wrong in the 1.0.1 and 1.1.0 firmwares (the two I have tried) with regards to the interpretation of the extra function argument which is probably passed through as a structure containing additional data like the function mane and the device identifier…


#4

I’m thinking about what was called “Deep Update” and what concerns me is the fact the Device OS and Serial Number fields in the web console are Unknown


#5

The Core only has monolithic firmware (device OS and application in one file).


#6

I also tried the particle doctor procedure, but I still don’t see the firmware version in the console or the wed ide…

Reverting the firmware version down to 0.7.0 though fixed the issue: versions 1.0.0, 1.0.1 and 1.1.0 did all suffer from this.

I’m pretty sure there should be something wrong in the more recent firmware releases…

The downside though is the HTTP connection now just doesn’t work anymore (possible incompatibility of the HttpClient library with firmware version 0.7.0?)


#7

Yup, there is a still open issue referencing that

If you could comment on that issue this might help raise the priority.


#8

Comment added, I also tried to search github and found @BDub had already fixed that at some point, or at least tried to do so… Maybe he knows better what’s going on here.


#9

Please try Device OS 1.2.0-rc.1 or better yet, 1.2.1-rc.2 which also has this fix.
If you don’t have enough memory for your application on the Core, you can also try downgrading to 0.7.0, 0.6.3, etc… for Core.


#10

Thanks for jumping in @BDub. I already downgraded to 0.7.0, but the library I’m using (HttpClient) doesn’t seem compatible with that old version.

I was hoping Particle would have been willing to maintain a decent level of compatibility for older devices, even if it’s normal certain features couldn’t be kept up to date forever.

I’ll give a try to latest release candidates and provide my feedback.


#11

I confirm 1.2.1-rc2 is not suffering from this issue.


#12

Here’s a case where an older version of HttpClient will likely have to be manually checked out from Github and used. I’m mostly positive HttpClient used to work for the Core in the past.

If you can use the latest 1.2.x firmware though, that works.


#13

Weird… firmware 1.2.x solves the issue with the function call, but the HttpClient library still doesn’t work… I’m wondering if a memory overflow is occurring somewhere: as soon as I submit the http call I receive a -1 response, but this was exactly the same behaviour I was experiencing with 0.7.0… so that makes me think…

To factor out my potential dumbness I went back and pushed the exact same code to the Photon running firmware 1.1.0 and it worked… then I upgraded the photon to 1.2.1-rc2 and…It worked flawlessly.

I know, you are not the library author and you don’t have enough info to pinpoint the issue here, I just wanted to report what I’m experiencing in case someone else follows my same steps.

For completenes, here the code I’m uploading:

// This #include statement was automatically added by the Particle IDE.
#include <elapsedMillis.h>

// This #include statement was automatically added by the Particle IDE.
#include <HttpClient.h>

int count;
int interval;
String id;
String channel;
elapsedMillis elapsed;

void setup() {
    //Serial.begin(9600);
    Particle.function("update", update);
    Particle.function("setId", setId);
    Particle.function("setChannel", setChannel);
    Particle.function("setInterval", setInterval);
    Particle.variable("count", count);
    Particle.variable("channel", channel);
    Particle.variable("interval", interval);
    Particle.variable("id", id);
    channel = read(0);
    id = read(20);
    EEPROM.get(70, interval);
    if (interval < 10000) {
        interval = 10000;
    }
    update("start");
}

void loop() {
    if (elapsed > interval) {
        update("auto");
    }
}

HttpClient http;
http_request_t request;
http_response_t response;
http_header_t headers[] = {
    { "Accept" , "*/*"},
    { NULL, NULL } // NOTE: Always terminate headers with NULL
};

LEDStatus statusOk(RGB_COLOR_GREEN, LED_PATTERN_FADE);
LEDStatus statusRun(RGB_COLOR_BLUE, LED_PATTERN_FADE);
LEDStatus statusErr(RGB_COLOR_RED, LED_PATTERN_FADE);

int update(String extra) {
    elapsed = 0;
    statusRun.setActive();
    //Serial.print("updating...");
    request.hostname = "myserver.example.com";
    request.port = 80;
    request.path = "/counter/" + channel + "/" + id;
    http.get(request, response, headers);
    if (response.status == 200) {
        statusOk.setActive();
        count = atoi(response.body);
        Particle.publish("counter", channel + ":" + count + (extra.length() > 0 ? " - " + extra : ""));
    } else {
        statusErr.setActive();
    }
    return response.status;
}

int setInterval(String val) {
    //Serial.println(val);
    interval = atoi(val) * 1000;
    EEPROM.put(70, interval);
    return 1;
}

int setId(String val) {
    //Serial.println(val);
    id = val;
    write(20, val, 50);
    return 1;
}

int setChannel(String val) {
    //Serial.println(val);
    if (val.compareTo("youtube") == 0 || val.compareTo("twitter") == 0) {
        channel = val;
        write(0, val, 20);
        return 1;
    }
    return -1;
}

void write(int addr, String data, const int max) {
	char stringBuf[max];

	data.getBytes((unsigned char *)stringBuf, max);
	stringBuf[max - 1] = '\0'; // make sure it's null terminated
	EEPROM.put(addr, (unsigned char *)stringBuf);
}

const int STRING_BUF_SIZE = 64;
String read(int addr) {
	char stringBuf[STRING_BUF_SIZE];
	
    EEPROM.get(addr, stringBuf);
	stringBuf[sizeof(stringBuf) - 1] = 0; // make sure it's null terminated
	return String(stringBuf);
}

#14

I might have some more info to add: the part reading/writing the variables from EEPROM fails to work on the Core, looks like after setting them and power cycling the Core both variables get back a weird value of �M… I can call the setters functions which apparently correctly set the variables, but they are messed up after power cycle.

I went back to firmware 0.7.0 and after setting and power cycling the variables got again to a weird value, but different: channel is HL, while id has value @L: am I reading/writing into program area or is some even weirder issue I’m unable to recognize?

Sorry lads, I just wanted to put my old cores (2) to use into a project…


#15

Re: EEPROM not working… Well that’s no good! So for Core, HttpClient works on 0.7.0, but EEPROM doesn’t? Does your EEPROM code work on Photon? I wonder if it’s an EEPROM issue, or a String <-> char array issue? I don’t normally use .getBytes() so I’m not familiar with it. With the limited resources on the Core, I think you’d be better off just declaring a static global char array to use with EEPROM and not copying back and fourth between String and char arrays. That said you have to use String with Particle.function() arguments.

Did you know on Strings you can get the C string returned like this?

String val = "world";
Serial.printlnf("hello %s", val.c_str());

#16

Not entirely true IMO.
Although undocumented you can also use a function like this as Particle.function() callback

int fnHandler(const char* arg);

Granted, internally the incoming data is of type String but implicit casting allows you to catch it as const char* for further treatment inside the function.

Also an alternative (and my preferred version)

Serial.printlnf("hello %s", (const char*)val);

This also doesn’t break if you (at some future time) decide to do somthing like

//String val = "world";   // uhh, I was told to avoid String, so replace with
char val[64] = "world";

The c_str() version would barf and need additional attention, but the (const char*) version would still be happy with that :wink:
“Plan ahead so in future you can be lazy and keep maintainance/refactoring work minimal!” :sunglasses:


#17

Great tips @ScruffR, thanks!


#18

I completely removed the part reading the values from EEPROM and went for hardcoding the desired values without joy. I tried to also factor out any potential issue with cloud functions and re-enabled serial output… still no joy.

I tried both 0.7.0 and 1.2.1-rc2 with latest HttpClient but the call to http.get fails with a -1 return code.

The exact same code on Photon works nicely. I believe something inside the library is no more compatible with the Core.