Example of AWS IoT connection and Web Dashboard thru STM32 Nucleo evaluation board and mbed OS.
Dependencies: X_NUCLEO_IKS01A1 mbed FP MQTTPacket DnsQuery ATParser
Introduction
The demo is aimed to STM32 Nucleo board with WiFi and sensors expansions. The board is a "thing" for the AWS IoT service. It updates IoT service shadow with sensors data every second and checks subscription messages.
Hardware Configuration
- NUCLEO-F401RE - Nucleo Development Board
- X-NUCLEO-IDW01M1 - Wi-Fi expansion board for STM32 Nucleo
- X-NUCLEO-IKS01A1 - Motion MEMS and environmental sensor expansion board for STM32 Nucleo
Software Configuration
- Import this Project to mbed online compiler
- Find the next part of code in main.cpp file ...
WiFi network credential
#include "mbed.h" // WiFi network credential #define SSID "" // Network must be visible otherwise it can't connect #define PASSW "" #error "Wifi SSID & password empty"
- ... And set it to your Network Name and Password. Do not forget to remove "#error" pragma line.
Information
Nucleo WiFi module is not the same as your smartphone or laptope - it is based on demo board. To avoid connection problems:
- Place Nucleo as close to WiFi hot spot as possible. Or...
- Turn on mobile hot spot in your laptop as close to the device as possible.
- Make sure that hot spot permits 2.4 GHz band communications
- Setup BackEnd and store certificates using this backend setup instruction
- Find AWS_IOT_MQTT_HOST define and change it to HTTPS point mentioned in your AWS IoT thing properties named "interact"
#define AWS_IOT_MQTT_HOST "xxxxxxxxxx.iot.us-east-1.amazonaws.com" //Use your own host.
- Find the certificate defines clientCRT and clientKey in main.cpp file and change it to ones provided by Amazon.
/********************************************************************************************** *********************************************************************************************** Device Identity Certificates: Modify for your AWS IoT Thing *********************************************************************************************** ***********************************************************************************************/ /**************************************** (somecode)-certificate.pem.crt - Amazon signed PEM sertificate. *****************************************/ //This Client cert is example. Use own instead. const uint8_t clientCRT[] = "\ -----BEGIN CERTIFICATE-----\n\ MIIDBjCCAe6gAwIBAgIUVph856omeIxW3UPioq+UrX1DbwowDQYJKoZIhvcNAQEL\ BQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g\ SW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTE3MDUyNTExNTEy\ OVoXDTQ5MTIzMTIzNTk1OVowgZUxCzAJBgNVBAYTAkJZMQ4wDAYDVQQIDAVNaW5z\ azEOMAwGA1UEBwwFTWluc2sxFzAVBgNVBAoMDktsaWthLVRlY2ggTExDMRcwFQYD\ VQQLDA5LbGlrYS1UZWNoIExMQzEMMAoGA1UEAwwDUm5EMSYwJAYJKoZIhvcNAQkB\ FhdtdmF0YWxldUBrbGlrYS10ZWNoLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEH\ A0IABCJgOQJmoTBJVPfli9Hm/JVixaxkY5rtlgrYO3hSl633A2hg0P/ue0wXDbF3\ aQ0X57IRFE4k4FEbr3UXjT/IczKjYDBeMB8GA1UdIwQYMBaAFK3YzTUPlYB2Li75\ i/z8rEogr1d6MB0GA1UdDgQWBBT18HXBaXFJuAR/0SwegnxJ+pyJ6TAMBgNVHRMB\ Af8EAjAAMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAb0Ux1aH5\ RLxjrfGqXN6rPVqh8QQRS+AyBfzmaQN8HaPZMkX5WxXLvcn0A3uWlwQxPPkcZ4zf\ 51GHtFFQWB4YZ8dx8mUQ0v/j7onHjCJgZ8iDgwOyKMGtnsDZWCakQw+a6cj+NrMZ\ tzhjwCzEEP6ePcbXwErI5OOzLuWns2L/JEr2wWNkokgRuS8ewr/SQ9OLWIWa2rFM\ ahPNTb3y/qBeWdjeJmhI+TOxdqIpsF8roWP25zwo/zkzCHCjXFBrL+0CA4MpxIl9\ x02i7aAhlJ6ys80lDxdeWeeQJXRKkGknP8mcmKn3iEqqJ5s1dQePj2b5d3ldatya\ wsxQBqqZXzIWEw==\ \n\ -----END CERTIFICATE-----\n"; /********************************************************************************************** *********************************************************************************************** Private Key: Modify for your AWS IoT Thing *********************************************************************************************** ***********************************************************************************************/ /********************************************************************8**************************************** nucleo.key.pem - client key generated according to readme. **************************************************************************************************************/ //This Client Key is example. Use own instead. const uint8_t clientKey[] ="\ -----BEGIN EC PARAMETERS-----\n\ BggqhkjOPQMBBw==\ -----END EC PARAMETERS-----\n\ -----BEGIN EC PRIVATE KEY-----\n\ MHcCAQEEIHPRfWSC8/k/BsqDWKuP15dXsI9fGwpkTIsLZe6mIrAAoAoGCCqGSM49\ AwEHoUQDQgAEImA5AmahMElU9+WL0eb8lWLFrGRjmu2WCtg7eFKXrfcDaGDQ/+57\ TBcNsXdpDRfnshEUTiTgURuvdReNP8hzMg==\ -----END EC PRIVATE KEY-----\n";
Build and Check
- Plugin your board to USB of your PC. USB Disk Drive and USB COM Port should appear in your system.
- Open any Serial Console, connect it to your USB Serial Port and setup speed equal to 115200.
- Compile this Project and save .bin file to USB Disk Drive
- After board reset you should see next log in serial console:
X-NUCLEO-IDW01M1 mbed Application connecting to AP LOG: int main() L#361 Connected to WiFI. LOG: int connect(MQTT::Client<MQTTWiFi, Countdown, 350, 5> *, MQTTWiFi *) L#186 ===================================== LOG: int connect(MQTT::Client<MQTTWiFi, Countdown, 350, 5> *, MQTTWiFi *) L#187 Connecting WiFi. LOG: int connect(MQTT::Client<MQTTWiFi, Countdown, 350, 5> *, MQTTWiFi *) L#188 Nucleo IP ADDRESS: X.X.X.X LOG: int connect(MQTT::Client<MQTTWiFi, Countdown, 350, 5> *, MQTTWiFi *) L#189 Nucleo MAC ADDRESS: 00:11:22:33:44:55 LOG: int connect(MQTT::Client<MQTTWiFi, Countdown, 350, 5> *, MQTTWiFi *) L#190 Server Hostname: xxxxxxxx.iot.us-east-1.amazonaws.com port: 8883 LOG: int connect(MQTT::Client<MQTTWiFi, Countdown, 350, 5> *, MQTTWiFi *) L#191 Client ID: Nucleo LOG: int connect(MQTT::Client<MQTTWiFi, Countdown, 350, 5> *, MQTTWiFi *) L#194 ===================================== LOG: int MQTTSocket::getNTPtime(int) L#58 Success receiving time from ntp server. Tick from 1 Jan 1970 is equal to 1505399292. --->TCP Connected --->MQTT Connected --->>>MQTT subscribed to: Nucleo/test Length - 245, Publishing {"state": {"reported": {"temperature": 23.690001, "humidity": 98.190002, "pressure": 982.869141, "accelerometer": [-0.009000, 0.030000, 0.971000], "gyroscope": [0.420000, -2.660000, 1.750000], "magnetometer": [-3.600000, -7.100000, 53.300000]}}} Length - 245, Publishing {"state": {"reported": {"temperature": 23.660000, "humidity": 98.010002, "pressure": 982.770264, "accelerometer": [-0.009000, 0.030000, 0.971000], "gyroscope": [0.770000, -2.310000, 1.470000], "magnetometer": [-3.100000, -8.300000, 54.200000]}}} Length - 245, Publishing {"state": {"reported": {"temperature": 23.670000, "humidity": 98.129997, "pressure": 982.724121, "accelerometer": [-0.008000, 0.029000, 0.971000], "gyroscope": [0.630000, -2.380000, 1.400000], "magnetometer": [-3.100000, -7.900000, 53.400000]}}} Length - 245, Publishing {"state": {"reported": {"temperature": 23.690001, "humidity": 98.019997, "pressure": 982.840088, "accelerometer": [-0.009000, 0.030000, 0.972000], "gyroscope": [0.700000, -2.450000, 1.540000], "magnetometer": [-3.700000, -7.900000, 53.400000]}}} Length - 245, Publishing {"state": {"reported": {"temperature": 23.709999, "humidity": 98.040001, "pressure": 982.828613, "accelerometer": [-0.009000, 0.030000, 0.971000], "gyroscope": [0.630000, -2.520000, 1.470000], "magnetometer": [-2.900000, -7.400000, 52.400000]}}} Length - 245, Publishing {"state": {"reported": {"temperature": 23.719999, "humidity": 97.860001, "pressure": 982.917236, "accelerometer": [-0.026000, 0.103000, 0.891000], "gyroscope": [1.050000, -2.310000, 1.260000], "magnetometer": [-3.300000, -7.100000, 53.500000]}}}
Information
Device connection state might be checked by Green Led on the board. Green light means that device is connected and transferring data to cloud.
- Configure and start your dashboard using instruction and corresponding sources from github
- Use Blue button to set up markers to charts.
- Use AWS IoT console MQTT Client to test device subscription to "Nucleo/test". Just publish any message to this topic and serial port output.
- PROFIT!
Revision 0:4cdaf9b1e7d0, committed 2017-09-27
- Comitter:
- PavelSavyhin
- Date:
- Wed Sep 27 14:40:52 2017 +0300
- Child:
- 1:042ca9148926
- Commit message:
- Initial commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/FP.lib Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,1 @@ +http://mbed.org/users/sam_grove/code/FP/#3c62ba1807ac
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/MQTTAsync.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,607 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#if !defined(MQTTASYNC_H) +#define MQTTASYNC_H + +#include "FP.h" +#include "MQTTPacket.h" +#include "stdio.h" + +namespace MQTT +{ + + +enum QoS { QOS0, QOS1, QOS2 }; + + +struct Message +{ + enum QoS qos; + bool retained; + bool dup; + unsigned short id; + void *payload; + size_t payloadlen; +}; + + +class PacketId +{ +public: + PacketId(); + + int getNext(); + +private: + static const int MAX_PACKET_ID = 65535; + int next; +}; + +typedef void (*messageHandler)(Message*); + +typedef struct limits +{ + int MAX_MQTT_PACKET_SIZE; // + int MAX_MESSAGE_HANDLERS; // each subscription requires a message handler + int MAX_CONCURRENT_OPERATIONS; // each command which runs concurrently can have a result handler, when we are in multi-threaded mode + int command_timeout_ms; + + limits() + { + MAX_MQTT_PACKET_SIZE = 100; + MAX_MESSAGE_HANDLERS = 5; + MAX_CONCURRENT_OPERATIONS = 1; // 1 indicates single-threaded mode - set to >1 for multithreaded mode + command_timeout_ms = 30000; + } +} Limits; + + +/** + * @class Async + * @brief non-blocking, threaded MQTT client API + * @param Network a network class which supports send, receive + * @param Timer a timer class with the methods: + */ +template<class Network, class Timer, class Thread, class Mutex> class Async +{ + +public: + + struct Result + { + /* success or failure result data */ + Async<Network, Timer, Thread, Mutex>* client; + int rc; + }; + + typedef void (*resultHandler)(Result*); + + Async(Network* network, const Limits limits = Limits()); + + typedef struct + { + Async* client; + Network* network; + } connectionLostInfo; + + typedef int (*connectionLostHandlers)(connectionLostInfo*); + + /** Set the connection lost callback - called whenever the connection is lost and we should be connected + * @param clh - pointer to the callback function + */ + void setConnectionLostHandler(connectionLostHandlers clh) + { + connectionLostHandler.attach(clh); + } + + /** Set the default message handling callback - used for any message which does not match a subscription message handler + * @param mh - pointer to the callback function + */ + void setDefaultMessageHandler(messageHandler mh) + { + defaultMessageHandler.attach(mh); + } + + int connect(resultHandler fn, MQTTPacket_connectData* options = 0); + + template<class T> + int connect(void(T::*method)(Result *), MQTTPacket_connectData* options = 0, T *item = 0); // alternative to pass in pointer to member function + + int publish(resultHandler rh, const char* topic, Message* message); + + int subscribe(resultHandler rh, const char* topicFilter, enum QoS qos, messageHandler mh); + + int unsubscribe(resultHandler rh, const char* topicFilter); + + int disconnect(resultHandler rh); + +private: + + void run(void const *argument); + int cycle(int timeout); + int waitfor(int packet_type, Timer& atimer); + int keepalive(); + int findFreeOperation(); + + int decodePacket(int* value, int timeout); + int readPacket(int timeout); + int sendPacket(int length, int timeout); + int deliverMessage(MQTTString* topic, Message* message); + + Thread* thread; + Network* ipstack; + + Limits limits; + + char* buf; + char* readbuf; + + Timer ping_timer, connect_timer; + unsigned int keepAliveInterval; + bool ping_outstanding; + + PacketId packetid; + + typedef FP<void, Result*> resultHandlerFP; + resultHandlerFP connectHandler; + + typedef FP<void, Message*> messageHandlerFP; + struct MessageHandlers + { + const char* topic; + messageHandlerFP fp; + } *messageHandlers; // Message handlers are indexed by subscription topic + + // how many concurrent operations should we allow? Each one will require a function pointer + struct Operations + { + unsigned short id; + resultHandlerFP fp; + const char* topic; // if this is a publish, store topic name in case republishing is required + Message* message; // for publish, + Timer timer; // to check if the command has timed out + } *operations; // result handlers are indexed by packet ids + + static void threadfn(void* arg); + + messageHandlerFP defaultMessageHandler; + + typedef FP<int, connectionLostInfo*> connectionLostFP; + + connectionLostFP connectionLostHandler; + +}; + +} + + +template<class Network, class Timer, class Thread, class Mutex> void MQTT::Async<Network, Timer, Thread, Mutex>::threadfn(void* arg) +{ + ((Async<Network, Timer, Thread, Mutex>*) arg)->run(NULL); +} + + +template<class Network, class Timer, class Thread, class Mutex> MQTT::Async<Network, Timer, Thread, Mutex>::Async(Network* network, Limits limits) : limits(limits), packetid() +{ + this->thread = 0; + this->ipstack = network; + this->ping_timer = Timer(); + this->ping_outstanding = 0; + + // How to make these memory allocations portable? I was hoping to avoid the heap + buf = new char[limits.MAX_MQTT_PACKET_SIZE]; + readbuf = new char[limits.MAX_MQTT_PACKET_SIZE]; + this->operations = new struct Operations[limits.MAX_CONCURRENT_OPERATIONS]; + for (int i = 0; i < limits.MAX_CONCURRENT_OPERATIONS; ++i) + operations[i].id = 0; + this->messageHandlers = new struct MessageHandlers[limits.MAX_MESSAGE_HANDLERS]; + for (int i = 0; i < limits.MAX_MESSAGE_HANDLERS; ++i) + messageHandlers[i].topic = 0; +} + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::sendPacket(int length, int timeout) +{ + int sent = 0; + + while (sent < length) + sent += ipstack->write(&buf[sent], length, timeout); + if (sent == length) + ping_timer.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet + return sent; +} + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::decodePacket(int* value, int timeout) +{ + char c; + int multiplier = 1; + int len = 0; + const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4; + + *value = 0; + do + { + int rc = MQTTPACKET_READ_ERROR; + + if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) + { + rc = MQTTPACKET_READ_ERROR; /* bad data */ + goto exit; + } + rc = ipstack->read(&c, 1, timeout); + if (rc != 1) + goto exit; + *value += (c & 127) * multiplier; + multiplier *= 128; + } while ((c & 128) != 0); +exit: + return len; +} + + +/** + * If any read fails in this method, then we should disconnect from the network, as on reconnect + * the packets can be retried. + * @param timeout the max time to wait for the packet read to complete, in milliseconds + * @return the MQTT packet type, or -1 if none + */ +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::readPacket(int timeout) +{ + int rc = -1; + MQTTHeader header = {0}; + int len = 0; + int rem_len = 0; + + /* 1. read the header byte. This has the packet type in it */ + if (ipstack->read(readbuf, 1, timeout) != 1) + goto exit; + + len = 1; + /* 2. read the remaining length. This is variable in itself */ + decodePacket(&rem_len, timeout); + len += MQTTPacket_encode(readbuf + 1, rem_len); /* put the original remaining length back into the buffer */ + + /* 3. read the rest of the buffer using a callback to supply the rest of the data */ + if (ipstack->read(readbuf + len, rem_len, timeout) != rem_len) + goto exit; + + header.byte = readbuf[0]; + rc = header.bits.type; +exit: + return rc; +} + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::deliverMessage(MQTTString* topic, Message* message) +{ + int rc = -1; + + // we have to find the right message handler - indexed by topic + for (int i = 0; i < limits.MAX_MESSAGE_HANDLERS; ++i) + { + if (messageHandlers[i].topic != 0 && MQTTPacket_equals(topic, (char*)messageHandlers[i].topic)) + { + messageHandlers[i].fp(message); + rc = 0; + break; + } + } + + return rc; +} + + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::cycle(int timeout) +{ + /* get one piece of work off the wire and one pass through */ + + // read the socket, see what work is due + int packet_type = readPacket(timeout); + + int len, rc; + switch (packet_type) + { + case CONNACK: + if (this->thread) + { + Result res = {this, 0}; + if (MQTTDeserialize_connack(&res.rc, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1) + ; + connectHandler(&res); + connectHandler.detach(); // only invoke the callback once + } + break; + case PUBACK: + if (this->thread) + ; //call resultHandler + case SUBACK: + break; + case PUBLISH: + MQTTString topicName; + Message msg; + rc = MQTTDeserialize_publish((int*)&msg.dup, (int*)&msg.qos, (int*)&msg.retained, (int*)&msg.id, &topicName, + (char**)&msg.payload, (int*)&msg.payloadlen, readbuf, limits.MAX_MQTT_PACKET_SIZE);; + if (msg.qos == QOS0) + deliverMessage(&topicName, &msg); + break; + case PUBREC: + int type, dup, mypacketid; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1) + ; + // must lock this access against the application thread, if we are multi-threaded + len = MQTTSerialize_ack(buf, limits.MAX_MQTT_PACKET_SIZE, PUBREL, 0, mypacketid); + rc = sendPacket(len, timeout); // send the PUBREL packet + if (rc != len) + goto exit; // there was a problem + + break; + case PUBCOMP: + break; + case PINGRESP: + ping_outstanding = false; + break; + } + keepalive(); +exit: + return packet_type; +} + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::keepalive() +{ + int rc = 0; + + if (keepAliveInterval == 0) + goto exit; + + if (ping_timer.expired()) + { + if (ping_outstanding) + rc = -1; + else + { + int len = MQTTSerialize_pingreq(buf, limits.MAX_MQTT_PACKET_SIZE); + rc = sendPacket(len, 1000); // send the ping packet + if (rc != len) + rc = -1; // indicate there's a problem + else + ping_outstanding = true; + } + } + +exit: + return rc; +} + + +template<class Network, class Timer, class Thread, class Mutex> void MQTT::Async<Network, Timer, Thread, Mutex>::run(void const *argument) +{ + while (true) + cycle(ping_timer.left_ms()); +} + + +// only used in single-threaded mode where one command at a time is in process +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::waitfor(int packet_type, Timer& atimer) +{ + int rc = -1; + + do + { + if (atimer.expired()) + break; // we timed out + } + while ((rc = cycle(atimer.left_ms())) != packet_type); + + return rc; +} + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::connect(resultHandler resultHandler, MQTTPacket_connectData* options) +{ + connect_timer.countdown(limits.command_timeout_ms); + + MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer; + if (options == 0) + options = &default_options; // set default options if none were supplied + + this->keepAliveInterval = options->keepAliveInterval; + ping_timer.countdown(this->keepAliveInterval); + int len = MQTTSerialize_connect(buf, limits.MAX_MQTT_PACKET_SIZE, options); + int rc = sendPacket(len, connect_timer.left_ms()); // send the connect packet + if (rc != len) + goto exit; // there was a problem + + if (resultHandler == 0) // wait until the connack is received + { + // this will be a blocking call, wait for the connack + if (waitfor(CONNACK, connect_timer) == CONNACK) + { + int connack_rc = -1; + if (MQTTDeserialize_connack(&connack_rc, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1) + rc = connack_rc; + } + } + else + { + // set connect response callback function + connectHandler.attach(resultHandler); + + // start background thread + this->thread = new Thread((void (*)(void const *argument))&MQTT::Async<Network, Timer, Thread, Mutex>::threadfn, (void*)this); + } + +exit: + return rc; +} + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::findFreeOperation() +{ + int found = -1; + for (int i = 0; i < limits.MAX_CONCURRENT_OPERATIONS; ++i) + { + if (operations[i].id == 0) + { + found = i; + break; + } + } + return found; +} + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::subscribe(resultHandler resultHandler, const char* topicFilter, enum QoS qos, messageHandler messageHandler) +{ + int index = 0; + if (this->thread) + index = findFreeOperation(); + Timer& atimer = operations[index].timer; + + atimer.countdown(limits.command_timeout_ms); + MQTTString topic = {(char*)topicFilter, 0, 0}; + + int len = MQTTSerialize_subscribe(buf, limits.MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic, (int*)&qos); + int rc = sendPacket(len, atimer.left_ms()); // send the subscribe packet + if (rc != len) + goto exit; // there was a problem + + /* wait for suback */ + if (resultHandler == 0) + { + // this will block + if (waitfor(SUBACK, atimer) == SUBACK) + { + int count = 0, grantedQoS = -1, mypacketid; + if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1) + rc = grantedQoS; // 0, 1, 2 or 0x80 + if (rc != 0x80) + { + for (int i = 0; i < limits.MAX_MESSAGE_HANDLERS; ++i) + { + if (messageHandlers[i].topic == 0) + { + messageHandlers[i].topic = topicFilter; + messageHandlers[i].fp.attach(messageHandler); + rc = 0; + break; + } + } + } + } + } + else + { + // set subscribe response callback function + + } + +exit: + return rc; +} + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::unsubscribe(resultHandler resultHandler, const char* topicFilter) +{ + int index = 0; + if (this->thread) + index = findFreeOperation(); + Timer& atimer = operations[index].timer; + + atimer.countdown(limits.command_timeout_ms); + MQTTString topic = {(char*)topicFilter, 0, 0}; + + int len = MQTTSerialize_unsubscribe(buf, limits.MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic); + int rc = sendPacket(len, atimer.left_ms()); // send the subscribe packet + if (rc != len) + goto exit; // there was a problem + + // set unsubscribe response callback function + + +exit: + return rc; +} + + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::publish(resultHandler resultHandler, const char* topicName, Message* message) +{ + int index = 0; + if (this->thread) + index = findFreeOperation(); + Timer& atimer = operations[index].timer; + + atimer.countdown(limits.command_timeout_ms); + MQTTString topic = {(char*)topicName, 0, 0}; + + if (message->qos == QOS1 || message->qos == QOS2) + message->id = packetid.getNext(); + + int len = MQTTSerialize_publish(buf, limits.MAX_MQTT_PACKET_SIZE, 0, message->qos, message->retained, message->id, topic, (char*)message->payload, message->payloadlen); + int rc = sendPacket(len, atimer.left_ms()); // send the subscribe packet + if (rc != len) + goto exit; // there was a problem + + /* wait for acks */ + if (resultHandler == 0) + { + if (message->qos == QOS1) + { + if (waitfor(PUBACK, atimer) == PUBACK) + { + int type, dup, mypacketid; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1) + rc = 0; + } + } + else if (message->qos == QOS2) + { + if (waitfor(PUBCOMP, atimer) == PUBCOMP) + { + int type, dup, mypacketid; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1) + rc = 0; + } + + } + } + else + { + // set publish response callback function + + } + +exit: + return rc; +} + + +template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::disconnect(resultHandler resultHandler) +{ + Timer timer = Timer(limits.command_timeout_ms); // we might wait for incomplete incoming publishes to complete + int len = MQTTSerialize_disconnect(buf, limits.MAX_MQTT_PACKET_SIZE); + int rc = sendPacket(len, timer.left_ms()); // send the disconnect packet + + return (rc == len) ? 0 : -1; +} + + + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/MQTTClient.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,939 @@ +/******************************************************************************* + * Copyright (c) 2014, 2015 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: * + * Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs - fix for bug 458512 - QoS 2 messages + * Ian Craggs - fix for bug 460389 - send loop uses wrong length + * Ian Craggs - fix for bug 464169 - clearing subscriptions + * Ian Craggs - fix for bug 464551 - enums and ints can be different size + *******************************************************************************/ + +#if !defined(MQTTCLIENT_H) +#define MQTTCLIENT_H + +#include "FP.h" +#include "MQTTPacket.h" +#include "stdio.h" +#include "MQTTLogging.h" + +#if !defined(MQTTCLIENT_QOS1) + #define MQTTCLIENT_QOS1 1 +#endif +#if !defined(MQTTCLIENT_QOS2) + #define MQTTCLIENT_QOS2 0 +#endif + +namespace MQTT +{ + + +enum QoS { QOS0, QOS1, QOS2 }; + +// all failure return codes must be negative +enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 }; + + +struct Message +{ + enum QoS qos; + bool retained; + bool dup; + unsigned short id; + void *payload; + size_t payloadlen; +}; + + +struct MessageData +{ + MessageData(MQTTString &aTopicName, struct Message &aMessage) : message(aMessage), topicName(aTopicName) + { } + + struct Message &message; + MQTTString &topicName; +}; + + +class PacketId +{ +public: + PacketId() + { + next = 0; + } + + int getNext() + { + return next = (next == MAX_PACKET_ID) ? 1 : ++next; + } + +private: + static const int MAX_PACKET_ID = 65535; + int next; +}; + + +/** + * @class Client + * @brief blocking, non-threaded MQTT client API + * + * This version of the API blocks on all method calls, until they are complete. This means that only one + * MQTT request can be in process at any one time. + * @param Network a network class which supports send, receive + * @param Timer a timer class with the methods: + */ +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE = 100, int MAX_MESSAGE_HANDLERS = 5> +class Client +{ + +public: + + typedef void (*messageHandler)(MessageData&); + + /** Construct the client + * @param network - pointer to an instance of the Network class - must be connected to the endpoint + * before calling MQTT connect + * @param limits an instance of the Limit class - to alter limits as required + */ + Client(Network& network, unsigned int command_timeout_ms = 30000); + + /** Set the default message handling callback - used for any message which does not match a subscription message handler + * @param mh - pointer to the callback function + */ + void setDefaultMessageHandler(messageHandler mh) + { + defaultMessageHandler.attach(mh); + } + + /** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack + * The nework object must be connected to the network endpoint before calling this + * Default connect options are used + * @return success code - + */ + int connect(); + + /** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack + * The nework object must be connected to the network endpoint before calling this + * @param options - connect options + * @return success code - + */ + int connect(MQTTPacket_connectData& options); + + /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs + * @param topic - the topic to publish to + * @param message - the message to send + * @return success code - + */ + int publish(const char* topicName, Message& message); + + /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs + * @param topic - the topic to publish to + * @param payload - the data to send + * @param payloadlen - the length of the data + * @param qos - the QoS to send the publish at + * @param retained - whether the message should be retained + * @return success code - + */ + int publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos = QOS0, bool retained = false); + + /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs + * @param topic - the topic to publish to + * @param payload - the data to send + * @param payloadlen - the length of the data + * @param id - the packet id used - returned + * @param qos - the QoS to send the publish at + * @param retained - whether the message should be retained + * @return success code - + */ + int publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos = QOS1, bool retained = false); + + /** MQTT Subscribe - send an MQTT subscribe packet and wait for the suback + * @param topicFilter - a topic pattern which can include wildcards + * @param qos - the MQTT QoS to subscribe at + * @param mh - the callback function to be invoked when a message is received for this subscription + * @return success code - + */ + int subscribe(const char* topicFilter, enum QoS qos, messageHandler mh); + + /** MQTT Unsubscribe - send an MQTT unsubscribe packet and wait for the unsuback + * @param topicFilter - a topic pattern which can include wildcards + * @return success code - + */ + int unsubscribe(const char* topicFilter); + + /** MQTT Disconnect - send an MQTT disconnect packet, and clean up any state + * @return success code - + */ + int disconnect(); + + /** A call to this API must be made within the keepAlive interval to keep the MQTT connection alive + * yield can be called if no other MQTT operation is needed. This will also allow messages to be + * received. + * @param timeout_ms the time to wait, in milliseconds + * @return success code - on failure, this means the client has disconnected + */ + int yield(unsigned long timeout_ms = 1000L); + + /** Is the client connected? + * @return flag - is the client connected or not? + */ + bool isConnected() + { + return isconnected; + } + +private: + + void cleanSession(); + int cycle(Timer& timer); + int waitfor(int packet_type, Timer& timer); + int keepalive(); + int publish(int len, Timer& timer, enum QoS qos); + + int decodePacket(int* value, int timeout); + int readPacket(Timer& timer); + int sendPacket(int length, Timer& timer); + int deliverMessage(MQTTString& topicName, Message& message); + bool isTopicMatched(char* topicFilter, MQTTString& topicName); + + Network& ipstack; + unsigned long command_timeout_ms; + + unsigned char sendbuf[MAX_MQTT_PACKET_SIZE]; + unsigned char readbuf[MAX_MQTT_PACKET_SIZE]; + + Timer last_sent, last_received; + unsigned int keepAliveInterval; + bool ping_outstanding; + bool cleansession; + + PacketId packetid; + + struct MessageHandlers + { + const char* topicFilter; + FP<void, MessageData&> fp; + } messageHandlers[MAX_MESSAGE_HANDLERS]; // Message handlers are indexed by subscription topic + + FP<void, MessageData&> defaultMessageHandler; + + bool isconnected; + +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + unsigned char pubbuf[MAX_MQTT_PACKET_SIZE]; // store the last publish for sending on reconnect + int inflightLen; + unsigned short inflightMsgid; + enum QoS inflightQoS; +#endif + +#if MQTTCLIENT_QOS2 + bool pubrel; + #if !defined(MAX_INCOMING_QOS2_MESSAGES) + #define MAX_INCOMING_QOS2_MESSAGES 10 + #endif + unsigned short incomingQoS2messages[MAX_INCOMING_QOS2_MESSAGES]; + bool isQoS2msgidFree(unsigned short id); + bool useQoS2msgid(unsigned short id); + void freeQoS2msgid(unsigned short id); +#endif + +}; + +} + + +template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS> +void MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::cleanSession() +{ + ping_outstanding = false; + for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + messageHandlers[i].topicFilter = 0; + isconnected = false; + +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + inflightMsgid = 0; + inflightQoS = QOS0; +#endif + +#if MQTTCLIENT_QOS2 + pubrel = false; + for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) + incomingQoS2messages[i] = 0; +#endif +} + + +template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS> +MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::Client(Network& network, unsigned int command_timeout_ms) : ipstack(network), packetid() +{ + last_sent = Timer(); + last_received = Timer(); + this->command_timeout_ms = command_timeout_ms; + cleanSession(); +} + + +#if MQTTCLIENT_QOS2 +template<class Network, class Timer, int a, int b> +bool MQTT::Client<Network, Timer, a, b>::isQoS2msgidFree(unsigned short id) +{ + for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) + { + if (incomingQoS2messages[i] == id) + return false; + } + return true; +} + + +template<class Network, class Timer, int a, int b> +bool MQTT::Client<Network, Timer, a, b>::useQoS2msgid(unsigned short id) +{ + for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) + { + if (incomingQoS2messages[i] == 0) + { + incomingQoS2messages[i] = id; + return true; + } + } + return false; +} + + +template<class Network, class Timer, int a, int b> +void MQTT::Client<Network, Timer, a, b>::freeQoS2msgid(unsigned short id) +{ + for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) + { + if (incomingQoS2messages[i] == id) + { + incomingQoS2messages[i] = 0; + return; + } + } +} +#endif + + +template<class Network, class Timer, int a, int b> +int MQTT::Client<Network, Timer, a, b>::sendPacket(int length, Timer& timer) +{ + int rc = FAILURE, + sent = 0; + + while (sent < length && !timer.expired()) + { + rc = ipstack.write(&sendbuf[sent], length - sent, timer.left_ms()); + if (rc < 0) // there was an error writing the data + break; + sent += rc; + } + if (sent == length) + { + if (this->keepAliveInterval > 0) + last_sent.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet + rc = SUCCESS; + } + else + rc = FAILURE; + +#if defined(MQTT_DEBUG) + char printbuf[150]; + DEBUG("Rc %d from sending packet %s\n", rc, MQTTFormat_toServerString(printbuf, sizeof(printbuf), sendbuf, length)); +#endif + return rc; +} + + +template<class Network, class Timer, int a, int b> +int MQTT::Client<Network, Timer, a, b>::decodePacket(int* value, int timeout) +{ + unsigned char c; + int multiplier = 1; + int len = 0; + const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4; + + *value = 0; + do + { + int rc = MQTTPACKET_READ_ERROR; + + if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) + { + rc = MQTTPACKET_READ_ERROR; /* bad data */ + goto exit; + } + rc = ipstack.read(&c, 1, timeout); + if (rc != 1) + goto exit; + *value += (c & 127) * multiplier; + multiplier *= 128; + } while ((c & 128) != 0); +exit: + return len; +} + + +/** + * If any read fails in this method, then we should disconnect from the network, as on reconnect + * the packets can be retried. + * @param timeout the max time to wait for the packet read to complete, in milliseconds + * @return the MQTT packet type, or -1 if none + */ +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::readPacket(Timer& timer) +{ + int rc = FAILURE; + MQTTHeader header = {0}; + int len = 0; + int rem_len = 0; + + /* 1. read the header byte. This has the packet type in it */ + if (ipstack.read(readbuf, 1, timer.left_ms()) != 1) + goto exit; + + len = 1; + /* 2. read the remaining length. This is variable in itself */ + decodePacket(&rem_len, timer.left_ms()); + len += MQTTPacket_encode(readbuf + 1, rem_len); /* put the original remaining length into the buffer */ + + if (rem_len > (MAX_MQTT_PACKET_SIZE - len)) + { + rc = BUFFER_OVERFLOW; + goto exit; + } + + /* 3. read the rest of the buffer using a callback to supply the rest of the data */ + if (rem_len > 0 && (ipstack.read(readbuf + len, rem_len, timer.left_ms()) != rem_len)) + goto exit; + + header.byte = readbuf[0]; + rc = header.bits.type; + if (this->keepAliveInterval > 0) + last_received.countdown(this->keepAliveInterval); // record the fact that we have successfully received a packet +exit: + +#if defined(MQTT_DEBUG) + if (rc >= 0) + { + char printbuf[50]; + DEBUG("Rc %d from receiving packet %s\n", rc, MQTTFormat_toClientString(printbuf, sizeof(printbuf), readbuf, len)); + } +#endif + return rc; +} + + +// assume topic filter and name is in correct format +// # can only be at end +// + and # can only be next to separator +template<class Network, class Timer, int a, int b> +bool MQTT::Client<Network, Timer, a, b>::isTopicMatched(char* topicFilter, MQTTString& topicName) +{ + char* curf = topicFilter; + char* curn = topicName.lenstring.data; + char* curn_end = curn + topicName.lenstring.len; + + while (*curf && curn < curn_end) + { + if (*curn == '/' && *curf != '/') + break; + if (*curf != '+' && *curf != '#' && *curf != *curn) + break; + if (*curf == '+') + { // skip until we meet the next separator, or end of string + char* nextpos = curn + 1; + while (nextpos < curn_end && *nextpos != '/') + nextpos = ++curn + 1; + } + else if (*curf == '#') + curn = curn_end - 1; // skip until end of string + curf++; + curn++; + }; + + return (curn == curn_end) && (*curf == '\0'); +} + + + +template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS> +int MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::deliverMessage(MQTTString& topicName, Message& message) +{ + int rc = FAILURE; + + // we have to find the right message handler - indexed by topic + for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + { + if (messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(&topicName, (char*)messageHandlers[i].topicFilter) || + isTopicMatched((char*)messageHandlers[i].topicFilter, topicName))) + { + if (messageHandlers[i].fp.attached()) + { + MessageData md(topicName, message); + messageHandlers[i].fp(md); + rc = SUCCESS; + } + } + } + + if (rc == FAILURE && defaultMessageHandler.attached()) + { + MessageData md(topicName, message); + defaultMessageHandler(md); + rc = SUCCESS; + } + + return rc; +} + + + +template<class Network, class Timer, int a, int b> +int MQTT::Client<Network, Timer, a, b>::yield(unsigned long timeout_ms) +{ + int rc = SUCCESS; + Timer timer = Timer(); + + timer.countdown_ms(timeout_ms); + while (!timer.expired()) + { + if (cycle(timer) < 0) + { + rc = FAILURE; + break; + } + } + + return rc; +} + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::cycle(Timer& timer) +{ + /* get one piece of work off the wire and one pass through */ + + // read the socket, see what work is due + int packet_type = readPacket(timer); + + int len = 0, + rc = SUCCESS; + + switch (packet_type) + { + case FAILURE: + case BUFFER_OVERFLOW: + rc = packet_type; + break; + case CONNACK: + case PUBACK: + case SUBACK: + break; + case PUBLISH: + { + MQTTString topicName = MQTTString_initializer; + Message msg; + int intQoS; + if (MQTTDeserialize_publish((unsigned char*)&msg.dup, &intQoS, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName, + (unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_MQTT_PACKET_SIZE) != 1) + goto exit; + msg.qos = (enum QoS)intQoS; +#if MQTTCLIENT_QOS2 + if (msg.qos != QOS2) +#endif + deliverMessage(topicName, msg); +#if MQTTCLIENT_QOS2 + else if (isQoS2msgidFree(msg.id)) + { + if (useQoS2msgid(msg.id)) + deliverMessage(topicName, msg); + else + WARN("Maximum number of incoming QoS2 messages exceeded"); + } +#endif +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + if (msg.qos != QOS0) + { + if (msg.qos == QOS1) + len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBACK, 0, msg.id); + else if (msg.qos == QOS2) + len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREC, 0, msg.id); + if (len <= 0) + rc = FAILURE; + else + rc = sendPacket(len, timer); + if (rc == FAILURE) + goto exit; // there was a problem + } + break; +#endif + } +#if MQTTCLIENT_QOS2 + case PUBREC: + case PUBREL: + unsigned short mypacketid; + unsigned char dup, type; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) + rc = FAILURE; + else if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, + (packet_type == PUBREC) ? PUBREL : PUBCOMP, 0, mypacketid)) <= 0) + rc = FAILURE; + else if ((rc = sendPacket(len, timer)) != SUCCESS) // send the PUBREL packet + rc = FAILURE; // there was a problem + if (rc == FAILURE) + goto exit; // there was a problem + if (packet_type == PUBREL) + freeQoS2msgid(mypacketid); + break; + + case PUBCOMP: + break; +#endif + case PINGRESP: + ping_outstanding = false; + break; + } + keepalive(); +exit: + if (rc == SUCCESS) + rc = packet_type; + return rc; +} + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::keepalive() +{ + int rc = FAILURE; + + if (keepAliveInterval == 0) + { + rc = SUCCESS; + goto exit; + } + + if (last_sent.expired() || last_received.expired()) + { + if (!ping_outstanding) + { + Timer timer(1000); + int len = MQTTSerialize_pingreq(sendbuf, MAX_MQTT_PACKET_SIZE); + if (len > 0 && (rc = sendPacket(len, timer)) == SUCCESS) // send the ping packet + ping_outstanding = true; + } + } + +exit: + return rc; +} + + +// only used in single-threaded mode where one command at a time is in process +template<class Network, class Timer, int a, int b> +int MQTT::Client<Network, Timer, a, b>::waitfor(int packet_type, Timer& timer) +{ + int rc = FAILURE; + + do + { + if (timer.expired()) + break; // we timed out + } + while ((rc = cycle(timer)) != packet_type); + + return rc; +} + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect(MQTTPacket_connectData& options) +{ + Timer connect_timer(command_timeout_ms); + int rc = FAILURE; + int len = 0; + + if (isconnected) // don't send connect packet again if we are already connected + goto exit; + + this->keepAliveInterval = options.keepAliveInterval; + this->cleansession = options.cleansession; + if ((len = MQTTSerialize_connect(sendbuf, MAX_MQTT_PACKET_SIZE, &options)) <= 0) + goto exit; + if ((rc = sendPacket(len, connect_timer)) != SUCCESS) // send the connect packet + goto exit; // there was a problem + + if (this->keepAliveInterval > 0) + last_received.countdown(this->keepAliveInterval); + // this will be a blocking call, wait for the connack + if (waitfor(CONNACK, connect_timer) == CONNACK) + { + unsigned char connack_rc = 255; + bool sessionPresent = false; + if (MQTTDeserialize_connack((unsigned char*)&sessionPresent, &connack_rc, readbuf, MAX_MQTT_PACKET_SIZE) == 1) + rc = connack_rc; + else + rc = FAILURE; + } + else + rc = FAILURE; + +#if MQTTCLIENT_QOS2 + // resend any inflight publish + if (inflightMsgid > 0 && inflightQoS == QOS2 && pubrel) + { + if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREL, 0, inflightMsgid)) <= 0) + rc = FAILURE; + else + rc = publish(len, connect_timer, inflightQoS); + } + else +#endif +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + if (inflightMsgid > 0) + { + memcpy(sendbuf, pubbuf, MAX_MQTT_PACKET_SIZE); + rc = publish(inflightLen, connect_timer, inflightQoS); + } +#endif + +exit: + if (rc == SUCCESS) + isconnected = true; + return rc; +} + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect() +{ + MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer; + return connect(default_options); +} + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::subscribe(const char* topicFilter, enum QoS qos, messageHandler messageHandler) +{ + int rc = FAILURE; + Timer timer(command_timeout_ms); + int len = 0; + MQTTString topic = {(char*)topicFilter, {0, 0}}; + + if (!isconnected) + goto exit; + + len = MQTTSerialize_subscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic, (int*)&qos); + if (len <= 0) + goto exit; + if ((rc = sendPacket(len, timer)) != SUCCESS) // send the subscribe packet + goto exit; // there was a problem + + if (waitfor(SUBACK, timer) == SUBACK) // wait for suback + { + int count = 0, grantedQoS = -1; + unsigned short mypacketid; + if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, readbuf, MAX_MQTT_PACKET_SIZE) == 1) + rc = grantedQoS; // 0, 1, 2 or 0x80 + if (rc != 0x80) + { + for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + { + if (messageHandlers[i].topicFilter == 0) + { + messageHandlers[i].topicFilter = topicFilter; + messageHandlers[i].fp.attach(messageHandler); + rc = 0; + break; + } + } + } + } + else + rc = FAILURE; + +exit: + if (rc != SUCCESS) + cleanSession(); + return rc; +} + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::unsubscribe(const char* topicFilter) +{ + int rc = FAILURE; + Timer timer(command_timeout_ms); + MQTTString topic = {(char*)topicFilter, {0, 0}}; + int len = 0; + + if (!isconnected) + goto exit; + + if ((len = MQTTSerialize_unsubscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic)) <= 0) + goto exit; + if ((rc = sendPacket(len, timer)) != SUCCESS) // send the unsubscribe packet + goto exit; // there was a problem + + if (waitfor(UNSUBACK, timer) == UNSUBACK) + { + unsigned short mypacketid; // should be the same as the packetid above + if (MQTTDeserialize_unsuback(&mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) == 1) + { + rc = 0; + + // remove the subscription message handler associated with this topic, if there is one + for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + { + if (messageHandlers[i].topicFilter && strcmp(messageHandlers[i].topicFilter, topicFilter) == 0) + { + messageHandlers[i].topicFilter = 0; + break; + } + } + } + } + else + rc = FAILURE; + +exit: + if (rc != SUCCESS) + cleanSession(); + return rc; +} + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(int len, Timer& timer, enum QoS qos) +{ + int rc; + + if ((rc = sendPacket(len, timer)) != SUCCESS) // send the publish packet + goto exit; // there was a problem + +#if MQTTCLIENT_QOS1 + if (qos == QOS1) + { + if (waitfor(PUBACK, timer) == PUBACK) + { + unsigned short mypacketid; + unsigned char dup, type; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) + rc = FAILURE; + else if (inflightMsgid == mypacketid) + inflightMsgid = 0; + } + else + rc = FAILURE; + } +#elif MQTTCLIENT_QOS2 + else if (qos == QOS2) + { + if (waitfor(PUBCOMP, timer) == PUBCOMP) + { + unsigned short mypacketid; + unsigned char dup, type; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) + rc = FAILURE; + else if (inflightMsgid == mypacketid) + inflightMsgid = 0; + } + else + rc = FAILURE; + } +#endif + +exit: + if (rc != SUCCESS) + cleanSession(); + return rc; +} + + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos, bool retained) +{ + int rc = FAILURE; + Timer timer(command_timeout_ms); + MQTTString topicString = MQTTString_initializer; + int len = 0; + + if (!isconnected) + goto exit; + + topicString.cstring = (char*)topicName; + +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + if (qos == QOS1 || qos == QOS2) + id = packetid.getNext(); +#endif + + len = MQTTSerialize_publish(sendbuf, MAX_MQTT_PACKET_SIZE, 0, qos, retained, id, + topicString, (unsigned char*)payload, payloadlen); + if (len <= 0) + goto exit; + +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + if (!cleansession) + { + memcpy(pubbuf, sendbuf, len); + inflightMsgid = id; + inflightLen = len; + inflightQoS = qos; +#if MQTTCLIENT_QOS2 + pubrel = false; +#endif + } +#endif + + rc = publish(len, timer, qos); +exit: + return rc; +} + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos, bool retained) +{ + unsigned short id = 0; // dummy - not used for anything + return publish(topicName, payload, payloadlen, id, qos, retained); +} + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, Message& message) +{ + return publish(topicName, message.payload, message.payloadlen, message.qos, message.retained); +} + + +template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b> +int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::disconnect() +{ + int rc = FAILURE; + Timer timer(command_timeout_ms); // we might wait for incomplete incoming publishes to complete + int len = MQTTSerialize_disconnect(sendbuf, MAX_MQTT_PACKET_SIZE); + if (len > 0) + rc = sendPacket(len, timer); // send the disconnect packet + + if (cleansession) + cleanSession(); + else + isconnected = false; + return rc; +} + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/MQTTEthernet.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,35 @@ + +#if !defined(MQTTETHERNET_H) +#define MQTTETHERNET_H + +#include "MQTTmbed.h" +#include "EthernetInterface.h" +#include "MQTTSocket.h" + +class MQTTEthernet : public MQTTSocket +{ +public: + MQTTEthernet() + { + eth.init(); // Use DHCP + eth.connect(); + } + + EthernetInterface& getEth() + { + return eth; + } + + void reconnect() + { + eth.connect(); // nothing I've tried actually works to reconnect + } + +private: + + EthernetInterface eth; + +}; + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/MQTTLogging.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,39 @@ +#if !defined(MQTT_LOGGING_H) +#define MQTT_LOGGING_H + +#define STREAM stdout +#if !defined(DEBUG) +#define DEBUG(...) \ + {\ + fprintf(STREAM, "DEBUG: %s L#%d ", __PRETTY_FUNCTION__, __LINE__); \ + fprintf(STREAM, ##__VA_ARGS__); \ + fflush(STREAM); \ + } +#endif +#if !defined(LOG) +#define LOG(...) \ + {\ + fprintf(STREAM, "LOG: %s L#%d ", __PRETTY_FUNCTION__, __LINE__); \ + fprintf(STREAM, ##__VA_ARGS__); \ + fflush(STREAM); \ + } +#endif +#if !defined(WARN) +#define WARN(...) \ + { \ + fprintf(STREAM, "WARN: %s L#%d ", __PRETTY_FUNCTION__, __LINE__); \ + fprintf(STREAM, ##__VA_ARGS__); \ + fflush(STREAM); \ + } +#endif +#if !defined(ERROR) +#define ERROR(...) \ + { \ + fprintf(STREAM, "ERROR: %s L#%d ", __PRETTY_FUNCTION__, __LINE__); \ + fprintf(STREAM, ##__VA_ARGS__); \ + fflush(STREAM); \ + exit(1); \ + } +#endif + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/MQTTPacket.lib Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,1 @@ +http://mbed.org/teams/mqtt/code/MQTTPacket/#62396c1620b6
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/MQTTSocket.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,118 @@ +#if !defined(MQTTSOCKET_H) +#define MQTTSOCKET_H + +#include "MQTTmbed.h" +#include "TCPSocket.h" + +class MQTTSocket +{ +public: + + MQTTSocket(): mysock(true) + { + + } + + int open(NetworkStack *ipstack) + { + _time_last_ticks = 0; + timesock.open(ipstack); + return mysock.open(ipstack); + } + + int getNTPtime(int timeout=1000) + { + int err; + + uint8_t timedata[4]; + + timesock.set_timeout(timeout*3); + do + { + err= timesock.connect("time-d.nist.gov", 37); + + if (err != 0) + { + LOG("ERROR resolving ntp server IP and connecting to it! \r\n"); + } + } while (err != 0); + + while (err != 4) + { + err = timesock.recv((char*)timedata, 4); + + if (err != 4) + { + LOG("ERROR receiving time from ntp server! \r\n"); + //return -1; + } + else + { + /// + /// Time Protocol provides the time as a binary number of seconds since 1900, + /// + /// 2,208,988,800 corresponds to 00:00 1 Jan 1970 GMT from 12:00:01 am on 1 January 1900 GMT + /// + _time_last_ticks = ((timedata[0]<<24 )|(timedata[1]<<16)|(timedata[2]<<8)| timedata[3]) - 2208988800ul; + + LOG("Success receiving time from ntp server. Tick from 1 Jan 1970 is equal to %d. \r\n", _time_last_ticks); + + } + } + + err = timesock.close(); + + return err; + } + + int connect(char* hostname, int port, int timeout=1000) + { + int err; + + mysock.set_timeout(timeout); + err = mysock.connect(hostname, port); +// t.start(); + return err; + } + + int read(unsigned char* buffer, int len, int timeout) + { + mysock.set_timeout(timeout); +//t.reset(); +// int start = t.read_ms(); + int rc = mysock.recv((char*)buffer, len); +// int stop = t.read_ms(); +// if (rc>0) printf ("recv File: %s, Line: %d Read nB: %d rc: %d timeout: %d elaps: %d\n\r",__FILE__,__LINE__, len, rc, timeout, stop-start); + return rc; + } + + int write(unsigned char* buffer, int len, int timeout) + { + mysock.set_timeout(timeout); +// mysock.set_blocking(false, timeout); +// mysock.set_blocking(false); + int rc = mysock.send((char*)buffer, len); +// printf ("send File: %s, Line: %d Write nB: %d rc: %d\n\r",__FILE__,__LINE__, len, rc); + return rc; + } + + int disconnect() + { +// t.stop(); + return mysock.close(); + } + + inline int getTime() + { + return _time_last_ticks; + } + +private: + TCPSocket mysock; + TCPSocket timesock; + + uint32_t _time_last_ticks; + // Timer t; + +}; +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/MQTTWiFi.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,30 @@ + +#if !defined(MQTTWIFI_H) +#define MQTTWIFI_H + +#include "MQTTmbed.h" +#include "WiFiInterface.h" +#include "MQTTSocket.h" + +class MQTTWiFi : public MQTTSocket +{ +public: + MQTTWiFi(SpwfSAInterface &WiFiIntf, const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE) : WiFi(WiFiIntf) + { + WiFi.reset_chip(); + WiFi.connect(ssid, pass, security); + } + + SpwfSAInterface& getWiFi() + { + return WiFi; + } + +private: + + SpwfSAInterface& WiFi; + +}; + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/MQTTmbed.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,49 @@ +#if !defined(MQTT_MBED_H) +#define MQTT_MBED_H + +#include "mbed.h" + +class Countdown +{ +public: + Countdown() + { + t = Timer(); + } + + Countdown(int ms) + { + t = Timer(); + countdown_ms(ms); + } + + + bool expired() + { + return t.read_ms() >= interval_end_ms; + } + + void countdown_ms(unsigned long ms) + { + t.stop(); + interval_end_ms = ms; + t.reset(); + t.start(); + } + + void countdown(int seconds) + { + countdown_ms((unsigned long)seconds * 1000L); + } + + int left_ms() + { + return interval_end_ms - t.read_ms(); + } + +private: + Timer t; + unsigned long interval_end_ms; +}; + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/NOTICE.TXT Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,4 @@ + +This library is originated from https://developer.mbed.org/teams/ST/code/MQTT/#66826ea709ea +MQTTWiFi class was modified to support hardware wifi reset. +MQTTSocket class was modified to support NTP server time request.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NOTICE.txt Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,2 @@ +This project is originally got from https://developer.mbed.org/teams/ST/code/IDW01M1_Cloud_IBM/ +It was completely adopted to Amazon AWS services. IBM IoT service connection is not supported. \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/CellularInterface.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,51 @@ +/* CellularInterface + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CELLULAR_INTERFACE_H +#define CELLULAR_INTERFACE_H + +#include "NetworkStack.h" + +/** CellularInterface class + * + * Common interface that is shared between ethernet hardware + */ +class CellularInterface +{ +public: + /** Start the interface + * + * @param apn Optional name of the network to connect to + * @param username Optional username for your APN + * @param password Optional password for your APN + * @return 0 on success, negative error code on failure + */ + virtual int connect(const char *apn = 0, const char *username = 0, const char *password = 0) = 0; + + /** Stop the interface + * + * @return 0 on success, negative error code on failure + */ + virtual int disconnect() = 0; + + /** Get the local MAC address + * + * @return Null-terminated representation of the local MAC address + */ + virtual const char *get_mac_address() = 0; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/DnsQuery.lib Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,1 @@ +https://developer.mbed.org/teams/NetworkSocketAPI/code/DnsQuery/#2cb1fffed50c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/EthernetInterface.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,48 @@ +/* EthernetInterface + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ETHERNET_INTERFACE_H +#define ETHERNET_INTERFACE_H + +#include "NetworkStack.h" + +/** EthernetInterface class + * + * Common interface that is shared between ethernet hardware. + */ +class EthernetInterface +{ +public: + /** Start the interface + * + * @return 0 on success, negative error code on failure + */ + virtual int connect() = 0; + + /** Stop the interface + * + * @return 0 on success, negative error code on failure + */ + virtual int disconnect() = 0; + + /** Get the local MAC address + * + * @return Null-terminated representation of the local MAC address + */ + virtual const char *get_mac_address() = 0; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/MeshInterface.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,48 @@ +/* MeshInterface + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MESH_INTERFACE_H +#define MESH_INTERFACE_H + +#include "NetworkStack.h" + +/** MeshInterface class + * + * Common interface that is shared between mesh hardware + */ +class MeshInterface +{ +public: + /** Start the interface + * + * @return 0 on success, negative on failure + */ + virtual int connect() = 0; + + /** Stop the interface + * + * @return 0 on success, negative on failure + */ + virtual int disconnect() = 0; + + /** Get the local MAC address + * + * @return Null-terminated representation of the local MAC address + */ + virtual const char *get_mac_address() = 0; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/NOTICE.txt Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,3 @@ +This library is origionated from http://mbed.org/teams/NetworkSocketAPI/code/NetworkSocketAPI/#ea3a618e0818 +"enum nsapi_protocol_t" is extended with security socket +"class TCPSocket" is extended with security socket support
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/NetworkStack.cpp Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,50 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DnsQuery.h" +#include "mbed.h" + +int NetworkStack::gethostbyname(SocketAddress *address, const char *name) +{ + char buffer[NSAPI_IP_SIZE]; + int err = dnsQuery(this, name, buffer); + if (err) { + return err; + } + + address->set_ip_address(buffer); + return 0; +} + +int NetworkStack::setstackopt(int level, int optname, const void *optval, unsigned optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int NetworkStack::getstackopt(int level, int optname, void *optval, unsigned *optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int NetworkStack::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +int NetworkStack::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/NetworkStack.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,339 @@ +/* NetworkStack + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NETWORK_INTERFACE_H +#define NETWORK_INTERFACE_H + +#include "mbed.h" +#include "SocketAddress.h" + + +/** Enum of standardized error codes + * + * Valid error codes have negative values and may + * be returned by any network operation. + * + * @enum nsapi_error_t + */ +enum nsapi_error_t { + NSAPI_ERROR_WOULD_BLOCK = -3001, /*!< no data is not available but call is non-blocking */ + NSAPI_ERROR_UNSUPPORTED = -3002, /*!< unsupported functionality */ + NSAPI_ERROR_PARAMETER = -3003, /*!< invalid configuration */ + NSAPI_ERROR_NO_CONNECTION = -3004, /*!< not connected to a network */ + NSAPI_ERROR_NO_SOCKET = -3005, /*!< socket not available for use */ + NSAPI_ERROR_NO_ADDRESS = -3006, /*!< IP address is not known */ + NSAPI_ERROR_NO_MEMORY = -3007, /*!< memory resource not available */ + NSAPI_ERROR_DNS_FAILURE = -3008, /*!< DNS failed to complete successfully */ + NSAPI_ERROR_DHCP_FAILURE = -3009, /*!< DHCP failed to complete successfully */ + NSAPI_ERROR_AUTH_FAILURE = -3010, /*!< connection to access point faield */ + NSAPI_ERROR_DEVICE_ERROR = -3011, /*!< failure interfacing with the network procesor */ +}; + +/** Enum of socket protocols + * + * The socket protocol specifies a particular protocol to + * be used with a newly created socket. + * + * @enum nsapi_protocol_t + */ +enum nsapi_protocol_t { + NSAPI_TCP, /*!< Socket is of TCP type */ + NSAPI_UDP, /*!< Socket is of UDP type */ + NSAPI_TLS /*!< Socket is of TCP Secure type */ +}; + +/* Enum of standardized stack option levels + * + * @enum nsapi_level_t + */ +enum nsapi_level_t { + NSAPI_STACK, /*!< Stack option level */ + NSAPI_SOCKET, /*!< Socket option level */ +}; + +/* Enum of standardized stack options + * + * These options may not be supported on all stacks, in which + * case NSAPI_ERROR_UNSUPPORTED may be returned from setsockopt. + * + * @enum nsapi_option_t + */ +enum nsapi_option_t { + NSAPI_REUSEADDR, /*!< Allow bind to reuse local addresses */ + NSAPI_KEEPALIVE, /*!< Enables sending of keepalive messages */ + NSAPI_LINGER, /*!< Keeps close from returning until queues empty */ + NSAPI_SNDBUF, /*!< Sets send buffer size */ + NSAPI_RCVBUF, /*!< Sets recv buffer size */ +}; + + +/** NetworkStack class + * + * Common interface that is shared between hardware that + * can connect to a network over IP. By implementing the + * NetworkStack, a network stack can be used as a target + * for instantiating network sockets. + */ +class NetworkStack +{ +public: + virtual ~NetworkStack() {}; + + /** Get the local IP address + * + * @return Null-terminated representation of the local IP address + * or null if not yet connected + */ + virtual const char *get_ip_address() = 0; + + /** Translates a hostname to an IP address + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * If no stack-specific DNS resolution is provided, the hostname + * will be resolve using a UDP socket on the stack. + * + * @param address Destination for the host SocketAddress + * @param host Hostname to resolve + * @return 0 on success, negative error code on failure + */ + virtual int gethostbyname(SocketAddress *address, const char *host); + + /* Set stack-specific stack options + * + * The setstackopt allow an application to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the stack is unmodified. + * + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual int setstackopt(int level, int optname, const void *optval, unsigned optlen); + + /* Get stack-specific stack options + * + * The getstackopt allow an application to retrieve stack-specific hints + * from the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified. + * + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Destination for option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual int getstackopt(int level, int optname, void *optval, unsigned *optlen); + +protected: + friend class Socket; + friend class UDPSocket; + friend class TCPSocket; + friend class TCPServer; + + /** Opens a socket + * + * Creates a network socket and stores it in the specified handle. + * The handle must be passed to following calls on the socket. + * + * A stack may have a finite number of sockets, in this case + * NSAPI_ERROR_NO_SOCKET is returned if no socket is available. + * + * @param handle Destination for the handle to a newly created socket + * @param proto Protocol of socket to open, NSAPI_TCP or NSAPI_UDP + * @return 0 on success, negative error code on failure + */ + virtual int socket_open(void **handle, nsapi_protocol_t proto) = 0; + + /** Close the socket + * + * Closes any open connection and deallocates any memory associated + * with the socket. + * + * @param handle Socket handle + * @return 0 on success, negative error code on failure + */ + virtual int socket_close(void *handle) = 0; + + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to recieve + * data. If the IP address is zeroed, only the port is bound. + * + * @param handle Socket handle + * @param address Local address to bind + * @return 0 on success, negative error code on failure. + */ + virtual int socket_bind(void *handle, const SocketAddress &address) = 0; + + /** Listen for connections on a TCP socket + * + * Marks the socket as a passive socket that can be used to accept + * incoming connections. + * + * @param handle Socket handle + * @param backlog Number of pending connections that can be queued + * simultaneously + * @return 0 on success, negative error code on failure + */ + virtual int socket_listen(void *handle, int backlog) = 0; + + /** Connects TCP socket to a remote host + * + * Initiates a connection to a remote server specified by the + * indicated address. + * + * @param handle Socket handle + * @param address The SocketAddress of the remote host + * @return 0 on success, negative error code on failure + */ + virtual int socket_connect(void *handle, const SocketAddress &address) = 0; + + /** Accepts a connection on a TCP socket + * + * The server socket must be bound and set to listen for connections. + * On a new connection, creates a network socket and stores it in the + * specified handle. The handle must be passed to following calls on + * the socket. + * + * A stack may have a finite number of sockets, in this case + * NSAPI_ERROR_NO_SOCKET is returned if no socket is available. + * + * This call is non-blocking. If accept would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param handle Destination for a handle to the newly created sockey + * @param server Socket handle to server to accept from + * @return 0 on success, negative error code on failure + */ + virtual int socket_accept(void **handle, void *server) = 0; + + /** Send data over a TCP socket + * + * The socket must be connected to a remote host. Returns the number of + * bytes sent from the buffer. + * + * This call is non-blocking. If send would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param handle Socket handle + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure + */ + virtual int socket_send(void *handle, const void *data, unsigned size) = 0; + + /** Receive data over a TCP socket + * + * The socket must be connected to a remote host. Returns the number of + * bytes received into the buffer. + * + * This call is non-blocking. If recv would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param handle Socket handle + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure + */ + virtual int socket_recv(void *handle, void *data, unsigned size) = 0; + + /** Send a packet over a UDP socket + * + * Sends data to the specified address. Returns the number of bytes + * sent from the buffer. + * + * This call is non-blocking. If sendto would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param handle Socket handle + * @param address The SocketAddress of the remote host + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure + */ + virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size) = 0; + + /** Receive a packet over a UDP socket + * + * Receives data and stores the source address in address if address + * is not NULL. Returns the number of bytes received into the buffer. + * + * This call is non-blocking. If recvfrom would block, + * NSAPI_ERROR_WOULD_BLOCK is returned immediately. + * + * @param handle Socket handle + * @param address Destination for the source address or NULL + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure + */ + virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size) = 0; + + /** Register a callback on state change of the socket + * + * The specified callback will be called on state changes such as when + * the socket can recv/send/accept successfully and on when an error + * occurs. The callback may also be called spuriously without reason. + * + * The callback may be called in an interrupt context and should not + * perform expensive operations such as recv/send calls. + * + * @param handle Socket handle + * @param callback Function to call on state change + * @param data Argument to pass to callback + */ + virtual void socket_attach(void *handle, void (*callback)(void *), void *data) = 0; + + /* Set stack-specific socket options + * + * The setsockopt allow an application to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the socket is unmodified. + * + * @param handle Socket handle + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual int setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen); + + /* Get stack-specific socket options + * + * The getstackopt allow an application to retrieve stack-specific hints + * from the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified. + * + * @param handle Socket handle + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Destination for option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + virtual int getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/Socket.cpp Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,127 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Socket.h" + +Socket::Socket() + : _iface(0) + , _socket(0) + , _timeout(-1) +{ +} + +Socket::~Socket() +{ + if (_socket) { + close(); + } +} + +int Socket::open(NetworkStack *iface, nsapi_protocol_t proto) +{ + _iface = iface; + + void *socket; + int err = _iface->socket_open(&socket, proto); + if (err) { + return err; + } + + _socket = socket; + _iface->socket_attach(_socket, &Socket::thunk, this); + + return 0; +} + +int Socket::close() +{ + if (!_socket) { + return 0; + } + + _iface->socket_attach(_socket, 0, 0); + + void *volatile socket = _socket; + _socket = 0; + return _iface->socket_close(socket); +} + +int Socket::bind(uint16_t port) +{ + SocketAddress addr(0, port); + return bind(addr); +} + +int Socket::bind(const char *address, uint16_t port) +{ + SocketAddress addr(address, port); + return bind(addr); +} + +int Socket::bind(const SocketAddress &address) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->socket_bind(_socket, address); +} + +void Socket::set_blocking(bool blocking) +{ + set_timeout(blocking ? -1 : 0); +} + +void Socket::set_timeout(int timeout) +{ + _timeout = timeout; +} + +int Socket::setsockopt(int level, int optname, const void *optval, unsigned optlen) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->setsockopt(_socket, level, optname, optval, optlen); +} + +int Socket::getsockopt(int level, int optname, void *optval, unsigned *optlen) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->getsockopt(_socket, level, optname, optval, optlen); + +} + +void Socket::wakeup() +{ +} + +void Socket::thunk(void *data) +{ + Socket *self = (Socket *)data; + if (self->_callback) { + self->_callback(); + } +} + +void Socket::attach(FunctionPointer callback) +{ + _callback = callback; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/Socket.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,180 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOCKET_H +#define SOCKET_H + +#include "SocketAddress.h" +#include "NetworkStack.h" + +/** Abstract socket class + */ +class Socket { +public: + /** Destroy a socket + * + * Closes socket if the socket is still open + */ + virtual ~Socket(); + + /** Opens a socket + * + * Creates a network socket on the specified network stack. + * Not needed if stack is passed to the socket's constructor. + * + * @param iface Network stack as target for socket + * @return 0 on success, negative error code on failure + */ + virtual int open(NetworkStack *iface) = 0; + + /** Close the socket + * + * Closes any open connection and deallocates any memory associated + * with the socket. Called from destructor if socket is not closed. + * + * @return 0 on success, negative error code on failure + */ + int close(); + + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to recieve + * data. + * + * @param port Local port to bind + * @return 0 on success, negative error code on failure. + */ + int bind(uint16_t port); + + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to recieve + * data. If the IP address is zeroed, only the port is bound. + * + * @param address Null-terminated local address to bind + * @param port Local port to bind + * @return 0 on success, negative error code on failure. + */ + int bind(const char *address, uint16_t port); + + /** Bind a specific address to a socket + * + * Binding a socket specifies the address and port on which to recieve + * data. If the IP address is zeroed, only the port is bound. + * + * @param address Local address to bind + * @return 0 on success, negative error code on failure. + */ + int bind(const SocketAddress &address); + + /** Set blocking or non-blocking mode of the socket + * + * Initially all sockets are in blocking mode. In non-blocking mode + * blocking operations such as send/recv/accept return + * NSAPI_ERROR_WOULD_BLOCK if they can not continue. + * + * set_blocking(false) is equivalent to set_timeout(-1) + * set_blocking(true) is equivalent to set_timeout(0) + * + * @param blocking true for blocking mode, false for non-blocking mode. + */ + void set_blocking(bool blocking); + + /** Set timeout on blocking socket operations + * + * Initially all sockets have unbounded timeouts. NSAPI_ERROR_WOULD_BLOCK + * is returned if a blocking operation takes longer than the specified + * timeout. A timeout of -1 removes the timeout from the socket. + * + * set_timeout(-1) is equivalent to set_blocking(false) + * set_timeout(0) is equivalent to set_blocking(true) + * + * @param timeout Timeout in milliseconds + */ + void set_timeout(int timeout); + + /* Set stack-specific socket options + * + * The setsockopt allow an application to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the socket is unmodified. + * + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + int setsockopt(int level, int optname, const void *optval, unsigned optlen); + + /* Get stack-specific socket options + * + * The getstackopt allow an application to retrieve stack-specific hints + * from the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and optval is unmodified. + * + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Destination for option value + * @param optlen Length of the option value + * @return 0 on success, negative error code on failure + */ + int getsockopt(int level, int optname, void *optval, unsigned *optlen); + + /** Register a callback on state change of the socket + * + * The specified callback will be called on state changes such as when + * the socket can recv/send/accept successfully and on when an error + * occurs. The callback may also be called spuriously without reason. + * + * The callback may be called in an interrupt context and should not + * perform expensive operations such as recv/send calls. + * + * @param callback Function to call on state change + */ + void attach(FunctionPointer callback); + + /** Register a callback on state change of the socket + * + * The specified callback will be called on state changes such as when + * the socket can recv/send/accept successfully and on when an error + * occurs. The callback may also be called spuriously without reason. + * + * The callback may be called in an interrupt context and should not + * perform expensive operations such as recv/send calls. + * + * @param tptr Pointer to object to call method on + * @param mptr Method to call on state change + */ + template <typename T, typename M> + void attach(T *tptr, M mptr) { + attach(FunctionPointer(tptr, mptr)); + } + +protected: + Socket(); + int open(NetworkStack *iface, nsapi_protocol_t proto); + + static void thunk(void *); + static void wakeup(); + + NetworkStack *_iface; + void *_socket; + int _timeout; + FunctionPointer _callback; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/SocketAddress.cpp Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,275 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SocketAddress.h" +#include "NetworkStack.h" +#include <string.h> +#include "mbed.h" + + +static bool ipv4_is_valid(const char *addr) +{ + int i = 0; + + // Check each digit for [0-9.] + for (; addr[i]; i++) { + if (!(addr[i] >= '0' && addr[i] <= '9') && addr[i] != '.') { + return false; + } + } + + // Ending with '.' garuntees host + if (i > 0 && addr[i-1] == '.') { + return false; + } + + return true; +} + +static bool ipv6_is_valid(const char *addr) +{ + // Check each digit for [0-9a-fA-F:] + for (int i = 0; addr[i]; i++) { + if (!(addr[i] >= '0' && addr[i] <= '9') && + !(addr[i] >= 'a' && addr[i] <= 'f') && + !(addr[i] >= 'A' && addr[i] <= 'F') && + addr[i] != ':') { + return false; + } + } + + return true; +} + +static void ipv4_from_address(uint8_t *bytes, const char *addr) +{ + int count = 0; + int i = 0; + + for (; count < NSAPI_IPv4_BYTES; count++) { + int scanned = sscanf(&addr[i], "%hhu", &bytes[count]); + if (scanned < 1) { + return; + } + + for (; addr[i] != '.'; i++) { + if (!addr[i]) { + return; + } + } + + i++; + } +} + +static int ipv6_scan_chunk(uint16_t *shorts, const char *chunk) { + int count = 0; + int i = 0; + + for (; count < NSAPI_IPv6_BYTES/2; count++) { + int scanned = sscanf(&chunk[i], "%hx", &shorts[count]); + if (scanned < 1) { + return count; + } + + for (; chunk[i] != ':'; i++) { + if (!chunk[i]) { + return count+1; + } + } + + i++; + } + + return count; +} + +static void ipv6_from_address(uint8_t *bytes, const char *addr) +{ + // Start with zeroed address + uint16_t shorts[NSAPI_IPv6_BYTES/2]; + memset(shorts, 0, sizeof shorts); + + int suffix = 0; + + // Find double colons and scan suffix + for (int i = 0; addr[i]; i++) { + if (addr[i] == ':' && addr[i+1] == ':') { + suffix = ipv6_scan_chunk(shorts, &addr[i+2]); + break; + } + } + + // Move suffix to end + memmove(&shorts[NSAPI_IPv6_BYTES/2-suffix], &shorts[0], + suffix*sizeof(uint16_t)); + + // Scan prefix + ipv6_scan_chunk(shorts, &addr[0]); + + // Flip bytes + for (int i = 0; i < NSAPI_IPv6_BYTES/2; i++) { + bytes[2*i+0] = (uint8_t)(shorts[i] >> 8); + bytes[2*i+1] = (uint8_t)(shorts[i] >> 0); + } +} + +static void ipv4_to_address(char *addr, const uint8_t *bytes) +{ + sprintf(addr, "%d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3]); +} + +static void ipv6_to_address(char *addr, const uint8_t *bytes) +{ + for (int i = 0; i < NSAPI_IPv6_BYTES/2; i++) { + sprintf(&addr[5*i], "%02x%02x", bytes[2*i], bytes[2*i+1]); + addr[5*i+4] = ':'; + } + addr[NSAPI_IPv6_SIZE-1] = '\0'; +} + + +SocketAddress::SocketAddress(NetworkStack *iface, const char *host, uint16_t port) +{ + memset(&_ip_address, 0, sizeof _ip_address); + + // Check for valid IP addresses + if (host && ipv4_is_valid(host)) { + _ip_version = NSAPI_IPv4; + ipv4_from_address(_ip_bytes, host); + set_port(port); + } else if (host && ipv6_is_valid(host)) { + _ip_version = NSAPI_IPv6; + ipv6_from_address(_ip_bytes, host); + set_port(port); + } else { + // DNS lookup + int err = iface->gethostbyname(this, host); + if (!err) { + set_port(port); + } else { + _ip_version = NSAPI_IPv4; + memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); + set_port(0); + } + } +} + +SocketAddress::SocketAddress(const char *addr, uint16_t port) +{ + memset(&_ip_address, 0, sizeof _ip_address); + set_ip_address(addr); + set_port(port); +} + +SocketAddress::SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port) +{ + memset(&_ip_address, 0, sizeof _ip_address); + set_ip_bytes(bytes, version); + set_port(port); +} + +SocketAddress::SocketAddress(const SocketAddress &addr) +{ + memset(&_ip_address, 0, sizeof _ip_address); + set_ip_bytes(addr.get_ip_bytes(), addr.get_ip_version()); + set_port(addr.get_port()); +} + +void SocketAddress::set_ip_address(const char *addr) +{ + _ip_address[0] = '\0'; + + if (addr && ipv4_is_valid(addr)) { + _ip_version = NSAPI_IPv4; + ipv4_from_address(_ip_bytes, addr); + } else if (addr && ipv6_is_valid(addr)) { + _ip_version = NSAPI_IPv6; + ipv6_from_address(_ip_bytes, addr); + } else { + _ip_version = NSAPI_IPv4; + memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); + } +} + +void SocketAddress::set_ip_bytes(const void *bytes, nsapi_version_t version) +{ + _ip_address[0] = '\0'; + + if (version == NSAPI_IPv4) { + _ip_version = NSAPI_IPv4; + memcpy(_ip_bytes, bytes, NSAPI_IPv4_BYTES); + } else if (version == NSAPI_IPv6) { + _ip_version = NSAPI_IPv6; + memcpy(_ip_bytes, bytes, NSAPI_IPv6_BYTES); + } else { + _ip_version = NSAPI_IPv4; + memset(_ip_bytes, 0, NSAPI_IPv4_BYTES); + } +} + +void SocketAddress::set_port(uint16_t port) +{ + _port = port; +} + +const char *SocketAddress::get_ip_address() const +{ + char *ip_address = (char *)_ip_address; + + if (!ip_address[0]) { + if (_ip_version == NSAPI_IPv4) { + ipv4_to_address(ip_address, _ip_bytes); + } else if (_ip_version == NSAPI_IPv6) { + ipv6_to_address(ip_address, _ip_bytes); + } + } + + return ip_address; +} + +const void *SocketAddress::get_ip_bytes() const +{ + return _ip_bytes; +} + +nsapi_version_t SocketAddress::get_ip_version() const +{ + return _ip_version; +} + +uint16_t SocketAddress::get_port() const +{ + return _port; +} + +SocketAddress::operator bool() const +{ + int count = 0; + if (_ip_version == NSAPI_IPv4) { + count = NSAPI_IPv4_BYTES; + } else if (_ip_version == NSAPI_IPv6) { + count = NSAPI_IPv6_BYTES; + } + + for (int i = 0; i < count; i++) { + if (_ip_bytes[i]) { + return true; + } + } + + return false; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/SocketAddress.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,166 @@ +/* SocketAddress + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOCKET_ADDRESS_H +#define SOCKET_ADDRESS_H + +#include <stdint.h> + + +/** Maximum size of IP address representation + */ +#define NSAPI_IP_SIZE NSAPI_IPv6_SIZE + +/** Maximum number of bytes for IP address + */ +#define NSAPI_IP_BYTES NSAPI_IPv6_BYTES + +/** Maximum size of MAC address representation + */ +#define NSAPI_MAC_SIZE 18 + +/** Maximum number of bytes for MAC address + */ +#define NSAPI_MAC_BYTES 6 + +/** Enum of IP address versions + * + * The IP version specifies the type of an IP address. + * + * @enum nsapi_version_t + */ +enum nsapi_version_t { + NSAPI_IPv4, /*!< Address is IPv4 */ + NSAPI_IPv6, /*!< Address is IPv6 */ +}; + +/** Size of IPv4 representation + */ +#define NSAPI_IPv4_SIZE 16 + +/** Number of bytes in IPv4 address + */ +#define NSAPI_IPv4_BYTES 4 + +/** Size of IPv6 representation + */ +#define NSAPI_IPv6_SIZE 40 + +/** Number of bytes in IPv6 address + */ +#define NSAPI_IPv6_BYTES 16 + +// Predeclared classes +class NetworkStack; + + +/** SocketAddress class + * + * Representation of an IP address and port pair. + */ +class SocketAddress { +public: + /** Create a SocketAddress from a hostname and port + * + * The hostname may be either a domain name or an IP address. If the + * hostname is an IP address, no network transactions will be performed. + * + * On failure, the IP address and port will be set to zero + * + * @param iface Network stack to use for DNS resolution + * @param host Hostname to resolve + * @param port Optional 16-bit port + */ + SocketAddress(NetworkStack *iface, const char *host, uint16_t port = 0); + + /** Create a SocketAddress from an IP address and port + * + * @param host Null-terminated representation of the IP address + * @param port Optional 16-bit port + */ + SocketAddress(const char *addr = 0, uint16_t port = 0); + + /** Create a SocketAddress from a raw IP address and port + * + * @param bytes Raw IP address in big-endian order + * @param version IP address version, NSAPI_IPv4 or NSAPI_IPv6 + * @param port Optional 16-bit port + */ + SocketAddress(const void *bytes, nsapi_version_t version, uint16_t port = 0); + + /** Create a SocketAddress from another SocketAddress + * + * @param address SocketAddress to copy + */ + SocketAddress(const SocketAddress &addr); + + /** Set the IP address + * + * @param addr Null-terminated represention of the IP address + */ + void set_ip_address(const char *addr); + + /** Set the raw IP address + * + * @param bytes Raw IP address in big-endian order + * @param version IP address version, NSAPI_IPv4 or NSAPI_IPv6 + */ + void set_ip_bytes(const void *bytes, nsapi_version_t version); + + /** Set the port + * + * @param port 16-bit port + */ + void set_port(uint16_t port); + + /** Get the IP address + * + * @return Null-terminated representation of the IP Address + */ + const char *get_ip_address() const; + + /** Get the raw IP address + * + * @return Raw IP address in big-endian order + */ + const void *get_ip_bytes() const; + + /** Get the IP address version + * + * @return IP address version, NSAPI_IPv4 or NSAPI_IPv6 + */ + nsapi_version_t get_ip_version() const; + + /** Get the port + * + * @return The 16-bit port + */ + uint16_t get_port() const; + + /** Test if address is zero + * + * @return True if address is not zero + */ + operator bool() const; + +private: + char _ip_address[NSAPI_IP_SIZE]; + uint8_t _ip_bytes[NSAPI_IP_BYTES]; + nsapi_version_t _ip_version; + uint16_t _port; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/TCPServer.cpp Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,75 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TCPServer.h" +#include "Timer.h" + +TCPServer::TCPServer() +{ +} + +TCPServer::TCPServer(NetworkStack *iface) +{ + open(iface); +} + +int TCPServer::open(NetworkStack *iface) +{ + return Socket::open(iface, NSAPI_TCP); +} + +int TCPServer::listen(int backlog) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->socket_listen(_socket, backlog); +} + +int TCPServer::accept(TCPSocket *connection) +{ + mbed::Timer timer; + timer.start(); + mbed::Timeout timeout; + if (_timeout >= 0) { + timeout.attach_us(&Socket::wakeup, _timeout * 1000); + } + + if (connection->_socket) { + connection->close(); + } + + while (true) { + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + void *socket; + int err = _iface->socket_accept(&socket, _socket); + if (!err) { + connection->_iface = _iface; + connection->_socket = socket; + } + + if (err != NSAPI_ERROR_WOULD_BLOCK + || (_timeout >= 0 && timer.read_ms() >= _timeout)) { + return err; + } + + __WFI(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/TCPServer.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,79 @@ +/* TCPServer + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TCPSERVER_H +#define TCPSERVER_H + +#include "Socket.h" +#include "TCPSocket.h" +#include "NetworkStack.h" + +/** TCP socket server + */ +class TCPServer : public Socket { +public: + /** Create an uninitialized socket + * + * Must call open to initialize the socket on a network stack. + */ + TCPServer(); + + /** Create a socket on a network stack + * + * Creates and opens a socket on the specified network stack. + * + * @param iface Network stack as target for socket + */ + TCPServer(NetworkStack *iface); + + /** Opens a socket + * + * Creates a network socket on the specified network stack. + * Not needed if stack is passed to the socket's constructor. + * + * @param iface Network stack as target for socket + * @return 0 on success, negative error code on failure + */ + virtual int open(NetworkStack *iface); + + /** Listen for connections on a TCP socket + * + * Marks the socket as a passive socket that can be used to accept + * incoming connections. + * + * @param backlog Number of pending connections that can be queued + * simultaneously, defaults to 1 + * @return 0 on success, negative error code on failure + */ + int listen(int backlog = 1); + + /** Accepts a connection on a TCP socket + * + * The server socket must be bound and set to listen for connections. + * On a new connection, creates a network socket using the specified + * socket instance. + * + * By default, accept blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param socket TCPSocket instance that will handle the incoming connection. + * @return 0 on success, negative error code on failure + */ + int accept(TCPSocket *connection); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/TCPSocket.cpp Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,100 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TCPSocket.h" +#include "Timer.h" + +TCPSocket::TCPSocket(bool isTLS) : _isTLS(isTLS) +{ +} + +TCPSocket::TCPSocket(NetworkStack *iface) +{ + open(iface); +} + +int TCPSocket::open(NetworkStack *iface) +{ + return Socket::open(iface, _isTLS ? NSAPI_TLS : NSAPI_TCP); +} + +int TCPSocket::connect(const SocketAddress &addr) +{ + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + return _iface->socket_connect(_socket, addr); +} + + +int TCPSocket::connect(const char *host, uint16_t port) +{ + SocketAddress addr(_iface, host, port); + if (!addr) { + return NSAPI_ERROR_DNS_FAILURE; + } + + return connect(addr); +} + +int TCPSocket::send(const void *data, unsigned size) +{ + mbed::Timer timer; + timer.start(); + mbed::Timeout timeout; + if (_timeout >= 0) { + timeout.attach_us(&Socket::wakeup, _timeout * 1000); + } + + while (true) { + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int sent = _iface->socket_send(_socket, data, size); + if (sent != NSAPI_ERROR_WOULD_BLOCK + || (_timeout >= 0 && timer.read_ms() >= _timeout)) { + return sent; + } + + __WFI(); + } +} + +int TCPSocket::recv(void *data, unsigned size) +{ + mbed::Timer timer; + timer.start(); + mbed::Timeout timeout; + if (_timeout >= 0) { + timeout.attach_us(&Socket::wakeup, _timeout * 1000); + } + + while (true) { + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int recv = _iface->socket_recv(_socket, data, size); + if (recv != NSAPI_ERROR_WOULD_BLOCK + || (_timeout >= 0 && timer.read_ms() >= _timeout)) { + return recv; + } + + __WFI(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/TCPSocket.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,110 @@ +/* TCPSocket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TCPSOCKET_H +#define TCPSOCKET_H + +#include "Socket.h" +#include "NetworkStack.h" + +/** TCP socket connection + */ +class TCPSocket : public Socket { +public: + /** Create an uninitialized socket + * + * Must call open to initialize the socket on a network stack. + */ + TCPSocket(bool isTLS = false); + + /** Create a socket on a network stack + * + * Creates and opens a socket on the specified network stack. + * + * @param iface Network stack as target for socket + */ + TCPSocket(NetworkStack *iface); + + /** Opens a socket + * + * Creates a network socket on the specified network stack. + * Not needed if stack is passed to the socket's constructor. + * + * @param iface Network stack as target for socket + * @return 0 on success, negative error code on failure + */ + virtual int open(NetworkStack *iface); + + /** Connects TCP socket to a remote host + * + * Initiates a connection to a remote server specified by either + * a domain name or an IP address and a port. + * + * @param host Hostname of the remote host + * @param port Port of the remote host + * @return 0 on success, negative error code on failure + */ + int connect(const char *host, uint16_t port); + + /** Connects TCP socket to a remote host + * + * Initiates a connection to a remote server specified by the + * indicated address. + * + * @param address The SocketAddress of the remote host + * @return 0 on success, negative error code on failure + */ + int connect(const SocketAddress &address); + + /** Send data over a TCP socket + * + * The socket must be connected to a remote host. Returns the number of + * bytes sent from the buffer. + * + * By default, send blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure + */ + int send(const void *data, unsigned size); + + /** Receive data over a TCP socket + * + * The socket must be connected to a remote host. Returns the number of + * bytes received into the buffer. + * + * By default, recv blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure + */ + int recv(void *data, unsigned size); + +private: + friend class TCPServer; + + bool _isTLS; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/UDPSocket.cpp Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,90 @@ +/* Socket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "UDPSocket.h" +#include "Timer.h" + +UDPSocket::UDPSocket() +{ +} + +UDPSocket::UDPSocket(NetworkStack *iface) +{ + open(iface); +} + +int UDPSocket::open(NetworkStack *iface) +{ + return Socket::open(iface, NSAPI_UDP); +} + +int UDPSocket::sendto(const char *host, uint16_t port, const void *data, unsigned size) +{ + SocketAddress addr(_iface, host, port); + if (!addr) { + return NSAPI_ERROR_DNS_FAILURE; + } + + return sendto(addr, data, size); +} + +int UDPSocket::sendto(const SocketAddress &address, const void *data, unsigned size) +{ + mbed::Timer timer; + timer.start(); + mbed::Timeout timeout; + if (_timeout >= 0) { + timeout.attach_us(&Socket::wakeup, _timeout * 1000); + } + + while (true) { + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int sent = _iface->socket_sendto(_socket, address, data, size); + if (sent != NSAPI_ERROR_WOULD_BLOCK + || (_timeout >= 0 && timer.read_ms() >= _timeout)) { + return sent; + } + + __WFI(); + } +} + +int UDPSocket::recvfrom(SocketAddress *address, void *buffer, unsigned size) +{ + mbed::Timer timer; + timer.start(); + mbed::Timeout timeout; + if (_timeout >= 0) { + timeout.attach_us(&Socket::wakeup, _timeout * 1000); + } + + while (true) { + if (!_socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + int recv = _iface->socket_recvfrom(_socket, address, buffer, size); + if (recv != NSAPI_ERROR_WOULD_BLOCK + || (_timeout >= 0 && timer.read_ms() >= _timeout)) { + return recv; + } + + __WFI(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/UDPSocket.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,105 @@ +/* UDPSocket + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UDPSOCKET_H +#define UDPSOCKET_H + +#include "Socket.h" +#include "NetworkStack.h" + +/** UDP socket + */ +class UDPSocket : public Socket { +public: + /** Create an uninitialized socket + * + * Must call open to initialize the socket on a network stack. + */ + UDPSocket(); + + /** Create a socket on a network stack + * + * Creates and opens a socket on the specified network stack. + * + * @param iface Network stack as target for socket + */ + UDPSocket(NetworkStack *iface); + + /** Opens a socket + * + * Creates a network socket on the specified network stack. + * Not needed if stack is passed to the socket's constructor. + * + * @param iface Network stack as target for socket + * @return 0 on success, negative error code on failure + */ + virtual int open(NetworkStack *iface); + + /** Send a packet over a UDP socket + * + * Sends data to the specified address specified by either a domain name + * or an IP address and port. Returns the number of bytes sent from the + * buffer. + * + * By default, sendto blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param host Hostname of the remote host + * @param port Port of the remote host + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure + */ + int sendto(const char *host, uint16_t port, const void *data, unsigned size); + + /** Send a packet over a UDP socket + * + * Sends data to the specified address. Returns the number of bytes + * sent from the buffer. + * + * By default, sendto blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param address The SocketAddress of the remote host + * @param data Buffer of data to send to the host + * @param size Size of the buffer in bytes + * @return Number of sent bytes on success, negative error + * code on failure + */ + int sendto(const SocketAddress &address, const void *data, unsigned size); + + /** Receive a packet over a UDP socket + * + * Receives data and stores the source address in address if address + * is not NULL. Returns the number of bytes received into the buffer. + * + * By default, recvfrom blocks until data is sent. If socket is set to + * non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned + * immediately. + * + * @param address Destination for the source address or NULL + * @param data Destination buffer for data received from the host + * @param size Size of the buffer in bytes + * @return Number of received bytes on success, negative error + * code on failure + */ + int recvfrom(SocketAddress *address, void *data, unsigned size); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NetworkSocketAPI/WiFiInterface.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,68 @@ +/* WiFiInterface + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WIFI_INTERFACE_H +#define WIFI_INTERFACE_H + +#include "NetworkStack.h" + +/** Enum of WiFi encryption types + * + * The security type specifies a particular security to use when + * connected to a WiFi network + * + * @enum nsapi_protocol_t + */ +enum nsapi_security_t { + NSAPI_SECURITY_NONE = 0, /*!< open access point */ + NSAPI_SECURITY_WEP, /*!< phrase conforms to WEP */ + NSAPI_SECURITY_WPA, /*!< phrase conforms to WPA */ + NSAPI_SECURITY_WPA2, /*!< phrase conforms to WPA2 */ +}; + +/** WiFiInterface class + * + * Common interface that is shared between WiFi devices + */ +class WiFiInterface +{ +public: + /** Start the interface + * + * Attempts to connect to a WiFi network. If passphrase is invalid, + * NSAPI_ERROR_AUTH_ERROR is returned. + * + * @param ssid Name of the network to connect to + * @param pass Security passphrase to connect to the network + * @param security Type of encryption for connection + * @return 0 on success, negative error code on failure + */ + virtual int connect(const char *ssid, const char *pass, nsapi_security_t security = NSAPI_SECURITY_NONE) = 0; + + /** Stop the interface + * + * @return 0 on success, negative error code on failure + */ + virtual int disconnect() = 0; + + /** Get the local MAC address + * + * @return Null-terminated representation of the local MAC address + */ + virtual const char *get_mac_address() = 0; +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/X_NUCLEO_IDW01M1v2/NOTICE.txt Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,7 @@ +This library is originated from: https://developer.mbed.org/teams/ST/code/X_NUCLEO_IDW01M1v2/#0368732b5b9d +"class SpwfSAInterface" is extended with: + - method to setup WiFi module security parameters + - method to initiate hardware reset of WiFi module + - security socket support + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/X_NUCLEO_IDW01M1v2/SPWF01SA/ATParser.lib Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,1 @@ +https://developer.mbed.org/teams/ST/code/ATParser/#ea155e6b1fb1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/X_NUCLEO_IDW01M1v2/SPWF01SA/NOTICE.txt Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,12 @@ +This library is originated from https://developer.mbed.org/teams/ST/code/SPWF01SA/#419285201dba +It is extended with: + - TLS parameters setup + - Increased ATParser buffer size + - next wifi parameters are hardcoded to obtain higher robustness of WiFi Module: + * high-speed transfer mode is switched off + * operational rate is limited to 1 Mbit + * basic data rates are limited to 802.11ab + * transmit power is forced to maximum + * power saving and sleep mode is switched off + * dhcp is forced to be switched on + * dhcp timeout is forced to 20 seconds
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/X_NUCLEO_IDW01M1v2/SPWF01SA/SPWFSA01.cpp Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,492 @@ +/* SPWFInterface Example + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SPWFSA01.h" +#include "mbed_debug.h" +#include "stdint.h" + +#define SPWFSA01_CONNECT_TIMEOUT 15000 +#define SPWFSA01_SEND_TIMEOUT 500 +#define SPWFSA01_RECV_TIMEOUT 1500//some commands like AT&F/W takes some time to get the result back! +#define SPWFSA01_MISC_TIMEOUT 500 +#define SPWFSA01_SOCKQ_TIMEOUT 3000 + +#define EPOCH_TIME 1453727657//Human time (GMT): Mon, 25 Jan 2016 13:14:17 GMT + + SPWFSA01::SPWFSA01(PinName tx, PinName rx, PinName reset, PinName wakeup, bool debug) + : _serial(tx, rx, 1024), _parser(_serial,"\r\n", 2048), + _wakeup(wakeup, PIN_OUTPUT, PullNone, 0), + _reset(reset, PIN_OUTPUT, PullNone, 1), + dbg_on(debug) +{ + _serial.baud(115200); // LICIO FIXME increase the speed + _parser.debugOn(debug); +} + +bool SPWFSA01::startup(int mode) +{ + _parser.setTimeout(SPWFSA01_MISC_TIMEOUT); + /*Test module before reset*/ + waitSPWFReady(); + /*Reset module*/ + reset(); + + /*set local echo to 0*/ + if(!(_parser.send("AT+S.SCFG=localecho1,%d\r", 0) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error local echo set\r\n"); + return false; + } + /*reset factory settings*/ + if(!(_parser.send("AT&F") && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error AT&F\r\n"); + return false; + } + + /*set Wi-Fi mode and rate to b/g*/ + if(!(_parser.send("AT+S.SCFG=wifi_ht_mode,%d\r", 0) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error setting ht_mode\r\n"); + return false; + } + + //if(!(_parser.send("AT+S.SCFG=wifi_opr_rate_mask,0x003FFFCF\r") && _parser.recv("OK"))) + if(!(_parser.send("AT+S.SCFG=wifi_opr_rate_mask,0x00000001\r") && _parser.recv("OK")))// set most effective speed for such kind of device + { + debug_if(dbg_on, "SPWF> error setting operational rates\r\n"); + return false; + } + + if(!(_parser.send("AT+S.SCFG=wifi_bas_rate_mask,0x0000000F\r") && _parser.recv("OK")))// set most effective speed for such kind of device + { + debug_if(dbg_on, "SPWF> error setting basic rates\r\n"); + return false; + } + + if(!(_parser.send("AT+S.SCFG=wifi_powersave,0\r") && _parser.recv("OK")))// set most effective speed for such kind of device + { + debug_if(dbg_on, "SPWF> error setting power save mode\r\n"); + return false; + } + + if(!(_parser.send("AT+S.SCFG=sleep_enabled,0\r") && _parser.recv("OK")))// set most effective speed for such kind of device + { + debug_if(dbg_on, "SPWF> error setting sleep mode\r\n"); + return false; + } + + if(!(_parser.send("AT+S.SCFG=wifi_tx_power,18\r") && _parser.recv("OK")))// set most effective speed for such kind of device + { + debug_if(dbg_on, "SPWF> error setting transmit power\r\n"); + return false; + } + + if(!(_parser.send("AT+S.SCFG=ip_use_dhcp,1\r") && _parser.recv("OK")))// set most effective speed for such kind of device + { + debug_if(dbg_on, "SPWF> error setting dhcp on\r\n"); + return false; + } + + if(!(_parser.send("AT+S.SCFG=ip_dhcp_timeout,20\r") && _parser.recv("OK")))// set most effective speed for such kind of device + { + debug_if(dbg_on, "SPWF> error setting dhcp timeout\r\n"); + return false; + } + + /*set idle mode (0->idle, 1->STA,3->miniAP, 2->IBSS)*/ + if(!(_parser.send("AT+S.SCFG=wifi_mode,%d\r", mode) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error wifi mode set\r\n"); + return false; + } + + /* save current setting in flash */ + if(!(_parser.send("AT&W") && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error AT&W\r\n"); + return false; + } + + /*reset again and send AT command and check for result (AT->OK)*/ + reset(); + + return true; +} + +bool SPWFSA01::hw_reset(void) +{ + if (_reset.is_connected()) { + /* reset the pin PC12 */ + _reset.write(0); + wait_ms(200); + _reset.write(1); + wait_ms(100); + return 1; + } else { return 0; } +} + +bool SPWFSA01::reset(void) +{ + if(!_parser.send("AT+CFUN=1")) return false; + while(1) { + if (_parser.recv("+WIND:32:WiFi Hardware Started\r")) { + return true; + } + } +} + +void SPWFSA01::waitSPWFReady(void) +{ + //wait_ms(200); + while(1) + if(_parser.send("AT") && _parser.recv("OK")) + //till we get OK from AT command + //printf("\r\nwaiting for reset to complete..\n"); + return; + +} + +/* Security Mode + None = 0, + WEP = 1, + WPA_Personal = 2, +*/ +bool SPWFSA01::connect(const char *ap, const char *passPhrase, int securityMode) +{ + uint32_t n1, n2, n3, n4; + + _parser.setTimeout(SPWFSA01_CONNECT_TIMEOUT); + //AT+S.SCFG=wifi_wpa_psk_text,%s\r + if(!(_parser.send("AT+S.SCFG=wifi_wpa_psk_text,%s", passPhrase) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error pass set\r\n"); + return false; + } + //AT+S.SSIDTXT=%s\r + if(!(_parser.send("AT+S.SSIDTXT=%s", ap) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error ssid set\r\n"); + return false; + } + //AT+S.SCFG=wifi_priv_mode,%d\r + if(!(_parser.send("AT+S.SCFG=wifi_priv_mode,%d", securityMode) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error security mode set\r\n"); + return false; + } + //"AT+S.SCFG=wifi_mode,%d\r" + /*set idle mode (0->idle, 1->STA,3->miniAP, 2->IBSS)*/ + if(!(_parser.send("AT+S.SCFG=wifi_mode,%d\r", 1) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error wifi mode set\r\n"); + return false; + } + //AT&W + /* save current setting in flash */ + if(!(_parser.send("AT&W") && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error AT&W\r\n"); + return false; + } + //reset module + reset(); + + while(1) + if((_parser.recv("+WIND:24:WiFi Up:%u.%u.%u.%u",&n1, &n2, &n3, &n4))) + { + break; + } + + return true; +} + +bool SPWFSA01::disconnect(void) +{ + //"AT+S.SCFG=wifi_mode,%d\r" + /*set idle mode (0->idle, 1->STA,3->miniAP, 2->IBSS)*/ + if(!(_parser.send("AT+S.SCFG=wifi_mode,%d\r", 0) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error wifi mode set\r\n"); + return false; + } + //AT&W + /* save current setting in flash */ + if(!(_parser.send("AT&W") && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error AT&W\r\n"); + return false; + } + //reset module + reset(); + return true; +} + +bool SPWFSA01::dhcp(int mode) +{ + //only 3 valid modes + //0->off(ip_addr must be set by user), 1->on(auto set by AP), 2->on&customize(miniAP ip_addr can be set by user) + if(mode < 0 || mode > 2) { + return false; + } + + return _parser.send("AT+S.SCFG=ip_use_dhcp,%d\r", mode) + && _parser.recv("OK"); +} + + +const char *SPWFSA01::getIPAddress(void) +{ + uint32_t n1, n2, n3, n4; + + if (!(_parser.send("AT+S.STS=ip_ipaddr") + && _parser.recv("# ip_ipaddr = %u.%u.%u.%u", &n1, &n2, &n3, &n4) + && _parser.recv("OK"))) { + debug_if(dbg_on, "SPWF> getIPAddress error\r\n"); + return 0; + } + + sprintf((char*)_ip_buffer,"%u.%u.%u.%u", n1, n2, n3, n4); + + return _ip_buffer; +} + +const char *SPWFSA01::getMACAddress(void) +{ + uint32_t n1, n2, n3, n4, n5, n6; + + if (!(_parser.send("AT+S.GCFG=nv_wifi_macaddr") + && _parser.recv("# nv_wifi_macaddr = %x:%x:%x:%x:%x:%x", &n1, &n2, &n3, &n4, &n5, &n6) + && _parser.recv("OK"))) { + debug_if(dbg_on, "SPWF> getMACAddress error\r\n"); + return 0; + } + + sprintf((char*)_mac_buffer,"%02X:%02X:%02X:%02X:%02X:%02X", n1, n2, n3, n4, n5, n6); + + return _mac_buffer; +} + +bool SPWFSA01::isConnected(void) +{ + return getIPAddress() != 0; +} + +bool SPWFSA01::open(const char *type, int* id, const char* addr, int port) +{ + Timer timer; + timer.start(); + socket_closed = 0; + + if(!_parser.send("AT+S.SOCKON=%s,%d,%s,ind", addr, port, type)) + { + debug_if(dbg_on, "SPWF> error opening socket\r\n"); + return false; + } + + while(1) + { + if( _parser.recv(" ID: %d", id) + && _parser.recv("OK")) + break; + + if (timer.read_ms() > SPWFSA01_CONNECT_TIMEOUT) { + return false; + } + + //TODO:implement time-out functionality in case of no response + //if(timeout) return false; + //TODO: deal with errors like "ERROR: Failed to resolve name" + //TODO: deal with errors like "ERROR: Data mode not available" + } + + return true; +} + +bool SPWFSA01::send(int id, const void *data, uint32_t amount) +{ + char _buf[18]; + _parser.setTimeout(SPWFSA01_SEND_TIMEOUT); + + sprintf((char*)_buf,"AT+S.SOCKW=%d,%d\r", id, amount); + + //May take a second try if device is busy + for (unsigned i = 0; i < 2; i++) { + if (_parser.write((char*)_buf, strlen(_buf)) >=0 + && _parser.write((char*)data, (int)amount) >= 0 + && _parser.recv("OK")) { + return true; + } + } + return false; +} + + +int32_t SPWFSA01::recv(int id, void *data, uint32_t amount) +{ + uint32_t recv_amount=0; + int wind_id; + + if (socket_closed) { + socket_closed = 0; + return -3; + } + if(!(_parser.send("AT+S.SOCKQ=%d", id) //send a query (will be required for secure sockets) + && _parser.recv(" DATALEN: %u", &recv_amount) + && _parser.recv("OK"))) { + return -2; + } + if (recv_amount==0) { return -1; } + if(recv_amount > amount) + recv_amount = amount; + + int par_timeout = _parser.getTimeout(); + _parser.setTimeout(0); + + while(_parser.recv("+WIND:%d:", &wind_id)) { +// printf("Wind received: %d\n\r", wind_id); + if (wind_id == 58) { + socket_closed = 1; + _parser.flush(); + } + } + _parser.setTimeout(par_timeout); + + _parser.flush(); + if(!(_parser.send("AT+S.SOCKR=%d,%d", id, recv_amount))){ + return -2; + } + if(!((_parser.read((char*)data, recv_amount) >0) + && _parser.recv("OK"))) { + return -2; + } + return recv_amount; +} + +bool SPWFSA01::close(int id) +{ + uint32_t recv_amount=0; + void * data = NULL; + + _parser.setTimeout(SPWFSA01_MISC_TIMEOUT); + _parser.flush(); + /* socket flush */ + if(!(_parser.send("AT+S.SOCKQ=%d", id) //send a query (will be required for secure sockets) + && _parser.recv(" DATALEN: %u", &recv_amount) + && _parser.recv("OK"))) { + return -2; + } + if (recv_amount>0) { + data = malloc (recv_amount+4); + if(!(_parser.send("AT+S.SOCKR=%d,%d", id, recv_amount))) { + free (data); + return -2; + } + // printf ("--->>>Close flushing recv_amount: %d \n\r",recv_amount); + if(!((_parser.read((char*)data, recv_amount) >0) + && _parser.recv("OK"))) { + free (data); + return -2; + } + free (data); + } + + //May take a second try if device is busy or error is returned + for (unsigned i = 0; i < 2; i++) { + if (_parser.send("AT+S.SOCKC=%d", id) + && _parser.recv("OK")) { + socket_closed = 1; + return true; + } + else + { + if(_parser.recv("ERROR: Pending data")) { + debug_if(dbg_on, "SPWF> ERROR!!!!\r\n"); + return false; + } + } + //TODO: Deal with "ERROR: Pending data" (Closing a socket with pending data) + } + return false; +} + + +bool SPWFSA01::readable() +{ + return _serial.readable(); +} + +bool SPWFSA01::writeable() +{ + return _serial.writeable(); +} + +int SPWFSA01::setSocketClientSecurity(uint8_t* tls_mode, uint8_t* root_ca_server, uint8_t* client_cert, uint8_t* client_key, uint8_t* client_domain, uint32_t tls_epoch_time) +{ + int err = 0; + unsigned long epoch_time; + + if(!(_parser.send("AT+S.TLSCERT2=clean,all\r", 0) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> TLSCERT2 cleared\r\n"); + return -1; + } + + if(tls_epoch_time==0) + epoch_time = EPOCH_TIME; + else + epoch_time = tls_epoch_time; + + if(!(_parser.send("AT+S.SETTIME=%lu\r", (unsigned long)epoch_time) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> time is set to %d\r\n", epoch_time); + return -1; + } + + /*AT+S.TLSCERT=f_ca,<size><CR><data>*/ + if(!(_parser.send("AT+S.TLSCERT=f_ca,%d\r%s\r", strlen((const char *)root_ca_server) - 1, root_ca_server) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error TLSCERT=f_ca set\r\n"); + return -1; + } + + /*AT+S.TLSCERT=f_cert,<size><CR><data>*/ + if(tls_mode[0]=='m') + { + if(!(_parser.send("AT+S.TLSCERT=f_cert,%d\r%s", strlen((const char *)client_cert) - 1, client_cert) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error TLSCERT=f_cert set\r\n"); + return -1; + } + + /*AT+S.TLSCERT=f_key,<size><CR><data>*/ + if(!(_parser.send("AT+S.TLSCERT=f_key,%d\r%s", strlen((const char *)client_key) - 1, client_key) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error TLSCERT=f_key set\r\n"); + return -1; + } + } + + /*AT+S.TLSDOMAIN=f_domain,<server domain>*/ + if(!(_parser.send("AT+S.TLSDOMAIN=f_domain,%s\r", client_domain) && _parser.recv("OK"))) + { + debug_if(dbg_on, "SPWF> error TLSDOMAIN=f_domain set\r\n"); + return -1; + } + + return err; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/X_NUCLEO_IDW01M1v2/SPWF01SA/SPWFSA01.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,168 @@ +/* SPWFInterface Example + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPWFSA01_H +#define SPWFSA01_H + +#include "ATParser.h" + +/** SPWFSA01Interface class. + This is an interface to a SPWFSA01 module. + */ +class SPWFSA01 +{ +public: + + SPWFSA01(PinName tx, PinName rx, PinName reset=NC, PinName wakeup=NC, bool debug=false); + + /** + * Init the SPWFSA01 + * + * @param mode mode in which to startup + * @return true only if SPWFSA01 has started up correctly + */ + bool startup(int mode); + + void waitSPWFReady(void); + /** + * Reset SPWFSA01 + * + * @return true only if SPWFSA01 resets successfully + */ + bool reset(void); + + bool hw_reset(void); + + /** + * Enable/Disable DHCP + * + * @param mode mode of DHCP 2-softAP, 1-on, 0-off + * @return true only if SPWFSA01 enables/disables DHCP successfully + */ + bool dhcp(int mode); + + /** + * Connect SPWFSA01 to AP + * + * @param ap the name of the AP + * @param passPhrase the password of AP + * @param securityMode the security mode of AP (WPA/WPA2, WEP, Open) + * @return true only if SPWFSA01 is connected successfully + */ + bool connect(const char *ap, const char *passPhrase, int securityMode); + + /** + * Disconnect SPWFSA01 from AP + * + * @return true only if SPWFSA01 is disconnected successfully + */ + bool disconnect(void); + + /** + * Get the IP address of SPWFSA01 + * + * @return null-teriminated IP address or null if no IP address is assigned + */ + const char *getIPAddress(void); + + /** + * Get the MAC address of SPWFSA01 + * + * @return null-terminated MAC address or null if no MAC address is assigned + */ + const char *getMACAddress(void); + + /** + * Check if SPWFSA01 is conenected + * + * @return true only if the chip has an IP address + */ + bool isConnected(void); + + /** + * @brief wifi_socket_client_security + * Set the security certificates and key for secure socket (TLS) + * @param None + * @retval return nonzero in case of error + */ + int setSocketClientSecurity(uint8_t* tls_mode, uint8_t* root_ca_server, uint8_t* client_cert, uint8_t* client_key, uint8_t* client_domain, uint32_t tls_epoch_time); + + /** + * Open a socketed connection + * + * @param type the type of socket to open "u" (UDP) or "t" (TCP) + * @param id id to get the new socket number, valid 0-7 + * @param port port to open connection with + * @param addr the IP address of the destination + * @return true only if socket opened successfully + */ + bool open(const char *type, int* id, const char* addr, int port); + + /** + * Sends data to an open socket + * + * @param id id of socket to send to + * @param data data to be sent + * @param amount amount of data to be sent - max 1024 + * @return true only if data sent successfully + */ + bool send(int id, const void *data, uint32_t amount); + + /** + * Receives data from an open socket + * + * @param id id to receive from + * @param data placeholder for returned information + * @param amount number of bytes to be received + * @return the number of bytes received + */ + int32_t recv(int id, void *data, uint32_t amount); + + /** + * Closes a socket + * + * @param id id of socket to close, valid only 0-4 + * @return true only if socket is closed successfully + */ + bool close(int id); + + + /** + * Checks if data is available + */ + bool readable(); + + /** + * Checks if data can be written + */ + bool writeable(); + +private: + BufferedSerial _serial; + ATParser _parser; + DigitalInOut _wakeup; + DigitalInOut _reset; + char _ip_buffer[16]; + char _mac_buffer[18]; + bool dbg_on; +// int _timeout; // FIXME LICIO we have "virtual" socket tmo, module socket tmo, +// AT parser tmo, recv/send tmo, actually used the NetworksocketAPI socket tmo + unsigned int _recv_timeout; // see SO_RCVTIMEO setsockopt + unsigned int _send_timeout; // see SO_SNDTIMEO setsockopt + unsigned int socket_closed; +}; + +#endif //SPWFSA01_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/X_NUCLEO_IDW01M1v2/SpwfInterface.cpp Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,464 @@ +/* mbed Microcontroller Library +* Copyright (c) 20015 ARM Limited +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** + ****************************************************************************** + * @file SpwfInterface.cpp + * @author STMicroelectronics + * @brief Implementation of the NetworkStack for the SPWF Device + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * <h2><center>© COPYRIGHT 2016 STMicroelectronics</center></h2> + ****************************************************************************** + */ + +#include "SpwfInterface.h" + +// Various timeouts for different SPWF operations +#define SPWF_CONNECT_TIMEOUT 20000 +#define SPWF_SEND_TIMEOUT 500 +#define SPWF_RECV_TIMEOUT 500 +#define SPWF_MISC_TIMEOUT 15000 + +/** spwf_socket class + * Implementation of SPWF socket structure + */ +struct spwf_socket { + int id; + int server_port; + nsapi_protocol_t proto; + bool connected; +}; + + +/** +* @brief SpwfSAInterface constructor +* @param tx: Pin USART TX +* rx: Pin USART RX +* rst: reset pin for Spwf module +* wkup: reset pin for Spwf module +* rts: Pin USART RTS +* debug : not used +* @retval none +*/ +SpwfSAInterface::SpwfSAInterface(PinName tx, PinName rx, bool debug) + : _spwf(tx, rx, PC_12, PC_8, debug) +{ + memset(_ids, 0, sizeof(_ids)); + isInitialized = false; + isListening = false; +} + +SpwfSAInterface::SpwfSAInterface(PinName tx, PinName rx, PinName reset, PinName wakeup, bool debug) + : _spwf(tx, rx, reset, wakeup, debug) +{ + memset(_ids, 0, sizeof(_ids)); + isInitialized = false; + isListening = false; +} + +/** +* @brief SpwfSAInterface destructor +* @param none +* @retval none +*/ +SpwfSAInterface::~SpwfSAInterface() +{ +} + +/** +* @brief init function + initializes SPWF FW and module +* @param none +* @retval error value +*/ +int SpwfSAInterface::init(void) +{ + if(_spwf.startup(0)) { + isInitialized=true; + return true; + } + else return NSAPI_ERROR_DEVICE_ERROR; +} + +/** +* @brief network connect + connects to Access Point +* @param ap: Access Point (AP) Name String +* pass_phrase: Password String for AP +* security: type of NSAPI security supported +* @retval NSAPI Error Type +*/ +int SpwfSAInterface::connect(const char *ap, + const char *pass_phrase, + nsapi_security_t security) +{ + int mode; + + //initialize the device before connecting + if(!isInitialized) + { + if(!init()) + return NSAPI_ERROR_DEVICE_ERROR; + } + + switch(security) + { + case NSAPI_SECURITY_NONE: + mode = 0; + pass_phrase = NULL; + break; + case NSAPI_SECURITY_WEP: + mode = 1; + break; + case NSAPI_SECURITY_WPA: + case NSAPI_SECURITY_WPA2: + mode = 2; + break; + default: + mode = 2; + break; + } + return (_spwf.connect((char*)ap, (char*)pass_phrase, mode)); +} + +/** +* @brief network disconnect + disconnects from Access Point +* @param none +* @retval NSAPI Error Type +*/ +int SpwfSAInterface::disconnect() +{ + return (_spwf.disconnect()); +} + +/** +* @brief Get the local IP address +* @param none +* @retval Null-terminated representation of the local IP address +* or null if not yet connected +*/ +const char *SpwfSAInterface::get_ip_address() +{ + return _spwf.getIPAddress(); +} + +/** +* @brief Get the MAC address +* @param none +* @retval Null-terminated representation of the MAC address +* or null if not yet connected +*/ +const char *SpwfSAInterface::get_mac_address() +{ + return _spwf.getMACAddress(); +} + +/** +* @brief open a socket handle +* @param handle: Pointer to handle +* proto: TCP/UDP protocol +* @retval NSAPI Error Type +*/ +int SpwfSAInterface::socket_open(void **handle, nsapi_protocol_t proto) +{ + int id = -1; + + struct spwf_socket *socket = new struct spwf_socket; + if (!socket) { + return NSAPI_ERROR_NO_SOCKET; + } + + socket->id = id; + socket->server_port = id; + socket->proto = proto; + socket->connected = false; + *handle = socket; + return 0; +} + +/** +* @brief connect to a remote socket +* @param handle: Pointer to socket handle +* addr: Address to connect to +* @retval NSAPI Error Type +*/ +int SpwfSAInterface::socket_connect(void *handle, const SocketAddress &addr) +{ + int sock_id = 99; + struct spwf_socket *socket = (struct spwf_socket *)handle; + const char *proto; + + switch (socket->proto) + { + case NSAPI_UDP: + proto = "u"; + break; + + case NSAPI_TCP: + proto = "t"; + break; + + case NSAPI_TLS: + proto = "s"; + break; + default: + return NSAPI_ERROR_UNSUPPORTED; + break;// defensive programming + } + + if (!_spwf.open(proto, &sock_id, addr.get_ip_address(), addr.get_port())) {;//sock ID is allocated NOW + return NSAPI_ERROR_DEVICE_ERROR; + } + + //TODO: Maintain a socket table to map socket ID to host & port + //TODO: lookup on client table to see if already socket is allocated to same host/port + //multimap <char *, vector <uint16_t> > ::iterator i = c_table.find((char*)ip); + + if(sock_id <= SPWFSA_SOCKET_COUNT) + { + socket->id = sock_id;//the socket ID of this Socket instance + _ids[socket->id] = true; + socket->connected = true; + } + else + return NSAPI_ERROR_NO_SOCKET; + + return 0; +} + +/** +* @brief bind to a port number and address +* @param handle: Pointer to socket handle +* proto: address to bind to +* @retval NSAPI Error Type +*/ +int SpwfSAInterface::socket_bind(void *handle, const SocketAddress &address) +{ + struct spwf_socket *socket = (struct spwf_socket *)handle; + socket->server_port = address.get_port(); + return 0; +} + +/** +* @brief start listening on a port and address +* @param handle: Pointer to handle +* backlog: not used (always value is 1) +* @retval NSAPI Error Type +*/ +int SpwfSAInterface::socket_listen(void *handle, int backlog) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +/** +* @brief accept connections from remote sockets +* @param handle: Pointer to handle of client socket (connecting) +* proto: handle of server socket which will accept connections +* @retval NSAPI Error Type +*/ +int SpwfSAInterface::socket_accept(void **handle, void *server) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +/** +* @brief close a socket +* @param handle: Pointer to handle +* @retval NSAPI Error Type +*/ +int SpwfSAInterface::socket_close(void *handle) +{ + struct spwf_socket *socket = (struct spwf_socket *)handle; + int err = 0; + + if(socket->id!=-1) + { + if (_spwf.close(socket->id)) { + if(socket->id==SERVER_SOCKET_NO) + isListening = false; + else + _ids[socket->id] = false; + } + else err = NSAPI_ERROR_DEVICE_ERROR; + } + + delete socket; + return err; +} + +/** +* @brief write to a socket +* @param handle: Pointer to handle +* data: pointer to data +* size: size of data +* @retval no of bytes sent +*/ +int SpwfSAInterface::socket_send(void *handle, const void *data, unsigned size) +{ + struct spwf_socket *socket = (struct spwf_socket *)handle; + //int err; + + /*if(socket->id==SERVER_SOCKET_NO) + { + if(socket->server_port==-1 || !isListening) + return NSAPI_ERROR_NO_SOCKET; //server socket not bound or not listening + + err = _spwf.socket_server_write((uint16_t)size, (char*)data); + } + else + { + err = _spwf.send(socket->id, (char*)data, (uint32_t)size); + }*/ + if (!_spwf.send(socket->id, (char*)data, (uint32_t)size)) { + return NSAPI_ERROR_DEVICE_ERROR; + } + + return size; +} + +/** +* @brief receive data on a socket +* @param handle: Pointer to handle +* data: pointer to data +* size: size of data +* @retval no of bytes read +*/ +int SpwfSAInterface::socket_recv(void *handle, void *data, unsigned size) +{ + struct spwf_socket *socket = (struct spwf_socket *)handle; + int32_t recv; + + //CHECK:Receive for both Client and Server Sockets same? + recv = _spwf.recv(socket->id, (char*)data, (uint32_t)size); + if (recv < 0) { + //wait_ms(1);//delay of 1ms <for F4>?? + //printf("."); + if (recv == -1) return NSAPI_ERROR_WOULD_BLOCK;//send this if we want to block call (else timeout will happen) + else if (recv == -2)return NSAPI_ERROR_DEVICE_ERROR; + else if (recv == -3)return NSAPI_ERROR_NO_CONNECTION; + } + return recv; +} + +/** +* @brief send data to a udp socket +* @param handle: Pointer to handle +* addr: address of udp socket +* data: pointer to data +* size: size of data +* @retval no of bytes sent +*/ +int SpwfSAInterface::socket_sendto(void *handle, const SocketAddress &addr, const void *data, unsigned size) +{ + struct spwf_socket *socket = (struct spwf_socket *)handle; + if (!socket->connected) { + int err = socket_connect(socket, addr); + if (err < 0) { + return err; + } + } + + return socket_send(socket, data, size); +} + +/** +* @brief receive data on a udp socket +* @param handle: Pointer to handle +* addr: address of udp socket +* data: pointer to data +* size: size of data +* @retval no of bytes read +*/ +int SpwfSAInterface::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size) +{ + struct spwf_socket *socket = (struct spwf_socket *)handle; + return socket_recv(socket, data, size); +} + +/** +* @brief attach function/callback to the socket +* Not used +* @param handle: Pointer to handle +* callback: callback function pointer +* data: pointer to data +* @retval none +*/ +void SpwfSAInterface::socket_attach(void *handle, void (*callback)(void *), void *data) +{ + //No implementation yet +} + +/** +* @brief utility debug function for printing to serial terminal +* @param string: Pointer to data +* @retval none +*/ +void SpwfSAInterface::debug(const char * string) +{ + //_spwf.debug_print(string); +} + +/** +* @brief Set the socket options +* Not used +* @param handle: Pointer to handle +* level: SOL_SOCKET +* optname: option name +* optval: pointer to option value +* optlen: option length +@retval NSAPI Error Type +*/ +int SpwfSAInterface::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen) +{ +// struct spwf_socket *socket = (struct spwf_socket *)handle; + + switch (optname) { + case NSAPI_REUSEADDR: /*!< Allow bind to reuse local addresses */ + case NSAPI_KEEPALIVE: /*!< Enables sending of keepalive messages */ + case NSAPI_LINGER: /*!< Keeps close from returning until queues empty */ + case NSAPI_SNDBUF: /*!< Sets send buffer size */ + case NSAPI_RCVBUF: /*!< Sets recv buffer size */ + default: + printf("SpwfSAInterface::setsockopt> ERROR!!!! Unknown optname: %d \r\n", optname); + return -1; + } + return NSAPI_ERROR_UNSUPPORTED;// defensive programming +} + +/** +* @brief Get the socket options +* Not used +* @param handle: Pointer to handle +* level: SOL_SOCKET +* optname: option name +* optval: pointer to option value +* optlen: pointer to option length +@retval NSAPI Error Type +*/ +int SpwfSAInterface::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen) +{ + return NSAPI_ERROR_UNSUPPORTED; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/X_NUCLEO_IDW01M1v2/SpwfInterface.h Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,99 @@ +/* mbed Microcontroller Library +* Copyright (c) 20015 ARM Limited +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** + ****************************************************************************** + * @file SpwfInterface.h + * @author STMicroelectronics + * @brief Header file of the NetworkStack for the SPWF Device + ****************************************************************************** + * @copy + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * <h2><center>© COPYRIGHT 2016 STMicroelectronics</center></h2> + ****************************************************************************** + */ + +#ifndef SPWFSA_INTERFACE_H +#define SPWFSA_INTERFACE_H + +#include "WiFiInterface.h" +#include "SPWFSA01.h" + +#define SPWFSA_SOCKET_COUNT 8 +#define SERVER_SOCKET_NO 9 + +/** SpwfSAInterface class + * Implementation of the NetworkStack for the SPWF Device + */ +class SpwfSAInterface : public NetworkStack, public WiFiInterface +{ +public: + SpwfSAInterface(PinName tx, PinName rx, bool debug); + SpwfSAInterface(PinName tx, PinName rx, PinName reset = PC_12, PinName wakeup = PC_8, bool debug = false); + virtual ~SpwfSAInterface(); + + // Implementation of WiFiInterface + virtual int connect(const char *ssid, + const char *pass, + nsapi_security_t security = NSAPI_SECURITY_NONE); + + inline int setSocketClientSecurity(uint8_t* tls_mode, uint8_t* root_ca_server, uint8_t* client_cert, uint8_t* client_key, uint8_t* client_domain, uint32_t tls_epoch_time) + { + return _spwf.setSocketClientSecurity(tls_mode, root_ca_server, client_cert, client_key, client_domain, tls_epoch_time); + } + + virtual int disconnect(); + virtual const char *get_mac_address(); + void debug(const char * string); + inline bool reset_chip() {return _spwf.hw_reset();}; + + //Implementation of NetworkStack + virtual const char *get_ip_address(); + +protected: + //Implementation of NetworkStack + virtual int socket_open(void **handle, nsapi_protocol_t proto); + virtual int socket_close(void *handle); + virtual int socket_bind(void *handle, const SocketAddress &address); //not supported + virtual int socket_listen(void *handle, int backlog); + virtual int socket_connect(void *handle, const SocketAddress &address); + virtual int socket_accept(void **handle, void *server); + virtual int socket_send(void *handle, const void *data, unsigned size); + virtual int socket_recv(void *handle, void *data, unsigned size); + virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size); + virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size); + virtual void socket_attach(void *handle, void (*callback)(void *), void *data); + virtual int setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen); + virtual int getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen); + +private: + int init(void); + + SPWFSA01 _spwf; + bool _ids[SPWFSA_SOCKET_COUNT]; + bool isListening; + bool isInitialized; +}; + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/X_NUCLEO_IKS01A1.lib Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,1 @@ +https://developer.mbed.org/teams/ST/code/X_NUCLEO_IKS01A1/#d17ab29129ce
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,399 @@ +/* SpwfInterface NetworkSocketAPI Example Program + * Copyright (c) 2015 ARM Limited + * Copyright (c) 2017 KLIKA TECH, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Contributors: + * Klika Tech - completely adopted to Amazon AWS IoT service + + */ + +#include "mbed.h" +#include "SpwfInterface.h" +#include "TCPSocket.h" +#include "MQTTClient.h" +#include "MQTTWiFi.h" +#include <ctype.h> +#include "x_nucleo_iks01a1.h" + +//------------------------------------ +// Hyperterminal default configuration +// 9600 bauds, 8-bit data, no parity +//------------------------------------ +Serial pc(SERIAL_TX, SERIAL_RX); +DigitalOut myled(LED2); +DigitalOut butled(LED3); +InterruptIn mybutton(USER_BUTTON); + +bool myButtonPressed = false; + +#define MQTT_MAX_PACKET_SIZE 350 +#define MQTT_MAX_PAYLOAD_SIZE 300 + +#define AWS_IOT_MQTT_HOST "xxxxxxxxxx.iot.us-east-1.amazonaws.com" //Use your own host. +#define AWS_IOT_MQTT_PORT 8883 +#define AWS_IOT_MQTT_CLIENT_ID "Nucleo" //Should be kept if you are using same device clent. +#define AWS_IOT_MY_THING_NAME "Nucleo" //Should be kept if you are using same device thing name. +#define AWS_IOT_MQTT_TOPIC_TEST "Nucleo/test" +#define AWS_IOT_MQTT_TOPIC_DATA "Nucleo/data" +#define AWS_IOT_MQTT_TOPIC_SHADOW "$aws/things/Nucleo/shadow/update" +#define AWS_IOT_ID "" +#define AWS_IOT_AUTH_TOKEN "" + +// WiFi network credential +#define SSID "" // Network must be visible otherwise it can't connect +#define PASSW "" +#error "Wifi SSID & password empty" + +#include "stdint.h" + + +/********************************************************************************************** +*********************************************************************************************** + Root CA certificate: Never modify +*********************************************************************************************** +***********************************************************************************************/ + +//This root CA can be used. +const uint8_t rootCA[] = "\ +-----BEGIN CERTIFICATE-----\n\ +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\ +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\ +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\ +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\ +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\ +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL\ +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW\ +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln\ +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp\ +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y\ +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1\ +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex\ +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz\ +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG\ +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+\ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/\ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E\ +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH\ +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy\ +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv\ +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE\ +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y\ +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK\ +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ\ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N\ +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao8WNq\n\ +-----END CERTIFICATE-----\n"; + +/********************************************************************************************** +*********************************************************************************************** + Device Identity Certificates: Modify for your AWS IoT Thing +*********************************************************************************************** +***********************************************************************************************/ + +/**************************************** +(somecode)-certificate.pem.crt - Amazon signed PEM sertificate. +*****************************************/ + +//This Client cert is example. Use own instead. +const uint8_t clientCRT[] = "\ +-----BEGIN CERTIFICATE-----\n\ +MIIDBjCCAe6gAwIBAgIUVph856omeIxW3UPioq+UrX1DbwowDQYJKoZIhvcNAQEL\ +BQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g\ +SW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTE3MDUyNTExNTEy\ +OVoXDEQ5MTIzMEIzNTk1OVowgZUxCzAJBgNVBAETAkJZMQ4wDAYDVQQIDAVNaW5z\ +azEOMAwGA1UEBwwFTWluc2sxFzAVBgNVBAoMDktsaWthLVRlY2ggTExDMRcwFQYD\ +VQQLDA5LbGlrYS1UZWNoIExMQzEMMAoGA1UEAwwDUm5EMSYwJAYJKoZIhvcNAQkB\ +FhdtdmF0YExldUBrbGlrYS10ZWNoLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEH\ +A0IABCJgOQJmoTBJVPfEi9Hm/JVixaxkY5rtlgrYO3hSl633A2hg0P/ue0wXDbF3\ +aQ0X57IRFE4k4FEbr3UXjT/IczKjYDBeMB8GA1UdIwQYMBaAFK3YzTUPlYB2Li75\ +i/z8rEogr1d6MB0GA1UdDgQWBBT18HXBaXFJuER/0SwegnxJ+pyJ6TAMBgNVHRMB\ +Af8EAjAAMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAb0Ux1aH5\ +RLxjrfGqXN6rPVqh8QQRS+AyBfzmaQN8HaPZMkX5WxXLvcn0A3uWlwQxPPkcZ4zf\ +51GHtFFQWB4YZ8dx8mUQ0v/j7onHjCJgZ8iDgwOyKMGtnsDEWCakQw+a6cj+NrMZ\ +tzhjwCzEEP6EPcbXwErI5OOzLuWns2L/JEr2wWNkokgRuS8ewr/SQ9OLWIWa2rFM\ +ahPNTb3y/qBeWdjeJmhI+TOxdqIpsF8roWP25zwo/zkzCHCjXFBrL+0CA4MpxIl9\ +x02i7aAhlJ6ys80lDxdeWeeQJXRKkGknP8mcmKn3iEqqJ5s1dQePj2b5d3ldatya\ +wsxQBqqZXzIWEw==\ +\n\ +-----END CERTIFICATE-----\n"; + + + +/********************************************************************************************** +*********************************************************************************************** + Private Key: Modify for your AWS IoT Thing +*********************************************************************************************** +***********************************************************************************************/ + +/********************************************************************8**************************************** +nucleo.key.pem - client key generated according to readme. +**************************************************************************************************************/ + +//This Client Key is example. Use own instead. +const uint8_t clientKey[] ="\ +-----BEGIN EC PARAMETERS-----\n\ +BggqhEEOPQMBEw==\ +-----END EC PARAMETERS-----\n\ +-----BEGIN EC PRIVATE KEY-----\n\ +MHcCAQEEIHPRfWSC8/k/BsqDWKuP15dXsI9fGwpkTIsLZe6mIrAEoAoGCCqGSM49\ +AwEHoUQDQEAEImAEAEahMElU9+WL0eb8lWLFrGRjmu2WCtg7eFKXrfcDaGDQ/+57\ +TBcNsXdpDRfnshEETiTgURuvdReNP8hEMg==\ +-----END EC PRIVATE KEY-----\n"; + +int connack_rc = 0; // MQTT connack return code +int connectTimeout = 1000; +int retryAttempt = 0; + +PressureSensor *pressure_sensor; +HumiditySensor *humidity_sensor; +TempSensor *temp_sensor1; +MagneticSensor *magnetic_sensor; +GyroSensor *gyro_sensor; +MotionSensor *accel_sensor; + +MQTT::Message message; +MQTTString TopicName= { AWS_IOT_MQTT_TOPIC_TEST }; +MQTT::MessageData MsgData(TopicName, message); + +void subscribe_cb(MQTT::MessageData & msgMQTT) { + char msg[MQTT_MAX_PAYLOAD_SIZE]; + msg[0]='\0'; + strncat (msg, (char*)msgMQTT.message.payload, msgMQTT.message.payloadlen); + printf ("--->>> subscribe_cb msg: %s\n\r", msg); +} + +int subscribe(MQTT::Client<MQTTWiFi, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTWiFi* ipstack) +{ + char* pubTopic = AWS_IOT_MQTT_TOPIC_TEST; + return client->subscribe(pubTopic, MQTT::QOS0, subscribe_cb); +} + +int connect(MQTT::Client<MQTTWiFi, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTWiFi* ipstack) +{ + SpwfSAInterface& WiFi = ipstack->getWiFi(); + + // Network debug statements + LOG("=====================================\n\r"); + LOG("Connecting WiFi.\n\r"); + LOG("Nucleo IP ADDRESS: %s\n\r", WiFi.get_ip_address()); + LOG("Nucleo MAC ADDRESS: %s\n\r", WiFi.get_mac_address()); + LOG("Server Hostname: %s port: %d\n\r", AWS_IOT_MQTT_HOST, AWS_IOT_MQTT_PORT); + LOG("Client ID: %s\n\r", AWS_IOT_MQTT_CLIENT_ID); + //LOG("Topic: %s\n\r", AWS_IOT_MQTT_TOPIC_TEST); + //LOG("Subscription URL: %s\n\r", subscription_url); + LOG("=====================================\n\r"); + + ipstack->open(&ipstack->getWiFi()); + + int rc=ipstack->getNTPtime(); + + if (rc != 0) + { + ERROR("Get NTP time error: %d\n", rc); + return rc; + } + + rc = WiFi.setSocketClientSecurity((uint8_t *)"m", (uint8_t *)rootCA, (uint8_t *)clientCRT, (uint8_t *)clientKey, (uint8_t *)AWS_IOT_MQTT_HOST, ipstack->getTime()); + + if (rc != 0) + { + ERROR("Set security params error: %d\n", rc); + return rc; + } + + rc = ipstack->connect(AWS_IOT_MQTT_HOST, AWS_IOT_MQTT_PORT, connectTimeout); + + if (rc != 0) + { + WARN("IP Stack connect returned: %d\n", rc); + return rc; + } + + printf ("--->TCP Connected\n\r"); + + // MQTT Connect + MQTTPacket_connectData data = MQTTPacket_connectData_initializer; + data.MQTTVersion = 4; + data.struct_version=0; + data.clientID.cstring = AWS_IOT_MQTT_CLIENT_ID; + //data.username.cstring = "use-token-auth"; + //data.password.cstring = AWS_IOT_AUTH_TOKEN; + + if ((rc = client->connect(data)) == 0) + { + printf ("--->MQTT Connected\n\r"); + + if (!subscribe(client, ipstack)) printf ("--->>>MQTT subscribed to: %s\n\r",AWS_IOT_MQTT_TOPIC_TEST); + } + else + { + WARN("MQTT connect returned %d\n", rc); + } + if (rc >= 0) + connack_rc = rc; + return rc; +} + +int getConnTimeout(int attemptNumber) +{ // First 10 attempts try within 3 seconds, next 10 attempts retry after every 1 minute + // after 20 attempts, retry every 10 minutes + return (attemptNumber < 10) ? 3 : (attemptNumber < 20) ? 60 : 600; +} + +void attemptConnect(MQTT::Client<MQTTWiFi, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTWiFi* ipstack) +{ + while (connect(client, ipstack) != MQTT_CONNECTION_ACCEPTED) + { + if (connack_rc == MQTT_NOT_AUTHORIZED || connack_rc == MQTT_BAD_USERNAME_OR_PASSWORD) + { + printf ("File: %s, Line: %d Error: %d\n\r",__FILE__,__LINE__, connack_rc); + return; // don't reattempt to connect if credentials are wrong + } + + int timeout = getConnTimeout(++retryAttempt); + WARN("Retry attempt number %d waiting %d\n", retryAttempt, timeout); + + // if ipstack and client were on the heap we could deconstruct and goto a label where they are constructed + // or maybe just add the proper members to do this disconnect and call attemptConnect(...) + // this works - reset the system when the retry count gets to a threshold + if (retryAttempt == 5) + { + ipstack->getWiFi().reset_chip(); + NVIC_SystemReset(); + } + else + wait(timeout); + } +} + +int publish(MQTT::Client<MQTTWiFi, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTWiFi* ipstack) +{ + MQTT::Message message; + char* pubTopic = AWS_IOT_MQTT_TOPIC_SHADOW; + + char buf[MQTT_MAX_PAYLOAD_SIZE]; + float temp, press, hum; + int32_t magnet[3]; + int32_t gyro[3]; + int32_t accel[3]; + + temp_sensor1->GetTemperature(&temp); + pressure_sensor->GetPressure(&press); + humidity_sensor->GetHumidity(&hum); + magnetic_sensor->Get_M_Axes(magnet); + gyro_sensor->Get_G_Axes(gyro); + accel_sensor->Get_X_Axes(accel); + + /*sprintf(buf, + "{\"d\":{\"ST\":\"Nucleo-IoT-mbed\",\"Temp\":%0.4f,\"Pressure\":%0.4f,\"Humidity\":%0.4f}}", + temp, press, hum);*/ + + if (!myButtonPressed) + { + butled = 1; + sprintf(buf, "{\"state\": {\"reported\": {\"temperature\": %f, \"humidity\": %f, \"pressure\": %f, \"accelerometer\": [%f, %f, %f], \"gyroscope\": [%f, %f, %f], \"magnetometer\": [%f, %f, %f]}}}", + temp, hum, press, accel[0]/1000.0, accel[1]/1000.0, accel[2]/1000.0, gyro[0]/1000.0, gyro[1]/1000.0, gyro[2]/1000.0, magnet[0]/10.0, magnet[1]/10.0, magnet[2]/10.0); + } + else + { + myButtonPressed = false; // reset state + butled = 0; + + sprintf(buf, "{\"temperature\": %f, \"humidity\": %f, \"pressure\": %f, \"accelerometer\": [%f, %f, %f], \"gyroscope\": [%f, %f, %f], \"magnetometer\": [%f, %f, %f], \"marker\": true}", + temp, hum, press, accel[0]/1000.0, accel[1]/1000.0, accel[2]/1000.0, gyro[0]/1000.0, gyro[1]/1000.0, gyro[2]/1000.0, magnet[0]/10.0, magnet[1]/10.0, magnet[2]/10.0); + pubTopic = AWS_IOT_MQTT_TOPIC_DATA; + } + + message.qos = MQTT::QOS0; + message.retained = false; + message.dup = false; + message.payload = (void*)buf; + message.payloadlen = strlen(buf); + + printf("Length - %d, Publishing %s\n\r", strlen(buf), buf); + + return client->publish(pubTopic, message); +} + +void pressed() +{ + myButtonPressed = true; +} + +int main() +{ + const char * ssid = SSID; // Network must be visible otherwise it can't connect + const char * seckey = PASSW; + + pc.baud(115200); + + SpwfSAInterface spwf(D8, D2, false); + + myled=0; + DevI2C *i2c = new DevI2C(I2C_SDA, I2C_SCL); + i2c->frequency(400000); + + mybutton.fall(&pressed); + + X_NUCLEO_IKS01A1 *mems_expansion_board = X_NUCLEO_IKS01A1::Instance(i2c); + pressure_sensor = mems_expansion_board->pt_sensor; + temp_sensor1 = mems_expansion_board->ht_sensor; + humidity_sensor = mems_expansion_board->ht_sensor; + magnetic_sensor = mems_expansion_board->magnetometer; + gyro_sensor = mems_expansion_board->GetGyroscope(); + accel_sensor = mems_expansion_board->GetAccelerometer(); + + pc.printf("\r\nX-NUCLEO-IDW01M1 mbed Application\r\n"); + pc.printf("\r\nconnecting to AP\r\n"); + + MQTTWiFi ipstack(spwf, ssid, seckey, NSAPI_SECURITY_WPA2); + + LOG("Connected to WiFI.\r\n"); + + MQTT::Client<MQTTWiFi, Countdown, MQTT_MAX_PACKET_SIZE> client(ipstack); + + attemptConnect(&client, &ipstack); + + if (connack_rc == MQTT_NOT_AUTHORIZED || connack_rc == MQTT_BAD_USERNAME_OR_PASSWORD) + { + while (true) + wait(1.0); // Permanent failures - don't retry + } + + myled=1; + + int count = 0; + + while (true) + { + if (++count == 100) + { + myled = 0; + // Publish a message every second + if (publish(&client, &ipstack) != 0) + { + myled=0; + ipstack.getWiFi().reset_chip(); + NVIC_SystemReset(); + attemptConnect(&client, &ipstack); // if we have lost the connection + } + else myled=1; + + count = 0; + } + + client.yield(10); // allow the MQTT client to receive messages + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Wed Sep 27 14:40:52 2017 +0300 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/2e9cc70d1897 \ No newline at end of file