Dependencies: C12832 EthernetInterface LM75B MMA7660 MQTT mbed-rtos mbed
Fork of IoTClientEthernet by
Diff: main.cpp
- Revision:
- 6:37b6d0d56190
- Parent:
- 5:11fd21af0c0f
- Child:
- 7:63a7aa4deaf8
--- a/main.cpp Thu Aug 07 09:33:26 2014 +0000 +++ b/main.cpp Wed Aug 20 12:45:14 2014 +0000 @@ -1,538 +1,391 @@ -/******************************************************************************* -* Copyright (c) 2014 IBM Corporation and other Contributors. -* -* All rights reserved. This program and the accompanying materials -* are made available under the terms of the Eclipse Public License v1.0 -* which accompanies this distribution, and is available at -* http://www.eclipse.org/legal/epl-v10.html -* -* Contributors: Sam Danbury -* IBM - Initial Contribution -*******************************************************************************/ - -#include "stdio.h" -#include "mbed.h" -#include "rtos.h" -#include "C12832.h" -#include "LM75B.h" -#include "MMA7660.h" -#include "EthernetInterface.h" -#include "MQTTSocket.h" -#include "MQTTClient.h" -#include "ConfigFile.h" -#include "Arial12x12.h" - -#include <string> -#include <sstream> -#include <algorithm> - -using namespace std; - -#ifdef TARGET_LPC1768 - -#warning "Compiling for mbed LPC1768" - -LocalFileSystem local("local"); -C12832 lcd(p5, p7, p6, p8, p11); -DigitalOut led2(LED2); -PwmOut r (p23); -PwmOut g (p24); -MMA7660 MMA(p28, p27); -LM75B sensor(p28, p27); -DigitalIn Down(p12); -DigitalIn Left(p13); -DigitalIn Click(p14); -DigitalIn Up(p15); -DigitalIn Right(p16); -AnalogIn ain1(p19); -AnalogIn ain2(p20); - -#elif TARGET_K64F - -#warning "Compiling for mbed K64F" - -//#define ORGANISATION "<org>"; -//#define TYPE "<type>"; -//#define ID "<id>"; -//#define AUTHMETHOD "<auth-method>"; -//#define AUTHTOKEN "<auth-token>"; - -C12832 lcd(D11, D13, D12, D7, D10); -BusOut r (D5); -BusOut g (D9); -BusOut led2 (LED_BLUE); -MMA7660 MMA(PTE25, PTE24); -LM75B sensor(PTE25, PTE24); -DigitalIn Up(A2); -DigitalIn Down(A3); -DigitalIn Right(A4); -DigitalIn Left(A5); -DigitalIn Click(D4); -AnalogIn ain1(A0); -AnalogIn ain2(A1); - -#else - -LocalFileSystem local("local"); -C12832 lcd(D11, D13, D12, D7, D10); -DigitalOut led1(LED1); -DigitalOut led2(LED2); -DigitalOut led3(LED3); -MMA7660 MMA(D14, D15); -LM75B sensor(D14,D15); -DigitalIn Up(A2); -DigitalIn Down(A3); -DigitalIn Left(A4); -DigitalIn Right(A5); -DigitalIn Click(D4); -AnalogIn ain1 (A0); -AnalogIn ain2 (A1); - -#endif - -//Joystick -string joystickPos; -void joystickThread(void const *args); - -//Commands -enum command { - blink -}; -command getCommand (std::string const& command); -void messageArrived(MQTT::MessageData& md); - -//MQTT -void connect(); -void attemptConnect(); -int getConnTimeout(int attemptNumber); -void subscribe(); - -//Config -void parseConfig(); -bool quickstartMode = true; -string org = ""; -string type = ""; -string id = ""; -string auth_method = ""; -string auth_token = ""; -string mac = ""; - -//LCD menu -bool connected = false; -bool menuActivated = false; -int menu = 0; -void printMenu(); - -int interval; -string getUUID48(); - -MQTTSocket ipstack; -MQTT::Client<MQTTSocket, Countdown, 250>* client; - -void parseConfig() { - - ConfigFile cfg; - - char value[30]; - char value1[30]; - char value2[30]; - char value3[30]; - - if (cfg.read("/local/device.cfg")) { - quickstartMode = false; - - if (cfg.getValue("org", value, sizeof(value))) { - stringstream ss(value); - ss >> org; - } - if (cfg.getValue("type", value1, sizeof(value1))) { - stringstream ss(value1); - ss >> type; - } - if (cfg.getValue("id", value2, sizeof(value2))) { - stringstream ss(value2); - ss >> id; - } - if (cfg.getValue("auth-token", value3, sizeof(value3))) { - stringstream ss(value3); - ss >> auth_token; - } - - } else { - quickstartMode = true; - org = "quickstart"; - #ifdef TARGET_K64F - type = "iotsample-mbed-k64f"; - #else - type = "iotsample-mbed-lpc1768"; - #endif - id = mac; - } - - #ifdef TARGET_K64F - #ifdef ORGANISATION - quickstartMode = false; - org = ORGANISATION; - - #ifdef TYPE - type = TYPE; - #else - lcd.printf("Type is not defined"); - #endif - - #ifdef ID - id = ID; - #else - lcd.printf("ID is not defined"); - #endif - - #ifdef AUTHMETHOD - auth_method = AUTHMETHOD; - #else - lcd.printf("Auth method is not defined"); - #endif - - #ifdef AUTHTOKEN - auth_token = AUTHTOKEN; - #else - lcd.printf("Auth token is not defined"); - #endif - #endif - #endif -} - -int main() -{ - //RGB: yellow - r = 0; - g = 0; - - #ifdef TARGET_K64F - led2 = 1; - #endif - - lcd.cls(); - lcd.set_font((unsigned char*) Arial12x12); - lcd.locate(0,0); - lcd.printf("IBM IoT Cloud"); - lcd.locate(0,16); - lcd.printf("Connecting"); - - //Connect to network - EthernetInterface eth; - eth.init(); - eth.connect(); - - //Obtain mac address of mbed - #ifdef TARGET_K64F - mac = getUUID48(); - #else - mac = eth.getMACAddress(); - - //Remove colons from mac address - mac.erase(remove(mac.begin(), mac.end(), ':'), mac.end()); - #endif - - //Parse config file if present - parseConfig(); - - attemptConnect(); - - if (!quickstartMode) { - subscribe(); - } - - //Start thread to read data from joystick - joystickPos = "CENTRE"; - Thread jThd(joystickThread); - - interval = 0; - int i = 0; - - while(1) - { - //Message published every second - if (i == 100) { - //MQTT Publish - MQTT::Message message; - char* pubTopic = "iot-2/evt/status/fmt/json"; - - char buf[250]; - sprintf(buf, - "{\"d\":{\"myName\":\"IoT mbed\",\"accelX\":%0.4f,\"accelY\":%0.4f,\"accelZ\":%0.4f,\"temp\":%0.4f,\"joystick\":\"%s\",\"potentiometer1\":%0.4f,\"potentiometer2\":%0.4f}}", - MMA.x(), MMA.y(), MMA.z(), - sensor.temp(), - joystickPos, - ain1.read(), - ain2.read()); - message.qos = MQTT::QOS0; - message.retained = false; - message.dup = false; - message.payload = (void*)buf; - message.payloadlen = strlen(buf); - - int rc = 0; - if ((rc = client->publish(pubTopic, &message)) != 0) { - connected = false; - attemptConnect(); - } - - i = 0; - } - - if (interval == 0) { - #ifdef TARGET_K64F - led2 = 1; - #else - led2 = 0; - #endif - } else { - if (i%(interval)==0) { - led2 = !led2; - } - } - - wait(0.01); - i++; - client->yield(1); - } -} - -void attemptConnect() { - int retryAttempt = 0; - menuActivated = false; - - //RGB: yellow - r = 0; - g = 0; - - lcd.cls(); - lcd.locate(0,0); - lcd.printf("IBM IoT Cloud"); - lcd.locate(0,16); - lcd.printf("Connecting"); - - while (!connected) { - - int connTimeout = getConnTimeout(++retryAttempt); - - connect(); - - if (!connected) { - wait(connTimeout); - } else { - break; - } - } -} - -int getConnTimeout(int attemptNumber) { - if (attemptNumber < 10) { - return 3; //First 10 attempts try within 3 seconds - } else if (attemptNumber < 20) { - return 60; //Next 10 attempts retry after every 1 minute - } else { - return 600; //After 20 attempts, retry every 10 minutes - } -} - -void connect() { - ipstack = MQTTSocket(); - client = new MQTT::Client<MQTTSocket, Countdown, 250>(ipstack); - - //TCP Connect - string ip = org + ".messaging.internetofthings.ibmcloud.com"; - - char* hostname = new char[ip.length() + 1]; - strcpy(hostname, ip.c_str()); - - int port = 1883; - int rc = ipstack.connect(hostname, port); - if (rc != 0) { - lcd.printf("TCP connect failed"); - } - - //Construct clientId based on config - string str = string("d:") + org + ":" + type + ":" + id; - char clientId[str.size()]; - memcpy(clientId, str.c_str(), str.size() + 1); - - //MQTT Connect - MQTTPacket_connectData data = MQTTPacket_connectData_initializer; - data.MQTTVersion = 3; - data.clientID.cstring = clientId; - - if (!quickstartMode) { - char* password = new char[auth_token.length() + 1]; - strcpy(password, auth_token.c_str()); - - data.username.cstring = "use-token-auth"; - data.password.cstring = password; - } - - if ((rc = client->connect(&data)) == 0) { - connected = true; - - //RGB: green - r = 1; - g = 0; - - lcd.locate(0,0); - lcd.printf("IBM IoT Cloud"); - lcd.locate(0,16); - lcd.printf("Connected"); - - wait(2); - - lcd.locate(0,0); - lcd.printf("IBM IoT Cloud"); - lcd.locate(0,16); - lcd.printf("Scroll with joystick"); - - menuActivated = true; - } -} - -void subscribe() { - char* subTopic = "iot-2/cmd/+/fmt/json"; - int rc = 0; - if ((rc = client->subscribe(subTopic, MQTT::QOS1, messageArrived)) != 0) - lcd.printf("rc from MQTT subscribe is %d\n", rc); -} - -void messageArrived(MQTT::MessageData& md) { - MQTT::Message &message = md.message; - - char* topic = new char[md.topicName.lenstring.len + 1]; - sprintf(topic, "%.*s", md.topicName.lenstring.len, md.topicName.lenstring.data); - - char* payload = new char[message.payloadlen + 1]; - sprintf(payload, "%.*s", message.payloadlen, (char*)message.payload); - - string topicStr = topic; - string payloadStr = payload; - - //Command topic: iot-2/cmd/blink/fmt/json - string cmd = topicStr.substr(10, topicStr.find("/fmt/") - 10); - - switch(getCommand(cmd)) { - case blink: { - string str = payloadStr.substr(8, payloadStr.find("}") - 8); - int rate = atoi(str.c_str()); - - if (rate == 0) { - interval = 0; - } else if (rate > 50) { - interval = 1; - } else if (rate > 0) { - interval = 50/rate; - } - - break; - } - default: - lcd.printf("Unsupported command: %s\n", cmd); - } - - if (topic) { - delete[] topic; - } - if (payload) { - delete[] payload; - } - -} - -command getCommand (string const& command) { - if (command == "blink") - return blink; -} - -void joystickThread(void const *args) { - while (true) { - - if (!menuActivated) { - menu = 0; - } - - if (Down) { - joystickPos = "DOWN"; - if (menu >= 0 && menu < 3) { - menu++; - printMenu(); - } - } else if (Left) { - joystickPos = "LEFT"; - } else if (Click) { - joystickPos = "CLICK"; - } else if (Up) { - joystickPos = "UP"; - if (menu <= 3 && menu > 0) { - menu--; - printMenu(); - } - } else if (Right) { - joystickPos = "RIGHT"; - } else { - joystickPos = "CENTRE"; - } - wait(0.2); - } -} - -void printMenu() { - if (menuActivated) { - lcd.cls(); - lcd.locate(0,0); - - switch(menu) { - case 0: - lcd.printf("IBM IoT Cloud"); - lcd.locate(0,16); - lcd.printf("Scroll with joystick"); - break; - case 1: - lcd.printf("Go to:"); - lcd.locate(0,16); - lcd.printf("http://ibm.biz/iotqstart"); - break; - case 2: - lcd.printf("Device Identity:"); - lcd.locate(0,16); - lcd.printf("%s", mac); - break; - case 3: - lcd.printf("Status:"); - lcd.locate(0,16); - lcd.printf("Connected"); - break; - } - } else { - menu = 0; - } -} - -string getUUID48 () { - - unsigned int UUID_LOC_WORD0 = 0x40048060; - unsigned int UUID_LOC_WORD1 = 0x4004805C; - - // Fetch word 0 - uint32_t Word0 = *(uint32_t *)UUID_LOC_WORD0; - - // Fetch word 1 - // we only want bottom 16 bits of word1 (MAC bits 32-47) - // and bit 9 forced to 1, bit 8 forced to 0 - // Locally administered MAC, reduced conflicts - // http://en.wikipedia.org/wiki/MAC_address - uint32_t Word1 = *(uint32_t *)UUID_LOC_WORD1; - Word1 |= 0x00000200; - Word1 &= 0x0000FEFF; - - string sd; - char stemp[100] = ""; - snprintf(stemp, 100, "%4X%08X", Word1,Word0); // I use the safer version of sprintf() -- snprintf() - sd = stemp; // the contents of sd are now "This is a string!" - - return (sd); -} \ No newline at end of file +/******************************************************************************* + * 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: + * Sam Danbury - initial implementation + * Ian Craggs - refactoring to remove STL and other changes + *******************************************************************************/ + +#include "LM75B.h" +#include "MMA7660.h" +#include "MQTTClient.h" +#include "MQTTEthernet.h" +#include "C12832.h" +#include "Arial12x12.h" +#include "rtos.h" + +// Configuration values needed to connect to IBM IoT Cloud +#define QUICKSTARTMODE 1 +#if (QUICKSTARTMODE) +#define ORG "quickstart" +#define ID "" +#define AUTH_TOKEN "" +#define TYPE DEFAULT_TYPE_NAME +#else +#define ORG "Replace with your org" +#define ID "Replace with your id" +#define TYPE "Replace with your type" +#define AUTH_TOKEN "Replace with your auth-token" +#endif + +#define MQTT_PORT 1883 +#define MQTT_TLS_PORT 8883 +#define IBM_IOT_PORT MQTT_PORT + +#define MQTT_MAX_PACKET_SIZE 250 + +#if defined(TARGET_LPC1768) +#warning "Compiling for mbed LPC1768" +#include "LPC1768.h" +#elif defined(TARGET_K64F) +#warning "Compiling for mbed K64F" +#include "K64F.h" +#endif + +bool quickstartMode = (QUICKSTARTMODE) ? true : false; +char org[11] = ORG; +char type[30] = TYPE; +char id[30] = ID; // mac without colons +char auth_token[30] = AUTH_TOKEN; // Auth_token is only used in non-quickstart mode + +bool connected = false; +char* joystickPos = "CENTRE"; +int blink_interval = 0; + + +void off() +{ + r = g = b = 1.0; // 1 is off, 0 is full brightness +} + +void red() +{ + r = 0.7; g = 1.0; b = 1.0; // 1 is off, 0 is full brightness +} + +void yellow() +{ + r = 0.7; g = 0.7; b = 1.0; // 1 is off, 0 is full brightness +} + +void green() +{ + r = 1.0; g = 0.7; b = 1.0; // 1 is off, 0 is full brightness +} + + +void flashing_yellow(void const *args) +{ + bool on = false; + while (!connected) // flashing yellow only while connecting + { + on = !on; + if (on) + yellow(); + else + off(); + wait(0.5); + } +} + + +void flashing_red(void const *args) // to be used when the connection is lost +{ + bool on = false; + while (!connected) + { + on = !on; + if (on) + red(); + else + off(); + wait(2.0); + } +} + + +void printMenu(int menuItem) +{ + lcd.cls(); + lcd.locate(0,0); + switch (menuItem) + { + case 0: + lcd.printf("IBM IoT Cloud"); + lcd.locate(0,16); + lcd.printf("Scroll with joystick"); + break; + case 1: + lcd.printf("Go to:"); + lcd.locate(0,16); + lcd.printf("http://ibm.biz/iotqstart"); + break; + case 2: + lcd.printf("Device Identity:"); + lcd.locate(0,16); + lcd.printf("%s", id); + break; + case 3: + lcd.printf("Status:"); + lcd.locate(0,16); + lcd.printf(connected ? "Connected" : "Disconnected"); + break; + } +} + + +void setMenu() +{ + static int menuItem = 0; + if (Down) + { + joystickPos = "DOWN"; + if (menuItem >= 0 && menuItem < 3) + printMenu(++menuItem); + } + else if (Left) + joystickPos = "LEFT"; + else if (Click) + joystickPos = "CLICK"; + else if (Up) + { + joystickPos = "UP"; + if (menuItem <= 3 && menuItem > 0) + printMenu(--menuItem); + } + else if (Right) + joystickPos = "RIGHT"; + else + joystickPos = "CENTRE"; +} + + +/** + * Display a message on the LCD screen prefixed with IBM IoT Cloud + */ +void displayMessage(char* message) +{ + lcd.cls(); + lcd.locate(0,0); + lcd.printf("IBM IoT Cloud"); + lcd.locate(0,16); + lcd.printf(message); +} + + +int connect(MQTT::Client<MQTTEthernet, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTEthernet* ipstack) +{ + const char* iot_ibm = ".messaging.internetofthings.ibmcloud.com"; + + char hostname[strlen(org) + strlen(iot_ibm) + 1]; + sprintf(hostname, "%s%s", org, iot_ibm); + int rc = ipstack->connect(hostname, IBM_IOT_PORT); + if (rc != 0) + return rc; + + // Construct clientId - d:org:type:id + char clientId[strlen(org) + strlen(type) + strlen(id) + 5]; + sprintf(clientId, "d:%s:%s:%s", org, type, id); + DEBUG("clientid is %s\n", clientId); + + // MQTT Connect + MQTTPacket_connectData data = MQTTPacket_connectData_initializer; + data.MQTTVersion = 3; + data.clientID.cstring = clientId; + + if (!quickstartMode) + { + data.username.cstring = "use-token-auth"; + data.password.cstring = auth_token; + } + + if ((rc = client->connect(&data)) == 0) + { + connected = true; + green(); + displayMessage("Connected"); + wait(2); + displayMessage("Scroll with joystick"); + } + 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<MQTTEthernet, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTEthernet* ipstack) +{ + int retryAttempt = 0; + connected = false; + + while (connect(client, ipstack) != 0) + { +#if defined(TARGET_K64F) + red(); +#else + Thread red_thread(flashing_red); +#endif + int timeout = getConnTimeout(++retryAttempt); + WARN("Retry attempt number %d waiting %d\n", retryAttempt, timeout); + wait(timeout); + } +} + + +int publish(MQTT::Client<MQTTEthernet, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTEthernet* ipstack) +{ + MQTT::Message message; + char* pubTopic = "iot-2/evt/status/fmt/json"; + + char buf[250]; + sprintf(buf, + "{\"d\":{\"myName\":\"IoT mbed\",\"accelX\":%0.4f,\"accelY\":%0.4f,\"accelZ\":%0.4f,\"temp\":%0.4f,\"joystick\":\"%s\",\"potentiometer1\":%0.4f,\"potentiometer2\":%0.4f}}", + MMA.x(), MMA.y(), MMA.z(), sensor.temp(), joystickPos, ain1.read(), ain2.read()); + message.qos = MQTT::QOS0; + message.retained = false; + message.dup = false; + message.payload = (void*)buf; + message.payloadlen = strlen(buf); + + LOG("Publishing %s\n", buf); + return client->publish(pubTopic, &message); +} + + +#if defined(TARGET_K64F) +int getUUID48(char* buf, int buflen) +{ + unsigned int UUID_LOC_WORD0 = 0x40048060; + unsigned int UUID_LOC_WORD1 = 0x4004805C; + + // Fetch word 0 + uint32_t word0 = *(uint32_t *)UUID_LOC_WORD0; + + // Fetch word 1 + // we only want bottom 16 bits of word1 (MAC bits 32-47) + // and bit 9 forced to 1, bit 8 forced to 0 + // Locally administered MAC, reduced conflicts + // http://en.wikipedia.org/wiki/MAC_address + uint32_t word1 = *(uint32_t *)UUID_LOC_WORD1; + word1 |= 0x00000200; + word1 &= 0x0000FEFF; + + int rc = snprintf(buf, buflen, "%4X%08X", word1, word0); + + return rc; +} +#else +char* getMac(EthernetInterface& eth, char* buf, int buflen) // Obtain MAC address +{ + strncpy(buf, eth.getMACAddress(), buflen); + + char* pos; // Remove colons from mac address + while ((pos = strchr(buf, ':')) != NULL) + memmove(pos, pos + 1, strlen(pos) + 1); + return buf; +} +#endif + + +void messageArrived(MQTT::MessageData& md) +{ + 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: %.*s\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; + + if (memcmp(start, "blink", len) == 0) + { + char payload[message.payloadlen + 1]; + sprintf(payload, "%.*s", message.payloadlen, (char*)message.payload); + + char* pos = strchr(payload, '}'); + if (pos != NULL) + { + *pos = '\0'; + if ((pos = strchr(payload, ':')) != NULL) + { + int blink_rate = atoi(pos + 1); + blink_interval = (blink_rate <= 0) ? 0 : (blink_rate > 50 ? 1 : 50/blink_rate); + } + } + } + else + WARN("Unsupported command: %.*s\n", len, start); +} + + +int main() +{ + lcd.set_font((unsigned char*) Arial12x12); // Set a nice font for the LCD screen + + led2 = LED2_OFF; // K64F: turn off the main board LED + + displayMessage("Connecting"); +#if defined(TARGET_K64F) + yellow(); // Don't flash on the K64F, because starting a thread causes the EthernetInterface init call to hang +#else + Thread yellow_thread(flashing_yellow); +#endif + + MQTTEthernet ipstack; + MQTT::Client<MQTTEthernet, Countdown, MQTT_MAX_PACKET_SIZE> client(ipstack); + + if (quickstartMode) + { +#if defined(TARGET_K64F) + getUUID48(id, sizeof(id)); // getMac doesn't work on the K64F +#else + getMac(ipstack.getEth(), id, sizeof(id)); +#endif + } + + attemptConnect(&client, &ipstack); + + if (!quickstartMode) + { + int rc = 0; + if ((rc = client.subscribe("iot-2/cmd/+/fmt/json", MQTT::QOS1, messageArrived)) != 0) + WARN("rc from MQTT subscribe is %d\n", rc); + } + + blink_interval = 0; + int count = 0; + while (true) + { + if (++count == 100) + { // Publish a message every second + if (publish(&client, &ipstack) != 0) + attemptConnect(&client, &ipstack); // if we have lost the connection + count = 0; + } + + if (blink_interval == 0) + led2 = LED2_OFF; + else if (count % blink_interval == 0) + led2 = !led2; + if (count % 20 == 0) + setMenu(); + client.yield(10); // allow the MQTT client to receive messages + } +}