├── arduino.sh ├── .gitignore ├── Transport.h ├── Strings.h ├── NodePool.h ├── WebCore.cpp ├── Names.h ├── WSEvent.h ├── Transport.cpp ├── WSEvent.cpp ├── Strings.cpp ├── WebCore.h ├── WebThings.h ├── AvlNode.h ├── NodePool.cpp ├── Names.cpp ├── WiznetTCP.h ├── sketch.ino ├── MessageCoder.h ├── DHCP.h ├── JSON.h ├── readme.md ├── AvlNode.cpp ├── WebThings.cpp ├── readme.txt ├── MessageCoder.cpp ├── JSON.cpp └── WiznetTCP.cpp /arduino.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cp ~/Projects/wot-arduino/*.cpp . 3 | cp ~/Projects/wot-arduino/*.h . 4 | rm demo.cpp arduino.h 5 | for i in *.cpp; do 6 | sed -i "" s/PRINTLN/Serial.println/g $i 7 | sed -i "" s/PRINT/Serial.print/g $i 8 | done 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # don't sync node modules that this project pulls in 2 | 3 | .DS_Store 4 | design.txt 5 | readme 6 | notes 7 | test 8 | *.o 9 | 10 | # don't sync Ethercard 11 | # Ethercard/ 12 | 13 | # don't sync Visual Studio files 14 | .ntvs_analysis.dat 15 | bin/ 16 | obj/ 17 | *.njsproj 18 | *.suo 19 | *.sln 20 | 21 | -------------------------------------------------------------------------------- /Transport.h: -------------------------------------------------------------------------------- 1 | // Web of Things transport layer 2 | 3 | #ifndef _WOTF_TRANSPORT 4 | #define _WOTF_TRANSPORT 5 | 6 | class Transport 7 | { 8 | private: 9 | WiznetTCP tcp; 10 | 11 | public: 12 | void start(); 13 | void stop(); 14 | void serve(); 15 | }; 16 | 17 | #endif -------------------------------------------------------------------------------- /Strings.h: -------------------------------------------------------------------------------- 1 | // support for strings in RAM and program memory on the AVR processors 2 | // this merges the address space by using an offset of #8000 3 | 4 | #ifndef _WOTF_STRINGS 5 | #define _WOTF_STRINGS 6 | 7 | #define PROGMEM_BOUNDARY 0x8000 8 | 9 | class Strings 10 | { 11 | public: 12 | 13 | static char get_char(const char *p); 14 | static void print(const char *s, unsigned int len); 15 | static unsigned int strlen(const char *p); 16 | static int strcmp(const char *s1, const char *s2); 17 | static int strcmp(const char *s1, unsigned int len1, 18 | const char *s2, unsigned int len2); 19 | static char *strcpy(char *dst, const char *src); 20 | }; 21 | #endif -------------------------------------------------------------------------------- /NodePool.h: -------------------------------------------------------------------------------- 1 | #ifndef _WOT_NODE_POOL 2 | #define _WOT_NODE_POOL 3 | 4 | #define WOT_NODE_POOL_SIZE 80 5 | 6 | typedef uint8_t NPIndex; 7 | 8 | typedef struct { 9 | char byte[4]; 10 | void *pointer; 11 | } wot_node_pool_t; 12 | 13 | class WotNodePool 14 | { 15 | public: 16 | WotNodePool(); 17 | wot_node_pool_t wot_pool[WOT_NODE_POOL_SIZE]; 18 | unsigned int used; 19 | unsigned int last_allocated; 20 | boolean gc_phase; 21 | 22 | void initialise_node_pool(wot_node_pool_t *buffer, unsigned int size); 23 | unsigned int size(); 24 | float percent_used(); 25 | void *allocate_node(); 26 | void *get_node(unsigned int index); 27 | wot_node_pool_t *get_pool(); 28 | void free(void *node); 29 | }; 30 | #endif -------------------------------------------------------------------------------- /WebCore.cpp: -------------------------------------------------------------------------------- 1 | /* WebCore.cpp - allocating thing & proxy objects from a static pool */ 2 | 3 | #include 4 | #include "NodePool.h" 5 | #include "AvlNode.h" 6 | #include "Names.h" 7 | #include "JSON.h" 8 | #include "WebThings.h" 9 | 10 | static Thing thing_pool[MAX_THINGS]; 11 | static unsigned int things_count; 12 | 13 | CoreThing *ThingPool::allocate() 14 | { 15 | for (unsigned int i = 0; i < MAX_THINGS; ++i) { 16 | if (thing_pool[i].id == 0) 17 | return (CoreThing *)(thing_pool + i); 18 | } 19 | 20 | return (CoreThing *)0; 21 | } 22 | 23 | void ThingPool::free(CoreThing *thing) 24 | { 25 | if (thing->id != 0) { 26 | ++things_count; 27 | thing_pool->id = 0; 28 | } 29 | } 30 | 31 | unsigned int ThingPool::size() 32 | { 33 | return things_count; 34 | } 35 | 36 | float ThingPool::used() 37 | { 38 | // return percentage of allocated nodes 39 | return 100.0 * things_count / (1.0 * MAX_THINGS); 40 | } 41 | -------------------------------------------------------------------------------- /Names.h: -------------------------------------------------------------------------------- 1 | // hash table for symbol dictionary 2 | 3 | #ifndef _WOTF_NAMES 4 | #define _WOTF_NAMES 5 | 6 | #ifndef null 7 | #define null 0 8 | #endif 9 | 10 | // pick the table size based upon practical experience 11 | // call usage() method to measure how full the table is 12 | 13 | #define HASH_TABLE_SIZE 31 14 | 15 | #if defined(pgm_read_byte) 16 | #define PROGMEM_BOUNDARY 0x8000 17 | #endif 18 | 19 | class Names 20 | { 21 | public: 22 | Names(); 23 | #if defined(pgm_read_byte) 24 | unsigned int symbol(const __FlashStringHelper *name); 25 | #endif 26 | unsigned int symbol(const char *name); 27 | unsigned int symbol(const char *name, unsigned int length); 28 | void print(); 29 | float used(); 30 | 31 | private: 32 | class HashEntry 33 | { 34 | public: 35 | 36 | const char *name; 37 | unsigned int length; 38 | unsigned int symbol; 39 | }; 40 | 41 | unsigned int entries; 42 | HashEntry table[HASH_TABLE_SIZE]; 43 | unsigned int hash(const char *name, unsigned int length); 44 | }; 45 | 46 | #endif -------------------------------------------------------------------------------- /WSEvent.h: -------------------------------------------------------------------------------- 1 | #ifndef _WS_EVENT 2 | #define _WS_EVENT 3 | 4 | /* 5 | Event queue support to provide non-overlapping software interrupts for a programming model similar to Web page scripts. 6 | 7 | Interrupt service handlers should disable the interrupt, and push an event onto the queue and then re-enable the interrupt. The loop() function should call dispatch() to handle the events from the queue 8 | */ 9 | 10 | #define EVENT_QUEUE_LENGTH 6 11 | 12 | // add additional event names this enum and update WSEvent.cpp to match 13 | enum Event_t { Network_Readable_Event_t }; 14 | 15 | typedef void (*Event_hander_t)(void *data); 16 | 17 | typedef struct { 18 | Event_t event; 19 | void *data; 20 | } EventQueueEntry; 21 | 22 | class EventQueue 23 | { 24 | private: 25 | int begin; 26 | int count; 27 | EventQueueEntry queue[EVENT_QUEUE_LENGTH]; 28 | EventQueueEntry *dequeue(); 29 | 30 | public: 31 | EventQueue(); 32 | boolean is_empty(); 33 | int get_size(); 34 | boolean enqueue(Event_t event, void *data); 35 | void dispatch(); 36 | void set_handler(Event_t event, Event_hander_t handler); 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /Transport.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "NodePool.h" 4 | #include "AvlNode.h" 5 | #include "Names.h" 6 | #include "JSON.h" 7 | #include "WSEvent.h" 8 | #include "WiznetTCP.h" 9 | #include "WebThings.h" 10 | #include "Transport.h" 11 | 12 | void Transport::start() 13 | { 14 | delay(1000); 15 | tcp.begin(192,168,1,127, 1234); 16 | //tcp.begin(1234); // using DHCP for IP config 17 | tcp.discover_gateway(); 18 | Serial.println(F("started server")); 19 | } 20 | 21 | void Transport::stop() 22 | { 23 | tcp.close(); 24 | } 25 | 26 | #define TCP_BUF_LEN 256 27 | 28 | void Transport::serve() 29 | { 30 | unsigned int n; 31 | 32 | switch (tcp.get_socket_status()) { 33 | case SOCK_INIT: 34 | tcp.listen(); 35 | break; 36 | 37 | case SOCK_CLOSED: 38 | tcp.open(); 39 | break; 40 | 41 | case SOCK_LISTEN: 42 | break; 43 | 44 | case SOCK_ESTABLISHED: 45 | n = tcp.receive_available(); 46 | 47 | if (n) { 48 | char buffer[TCP_BUF_LEN]; 49 | 50 | if (n > TCP_BUF_LEN - 1) 51 | n = TCP_BUF_LEN - 1; 52 | 53 | n = tcp.receive(buffer, n); 54 | buffer[n] = '\0'; 55 | 56 | Serial.print("received "); 57 | Serial.print(n); 58 | Serial.print(" bytes: \""); 59 | Serial.print(buffer); 60 | Serial.println("\""); 61 | 62 | tcp.send(buffer, n); 63 | 64 | // assume no further requests 65 | tcp.disconnect(); 66 | } 67 | 68 | break; 69 | 70 | case SOCK_FIN_WAIT: 71 | case SOCK_CLOSING: 72 | case SOCK_TIME_WAIT: 73 | case SOCK_CLOSE_WAIT: 74 | case SOCK_LAST_ACK: 75 | // force socket to close 76 | tcp.close(); 77 | break; 78 | 79 | default: 80 | break; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /WSEvent.cpp: -------------------------------------------------------------------------------- 1 | // event queue support 2 | 3 | #include 4 | #include "WSEvent.h" 5 | 6 | static Event_hander_t network_readable_event_handler; 7 | 8 | EventQueue::EventQueue() 9 | { 10 | begin = count = 0; 11 | network_readable_event_handler = NULL; 12 | } 13 | 14 | boolean EventQueue::is_empty() 15 | { 16 | return count == 0; 17 | } 18 | 19 | int EventQueue::get_size() 20 | { 21 | return count; 22 | } 23 | 24 | boolean EventQueue::enqueue(Event_t event, void *data) 25 | { 26 | EventQueueEntry *entry; 27 | int index = begin + count; 28 | 29 | if (count >= EVENT_QUEUE_LENGTH) { 30 | //cout << "** event queue overflow\n"; 31 | // should record error somewhere accessible 32 | Serial.println("event queue overflow"); 33 | return false; 34 | } 35 | 36 | if (index >= EVENT_QUEUE_LENGTH) 37 | index -= EVENT_QUEUE_LENGTH; 38 | entry = queue + index; 39 | entry->event = event; 40 | entry->data = data; 41 | ++count; 42 | return true; 43 | } 44 | 45 | EventQueueEntry *EventQueue::dequeue() 46 | { 47 | if (count == 0) 48 | return NULL; 49 | 50 | noInterrupts(); 51 | int index = begin; 52 | 53 | if (++begin >= EVENT_QUEUE_LENGTH) 54 | begin = 0; 55 | 56 | --count; 57 | interrupts(); 58 | return queue + index; 59 | } 60 | 61 | // called from sketch loop function 62 | void EventQueue::dispatch() 63 | { 64 | EventQueueEntry *entry; 65 | 66 | while ((entry = dequeue())) 67 | { 68 | if (entry->event == Network_Readable_Event_t && network_readable_event_handler) 69 | (*network_readable_event_handler)(entry->data); 70 | } 71 | } 72 | 73 | void EventQueue::set_handler(Event_t event, Event_hander_t handler) 74 | { 75 | if (event == Network_Readable_Event_t ) 76 | network_readable_event_handler = handler; 77 | } 78 | -------------------------------------------------------------------------------- /Strings.cpp: -------------------------------------------------------------------------------- 1 | // some common operations on strings that are designed to work 2 | // whether the strings are stored in AVR RAM or program memory 3 | 4 | #include 5 | #include "Strings.h" 6 | 7 | char Strings::get_char(const char *p) 8 | { 9 | #if defined(pgm_read_byte) 10 | if ((unsigned int)p >= PROGMEM_BOUNDARY) { 11 | return (char)pgm_read_byte(p - PROGMEM_BOUNDARY); 12 | } else 13 | return *p; 14 | #else 15 | return *p; 16 | #endif 17 | } 18 | 19 | unsigned int Strings::strlen(const char *s) 20 | { 21 | unsigned int len = 0; 22 | 23 | while (get_char(s++)) 24 | ++len; 25 | 26 | return len; 27 | } 28 | 29 | void Strings::print(const char *s, unsigned int len) 30 | { 31 | Serial.print(len); 32 | Serial.print(F(" \"")); 33 | while (len--) 34 | Serial.print(get_char(s++)); 35 | Serial.print(F("\"")); 36 | } 37 | 38 | // for null terminated strings 39 | int Strings::strcmp(const char *s1, const char *s2) 40 | { 41 | char c1, c2; 42 | 43 | while ((c1 = get_char(s1)) == (c2 = get_char(s2))) 44 | { 45 | if (!c1) 46 | return 0; 47 | 48 | s1++; 49 | s2++; 50 | } 51 | 52 | return ((unsigned)c1 - (unsigned)c2); 53 | } 54 | 55 | // for strings with known length 56 | int Strings::strcmp(const char *s1, unsigned int len1, 57 | const char *s2, unsigned int len2) 58 | { 59 | if (len1 && len2) 60 | { 61 | while(--len1 && --len2 && (get_char(s1)==get_char(s2))) 62 | { 63 | s1++; 64 | s2++; 65 | } 66 | 67 | return ((unsigned)get_char(s1)-(unsigned)get_char(s2)); 68 | } 69 | 70 | if (!(len1 | len2)) 71 | return 0; 72 | 73 | return (len1 ? 1 : -1); 74 | } 75 | 76 | char *Strings::strcpy(char *dst, const char *src) 77 | { 78 | while ((*dst = get_char(src++))) 79 | ++dst; 80 | 81 | return dst; 82 | } 83 | -------------------------------------------------------------------------------- /WebCore.h: -------------------------------------------------------------------------------- 1 | // Web of Things Core class for Arduino IDE 2 | 3 | #ifndef _WOTF_CORE 4 | #define _WOTF_CORE 5 | 6 | // forward references 7 | 8 | class Thing; 9 | class JSON; 10 | 11 | #define MAX_THINGS 8 12 | 13 | typedef uint8_t CTIndex; 14 | 15 | typedef void *Any; 16 | typedef void (*SetupFunc)(Thing *thing, Names *table); 17 | typedef void (*ThingFunc)(Thing *thing); 18 | typedef void (*EventFunc)(Symbol event, Thing *thing, ...); 19 | typedef void (*ActionFunc)(unsigned int proxy_id, unsigned int action_id, ...); 20 | typedef void (*ResponseFunc)(unsigned int id, Thing *thing, ...); 21 | 22 | // abstract class with methods common to Thing and Proxy 23 | class CoreThing 24 | { 25 | public: 26 | Symbol id; // identifies this thing in messages 27 | char *uri; // URI for a thing or proxy 28 | NPIndex model; // parsed JSON-LD model 29 | NPIndex events; // observers for each event 30 | NPIndex properties; // property values in JSON 31 | NPIndex actions; // functions implementing each action 32 | NPIndex proxies; // set of registered proxies for this thing 33 | CoreThing *next; // linked list of registered things/proxies 34 | 35 | virtual void register_observer(Symbol event, EventFunc handler) = 0; 36 | virtual void raise_event(Symbol event, ...) = 0; 37 | virtual void set_property(Symbol property, JSON *value) = 0; 38 | virtual JSON *get_property(Symbol property) = 0; 39 | virtual int invoke(Symbol action, ...) = 0; 40 | virtual void reachable(boolean phase) = 0; 41 | virtual void sweep(boolean phase) = 0; 42 | virtual void remove() = 0; 43 | }; 44 | 45 | // for managing allocation of thing and proxy objects from static pool 46 | class ThingPool 47 | { 48 | public: 49 | static CoreThing *allocate(); 50 | static void free(CoreThing *thing); 51 | static unsigned int size(); 52 | static float used(); 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /WebThings.h: -------------------------------------------------------------------------------- 1 | // Web of Things Framework for Arduino IDE 2 | 3 | #ifndef _WOTF_FRAMEWORK 4 | #define _WOTF_FRAMEWORK 5 | 6 | #include "WebCore.h" 7 | 8 | #define MAX_STALE 10 9 | 10 | class Thing : public CoreThing 11 | { 12 | public: 13 | void set_start(ThingFunc handler); 14 | void set_stop(ThingFunc handler); 15 | void set_property(Symbol property, JSON *value); 16 | JSON *get_property(Symbol property); 17 | int invoke(Symbol action, ...); 18 | void register_action_handler(Symbol action, ActionFunc handler); 19 | void register_observer(Symbol event, EventFunc handler); 20 | void raise_event(Symbol event, ...); 21 | void reachable(boolean phase); 22 | void sweep(boolean phase); 23 | void remove(); 24 | void print(); 25 | }; 26 | 27 | class Proxy : public CoreThing 28 | { 29 | public: 30 | void set_property(Symbol property, JSON *value); 31 | JSON *get_property(Symbol property); 32 | int invoke(Symbol action, ...); 33 | void register_response_handler(Symbol action, ResponseFunc handler); 34 | void register_observer(Symbol event, EventFunc handler); 35 | void raise_event(Symbol event, ...); 36 | void reachable(boolean phase); 37 | void sweep(boolean phase); 38 | void remove(); 39 | void print(); 40 | }; 41 | 42 | class WebThings 43 | { 44 | public: 45 | WebThings(); 46 | static unsigned int used(); 47 | #if defined(pgm_read_byte) 48 | static void thing(const char *name, const __FlashStringHelper *model, SetupFunc setup); 49 | #endif 50 | static void thing(const char *name, char *model, SetupFunc setup); 51 | static void register_proxy(const char *uri, ThingFunc setup); 52 | static JSON *get_json(NPIndex index); 53 | static NPIndex get_index(JSON *json); 54 | static void collect_garbage(); 55 | static void remove_thing(Thing *thing); 56 | static void remove_proxy(Proxy *proxy); 57 | static void add_stale(JSON *json); 58 | 59 | private: 60 | static void reachable(NPIndex index); 61 | static Thing *find_thing(const char *name); 62 | static Proxy *find_proxy(const char *name); 63 | static void register_thing(Thing *thing); 64 | static boolean strcmp(const char *s1, const char *s2); 65 | }; 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /AvlNode.h: -------------------------------------------------------------------------------- 1 | // AvlTrees - a variant on balanced binary trees 2 | 3 | #ifndef _WOTF_AVLNODE 4 | #define _WOTF_AVLNODE 5 | 6 | #ifndef null 7 | #define null 0 8 | #endif 9 | 10 | #define AVL_MAX_INDEX 255 11 | 12 | typedef uint8_t AvlIndex; // index into memory pool of AvlNodes 13 | typedef uint8_t AvlKey; // a symbol or array index 14 | typedef void *AvlValue; // e.g. pointer to a JSON object 15 | typedef void (*AvlApplyFn)(AvlKey key, AvlValue value, void *data); 16 | 17 | // key must be a positive integer as 0 is used to denote null 18 | // if at some point we want to have arrays with both named 19 | // *and* numeric indices, then it will be appropriate to make 20 | // the key into a signed integer with negative values as 21 | // symbols denoting named indices, and positive values as 22 | // numeric indices 23 | 24 | class AvlNode 25 | { 26 | public: 27 | 28 | AvlKey get_key(); 29 | AvlValue get_value(); 30 | 31 | static void initialise_pool(WotNodePool *wot_node_pool); 32 | static AvlNode * get_node(AvlIndex index); 33 | static AvlValue find_key(AvlIndex tree, AvlKey key); 34 | static AvlIndex insert_key(AvlIndex tree, AvlKey key, AvlValue value); 35 | static AvlIndex first(AvlIndex tree); 36 | static AvlIndex last(AvlIndex tree); 37 | static AvlKey last_key(AvlIndex tree); 38 | static unsigned int get_size(AvlIndex tree); 39 | static void apply(AvlIndex tree, AvlApplyFn applyFn, void *data); 40 | static void free(AvlIndex tree); 41 | static void print_keys(AvlIndex tree); 42 | static void print(AvlIndex tree); 43 | 44 | private: 45 | 46 | uint8_t height; 47 | int8_t left; 48 | int8_t right; 49 | AvlKey key; 50 | AvlValue value; 51 | 52 | static unsigned int length; 53 | static unsigned int size; 54 | static AvlNode *pool; 55 | 56 | static AvlIndex new_node(AvlKey key, AvlValue value); 57 | 58 | 59 | static int tree_height(AvlIndex tree); 60 | static int node_height(AvlIndex node); 61 | static AvlIndex left_left(AvlIndex p5); 62 | static AvlIndex right_right(AvlIndex p3); 63 | static AvlIndex left_right(AvlIndex p5); 64 | static AvlIndex right_left(AvlIndex p3); 65 | static int get_balance(AvlIndex tree); 66 | static AvlIndex balance(AvlIndex tree); 67 | }; 68 | 69 | #endif -------------------------------------------------------------------------------- /NodePool.cpp: -------------------------------------------------------------------------------- 1 | /* a static memory pool for JSON and AvlNode objects */ 2 | 3 | #include 4 | #include "NodePool.h" 5 | #include "AvlNode.h" 6 | #include "Names.h" 7 | #include "JSON.h" 8 | #include "WebThings.h" 9 | 10 | WotNodePool::WotNodePool() 11 | { 12 | used = 0; // index for allocation next node 13 | last_allocated = WOT_NODE_POOL_SIZE - 1; 14 | } 15 | 16 | unsigned int WotNodePool::size() 17 | { 18 | return WOT_NODE_POOL_SIZE * sizeof(wot_node_pool_t); 19 | } 20 | 21 | float WotNodePool::percent_used() 22 | { 23 | // return percentage of allocated nodes 24 | return 100.0 * used / (1.0 * WOT_NODE_POOL_SIZE); 25 | } 26 | 27 | // dumb allocator - I should form free nodes into a linked list 28 | void *WotNodePool::allocate_node() 29 | { 30 | if (used >= WOT_NODE_POOL_SIZE) 31 | WebThings::collect_garbage(); 32 | 33 | if (used < WOT_NODE_POOL_SIZE) { 34 | for (unsigned int i = last_allocated + 1; i < WOT_NODE_POOL_SIZE; ++i) { 35 | if (wot_pool[i].byte[0] == 0) { 36 | used++; 37 | last_allocated = i; 38 | return (void *)(wot_pool + i); 39 | } 40 | } 41 | } 42 | 43 | if (used < WOT_NODE_POOL_SIZE) { 44 | for (unsigned int i = 0; i < last_allocated + 1; ++i) { 45 | if (wot_pool[i].byte[0] == 0) { 46 | used++; 47 | last_allocated = i; 48 | return (void *)(wot_pool + i); 49 | } 50 | } 51 | } 52 | 53 | // should record this in EEPROM then restart server 54 | Serial.println(F("Error: exhausted node pool")); 55 | return 0; 56 | } 57 | 58 | void *WotNodePool::get_node(unsigned int index) 59 | { 60 | if (index < WOT_NODE_POOL_SIZE) 61 | return (void *)(wot_pool + index); 62 | 63 | return 0; 64 | } 65 | 66 | wot_node_pool_t *WotNodePool::get_pool() 67 | { 68 | return wot_pool; 69 | } 70 | 71 | void WotNodePool::free(void *node) 72 | { 73 | // check for valid pointer to node pool and 74 | // take care to avoid freeing a node twice 75 | // since we would then mess up the used count 76 | 77 | if ((wot_node_pool_t *)node >= wot_pool && 78 | (wot_node_pool_t *)node < wot_pool + WOT_NODE_POOL_SIZE && 79 | ((char *)node)[0]) { 80 | memset((char *)node, 0, sizeof(wot_node_pool_t)); 81 | 82 | if (used) 83 | --used; 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /Names.cpp: -------------------------------------------------------------------------------- 1 | /* simple fixed size hashtable with no chaining for use on constrained devices */ 2 | 3 | #include 4 | #include "Strings.h" 5 | #include "Names.h" 6 | 7 | Names::Names() 8 | { 9 | this->entries = 0; 10 | memset(&table[0], 0, sizeof(HashEntry) * HASH_TABLE_SIZE); 11 | } 12 | 13 | float Names::used() 14 | { 15 | // return percentage table is filled 16 | return 100.0 * entries / (1.0 * HASH_TABLE_SIZE); 17 | } 18 | 19 | void Names::print() 20 | { 21 | Serial.print(F("Hash table has ")); 22 | Serial.print(entries); 23 | Serial.print(F(" entries, ")); 24 | Serial.print(used()); 25 | Serial.println(F("% full")); 26 | 27 | for (int i = HASH_TABLE_SIZE; i > 0; ) 28 | { 29 | HashEntry *entry = table + (--i); 30 | 31 | if (entry->name) 32 | { 33 | char *p = (char *)entry->name; 34 | Serial.print(" "); 35 | 36 | for (int j = entry->length; j; --j) 37 | Serial.print(Strings::get_char(p++)); 38 | 39 | Serial.print(" : "); 40 | Serial.println(entry->symbol); 41 | } 42 | } 43 | 44 | } 45 | 46 | unsigned int Names::hash(const char *name, unsigned int length) 47 | { 48 | // Jenkins One-at-a-Time hash 49 | unsigned h = 0; 50 | 51 | while (length--) 52 | { 53 | h += (unsigned char)Strings::get_char(name++); 54 | h += ( h << 10 ); 55 | h ^= ( h >> 6 ); 56 | } 57 | 58 | h += ( h << 3 ); 59 | h ^= ( h >> 11 ); 60 | h += ( h << 15 ); 61 | 62 | return h; 63 | } 64 | 65 | #if defined(pgm_read_byte) 66 | unsigned int Names::symbol(const __FlashStringHelper *name) 67 | { 68 | return symbol(((const char *)name)+PROGMEM_BOUNDARY); 69 | } 70 | #endif 71 | 72 | unsigned int Names::symbol(const char *name) 73 | { 74 | return symbol(name, Strings::strlen(name)); 75 | } 76 | 77 | unsigned int Names::symbol(const char *name, unsigned int length) 78 | { 79 | unsigned int i = HASH_TABLE_SIZE; 80 | unsigned int hashval = hash(name, length); 81 | unsigned int index = hashval % HASH_TABLE_SIZE; 82 | HashEntry *entry = 0; 83 | 84 | while (i-- && (entry = table+index, entry->name)) 85 | { 86 | // symbol already defined 87 | if (!Strings::strcmp(entry->name, entry->length, name, length)) 88 | return entry->symbol; 89 | 90 | index = ++index % HASH_TABLE_SIZE; 91 | } 92 | 93 | // need to define new symbol 94 | if (i && entry) 95 | { 96 | entry->name = name; 97 | entry->length = length; 98 | entry->symbol = entries++; 99 | return entry->symbol; 100 | } 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /WiznetTCP.h: -------------------------------------------------------------------------------- 1 | #ifndef _WIZNET_TCP 2 | #define _WIZNET_TCP 3 | 4 | /* 5 | TCP driver for Wiznet W5100 chip via SPI interface 6 | 7 | This supports client and server connections. For server, the 8 | listen method waits until a client connects to the socket. 9 | The server thus can only process one connection at a time. 10 | */ 11 | 12 | // W5100 Socket status 13 | 14 | #define SOCK_CLOSED 0 15 | #define SOCK_INIT 19 16 | #define SOCK_LISTEN 20 17 | #define SOCK_SYNSENT 21 18 | #define SOCK_SYNRECV 22 19 | #define SOCK_ESTABLISHED 23 20 | #define SOCK_FIN_WAIT 24 21 | #define SOCK_CLOSING 26 22 | #define SOCK_TIME_WAIT 27 23 | #define SOCK_CLOSE_WAIT 28 24 | #define SOCK_LAST_ACK 29 25 | #define SOCK_UDP 34 26 | #define SOCK_IPRAW 50 27 | #define SOCK_MACRAW 66 28 | #define SOCK_PPPOE 95 29 | 30 | typedef unsigned char uint8_t; 31 | typedef unsigned int uint16_t; 32 | typedef unsigned long uint32_t; 33 | 34 | class WiznetTCP 35 | { 36 | private: 37 | uint32_t gateway_ip; 38 | uint16_t gateway_port; 39 | 40 | void spi_init(); 41 | void get_data(uint16_t ptr, uint8_t *buffer, uint16_t length); 42 | void put_data(uint16_t ptr, uint8_t *buffer, uint16_t length); 43 | uint8_t run_DHCP_client(void); 44 | uint8_t handle_mDNS_response(uint8_t *query); 45 | uint16_t get_link(uint16_t offset); 46 | uint16_t parse_name(uint16_t offset, uint8_t *buffer, 47 | uint8_t *sn, uint8_t *matched); 48 | void dump_buffer(uint8_t *buffer, uint16_t length); 49 | 50 | public: 51 | WiznetTCP(); 52 | ~WiznetTCP(); 53 | void begin(uint16_t port); 54 | void begin(uint8_t n0, uint8_t n1, uint8_t n2, uint8_t n3, uint16_t port); 55 | bool listen(); 56 | void disconnect(); 57 | bool connect(uint8_t n0, uint8_t n1, uint8_t n2, uint8_t n3, uint16_t port); 58 | void open(); 59 | void close(); 60 | void discover_gateway(); 61 | 62 | void print_mac_address(); 63 | 64 | uint8_t get_socket_status(); 65 | uint16_t send_available(); 66 | uint16_t send(char *buffer, uint16_t length); 67 | uint16_t send_mac(char *buffer, uint16_t length); 68 | uint16_t receive_available(); 69 | uint16_t receive_available(uint16_t ms); 70 | uint16_t flush_receive(); 71 | uint16_t skip(uint16_t length); 72 | uint16_t peek(uint16_t offset, char *buffer, uint16_t length); 73 | uint16_t receive(char *buffer, uint16_t length); 74 | uint16_t receive(char *buffer, uint16_t length, uint32_t *ip, uint16_t *port); 75 | 76 | void set_ip(uint32_t ip); 77 | void set_ip(uint8_t n0, uint8_t n1, uint8_t n2, uint8_t n3); 78 | void get_ip(uint8_t *n0, uint8_t *n1, uint8_t *n2, uint8_t *n3); 79 | void get_local_ip(uint8_t *n0, uint8_t *n1, uint8_t *n2, uint8_t *n3); 80 | void set_port(uint16_t port); 81 | uint16_t get_port(); 82 | uint16_t get_local_port(); 83 | }; 84 | 85 | #endif -------------------------------------------------------------------------------- /sketch.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define null 0 15 | 16 | // an example of a function used to initialise a Thing's implementation 17 | // here you should set event observers, initialise property values and 18 | // binding the thing's actions to the functions you provide for them. 19 | // note that this is your last chance to look up properties by name! 20 | 21 | #if 0 22 | void setup_agent(Thing *thing, Names *names) 23 | { 24 | // CoreThing *door = thing->get_property(names->get_symbol("door")); 25 | } 26 | 27 | void setup_door(Thing *thing, Names *names) 28 | { 29 | } 30 | 31 | void setup_light(Thing *thing, Names *names) 32 | { 33 | } 34 | #endif 35 | 36 | //Names names; // to pull in hash table 37 | WebThings wot; // sets up node pool 38 | Transport transport; // TCP client/server 39 | EventQueue event_queue; // sets up event queue 40 | 41 | Thing *test1; 42 | 43 | void size() 44 | { 45 | Serial.print(F("Node Pool is using ")); 46 | Serial.print(wot.used()); 47 | Serial.println(F(" nodes")); 48 | } 49 | 50 | void setup_test(Thing *thing, Names *names) 51 | { 52 | test1 = thing; 53 | 54 | Symbol foo = names->symbol(F("foo")); 55 | Symbol age = names->symbol(F("age")); 56 | names->print(); 57 | 58 | size(); 59 | thing->print(); 60 | 61 | Serial.println(F("set foo to \"hello\"")); 62 | thing->set_property(foo, JSON::new_string(F("hello"))); 63 | 64 | size(); 65 | thing->print(); 66 | 67 | Serial.println("set foo to \"world\""); 68 | thing->set_property(foo, JSON::new_string(F("world"))); 69 | 70 | size(); 71 | thing->print(); 72 | 73 | //WebThings::collect_garbage(); 74 | 75 | //size(); 76 | 77 | //thing->print(); 78 | 79 | Serial.println(F("update property foo to an object with age=42")); 80 | JSON *obj = JSON::new_object(); 81 | obj->insert_property(age, JSON::new_unsigned(42)); 82 | thing->set_property(foo, obj); 83 | 84 | WebThings::collect_garbage(); 85 | 86 | size(); 87 | thing->print(); 88 | 89 | Serial.println(F("update property foo to true")); 90 | thing->set_property(foo, JSON::new_boolean(true)); 91 | 92 | size(); 93 | thing->print(); 94 | 95 | WebThings::collect_garbage(); 96 | 97 | size(); 98 | thing->print(); 99 | } 100 | 101 | void setup() { 102 | Serial.begin(19200); 103 | transport.start(); 104 | 105 | #define TEST_MODEL \ 106 | "{\"properties\": {\"pressure\": \"bar\"}}" 107 | 108 | // wot.thing("test1", F(TEST_MODEL), setup_test); 109 | } 110 | 111 | void loop() { 112 | // put your main code here, to run repeatedly: 113 | 114 | event_queue.dispatch(); // queued by interrupt services routines 115 | transport.serve(); 116 | } 117 | 118 | -------------------------------------------------------------------------------- /MessageCoder.h: -------------------------------------------------------------------------------- 1 | #ifndef _WOTF_MESSAGECODER 2 | #define _WOTF_MESSAGECODER 3 | 4 | // unsigned char tag codes 5 | 6 | #define WOT_END_OBJECT 0 7 | #define WOT_START_OBJECT 1 8 | #define WOT_END_ARRAY 2 9 | #define WOT_START_ARRAY 3 10 | #define WOT_STRING 4 11 | #define WOT_UNSIGNED_INT_8 5 12 | #define WOT_UNSIGNED_INT_16 6 13 | #define WOT_UNSIGNED_INT_32 7 14 | #define WOT_SIGNED_INT_8 8 15 | #define WOT_SIGNED_INT_16 9 16 | #define WOT_SIGNED_INT_32 10 17 | #define WOT_FLOAT_32 11 18 | #define WOT_VALUE_TRUE 12 19 | #define WOT_VALUE_FALSE 13 20 | #define WOT_VALUE_NULL 14 21 | 22 | // used to signal decoding overrun 23 | #define WOT_BUFFER_EMPTY 256 24 | 25 | // tags in range 15-22 are reserved for future use 26 | 27 | #define WOT_RESERVED_START 15 28 | #define WOT_RESRVED_END 22 29 | 30 | // tags in range 23-54 are integers (0-31) 31 | 32 | #define WOT_NUM_BASE 23 33 | 34 | // tags in range 55-255 are symbols (1 through 200) 35 | 36 | #define WOT_SYM_BASE 55 37 | 38 | class MessageBuffer 39 | { 40 | private: 41 | unsigned char *buffer; 42 | unsigned int length; 43 | unsigned int index; 44 | unsigned int size; 45 | int big_endian; 46 | bool overflow; 47 | 48 | public: 49 | boolean is_big_endian(); 50 | void set_buffer(unsigned char *buf, unsigned len); 51 | void reset(); 52 | void restart(); 53 | unsigned char * get_pointer(); 54 | boolean overflowed(); 55 | unsigned int get_size(); 56 | unsigned int get_byte(); 57 | unsigned int view_byte(); 58 | unsigned int remaining(); 59 | bool put_byte(unsigned char c); 60 | }; 61 | 62 | class MessageCoder 63 | { 64 | private: 65 | static boolean decode_value(MessageBuffer *buffer); 66 | static boolean decode_object(MessageBuffer *buffer); 67 | static boolean decode_array(MessageBuffer *buffer); 68 | 69 | public: 70 | //static void test(); 71 | 72 | static boolean decode(MessageBuffer *buffer); 73 | static void encode_unsigned8(MessageBuffer *buffer, unsigned char n); 74 | static void encode_unsigned16(MessageBuffer *buffer, uint16_t n); 75 | static void encode_unsigned32(MessageBuffer *buffer, uint32_t n); 76 | static void encode_signed8(MessageBuffer *buffer, char n); 77 | static void encode_signed16(MessageBuffer *buffer, int16_t n); 78 | static void encode_signed32(MessageBuffer *buffer, int32_t n); 79 | static void encode_float(MessageBuffer *buffer, float x); 80 | static void encode_null(MessageBuffer *buffer); 81 | static void encode_true(MessageBuffer *buffer); 82 | static void encode_false(MessageBuffer *buffer); 83 | static void encode_symbol(MessageBuffer *buffer, unsigned int sym); 84 | static void encode_string(MessageBuffer *buffer, unsigned char *str); 85 | #if defined(pgm_read_byte) 86 | static void encode_string(MessageBuffer *buffer, const __FlashStringHelper *str); 87 | #endif 88 | static void encode_object_start(MessageBuffer *buffer); 89 | static void encode_object_end(MessageBuffer *buffer); 90 | static void encode_array_start(MessageBuffer *buffer); 91 | static void encode_array_end(MessageBuffer *buffer); 92 | }; 93 | 94 | #endif -------------------------------------------------------------------------------- /DHCP.h: -------------------------------------------------------------------------------- 1 | /* 2 | Basic DHCP client to configure device's local IP address 3 | With thanks to Nabeel Ahmad (nbl14@hotmail.com) who 4 | in turn adapted it from code by Wiznet 5 | */ 6 | 7 | #ifndef _DHCP_H_ 8 | #define _DHCP_H_ 9 | 10 | // DHCP state machine 11 | #define STATE_DHCP_DISCOVER 1 12 | #define STATE_DHCP_REQUEST 2 13 | #define STATE_DHCP_LEASED 3 14 | #define STATE_DHCP_REREQUEST 4 15 | #define STATE_DHCP_RELEASE 5 16 | 17 | #define MAX_DHCP_RETRY 10 // maximum number of retries 18 | #define DHCP_WAIT_TIME 2000 // milliseconds between retries 19 | 20 | #define DHCP_FLAGSBROADCAST 0x0080 21 | 22 | // UDP port numbers for DHCP 23 | #define DHCP_SERVER_PORT 67 // from server to client 24 | #define DHCP_CLIENT_PORT 68 // from client to server 25 | 26 | // DHCP message OP code 27 | #define DHCP_BOOTREQUEST 1 28 | #define DHCP_BOOTREPLY 2 29 | 30 | // DHCP message type 31 | #define DHCP_DISCOVER 1 32 | #define DHCP_OFFER 2 33 | #define DHCP_REQUEST 3 34 | #define DHCP_DECLINE 4 35 | #define DHCP_ACK 5 36 | #define DHCP_NAK 6 37 | #define DHCP_RELEASE 7 38 | #define DHCP_INFORM 8 39 | 40 | // DHCP RETRANSMISSION TIMEOUT (microseconds) 41 | #define DHCP_INITIAL_RTO (4*1000000) 42 | #define DHCP_MAX_RTO (64*1000000) 43 | 44 | #define DHCP_HTYPE10MB 1 45 | #define DHCP_HTYPE100MB 2 46 | 47 | #define DHCP_HLENETHERNET 6 48 | #define DHCP_HOPS 0 49 | #define DHCP_SECS 0 50 | 51 | #define MAGIC_COOKIE 0x63825363 52 | 53 | #define DEFAULT_LEASETIME 0xffffffff // infinite lease time 54 | #define MAX_DHCP_options 16 55 | 56 | // DHCP option and value (cf. RFC1533) 57 | 58 | enum 59 | { 60 | padOption = 0, 61 | subnetMask = 1, 62 | timerOffset = 2, 63 | routersOnSubnet = 3, 64 | timeServer = 4, 65 | nameServer = 5, 66 | dns = 6, 67 | logServer = 7, 68 | cookieServer = 8, 69 | lprServer = 9, 70 | impressServer = 10, 71 | resourceLocationServer = 11, 72 | hostName = 12, 73 | bootFileSize = 13, 74 | meritDumpFile = 14, 75 | domainName = 15, 76 | swapServer = 16, 77 | rootPath = 17, 78 | extentionsPath = 18, 79 | IPforwarding = 19, 80 | nonLocalSourceRouting = 20, 81 | policyFilter = 21, 82 | maxDgramReasmSize = 22, 83 | defaultIPTTL = 23, 84 | pathMTUagingTimeout = 24, 85 | pathMTUplateauTable = 25, 86 | ifMTU = 26, 87 | allSubnetsLocal = 27, 88 | broadcastAddr = 28, 89 | performMaskDiscovery = 29, 90 | maskSupplier = 30, 91 | performRouterDiscovery = 31, 92 | routerSolicitationAddr = 32, 93 | staticRoute = 33, 94 | trailerEncapsulation = 34, 95 | arpCacheTimeout = 35, 96 | ethernetEncapsulation = 36, 97 | tcpDefaultTTL = 37, 98 | tcpKeepaliveInterval = 38, 99 | tcpKeepaliveGarbage = 39, 100 | nisDomainName = 40, 101 | nisServers = 41, 102 | ntpServers = 42, 103 | vendorSpecificInfo = 43, 104 | netBIOSnameServer = 44, 105 | netBIOSdgramDistServer = 45, 106 | netBIOSnodeType = 46, 107 | netBIOSscope = 47, 108 | xFontServer = 48, 109 | xDisplayManager = 49, 110 | dhcpRequestedIPaddr = 50, 111 | dhcpIPaddrLeaseTime = 51, 112 | dhcpOptionOverload = 52, 113 | dhcpMessageType = 53, 114 | dhcpServerIdentifier = 54, 115 | dhcpParamRequest = 55, 116 | dhcpMsg = 56, 117 | dhcpMaxMsgSize = 57, 118 | dhcpT1value = 58, 119 | dhcpT2value = 59, 120 | dhcpClassIdentifier = 60, 121 | dhcpClientIdentifier = 61, 122 | endOption = 255 123 | }; 124 | 125 | typedef unsigned char uint8_t; 126 | typedef unsigned int uint16_t; 127 | typedef unsigned long uint32_t; 128 | 129 | // for the DHCP message see http://www.tcpipguide.com/free/t_DHCPMessageFormat.htm 130 | 131 | typedef struct _RIP_MSG 132 | { 133 | uint8_t op; 134 | uint8_t htype; 135 | uint8_t hlen; 136 | uint8_t hops; 137 | uint32_t xid; 138 | uint16_t secs; 139 | uint16_t flags; 140 | uint8_t ciaddr[4]; 141 | uint8_t yiaddr[4]; 142 | uint8_t siaddr[4]; 143 | uint8_t giaddr[4]; 144 | uint8_t chaddr[16]; 145 | uint8_t sname[64]; 146 | uint8_t file[128]; 147 | uint8_t options[312]; 148 | } RIP_MSG; 149 | 150 | #endif -------------------------------------------------------------------------------- /JSON.h: -------------------------------------------------------------------------------- 1 | // Minimal JSON support for Arduino 2 | 3 | #ifndef _WOTF_JSON 4 | #define _WOTF_JSON 5 | 6 | #ifndef null 7 | #define null 0 8 | #endif 9 | 10 | 11 | // Function and Proxy are special nodes that are used for things 12 | enum Json_Tag { Unused_t, // used to identify unused JSON nodes 13 | Object_t, Array_t, String_t, Unsigned_t, 14 | Signed_t, Float_t, Boolean_t, Null_t, 15 | Function_t, Proxy_t, Thing_t }; 16 | 17 | enum Json_Token {Error_token, String_token, Colon_token, Comma_token, 18 | Object_start_token, Object_stop_token, Array_start_token, Array_stop_token, 19 | Float_token, Unsigned_token, Signed_token, Null_token, True_token, False_token}; 20 | 21 | class JSON; // forward reference 22 | 23 | typedef void (*GenericFn)(JSON *data); 24 | typedef uint8_t Symbol; // used in place of names to save memory & message size 25 | 26 | #define JSON_SYMBOL_BASE 10 27 | 28 | // forward references 29 | class Thing; 30 | class Proxy; 31 | 32 | class JSON 33 | { 34 | public: 35 | static void initialise_pool(WotNodePool *wot_node_pool); 36 | #if defined(pgm_read_byte) 37 | static JSON * parse(const __FlashStringHelper *, Names *table); 38 | #endif 39 | static JSON * parse(const char *, Names *table); 40 | static void print_string(const char *name, unsigned int length); 41 | static void print_name_value(AvlKey key, AvlValue value, void *context); 42 | static void print_array_item(AvlKey key, AvlValue value, void *context); 43 | static void reachable_item(AvlKey key, AvlValue value, void *context); 44 | static void sweep_item(AvlKey key, AvlValue value, void *context); 45 | static JSON * new_unsigned(unsigned int x); 46 | static JSON * new_signed(int x); 47 | static JSON * new_float(float x); 48 | static JSON * new_null(); 49 | static JSON * new_boolean(boolean value); 50 | static JSON * new_string(const __FlashStringHelper *str); 51 | static JSON * new_string(char *str); 52 | static JSON * new_string(char *str, unsigned int length); 53 | static JSON * new_object(); 54 | static JSON * new_array(); 55 | static JSON * new_function(GenericFn func); 56 | static void set_gc_phase(boolean phase); 57 | 58 | boolean free_leaves(); 59 | void reachable(boolean phase); 60 | void sweep(boolean phase); 61 | boolean marked(boolean phase); 62 | 63 | void print(); 64 | Json_Tag get_tag(); 65 | 66 | void insert_property(Symbol symbol, JSON *value); 67 | JSON * retrieve_property(Symbol symbol); 68 | 69 | GenericFn retrieve_function(Symbol symbol); 70 | 71 | void append_array_item(JSON *value); 72 | void insert_array_item(unsigned int index, JSON *value); 73 | JSON * retrieve_array_item(unsigned int index); 74 | 75 | private: 76 | class Lexer 77 | { 78 | public: 79 | Names *table; 80 | const char *src; 81 | unsigned int length; 82 | char *token_src; 83 | unsigned int token_len; 84 | unsigned int unsigned_num; 85 | int signed_num; 86 | float float_num; 87 | Json_Token get_token(); 88 | Json_Token get_number(unsigned int c); 89 | Json_Token get_string(); 90 | Json_Token get_special(unsigned int c); 91 | boolean end_of_array(); 92 | void next_byte(); 93 | unsigned int peek_byte(); 94 | }; 95 | 96 | static unsigned int json_pool_length; 97 | static unsigned int json_pool_size; 98 | static JSON * json_pool; 99 | 100 | static JSON * new_node(); 101 | static JSON * parse_private(Lexer *lexer); 102 | static JSON * parse_object(Lexer *lexer); 103 | static JSON * parse_array(Lexer *lexer); 104 | static JSON * parse(const char *, unsigned int length, Names *table); 105 | 106 | // the first 2 bytes combine the 4 bit JSON tag, a mark/sweep 107 | // flag and 11 bits for the string length or object id 108 | 109 | void set_tag(Json_Tag tag); 110 | void set_obj_id(unsigned int id); 111 | unsigned int get_obj_id(); 112 | void set_str_length(unsigned int length); 113 | unsigned int get_str_length(); 114 | void toggle_mark(); 115 | void set_mark(); 116 | void reset_mark(); 117 | void check_if_stale(JSON * old_value, JSON * new_value); 118 | void free(); 119 | 120 | uint16_t taglen; // composite field for tag, mark, id and string length 121 | 122 | union js_union 123 | { 124 | char *str; // 16 bits on AVR 125 | float number; // 32 bits on AVR 126 | unsigned int u; 127 | int i; // 16 bits on AVR 128 | boolean truth; 129 | AvlIndex object; 130 | Thing *thing; 131 | Proxy *proxy; 132 | GenericFn function; // 16 bits on AVR 133 | } variant; 134 | }; 135 | 136 | #endif -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Web of Things Framework for Arduino 2 | 3 | This will be an experimental implementation of the Web of Things Framework written in the C++ programming language for the Arduino. This project is a sister of the [NodeJS server for the Web of Things](https://github.com/w3c/web-of-things-framework). 4 | 5 | A hundred billion IoT devices are expected to be deployed over the next ten years. There are many IoT platforms, and an increasing number of IoT technologies. However, the IoT products are currently beset by silos and a lack of interoperability, which blocks the benefits of the network effect from taking hold. At W3C, we are exploring the potential for bridging IoT platforms and devices through the World Wide Web via a new class of Web servers that are connected through a common framework, and available in a variety of scales from microcontrollers, to smart phones and home/office hubs, and cloud-based server farms. 6 | 7 | This framework involves software objects ("things") on behalf of physical and abstract entities. These things are modelled in terms of metadata, properties, actions and events, with bindings to scripting APIs and a variety of protocols, given that no one protocol will fulfil all needs. This server will start with bindings to TCP, based upon the native TCP support in the Arduino Ethernet Shield, thanks to the Wiznet W5100 network controller. 8 | 9 | ## Technical Details 10 | 11 | The Arduino boards like the Uno are extremely limited when it comes to RAM and Flash memory. There are a range of possibilities for connectivity. The initial focus is on IP connectivity via the Arduino Ethernet Shield. Future work is expected on other technologies e.g. sensor networks using the Nordic nRF24L01+, and Bluetooth using the Bluetooth Bee V2.0 module. 12 | 13 | The server implements memory management for JSON nodes along with average length (AVL) binary trees for arrays and name/value sets. These are allocated from statically declared buffers to avoid issues that can arise with malloc and free. A mark/sweep garbage collector is used to reclaim unused nodes when memory is running low. There is an encoder/decoder for binary encoding of JSON messages. This uses numeric symbols in place of property names where the symbols are deterministically generated from the thing data models. 14 | 15 | A constrained web of things server can be used with one or more sensors or actuators. This project will allow you to write drivers for simple sensors and actuators, without needing to deal with the details of the protocols. Developers will be able to script the sensors and actuators on a more powerful server where the Web of Things Framework provide the glue between the proxy object and the object it acts on behalf of. 16 | 17 | When starting up the server can use a fixed IP address or it can use DHCP for a dynamically assigned address. You can likewise use a fixed IP address and port for the gateway, or make use of multicast DNS for its discovery. The server will then register the things defined by the sketch with the gateway. 18 | 19 | Every attempt is being made to reduce the memory footprint. If you have suggestions for further improvments, please let us know! 20 | 21 | n.b. The readme.txt is generally more upto date when it comes to recent work on the server design. This is a work in progress and not yet complete! 22 | 23 | ## Prerequisites 24 | 25 | * An Arduino Uno and Wiznet W5100 based Ethernet Shield. These can be purchased very cheaply on ebay. 26 | * A wired Ethernet connection to your router, e.g. a broadband device like BT's Homehub. 27 | * The Arduino IDE - note that many Arduino clones use the CH340G chip that isn't supported directly by the Arduino IDE. There are free drivers and installation instructions for different host platforms. 28 | * A knowledge of C++ and an appreciation of how to design for microcontrollers. 29 | 30 | ## Installation 31 | 32 | The starting point is to install Git, see: 33 | 34 | http://git-scm.com/book/en/v2/Getting-Started-Installing-Git 35 | 36 | Next create a copy of this directory and change to it as follows: 37 | 38 | ``` 39 | git clone https://github.com/w3c/wot-arduino 40 | cd wot-arduino 41 | ``` 42 | 43 | ## Contributing 44 | 45 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/w3c/web-of-things-framework?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 46 | 47 | We welcome contributions. If you find a bug in the source code or a mistake in the documentation, you can help us by submitting an issue to our [GitHub repository](https://github.com/w3c/arduino-wot), and likewise if you have suggestions for new features. Even better you can submit a Pull Request with a fix. We also have a Gitter chat channel, see above link. 48 | 49 | We encourage you to join the W3C [Web of Things Community Group](https://www.w3.org/community/wot/) where contribution and discussions happen. Anyone can join and there are no fees. 50 | 51 | The amount of time you contribute and the areas in which you contribute is up to you. 52 | 53 | ### Acknowledgements 54 | 55 | This work has been funded in part (through October 2015) by the European Union's 7th Research Framework Programme (FP7/ 2013-2015) under grant agreement nº317862 - Compose. 56 | 57 | ## License 58 | 59 | (The MIT License) 60 | 61 | Copyright (c) 2015-2016 Dave Raggett <dsr@w3.org> 62 | 63 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 64 | 65 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 66 | 67 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 68 | -------------------------------------------------------------------------------- /AvlNode.cpp: -------------------------------------------------------------------------------- 1 | /* AvlTrees - a variant on balanced binary trees */ 2 | 3 | #include 4 | #include "NodePool.h" 5 | #include "AvlNode.h" 6 | 7 | #define MAX(x, y) (((x) > (y))?(x):(y)) 8 | 9 | #define AVLNODE(i) ((AvlNode *)(node_pool + i - 1)) 10 | #define AVLINDEX(node) ((AvlNode *)((wot_node_pool_t *)node - node_pool + 1)); 11 | 12 | // initialise memory pool for allocating nodes 13 | static wot_node_pool_t *node_pool; 14 | static WotNodePool *node_pool_manager; 15 | 16 | void AvlNode::initialise_pool(WotNodePool *pool) 17 | { 18 | node_pool_manager = pool; 19 | node_pool = pool->get_pool(); 20 | } 21 | 22 | AvlNode * AvlNode::get_node(AvlIndex index) 23 | { 24 | return AVLNODE(index); 25 | } 26 | 27 | AvlKey AvlNode::get_key() 28 | { 29 | return key; 30 | } 31 | 32 | AvlValue AvlNode::get_value() 33 | { 34 | return value; 35 | } 36 | 37 | void AvlNode::print_keys(AvlIndex tree) 38 | { 39 | if (tree) 40 | { 41 | print_keys(AVLNODE(tree)->left); 42 | Serial.print(F(" ")); 43 | Serial.println((int)AVLNODE(tree)->key); 44 | print_keys(AVLNODE(tree)->right); 45 | } 46 | } 47 | 48 | void AvlNode::print(AvlIndex tree) 49 | { 50 | if (tree) 51 | { 52 | print(AVLNODE(tree)->left); 53 | Serial.print(F(" ")); 54 | Serial.print((unsigned int)AVLNODE(tree)->key); 55 | Serial.print(F(" : ")); 56 | Serial.println((unsigned long)AVLNODE(tree)->value); 57 | print(AVLNODE(tree)->right); 58 | } 59 | } 60 | 61 | void AvlNode::apply(AvlIndex tree, AvlApplyFn applyFn, void *data) 62 | { 63 | if (tree) 64 | { 65 | apply(AVLNODE(tree)->left, applyFn, data); 66 | (*applyFn)(AVLNODE(tree)->key, AVLNODE(tree)->value, data); 67 | apply(AVLNODE(tree)->right, applyFn, data); 68 | } 69 | } 70 | 71 | unsigned int AvlNode::get_size(AvlIndex tree) 72 | { 73 | if (tree) 74 | return get_size(AVLNODE(tree)->left) + 1 + get_size(AVLNODE(tree)->right); 75 | 76 | return 0; 77 | } 78 | 79 | void AvlNode::free(AvlIndex tree) 80 | { 81 | if (tree) 82 | { 83 | free(AVLNODE(tree)->left); 84 | free(AVLNODE(tree)->right); 85 | node_pool_manager->free(AVLNODE(tree)); 86 | Serial.println(F("freeing AVL node")); 87 | } 88 | } 89 | 90 | // allocate node from fixed memory pool 91 | AvlIndex AvlNode::new_node(AvlKey key, AvlValue value) 92 | { 93 | AvlNode *node = (AvlNode *)(node_pool_manager->allocate_node()); 94 | AvlIndex index = 0; 95 | 96 | if (node) 97 | { 98 | Serial.println(F("allocating AVL node")); 99 | index = node - ((AvlNode *)node_pool) + 1; 100 | node->key = key; 101 | node->value = value; 102 | node->height = 1; 103 | node->left = node->right = 0; 104 | } 105 | else 106 | Serial.println(F("Out of memory for JSON Node pool")); 107 | 108 | return index; 109 | } 110 | 111 | AvlValue AvlNode::find_key(AvlIndex tree, AvlKey key) 112 | { 113 | while (tree) 114 | { 115 | if (key == AVLNODE(tree)->key) 116 | return AVLNODE(tree)->value; 117 | 118 | tree = (key < AVLNODE(tree)->key ? AVLNODE(tree)->left : AVLNODE(tree)->right); 119 | } 120 | 121 | return 0; 122 | } 123 | 124 | AvlIndex AvlNode::first(AvlIndex tree) 125 | { 126 | if (tree) 127 | { 128 | while (AVLNODE(tree)->left) 129 | tree = AVLNODE(tree)->left; 130 | } 131 | return tree; 132 | } 133 | 134 | AvlIndex AvlNode::last(AvlIndex tree) 135 | { 136 | if (tree) 137 | { 138 | while (AVLNODE(tree)->right) 139 | tree = AVLNODE(tree)->right; 140 | } 141 | 142 | return tree; 143 | } 144 | 145 | AvlKey AvlNode::last_key(AvlIndex tree) 146 | { 147 | AvlIndex i = last(tree); 148 | 149 | if (i) 150 | return AVLNODE(i)->key; 151 | 152 | return 0; 153 | } 154 | 155 | int AvlNode::tree_height(AvlIndex tree) 156 | { 157 | if (!tree) 158 | return 0; 159 | 160 | int lh = tree_height(AVLNODE(tree)->left); 161 | int rh = tree_height(AVLNODE(tree)->right); 162 | 163 | return 1 + MAX(lh, rh); 164 | } 165 | 166 | int AvlNode::node_height(AvlIndex node) 167 | { 168 | return (node ? AVLNODE(node)->height : 0); 169 | } 170 | 171 | // rebalancing operations from wikipedia diagram 172 | // see http://en.wikipedia.org/wiki/AVL_tree 173 | AvlIndex AvlNode::left_left(AvlIndex p5) 174 | { 175 | AvlIndex p3 = AVLNODE(p5)->left; 176 | AVLNODE(p5)->left = AVLNODE(p3)->right; 177 | AVLNODE(p3)->right = p5; 178 | AVLNODE(p5)->height = max(node_height(AVLNODE(p5)->left), node_height(AVLNODE(p5)->right)) + 1; 179 | AVLNODE(p3)->height = max(node_height(AVLNODE(p3)->left), AVLNODE(p5)->height) + 1; 180 | return p3; 181 | } 182 | 183 | AvlIndex AvlNode::right_right(AvlIndex p3) 184 | { 185 | AvlIndex p5 = AVLNODE(p3)->right; 186 | AVLNODE(p3)->right = AVLNODE(p5)->left; 187 | AVLNODE(p5)->left = p3; 188 | AVLNODE(p3)->height = max(node_height(AVLNODE(p3)->left), node_height(AVLNODE(p3)->right)) + 1; 189 | AVLNODE(p5)->height = max(AVLNODE(p3)->height, node_height(AVLNODE(p5)->right)) + 1; 190 | return p5; 191 | } 192 | 193 | AvlIndex AvlNode::left_right(AvlIndex p5) 194 | { 195 | AVLNODE(p5)->left = right_right(AVLNODE(p5)->left); 196 | return left_left(p5); 197 | } 198 | 199 | AvlIndex AvlNode::right_left(AvlIndex p3) 200 | { 201 | AVLNODE(p3)->right = left_left(AVLNODE(p3)->right); 202 | return right_right(p3); 203 | } 204 | 205 | int AvlNode::get_balance(AvlIndex tree) 206 | { 207 | int balance = 0; 208 | 209 | if (tree) 210 | { 211 | if (AVLNODE(tree)->left) 212 | balance -= AVLNODE(AVLNODE(tree)->left)->height; 213 | 214 | if (AVLNODE(tree)->right) 215 | balance += AVLNODE(AVLNODE(tree)->right)->height; 216 | } 217 | 218 | return balance; 219 | } 220 | 221 | AvlIndex AvlNode::balance(AvlIndex tree) 222 | { 223 | int balance, lh, rh; 224 | 225 | balance = get_balance(tree); 226 | 227 | if (balance < -1) 228 | { 229 | balance = get_balance(AVLNODE(tree)->left); 230 | 231 | if (balance > 0) 232 | tree = left_right(tree); 233 | else 234 | tree = left_left(tree); 235 | } 236 | else if (balance > 1) 237 | { 238 | balance = get_balance(AVLNODE(tree)->right); 239 | 240 | if (balance < 0) 241 | tree = right_left(tree); 242 | else 243 | tree = right_right(tree); 244 | } 245 | 246 | lh = node_height(AVLNODE(tree)->left); 247 | rh = node_height(AVLNODE(tree)->right); 248 | AVLNODE(tree)->height = (lh > rh ? lh : rh) + 1; 249 | return tree; 250 | } 251 | 252 | AvlIndex AvlNode::insert_key(AvlIndex tree, AvlKey key, AvlValue value) 253 | { 254 | if (!tree) 255 | { 256 | tree = new_node(key, value); 257 | return tree; 258 | } 259 | 260 | if (key < AVLNODE(tree)->key) 261 | { 262 | AVLNODE(tree)->left = insert_key(AVLNODE(tree)->left, key, value); 263 | tree = balance(tree); 264 | } 265 | else if (key > AVLNODE(tree)->key) 266 | { 267 | AVLNODE(tree)->right = insert_key(AVLNODE(tree)->right, key, value); 268 | tree = balance(tree); 269 | } 270 | else // tree already has key 271 | AVLNODE(tree)->value = value; 272 | 273 | return tree; 274 | } 275 | -------------------------------------------------------------------------------- /WebThings.cpp: -------------------------------------------------------------------------------- 1 | /* Web of Things Framework for Arduino */ 2 | 3 | #include 4 | #include 5 | #include "NodePool.h" 6 | #include "AvlNode.h" 7 | #include "Names.h" 8 | #include "JSON.h" 9 | #include "WebThings.h" 10 | 11 | #ifndef null 12 | #define null 0 13 | #endif 14 | 15 | // static allocation of memory to avoid new/free fragmentation 16 | 17 | static WotNodePool wot_node_pool; // for allocation of JSON and AVL tree nodes 18 | static ThingPool thing_pool; // for allocation of thing and proxy objects 19 | static NPIndex stale[MAX_STALE]; // for tracking overwritten JSON references 20 | static unsigned int stale_count; // number of stale references 21 | static boolean gc_phase; // alternates between odd and even on successive cycles 22 | static Thing *things; // linked list of things hosted on this server 23 | static Proxy *proxies; // linked list of proxies for things on other servers 24 | 25 | 26 | WebThings::WebThings() 27 | { 28 | Serial.print(F("stale count ")); Serial.println(stale_count); 29 | AvlNode::initialise_pool(&wot_node_pool); 30 | JSON::initialise_pool(&wot_node_pool); 31 | 32 | Serial.print(F("avl node size: ")); 33 | Serial.print((sizeof(AvlNode))); 34 | Serial.println(F(" bytes")); 35 | 36 | Serial.print(F("json node size: ")); 37 | Serial.print((sizeof(JSON))); 38 | Serial.println(F(" bytes")); 39 | 40 | Serial.print(F("pool node size: ")); 41 | Serial.print((sizeof(wot_node_pool_t))); 42 | Serial.println(F(" bytes")); 43 | 44 | Serial.print(F("WoT pool size: ")); 45 | Serial.print(wot_node_pool.size()); 46 | Serial.println(F(" bytes")); 47 | 48 | gc_phase = 0; 49 | JSON::set_gc_phase(gc_phase); 50 | } 51 | 52 | unsigned int WebThings::used() 53 | { 54 | return wot_node_pool.used; 55 | } 56 | 57 | void WebThings::add_stale(JSON *json) 58 | { 59 | // free unless its an object or array 60 | if (!json->free_leaves()) { 61 | NPIndex index = get_index(json); 62 | 63 | // check if already in the stale set 64 | for (unsigned int i = 0; i < stale_count; ++i) { 65 | if (stale[i] == index) 66 | return; 67 | } 68 | 69 | if (stale_count >= MAX_STALE) 70 | collect_garbage(); 71 | 72 | if (stale_count < MAX_STALE) 73 | stale[stale_count++] = index; 74 | else 75 | Serial.println(F("Exhausted room in stale set")); 76 | 77 | } 78 | } 79 | 80 | void WebThings::collect_garbage() 81 | { 82 | if (stale_count) { 83 | // mark all nodes reachable from the roots 84 | for (Thing *t = things; t; t = (Thing *)t->next) 85 | t->reachable(gc_phase); 86 | /* 87 | for (Proxy *proxy = proxies; proxy; proxy = (Proxy *)proxy->next) { 88 | reachable(proxy->properties); 89 | reachable(proxy->proxies); 90 | } 91 | */ 92 | 93 | // now sweep through the nodes reachable from the stale list 94 | // and delete the ones that aren't reachable from the roots 95 | for (unsigned int i = 0; i < stale_count; ++i) { 96 | JSON *json = get_json(stale[i]); 97 | 98 | if (!json->marked(gc_phase)) { 99 | json->sweep(gc_phase); 100 | for (unsigned int j = i; j < stale_count - 1; ++j) 101 | stale[j] = stale[j+1]; 102 | --stale_count; 103 | } 104 | } 105 | 106 | gc_phase = !gc_phase; 107 | JSON::set_gc_phase(gc_phase); // to ensure new nodes are marked correctly 108 | } 109 | } 110 | 111 | void WebThings::reachable(NPIndex index) 112 | { 113 | JSON *json = get_json(index); 114 | 115 | if (json) 116 | json->reachable(gc_phase); 117 | } 118 | 119 | void WebThings::remove_thing(Thing *thing) 120 | { 121 | Thing *t, *next; 122 | 123 | if (things == thing) { 124 | things = (Thing *)thing->next; 125 | } else { 126 | for (t = things; t; t = next) { 127 | next = (Thing *)t->next; 128 | 129 | if (next == thing) { 130 | t->next = thing->next; 131 | break; 132 | } 133 | } 134 | } 135 | 136 | thing->remove(); 137 | } 138 | 139 | void WebThings::remove_proxy(Proxy *proxy) 140 | { 141 | Proxy *p, *next; 142 | 143 | if (proxies == proxy) { 144 | proxies = (Proxy *)proxy->next; 145 | } else { 146 | for (p = proxies; p; p = next) { 147 | next = (Proxy *)p->next; 148 | 149 | if (next == proxy) { 150 | p->next = proxy->next; 151 | break; 152 | } 153 | } 154 | } 155 | 156 | proxy->remove(); 157 | } 158 | 159 | #if defined(pgm_read_byte) 160 | void WebThings::thing(const char *name, const __FlashStringHelper *model, SetupFunc setup) 161 | { 162 | thing(name, ((char *)model)+PROGMEM_BOUNDARY, setup); 163 | } 164 | #endif 165 | 166 | void WebThings::thing(const char *name, char *model, SetupFunc setup) 167 | { 168 | Names table; 169 | unsigned int id = 0; 170 | Thing *t = things, *thing = (Thing *)ThingPool::allocate(); 171 | 172 | if (thing) 173 | { 174 | while (t) 175 | { 176 | ++id; 177 | t = (Thing *)t->next; 178 | } 179 | 180 | thing->uri = (char *)name; 181 | thing->id = ++id; 182 | thing->model = get_index(JSON::parse(model, &table)); 183 | thing->events = get_index(JSON::new_object()); 184 | thing->properties = get_index(JSON::new_object()); 185 | thing->actions = get_index(JSON::new_object()); 186 | thing->proxies = get_index(JSON::new_array()); 187 | 188 | thing->next = (Thing *)things; 189 | things = thing; 190 | setup(thing, &table); 191 | } 192 | } 193 | 194 | // find the proxy's data model and set up new proxy object 195 | // *** to be implemented *** 196 | void WebThings::register_proxy(const char *uri, ThingFunc setup) 197 | { 198 | Proxy *proxy = find_proxy(uri); 199 | 200 | if (!proxy) 201 | { 202 | proxy = new Proxy(); 203 | } 204 | } 205 | 206 | Thing *WebThings::find_thing(const char *name) 207 | { 208 | Thing *thing = things; 209 | 210 | while (thing) 211 | { 212 | if (!strcmp(thing->uri, name)) 213 | return thing; 214 | } 215 | 216 | return thing; 217 | } 218 | 219 | Proxy *WebThings::find_proxy(const char *name) 220 | { 221 | Proxy *proxy = proxies; 222 | 223 | while (proxy) 224 | { 225 | if (!strcmp(proxy->uri, name)) 226 | return proxy; 227 | } 228 | 229 | return proxy; 230 | } 231 | 232 | void WebThings::register_thing(Thing *thing) 233 | { 234 | thing->next = things; 235 | things = thing; 236 | } 237 | 238 | boolean WebThings::strcmp(const char *s1, const char *s2) 239 | { 240 | while (*s1 && *s2 && *s1++ == *s2++); 241 | return (boolean)(*s1 == *s1); 242 | } 243 | 244 | JSON *WebThings::get_json(NPIndex index) 245 | { 246 | wot_node_pool_t *pool = wot_node_pool.get_pool(); 247 | return (JSON *)(pool + index - 1); 248 | } 249 | 250 | NPIndex WebThings::get_index(JSON *json) 251 | { 252 | if (json) 253 | { 254 | wot_node_pool_t *pool = wot_node_pool.get_pool(); 255 | return (NPIndex)((wot_node_pool_t *)json - pool + 1); 256 | } 257 | 258 | return 0; 259 | } 260 | 261 | void Thing::print() 262 | { 263 | Serial.print(F(" model: ")); 264 | WebThings::get_json(this->model)->print(); 265 | Serial.print(F("\n properties: ")); 266 | WebThings::get_json(this->properties)->print(); 267 | Serial.print(F("\n actions: ")); 268 | WebThings::get_json(this->actions)->print(); 269 | Serial.print(F("\n events: ")); 270 | WebThings::get_json(this->events)->print(); 271 | Serial.print(F("\n")); 272 | } 273 | 274 | // this ignores the need to inform the proxy chain 275 | void Thing::set_property(Symbol property, JSON *value) 276 | { 277 | JSON *properties = WebThings::get_json(this->properties); 278 | properties->insert_property(property, value); 279 | } 280 | 281 | JSON *Thing::get_property(Symbol property) 282 | { 283 | JSON *properties = WebThings::get_json(this->properties); 284 | return properties->retrieve_property(property); 285 | } 286 | 287 | // set the handler for implementing the named action 288 | void Thing::register_action_handler(Symbol action, ActionFunc handler) 289 | { 290 | JSON *func = JSON::new_function((GenericFn)handler); 291 | JSON *actions = WebThings::get_json(this->actions); 292 | actions->insert_property(action, func); 293 | } 294 | 295 | int Thing::invoke(Symbol action, ...) 296 | { 297 | JSON *actions = WebThings::get_json(this->actions); 298 | GenericFn func = actions->retrieve_function(action); 299 | 300 | // check to see if there is any data to pass 301 | // using varargs and possibly the data model 302 | if (func) 303 | { 304 | // cheat for now and assume no data and no response 305 | (*func)(null); 306 | } 307 | 308 | return -1; 309 | } 310 | 311 | void Thing::register_observer(Symbol event, EventFunc handler) 312 | { 313 | JSON *func = JSON::new_function((GenericFn)handler); 314 | JSON *events = WebThings::get_json(this->events); 315 | events->insert_property(event, func); 316 | } 317 | 318 | void Thing::raise_event(Symbol event, ...) 319 | { 320 | } 321 | 322 | void Thing::reachable(boolean phase) 323 | { 324 | WebThings::get_json(properties)->reachable(phase); 325 | WebThings::get_json(proxies)->reachable(phase); 326 | } 327 | 328 | // called when sweep comes across a JSON node that is a Thing 329 | void Thing::sweep(boolean phase) 330 | { 331 | WebThings::get_json(properties)->sweep(phase); 332 | } 333 | 334 | void Thing::remove() 335 | { 336 | AvlNode::free(properties); 337 | AvlNode::free(actions); 338 | AvlNode::free(events); 339 | ThingPool::free(this); 340 | } 341 | 342 | // this needs to forward the new value along the 343 | // proxy chain to the thing which in turn will notify 344 | // all proxies when the value has actually been applied 345 | void Proxy::set_property(Symbol property, JSON *value) 346 | { 347 | JSON *properties = WebThings::get_json(this->properties); 348 | JSON *old_value = properties->retrieve_property(property); 349 | 350 | // reference to old value will become stale 351 | if (value != old_value) 352 | WebThings::add_stale(old_value); 353 | 354 | properties->insert_property(property, value); 355 | } 356 | 357 | // this gets the locally cached value which is not 358 | // guaranteed to be be up to date as that isn't 359 | // possible in a distributed system with inevitable 360 | // delays for messages that depend on the protocols, 361 | // communication patterns, and the time for a device 362 | // to wake up from a power preserving mode 363 | JSON *Proxy::get_property(Symbol property) 364 | { 365 | JSON *properties = WebThings::get_json(this->properties); 366 | return properties->retrieve_property(property); 367 | } 368 | 369 | // set the response handler for the named action 370 | void Proxy::register_response_handler(Symbol action, ResponseFunc handler) 371 | { 372 | JSON *func = JSON::new_function((GenericFn)handler); 373 | JSON *actions = WebThings::get_json(this->actions); 374 | actions->insert_property(action, func); 375 | } 376 | 377 | // forward action & data along proxy chain to thing 378 | // need to provide id so that responses can be matched 379 | // note that there may be zero, one or more responses 380 | int Proxy::invoke(Symbol action, ...) 381 | { 382 | return -1; 383 | } 384 | 385 | void Proxy::register_observer(Symbol event, EventFunc handler) 386 | { 387 | JSON *node = JSON::new_function((GenericFn)handler); 388 | JSON *events = WebThings::get_json(this->events); 389 | events->insert_property(event, node); 390 | } 391 | 392 | void Proxy::raise_event(Symbol event, ...) 393 | { 394 | } 395 | 396 | void Proxy::reachable(boolean phase) 397 | { 398 | WebThings::get_json(properties)->reachable(phase); 399 | WebThings::get_json(proxies)->reachable(phase); 400 | } 401 | 402 | void Proxy::sweep(boolean phase) 403 | { 404 | WebThings::get_json(properties)->sweep(phase); 405 | } 406 | 407 | void Proxy::remove() 408 | { 409 | AvlNode::free(properties); 410 | AvlNode::free(actions); 411 | AvlNode::free(events); 412 | ThingPool::free(this); 413 | } 414 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | Web of Things server for the Arduino Uno + Ethernet shield (W5100) 2 | ================================================================== 3 | 4 | News: the Arduino MKR1000 looks like what I'm looking for as the step up from the Arduino Mega! 5 | The Mega has 8KB RAM, 256KB Flash, 4KB EEPROM. The MKR1000 has 32KB RAM, 256KB Flash, no EEPROM, but includes WiFi and a 32 bit Cortex-M0+ MCU and would be interesting to compare with ESP8266. The latter still wins on price! 6 | 7 | This is a experimental web of things server for the Arduino Uno plus the Ethernet shield based upon the Wiznet W5100 network controller. The server supports a lightweight protocol using a binary encoding of JSON messages over TCP/IP. This takes advantage of the native TCP support found in Wiznet chips. Future work will look at other protocols that are better suited to devices that need to run off ambient power or operate for a very long time on batteries. 8 | 9 | None of the Arduino boards are ideal since they all include inefficient linear voltage regulators and LEDs. The The MKR1000 is an exception but looks like it won't be cheap! The ESP8266 ESP-01 board doesn't include a voltage regulator, but you need to de-solder the LED. The Arduino mini is another option provided you de-solder the LED and power regulator. The mini lacks the serial chip so an external board is needed to connect it to the computer. A switching voltage regulator will then allow the board to run efficiently off batteries. 10 | 11 | My initial goal is to use a fixed IP address and demonstrate interoperability with a server running on my laptop. This involves developing both servers at the same time. I may be able to adapt my NodeJS based server, or I may develop a C++ server that re-uses much of the code from the Arduino project. This is quite attractive as it means I only need to think using one programming language rather than two. I have already demonstrated how to emulate the Arduino framework on OS X using POSIX. 12 | 13 | The desktop server would connect to the Arduino and request the model for a thing, and use this model to construct a proxy for that thing. The Arduino would store the model in Flash and copy it to the socket connection. I envisage using the same abstract message formats as for my NodeJS project. The only difference is the use of a binary encoding to reduce message size. The Arduino has very little RAM, so we need to encode the messages directly to the socket rather than buffering then in RAM. 14 | 15 | A symbol table is computed from the data models to encode property names etc. as numeric identifiers. This requires both servers to use the same algorithm to ensure that the mappings are the same. 16 | 17 | JSON is represented in memory as nodes assigned from a statically allocated array. This array is also used to allocate nodes for balanced binary trees representing JSON associative arrays and arrays with numeric indices. A hash table is used for the symbol dictionary, but I plan to discard the table after parsing the models in order to free up memory for application scripts. 18 | 19 | So far I have the parser and message encoder/decoder done. I need to integrate the network library, and to finish the logic that wires the properties, actions and events to the API exposed to applications scripts. 20 | 21 | I then want to look at an API for registering a remote proxy, and also for adding support for discovery, so that the device can discover a hub using a very lightweight multicast socket or the broadcast address (UDP message sent to 255.255.255.255). 22 | 23 | I have extended the MessageCoder to work with the F macro for string literals stored in Flash as well as regular string literals. For thing data models defined by this device, it also makes sense to hold them in Flash and avoid the compiler copying them to RAM when initialising local variables. The JSON parser will now work with and without the F macro. I was able to combine the RAM and Program address spaces by adding an offset of 0x8000 to program memory addresses, since the Uno only has 32KB flash. I've introduce a Strings class to support string operations on RAM and flash based strings. 24 | 25 | Here is an example sketch: 26 | 27 | Thing *agent, *door, *light; Symbol unlock_sym, on_sym; 28 | 29 | void setup() { 30 | RegisterThing(agent_model, init_agent); 31 | } 32 | 33 | void init_agent(Thing *thing, Names *names) { 34 | agent = thing; 35 | door = agent->get_property(names, F("door")); 36 | light = agent->get_property(names, F("light")); 37 | unlock_sym = names->symbol(F("unlock")); 38 | on_sym = names->symbol(F("on")); 39 | agent->observe(names, F("key"), unlock); 40 | } 41 | 42 | void unlock(JSON *valid) { 43 | if (JSON_BOOLEAN(valid)) { 44 | door->invoke(unlock_sym); 45 | light->set_property(on_sym, JSON_TRUE); 46 | } 47 | } 48 | 49 | void loop() { 50 | DispatchEvents(); 51 | } 52 | 53 | This maps names to symbols, which are used to index the balanced binary trees that internally represent the name/value pairs for the thing's properties. The name to symbol map is discarded when the setup method returns. You will need to look up and save symbols for later use. Messages identify the object and this requires a map from the id to the memory node. The messages use symbols in place of names. 54 | 55 | If we want to enable setting/unsetting observers after the setup, we will need the symbol for "events", likewise for "properties" and "actions". For now this isn't supported. 56 | 57 | How do deal with objects that are passed as values? The message format would use symbols, but the app developer wouldn't know what symbols to use to access or update their properties. This could be avoided when the developer knows about the object in advance, i.e. declares it as a thing. Otherwise, we need some form of reflection. In other words, the ability to ask the object for its description. 58 | 59 | If you declare a thing, you either have its description or you have a link to it that you can dereference. We thus need a means to find the link for objects passed as values. This could be part of a registration process that binds an object id to its description. 60 | 61 | Memory Allocation 62 | ================= 63 | 64 | I am avoiding the use of new and free on the advice that these cause problems for microcontrollers. Instead, I use static allocation with arrays. This implies the need to monitor the usage levels of the various arrays. 65 | 66 | NodePool: An array for allocating JSON nodes and AVL balanced binary tree nodes. On the ATmega328P, these both take 6 bytes. WOT_NODE_POOL_SIZE sets the array size and is defined in NodePool.h. The pool is allocated statically in WebThings.cpp. Balanced binary trees are used for associative and numerically indexed arrays. JSON nodes include a union for their different types. 67 | 68 | Names: this defines a hash table that maps string to numeric symbols. The hash table is dynamically assigned as a local variable in WebThings:thing() and WebThings::proxy(); The table holds strings with a pointer to the string plus its length. The ATmega328P uses the Harvard memory architecture which separates data and code into distinct address spaces. I allow for static strings to be held in the code address space to save RAM. The Strings class provides an abstraction layer that hides where strings are stored. 69 | 70 | CoreThings: Things and Proxies are derived from the CoreThings class, and both take 10 bytes on the ATmega328P. This allows them to allocated from the things_pool buffer in WebThings.cpp. 71 | 72 | Stale: this is the set of references that were lost when updating the value of a property for a thing or proxy. This is used by the garbage collector when sweeping for nodes that aren't reachable from the roots, and is needed because we can't distinguish JSON and AvlNodes except by how they are referenced. That prevents a sweep algorithm from simply iterating through the node pool. 73 | 74 | Garbage Collection 75 | ================== 76 | 77 | When overriding a item in a JSON object or array, the reference to the former value becomes stale and a candidate for garbage collection if there are no other references to it. The garbage collector is invoked when the stale set becomes full, or the node allocator runs out of free nodes. The process involves two stages. The first is to mark all JSON nodes reachable from the roots. The second is to sweep the nodes reachable from the set of stale references to find nodes that aren't marked and hence can be freed. The mark uses a bit in the first byte of the JSON node, and the sense alternates between successive calls to the garbage collector. New nodes are given the mark state that reachable nodes were left in by the last garbage collection. 78 | 79 | JSON nodes may reference things and proxies. This results in a mark or sweep of the properties for the referenced thing or proxy. The AVL tree for a JSON object or array is cleaned when that object is freed. The node pool could be improved by maintaining free nodes in a linked list. I also need a way to recover from memory exhaustion, e.g. note problem in EEPROM then restart the server. Likewise for when the stale set is exhausted. 80 | 81 | Thing Properties 82 | ================ 83 | 84 | These are part of the thing data model and implemented with a JSON associative array to map the property name to its JSON value. Values can be null, true, false, floats, signed and unsigned integers, strings, associative or indexed arrays, and in future, streams, things and enumerations. Future work is anticipated on integrity constraints acting on individual properties, across properties belonging to the same thing, and across things. This is important for robust operation in the presence of faults. 85 | 86 | Properties are identified with numeric symbols assigned per thing based upon the thing's data model. This allows for compact message encodings that avoid the need to send string identifiers. Things are likewise identified by numeric ids which are scoped to the server originating the message. Servers are therefore required to maintain a mapping to the thing's model and the memory object for the thing. 87 | 88 | Thing Events 89 | ============ 90 | 91 | These are part of the thing data model and not to be confused with platform events for responding to hardware interrupts in a safe way. 92 | 93 | I am using a JSON associative array to map the thing event name to its handler. This assumes that there is only one handler per event, which should be sufficient. In principle, I could generalise to JSON array of functions if really needed. The event is delivered to the handler which is defined as: 94 | 95 | void (*EventFunc)(Symbol event, Thing *thing, ...) 96 | 97 | Events are raised by calling: 98 | 99 | void raise_event(Symbol event, ...) 100 | 101 | If varargs is too painful to describe in the data model and for the messages, I could allow an optional single argument. 102 | 103 | Thing Actions 104 | ============= 105 | 106 | JSON associative array are used to to map the action name to its handler. There may be zero, one or more responses for action invokation, and in principle a response may itself be a Thing. The method for invoking an action on a proxy is defined as 107 | 108 | int invoke(Symbol action, ResponseFunc response, ...) 109 | 110 | Pass null should for response in no response is needed. The following args are optional and used to pass data along with the action. The invoke method returns a positive id that is later delivered to the response handler whose function is defined as: 111 | 112 | void (*ResponseFunc)(unsigned int id, Thing *thing, ...) 113 | 114 | If varargs is too painful to describe in the data model and for the messages, I could allow an optional single argument. 115 | 116 | For Things, I need a way to bind the action name to the application supplied handler. The method signature could be something like: 117 | 118 | void (*ActionFunc)(ProxyId proxy_id, ActionId action_id, ...) 119 | 120 | Where the proxy_id identifies the proxy that initiated the action, and the action_id is used by that proxy to associate responses with invocations. 121 | 122 | Discovery 123 | ========= 124 | 125 | Battery or ambient powered devices will need to sleep a lot to conserve power. It seems reasonable for these devices to discover a powered gateway that is always listening. My first idea was to create a very simple discovery protocol using multicast packets. However, it seems that the MacBook Pro blocks all multicast sockets except for mDNS, so I am therefore looking to exploit mDNS for gateway discovery. This will also allow me to use tools like the Bonjour Browser to check that the gateway is discoverable. 126 | 127 | dns-sd -R "Web of Things Gateway" _wot._tcp . 12345 128 | 129 | This times out and needs to be Ctrl-C and redone. I don't know how to increase the lease time. This could be a problem for demos! The work around is to reregister the service at the same time as starting the Arduino - not so easy in practice. Perhaps there is a way to force the MacBook to respond to a request? 130 | 131 | Another idea would be to use socket 1 for a background mDNS service and to track messages announcing a new gateway and when a gateway has been unregistered. 132 | 133 | see http://grouper.ieee.org/groups/1722/contributions/2009/Bonjour%20Device%20Discovery.pdf 134 | 135 | Wireshark shows my mDNS queries, but OS X only sends the responses within a short interval after registering the service. Unregistering the service causes OS X to send a resource record with a TTL of zero. This compares with TTL of 120 for the SRV record and TTL of 4500 for the TXT record. I need to learn more about how this is supposed to work! See RFC6762. 136 | 137 | RFC1035 states that Resource Records have the following format: 138 | 139 | NAME variable length 140 | TYPE 2 bytes 141 | CLASS 2 bytes 142 | TTL 4 bytes 143 | RDLENGTH 2 bytes 144 | RDATA which is RDLENGTH bytes long 145 | 146 | The NAME format is a sequence of labels preceded by a byte with the length in bytes for the label and terminated by a null length. If the length > 0xC0 (192) you need to jump to the address given by msgstart + (p[i] - 0xC0)<<8 + p[i+1]. This requires random access into the read message buffer on the W5100, i.e. to peek at the message data and only update the read pointer when I've finished parsing. 147 | 148 | DNS provides a "SRV" record that binds a server to a port and name, but not IP. "A" records bind a name to an IPv4 address, e.g. 149 | 150 | "SRV" record for "Web of Things Gateway._wot._tcp.local" 151 | port 12345 152 | name "Daves-MacBook-Pro.local" 153 | 154 | "A" record for "Daves-MacBook-Pro.local" 155 | IP 192.168.1.133 156 | 157 | mDNS uses PTR (pointer) records to map service types to instances of that service, e.g. 158 | 159 | _printer._tcp._local. 28800 PTR PrintsAlot._printer._tcp._local. 160 | 161 | I currently ignore these records and instead allow for _wot._tcp.local to match to itself and to foo._wot._tcp._local for any foo. DNS "TXT" records can be used to convey name value pairs with a length byte followed by a string with a key and value separated by "=", or flags as strings without an "=". In principle, this could be used to convey the message encoding for the service and other properties, but a better idea is to express this in the service protocol. I think that a single text record can contain multiple text strings, but need to check this. 162 | 163 | I therefore need to note the server name and match that to the "A" records! In principle, a single message could convey info for multiple servers, or multiple addresses for the same server name. For now I will assume a single server. The complication comes in matching the name. This is made harder since you can't get the record type until you've parsed the name. The algorithm for matching the name whilst parsing it requires the target to be provided in advance. 164 | 165 | A possible hack would be to re-parse the record name after determining its type, as we could the provide the desired target for the server name. The challenge is that the server name is held in the W5100 buffer and may itself involve links. It might be easier to use a separate algorithm that takes the offsets for both names. This doesn't have to be fast, so we could retrieve one byte a time. 166 | 167 | Another idea reduces the match to comparing two 16 bit numbers corresponding to the offsets within the DNS message. This assumes that the SRV records precede the "A" record, and that the DNS server maximally optimises name storage. We could re-read the link value for the NAME field in the "A" record, or we could complicate parse_name to pass it back. The former seems cleaner. This is now working! 168 | 169 | *** to do next 170 | 171 | Allow incremental preparation of send buffer for sending messages. This is needed to avoid having to hold entire message in RAM. 172 | 173 | Use Flash for mDNS query and switch to Flash safe method for matching LABELs, i.e use Strings::strcmp(s1, len1, s2, len2) 174 | 175 | *** 176 | 177 | TCP based message exchange 178 | ========================== 179 | 180 | My NodeJS web of things server supports HTTP for accessing thing data models, and Web Sockets for asynchronous message exchange. HTTP and Web Sockets are expensive to support on low end devices, so it is interesting to look at alternatives. The Ethernet driver chips such as Wiznet W5100 and the ENC28J60 provide native support for TCP. This makes it attractive to use TCP as this avoids the need for a reliable messaging layer over UDP. Why re-invent a very good wheel! This works well for the star topology where battery operated IoT devices connect to a permanently powered gateway. Other approaches are better suited to sensor networks and other forms of peer to peer topologies for thing servers. 181 | 182 | I should be able to use a similar set of messages to those I defined for Web Sockets. The main differences are: the use of binary encodings and numeric ids in place of strings; and the need for IoT devices to register themselves with the gateway, passing the thing descriptions explicitly or by reference or some combination of the two. 183 | 184 | DNS client for external gateway 185 | =============================== 186 | 187 | At some point I may want to add a DNS name resolver for external gateways, or as a means to download external thing descriptions. This is likely to require larger Flash storage than is available on the ATmega328P. Here is a link to aprogram that I should be able to adapt for the W5100: 188 | 189 | http://www.binarytides.com/dns-query-code-in-c-with-linux-sockets/ 190 | 191 | Sharing memory at start up 192 | ========================== 193 | 194 | The memory needed for DHCP and for mDNS is allocated on the stack, and competes for limited RAM with the statically allocated buffers for JSON, etc. In principle, I could borrow the statically allocated buffers and only initialise these buffers once the network has been set up. 195 | 196 | Initial demo & testing 197 | ====================== 198 | 199 | How to provide a clean interface between the transport later and WebThings? The serve method gets requests, parses them and then acts on them. It thus needs access to the WebThings wot object. I may also poll sensors for input, possibly via an interrupt server routine tha queues an event. How does the event handler know about web things and the transport layer? The simplest idea is recognise wot, and transport as external global objects. 200 | 201 | Use a fixed IP address and have the laptop connect to the Arduino to set up a proxy for a thing on the Arduino. This involves a message to register a proxy and get the thing id and its description. I should have another message to unregister a proxy 202 | 203 | 204 | 205 | 206 | Plans 207 | ===== 208 | 209 | 1. clean up discovery code and interface 210 | 211 | 2. setup OS X clone of project as a gateway server 212 | 213 | 3. setup Arduino project as a gateway client that discovers the gateway and registers its things with the gateway 214 | 215 | 4. implement get_property, set_property, observe and invoke along with a means to handle responses. 216 | 217 | 5. support raising and handling of events 218 | 219 | 6. implement the messaging and lifecycle support 220 | 221 | 7. allow for downloading thing descriptions from another server 222 | 223 | 8. allow for late bound values 224 | 225 | 9. support streams as first class types 226 | 227 | 10. support EEPROM for saving config 228 | 229 | 11. watch dog timer and restart on errors 230 | 231 | 12. port to Arduino Mega with 8KB RAM and larger EEPROM and Flash 232 | 233 | 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /MessageCoder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Experimental WoT Message encoder/decoder 3 | 4 | A server hosting a proxy for a thing, and the server hosting the 5 | thing it proxies, have shared access to the thing's data model. 6 | This can be exploited for concise message encodings by sending a 7 | symbol in place of a string. For common situations, a symbol can be 8 | encoded with a singe byte. 9 | 10 | The message format starts with an identifier for the thing. This is 11 | followed by an item. Each item is at least one byte in length and 12 | is one of: symbol, number, string, array or object. Arrays are formed 13 | from zero or more values followed by a byte code marking the end 14 | of the array. Objects are formed by zero or more pairs of name/value 15 | pairs followed by a byte code marking the end of the object. Names 16 | are symbols or strings. Values are items. Strings are null terminated 17 | UTF-8 byte sequences. Numbers are signed or unsigned integers and 18 | single precision floats. Integers take one or more bytes to encode. 19 | 20 | Symbols include a core set that is used in messages, specials such 21 | as true, false, and null, and thing specific symbols. A single byte 22 | is used for the first 200 symbols, which should be sufficient in 23 | most circumstances for microcontroller based servers. 24 | 25 | Open questions: 26 | 27 | For microcontrollers with low memory and small packet sizes, 28 | we just need to support a bare minimum of core data types. 29 | 30 | If we want to expand the scope to very high through-put servers, 31 | then a more extensive set of core data types could be valuable. 32 | 33 | Do we need 64 bit precision floats for IoT applications? 34 | Do we need 64 bit timestamps (mS since epoch)? 35 | Do we need to distinguish URIs from strings? 36 | Do we need an arbitrary length bit vector? 37 | If we need binary blobs, how to encode their content type? 38 | 39 | The answer is no for all of these, at least for now. There are 40 | plenty of reserved tags we could use for extensions. It would be 41 | worth defining a means to declare the version of the message 42 | format, but this doesn't need to be sent with every message and 43 | could be disclosed as part of the server's metadata. 44 | 45 | The Arduino treats both float and double as 32 bit IEEE floating 46 | point numbers, and also lacks support for 64 bit integers. There 47 | is a library for getting the time in seconds since epoch. Finer 48 | grained time will be needed for machine control applications. 49 | */ 50 | 51 | #include 52 | #include 53 | #include "MessageCoder.h" 54 | 55 | void MessageBuffer::set_buffer(unsigned char *buf, unsigned len) 56 | { 57 | union unum { 58 | unsigned char bytes[2]; 59 | uint16_t u; 60 | } num; 61 | 62 | buffer = buf; 63 | length = len; 64 | size = index = 0; 65 | overflow = false; 66 | 67 | num.u = 1; 68 | big_endian = (num.bytes[0] == 1 ? true : false); 69 | } 70 | 71 | void MessageBuffer::restart() 72 | { 73 | index = 0; 74 | } 75 | 76 | void MessageBuffer::reset() 77 | { 78 | size = index = 0; 79 | overflow = false; 80 | } 81 | 82 | unsigned int MessageBuffer::remaining() 83 | { 84 | return index == size; 85 | } 86 | 87 | boolean MessageBuffer::is_big_endian() 88 | { 89 | return big_endian; 90 | } 91 | 92 | boolean MessageBuffer::overflowed() 93 | { 94 | return overflow; 95 | } 96 | 97 | unsigned char * MessageBuffer::get_pointer() 98 | { 99 | return buffer + index; 100 | } 101 | 102 | unsigned int MessageBuffer::get_size() 103 | { 104 | return size; 105 | } 106 | 107 | unsigned int MessageBuffer::get_byte() 108 | { 109 | if (index < length) 110 | return buffer[index++]; 111 | 112 | return WOT_BUFFER_EMPTY; // to signal error 113 | } 114 | 115 | unsigned int MessageBuffer::view_byte() 116 | { 117 | if (index < length) 118 | return buffer[index]; 119 | 120 | return WOT_BUFFER_EMPTY; // to signal error 121 | } 122 | 123 | boolean MessageBuffer::put_byte(unsigned char c) 124 | { 125 | if (size < length) 126 | { 127 | buffer[size++] = c & 255; 128 | return true; 129 | } 130 | 131 | overflow = true; 132 | return false; 133 | } 134 | 135 | boolean MessageCoder::decode_object(MessageBuffer *buffer) 136 | { 137 | unsigned int c; 138 | unsigned char *s; 139 | 140 | Serial.println(F("start object")); 141 | 142 | for (;;) 143 | { 144 | // get name 145 | c = buffer->get_byte(); 146 | 147 | if (c == WOT_STRING) 148 | { 149 | s = buffer->get_pointer(); 150 | while ((c = buffer->get_byte()) && c < 256); 151 | 152 | if (c) 153 | { 154 | Serial.println(F("unterminated string")); 155 | return false; 156 | } 157 | else 158 | { 159 | Serial.print(F("string \"")); 160 | Serial.print((const char *)s); 161 | Serial.println(F("\" :")); 162 | } 163 | 164 | // get value 165 | 166 | if (!decode_value(buffer)) 167 | return false; 168 | } 169 | else if (WOT_SYM_BASE <= c && c < 256) 170 | { 171 | c = c - WOT_SYM_BASE; 172 | Serial.print(F("symbol ")); 173 | Serial.print(c); 174 | Serial.print(F(" :\n")); 175 | // get value 176 | 177 | if (!decode_value(buffer)) 178 | return false; 179 | } 180 | else if (c == WOT_END_OBJECT) 181 | { 182 | break; 183 | } 184 | else 185 | { 186 | Serial.print(F("didn't find string or symbol for object property name")); 187 | return false; 188 | } 189 | } 190 | Serial.println(F("end object")); 191 | return true; 192 | } 193 | 194 | boolean MessageCoder::decode_array(MessageBuffer *buffer) 195 | { 196 | unsigned int c; 197 | 198 | Serial.println(F("start array")); 199 | 200 | for (;;) 201 | { 202 | c = buffer->view_byte(); 203 | 204 | if (c == WOT_END_ARRAY) 205 | break; 206 | 207 | if (c == WOT_END_OBJECT) 208 | { 209 | Serial.println(F("found unexpected end of object")); 210 | return false; 211 | } 212 | 213 | if (!decode_value(buffer)) 214 | return false; 215 | } 216 | 217 | Serial.println(F("end array")); 218 | return true; 219 | } 220 | 221 | boolean MessageCoder::decode_value(MessageBuffer *buffer) 222 | { 223 | unsigned int c; 224 | c = buffer->get_byte(); 225 | 226 | switch (c) 227 | { 228 | case WOT_START_OBJECT: 229 | return decode_object(buffer); 230 | 231 | case WOT_START_ARRAY: 232 | return decode_array(buffer); 233 | 234 | case WOT_STRING: 235 | { 236 | unsigned char *s = buffer->get_pointer(); 237 | while ((c = buffer->get_byte()) && c < 256); 238 | 239 | if (c) 240 | { 241 | Serial.println(F("unterminated string")); 242 | return false; 243 | } 244 | else 245 | { 246 | Serial.print(F("string \"")); 247 | Serial.print((const char *)s); 248 | Serial.println(F("\"")); 249 | } 250 | break; 251 | } 252 | 253 | case WOT_UNSIGNED_INT_8: 254 | c = buffer->get_byte(); 255 | Serial.print(F("unsigned 8 bit integer ")); 256 | Serial.println(c); 257 | break; 258 | 259 | case WOT_SIGNED_INT_8: 260 | { 261 | c = buffer->get_byte(); 262 | uint16_t i = (uint16_t)c; 263 | Serial.print(F("signed 8 bit integer ")); 264 | Serial.println(i); 265 | break; 266 | } 267 | 268 | case WOT_UNSIGNED_INT_16: 269 | case WOT_SIGNED_INT_16: 270 | { 271 | union unum 272 | { 273 | unsigned char bytes2[2]; 274 | uint16_t u; 275 | int16_t i; 276 | } num; 277 | 278 | if (buffer->is_big_endian()) 279 | { 280 | num.bytes2[1] = buffer->get_byte(); 281 | num.bytes2[0] = buffer->get_byte(); 282 | } 283 | else 284 | { 285 | num.bytes2[0] = buffer->get_byte(); 286 | num.bytes2[1] = buffer->get_byte(); 287 | } 288 | 289 | if (c == WOT_UNSIGNED_INT_16) 290 | { 291 | Serial.print(F("unsigned 16 bit integer ")); 292 | Serial.println(num.u); 293 | } 294 | else 295 | { 296 | Serial.print(F("signed 16 bit integer ")); 297 | Serial.println(num.i); 298 | } 299 | break; 300 | } 301 | 302 | case WOT_UNSIGNED_INT_32: 303 | case WOT_SIGNED_INT_32: 304 | case WOT_FLOAT_32: 305 | { 306 | union unum 307 | { 308 | unsigned char bytes4[4]; 309 | uint32_t u; 310 | int32_t i; 311 | float x; 312 | } num; 313 | 314 | if (buffer->is_big_endian()) 315 | { 316 | num.bytes4[3] = buffer->get_byte(); 317 | num.bytes4[2] = buffer->get_byte(); 318 | num.bytes4[1] = buffer->get_byte(); 319 | num.bytes4[0] = buffer->get_byte(); 320 | } 321 | else 322 | { 323 | num.bytes4[0] = buffer->get_byte(); 324 | num.bytes4[1] = buffer->get_byte(); 325 | num.bytes4[2] = buffer->get_byte(); 326 | num.bytes4[3] = buffer->get_byte(); 327 | } 328 | 329 | if (c == WOT_UNSIGNED_INT_32) 330 | { 331 | Serial.print(F("unsigned 32 bit integer ")); 332 | Serial.println(num.u); 333 | } 334 | else if (c == WOT_SIGNED_INT_32) 335 | { 336 | Serial.print(F("signed 32 bit integer ")); 337 | Serial.println(num.i); 338 | } 339 | else 340 | { 341 | Serial.print(F("float ")); 342 | Serial.println(num.x); 343 | } 344 | break; 345 | } 346 | 347 | case WOT_VALUE_NULL: 348 | Serial.println(F("null")); 349 | break; 350 | 351 | case WOT_VALUE_TRUE: 352 | Serial.println(F("true")); 353 | break; 354 | 355 | case WOT_VALUE_FALSE: 356 | Serial.println(F("false")); 357 | break; 358 | 359 | case WOT_END_OBJECT: 360 | Serial.println(F("unexpected object end marker")); 361 | return false; 362 | 363 | case WOT_END_ARRAY: 364 | Serial.println(F("unexpected array end marker")); 365 | return false; 366 | 367 | default: 368 | { 369 | if (WOT_RESERVED_START <= c && c <= WOT_RESRVED_END) 370 | { 371 | Serial.println(F("illegal use of reserved tag")); 372 | return false; 373 | } 374 | else if (WOT_NUM_BASE <= c && c < WOT_SYM_BASE) 375 | { 376 | uint16_t u = c - WOT_NUM_BASE; 377 | Serial.print(F("unsigned 4 bit integer ")); 378 | Serial.println(u); 379 | } 380 | else if (WOT_SYM_BASE <= c && c < 256) 381 | { 382 | c -= WOT_SYM_BASE; 383 | Serial.print(F("symbol ")); 384 | Serial.println(c); 385 | } 386 | else // unexpected end of buffer 387 | { 388 | Serial.println(F("unexpectedly reached end of buffer")); 389 | return false; 390 | } 391 | } 392 | } 393 | 394 | return true; 395 | } 396 | 397 | boolean MessageCoder::decode(MessageBuffer *buffer) 398 | { 399 | buffer->restart(); 400 | 401 | if (decode_value(buffer)) 402 | { 403 | if (!buffer->remaining()) 404 | return true; 405 | 406 | Serial.println(F("message isn't empty")); 407 | } 408 | 409 | return false; 410 | } 411 | 412 | void MessageCoder::encode_unsigned8(MessageBuffer *buffer, unsigned char n) 413 | { 414 | if (n < (WOT_SYM_BASE - WOT_NUM_BASE)) 415 | buffer->put_byte(n + WOT_NUM_BASE); 416 | else 417 | { 418 | buffer->put_byte(WOT_UNSIGNED_INT_8); 419 | buffer->put_byte(n); 420 | } 421 | } 422 | 423 | void MessageCoder::encode_unsigned16(MessageBuffer *buffer, uint16_t n) 424 | { 425 | if (n < (WOT_SYM_BASE - WOT_NUM_BASE)) 426 | buffer->put_byte(n + WOT_NUM_BASE); 427 | else if (n < 256) 428 | { 429 | buffer->put_byte(WOT_UNSIGNED_INT_8); 430 | buffer->put_byte(n); 431 | } 432 | else 433 | { 434 | uint16_t u = (uint16_t) n; 435 | 436 | buffer->put_byte(WOT_UNSIGNED_INT_16); 437 | buffer->put_byte(u >> 8); 438 | buffer->put_byte(u & 255); 439 | } 440 | } 441 | 442 | void MessageCoder::encode_unsigned32(MessageBuffer *buffer, uint32_t n) 443 | { 444 | if (n < (WOT_SYM_BASE - WOT_NUM_BASE)) 445 | buffer->put_byte(n + WOT_NUM_BASE); 446 | else if (n < 256) 447 | { 448 | buffer->put_byte(WOT_UNSIGNED_INT_8); 449 | buffer->put_byte(n); 450 | } 451 | else if (n < 65536) 452 | { 453 | uint16_t u = (uint16_t) n; 454 | 455 | buffer->put_byte(WOT_UNSIGNED_INT_16); 456 | buffer->put_byte(u >> 8); 457 | buffer->put_byte(u & 255); 458 | } 459 | else // assume 32 bit unsigned int 460 | { 461 | uint32_t u = (uint32_t) n; 462 | 463 | buffer->put_byte(WOT_UNSIGNED_INT_32); 464 | buffer->put_byte((u >> 24) & 255); 465 | buffer->put_byte((u >> 1) & 255); 466 | buffer->put_byte((u >> 8) & 255); 467 | buffer->put_byte(u & 255); 468 | } 469 | } 470 | 471 | void MessageCoder::encode_signed8(MessageBuffer *buffer, char n) 472 | { 473 | if (n < (WOT_SYM_BASE - WOT_NUM_BASE)) 474 | buffer->put_byte((unsigned char)n + WOT_NUM_BASE); 475 | else 476 | { 477 | buffer->put_byte(WOT_SIGNED_INT_8); 478 | buffer->put_byte((unsigned char)n); 479 | } 480 | } 481 | 482 | void MessageCoder::encode_signed16(MessageBuffer *buffer, int16_t n) 483 | { 484 | if (0 <= n && n < (WOT_SYM_BASE - WOT_NUM_BASE)) 485 | buffer->put_byte(n + WOT_NUM_BASE); 486 | else if (-128 < n && n <= 127) 487 | { 488 | buffer->put_byte(WOT_SIGNED_INT_8); 489 | buffer->put_byte((unsigned char)n); 490 | } 491 | else 492 | { 493 | uint16_t u = (uint16_t) n; 494 | 495 | buffer->put_byte(WOT_SIGNED_INT_16); 496 | buffer->put_byte(u >> 8); 497 | buffer->put_byte(u & 255); 498 | } 499 | } 500 | 501 | void MessageCoder::encode_signed32(MessageBuffer *buffer, int32_t n) 502 | { 503 | if (0 <= n && n < (WOT_SYM_BASE - WOT_NUM_BASE)) 504 | buffer->put_byte(n + WOT_NUM_BASE); 505 | else if (-128 < n && n <= 127) 506 | { 507 | buffer->put_byte(WOT_SIGNED_INT_8); 508 | buffer->put_byte((unsigned char)n); 509 | } 510 | else if (n < 32768) 511 | { 512 | uint16_t u = (uint16_t) n; 513 | buffer->put_byte(WOT_SIGNED_INT_16); 514 | buffer->put_byte(u >> 8); 515 | buffer->put_byte(u & 255); 516 | } 517 | else 518 | { 519 | uint32_t u = (uint32_t) n; 520 | buffer->put_byte(WOT_SIGNED_INT_32); 521 | buffer->put_byte((u >> 24) & 255); 522 | buffer->put_byte((u >> 1) & 255); 523 | buffer->put_byte((u >> 8) & 255); 524 | buffer->put_byte(u & 255); 525 | } 526 | } 527 | 528 | // Arduino uses 32 bits for both float and double 529 | void MessageCoder::encode_float(MessageBuffer *buffer, float x) 530 | { 531 | buffer->put_byte(WOT_FLOAT_32); 532 | 533 | union unum { 534 | unsigned char bytes4[4]; 535 | float x; 536 | } num; 537 | 538 | num.x = x; 539 | 540 | if (buffer->is_big_endian()) 541 | { 542 | buffer->put_byte(num.bytes4[3]); 543 | buffer->put_byte(num.bytes4[2]); 544 | buffer->put_byte(num.bytes4[1]); 545 | buffer->put_byte(num.bytes4[0]); 546 | } 547 | else 548 | { 549 | buffer->put_byte(num.bytes4[0]); 550 | buffer->put_byte(num.bytes4[1]); 551 | buffer->put_byte(num.bytes4[2]); 552 | buffer->put_byte(num.bytes4[3]); 553 | } 554 | } 555 | 556 | void MessageCoder::encode_symbol(MessageBuffer *buffer, unsigned int sym) 557 | { 558 | if (sym > 200) 559 | { 560 | Serial.println(F("symbol out of range")); 561 | } 562 | else 563 | { 564 | buffer->put_byte(sym + WOT_SYM_BASE); 565 | } 566 | } 567 | 568 | void MessageCoder::encode_null(MessageBuffer *buffer) 569 | { 570 | buffer->put_byte(WOT_VALUE_NULL); 571 | } 572 | 573 | void MessageCoder::encode_true(MessageBuffer *buffer) 574 | { 575 | buffer->put_byte(WOT_VALUE_TRUE); 576 | } 577 | 578 | void MessageCoder::encode_false(MessageBuffer *buffer) 579 | { 580 | buffer->put_byte(WOT_VALUE_FALSE); 581 | } 582 | 583 | void MessageCoder::encode_string(MessageBuffer *buffer, unsigned char *str) 584 | { 585 | unsigned char c, *p = (unsigned char *)str; 586 | 587 | buffer->put_byte(WOT_STRING); 588 | 589 | while ((c = *p++)) 590 | buffer->put_byte(c); 591 | 592 | buffer->put_byte(0); 593 | } 594 | 595 | #if defined(pgm_read_byte) 596 | void MessageCoder::encode_string(MessageBuffer *buffer, const __FlashStringHelper * str) 597 | { 598 | unsigned char c, *p = (unsigned char *)str; 599 | 600 | buffer->put_byte(WOT_STRING); 601 | 602 | while ((c = pgm_read_byte(p++))) 603 | buffer->put_byte(c); 604 | 605 | buffer->put_byte(0); 606 | } 607 | #endif 608 | 609 | void MessageCoder::encode_object_start(MessageBuffer *buffer) 610 | { 611 | buffer->put_byte(WOT_START_OBJECT); 612 | } 613 | 614 | void MessageCoder::encode_object_end(MessageBuffer *buffer) 615 | { 616 | buffer->put_byte(WOT_END_OBJECT); 617 | } 618 | 619 | void MessageCoder::encode_array_start(MessageBuffer *buffer) 620 | { 621 | buffer->put_byte(WOT_START_ARRAY); 622 | } 623 | 624 | void MessageCoder::encode_array_end(MessageBuffer *buffer) 625 | { 626 | buffer->put_byte(WOT_END_ARRAY); 627 | } 628 | 629 | #define WOT_MESSAGE_LENGTH 128 630 | 631 | #if 0 632 | void MessageCoder::test() 633 | { 634 | MessageBuffer membuf; 635 | 636 | unsigned char buffer[WOT_MESSAGE_LENGTH]; 637 | const float pi = 3.1415926; 638 | 639 | Serial.print(F("int has ")); Serial.print(sizeof(int)); Serial.println(F(" bytes")); 640 | Serial.print(F("long has ")); Serial.print(sizeof(long)); Serial.println(F(" bytes")); 641 | Serial.print(F("float has ")); Serial.print(sizeof(float)); Serial.println(F(" bytes")); 642 | Serial.print(F("double has ")); Serial.print(sizeof(double)); Serial.println(F(" bytes\n")); 643 | 644 | membuf.set_buffer(&buffer[0], WOT_MESSAGE_LENGTH); 645 | 646 | 647 | MessageCoder::encode_string(&membuf, F("hello world")); 648 | 649 | if (membuf.overflowed()) 650 | Serial.println(F("overflowed: ")); 651 | else { 652 | Serial.print(F("used ")); 653 | Serial.print(membuf.get_size()); 654 | Serial.println(F(" bytes")); 655 | MessageCoder::decode(&membuf); 656 | } 657 | 658 | membuf.reset(); 659 | MessageCoder::encode_array_start(&membuf); 660 | MessageCoder::encode_string(&membuf, F("one")); 661 | MessageCoder::encode_string(&membuf, F("two")); 662 | MessageCoder::encode_string(&membuf, F("three")); 663 | MessageCoder::encode_symbol(&membuf, 79); 664 | MessageCoder::encode_null(&membuf); 665 | MessageCoder::encode_true(&membuf); 666 | MessageCoder::encode_false(&membuf); 667 | MessageCoder::encode_unsigned16(&membuf, 7); 668 | MessageCoder::encode_unsigned16(&membuf, 24); 669 | MessageCoder::encode_unsigned16(&membuf, 200); 670 | MessageCoder::encode_unsigned16(&membuf, 300); 671 | MessageCoder::encode_unsigned32(&membuf, 65536); 672 | MessageCoder::encode_signed16(&membuf, -320); 673 | MessageCoder::encode_signed16(&membuf, -32768); 674 | MessageCoder::encode_signed32(&membuf, 32768); 675 | MessageCoder::encode_float(&membuf, (float)pi); 676 | MessageCoder::encode_array_end(&membuf); 677 | 678 | if (membuf.overflowed()) 679 | Serial.println(F("overflowed: ")); 680 | else { 681 | Serial.print(F("used ")); 682 | Serial.print(membuf.get_size()); 683 | Serial.println(F(" bytes")); 684 | MessageCoder::decode(&membuf); 685 | } 686 | } 687 | #endif 688 | -------------------------------------------------------------------------------- /JSON.cpp: -------------------------------------------------------------------------------- 1 | /* Minimal JSON support for Arduino */ 2 | 3 | #include 4 | #include 5 | #include "Strings.h" 6 | #include "NodePool.h" 7 | #include "AvlNode.h" 8 | #include "Names.h" 9 | #include "JSON.h" 10 | #include "WebThings.h" 11 | 12 | static WotNodePool *node_pool; 13 | static boolean gc_phase; 14 | 15 | void JSON::initialise_pool(WotNodePool *wot_node_pool) 16 | { 17 | node_pool = wot_node_pool; 18 | } 19 | 20 | #if defined(pgm_read_byte) 21 | JSON * JSON::parse(const __FlashStringHelper *src, Names *table) 22 | { 23 | Lexer lexer; 24 | lexer.src = ((char *)src)+PROGMEM_BOUNDARY; 25 | lexer.length = Strings::strlen(lexer.src); 26 | lexer.table = table; 27 | 28 | return parse_private(&lexer); 29 | } 30 | #endif 31 | 32 | JSON * JSON::parse(const char *src, Names *table) 33 | { 34 | Lexer lexer; 35 | lexer.src = src; 36 | lexer.length = Strings::strlen(src); 37 | lexer.table = table; 38 | 39 | return parse_private(&lexer); 40 | } 41 | 42 | // build JSON object hierarchy 43 | JSON * JSON::parse_private(Lexer *lexer) 44 | { 45 | // check token to determine what JSON type to construct 46 | Json_Token token = lexer->get_token(); 47 | 48 | switch (token) 49 | { 50 | case Object_start_token: 51 | return parse_object(lexer); 52 | case Array_start_token: 53 | return parse_array(lexer); 54 | case String_token: 55 | return new_string(lexer->token_src, lexer->token_len); 56 | case Null_token: 57 | return new_null(); 58 | case True_token: 59 | return new_boolean(true); 60 | case False_token: 61 | return new_boolean(false); 62 | case Float_token: 63 | return new_float(lexer->float_num); 64 | case Unsigned_token: 65 | return new_unsigned(lexer->unsigned_num); 66 | case Signed_token: 67 | return new_signed(lexer->signed_num); 68 | default: 69 | Serial.println(F("JSON syntax error")); 70 | } 71 | 72 | return null; 73 | } 74 | 75 | JSON * JSON::parse_object(Lexer *lexer) 76 | { 77 | JSON *object = new_object(); 78 | Json_Token token = lexer->get_token(); 79 | 80 | while (token != Error_token) 81 | { 82 | if (token == Object_stop_token) 83 | return object; 84 | 85 | if (token != String_token) 86 | break; 87 | 88 | Symbol symbol = lexer->table->symbol(lexer->token_src, 89 | lexer->token_len); 90 | 91 | token = lexer->get_token(); 92 | 93 | if (token != Colon_token) 94 | break; 95 | 96 | JSON *value = parse_private(lexer); 97 | 98 | if (!value) 99 | break; 100 | 101 | object->insert_property(symbol, value); 102 | token = lexer->get_token(); 103 | 104 | if (token == Object_stop_token) 105 | continue; 106 | 107 | if (token != Comma_token) 108 | break; 109 | 110 | token = lexer->get_token(); 111 | } 112 | 113 | // free incomplete object here along with its map from symbols to values 114 | 115 | Serial.print(F("JSON syntax error in object, token is ")); Serial.println(token); 116 | return null; 117 | } 118 | 119 | JSON * JSON::parse_array(Lexer *lexer) 120 | { 121 | JSON *array = new_array(); 122 | unsigned int index = 0; 123 | 124 | if (lexer->end_of_array()) 125 | return array; // empty array 126 | 127 | for (;;) 128 | { 129 | JSON *item = parse_private(lexer); 130 | 131 | if (item) 132 | array->insert_array_item(index++, item); 133 | else 134 | { 135 | Serial.println(F("missing array item")); 136 | break; 137 | } 138 | 139 | Json_Token token = lexer->get_token(); 140 | 141 | if (token == Array_stop_token) 142 | return array; 143 | 144 | if (token != Comma_token) 145 | break; 146 | } 147 | 148 | Serial.println(F("JSON syntax error in array")); 149 | return null; 150 | } 151 | 152 | void JSON::print_string(const char *name, unsigned int length) 153 | { 154 | unsigned int i; 155 | 156 | Serial.print(F("\"")); 157 | 158 | for (i = 0; i < length; ++i) 159 | Serial.print((Strings::get_char(name++))); 160 | 161 | Serial.print(F("\"")); 162 | } 163 | 164 | void JSON::print_name_value(AvlKey key, AvlValue value, void *context) 165 | { 166 | Serial.print(F(" ")); Serial.print((unsigned int)key); Serial.print(F(" : ")); 167 | ((JSON *)value)->print(); 168 | 169 | if ((void *)value != context) 170 | Serial.print(F(",")); 171 | } 172 | 173 | void JSON::print_array_item(AvlKey key, AvlValue value, void *context) 174 | { 175 | ((JSON *)value)->print(); 176 | 177 | if ((void *)value != context) 178 | Serial.print(F(",")); 179 | } 180 | 181 | void JSON::print() 182 | { 183 | switch (get_tag()) 184 | { 185 | case Object_t: 186 | Serial.print(F(" {")); 187 | AvlNode::apply(this->variant.object, (AvlApplyFn)print_name_value, 188 | (JSON *)(AvlNode::get_node(AvlNode::last(this->variant.object)))->get_value()); 189 | Serial.print(F("} ")); 190 | break; 191 | 192 | case Array_t: 193 | Serial.print(F(" [ ")); 194 | AvlNode::apply(this->variant.object, (AvlApplyFn)print_array_item, 195 | (JSON *)(AvlNode::get_node(AvlNode::last(this->variant.object)))->get_value()); 196 | Serial.print(F("] ")); 197 | break; 198 | 199 | case String_t: 200 | print_string(variant.str, get_str_length()); 201 | break; 202 | 203 | case Unsigned_t: 204 | Serial.print(this->variant.u); 205 | break; 206 | case Signed_t: 207 | Serial.print(this->variant.i); 208 | break; 209 | case Float_t: 210 | Serial.print(this->variant.number); 211 | break; 212 | 213 | case Boolean_t: 214 | Serial.print((this->variant.truth ? F(" true ") : F(" false "))); 215 | break; 216 | 217 | case Null_t: 218 | Serial.print(F(" null ")); 219 | break; 220 | 221 | case Function_t: 222 | Serial.print(F(" function ")); 223 | break; 224 | 225 | case Proxy_t: 226 | Serial.print(F(" proxy ")); 227 | break; 228 | 229 | case Thing_t: 230 | Serial.print(F(" thing ")); 231 | break; 232 | 233 | case Unused_t: 234 | break; // nothing to do 235 | } 236 | } 237 | 238 | void JSON::set_gc_phase(boolean phase) 239 | { 240 | gc_phase = phase; 241 | } 242 | 243 | // mark this node and nodes reachable from it 244 | void JSON::reachable(boolean phase) 245 | { 246 | if (!marked(phase)) 247 | { 248 | switch (get_tag()) 249 | { 250 | case Object_t: 251 | case Array_t: 252 | AvlNode::apply(this->variant.object, (AvlApplyFn)reachable_item, (void *)phase); 253 | break; 254 | 255 | case Thing_t: 256 | this->variant.thing->reachable(phase); 257 | break; 258 | 259 | case Proxy_t: 260 | this->variant.proxy->reachable(phase); 261 | break; 262 | 263 | case Unused_t: 264 | break; 265 | 266 | default: 267 | toggle_mark(); 268 | break; 269 | } 270 | } 271 | } 272 | 273 | void JSON::reachable_item(AvlKey key, AvlValue value, void *context) 274 | { 275 | ((JSON *)value)->reachable((boolean)context); 276 | } 277 | 278 | // sweep this node and all nodes reachable from it that 279 | // haven't already been marked as reachable from roots 280 | void JSON::sweep(boolean phase) 281 | { 282 | if (!marked(phase)) 283 | { 284 | switch (get_tag()) 285 | { 286 | case Object_t: 287 | case Array_t: 288 | AvlNode::apply(this->variant.object, (AvlApplyFn)sweep_item, (void *)phase); 289 | AvlNode::free(this->variant.object); 290 | break; 291 | 292 | case Thing_t: 293 | this->variant.thing->sweep(phase); 294 | WebThings::remove_thing(this->variant.thing); 295 | break; 296 | 297 | case Proxy_t: 298 | this->variant.proxy->sweep(phase); 299 | WebThings::remove_proxy(this->variant.proxy); 300 | break; 301 | 302 | case Unused_t: 303 | break; 304 | 305 | default: 306 | break; 307 | } 308 | 309 | free(); 310 | } 311 | } 312 | 313 | 314 | void JSON::sweep_item(AvlKey key, AvlValue value, void *context) 315 | { 316 | ((JSON *)value)->sweep((boolean)context); 317 | } 318 | 319 | void JSON::free() 320 | { 321 | Serial.print("freeing JSON node, tag "); Serial.println(get_tag()); 322 | 323 | // safe against already freed node 324 | node_pool->free((wot_node_pool_t *)this); 325 | } 326 | 327 | // allocate node from fixed memory pool 328 | JSON * JSON::new_node() 329 | { 330 | JSON * node = (JSON *)(node_pool->allocate_node()); 331 | 332 | if (node) 333 | { 334 | Serial.println("allocating JSON node"); 335 | node->set_tag(Null_t); 336 | node->variant.number = 0.0; 337 | 338 | // set mark for this garbage collection cycle 339 | if (gc_phase) 340 | node->set_mark(); 341 | else 342 | node->reset_mark(); 343 | } 344 | 345 | return node; 346 | } 347 | 348 | JSON * JSON::new_float(float n) 349 | { 350 | JSON *node = JSON::new_node(); 351 | 352 | if (node) 353 | { 354 | node->set_tag(Float_t); 355 | node->variant.number = n; 356 | } 357 | 358 | return node; 359 | } 360 | 361 | JSON * JSON::new_unsigned(unsigned int n) 362 | { 363 | JSON *node = JSON::new_node(); 364 | 365 | if (node) 366 | { 367 | node->set_tag(Unsigned_t); 368 | node->variant.u = n; 369 | } 370 | 371 | return node; 372 | } 373 | 374 | JSON * JSON::new_signed(int n) 375 | { 376 | JSON *node = JSON::new_node(); 377 | 378 | if (node) 379 | { 380 | node->set_tag(Signed_t); 381 | node->variant.i = n; 382 | } 383 | 384 | return node; 385 | } 386 | 387 | JSON * JSON::new_boolean(bool value) 388 | { 389 | JSON *node = JSON::new_node(); 390 | 391 | if (node) 392 | { 393 | node->set_tag(Boolean_t); 394 | node->variant.truth = value; 395 | } 396 | 397 | return node; 398 | } 399 | 400 | JSON * JSON::new_null() 401 | { 402 | JSON *node = JSON::new_node(); 403 | 404 | if (node) 405 | { 406 | node->set_tag(Null_t); 407 | } 408 | 409 | return node; 410 | } 411 | 412 | JSON * JSON::new_string(const __FlashStringHelper *str) 413 | { 414 | return new_string(((char *)str)+PROGMEM_BOUNDARY); 415 | } 416 | 417 | JSON * JSON::new_string(char *str) 418 | { 419 | return new_string(str, Strings::strlen(str)); 420 | } 421 | 422 | JSON * JSON::new_string(char *str, unsigned int length) 423 | { 424 | JSON *node = JSON::new_node(); 425 | 426 | if (node) 427 | { 428 | node->set_tag(String_t); 429 | node->variant.str = str; 430 | node->set_str_length(length); 431 | } 432 | 433 | return node; 434 | } 435 | 436 | 437 | JSON * JSON::new_object() 438 | { 439 | JSON *node = JSON::new_node(); 440 | 441 | if (node) 442 | { 443 | node->set_tag(Object_t); 444 | node->variant.object = 0; 445 | } 446 | 447 | return node; 448 | } 449 | 450 | // arrays are not yet implemented 451 | // need to decide how to store them! 452 | JSON * JSON::new_array() 453 | { 454 | JSON *node = JSON::new_node(); 455 | 456 | if (node) 457 | { 458 | node->set_tag(Array_t); 459 | node->variant.object = 0; 460 | } 461 | 462 | return node; 463 | } 464 | 465 | JSON * JSON::new_function(GenericFn func) 466 | { 467 | JSON *node = JSON::new_node(); 468 | 469 | if (node) 470 | { 471 | node->set_tag(Function_t); 472 | node->variant.function = func; 473 | } 474 | 475 | return node; 476 | } 477 | 478 | // only JSON objects or arrays can have multiple 479 | // references, so they need to be added the set 480 | // of stale references, other nodes can be freed 481 | boolean JSON::free_leaves() 482 | { 483 | Json_Tag tag = get_tag(); 484 | 485 | if (tag == Object_t || tag == Array_t) 486 | return 0; 487 | 488 | free(); 489 | return 1; 490 | } 491 | 492 | Json_Tag JSON::get_tag() 493 | { 494 | return (Json_Tag)(taglen & 0xF); 495 | } 496 | 497 | void JSON::set_tag(Json_Tag tag) 498 | { 499 | taglen &= ~0xF; 500 | taglen |= (unsigned int)tag; 501 | } 502 | 503 | boolean JSON::marked(boolean phase) 504 | { 505 | if (phase) 506 | return !(taglen & 0x10); 507 | 508 | return taglen & 0x10; 509 | } 510 | 511 | void JSON::toggle_mark() 512 | { 513 | if (taglen & 0x10) 514 | taglen &= ~0x10; 515 | else 516 | taglen |= 0x10; 517 | } 518 | 519 | void JSON::set_mark() 520 | { 521 | taglen |= 0x10; 522 | } 523 | 524 | void JSON::reset_mark() 525 | { 526 | taglen &= ~0x10; 527 | } 528 | 529 | void JSON::set_obj_id(unsigned int id) 530 | { 531 | taglen &= 0x1F; 532 | taglen |= (id << 5); 533 | } 534 | 535 | unsigned int JSON::get_obj_id() 536 | { 537 | return taglen >> 5; 538 | } 539 | 540 | void JSON::set_str_length(unsigned int length) 541 | { 542 | taglen &= 0x1F; 543 | taglen |= (length << 5); 544 | } 545 | 546 | unsigned int JSON::get_str_length() 547 | { 548 | return taglen >> 5; 549 | } 550 | 551 | JSON * JSON::retrieve_property(Symbol symbol) 552 | { 553 | AvlKey key = (AvlKey)symbol + 1; 554 | AvlNode::print_keys(variant.object); 555 | 556 | if (get_tag() == Object_t) 557 | return (JSON *)AvlNode::find_key(variant.object, key); 558 | 559 | return null; 560 | } 561 | 562 | void JSON::insert_property(Symbol symbol, JSON *new_value) 563 | { 564 | if (get_tag() == Object_t) { 565 | AvlKey key = (AvlKey)symbol + 1; 566 | JSON *old_value =(JSON *)AvlNode::find_key(variant.object, key); 567 | variant.object = AvlNode::insert_key(variant.object, key, (void *)new_value); 568 | check_if_stale(old_value, new_value); 569 | } 570 | } 571 | 572 | GenericFn JSON::retrieve_function(Symbol action) 573 | { 574 | if (get_tag() == Object_t) { 575 | JSON *func = (JSON *)AvlNode::find_key(variant.object, action); 576 | 577 | if (func && func->get_tag() == Function_t) 578 | return func->variant.function; 579 | } 580 | 581 | return null; 582 | } 583 | 584 | JSON * JSON::retrieve_array_item(unsigned int index) 585 | { 586 | AvlKey key = (AvlKey)index + 1; 587 | 588 | if (get_tag() == Array_t) 589 | return (JSON *)AvlNode::find_key(variant.object, key); 590 | 591 | return null; 592 | } 593 | 594 | // Prepending is a lot harder and could be done 595 | // by appending a copy of the last node value then 596 | // shifting the values from one node to the next 597 | // in a left to right traversal, and finally 598 | // updating the first node. This will require a 599 | // special helper in the AvlNode class. 600 | 601 | void JSON::append_array_item(JSON *value) 602 | { 603 | if (get_tag() == Array_t) { 604 | AvlKey last = AvlNode::last_key(variant.object); 605 | insert_array_item((unsigned int)last, value); 606 | } 607 | } 608 | 609 | void JSON::insert_array_item(unsigned int index, JSON *new_value) 610 | { 611 | if (get_tag() == Array_t) { 612 | AvlKey key = (AvlKey)index + 1; 613 | JSON * old_value = (JSON *)AvlNode::find_key(variant.object, key); 614 | variant.object = AvlNode::insert_key(variant.object, key, (void *)new_value); 615 | check_if_stale(old_value, new_value); 616 | } 617 | } 618 | 619 | void JSON::check_if_stale(JSON * old_value, JSON * new_value) 620 | { 621 | if (old_value && new_value != old_value) 622 | WebThings::add_stale(old_value); 623 | } 624 | 625 | boolean JSON::Lexer::end_of_array() 626 | { 627 | unsigned int c = peek_byte(); 628 | 629 | while (isspace(c)) 630 | { 631 | next_byte(); 632 | c = peek_byte(); 633 | } 634 | 635 | if (c == ']') 636 | return true; 637 | 638 | return false; 639 | } 640 | 641 | Json_Token JSON::Lexer::get_token() 642 | { 643 | unsigned int c = peek_byte(); 644 | 645 | while (isspace(c)) 646 | { 647 | next_byte(); 648 | c = peek_byte(); 649 | } 650 | 651 | switch (c) 652 | { 653 | case ':': 654 | next_byte(); 655 | return Colon_token; 656 | case ',': 657 | next_byte(); 658 | return Comma_token; 659 | case '{': 660 | next_byte(); 661 | return Object_start_token; 662 | case '}': 663 | next_byte(); 664 | return Object_stop_token; 665 | case '[': 666 | next_byte(); 667 | return Array_start_token; 668 | case ']': 669 | next_byte(); 670 | return Array_stop_token; 671 | case '"': 672 | return get_string(); 673 | case '-': 674 | case '.': 675 | case '0': 676 | case '1': 677 | case '2': 678 | case '3': 679 | case '4': 680 | case '5': 681 | case '6': 682 | case '7': 683 | case '8': 684 | case '9': 685 | return get_number(c); 686 | default: 687 | return get_special(c); 688 | } 689 | } 690 | 691 | // signed and unsigned integers, and floats 692 | Json_Token JSON::Lexer::get_number(unsigned int c) 693 | { 694 | boolean negative = false; 695 | boolean real = false; 696 | int d = 0, e = 0; 697 | token_src = (char *)src; 698 | token_len = 0; 699 | 700 | if (c == '-') 701 | { 702 | negative = true; 703 | next_byte(); 704 | c = peek_byte(); 705 | } 706 | 707 | if (c == '.') 708 | { 709 | next_byte(); 710 | real = true; 711 | c = peek_byte(); 712 | } 713 | 714 | while (isdigit(c)) 715 | { 716 | ++d; 717 | next_byte(); 718 | c = peek_byte(); 719 | } 720 | 721 | if (c == '.') 722 | { 723 | next_byte(); 724 | real = true; 725 | c = peek_byte(); 726 | } 727 | 728 | while (isdigit(c)) 729 | { 730 | ++d; 731 | next_byte(); 732 | c = peek_byte(); 733 | } 734 | 735 | if (c == 'e' || c == 'E') 736 | { 737 | if (!d) 738 | return Error_token; 739 | 740 | next_byte(); 741 | real = true; 742 | c = peek_byte(); 743 | } 744 | 745 | if (c == '-') 746 | { 747 | next_byte(); 748 | c = peek_byte(); 749 | } 750 | 751 | while (isdigit(c)) 752 | { 753 | ++e; 754 | next_byte(); 755 | c = peek_byte(); 756 | } 757 | 758 | if (! (d|e)) 759 | return Error_token; 760 | 761 | if (real) 762 | { 763 | sscanf((const char *)token_src, "%f", &float_num); 764 | return Float_token; 765 | } 766 | 767 | if (negative) 768 | { 769 | sscanf((const char *)token_src, "%d", &signed_num); 770 | return Signed_token; 771 | } 772 | 773 | sscanf((const char *)token_src, "%u", &unsigned_num); 774 | return Unsigned_token; 775 | } 776 | 777 | // null, true or false 778 | Json_Token JSON::Lexer::get_special(unsigned int c) 779 | { 780 | if (c == 'n') 781 | { 782 | if (length >= 4 && 783 | Strings::get_char(src+1) == 'u' && 784 | Strings::get_char(src+2) == 'l' && 785 | Strings::get_char(src+3) == 'l') 786 | { 787 | src += 4; 788 | length += 4; 789 | return Null_token; 790 | } 791 | } 792 | else if (c == 't') 793 | { 794 | if (length >= 4 && 795 | Strings::get_char(src+1) == 'r' && 796 | Strings::get_char(src+2) == 'u' && 797 | Strings::get_char(src+3) == 'e') 798 | { 799 | src += 4; 800 | length += 4; 801 | return True_token; 802 | } 803 | } 804 | else if (c == 'f') 805 | { 806 | if (length >= 5 && 807 | Strings::get_char(src+1) == 'a' && 808 | Strings::get_char(src+2) == 'l' && 809 | Strings::get_char(src+3) == 's' && 810 | Strings::get_char(src+4) == 'e') 811 | { 812 | src += 5; 813 | length += 5; 814 | return False_token; 815 | } 816 | } 817 | 818 | return Error_token; 819 | } 820 | 821 | Json_Token JSON::Lexer::get_string() 822 | { 823 | next_byte(); 824 | token_src = (char *)src; 825 | token_len = 0; 826 | 827 | while (length > 0) 828 | { 829 | --length; 830 | 831 | if (Strings::get_char(src++) == '"') 832 | return String_token; 833 | 834 | ++token_len; 835 | } 836 | 837 | Serial.println(F("Missing trailing quote mark")); 838 | return Error_token; 839 | } 840 | 841 | void JSON::Lexer::next_byte() 842 | { 843 | if (length > 0) 844 | { 845 | --length; 846 | src++; 847 | } 848 | } 849 | 850 | unsigned int JSON::Lexer::peek_byte() 851 | { 852 | if (length > 0) 853 | return Strings::get_char(src); 854 | 855 | return 256; 856 | } 857 | -------------------------------------------------------------------------------- /WiznetTCP.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Strings.h" 3 | #include "WiznetTCP.h" 4 | #include "DHCP.h" 5 | 6 | // MULTICAST FOR GATEWAY DISCOVERY 7 | 8 | #define DISCOVERY_PORT 54321 9 | #define DISCOVERY_GROUP "225.0.0.37" 10 | 11 | #define MAX_MDNS_RETRY 10 12 | #define MDNS_WAIT_TIME 2000 13 | 14 | // COMMON REGISTERS 15 | 16 | #define W5100_MODE_REGISTER 0X0000 // 1 byte 17 | #define W5100_GATEWAY 0X0001 // 4 bytes 18 | #define W5100_SUBNET_MASK 0X0005 // 4 bytes 19 | #define W5100_MAC_ADDRESS 0X0009 // 6 bytes 20 | #define W5100_LOCAL_IP_ADDRESS 0X000F // 4 bytes 21 | #define W5100_INTR_REGISTER 0x0015 22 | #define W5100_INTR_MASK_REGISTER 0x0016 23 | #define W5100_RMSR_REGISTER 0X001A // 1 byte 24 | #define W5100_TMSR_REGISTER 0X001B // 1 byte 25 | #define W5100_UIPR0_REGISTER 0X002A // 4 bytes 26 | #define W5100_UPORT0_REGISTER 0X002E // 2 bytes 27 | 28 | // SOCKET REGISTER OFFSETS 29 | 30 | #define SOCKET_MODE 0x0 31 | #define SOCKET_COMMAND 0x1 32 | #define SOCKET_INTERRUPT 0x2 33 | #define SOCKET_STATUS 0x3 34 | #define SOCKET_SRC_PORT 0x4 35 | #define SOCKET_MAC_ADDRESS 0x6 36 | #define SOCKET_IP_ADDRESS 0xC 37 | #define SOCKET_DEST_PORT 0x10 38 | #define SOCKET_MAX_SEG_SIZE 0x12 39 | #define SOCKET_PROTO 0x14 40 | #define SOCKET_TOS 0x15 41 | #define SOCKET_TTL 0x16 42 | #define SOCKET_TX_FREE_SIZE 0x20 43 | #define SOCKET_TX_READ_PNTR 0x22 44 | #define SOCKET_TX_WRITE_PNTR 0x24 45 | #define SOCKET_RX_RECV_SIZE 0x26 46 | #define SOCKET_RX_READ_PNTR 0x28 47 | 48 | // STATUS FLAGs 49 | 50 | #define W5100_SEND_OK 16 51 | #define W5100_TIMEOUT 8 52 | #define W5100_RECV 4 53 | #define W5100_DISCON 2 54 | #define W5100_CON 1 55 | 56 | 57 | // SOCKET BUFER INFO 58 | // 2 sockets each with 4KB for TX and RX buffers 59 | // more generally will need a macro based upon 60 | // number of sockets that have been initialised 61 | // for now only concerned with socket zero 62 | 63 | #define BASE_TX_BUFFER 0x4000 64 | #define BASE_RX_BUFFER 0x6000 65 | #define SOCKET_SIZE 0x1000 66 | #define TX_BUFFER_MASK 0xFFF 67 | #define RX_BUFFER_MASK 0xFFF 68 | 69 | // OFFSETS 70 | 71 | #define SOCKET_ZERO 0x400 72 | #define SOCKET_ONE 0x500 73 | #define SOCKET_TWO 0x600 74 | #define SOCKET_THREE 0x700 75 | 76 | // COMMANDs 77 | 78 | #define SOCKET_OPEN 0x01 79 | #define SOCKET_LISTEN 0x02 80 | #define SOCKET_CONNECT 0x04 81 | #define SOCKET_DISCONNECT 0x08 82 | #define SOCKET_CLOSE 0x10 83 | #define SOCKET_SEND 0x20 84 | #define SOCKET_SEND_MAC 0x21 85 | #define SOCKET_SEND_KEEP 0x22 86 | #define SOCKET_RECEIVE 0x40 87 | 88 | // MODE 89 | 90 | #define SOCKET_CLOSED 0x00 91 | #define SOCKET_TCP 0x01 92 | #define SOCKET_UDP 0x02 93 | #define SOCKET_RAW 0x03 94 | #define SOCKET_MULTICAST 0x80 95 | #define IGMP_V1 0x20 96 | #define IGMP_V2 0x00 97 | 98 | // SOCKET STATUS defined in include file for use by apps 99 | 100 | // forward reference to SPI data transfer functions 101 | inline static uint8_t read_byte(uint16_t addr); 102 | inline static void write_byte(uint16_t addr, uint8_t data); 103 | static uint16_t read_word(uint16_t addr); 104 | static void write_word(uint16_t addr, uint16_t word); 105 | static void write32(uint16_t addr, uint8_t *p); 106 | static void write48(uint16_t addr, uint8_t *p); 107 | static void write32(uint16_t addr, uint8_t n0, uint8_t n1, uint8_t n2, uint8_t n3); 108 | static void write48(uint16_t addr, uint8_t n0, uint8_t n1, uint8_t n2, 109 | uint8_t n3, uint8_t n4, uint8_t n5); 110 | 111 | 112 | WiznetTCP::WiznetTCP() 113 | { 114 | // wait until W5100 has started up and is ready for commands 115 | delayMicroseconds(500); 116 | spi_init(); 117 | 118 | write_byte(W5100_MODE_REGISTER, 0x80); // reset 119 | 120 | // 2 sockets with 8KB each, i.e. 4KB max packet size 121 | // but for 4 sockets with 4KB use value of 0x55 122 | // if we only use 1 socket we can use 2 * 4KB for RAM 123 | write_byte(W5100_RMSR_REGISTER, 0xAA); 124 | write_byte(W5100_TMSR_REGISTER, 0xAA); 125 | } 126 | 127 | WiznetTCP::~WiznetTCP() 128 | { 129 | // close W5100 socket 130 | } 131 | 132 | void WiznetTCP::begin(uint16_t port) 133 | { 134 | delay(200); // not needed, here for luck 135 | 136 | // ask server for IP address, subnet mask and gateway 137 | if (!run_DHCP_client()) { 138 | // Couldn't get an IP address so use defaults 139 | Serial.println(F("Using default network configuration as fall back")); 140 | write32(W5100_GATEWAY, 192, 168, 1, 254); 141 | write32(W5100_SUBNET_MASK, 255, 255, 255, 0); 142 | write32(W5100_LOCAL_IP_ADDRESS, 192, 168, 1, 25); 143 | } 144 | 145 | uint8_t n0, n1, n2, n3; 146 | get_local_ip(&n0, &n1, &n2, &n3); 147 | Serial.print(F("IP = ")); 148 | Serial.print(n0); 149 | Serial.print(F(".")); 150 | Serial.print(n1); 151 | Serial.print(F(".")); 152 | Serial.print(n2); 153 | Serial.print(F(".")); 154 | Serial.print(n3); 155 | Serial.print(F(", port = ")); 156 | Serial.println(port); 157 | 158 | write_word(SOCKET_ZERO + SOCKET_SRC_PORT, port); 159 | open(); 160 | } 161 | 162 | void WiznetTCP::begin(uint8_t n0, uint8_t n1, uint8_t n2, uint8_t n3, uint16_t port) 163 | { 164 | delay(200); // not needed, here for luck 165 | 166 | write32(W5100_GATEWAY, n0, n1, n2, 254); 167 | write48(W5100_MAC_ADDRESS, 0x61, 0xf8, 0x1d, 0xbc, 0xf4, 0x2f); 168 | write32(W5100_SUBNET_MASK, 255, 255, 255, 0); 169 | write32(W5100_LOCAL_IP_ADDRESS, n0, n1, n2, n3); 170 | write_word(SOCKET_ZERO + SOCKET_SRC_PORT, port); 171 | open(); 172 | } 173 | 174 | void WiznetTCP::discover_gateway() 175 | { 176 | uint16_t length; 177 | uint8_t found = 0; 178 | 179 | uint8_t query[33] = { 180 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // header 181 | 0x04,0x5F,0x77,0x6F,0x74, // _wot 182 | 0x04,0x5F,0x74,0x63,0x70, // _tcp 183 | 0x05,0x6C,0x6F,0x63,0x61,0x6C, // local 184 | 0x00, // end of service type 185 | 0x00, 0x0C, // QTYPE PTR 186 | 0x00, 0x01, // QCLASS IN 187 | }; 188 | 189 | // the gateway is recored as public properties on WiznetTCP class 190 | // I need a better way to pass these to the transport layer 191 | gateway_ip = 0; 192 | gateway_port = 0; 193 | 194 | // save source port to restore it upon return 195 | uint16_t port = read_word(SOCKET_ZERO + SOCKET_SRC_PORT); 196 | 197 | for (uint8_t i = MAX_MDNS_RETRY; i; --i) { 198 | uint8_t status = get_socket_status(); 199 | 200 | if (status == SOCK_INIT || status == SOCK_CLOSED) { 201 | Serial.println(F("starting mDNS search")); 202 | 203 | write_word(SOCKET_ZERO + SOCKET_DEST_PORT, 5353); 204 | write_word(SOCKET_ZERO + SOCKET_SRC_PORT, 5353); 205 | 206 | // mDNS IPv4 dest address 224.0.0.251 port 5353 207 | write32(SOCKET_ZERO + SOCKET_IP_ADDRESS, 224, 0, 0, 251); 208 | 209 | // mDNS socket dest mac address 01:00:5E:00:00:FB 210 | // this is a simple mapping from the IPv4 address 211 | write48(SOCKET_ZERO + SOCKET_MAC_ADDRESS, 212 | 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFB); 213 | 214 | // The W5100 automatically sends the IGMP join (report) 215 | // when the multicast socket is opened, and likewise, sends 216 | // the IGMP leave automatically when the socket is closed 217 | 218 | write_byte(SOCKET_ZERO + SOCKET_MODE, SOCKET_UDP|SOCKET_MULTICAST|IGMP_V2); 219 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_OPEN); 220 | } 221 | 222 | if (get_socket_status() == SOCK_UDP) { 223 | Serial.print(F("querying _wot._tcp.local, ")); 224 | send((char *)query, sizeof(query)); 225 | } else { 226 | Serial.println(F("socket not open for UDP")); 227 | close(); 228 | continue; 229 | } 230 | 231 | if ((length = receive_available(MDNS_WAIT_TIME)) > 0) { 232 | Serial.print(F("got mDNS message wireshark length ")); 233 | Serial.println(length+34); 234 | 235 | if (!handle_mDNS_response(query)) 236 | Serial.println(F("error in DNS message")); 237 | 238 | if (gateway_ip) { 239 | found = 1; 240 | break; 241 | } 242 | 243 | flush_receive(); 244 | } 245 | } 246 | 247 | close(); 248 | 249 | if (!found) 250 | Serial.print(F("couldn't find gateway, ")); 251 | 252 | write_word(SOCKET_ZERO + SOCKET_SRC_PORT, port); 253 | Serial.println(F("ending mDNS search")); 254 | } 255 | 256 | // query is the mDNS query message that triggered the response 257 | uint8_t WiznetTCP::handle_mDNS_response(uint8_t *query) 258 | { 259 | uint32_t ip; 260 | uint16_t port, offset = 0, name_offset, srv_link = 0; 261 | uint8_t buffer[64]; 262 | uint8_t match; 263 | uint16_t length = receive_available(); 264 | 265 | // first 8 bytes are for IPv4 address, port and length 266 | // these are followed by the 12 byte DNS message header 267 | if (peek(offset, (char *)buffer, 20) != 20) 268 | return 0; 269 | 270 | offset += 20; 271 | uint16_t id = buffer[8]; id <<= 8; id |= buffer[9]; 272 | uint16_t flags = buffer[10]; flags <<= 8; flags |= buffer[11]; 273 | uint16_t qdcnt = buffer[12]; qdcnt <<= 8; qdcnt |= buffer[13]; 274 | uint16_t ancnt = buffer[14]; ancnt <<= 8; ancnt |= buffer[15]; 275 | uint16_t nscnt = buffer[16]; nscnt <<= 8; nscnt |= buffer[17]; 276 | uint16_t arcnt = buffer[18]; arcnt <<= 8; arcnt |= buffer[19]; 277 | uint8_t rrcount = ancnt + nscnt + arcnt; 278 | 279 | Serial.print(F(" id = 0x")); Serial.println(id, HEX); 280 | Serial.print(F(" flags = 0x")); Serial.println(flags, HEX); 281 | Serial.print(F(" Questions: ")); Serial.println(qdcnt); 282 | Serial.print(F(" Answer RRs: ")); Serial.println(ancnt); 283 | Serial.print(F(" Authority RRs: ")); Serial.println(nscnt); 284 | Serial.print(F(" Additional RRs: ")); Serial.println(arcnt); 285 | 286 | // Parse questions which may come from other clients 287 | 288 | while (qdcnt--) { 289 | Serial.print(F("QN: ")); 290 | 291 | if (!(offset = parse_name(offset, buffer, query+12, &match))) 292 | return 0; 293 | 294 | // skip over qtype and qclass 295 | if (peek(offset, (char *)buffer, 4) != 4) 296 | return 0; 297 | 298 | offset += 4; 299 | uint16_t qtype = buffer[0]; qtype <<= 8; qtype |= buffer[1]; 300 | uint16_t qclass = buffer[2]; qclass <<= 8; qclass |= buffer[3]; 301 | Serial.print(F(" type = 0x")); Serial.println(qtype, HEX); 302 | Serial.print(F(" class = 0x")); Serial.println(qclass, HEX); 303 | } 304 | 305 | // Parse resource records 306 | 307 | while (rrcount--) { 308 | // read DNS NAME and check it is as expected 309 | // NAMES are <= 255 bytes including length fields 310 | // LABELS are 63 bytes max 311 | // *** please implement the checking algorithm 312 | 313 | Serial.print(F("RN: ")); 314 | name_offset = offset; // note for later match 315 | 316 | if (!(offset = parse_name(offset, buffer, query+12, &match))) 317 | return 0; 318 | 319 | // TYPE 2 bytes with resource record type. 320 | // CLASS 2 bytes indicating the class of the data 321 | // TTL a 32 bit unsigned integer giving expiry time in seconds 322 | // RDLENGTH 16 bits indicating the length of the RDATA field in bytes. 323 | // RDATA varies depending on the type and class of the resource record. 324 | 325 | // read the resource record header 326 | if (peek(offset, (char *)buffer, 10) != 10) 327 | return 0; 328 | 329 | offset += 10; 330 | uint16_t rtype = buffer[0]; rtype <<= 8; rtype |= buffer[1]; 331 | uint16_t rclass = buffer[2]; rclass <<= 8; rclass |= buffer[3]; 332 | uint32_t time2live = buffer[4]; 333 | time2live <<= 8; time2live |= buffer[5]; 334 | time2live <<= 8; time2live |= buffer[6]; 335 | time2live <<= 8; time2live |= buffer[7]; 336 | uint16_t rdlength = buffer[8]; rdlength <<= 8; rdlength |= buffer[9]; 337 | 338 | Serial.print(F("Rtype: ")); 339 | Serial.println(rtype); 340 | Serial.print(F("Rclass: 0x")); 341 | Serial.println(rclass, HEX); 342 | Serial.print(F("Rttl: ")); 343 | Serial.println(time2live); 344 | Serial.print(F("Rlength: ")); 345 | Serial.println(rdlength); 346 | 347 | // followed by rdlength bytes of data 348 | 349 | if (rtype == 33) { 350 | // DNS SRV record - server port 351 | if (peek(offset, (char *)buffer, 6) != 6) 352 | return 0; 353 | 354 | offset += 6; 355 | uint16_t srv_port = buffer[4]; 356 | srv_port <<= 8; 357 | srv_port |= buffer[5]; 358 | srv_link = offset; 359 | rdlength -= 6; 360 | 361 | Serial.print(F("Got port ")); 362 | Serial.println(srv_port); 363 | 364 | if (match) 365 | gateway_port = srv_port; 366 | } else if (rtype == 1) { 367 | // DNS A record - IPv4 address 368 | if (peek(offset, (char *)buffer, 4) != 4) 369 | return 0; 370 | 371 | offset += 4; 372 | uint32_t srv_ip = buffer[0]; 373 | srv_ip <<= 8; srv_ip |= buffer[1]; 374 | srv_ip <<= 8; srv_ip |= buffer[2]; 375 | srv_ip <<= 8; srv_ip |= buffer[3]; 376 | rdlength -= 4; 377 | 378 | Serial.print(F("Got IP ")); 379 | Serial.print((srv_ip>>24)&255); 380 | Serial.print(F(".")); 381 | Serial.print((srv_ip>>16)&255); 382 | Serial.print(F(".")); 383 | Serial.print((srv_ip>>8)&255); 384 | Serial.print(F(".")); 385 | Serial.print(srv_ip&255); 386 | Serial.println(); 387 | 388 | uint16_t link = get_link(name_offset); 389 | 390 | if (link < 0xFFFF && link == srv_link) { 391 | Serial.println(F("Matching IP record")); 392 | gateway_ip = srv_ip; 393 | } 394 | } 395 | 396 | offset += rdlength; 397 | } 398 | 399 | // found gateway? 400 | 401 | if (gateway_ip && gateway_port) { 402 | Serial.print(F("Gateway IP = ")); 403 | Serial.print((gateway_ip>>24)&255); 404 | Serial.print(F(".")); 405 | Serial.print((gateway_ip>>16)&255); 406 | Serial.print(F(".")); 407 | Serial.print((gateway_ip>>8)&255); 408 | Serial.print(F(".")); 409 | Serial.print(gateway_ip&255); 410 | Serial.print(F(", port = ")); 411 | Serial.println(gateway_port); 412 | } 413 | 414 | return 1; 415 | } 416 | 417 | // link to first NAME is 12, but need to add 418 | // 8 for the preceding IPv4, port and length 419 | uint16_t WiznetTCP::get_link(uint16_t offset) 420 | { 421 | uint8_t buffer[2]; 422 | uint16_t link; 423 | 424 | if (peek(offset++, (char *)buffer, 2) != 2) 425 | return 0xFFFF; 426 | 427 | link = buffer[0]; 428 | 429 | if (link < 0xC0) 430 | return 0xFFFF; 431 | 432 | link -= 0xC0; 433 | link <<= 8; 434 | link |= buffer[1]; 435 | return link + 8; 436 | } 437 | 438 | // sn is the service name used in the mDNS query 439 | // buffer is large enough for all valid labels 440 | // offset is position to read within received message 441 | uint16_t WiznetTCP::parse_name(uint16_t offset, uint8_t *buffer, 442 | uint8_t *sn, uint8_t *matched) 443 | { 444 | uint16_t saved = 0; 445 | uint8_t i = 0; 446 | uint8_t match = 1; 447 | *matched = 0; 448 | 449 | for (;;) { 450 | // get number of characters in label 451 | if (peek(offset++, (char *)buffer, 1) != 1) 452 | return 0; 453 | 454 | uint8_t label_len = buffer[0]; 455 | 456 | // 0 signals end of NAME 457 | if (!label_len) { 458 | if (saved) 459 | offset = saved; 460 | break; 461 | } 462 | 463 | // check for link to NAME earlier in message 464 | if (label_len >= 0xC0) { 465 | if (peek(offset++, (char *)buffer+1, 1) != 1) 466 | return 0; 467 | 468 | if (!saved) 469 | saved = offset; 470 | 471 | offset = buffer[0] - 0xC0; 472 | offset <<= 8; 473 | offset |= buffer[1]; 474 | offset += 8; 475 | continue; 476 | } 477 | 478 | // RFC 1025: labels should be 63 octets or less 479 | if (label_len > 63) 480 | return 0; 481 | 482 | // read name part into buffer 483 | if (peek(offset, (char *)buffer, label_len) != label_len) 484 | return 0; 485 | 486 | offset += label_len; 487 | buffer[label_len] = '\0'; 488 | Serial.print((char *)buffer); 489 | Serial.print(F(".")); 490 | 491 | // check for incremental match to query service name 492 | if (match) { 493 | if (*sn == label_len && 494 | !strncmp((const char *)sn+1, (const char *)buffer, (size_t)label_len)) 495 | sn += 1 + label_len; 496 | else if (i > 0) 497 | match = 0; 498 | } 499 | 500 | ++i; 501 | } 502 | 503 | Serial.println(); 504 | 505 | if (match) 506 | Serial.println(F("record matches query")); 507 | 508 | *matched = match; 509 | return offset; 510 | } 511 | 512 | // to be scrapped as will waste flash space after debugging is done 513 | #if 0 514 | void WiznetTCP::dump_buffer(uint8_t *buffer, uint16_t length) 515 | { 516 | uint8_t i, c; 517 | 518 | Serial.print(F("Dump of ")); 519 | Serial.print(length); 520 | Serial.println(F(" bytes")); 521 | 522 | while (length) { 523 | for (i = 0; i < 8; ++i) { 524 | if (!length) 525 | break; 526 | 527 | c = *buffer++; 528 | 529 | if (c < 16) 530 | Serial.print(F("0")); 531 | 532 | Serial.print(c, HEX); 533 | Serial.print(F(" ")); 534 | --length; 535 | } 536 | 537 | Serial.print(F(" ")); 538 | 539 | for (; i < 16; ++i) { 540 | if (!length) 541 | break; 542 | 543 | c = *buffer++; 544 | 545 | if (c < 16) 546 | Serial.print(F("0")); 547 | 548 | Serial.print(c, HEX); 549 | Serial.print(F(" ")); 550 | --length; 551 | } 552 | 553 | Serial.println(); 554 | } 555 | 556 | Serial.println(); 557 | } 558 | 559 | void WiznetTCP::print_mac_address() 560 | { 561 | uint8_t m0 = read_byte(W5100_MAC_ADDRESS); 562 | uint8_t m1 = read_byte(W5100_MAC_ADDRESS+1); 563 | uint8_t m2 = read_byte(W5100_MAC_ADDRESS+2); 564 | uint8_t m3 = read_byte(W5100_MAC_ADDRESS+3); 565 | uint8_t m4 = read_byte(W5100_MAC_ADDRESS+4); 566 | uint8_t m5 = read_byte(W5100_MAC_ADDRESS+5); 567 | Serial.print(F("mac address: ")); 568 | Serial.print(m0, HEX); 569 | Serial.print(F(":")); 570 | Serial.print(m1, HEX); 571 | Serial.print(F(":")); 572 | Serial.print(m2, HEX); 573 | Serial.print(F(":")); 574 | Serial.print(m3, HEX); 575 | Serial.print(F(":")); 576 | Serial.print(m4, HEX); 577 | Serial.print(F(":")); 578 | Serial.print(m5, HEX); 579 | Serial.println(); 580 | } 581 | #endif 582 | 583 | void WiznetTCP::open() 584 | { 585 | write_byte(SOCKET_ZERO + SOCKET_MODE, SOCKET_TCP); 586 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_OPEN); 587 | } 588 | 589 | bool WiznetTCP::listen() 590 | { 591 | if (get_socket_status() == SOCK_INIT) 592 | { 593 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_LISTEN); 594 | 595 | // wait for command to be processed 596 | while (read_byte(SOCKET_ZERO + SOCKET_COMMAND)); 597 | 598 | if (get_socket_status() == SOCK_LISTEN) 599 | return true; 600 | 601 | close(); 602 | } 603 | 604 | return false; 605 | } 606 | 607 | bool WiznetTCP::connect(uint8_t n0, uint8_t n1, uint8_t n2, uint8_t n3, uint16_t port) 608 | { 609 | if (get_socket_status() == SOCK_INIT) 610 | { 611 | set_ip(n0, n1, n2, n3); 612 | set_port(port); 613 | 614 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_CONNECT); 615 | 616 | // wait for command to be processed 617 | while (read_byte(SOCKET_ZERO + SOCKET_COMMAND)); 618 | return true; 619 | } 620 | 621 | return false; 622 | } 623 | 624 | void WiznetTCP::close() 625 | { 626 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_CLOSE); 627 | 628 | // wait for command to be processed 629 | while (read_byte(SOCKET_ZERO + SOCKET_COMMAND)); 630 | 631 | // clear interrupts 632 | write_byte(SOCKET_ZERO + SOCKET_INTERRUPT, 0xFF); 633 | } 634 | 635 | void WiznetTCP::disconnect() 636 | { 637 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_DISCONNECT); 638 | 639 | // wait for command to be processed 640 | while (read_byte(SOCKET_ZERO + SOCKET_COMMAND)); 641 | 642 | // clear interrupts 643 | write_byte(SOCKET_ZERO + SOCKET_INTERRUPT, 0xFF); 644 | } 645 | 646 | void WiznetTCP::set_ip(uint32_t ip) 647 | { 648 | // ip is little endian but W5100 expects big endian 649 | write32(SOCKET_ZERO + SOCKET_IP_ADDRESS, 650 | (ip>>24) & 255, (ip>>16) & 255, (ip>>8) & 255, ip & 255); 651 | } 652 | 653 | // use ip(192, 43, 244, 18) for "192.43.244.18" 654 | void WiznetTCP::set_ip(uint8_t n0, uint8_t n1, uint8_t n2, uint8_t n3) 655 | { 656 | write32(SOCKET_ZERO + SOCKET_IP_ADDRESS, n0, n1, n2, n3); 657 | } 658 | 659 | void WiznetTCP::get_ip(uint8_t *n0, uint8_t *n1, uint8_t *n2, uint8_t *n3) 660 | { 661 | *n0 = read_byte(SOCKET_ZERO + SOCKET_IP_ADDRESS); 662 | *n1 = read_byte(SOCKET_ZERO + SOCKET_IP_ADDRESS + 1); 663 | *n2 = read_byte(SOCKET_ZERO + SOCKET_IP_ADDRESS + 2); 664 | *n3 = read_byte(SOCKET_ZERO + SOCKET_IP_ADDRESS + 3); 665 | } 666 | 667 | void WiznetTCP::get_local_ip(uint8_t *n0, uint8_t *n1, uint8_t *n2, uint8_t *n3) 668 | { 669 | *n0 = read_byte(W5100_LOCAL_IP_ADDRESS); 670 | *n1 = read_byte(W5100_LOCAL_IP_ADDRESS + 1); 671 | *n2 = read_byte(W5100_LOCAL_IP_ADDRESS + 2); 672 | *n3 = read_byte(W5100_LOCAL_IP_ADDRESS + 3); 673 | } 674 | 675 | void WiznetTCP::set_port(uint16_t port) 676 | { 677 | write_word(SOCKET_ZERO + SOCKET_DEST_PORT, port); 678 | } 679 | 680 | // does this get the local or remote port? 681 | // W5100 spec is ambiguous with use of Source and Destination 682 | // for IP address, socket dest IP is the IP of the remote device 683 | // but for ports, the question is how this varies from TX to RX 684 | uint16_t WiznetTCP::get_port() 685 | { 686 | return read_word(SOCKET_ZERO + SOCKET_DEST_PORT); 687 | } 688 | 689 | uint16_t WiznetTCP::get_local_port() 690 | { 691 | return read_word(SOCKET_ZERO+SOCKET_SRC_PORT);; 692 | } 693 | 694 | uint8_t WiznetTCP::get_socket_status() 695 | { 696 | return read_byte(SOCKET_ZERO + SOCKET_STATUS); 697 | } 698 | 699 | uint16_t WiznetTCP::send_available() 700 | { 701 | return read_word(SOCKET_ZERO + SOCKET_TX_FREE_SIZE); 702 | } 703 | 704 | uint16_t WiznetTCP::receive_available() 705 | { 706 | return read_word(SOCKET_ZERO + SOCKET_RX_RECV_SIZE); 707 | } 708 | 709 | // wait for data until ms milliseconds then return 0 710 | uint16_t WiznetTCP::receive_available(uint16_t ms) 711 | { 712 | unsigned long last_time = 0, now = millis(); 713 | 714 | while (ms) { 715 | uint16_t size = read_word(SOCKET_ZERO + SOCKET_RX_RECV_SIZE); 716 | 717 | if (size) 718 | return size; 719 | 720 | now = millis(); 721 | 722 | if (now != last_time) 723 | --ms; 724 | 725 | last_time = now; 726 | } 727 | 728 | return 0; 729 | } 730 | 731 | uint16_t WiznetTCP::flush_receive() 732 | { 733 | return skip(read_word(SOCKET_ZERO + SOCKET_RX_RECV_SIZE)); 734 | } 735 | 736 | uint16_t WiznetTCP::skip(uint16_t length) 737 | { 738 | uint16_t size = read_word(SOCKET_ZERO + SOCKET_RX_RECV_SIZE); 739 | 740 | if (size) { 741 | uint16_t ptr = read_word(SOCKET_ZERO + SOCKET_RX_READ_PNTR); 742 | 743 | if (size > length) 744 | size = length; 745 | 746 | // update socket's read pointer 747 | write_word(SOCKET_ZERO + SOCKET_RX_READ_PNTR, ptr+size); 748 | 749 | // re-enable receiving on this socket 750 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_RECEIVE); 751 | 752 | // wait for command to be processed 753 | while (read_byte(SOCKET_ZERO + SOCKET_COMMAND)); 754 | } 755 | return size; 756 | } 757 | 758 | uint16_t WiznetTCP::peek(uint16_t offset, char *buffer, uint16_t length) 759 | { 760 | uint16_t size = read_word(SOCKET_ZERO + SOCKET_RX_RECV_SIZE); 761 | 762 | if (size > offset) { 763 | uint16_t ptr = offset + read_word(SOCKET_ZERO + SOCKET_RX_READ_PNTR); 764 | 765 | if (size > length) 766 | size = length; 767 | 768 | get_data(ptr, (uint8_t *)buffer, size); 769 | } 770 | else 771 | size = 0; 772 | 773 | return size; 774 | } 775 | 776 | // returns what's currently available 777 | uint16_t WiznetTCP::receive(char *buffer, uint16_t length) 778 | { 779 | uint16_t size = read_word(SOCKET_ZERO + SOCKET_RX_RECV_SIZE); 780 | 781 | if (size) { 782 | uint16_t ptr = read_word(SOCKET_ZERO + SOCKET_RX_READ_PNTR); 783 | 784 | if (size > length) 785 | size = length; 786 | 787 | get_data(ptr, (uint8_t *)buffer, size); 788 | 789 | // update socket's read pointer 790 | write_word(SOCKET_ZERO + SOCKET_RX_READ_PNTR, ptr+size); 791 | 792 | // re-enable receiving on this socket 793 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_RECEIVE); 794 | 795 | // wait for command to be processed 796 | while (read_byte(SOCKET_ZERO + SOCKET_COMMAND)); 797 | } 798 | return size; 799 | } 800 | 801 | uint16_t WiznetTCP::receive(char *buffer, uint16_t length, uint32_t *ip, uint16_t *port) 802 | { 803 | uint16_t size = read_word(SOCKET_ZERO + SOCKET_RX_RECV_SIZE); 804 | 805 | if (size) { 806 | uint8_t header[8]; 807 | uint16_t ptr = read_word(SOCKET_ZERO + SOCKET_RX_READ_PNTR); 808 | get_data(ptr, header, 8); // IP4, port, length 809 | 810 | // ip is little endian but W5100 provides big endian 811 | *ip = header[0]; 812 | *ip <<= 8; *ip |= header[1]; 813 | *ip <<= 8; *ip |= header[2]; 814 | *ip <<= 8; *ip |= header[3]; 815 | *port = 256 * header[4] + header[5]; 816 | size = 256 * header[6] + header[7]; 817 | 818 | if (size > length) 819 | size = length; 820 | 821 | ptr += 8; // past header 822 | get_data(ptr, (uint8_t *)buffer, size); 823 | 824 | // update socket's read pointer 825 | write_word(SOCKET_ZERO + SOCKET_RX_READ_PNTR, ptr+size); 826 | 827 | // re-enable receiving on this socket 828 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_RECEIVE); 829 | 830 | // wait for command to be processed 831 | while (read_byte(SOCKET_ZERO + SOCKET_COMMAND)); 832 | } 833 | return size; 834 | } 835 | 836 | uint16_t WiznetTCP::send(char *buffer, uint16_t length) 837 | { 838 | uint16_t size = send_available(); 839 | 840 | // send up to the available space 841 | if (length < size) 842 | size = length; 843 | 844 | uint8_t mode = read_byte(SOCKET_ZERO + SOCKET_MODE); 845 | uint16_t ptr = read_word(SOCKET_ZERO + SOCKET_TX_WRITE_PNTR); 846 | put_data(ptr, (uint8_t *)buffer, size); 847 | 848 | // update socket's send pointer 849 | write_word(SOCKET_ZERO + SOCKET_TX_WRITE_PNTR, ptr+size); 850 | 851 | // ask W5100 to send the packet 852 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, 853 | (mode & SOCKET_MULTICAST ? SOCKET_SEND_MAC : SOCKET_SEND)); 854 | 855 | // wait for command to be processed 856 | while (read_byte(SOCKET_ZERO + SOCKET_COMMAND)); 857 | 858 | // wait for data to be sent or for a timeout 859 | while ((read_byte(SOCKET_ZERO + SOCKET_INTERRUPT) & W5100_SEND_OK) != W5100_SEND_OK); 860 | 861 | if (read_byte(SOCKET_ZERO + SOCKET_INTERRUPT) & W5100_TIMEOUT) 862 | Serial.println(F("send timeout")); 863 | 864 | // clear flags by writing high values as per W5100 datasheet 865 | write_byte(SOCKET_ZERO + SOCKET_INTERRUPT, (W5100_SEND_OK|W5100_TIMEOUT)); 866 | 867 | Serial.print(F("sent ")); 868 | Serial.print(size); 869 | Serial.println(F(" bytes")); 870 | return size; 871 | } 872 | 873 | #if 0 874 | uint16_t WiznetTCP::send_mac(char *buffer, uint16_t length) 875 | { 876 | uint16_t size = send_available(); 877 | 878 | // send up to the available space 879 | if (length < size) 880 | size = length; 881 | 882 | uint16_t ptr = read_word(SOCKET_ZERO + SOCKET_TX_WRITE_PNTR); 883 | put_data(ptr, (uint8_t *)buffer, size); 884 | 885 | // update socket's send pointer 886 | write_word(SOCKET_ZERO + SOCKET_TX_WRITE_PNTR, ptr+size); 887 | 888 | // ask W5100 to send the packet 889 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_SEND_MAC); 890 | 891 | // wait for command to be processed 892 | while (read_byte(SOCKET_ZERO + SOCKET_COMMAND)); 893 | 894 | // wait for data to be sent or for a timeout 895 | while ((read_byte(SOCKET_ZERO + SOCKET_INTERRUPT) & W5100_SEND_OK) != W5100_SEND_OK); 896 | 897 | if (read_byte(SOCKET_ZERO + SOCKET_INTERRUPT) & W5100_TIMEOUT) 898 | Serial.println(F("send timeout")); 899 | 900 | // clear flags by writing high values as per W5100 datasheet 901 | write_byte(SOCKET_ZERO + SOCKET_INTERRUPT, (W5100_SEND_OK|W5100_TIMEOUT)); 902 | 903 | Serial.print(F("sent ")); 904 | Serial.print(size); 905 | Serial.println(F(" bytes")); 906 | return size; 907 | } 908 | #endif 909 | 910 | // assumes buffer is large enough for at least size bytes 911 | void WiznetTCP::get_data(uint16_t ptr, uint8_t *buffer, uint16_t size) 912 | { 913 | uint16_t mask = ptr & RX_BUFFER_MASK; 914 | uint16_t i, src = BASE_RX_BUFFER + mask; 915 | 916 | // will data extend past top of RX buffer? 917 | 918 | if (mask + size > SOCKET_SIZE) { 919 | // data is not contiguous 920 | 921 | uint16_t len = SOCKET_SIZE - mask; 922 | 923 | for (i = 0; i < len; ++i, ++src) 924 | buffer[i] = read_byte(src); 925 | 926 | for (src = BASE_RX_BUFFER; i < size; ++i, ++src) 927 | buffer[i] = read_byte(src); 928 | 929 | } else { 930 | // data is contiguous 931 | 932 | for (i = 0; i < size; ++i, ++src) 933 | buffer[i] = read_byte(src); 934 | } 935 | } 936 | 937 | // assumes that socket transmit buffer has at least size bytes free 938 | void WiznetTCP::put_data(uint16_t ptr, uint8_t *buffer, uint16_t size) 939 | { 940 | uint16_t mask = ptr & TX_BUFFER_MASK; 941 | uint16_t i, dst = BASE_TX_BUFFER + mask; 942 | 943 | // will data extend past top of TX buffer? 944 | 945 | if (size + mask > SOCKET_SIZE) { 946 | // data is not contiguous 947 | 948 | uint16_t len = SOCKET_SIZE - mask; 949 | 950 | for (i = 0; i < len; ++i, ++dst) 951 | write_byte(dst, buffer[i]); 952 | 953 | for (dst = BASE_TX_BUFFER; i < size; ++i, ++dst) 954 | write_byte(dst, buffer[i]); 955 | 956 | } else { 957 | // data is contiguous 958 | 959 | for (i = 0; i < size; ++i, ++dst) 960 | write_byte(dst, buffer[i]); 961 | } 962 | } 963 | 964 | // SPI code 965 | 966 | /* 967 | PB2 slave select (SS), active low from master 968 | PB3 master output, slave input (MOSI) 969 | PB4 master input, slave output (MISO) 970 | PB5 serial clock generated by master (SCK) 971 | PC4 slave select for SD Card on Ethernet Shield 972 | */ 973 | 974 | void WiznetTCP::spi_init() 975 | { 976 | // configure SS, MOSI and SCK as output pins 977 | DDRB = _BV(2) | _BV(3) | _BV(5); 978 | PORTB |= _BV(2); // set W5100 slave select high 979 | PORTC |= _BV(4); // activate internal pull up to disable SD Card 980 | 981 | // no interrupt, enable SPI, MCU as master, SPI mode 0, 4 Mhz clock 982 | SPCR = (1<> 8); 1013 | transfer(addr & 0xFF); 1014 | data = transfer(0); 1015 | resetSS(); 1016 | return data; 1017 | } 1018 | 1019 | static void write32(uint16_t addr, uint8_t n0, uint8_t n1, uint8_t n2, uint8_t n3) 1020 | { 1021 | write_byte(addr++, n0); 1022 | write_byte(addr++, n1); 1023 | write_byte(addr++, n2); 1024 | write_byte(addr, n3); 1025 | } 1026 | 1027 | static void write32(uint16_t addr, uint8_t *p) 1028 | { 1029 | for (uint8_t i = 0; i < 4; ++i) 1030 | write_byte(addr++, *p++); 1031 | } 1032 | 1033 | static void write48(uint16_t addr, uint8_t n0, uint8_t n1, uint8_t n2, 1034 | uint8_t n3, uint8_t n4, uint8_t n5) 1035 | { 1036 | write_byte(addr++, n0); 1037 | write_byte(addr++, n1); 1038 | write_byte(addr++, n2); 1039 | write_byte(addr++, n3); 1040 | write_byte(addr++, n4); 1041 | write_byte(addr, n5); 1042 | } 1043 | 1044 | static void write48(uint16_t addr, uint8_t *p) 1045 | { 1046 | for (uint8_t i = 0; i < 6; ++i) 1047 | write_byte(addr++, *p++); 1048 | } 1049 | 1050 | // write one bye to SPI 1051 | inline static void write_byte(uint16_t addr, uint8_t data) 1052 | { 1053 | setSS(); 1054 | transfer(0xF0); 1055 | transfer((addr >> 8) & 255); 1056 | transfer(addr & 255); 1057 | transfer(data); 1058 | resetSS(); 1059 | } 1060 | 1061 | static uint16_t read_word(uint16_t addr) 1062 | { 1063 | uint16_t word = read_byte(addr) << 8; 1064 | word |= read_byte(addr+1); 1065 | return word; 1066 | } 1067 | 1068 | static void write_word(uint16_t addr, uint16_t word) 1069 | { 1070 | write_byte(addr, word >> 8); 1071 | write_byte(addr+1, word & 255); 1072 | } 1073 | 1074 | // DHCP client - ~3KB code including the debug statements 1075 | // See http://tools.ietf.org/html/rfc2132 for details 1076 | 1077 | uint8_t WiznetTCP::run_DHCP_client(void) 1078 | { 1079 | #define HOST_NAME "DAVES-ARDUINO" // host name - should be loaded from EEPROM 1080 | // *** FIX ME with EEPROM config module 1081 | uint8_t SRC_MAC_ADDR[6]; // local MAC address 1082 | uint8_t GET_SN_MASK[4]; // subnet mask received from the DHCP server 1083 | uint8_t GET_GW_IP[4]; // gateway ip address received from the DHCP server 1084 | uint8_t GET_SIP[4] = {0, 0, 0, 0}; // local ip address received from the DHCP server 1085 | uint8_t DHCP_SIP[4] = {0, 0, 0, 0}; // DHCP server ip address is discovered 1086 | 1087 | uint32_t DHCP_XID = 0x75269875; 1088 | RIP_MSG* pRIPMSG; // pointer for the DHCP message 1089 | RIP_MSG RIPMSG; // structure definition 1090 | 1091 | uint16_t recv_msg_size; 1092 | uint8_t *recv_msg_end; 1093 | uint8_t *current_option; 1094 | uint8_t option_length; 1095 | uint8_t host_name_length; 1096 | 1097 | uint16_t i=0; 1098 | uint16_t port; 1099 | uint32_t ip; 1100 | 1101 | Serial.println(F("running DHCP client")); 1102 | 1103 | // set local IP address to zero 1104 | write32(W5100_LOCAL_IP_ADDRESS, 0, 0, 0, 0); 1105 | 1106 | 1107 | // MAC address - fixed for now but should be loaded from EEPROM 1108 | SRC_MAC_ADDR[0] = 0x61, 1109 | SRC_MAC_ADDR[1] = 0xf8, 1110 | SRC_MAC_ADDR[2] = 0x1d, 1111 | SRC_MAC_ADDR[3] = 0xbc, 1112 | SRC_MAC_ADDR[4] = 0xf4, 1113 | SRC_MAC_ADDR[5] = 0x2f; 1114 | write48(W5100_MAC_ADDRESS, SRC_MAC_ADDR); 1115 | 1116 | // set up UDP socket for DHCP client port 1117 | write_word(SOCKET_ZERO + SOCKET_SRC_PORT, DHCP_CLIENT_PORT); 1118 | write_byte(SOCKET_ZERO + SOCKET_MODE, SOCKET_UDP); 1119 | write_byte(SOCKET_ZERO + SOCKET_COMMAND, SOCKET_OPEN); 1120 | 1121 | // send DHCPDISCOVER 1122 | 1123 | pRIPMSG = &RIPMSG; 1124 | 1125 | memset((void*)pRIPMSG, 0, sizeof(RIP_MSG)); // zero RIPMSG contents 1126 | 1127 | pRIPMSG->op = DHCP_BOOTREQUEST; 1128 | pRIPMSG->htype = DHCP_HTYPE10MB; 1129 | pRIPMSG->hlen = DHCP_HLENETHERNET; 1130 | pRIPMSG->hops = DHCP_HOPS; 1131 | pRIPMSG->xid = DHCP_XID; 1132 | pRIPMSG->secs = DHCP_SECS; 1133 | pRIPMSG->flags = DHCP_FLAGSBROADCAST; 1134 | pRIPMSG->chaddr[0] = SRC_MAC_ADDR[0]; 1135 | pRIPMSG->chaddr[1] = SRC_MAC_ADDR[1]; 1136 | pRIPMSG->chaddr[2] = SRC_MAC_ADDR[2]; 1137 | pRIPMSG->chaddr[3] = SRC_MAC_ADDR[3]; 1138 | pRIPMSG->chaddr[4] = SRC_MAC_ADDR[4]; 1139 | pRIPMSG->chaddr[5] = SRC_MAC_ADDR[5]; 1140 | 1141 | // MAGIC_COOKIE IN NETWORK ORDER 1142 | pRIPMSG->options[i++] = (char)((MAGIC_COOKIE >> 24)& 0xFF); 1143 | pRIPMSG->options[i++] = (char)((MAGIC_COOKIE >> 16)& 0xFF); 1144 | pRIPMSG->options[i++] = (char)((MAGIC_COOKIE >> 8)& 0xFF); 1145 | pRIPMSG->options[i++] = (char)(MAGIC_COOKIE& 0xFF); 1146 | 1147 | // Option Request Param. 1148 | pRIPMSG->options[i++] = dhcpMessageType; 1149 | pRIPMSG->options[i++] = 0x01; 1150 | pRIPMSG->options[i++] = DHCP_DISCOVER; 1151 | 1152 | // Client identifier 1153 | pRIPMSG->options[i++] = dhcpClientIdentifier; 1154 | pRIPMSG->options[i++] = 0x07; 1155 | pRIPMSG->options[i++] = 0x01; // Ethernet (10Mb) 1156 | pRIPMSG->options[i++] = SRC_MAC_ADDR[0]; 1157 | pRIPMSG->options[i++] = SRC_MAC_ADDR[1]; 1158 | pRIPMSG->options[i++] = SRC_MAC_ADDR[2]; 1159 | pRIPMSG->options[i++] = SRC_MAC_ADDR[3]; 1160 | pRIPMSG->options[i++] = SRC_MAC_ADDR[4]; 1161 | pRIPMSG->options[i++] = SRC_MAC_ADDR[5]; 1162 | 1163 | // host name 1164 | pRIPMSG->options[i++] = hostName; 1165 | pRIPMSG->options[i++] = host_name_length = strlen(HOST_NAME); 1166 | strcpy((char*)&(pRIPMSG->options[i]),HOST_NAME); 1167 | i+=host_name_length; 1168 | 1169 | // IP Address Lease Time request 1170 | pRIPMSG->options[i++] = dhcpIPaddrLeaseTime; 1171 | pRIPMSG->options[i++] = 4; 1172 | pRIPMSG->options[i++] = 0xFF; 1173 | pRIPMSG->options[i++] = 0xFF; 1174 | pRIPMSG->options[i++] = 0xFF; 1175 | pRIPMSG->options[i++] = 0xFF; 1176 | 1177 | // Parameter Request List 1178 | pRIPMSG->options[i++] = dhcpParamRequest; 1179 | pRIPMSG->options[i++] = 0x02; 1180 | pRIPMSG->options[i++] = subnetMask; 1181 | pRIPMSG->options[i++] = routersOnSubnet; 1182 | pRIPMSG->options[i++] = endOption; 1183 | 1184 | // send broadcasting packet 1185 | Serial.println(F("send DHCP DISCOVER")); 1186 | set_ip(255, 255, 255, 255); 1187 | set_port(DHCP_SERVER_PORT); 1188 | 1189 | for (i = MAX_DHCP_RETRY; i; --i) { 1190 | Serial.print(F("socket status: 0x")); 1191 | Serial.println(get_socket_status(), HEX); 1192 | send((char *)pRIPMSG, sizeof(RIP_MSG)); 1193 | 1194 | if (receive_available(DHCP_WAIT_TIME)) { 1195 | Serial.println(F("got DHCP OFFER")); 1196 | break; 1197 | } 1198 | } 1199 | 1200 | if (i == 0) { 1201 | Serial.println(F("failed to get DHCP OFFER")); 1202 | return 0; 1203 | } 1204 | 1205 | recv_msg_size = receive((char *)pRIPMSG, sizeof(RIPMSG), &ip, &port); 1206 | 1207 | if (port==DHCP_SERVER_PORT && 1208 | !memcmp(RIPMSG.chaddr, SRC_MAC_ADDR, 6) && 1209 | RIPMSG.xid == DHCP_XID) 1210 | { 1211 | // Check options 1212 | recv_msg_end = (uint8_t *)((uint16_t)&(RIPMSG.op) + recv_msg_size); 1213 | current_option = (uint8_t *)((uint16_t)&(RIPMSG.op) + 240); 1214 | 1215 | while(current_option < recv_msg_end) 1216 | { 1217 | switch (*(current_option++)) 1218 | { 1219 | case padOption: 1220 | break; 1221 | 1222 | case endOption: 1223 | break; 1224 | 1225 | case dhcpMessageType: 1226 | current_option++; 1227 | 1228 | if ((*current_option++) != DHCP_OFFER) 1229 | return 0; 1230 | 1231 | break; 1232 | 1233 | case subnetMask: 1234 | option_length = *current_option++; 1235 | memcpy(GET_SN_MASK, current_option, 4); 1236 | current_option += option_length; 1237 | break; 1238 | 1239 | case routersOnSubnet: 1240 | option_length = *current_option++; 1241 | memcpy(GET_GW_IP, current_option, 4); 1242 | current_option += option_length; 1243 | break; 1244 | 1245 | case dhcpServerIdentifier: 1246 | current_option++; 1247 | memcpy(DHCP_SIP, current_option, 4); 1248 | current_option += 4; 1249 | break; 1250 | 1251 | default: 1252 | option_length = *current_option++; 1253 | current_option += option_length; 1254 | break; 1255 | } 1256 | } 1257 | 1258 | memcpy(GET_SIP, (RIPMSG.yiaddr), 4); 1259 | 1260 | } 1261 | else { 1262 | close(); 1263 | return 0; 1264 | } 1265 | 1266 | // send DHCPREQUEST 1267 | 1268 | memset((void*)pRIPMSG, 0, sizeof(RIP_MSG)); 1269 | 1270 | pRIPMSG->op = DHCP_BOOTREQUEST; 1271 | pRIPMSG->htype = DHCP_HTYPE10MB; 1272 | pRIPMSG->hlen = DHCP_HLENETHERNET; 1273 | pRIPMSG->hops = DHCP_HOPS; 1274 | pRIPMSG->xid = DHCP_XID; 1275 | pRIPMSG->secs = DHCP_SECS; 1276 | pRIPMSG->flags = DHCP_FLAGSBROADCAST; 1277 | pRIPMSG->chaddr[0] = SRC_MAC_ADDR[0]; 1278 | pRIPMSG->chaddr[1] = SRC_MAC_ADDR[1]; 1279 | pRIPMSG->chaddr[2] = SRC_MAC_ADDR[2]; 1280 | pRIPMSG->chaddr[3] = SRC_MAC_ADDR[3]; 1281 | pRIPMSG->chaddr[4] = SRC_MAC_ADDR[4]; 1282 | pRIPMSG->chaddr[5] = SRC_MAC_ADDR[5]; 1283 | 1284 | i=0; 1285 | 1286 | // MAGIC_COOKIE 1287 | pRIPMSG->options[i++] = (char)((MAGIC_COOKIE >> 24)& 0xFF); 1288 | pRIPMSG->options[i++] = (char)((MAGIC_COOKIE >> 16)& 0xFF); 1289 | pRIPMSG->options[i++] = (char)((MAGIC_COOKIE >> 8)& 0xFF); 1290 | pRIPMSG->options[i++] = (char)(MAGIC_COOKIE& 0xFF); 1291 | 1292 | // option request param 1293 | pRIPMSG->options[i++] = dhcpMessageType; 1294 | pRIPMSG->options[i++] = 0x01; 1295 | pRIPMSG->options[i++] = DHCP_REQUEST; 1296 | 1297 | // client identifier 1298 | pRIPMSG->options[i++] = dhcpClientIdentifier; 1299 | pRIPMSG->options[i++] = 0x07; 1300 | pRIPMSG->options[i++] = 0x01; 1301 | pRIPMSG->options[i++] = SRC_MAC_ADDR[0]; 1302 | pRIPMSG->options[i++] = SRC_MAC_ADDR[1]; 1303 | pRIPMSG->options[i++] = SRC_MAC_ADDR[2]; 1304 | pRIPMSG->options[i++] = SRC_MAC_ADDR[3]; 1305 | pRIPMSG->options[i++] = SRC_MAC_ADDR[4]; 1306 | pRIPMSG->options[i++] = SRC_MAC_ADDR[5]; 1307 | 1308 | // request IP address 1309 | pRIPMSG->options[i++] = dhcpRequestedIPaddr; 1310 | pRIPMSG->options[i++] = 0x04; 1311 | pRIPMSG->options[i++] = GET_SIP[0]; 1312 | pRIPMSG->options[i++] = GET_SIP[1]; 1313 | pRIPMSG->options[i++] = GET_SIP[2]; 1314 | pRIPMSG->options[i++] = GET_SIP[3]; 1315 | 1316 | // server identifier 1317 | pRIPMSG->options[i++] = dhcpServerIdentifier; 1318 | pRIPMSG->options[i++] = 0x04; 1319 | pRIPMSG->options[i++] = DHCP_SIP[0]; 1320 | pRIPMSG->options[i++] = DHCP_SIP[1]; 1321 | pRIPMSG->options[i++] = DHCP_SIP[2]; 1322 | pRIPMSG->options[i++] = DHCP_SIP[3]; 1323 | 1324 | // host name 1325 | pRIPMSG->options[i++] = hostName; 1326 | pRIPMSG->options[i++] = host_name_length = strlen(HOST_NAME); 1327 | strcpy((char*)&(pRIPMSG->options[i]),HOST_NAME); 1328 | i+=host_name_length; 1329 | 1330 | // parameter request list 1331 | pRIPMSG->options[i++] = dhcpParamRequest; 1332 | pRIPMSG->options[i++] = 0x02; 1333 | pRIPMSG->options[i++] = subnetMask; 1334 | pRIPMSG->options[i++] = routersOnSubnet; 1335 | pRIPMSG->options[i++] = endOption; 1336 | 1337 | // send broadcasting packet 1338 | Serial.println(F("send DHCP REQUEST")); 1339 | set_ip(255, 255, 255, 255); 1340 | set_port(DHCP_SERVER_PORT); 1341 | 1342 | for (i = MAX_DHCP_RETRY; i; --i) { 1343 | send((char *)pRIPMSG, sizeof(RIP_MSG)); 1344 | 1345 | if (receive_available(DHCP_WAIT_TIME)) { 1346 | Serial.println(F("got DHCP ACK")); 1347 | break; 1348 | } 1349 | } 1350 | 1351 | if (i == 0) { 1352 | Serial.println(F("failed to get DHCP ACK")); 1353 | return 0; 1354 | } 1355 | 1356 | recv_msg_size = receive((char *)pRIPMSG, sizeof(RIPMSG), &ip, &port); 1357 | 1358 | if (port==DHCP_SERVER_PORT && 1359 | !memcmp(RIPMSG.chaddr, SRC_MAC_ADDR, 6) && 1360 | RIPMSG.xid == DHCP_XID) { 1361 | // Check options 1362 | recv_msg_end = (uint8_t *)((uint16_t)&(RIPMSG.op) + recv_msg_size); 1363 | current_option = (uint8_t *)((uint16_t)&(RIPMSG.op) + 240); 1364 | 1365 | while(current_option < recv_msg_end) { 1366 | switch (*(current_option++)) { 1367 | case padOption: 1368 | break; 1369 | 1370 | case endOption: 1371 | break; 1372 | 1373 | case dhcpMessageType: 1374 | current_option++; 1375 | if ((*current_option++) != DHCP_ACK) 1376 | return 0; 1377 | 1378 | break; 1379 | 1380 | case subnetMask: 1381 | option_length = *current_option++; 1382 | memcpy(GET_SN_MASK, current_option, 4); 1383 | current_option += option_length; 1384 | break; 1385 | 1386 | case routersOnSubnet: 1387 | option_length = *current_option++; 1388 | memcpy(GET_GW_IP, current_option, 4); 1389 | current_option += option_length; 1390 | break; 1391 | 1392 | default: 1393 | option_length = *current_option++; 1394 | current_option += option_length; 1395 | break; 1396 | } 1397 | } 1398 | 1399 | memcpy(GET_SIP, (RIPMSG.yiaddr), 4); 1400 | memcpy(DHCP_SIP, (RIPMSG.siaddr), 4); 1401 | } 1402 | else { 1403 | close(); 1404 | return 0; 1405 | } 1406 | 1407 | // set network parameters 1408 | write32(W5100_GATEWAY, GET_GW_IP); 1409 | write32(W5100_SUBNET_MASK, GET_SN_MASK); 1410 | write32(W5100_LOCAL_IP_ADDRESS, RIPMSG.yiaddr); 1411 | 1412 | // close the socket used to talk to DHCP server 1413 | close(); 1414 | 1415 | Serial.println(F("Got IP address via DHCP")); 1416 | return 1; 1417 | } 1418 | --------------------------------------------------------------------------------