Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
hirotakaster committed Feb 12, 2016
1 parent fce26ac commit 76f0eea
Show file tree
Hide file tree
Showing 8 changed files with 785 additions and 1 deletion.
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,32 @@
# CoAP-library
# CoAP client, server library for Arduino.
<a href="http://coap.technology/" target=_blank>CoAP</a> simple server, client library for Arduino.

## Source Code
This lightweight library source code are only 2 files. coap.cpp, coap.h.

## Example
Some sample sketches for Arduino included(/examples/).

- coaptest.ino : simple request/response sample.
- coapserver.ino : server endpoint url callback sample.

## How to use
Download this source code branch zip file and extract to the Arduino libraries directory or checkout repository. Here is checkout on MacOS X.

cd $HOME/Documents/Arduino/libraries/
git clone https://github.com/hirotakaster/CoAP-simple-library
# restart Arduino IDE, you can find CoAP-simple-library examples.

In this exmples need CoAP server libcoap or microcoap server for check the example program. There is setting the libcoap on Ubuntu Linux. But if there don't use CoAP server(request/reseponse), following setting don't be needed.

git clone https://github.com/obgm/libcoap
cd libcoap/
./autogen.sh
./configure --disable-examples
gcc -o coap-server ./examples/coap-server.c -I./include/coap/ -I. -L.libs -lcoap-1 -DWITH_POSIX
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.libs
./coap-server
# next start Arduino and check the request/response.

## Particle Photon, Core compatible
This library is Particle Photon, Core compatible. That's version is <a href="https://github.com/hirotakaster/CoAP">here</a>.
323 changes: 323 additions & 0 deletions coap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
#include "coap.h"

#if defined(ARDUINO)
#include "Arduino.h"
#elif defined(SPARK)
#include "application.h"
#endif

#define LOGGING


Coap::Coap(
#if defined(ARDUINO)
UDP& udp
#endif
) {

#if defined(ARDUINO)
this->_udp = &udp;
#elif defined(SPARK)
this->_udp = new UDP();
#endif

}

bool Coap::start() {
this->start(COAP_DEFAULT_PORT);
return true;
}

bool Coap::start(int port) {
this->_udp->begin(port);
return true;
}

uint16_t Coap::sendPacket(CoapPacket &packet, IPAddress ip) {
return this->sendPacket(packet, ip, COAP_DEFAULT_PORT);
}

uint16_t Coap::sendPacket(CoapPacket &packet, IPAddress ip, int port) {
uint8_t buffer[BUF_MAX_SIZE];
uint8_t *p = buffer;
uint16_t running_delta = 0;
uint16_t packetSize = 0;

// make coap packet base header
*p = 0x01 << 6;
*p |= (packet.type & 0x03) << 4;
*p++ |= (packet.tokenlen & 0x0F);
*p++ = packet.code;
*p++ = (packet.messageid >> 8);
*p++ = (packet.messageid & 0xFF);
p = buffer + COAP_HEADER_SIZE;
packetSize += 4;

// make token
if (packet.token != NULL && packet.tokenlen <= 0x0F) {
memcpy(p, packet.token, packet.tokenlen);
p += packet.tokenlen;
packetSize += packet.tokenlen;
}

// make option header
for (int i = 0; i < packet.optionnum; i++) {
uint32_t optdelta;
uint8_t len, delta;

if (packetSize + 5 + packet.options[i].length >= BUF_MAX_SIZE) {
return 0;
}
optdelta = packet.options[i].number - running_delta;
COAP_OPTION_DELTA(optdelta, &delta);
COAP_OPTION_DELTA((uint32_t)packet.options[i].length, &len);

*p++ = (0xFF & (delta << 4 | len));
if (delta == 13) {
*p++ = (optdelta - 13);
packetSize++;
} else if (delta == 14) {
*p++ = ((optdelta - 269) >> 8);
*p++ = (0xFF & (optdelta - 269));
packetSize+=2;
} if (len == 13) {
*p++ = (packet.options[i].length - 13);
packetSize++;
} else if (len == 14) {
*p++ = (packet.options[i].length >> 8);
*p++ = (0xFF & (packet.options[i].length - 269));
packetSize+=2;
}

memcpy(p, packet.options[i].buffer, packet.options[i].length);
p += packet.options[i].length;
packetSize += packet.options[i].length + 1;
running_delta = packet.options[i].number;
}

// make payload
if (packet.payloadlen > 0) {
if ((packetSize + 1 + packet.payloadlen) >= BUF_MAX_SIZE) {
return 0;
}
*p++ = 0xFF;
memcpy(p, packet.payload, packet.payloadlen);
packetSize += 1 + packet.payloadlen;
}

_udp->beginPacket(ip, port);
_udp->write(buffer, packetSize);
_udp->endPacket();

return packet.messageid;
}

