Using CoAP for device to device communication

Hi all!

I’ve been working on a project with the Spark Core that requires the core to talk to an application on a tablet WITHOUT an internet connection. The devices will be on the same WiFi network.

I began trying to implement UPnP but I have run into a problem where the core will not accept an incoming TCP connection. In trying to diagnose that with Wireshark I noticed that the core is broadcasting information using the CoAP protocol. I researched what that was and it seems like it might be a good alternative to my UPnP approach. (Mostly because the core firmware already implements it!)

Does a tutorial exist that would help me figure out how to make this work? If not can someone from the Spark team maybe give me a few pushes in the right direction?

Thanks in advance!

1.) the nodejs implementation for Core --> Server COAP Protocol http://github.com/spark/spark-protocol

2.) the c implementation for Core COAP protocol: https://github.com/spark/core-communication-lib

1 Like

Thanks for the links.

Now it’s time for some intensive reverse engineering!

That is unless someone can give me a high level on the conversation between the core and the cloud to help me focus my efforts.

Maybe this as well: https://community.spark.io/t/spark-cloud-bandwidth-requirements/9095/6

I have been doing research and have found an excellent paper here that may be of help to others!

1 Like

OK, I’ve been researching a lot and digging into the Core’s firmware code.

I see in the SparkProtocol::presence_announcement() method that it builds up a CoAP packet that is sent to a multicast address of 224.0.1.187 on port 5683. The thing that has mepuzzled about the packet is this:

buf[4] = 0xb1;

This is an option byte for CoAP. The 0xb0 is the option and the 0x01 is the length. In all of my research I cannot figure out that 0xb0 is a valid option. Is this something that the Spark team self defined or am I missing a source for some information?

Thanks in advance for helping me out on this!

0xB is URI-PATH. Heres the full current list of all numbers related to coap registered with the iana: http://www.iana.org/assignments/core-parameters/core-parameters.xhtml

I know that that one is actually specified in the main CoAP rfc (7252 I think). I suggest you read as much of the rfc as you can if you’re interested in knowing what you’re doing.

Azdle,

Thank you! I had already read the rfc and looked at the URL you referenced before posting here. BUT I’m a MORON and converted 0xB0 to decimal and then looked for the result in the list not 0xB! I realized it as soon as I read your post.

Thanks for helping out a slightly numerically challenged individual!

How is it going with this project. I am interested since my http communication seems to be too slow.

Hey @Sailing_Nut - been reading around your posts, namely this post and this other one - am hoping to implement something along your lines but am just getting started reading into it so thanks for the links above.

Is your project open, or can you give me any idea as to how you went about implementing your ideas? Would help me get off to a start…

I made a small coap sender since http client was too slow to be used on battery often.
It seems the UDP class cant take a hostname, so you have to find the ip first and store it in (fake)eeprom or similiar, or maybe your project can work with a statically programmed IP.

void coap_send(char* data)
{
Udp.beginPacket({1,2,3,4}, 5683);

uint8_t coapid = 1; //ideally this should be a unique id for each packet, but saving it in eeprom to survive a deep sleep seems a bit overkill
char tempbuf[400];
uint8_t p = 0;
// VVTTLLLL
tempbuf[p++] = 0b01010000; //V=01 T=01 (No confirm) 0 token length
tempbuf[p++] = 0b00000010; //0.2=POST req
tempbuf[p++] = (coapid & 0xFF00) >> 8; //Message id
tempbuf[p++] = (coapid & 0xff); //Message id 1
//tempbuf[p++] = 0xff; //no options

char host[13] = "hostname.dk";
//Option 3 - delta 3 - URI_Host
uint8_t hostlen = strlen(host);

if (hostlen > 12) //Hostname is limited to 99chars, so we dont need to support type 14
{
tempbuf[p++] = 0b00110000 | 13; //1101 = 13
tempbuf[p++] = hostlen - 13;
} else {
tempbuf[p++] = 0b00110000 | hostlen;
}

strncpy(&tempbuf[p], host, hostlen);
p += hostlen;

//Option 11 - delta 8 - URI_PATH - /incoming
//88 69 6e 63 6f 6d 69 6e 67
tempbuf[p++] = 0x88;
tempbuf[p++] = 0x69;
tempbuf[p++] = 0x6e;
tempbuf[p++] = 0x63;
tempbuf[p++] = 0x6f;
tempbuf[p++] = 0x6d;
tempbuf[p++] = 0x69;
tempbuf[p++] = 0x6e;
tempbuf[p++] = 0x67;

//Option 12 - delta 1 - format json
//11 32
tempbuf[p++] = 0x11;
tempbuf[p++] = 0x32;

tempbuf[p++] = 0xff; //end of options

strncpy(&tempbuf[p], data, strlen(data)); //would be better to send the 2 strings asis, but 2 calls to Udp.write seems to upset the node server, maybe a null char is being inserted.
Udp.write((uint8_t*)tempbuf, p+strlen(data));
Udp.endPacket();
}

Nodejs server to receive it, it checks the data is somewhat valid and then calls the same server over http with the data

const coap = require('coap')
, server = coap.createServer();
var http = require('http');

server.on('request', function(req, res) {
var url = req.url.split('/')[1];

// console.log(req.rsinfo.address+":"+req.rsinfo.port+" >> "+req.payload.toString('utf-8'));

if (url == "incoming")
{
var payload = req.payload.toString('utf-8');
try
{
var js = JSON.parse(payload.toString('utf-8'));
var version = js.v;
var id = js.id;
var sensors = js.sensors;

            var url = "/incoming.php?v="+version+"&id="+id;
            sensors.forEach(function (sensor) {
                    url += "&"+sensor.name+"="+sensor.value;
            });
            var options = {
              host: 'hostname.dk',
              path: url,
              port: '80',
              headers: {
                    'X-Forwarded-For': req.rsinfo.address,
                    'User-Agent':       'NodeJS CoAP proxy'
              }
            };
            var httpreq = http.request(options, function(httpres) {
                    console.log(req.rsinfo.address+":"+req.rsinfo.port+" >> "+url);
            });
            httpreq.end();
    } catch (ex) {
            console.log("Json parse failed >> ",payload.toString('utf-8'),ex);
    }

}
})

// the default CoAP port is 5683
server.listen(function() {
console.log("Server up ...");
})

Usage

	sprintf(msg, "{\"id\":\"%s\",\"v\":1,\"sensors\":[{\"name\":\"temp\",\"value\":\"%0d.%d\"},{\"name\":\"hum\",\"value\":\"%0d.%d\"},{\"name\":\"dp\",\"value\":\"%0d.%d\"},{\"name\":\"hi\",\"value\":\"%0d.%d\"},{\"name\":\"ldr\",\"value\":\"%i\"},{\"name\":\"batt\",\"value\":\"%i\"},{\"name\":\"orgbatt\",\"value\":\"%i\"}]}",
    id,(int)temp, temp1, (int)hum, temp2, (int)dp, temp3, (int)hi, temp4, ldr, batt, orgbatt);
	coap_send(msg);
1 Like