Had to fork with a different name, because of some incompatibility issues.
DeviceClient.cpp
- Committer:
- lamell
- Date:
- 2018-12-12
- Revision:
- 10:665cfb0da4c5
- Parent:
- 8:cb2c98efdca8
- Child:
- 12:ac091f717e40
File content as of revision 10:665cfb0da4c5:
/******************************************************************************* * Copyright (c) 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: * Sathisumar Palaniappan - initial implementation * Sathisumar Palaniappan - added reconnect logic and isConnected() method * Lokesh K Haralakatta - Port to mbed OS 5 support * Lokesh K Haralakatta - Added SSL/TLS Support *******************************************************************************/ #include "MQTTClient.h" #include "DeviceClient.h" // need a wrapper since K64F and LPC1768 wont have the same name for mii read methods #if defined(TARGET_UBLOX_C027) || defined(TARGET_K64F) static uint32_t linkStatus(void) { return (1); } #elif defined(TARGET_LPC1768) #include "lpc_phy.h" static uint32_t linkStatus(void) { return (lpc_mii_read_data() & 1); } #endif using namespace IoTF; CommandHandler handler = NULL; void msgArrived(MQTT::MessageData& md); /** * A client, used by device, that handles connections with the IBM Internet of Things Foundation. * This class allows device to publish events and receive commands to/from IBM IoT Foundation wtih simple function calls. */ DeviceClient::DeviceClient():org(NULL),deviceType(NULL),deviceId(NULL), authMethod(NULL),authToken(NULL),mqttNetwork(NULL),mqttClient(NULL),connected(false),port(0) { LOG("Constructor#1 called::\r\n"); } DeviceClient::DeviceClient(char *orgId, char *typeId, char *id, int port):org(orgId),deviceType(typeId), deviceId(id),authMethod(NULL),authToken(NULL),connected(false), port(port) { LOG("Constructor#2 called:: org=%s, type=%s, id=%s\r\n", (org==NULL)?"NULL":org, (deviceType==NULL)?"NULL":deviceType, (deviceId==NULL)?"NULL":deviceId); if(strcmp(this->org, QUICKSTART) != 0) { WARN("Registered flow must provide valid token\r\n"); } mqttNetwork = new MQTTNetwork(); mqttClient = new MQTT::Client<MQTTNetwork, Countdown>(*mqttNetwork); } DeviceClient::DeviceClient(char *orgId, char *typeId,char *id, char *method, char *token, int port):org(orgId), deviceType(typeId),deviceId(id),authMethod(method),authToken(token),connected(false), port(port) { // Don't print token for security reasons LOG("Constructor#3 called:: org=%s, type=%s, id=%s\r\n", (org==NULL)?"NULL":org, (deviceType==NULL)?"NULL":deviceType, (deviceId==NULL)?"NULL":deviceId); mqttNetwork = new MQTTNetwork(); mqttClient = new MQTT::Client<MQTTNetwork, Countdown>(*mqttNetwork); } /** * Connect to the IBM Internet of Things Foundation */ bool DeviceClient::connect() { char *organizationName, *typeId, *id; bool rc = false; // Check if any organization is set if(this->org == NULL || (strcmp("", this->org) == 0)) { organizationName = QUICKSTART; } else { organizationName = this->org; } // Check if device type is already mentioned if(this->deviceType == NULL || (strcmp("", this->deviceType) == 0)) { typeId = "iotsample-mbed"; } else { typeId = this->deviceType; } char hostname[strlen(organizationName) + strlen(IBM_IOT_MESSAGING) + 1]; sprintf(hostname, "%s%s", organizationName, IBM_IOT_MESSAGING); NetworkInterface* net = mqttNetwork->getEth(); const char* ip = net->get_ip_address(); // Get devices MAC address if deviceId is not set already if(this->deviceId == NULL || (strcmp("", this->deviceId) == 0)) { char tmpBuf[50]; id = getMac(tmpBuf, sizeof(tmpBuf)); } else { id = this->deviceId; } // Construct clientId - d:org:type:id char clientId[strlen(organizationName) + strlen(typeId) + strlen(id) + 5]; sprintf(clientId, "d:%s:%s:%s", organizationName, typeId, id); // Initialize MQTT Connect MQTTPacket_connectData data = MQTTPacket_connectData_initializer; data.MQTTVersion = 4; data.clientID.cstring = clientId; int quickstartMode = (strcmp(organizationName, QUICKSTART) == 0); if (!quickstartMode) { data.username.cstring = "use-token-auth"; data.password.cstring = this->authToken; //Check and initialize appropriate port if(port == 1883) port = MQTT_TLS_PORT; } logData(net, hostname, clientId); if(ip){ rc = tryConnect(hostname, data); // By default subscribe to commands if we are in registered flow if(rc == true && !quickstartMode) { subscribeToCommands(); } if(rc == true) { connected = true; LOG("Device Client Connected to %s:%d\r\n",hostname,port); } } else LOG("No IP Assigned to Network Interface...\r\n"); return rc; } /** * Reconnect when the connection is lost. This method disconnects the active connection if any * and tries to initiate a fresh connection. * This method uses the Ethernet Link status wherever applicable while reconnecting. i.e, tries to * initiate the connection only when the Ethernet cable is plugged in. */ bool DeviceClient::reConnect() { LOG("DeviceClient::reConnect() entry and connected = %s\r\n",(connected == true)?"true":"false"); if(connected == true) { disconnect(); } if(linkStatus()) { NetworkInterface* net = mqttNetwork->getEth(); if(net->connect() == 0) { bool status = connect(); if(status == false) { net->disconnect(); } return status; } } return false; } bool DeviceClient::tryConnect(char *hostname, MQTTPacket_connectData &data) { int rc = -1; int retryAttempt = 0; do { rc = mqttNetwork->connect(hostname, port); if (rc != 0) { WARN("mqttNetwork connect returned: %d\r\n", rc); } // MQTT connect if (rc == 0 && (rc = mqttClient->connect(data)) != 0) { WARN("MQTT connect returned %d\r\n", rc); if (rc == MQTT_NOT_AUTHORIZED || rc == MQTT_BAD_USERNAME_OR_PASSWORD) return false; // don't reattempt to connect if credentials are wrong } else if (rc == MQTT_CONNECTION_ACCEPTED) { return true; } int timeout = getConnTimeout(++retryAttempt); WARN("Retry attempt number %d waiting %d\r\n", retryAttempt, timeout); // enough retry is done - return to application if (retryAttempt == 5){ //Here's my modification. If not connected, RESET the board. NVIC_SystemReset(); return false; } else { wait(timeout); } } while(true); } void DeviceClient::logData(NetworkInterface* net, char *hostname, char *clientId) { // Network debug statements LOG("=====================================\r\n"); LOG("Connection Config Details:\r\n"); LOG("IP ADDRESS: %s\r\n", net->get_ip_address()); LOG("MAC ADDRESS: %s\r\n", net->get_mac_address()); LOG("Gateway: %s\r\n", net->get_gateway()); LOG("Network Mask: %s\r\n", net->get_netmask()); LOG("Server Hostname: %s\r\n", hostname); LOG("Server Port: %d\r\n", port); LOG("Client ID: %s\r\n", clientId); LOG("=====================================\r\n"); } int DeviceClient::getConnTimeout(int attemptNumber) { // Try to increase the timeout every time return (attemptNumber * attemptNumber * 5); } /** * Returns the connection status, connected or disconnected. */ bool DeviceClient::isConnected() { return mqttClient->isConnected(); } /** * Publish data to the IBM Internet of Things Foundation. Note that data is published * by default at Quality of Service (QoS) 0, which means that a successful send * does not guarantee receipt even if the publish has been successful. */ bool DeviceClient::publishEvent(char *eventName, char *data, MQTT::QoS qos) { if(!mqttClient->isConnected()) { WARN("Client is not connected \r\n"); return false; } MQTT::Message message; /* Topic format must be iot-2/evt/<eventName>/fmt/json (let us stick to json format for now) * * So length must be 10 + strlen(eventName) + 9 + 1 * iot-2/evt/ = 10 * /fmt/json = 9 * NULL char = 1 */ char topic[10 + strlen(eventName) + 9 + 1]; sprintf(topic, "%s%s%s", "iot-2/evt/", eventName, "/fmt/json"); message.qos = qos; message.retained = false; message.dup = false; message.payload = (void*)data; message.payloadlen = strlen(data); LOG("Publishing %s\r\n", data); int rc = mqttClient->publish(topic, message); return rc == 0; } void DeviceClient::setCommandCallback(CommandHandler callbackFunc) { handler = callbackFunc; } /** * Subscribe to commands from the application. This will be executed only for * registered flow (quickstart flow does not support command publish) */ int DeviceClient::subscribeToCommands() { int rc = 0; // iot-2/cmd/+/fmt/+ if ((rc = mqttClient->subscribe("iot-2/cmd/+/fmt/+", MQTT::QOS2, msgArrived)) != 0) WARN("rc from MQTT subscribe is %d\r\n", rc); return rc; } /** * Callback method to be registered with MQTT::Client. MQTT::Client calls whenever * any command is published to the topic subscribed earlier. */ void msgArrived(MQTT::MessageData& md) { // check whether callback is registered by the client code if(handler == NULL) { return; } MQTT::Message &message = md.message; char topic[md.topicName.lenstring.len + 1]; sprintf(topic, "%.*s", md.topicName.lenstring.len, md.topicName.lenstring.data); LOG("Message arrived on topic %s: Length: %ul. Payload: %s\r\n", topic, message.payloadlen, message.payload); // Command topic: iot-2/cmd/blink/fmt/json - cmd is the string between cmd/ and /fmt/ char* start = strstr(topic, "/cmd/") + 5; int len = strstr(topic, "/fmt/") - start; char name[len + 1]; memcpy(name, start, len); name[len] = NULL; start = strstr(topic, "/fmt/") + 5; char format[20]; // ToDO: need to find the length of the format strcpy(format, start); char payload[message.payloadlen + 1]; sprintf(payload, "%.*s", message.payloadlen, (char*)message.payload); IoTF::Command cmd(name, format, payload); (*handler)(cmd); } /** * Disconnects the connection in order. */ bool DeviceClient::disconnect() { int rc = 0; if(mqttClient->isConnected()) { rc = mqttClient->disconnect(); } NetworkInterface* net = mqttNetwork->getEth(); mqttNetwork->disconnect(); net->disconnect(); connected = false; return rc == 0; } // Yield to allow MQTT client to process the command void DeviceClient::yield(int ms) { if(mqttClient->isConnected()) { mqttClient->yield(ms); } } // Obtain DeviceId address char* DeviceClient::getDeviceId(char* buf, int buflen) { if(this->deviceId == NULL || (strcmp("", this->deviceId) == 0)) { return getMac(buf, buflen); } else { return strncpy(buf, this->deviceId, buflen); } } // Obtain MAC address char* DeviceClient::getMac(char* buf, int buflen) { NetworkInterface* net = mqttNetwork->getEth(); strncpy(buf, net->get_mac_address(), buflen); char* pos; // Remove colons from mac address while ((pos = strchr(buf, ':')) != NULL) memmove(pos, pos + 1, strlen(pos) + 1); return buf; } char* DeviceClient::ipaddress() { //char iplocal[25]; NetworkInterface* net = mqttNetwork->getEth(); //const char* ip = net->get_ip_address(); //strcpy(iplocal,ip); //return iplocal; return (char*)net->get_ip_address(); } //NetworkInterface* DeviceClient::eth() { // NetworkInterface* net = mqttNetwork->getEth(); // return (NetworkInterface*)net; //}