uint16_t Coap::get(IPAddress ip, int port, char *url) {
return this->send(ip, port, url, COAP_CON, COAP_GET, NULL, 0, NULL, 0);
}

uint16_t Coap::put(IPAddress ip, int port, char *url, char *payload) {
return this->send(ip, port, url, COAP_CON, COAP_PUT, NULL, 0, (uint8_t *)payload, strlen(payload));
}

uint16_t Coap::put(IPAddress ip, int port, char *url, char *payload, int payloadlen) {
return this->send(ip, port, url, COAP_CON, COAP_PUT, NULL, 0, (uint8_t *)payload, payloadlen);
}

uint16_t Coap::send(IPAddress ip, int port, char *url, COAP_TYPE type, COAP_METHOD method, uint8_t *token, uint8_t tokenlen, uint8_t *payload, uint32_t payloadlen) {

// make packet
CoapPacket packet;

packet.type = type;
packet.code = method;
packet.token = token;
packet.tokenlen = tokenlen;
packet.payload = payload;
packet.payloadlen = payloadlen;
packet.optionnum = 0;
packet.messageid = rand();

// if more options?
packet.options[packet.optionnum].buffer = (uint8_t *)url;
packet.options[packet.optionnum].length = strlen(url);
packet.options[packet.optionnum].number = COAP_URI_PATH;
packet.optionnum++;

// send packet
return this->sendPacket(packet, ip, port);
}

int Coap::parseOption(CoapOption *option, uint16_t *running_delta, uint8_t **buf, size_t buflen) {
uint8_t *p = *buf;
uint8_t headlen = 1;
uint16_t len, delta;

if (buflen < headlen) return -1;

delta = (p[0] & 0xF0) >> 4;
len = p[0] & 0x0F;

if (delta == 13) {
headlen++;
if (buflen < headlen) return -1;
delta = p[1] + 13;
p++;
} else if (delta == 14) {
headlen += 2;
if (buflen < headlen) return -1;
delta = ((p[1] << 8) | p[2]) + 269;
p+=2;
} else if (delta == 15) return -1;

if (len == 13) {
headlen++;
if (buflen < headlen) return -1;
len = p[1] + 13;
p++;
} else if (len == 14) {
headlen += 2;
if (buflen < headlen) return -1;
len = ((p[1] << 8) | p[2]) + 269;
p+=2;
} else if (len == 15)
return -1;

if ((p + 1 + len) > (*buf + buflen)) return -1;
option->number = delta + *running_delta;
option->buffer = p+1;
option->length = len;
*buf = p + 1 + len;
*running_delta += delta;

return 0;
}

