Commit f0c088a6 authored by Yann Stephen Mandza's avatar Yann Stephen Mandza Committed by Kishen Maloor

Arduino Port: Support for arduino AVR and ARM MCUs

The port target the arduino AVR (Mega2560) and ARM architectures.
Mostly arduino SAMD(arduino MKRZERO). However, any SAMD board
should work as well as  the arduino Due. The ipadapter for network
connectivity uses  Wiznet w5500 based shield( thus only IPV4 Ethernet
is supported as yet),required  arduino C++ libraries were adapted by
providing C wrappers. A FAT32 SD card is used to provide pesistant
storage for security build.

Note: iotivility lite stack have shown heavy  for the AVR
architecture. Thus the port allows the use of external
memory SRAM (up to 56KBytes).

Bug: https://github.com/yannS2016/iotivity-constrained-arduino/issuesSigned-off-by: yann's avatarYann Stephen Mandza <yann@tlabs.ac.za>
parent 51be7aba
#include "Ethernet2.h"
#include "serial.h"
#include "oc_api.h"
#include "oc_clock.h"
#include "oc_assert.h"
#include "oc_storage.h"
#include "oc_connectivity.h"
#include "util/oc_process.h"
#include "oc_network_events_mutex.h"
#ifdef __AVR__
#ifdef OC_XMEM
void extRAMinit(void)__attribute__ ((used, naked, section (".init3")));
void extRAMinit(void) {
// set up the xmem registers
XMCRB=0;
XMCRA=1<<SRE;
DDRD|=_BV(PD7);
DDRL|=(_BV(PL6)|_BV(PL7));
}
#endif
#endif
OC_PROCESS(sample_client_process, "client");
static int
app_init(void)
{
int ret = oc_init_platform("Apple", NULL, NULL);
ret |= oc_add_device("/oic/d", "oic.d.phone", "Kishen's IPhone", "ocf.1.0.0",
"ocf.res.1.0.0", NULL, NULL);
return ret;
}
#define MAX_URI_LENGTH (30)
static char a_light[MAX_URI_LENGTH];
static oc_endpoint_t *light_server;
static bool state;
static int power;
static oc_string_t name;
static oc_event_callback_retval_t
stop_observe(void *data)
{
(void)data;
OC_DBG("Stopping OBSERVE");
oc_stop_observe(a_light, light_server);
return OC_EVENT_DONE;
}
static void
observe_light(oc_client_response_t *data)
{
OC_DBG("OBSERVE_light:");
oc_rep_t *rep = data->payload;
while (rep != NULL) {
OC_DBG("key %s, value ", oc_string(rep->name));
switch (rep->type) {
case OC_REP_BOOL:
OC_DBG("%d", rep->value.boolean);
state = rep->value.boolean;
break;
case OC_REP_INT:
OC_DBG("%d", rep->value.integer);
power = rep->value.integer;
break;
case OC_REP_STRING:
OC_DBG("%s", oc_string(rep->value.string));
if (oc_string_len(name))
oc_free_string(&name);
oc_new_string(&name, oc_string(rep->value.string),
oc_string_len(rep->value.string));
break;
default:
break;
}
rep = rep->next;
}
}
static void
post2_light(oc_client_response_t *data)
{
OC_DBG("POST2_light:");
if (data->code == OC_STATUS_CHANGED)
OC_DBG("POST response: CHANGED");
else if (data->code == OC_STATUS_CREATED)
OC_DBG("POST response: CREATED");
else
OC_DBG("POST response code %d", data->code);
oc_do_observe(a_light, light_server, NULL, &observe_light, LOW_QOS, NULL);
oc_set_delayed_callback(NULL, &stop_observe, 30);
OC_DBG("Sent OBSERVE request");
}
static void
post_light(oc_client_response_t *data)
{
OC_DBG("POST_light:");
if (data->code == OC_STATUS_CHANGED)
OC_DBG("POST response: CHANGED");
else if (data->code == OC_STATUS_CREATED)
OC_DBG("POST response: CREATED");
else
OC_DBG("POST response code %d", data->code);
if (oc_init_post(a_light, light_server, NULL, &post2_light, LOW_QOS, NULL)) {
oc_rep_start_root_object();
oc_rep_set_boolean(root, state, true);
oc_rep_set_int(root, power, 55);
oc_rep_end_root_object();
if (oc_do_post())
OC_DBG("Sent POST request");
else
OC_DBG("Could not send POST request");
} else
OC_DBG("Could not init POST request");
}
static void
put_light(oc_client_response_t *data)
{
OC_DBG("PUT_light:");
if (data->code == OC_STATUS_CHANGED)
OC_DBG("PUT response: CHANGED");
else
OC_DBG("PUT response code %d", data->code);
if (oc_init_post(a_light, light_server, NULL, &post_light, LOW_QOS, NULL)) {
oc_rep_start_root_object();
oc_rep_set_boolean(root, state, false);
oc_rep_set_int(root, power, 105);
oc_rep_end_root_object();
if (oc_do_post())
OC_DBG("Sent POST request");
else
OC_DBG("Could not send POST request");
} else
OC_DBG("Could not init POST request");
}
static void
get_light(oc_client_response_t *data)
{
OC_DBG("GET_light:");
oc_rep_t *rep = data->payload;
while (rep != NULL) {
OC_DBG("key %s, value ", oc_string(rep->name));
switch (rep->type) {
case OC_REP_BOOL:
OC_DBG("%d", rep->value.boolean);
state = rep->value.boolean;
break;
case OC_REP_INT:
OC_DBG("%d", rep->value.integer);
power = rep->value.integer;
break;
case OC_REP_STRING:
OC_DBG("%s", oc_string(rep->value.string));
if (oc_string_len(name))
oc_free_string(&name);
oc_new_string(&name, oc_string(rep->value.string),
oc_string_len(rep->value.string));
break;
default:
break;
}
rep = rep->next;
}
if (oc_init_put(a_light, light_server, NULL, &put_light, LOW_QOS, NULL)) {
oc_rep_start_root_object();
oc_rep_set_boolean(root, state, true);
oc_rep_set_int(root, power, 15);
oc_rep_end_root_object();
if (oc_do_put())
OC_DBG("Sent PUT request");
else
OC_DBG("Could not send PUT request");
} else
OC_DBG("Could not init PUT request");
}
static oc_discovery_flags_t
discovery(const char *anchor, const char *uri, oc_string_array_t types,
oc_interface_mask_t interfaces, oc_endpoint_t *endpoint,
oc_resource_properties_t bm, void *user_data)
{
(void)anchor;
(void)user_data;
(void)interfaces;
(void)bm;
int i;
int uri_len = strlen(uri);
uri_len = (uri_len >= MAX_URI_LENGTH) ? MAX_URI_LENGTH - 1 : uri_len;
for (i = 0; i < (int)oc_string_array_get_allocated_size(types); i++) {
char *t = oc_string_array_get_item(types, i);
if (strlen(t) == 10 && strncmp(t, "core.light", 10) == 0) {
#ifdef OC_IPV4
#ifdef OC_ESP32 // this is experimental
light_server = endpoint;
#else
light_server = endpoint->next;
#endif
OC_DBG("IPV4 Resource ");
#else
light_server = endpoint;
OC_DBG("IPV6 Resource ");
#endif
strncpy(a_light, uri, uri_len);
a_light[uri_len] = '\0';
OC_DBG("Resource %s hosted at endpoints:", a_light);
oc_endpoint_t *ep = endpoint;
while (ep != NULL) {
PRINTipaddr(*ep);
PRINT("\n");
ep = ep->next;
}
oc_do_get(a_light, light_server, NULL, &get_light, LOW_QOS, NULL);
return OC_STOP_DISCOVERY;
}
}
oc_free_server_endpoints(endpoint);
return OC_CONTINUE_DISCOVERY;
}
static void
issue_requests(void)
{
oc_do_ip_discovery("core.light", &discovery, NULL);
}
static void
signal_event_loop(void)
{
oc_process_post(&sample_client_process, OC_PROCESS_EVENT_TIMER, NULL);
}
OC_PROCESS_THREAD(sample_client_process, ev, data)
{
(void)data;
static struct oc_etimer et;
static const oc_handler_t handler = {.init = app_init,
.signal_event_loop = signal_event_loop,
.requests_entry = issue_requests };
static oc_clock_time_t next_event;
oc_set_mtu_size(1024);
oc_set_max_app_data_size(1024);
OC_PROCESS_BEGIN();
OC_DBG("Initializing client for arduino");
while (ev != OC_PROCESS_EVENT_EXIT) {
oc_etimer_set(&et, (oc_clock_time_t)next_event);
if(ev == OC_PROCESS_EVENT_INIT){
int init = oc_main_init(&handler);
if (init < 0){
OC_DBG("Client Init failed!");
return init;
}
OC_DBG("Client process init!");
}
else if(ev == OC_PROCESS_EVENT_TIMER){
next_event = oc_main_poll();
next_event -= oc_clock_time();
}
OC_PROCESS_WAIT_EVENT();
}
OC_PROCESS_END();
}
// Arduino Ethernet Shield
uint8_t ConnectToNetwork()
{
// Note: ****Update the MAC address here with your shield's MAC address****
uint8_t ETHERNET_MAC[] = {0x90, 0xA2, 0xDA, 0x11, 0x44, 0xA9};
Ethernet.init(5); // CS Pin for MKRZERO
uint8_t error = Ethernet.begin(ETHERNET_MAC);
if (error == 0)
{
OC_ERR("Error connecting to Network: %d", error);
return -1;
}
IPAddress ip = Ethernet.localIP();
OC_DBG("Connected to Ethernet IP: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
return 0;
}
void setup() {
#if defined(__arm__) && defined(__SAMD21G18A__) || defined(__SAM3X8E__)
Serial.begin(250000);
#else
Serial.begin(115200);
#endif
#if defined(__SAMD21G18A__)
while (!Serial) {
}
#endif
if (ConnectToNetwork() != 0)
{
OC_ERR("Unable to connect to network");
return;
}
delay(500);
#ifdef OC_SECURITY
oc_storage_config("creds");
#endif /* OC_SECURITY */
oc_process_start(&sample_client_process, NULL);
delay(200);
}
void loop() {
oc_process_run();
}
#include "Ethernet2.h"
#include "serial.h"
#include "oc_api.h"
#include "oc_clock.h"
#include "oc_assert.h"
#include "oc_storage.h"
#include "oc_connectivity.h"
#include "util/oc_process.h"
#include "oc_network_events_mutex.h"
#ifdef __AVR__
#ifdef OC_XMEM
void extRAMinit(void)__attribute__ ((used, naked, section (".init3")));
void extRAMinit(void) {
// set up the xmem registers
XMCRB=0;
XMCRA=1<<SRE;
DDRD|=_BV(PD7);
DDRL|=(_BV(PL6)|_BV(PL7));
}
#endif
#endif
OC_PROCESS(sample_server_process, "server");
static bool state = false;
int power;
oc_string_t name;
static int
app_init(void)
{
int ret = oc_init_platform("Intel", NULL, NULL);
ret |= oc_add_device("/oic/d", "oic.d.light", "Lamp", "ocf.1.0.0",
"ocf.res.1.0.0", NULL, NULL);
return ret;
}
static void
get_light(oc_request_t *request, oc_interface_mask_t interface, void *user_data)
{
(void)user_data;
++power;
OC_DBG("GET_light:\n");
oc_rep_start_root_object();
switch (interface) {
case OC_IF_BASELINE:
oc_process_baseline_interface(request->resource);
/* fall through */
case OC_IF_RW:
oc_rep_set_boolean(root, state, state);
oc_rep_set_int(root, power, power);
oc_rep_set_text_string(root, name, oc_string(name));
break;
default:
break;
}
oc_rep_end_root_object();
oc_send_response(request, OC_STATUS_OK);
}
static void
post_light(oc_request_t *request, oc_interface_mask_t interface, void *user_data)
{
(void)interface;
(void)user_data;
OC_DBG("POST_light:\n");
oc_rep_t *rep = request->request_payload;
while (rep != NULL) {
OC_DBG(("key: %s "), oc_string(rep->name));
switch (rep->type) {
case OC_REP_BOOL:
state = rep->value.boolean;
OC_DBG("value: %d\n", state);
break;
case OC_REP_INT:
power = rep->value.integer;
OC_DBG("value: %d\n", power);
break;
case OC_REP_STRING:
oc_free_string(&name);
oc_new_string(&name, oc_string(rep->value.string),
oc_string_len(rep->value.string));
break;
default:
oc_send_response(request, OC_STATUS_BAD_REQUEST);
return;
break;
}
rep = rep->next;
}
oc_send_response(request, OC_STATUS_CHANGED);
}
static void
put_light(oc_request_t *request, oc_interface_mask_t interface,
void *user_data)
{
(void)interface;
(void)user_data;
post_light(request, interface, user_data);
}
static void
register_resources(void)
{
oc_resource_t *res = oc_new_resource("Yann's Light", "/a/light", 2, 0);
oc_resource_bind_resource_type(res, "core.light");
oc_resource_bind_resource_type(res, "core.brightlight");
oc_resource_bind_resource_interface(res, OC_IF_RW);
oc_resource_set_default_interface(res, OC_IF_RW);
oc_resource_set_discoverable(res, true);
oc_resource_set_periodic_observable(res, 1);
oc_resource_set_request_handler(res, OC_GET, get_light, NULL);
oc_resource_set_request_handler(res, OC_PUT, put_light, NULL);
oc_resource_set_request_handler(res, OC_POST, post_light, NULL);
oc_add_resource(res);
}
static void
signal_event_loop(void)
{
oc_process_post(&sample_server_process, OC_PROCESS_EVENT_TIMER, NULL);
}
OC_PROCESS_THREAD(sample_server_process, ev, data)
{
(void)data;
static struct oc_etimer et;
static const oc_handler_t handler = {.init = app_init,
.signal_event_loop = signal_event_loop,
.register_resources = register_resources };
static oc_clock_time_t next_event;
oc_set_mtu_size(512);
oc_set_max_app_data_size(880);
OC_PROCESS_BEGIN();
OC_DBG("Initializing server for arduino");
while (ev != OC_PROCESS_EVENT_EXIT) {
oc_etimer_set(&et, (oc_clock_time_t)next_event);
if(ev == OC_PROCESS_EVENT_INIT){
int init = oc_main_init(&handler);
if (init < 0){
OC_DBG("Server Init failed!");
return init;
}
OC_DBG("Server process init!");
}
else if(ev == OC_PROCESS_EVENT_TIMER){
next_event = oc_main_poll();
next_event -= oc_clock_time();
}
OC_PROCESS_WAIT_EVENT();
}
OC_PROCESS_END();
}
// Arduino Ethernet Shield
uint8_t ConnectToNetwork()
{
// Note: ****Update the MAC address here with your shield's MAC address****
uint8_t ETHERNET_MAC[] = {0x92, 0xA1, 0xDA, 0x11, 0x44, 0xA9};
#if defined(__SAMD21G18A__)
Ethernet.init(5); // CS Pin for MKRZERO
#endif
uint8_t error = Ethernet.begin(ETHERNET_MAC);
if (error == 0)
{
OC_ERR("Error connecting to Network: %d", error);
return -1;
}
IPAddress ip = Ethernet.localIP();
OC_DBG("Connected to Ethernet IP: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
return 0;
}
void setup() {
#if defined(__arm__) && defined(__SAMD21G18A__) || defined(__SAM3X8E__)
Serial.begin(250000);
#else
Serial.begin(115200);
#endif
#if defined(__SAMD21G18A__)
while (!Serial) {
}
#endif
if (ConnectToNetwork() != 0)
{
OC_ERR("Unable to connect to network");
return;
}
#ifdef OC_SECURITY
oc_storage_config("creds");
#endif /* OC_SECURITY */
oc_process_start(&sample_server_process, NULL);
delay(200);
}
void loop() {
oc_process_run();
}
include setup.mk
ROOT_DIR =../..
ifneq ($(ARCH),avr)
ifneq ($(ARCH),samd)
ifneq ($(ARCH),sam)
$(error Target Architecture must be define to proceed!)
endif
endif
endif
ifeq ($(APP),server)
LOCAL_CPP_SRCS += server_arduino.cpp
CXXFLAGS += -DOC_SERVER
TARGET = $(ARCH)_server
ifeq ($(XMEM),1)
CXXFLAGS += -DOC_XMEM
endif
else ifeq ($(APP),client)
CXXFLAGS += -DOC_CLIENT
ifeq ($(XMEM),1)
CXXFLAGS += -DOC_XMEM
endif
LOCAL_CPP_SRCS += client_arduino.cpp
TARGET = $ARCH_client
else
$(error Must define an app server/client to proceed!)
endif
ifeq ($(DYNAMIC),1)
CXXFLAGS += -DOC_DYNAMIC_ALLOCATION
endif
ifeq ($(XMEM),1)
CXXFLAGS += -DOC_XMEM
endif
ifeq ($(IPV4),1)
CXXFLAGS += -DOC_IPV4
endif
### Iotivity contrained includes
ifeq ($(ARCH),avr)
DEPS_HEADERS +=$(addprefix -Ideps/, pRNG wiz5500 serial sdFat)
else
DEPS_HEADERS +=$(addprefix -Ideps/, wiz5500 serial sdFat)
endif
CORE_HEADERS +=$(addprefix -I$(ROOT_DIR)/, . messaging/coap util util/pt include api port security deps/mbedtls/include/mbedtls)
LIB_HEADERS +=-I$(ARDUINO_DIR)/libraries/SdFat/src/FatLib
CXXFLAGS += $(DEPS_HEADERS) $(CORE_HEADERS) $(LIB_HEADERS) -Iadapter -Iapps/include
ifeq ($(SECURE),1)
CXXFLAGS +=$(addprefix -I$(ROOT_DIR)/deps/mbedtls/, include include/mbedtls)
CXXFLAGS += -DOC_SECURITY
endif
SERVER_ARCHIVE = build-$(BOARD_TAG)/libarduino-adapter.a
TIME_ARCHIVE = build-$(BOARD_TAG)/libarduino-time.a
WIZ5500_ARCHIVE = build-$(BOARD_TAG)/libarduino-wiz5500.a
SERIAL_ARCHIVE = build-$(BOARD_TAG)/libarduino-serial.a
SDFAT_ARCHIVE = build-$(BOARD_TAG)/libarduino-sdfat.a
DEPS_OBJ += $(TIME_ARCHIVE) $(WIZ5500_ARCHIVE) $(SDFAT_ARCHIVE) $(SERIAL_ARCHIVE)
ifeq ($(ARCH),avr)
PRNG_ARCHIVE = build-$(BOARD_TAG)/libarduino-prng.a
endif
DEPS_OBJ += $(PRNG_ARCHIVE)
SERVER_OBJ = adapter/build-$(BOARD_TAG)/libarduino-adapter.a
OTHER_OBJS += $(SERVER_OBJ)
ifeq ($(ARCH),avr)
OTHER_OBJS += deps/pRNG/$(PRNG_ARCHIVE)
endif
OTHER_OBJS += deps/Time/$(TIME_ARCHIVE)
OTHER_OBJS += deps/wiz5500/$(WIZ5500_ARCHIVE)
OTHER_OBJS += deps/serial/$(SERIAL_ARCHIVE)
OTHER_OBJS += deps/sdFat/$(SDFAT_ARCHIVE)
VPATH=apps/server:$(ROOT_DIR)/apps:
ifeq ($(ARCH),avr)
include avr.mk
else ifeq ($(ARCH),sam)
include sam.mk
else ifeq ($(ARCH),samd)
include samd.mk
else
$(error Target Architecture must be define to proceed!)
endif
$(SERVER_OBJ): $(DEPS_OBJ)
$(MAKE) -C adapter $(SERVER_ARCHIVE)
$(PRNG_ARCHIVE):
$(MAKE) -C deps/pRNG $@
$(TIME_ARCHIVE):
$(MAKE) -C deps/Time $@
$(WIZ5500_ARCHIVE):
$(MAKE) -C deps/wiz5500 $@
$(SERIAL_ARCHIVE) :
$(MAKE) -C deps/serial $@
$(SDFAT_ARCHIVE):
$(MAKE) -C deps/sdFat $@
clean::
$(MAKE) -C adapter clean
ifeq ($(ARCH),avr)
$(MAKE) -C deps/pRNG clean
endif
$(MAKE) -C deps/Time clean
$(MAKE) -C deps/wiz5500 clean
$(MAKE) -C deps/serial clean
$(MAKE) -C deps/sdFat clean
\ No newline at end of file
Porting iotivity constrained to arduino
----------------------------------------
The port target the arduino AVR(Mega2560)and ARM architectures, mostly arduino SAMD(arduino MKRZERO,
though andy SAMD board should work) as well as the arduino Due. Though most feature should be supported
on SAMD boards, use the arduino Due for more stable deployment of secuirty features. The ipadapter
for network requirement is based around the Wiznet w5500 shield, dependant arduino C++ libraries
were adapted by providing C wrappers. an SD card is used to provide pesistant storage for security
build.this is made available via the C adapter libarduino-sdfat
Preparing Arduino Cores and Tools
----------------------------------
I updated socket.cpp based on the mainline iotivity_1_2(arduino adapter) comment regarding the
fix required on the socket.cpp code for the udp receive method the patch is arduino_socket.patch.
apply this patch to your socket.cpp file.
The iotivity constrained logging system is not directly compatible with arduino at it is. More,
it needs to handle different architecture. a library is provided for this as libarduino-serial.
Still one need to patch the oc_log.h to declare the the different logging methods.
The arduino Time library target C++ code, though adding attribute like extern C or _cplusplus,
it uses method overloading that is not acceptable from C perspective. i provide a basic tweak
to make it usable. one can write a C++ class around it a provide a cleaner C wrapper
or just make it plain C.
### Build tools
- Arduino Makefile(https://github.com/sudar/Arduino-Makefile) Used to compile and
upload the hex file to the arduino board. this is will be downloaded and patched,