IoT Starter Kit Code
Dependencies: C12832 EthernetInterface LM75B MMA7660 MQTT mbed-rtos mbed
Diff: main.cpp
- Revision:
- 0:0777b6b0c36f
- Child:
- 1:8f47175f0f53
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Sep 29 11:44:51 2014 +0000 @@ -0,0 +1,367 @@ +/******************************************************************************* + * 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 + * Sam Grove - added check for Ethernet cable. + *******************************************************************************/ + +#include "LM75B.h" +#include "MMA7660.h" +#include "MQTTClient.h" +#include "MQTTEthernet.h" +#include "C12832.h" +#include "Arial12x12.h" +#include "rtos.h" +#include "K64F.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 + +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; + + // make sure a cable is connected before starting to connect + while (!linkStatus()) { + wait(1.0f); + WARN("Ethernet link not present. Check cable connection\n"); + } + + while (connect(client, ipstack) != 0) + { + red(); // Thread red_thread(flashing_red); + + int timeout = getConnTimeout(++retryAttempt); + WARN("Retry attempt number %d waiting %d\n", retryAttempt, timeout); + if (retryAttempt == 5) + NVIC_SystemReset(); + else + 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); +} + + +int getUUID48(char* buf, int buflen) +{ + unsigned int UUID_LOC_WORD0 = 0x40048060; + unsigned int UUID_LOC_WORD1 = 0x4004805C; + + uint32_t word0 = *(uint32_t *)UUID_LOC_WORD0; // Fetch word 0 + + uint32_t word1 = *(uint32_t *)UUID_LOC_WORD1; // 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 + word1 |= 0x00000200; + word1 &= 0x0000FEFF; + + int rc = snprintf(buf, buflen, "%4X%08X", word1, word0); + + return rc; +} + + +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; // turn off the main board LED + + displayMessage("Connecting"); + + yellow(); //Thread yellow_thread(flashing_yellow); causes the EthernetInterface init call to hang + + MQTTEthernet ipstack; + MQTT::Client<MQTTEthernet, Countdown, MQTT_MAX_PACKET_SIZE> client(ipstack); + + if (quickstartMode) + getUUID48(id, sizeof(id)); + + 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 + } +}