bool Coap::loop() {

uint8_t buffer[BUF_MAX_SIZE];
int32_t packetlen = _udp->parsePacket();

while (packetlen > 0) {
packetlen = _udp->read(buffer, packetlen >= BUF_MAX_SIZE ? BUF_MAX_SIZE : packetlen);

CoapPacket packet;

// parse coap packet header
if (packetlen < COAP_HEADER_SIZE || (((buffer[0] & 0xC0) >> 6) != 1)) {
packetlen = _udp->parsePacket();
continue;
}

packet.type = (buffer[0] & 0x30) >> 4;
packet.tokenlen = buffer[0] & 0x0F;
packet.code = buffer[1];
packet.messageid = 0xFF00 & (buffer[2] << 8);
packet.messageid |= 0x00FF & buffer[3];

if (packet.tokenlen == 0) packet.token = NULL;
else if (packet.tokenlen <= 8) packet.token = buffer + 4;
else {
packetlen = _udp->parsePacket();
continue;
}

// parse packet options/payload
if (COAP_HEADER_SIZE + packet.tokenlen < packetlen) {
int optionIndex = 0;
uint16_t delta = 0;
uint8_t *end = buffer + packetlen;
uint8_t *p = buffer + COAP_HEADER_SIZE + packet.tokenlen;
while(optionIndex < MAX_OPTION_NUM && *p != 0xFF && p < end) {
packet.options[optionIndex];
if (0 != parseOption(&packet.options[optionIndex], &delta, &p, end-p))
return false;
optionIndex++;
}
packet.optionnum = optionIndex;

if (p+1 < end && *p == 0xFF) {
packet.payload = p+1;
packet.payloadlen = end-(p+1);
} else {
packet.payload = NULL;
packet.payloadlen= 0;
}
}

if (packet.type == COAP_ACK) {
// call response function
resp(packet, _udp->remoteIP(), _udp->remotePort());

} else if (packet.type == COAP_CON) {
// call endpoint url function
for (int i = 0; i < packet.optionnum; i++) {
if (packet.options[i].number == COAP_URI_PATH && packet.options[i].length > 0) {
char urlname[packet.options[i].length + 1];
memcpy(urlname, packet.options[i].buffer, packet.options[i].length);
urlname[packet.options[i].length] = NULL;
String url = urlname;
#if defined(ARDUINO)
if (!uri.find(url)) {
#elif defined(SPARK)
if (uri.find(url) == uri.end()) {
#endif
sendResponse(_udp->remoteIP(), _udp->remotePort(), packet.messageid, NULL, 0,
COAP_NOT_FOUNT, COAP_NONE, NULL, 0);
} else {
#if defined(ARDUINO)
uri.find(url)(packet, _udp->remoteIP(), _udp->remotePort());
#elif defined(SPARK)
uri[url](packet, _udp->remoteIP(), _udp->remotePort());
#endif
}
}
}
}

// next packet
packetlen = _udp->parsePacket();
}

return true;
}

uint16_t Coap::sendResponse(IPAddress ip, int port, uint16_t messageid) {
this->sendResponse(ip, port, messageid, NULL, 0, COAP_CONTENT, COAP_TEXT_PLAIN, NULL, 0);
}

uint16_t Coap::sendResponse(IPAddress ip, int port, uint16_t messageid, char *payload) {
this->sendResponse(ip, port, messageid, payload, strlen(payload), COAP_CONTENT, COAP_TEXT_PLAIN, NULL, 0);
}

uint16_t Coap::sendResponse(IPAddress ip, int port, uint16_t messageid, char *payload, int payloadlen) {
this->sendResponse(ip, port, messageid, payload, payloadlen, COAP_CONTENT, COAP_TEXT_PLAIN, NULL, 0);
}


uint16_t Coap::sendResponse(IPAddress ip, int port, uint16_t messageid, char *payload, int payloadlen,
COAP_RESPONSE_CODE code, COAP_CONTENT_TYPE type, uint8_t *token, int tokenlen) {
// make packet
CoapPacket packet;

packet.type = COAP_ACK;
packet.code = code;
packet.token = token;
packet.tokenlen = tokenlen;
packet.payload = (uint8_t *)payload;
packet.payloadlen = payloadlen;
packet.optionnum = 0;
packet.messageid = messageid;

// if more options?
char optionBuffer[2];
optionBuffer[0] = ((uint16_t)type & 0xFF00) >> 8;
optionBuffer[1] = ((uint16_t)type & 0x00FF) ;
packet.options[packet.optionnum].buffer = (uint8_t *)optionBuffer;
packet.options[packet.optionnum].length = 2;
packet.options[packet.optionnum].number = COAP_CONTENT_FORMAT;
packet.optionnum++;

return this->sendPacket(packet, ip, port);
}

Loading

0 comments on commit 76f0eea

Please sign in to comment.